diff --git a/packages/@react-aria/select/src/HiddenSelect.tsx b/packages/@react-aria/select/src/HiddenSelect.tsx index b0d61ddc2aa..68548612db7 100644 --- a/packages/@react-aria/select/src/HiddenSelect.tsx +++ b/packages/@react-aria/select/src/HiddenSelect.tsx @@ -11,7 +11,7 @@ */ import {FocusableElement, RefObject} from '@react-types/shared'; -import React, {JSX, ReactNode, useRef} from 'react'; +import React, {InputHTMLAttributes, JSX, ReactNode, useRef} from 'react'; import {selectData} from './useSelect'; import {SelectState} from '@react-stately/select'; import {useFormReset} from '@react-aria/utils'; @@ -141,13 +141,38 @@ export function HiddenSelect(props: HiddenSelectProps): JSX.Element | null ); } else if (name) { + let data = selectData.get(state) || {}; + let {validationBehavior} = data; + + let inputProps: InputHTMLAttributes = { + type: 'hidden', + autoComplete: selectProps.autoComplete, + name, + disabled: isDisabled, + value: state.selectedKey ?? '' + }; + + if (validationBehavior === 'native') { + // Use a visually hidden rather than + // so that an empty value blocks HTML form submission when the field is required. + return ( + {/** Ignore react warning. */}} + onInvalid={(e) => { + // Prevent native browser error popup from appearing. + e.preventDefault(); + triggerRef.current?.focus(); + }} /> + ); + } + return ( - + ); } diff --git a/packages/react-aria-components/stories/Select.stories.tsx b/packages/react-aria-components/stories/Select.stories.tsx index 51f9473833a..d2f19fa601d 100644 --- a/packages/react-aria-components/stories/Select.stories.tsx +++ b/packages/react-aria-components/stories/Select.stories.tsx @@ -18,7 +18,13 @@ import {UNSTABLE_ListBoxLoadingSentinel} from '../src/ListBox'; import {useAsyncList} from 'react-stately'; export default { - title: 'React Aria Components' + title: 'React Aria Components', + argTypes: { + validationBehavior: { + control: 'select', + options: ['native', 'aria'] + } + } }; export const SelectExample = () => ( @@ -64,7 +70,11 @@ export const SelectRenderProps = () => ( ); -let manyItems = [...Array(100)].map((_, i) => ({id: i, name: `Item ${i}`})); +let makeItems = (length: number) => Array.from({length}, (_, i) => ({ + id: i, + name: `Item ${i}` +})); +let manyItems = makeItems(100); export const SelectManyItems = () => ( + + + + + {item => {item.name}} + + + + + +);