From 13bdc09c0cc4a4a0f79fa5ab71bb4b56c7e0a5cc Mon Sep 17 00:00:00 2001 From: "Simon Fisher (from Dev Box)" Date: Fri, 1 Mar 2024 10:46:23 +0000 Subject: [PATCH 1/2] Fixes issue of keyboard users unable to tab to the dismiss button and invoke it. --- .../src/components/SearchBox/SearchBox.types.ts | 5 ++++- .../src/components/SearchBox/useSearchBox.tsx | 12 +++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-search-preview/src/components/SearchBox/SearchBox.types.ts b/packages/react-components/react-search-preview/src/components/SearchBox/SearchBox.types.ts index 11aa98d2fdf0a1..2c0b9e75a6e006 100644 --- a/packages/react-components/react-search-preview/src/components/SearchBox/SearchBox.types.ts +++ b/packages/react-components/react-search-preview/src/components/SearchBox/SearchBox.types.ts @@ -37,4 +37,7 @@ export type SearchBoxState = ComponentState & }; /** Overloaded onChange event type, used to merge functionality of regular text entry and the dismiss button */ -export type SearchBoxChangeEvent = React.ChangeEvent | React.MouseEvent; +export type SearchBoxChangeEvent = + | React.ChangeEvent + | React.MouseEvent + | React.KeyboardEvent; diff --git a/packages/react-components/react-search-preview/src/components/SearchBox/useSearchBox.tsx b/packages/react-components/react-search-preview/src/components/SearchBox/useSearchBox.tsx index f08568ec87b12a..4ed21eb095edf3 100644 --- a/packages/react-components/react-search-preview/src/components/SearchBox/useSearchBox.tsx +++ b/packages/react-components/react-search-preview/src/components/SearchBox/useSearchBox.tsx @@ -55,6 +55,15 @@ export const useSearchBox_unstable = (props: SearchBoxProps, ref: React.Ref) => { + if ((event.key === 'Enter' || event.key === 'Space') && !event.altKey && !event.shiftKey && !event.ctrlKey) { + const newValue = ''; + setValue(newValue); + props.onChange?.(event, { value: newValue }); + searchBoxRef.current?.focus(); + } + }); + const inputState = useInput_unstable( { type: 'search', @@ -104,7 +113,7 @@ export const useSearchBox_unstable = (props: SearchBoxProps, ref: React.Ref, role: 'button', 'aria-label': 'clear', - tabIndex: -1, + tabIndex: 0, }, renderByDefault: true, elementType: 'span', @@ -116,6 +125,7 @@ export const useSearchBox_unstable = (props: SearchBoxProps, ref: React.Ref Date: Fri, 1 Mar 2024 11:47:36 +0000 Subject: [PATCH 2/2] Add unit tests and fix snapshot --- .../components/SearchBox/SearchBox.test.tsx | 28 +++++++++++++++++++ .../__snapshots__/SearchBox.test.tsx.snap | 2 +- .../src/components/SearchBox/useSearchBox.tsx | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-search-preview/src/components/SearchBox/SearchBox.test.tsx b/packages/react-components/react-search-preview/src/components/SearchBox/SearchBox.test.tsx index 4965620ef30b2e..5e2cb0198178b2 100644 --- a/packages/react-components/react-search-preview/src/components/SearchBox/SearchBox.test.tsx +++ b/packages/react-components/react-search-preview/src/components/SearchBox/SearchBox.test.tsx @@ -130,4 +130,32 @@ describe('SearchBox', () => { expect(onChange).toHaveBeenCalledTimes(1); expect(onChange).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({ value: '' })); }); + + it('can tab into the dismiss button', () => { + const onChange = jest.fn(); + renderedComponent = render(); + + // First click into the text box + userEvent.click(renderedComponent.getByRole('searchbox')); + userEvent.tab(); + + const focusedElement = document.activeElement; + expect(focusedElement).toBeDefined(); + expect(focusedElement?.tagName.toLowerCase()).toEqual('span'); + expect(focusedElement?.getAttribute('aria-label')).toEqual('clear'); + }); + + it('clears the searchbox when pressing {enter} on the dismiss button', () => { + const onChange = jest.fn(); + renderedComponent = render(); + + const input = renderedComponent.getByRole('searchbox'); + const dismissButton = renderedComponent.getByLabelText('clear'); + const previousValue = (input as HTMLInputElement).value; + userEvent.type(dismissButton, '{enter}'); + + expect(onChange).toHaveBeenLastCalledWith(expect.objectContaining({ key: 'Enter' }), { value: '' }); + expect(previousValue).toEqual('hello'); + expect((input as HTMLInputElement).value).toEqual(''); + }); }); diff --git a/packages/react-components/react-search-preview/src/components/SearchBox/__snapshots__/SearchBox.test.tsx.snap b/packages/react-components/react-search-preview/src/components/SearchBox/__snapshots__/SearchBox.test.tsx.snap index c524fe5a1393c6..71ba35069d6e8b 100644 --- a/packages/react-components/react-search-preview/src/components/SearchBox/__snapshots__/SearchBox.test.tsx.snap +++ b/packages/react-components/react-search-preview/src/components/SearchBox/__snapshots__/SearchBox.test.tsx.snap @@ -35,7 +35,7 @@ exports[`SearchBox renders a default state 1`] = ` aria-label="clear" class="fui-SearchBox__dismiss" role="button" - tabindex="-1" + tabindex="0" >