Skip to content

Commit

Permalink
TokenInput field: try alternative approach to fix screen reader focus…
Browse files Browse the repository at this point in the history
… issue (#44526)

* Test commit, revert later.

* Remove the aria-label from the ComboboxControl and let value take over.

* Try suggested changes to TokenInput

* CHANGELOG

* Improve unit tests

* Restore min users for combobox in post author field

Co-authored-by: Alex Stine <[email protected]>
  • Loading branch information
ciampo and alexstine authored Sep 29, 2022
1 parent a6e9a24 commit 1f4e508
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 9 deletions.
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- `Popover`: fix limitShift logic by adding iframe offset correctly [#42950](https://github.com/WordPress/gutenberg/pull/42950)).
- `Popover`: refine position-to-placement conversion logic, add tests ([#44377](https://github.com/WordPress/gutenberg/pull/44377)).
- `TokenInput`: improve logic around the `aria-activedescendant` attribute, which was causing unintended focus behavior for some screen readers ([#44526](https://github.com/WordPress/gutenberg/pull/44526)).

### Internal

Expand Down
5 changes: 0 additions & 5 deletions packages/components/src/combobox-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,6 @@ function ComboboxControl( {
instanceId={ instanceId }
ref={ inputContainer }
value={ isExpanded ? inputValue : currentLabel }
aria-label={
currentLabel
? `${ currentLabel }, ${ label }`
: null
}
onFocus={ onFocus }
onBlur={ onBlur }
isExpanded={ isExpanded }
Expand Down
23 changes: 22 additions & 1 deletion packages/components/src/form-token-field/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2057,7 +2057,12 @@ describe( 'FormTokenField', () => {

const suggestions = [ 'Pine', 'Pistachio', 'Sage' ];

render( <FormTokenFieldWithState suggestions={ suggestions } /> );
render(
<>
<FormTokenFieldWithState suggestions={ suggestions } />
<button>Click me</button>
</>
);

// No suggestions visible
const input = screen.getByRole( 'combobox' );
Expand Down Expand Up @@ -2093,6 +2098,22 @@ describe( 'FormTokenField', () => {
pineSuggestion.id
);

// Blur the input and make sure that the `aria-activedescendant`
// is removed
const button = screen.getByRole( 'button', { name: 'Click me' } );

await user.click( button );

expect( input ).not.toHaveAttribute( 'aria-activedescendant' );

// Focus the input again, `aria-activedescendant` should be added back.
await user.click( input );

expect( input ).toHaveAttribute(
'aria-activedescendant',
pineSuggestion.id
);

// Add the suggestion, which hides the list
await user.keyboard( '[Enter]' );

Expand Down
28 changes: 25 additions & 3 deletions packages/components/src/form-token-field/token-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
* External dependencies
*/
import classnames from 'classnames';
import type { ChangeEvent, ForwardedRef } from 'react';
import type { ChangeEvent, ForwardedRef, FocusEventHandler } from 'react';

/**
* WordPress dependencies
*/
import { forwardRef } from '@wordpress/element';
import { forwardRef, useState } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -26,9 +26,13 @@ export function UnForwardedTokenInput(
selectedSuggestionIndex,
className,
onChange,
onFocus,
onBlur,
...restProps
} = props;

const [ hasFocus, setHasFocus ] = useState( false );

const size = value ? value.length + 1 : 0;

const onChangeHandler = ( event: ChangeEvent< HTMLInputElement > ) => {
Expand All @@ -39,6 +43,18 @@ export function UnForwardedTokenInput(
}
};

const onFocusHandler: FocusEventHandler< HTMLInputElement > = ( e ) => {
setHasFocus( true );
onFocus?.( e );
};

const onBlurHandler: React.FocusEventHandler< HTMLInputElement > = (
e
) => {
setHasFocus( false );
onBlur?.( e );
};

return (
<input
ref={ ref }
Expand All @@ -47,6 +63,8 @@ export function UnForwardedTokenInput(
{ ...restProps }
value={ value || '' }
onChange={ onChangeHandler }
onFocus={ onFocusHandler }
onBlur={ onBlurHandler }
size={ size }
className={ classnames(
className,
Expand All @@ -62,7 +80,11 @@ export function UnForwardedTokenInput(
: undefined
}
aria-activedescendant={
selectedSuggestionIndex !== -1
// Only add the `aria-activedescendant` attribute when:
// - the user is actively interacting with the input (`hasFocus`)
// - there is a selected suggestion (`selectedSuggestionIndex !== -1`)
// - the list of suggestions are rendered in the DOM (`isExpanded`)
hasFocus && selectedSuggestionIndex !== -1 && isExpanded
? `components-form-token-suggestions-${ instanceId }-${ selectedSuggestionIndex }`
: undefined
}
Expand Down

0 comments on commit 1f4e508

Please sign in to comment.