Skip to content

Commit

Permalink
feat: control focus on search behavior (#7807)
Browse files Browse the repository at this point in the history
* feat: control focus on search behavior

* rename prop to disableAutoFocusFirst

* add unit test

* lint

* Update packages/@react-aria/autocomplete/src/useAutocomplete.ts

Co-authored-by: Devon Govett <[email protected]>

---------

Co-authored-by: Roman Sandler <[email protected]>
Co-authored-by: Reid Barber <[email protected]>
Co-authored-by: Devon Govett <[email protected]>
  • Loading branch information
4 people authored Feb 25, 2025
1 parent c8fdba5 commit ad70785
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 4 deletions.
13 changes: 10 additions & 3 deletions packages/@react-aria/autocomplete/src/useAutocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<AriaAutocompleteProps, 'children'> {
Expand Down Expand Up @@ -63,7 +69,8 @@ export function useAutocomplete(props: AriaAutocompleteOptions, state: Autocompl
let {
inputRef,
collectionRef,
filter
filter,
disableAutoFocusFirst = false
} = props;

let collectionId = useId();
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion packages/react-aria-components/src/Autocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const UNSTABLE_InternalAutocompleteContext = createContext<InternalAutoco
export function Autocomplete(props: AutocompleteProps) {
let ctx = useSlottedContext(AutocompleteContext, props.slot);
props = mergeProps(ctx, props);
let {filter} = props;
let {filter, disableAutoFocusFirst} = props;
let state = useAutocompleteState(props);
let inputRef = useRef<HTMLInputElement | null>(null);
let collectionRef = useRef<HTMLElement>(null);
Expand All @@ -51,6 +51,7 @@ export function Autocomplete(props: AutocompleteProps) {
} = useAutocomplete({
...removeDataAttributes(props),
filter,
disableAutoFocusFirst,
inputRef,
collectionRef
}, state);
Expand Down
21 changes: 21 additions & 0 deletions packages/react-aria-components/test/Autocomplete.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<AutocompleteWrapper autocompleteProps={{disableAutoFocusFirst: true}}>
<StaticMenu />
</AutocompleteWrapper>
);

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({
Expand Down

1 comment on commit ad70785

@rspbot
Copy link

@rspbot rspbot commented on ad70785 Feb 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.