diff --git a/src/components/Select/Select.stories.tsx b/src/components/Select/Select.stories.tsx index 4adbd71e5..5b14e88b8 100644 --- a/src/components/Select/Select.stories.tsx +++ b/src/components/Select/Select.stories.tsx @@ -67,6 +67,7 @@ function InteractiveExampleUsingChildren(props: Props) { className={clsx(!compact && 'w-60')} data-testid="dropdown" disabled={disabled} + name="interactive-select" onChange={setSelectedOption} optionsAlign={optionsAlign} optionsClassName={optionsClassName} @@ -99,6 +100,7 @@ function InteractiveExampleUsingFunctionChildren() { as="div" className="w-60" data-testid="dropdown" + name="interactive-with-children" onChange={setSelectedOption} value={selectedOption} > diff --git a/src/components/Select/Select.test.tsx b/src/components/Select/Select.test.tsx index baa942584..1da7a81a3 100644 --- a/src/components/Select/Select.test.tsx +++ b/src/components/Select/Select.test.tsx @@ -61,7 +61,11 @@ describe(' undefined} value={exampleOptions[0]}> + ', () => { it('does not throw an error if select uses ', () => { const dropdownWithDropdownLabel = ( - undefined} + value={exampleOptions[0]} + > Options: Select diff --git a/src/components/Select/Select.tsx b/src/components/Select/Select.tsx index c45238703..91bfa1c9f 100644 --- a/src/components/Select/Select.tsx +++ b/src/components/Select/Select.tsx @@ -26,6 +26,8 @@ type PropsWithRenderProp = { as?: ElementType; }; +let showNameWarning = true; + type SelectProps = ExtractProps & PopoverOptions & { /** @@ -41,11 +43,10 @@ type SelectProps = ExtractProps & */ className?: string; /** - * The style of the select. - * - * Compact renders select trigger button that is only as wide as the content. + * Name of the form element, which triggers the generation of a hidden form field. + * @see https://headlessui.com/react/listbox#using-with-html-forms */ - variant?: VariantType; + name?: string; /** * Align select's popover to the left (default) or right of the trigger button's bottom edge. * @deprecated @@ -58,6 +59,12 @@ type SelectProps = ExtractProps & * include the width property to define the options menu width. */ optionsClassName?: string; + /** + * The style of the select. + * + * Compact renders select trigger button that is only as wide as the content. + */ + variant?: VariantType; }; function childrenHaveLabelComponent(children?: ReactNode): boolean { @@ -95,21 +102,30 @@ const SelectContext = React.createContext({}); */ export function Select(props: SelectProps) { const { - className, - children, 'aria-label': ariaLabel, - // Defaulting to null is required to explicitly state that this component is controlled, and prevents warning from Headless - value = null, - variant, + children, + className, + modifiers = defaultPopoverModifiers, + name, + onFirstUpdate, optionsAlign, optionsClassName, placement = 'bottom-start', - modifiers = defaultPopoverModifiers, strategy, - onFirstUpdate, + // Defaulting to null is required to explicitly state that this component is controlled, and prevents warning from Headless + value = null, + variant, ...other } = props; + if (process.env.NODE_ENV !== 'production' && !name && showNameWarning) { + console.warn( + "%c`Select` won't render a form field unless you include a `name` prop.\n\n See https://headlessui.com/react/listbox#using-with-html-forms for more information", + 'font-weight: bold', + ); + showNameWarning = false; + } + // Translate old optionsAlign to placement values // TODO: when removing optionsAlign, also remove this const optionsPlacement: SelectProps['placement'] = optionsAlign @@ -117,6 +133,7 @@ export function Select(props: SelectProps) { optionsAlign ] as SelectProps['placement']) : placement; + const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); const { styles: popperStyles, attributes: popperAttributes } = usePopper(