Repository: segmentio/evergreen (language: javascript)

Issue:
Forward ref to select in Select control
I am trying to use ref with the `Select` control, however I'm getting the

`Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?`

error message. Is there any way that this control will work the same way as `TextInputField`, that redirects refs to the `<input>`?


Discussion / hints:
Looks like we're passing the ref to the wrapping `<Box />` instead of the `<Box />` that's actually rendering a `<select />`.


https://github.com/segmentio/evergreen/blob/ec0e180dc18871c8d6873de7f2a1a65fe1b28b3b/src/select/src/Select.js#L75-L87

https://github.com/segmentio/evergreen/blob/ec0e180dc18871c8d6873de7f2a1a65fe1b28b3b/src/text-input/src/TextInput.js#L56-L66

Should be simple to swap - I don't see any real reason to have it on the wrapping box!
Should be resolved in [v6.9.6](https://github.com/segmentio/evergreen/releases/tag/v6.9.6)!
@brandongregoryscott  What about the SelectField component? I'm still seeing the same error with that component in 6.9.6
> @brandongregoryscott What about the SelectField component? I'm still seeing the same error with that component in 6.9.6

Ah, good catch - looks like that component isn't even forwarding the ref right now. 
https://github.com/segmentio/evergreen/blob/890de4300a3d9a820eca6c4f2c8d045cf13bf23d/src/select/src/SelectField.js#L8

Additionally, I think I missed the TypeScript definition updates which still list them as div BoxComponents (should be `select` instead)
https://github.com/segmentio/evergreen/blob/890de4300a3d9a820eca6c4f2c8d045cf13bf23d/index.d.ts#L2246-L2251

@brandongregoryscott great! Thanks for the quick response

Fix patch:
diff --git a/index.d.ts b/index.d.ts
index 8052fab43..5c47ec701 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -2243,12 +2243,12 @@ export interface SelectOwnProps {
   size?: 'small' | 'medium' | 'large'
 }
 
-export type SelectProps = PolymorphicBoxProps<'div', SelectOwnProps>
-export declare const Select: BoxComponent<SelectOwnProps, 'div'>
+export type SelectProps = PolymorphicBoxProps<'select', SelectOwnProps>
+export declare const Select: BoxComponent<SelectOwnProps, 'select'>
 
 export type SelectFieldOwnProps = FormFieldOwnProps & SelectOwnProps
-export type SelectFieldProps = PolymorphicBoxProps<'div', SelectFieldOwnProps>
-export declare const SelectField: BoxComponent<SelectFieldOwnProps, 'div'>
+export type SelectFieldProps = PolymorphicBoxProps<'select', SelectFieldOwnProps>
+export declare const SelectField: BoxComponent<SelectFieldOwnProps, 'select'>
 
 export interface SelectMenuContentProps {
   close?: OptionsListProps['close']
diff --git a/src/select/src/SelectField.js b/src/select/src/SelectField.js
index 4bc212a20..107b9a1ba 100644
--- a/src/select/src/SelectField.js
+++ b/src/select/src/SelectField.js
@@ -1,65 +1,68 @@
-import React, { memo } from 'react'
+import React, { memo, forwardRef } from 'react'
 import PropTypes from 'prop-types'
 import { splitBoxProps } from 'ui-box'
 import { FormField } from '../../form-field'
 import { useId } from '../../hooks'
 import Select from './Select'
 
-const SelectField = memo(function SelectField(props) {
-  const id = useId('SelectField', props.id)
-
-  const {
-    // We are using the id from the state
-    appearance,
-
-    // FormField props
-    description,
-    disabled,
-    hint,
-    id: unusedId,
-
-    // TextInput props
-    inputHeight = 32,
-    /** The input width should be as wide as the form field. */
-    inputWidth = '100%',
-    isInvalid,
-    label,
-    required,
-    validationMessage,
-
-    // Rest props are spread on the FormField
-    ...rest
-  } = props
-
-  /**
-   * Split the wrapper props from the input props.
-   */
-  const { matchedProps, remainingProps } = splitBoxProps(rest)
-
-  return (
-    <FormField
-      marginBottom={24}
-      label={label}
-      isRequired={required}
-      hint={hint}
-      description={description}
-      validationMessage={validationMessage}
-      labelFor={id}
-      {...matchedProps}
-    >
-      <Select
-        id={id}
-        width={inputWidth}
-        height={inputHeight}
-        disabled={disabled}
-        required={required}
-        isInvalid={isInvalid}
-        appearance={appearance}
-        {...remainingProps}
-      />
-    </FormField>
-  )
-})
+const SelectField = memo(
+  forwardRef(function SelectField(props, ref) {
+    const id = useId('SelectField', props.id)
+
+    const {
+      // We are using the id from the state
+      appearance,
+
+      // FormField props
+      description,
+      disabled,
+      hint,
+      id: unusedId,
+
+      // TextInput props
+      inputHeight = 32,
+      /** The input width should be as wide as the form field. */
+      inputWidth = '100%',
+      isInvalid,
+      label,
+      required,
+      validationMessage,
+
+      // Rest props are spread on the FormField
+      ...rest
+    } = props
+
+    /**
+     * Split the wrapper props from the input props.
+     */
+    const { matchedProps, remainingProps } = splitBoxProps(rest)
+
+    return (
+      <FormField
+        marginBottom={24}
+        label={label}
+        isRequired={required}
+        hint={hint}
+        description={description}
+        validationMessage={validationMessage}
+        labelFor={id}
+        {...matchedProps}
+      >
+        <Select
+          id={id}
+          width={inputWidth}
+          height={inputHeight}
+          disabled={disabled}
+          required={required}
+          isInvalid={isInvalid}
+          appearance={appearance}
+          ref={ref}
+          {...remainingProps}
+        />
+      </FormField>
+    )
+  })
+)
 
 SelectField.propTypes = {
   /**


Test patch:
diff --git a/src/select/__tests__/SelectField.test.js b/src/select/__tests__/SelectField.test.js
new file mode 100644
index 000000000..79630201c
--- /dev/null
+++ b/src/select/__tests__/SelectField.test.js
@@ -0,0 +1,16 @@
+import React from 'react'
+import { render } from '@testing-library/react'
+import { SelectField } from '../'
+import { mockRef } from '../../test/utils'
+
+const makeSelectFieldFixture = (props = {}) => <SelectField data-testid="select-field" label="SelectField" {...props} />
+
+describe('SelectField', () => {
+  it('should forward ref to underlying <select />', () => {
+    const ref = mockRef()
+
+    render(makeSelectFieldFixture({ ref }))
+
+    expect(ref.current).toBeInstanceOf(HTMLSelectElement)
+  })
+})