Skip to content

Commit

Permalink
Fix ComboboxControl reset button when using the keyboard. (WordPress#…
Browse files Browse the repository at this point in the history
…63410)

* Fix combobox reset button when using the keyboard.

* Add changelog entry.

* Add unit tests.

* Adjust changelog entry.

* Improve unit tests.

Co-authored-by: afercia <[email protected]>
Co-authored-by: ciampo <[email protected]>
  • Loading branch information
3 people authored and carstingaxion committed Jul 18, 2024
1 parent c5c99c4 commit cc53a6a
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Bug Fixes

- `ComboboxControl`: Fix ComboboxControl reset button when using the keyboard. ([#63410](https://github.com/WordPress/gutenberg/pull/63410))
- `Button`: Never apply `aria-disabled` to anchor ([#63376](https://github.com/WordPress/gutenberg/pull/63376)).

### Internal
Expand Down
10 changes: 10 additions & 0 deletions packages/components/src/combobox-control/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,15 @@ function ComboboxControl( props: ComboboxControlProps ) {
inputContainer.current?.focus();
};

// Stop propagation of the keydown event when pressing Enter on the Reset
// button to prevent calling the onKeydown callback on the container div
// element which actually sets the selected suggestion.
const handleResetStopPropagation: React.KeyboardEventHandler<
HTMLButtonElement
> = ( event ) => {
event.stopPropagation();
};

// Update current selections when the filter input changes.
useEffect( () => {
const hasMatchingSuggestions = matchingSuggestions.length > 0;
Expand Down Expand Up @@ -350,6 +359,7 @@ function ComboboxControl( props: ComboboxControlProps ) {
// eslint-disable-next-line no-restricted-syntax
disabled={ ! value }
onClick={ handleOnReset }
onKeyDown={ handleResetStopPropagation }
label={ __( 'Reset' ) }
/>
</FlexItem>
Expand Down
128 changes: 127 additions & 1 deletion packages/components/src/combobox-control/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ describe.each( [
<Component options={ timezones } label={ defaultLabelText } />
);
const label = getLabel( defaultLabelText );
expect( label ).toBeInTheDocument();
expect( label ).toBeVisible();
} );

Expand Down Expand Up @@ -306,4 +305,131 @@ describe.each( [
expect( onChangeSpy ).toHaveBeenCalledWith( targetOption.value );
expect( input ).toHaveValue( targetOption.label );
} );

it( 'should render with Reset button disabled', () => {
render(
<Component
options={ timezones }
label={ defaultLabelText }
allowReset
/>
);

const resetButton = screen.getByRole( 'button', { name: 'Reset' } );

expect( resetButton ).toBeVisible();
expect( resetButton ).toBeDisabled();
} );

it( 'should reset input when clicking the Reset button', async () => {
const user = await userEvent.setup();
const targetOption = timezones[ 13 ];

render(
<Component
options={ timezones }
label={ defaultLabelText }
allowReset
/>
);

// Pressing tab selects the input and shows the options.
await user.tab();
// Type enough characters to ensure a predictable search result.
await user.keyboard( getOptionSearchString( targetOption ) );
// Pressing Enter/Return selects the currently focused option.
await user.keyboard( '{Enter}' );

const input = getInput( defaultLabelText );

expect( input ).toHaveValue( targetOption.label );

const resetButton = screen.getByRole( 'button', { name: 'Reset' } );

expect( resetButton ).toBeEnabled();

await user.click( resetButton );

expect( input ).toHaveValue( '' );
expect( resetButton ).toBeDisabled();
expect( input ).toHaveFocus();
} );

it( 'should reset input when pressing the Reset button with the Enter key', async () => {
const user = await userEvent.setup();
const targetOption = timezones[ 13 ];

render(
<Component
options={ timezones }
label={ defaultLabelText }
allowReset
/>
);

// Pressing tab selects the input and shows the options.
await user.tab();
// Type enough characters to ensure a predictable search result.
await user.keyboard( getOptionSearchString( targetOption ) );
// Pressing Enter/Return selects the currently focused option.
await user.keyboard( '{Enter}' );

const input = getInput( defaultLabelText );

expect( input ).toHaveValue( targetOption.label );

// Pressing tab moves focus to the Reset buttons
await user.tab();

const resetButton = screen.getByRole( 'button', { name: 'Reset' } );

// If the button has focus that implies it is enabled.
expect( resetButton ).toHaveFocus();

// Pressing Enter/Return resets the input.
await user.keyboard( '{Enter}' );

expect( input ).toHaveValue( '' );
expect( resetButton ).toBeDisabled();
expect( input ).toHaveFocus();
} );

it( 'should reset input when pressing the Reset button with the Spacebar key', async () => {
const user = await userEvent.setup();
const targetOption = timezones[ 13 ];

render(
<Component
options={ timezones }
label={ defaultLabelText }
allowReset
/>
);

// Pressing tab selects the input and shows the options.
await user.tab();
// Type enough characters to ensure a predictable search result.
await user.keyboard( getOptionSearchString( targetOption ) );
// Pressing Enter/Return selects the currently focused option.
await user.keyboard( '{Enter}' );

const input = getInput( defaultLabelText );

expect( input ).toHaveValue( targetOption.label );

// Pressing tab moves focus to the Reset buttons.
await user.tab();

const resetButton = screen.getByRole( 'button', { name: 'Reset' } );

// If the button has focus that implies it is enabled.
expect( resetButton ).toHaveFocus();

// Pressing Spacebar resets the input.
await user.keyboard( '[Space]' );

expect( input ).toHaveValue( '' );
expect( resetButton ).toBeDisabled();
expect( input ).toHaveFocus();
} );
} );

0 comments on commit cc53a6a

Please sign in to comment.