diff --git a/packages/@react-aria/autocomplete/src/useAutocomplete.ts b/packages/@react-aria/autocomplete/src/useAutocomplete.ts index 3126f1ce9b6..24abb0f0fbb 100644 --- a/packages/@react-aria/autocomplete/src/useAutocomplete.ts +++ b/packages/@react-aria/autocomplete/src/useAutocomplete.ts @@ -32,7 +32,13 @@ export interface AriaAutocompleteProps extends AutocompleteProps { * An optional filter function used to determine if a option should be included in the autocomplete list. * Include this if the items you are providing to your wrapped collection aren't filtered by default. */ - filter?: (textValue: string, inputValue: string) => boolean + filter?: (textValue: string, inputValue: string) => boolean, + + /** + * Whether or not to focus the first item in the collection after a filter is performed. + * @default false + */ + disableAutoFocusFirst?: boolean } export interface AriaAutocompleteOptions extends Omit { @@ -63,7 +69,8 @@ export function useAutocomplete(props: AriaAutocompleteOptions, state: Autocompl let { inputRef, collectionRef, - filter + filter, + disableAutoFocusFirst = false } = props; let collectionId = useId(); @@ -160,7 +167,7 @@ export function useAutocomplete(props: AriaAutocompleteOptions, state: Autocompl let onChange = (value: string) => { // Tell wrapped collection to focus the first element in the list when typing forward and to clear focused key when deleting text // for screen reader announcements - if (state.inputValue !== value && state.inputValue.length <= value.length) { + if (state.inputValue !== value && state.inputValue.length <= value.length && !disableAutoFocusFirst) { focusFirstItem(); } else { // Fully clear focused key when backspacing since the list may change and thus we'd want to start fresh again diff --git a/packages/react-aria-components/src/Autocomplete.tsx b/packages/react-aria-components/src/Autocomplete.tsx index f8096393c80..49478f06a33 100644 --- a/packages/react-aria-components/src/Autocomplete.tsx +++ b/packages/react-aria-components/src/Autocomplete.tsx @@ -39,7 +39,7 @@ export const UNSTABLE_InternalAutocompleteContext = createContext(null); let collectionRef = useRef(null); @@ -51,6 +51,7 @@ export function Autocomplete(props: AutocompleteProps) { } = useAutocomplete({ ...removeDataAttributes(props), filter, + disableAutoFocusFirst, inputRef, collectionRef }, state); diff --git a/packages/react-aria-components/test/Autocomplete.test.tsx b/packages/react-aria-components/test/Autocomplete.test.tsx index a7b49d89e72..79a7aa5cebe 100644 --- a/packages/react-aria-components/test/Autocomplete.test.tsx +++ b/packages/react-aria-components/test/Autocomplete.test.tsx @@ -610,6 +610,27 @@ describe('Autocomplete', () => { expect(document.activeElement).toBe(secondButton); }); + + it('should not auto focus first item when disableAutoFocusFirst is true', async () => { + let {getByRole} = render( + + + + ); + + let input = getByRole('searchbox'); + await user.tab(); + expect(document.activeElement).toBe(input); + + await user.keyboard('Foo'); + + expect(input).not.toHaveAttribute('aria-activedescendant'); + + await user.keyboard('{ArrowDown}'); + let menu = getByRole('menu'); + let options = within(menu).getAllByRole('menuitem'); + expect(input).toHaveAttribute('aria-activedescendant', options[0].id); + }); }); AriaAutocompleteTests({