diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md
index cb3f378ff042bb..f7df0f0bb9078d 100644
--- a/packages/components/CHANGELOG.md
+++ b/packages/components/CHANGELOG.md
@@ -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
diff --git a/packages/components/src/combobox-control/index.js b/packages/components/src/combobox-control/index.js
index a12b58f8eaa516..eae04e09d8cf71 100644
--- a/packages/components/src/combobox-control/index.js
+++ b/packages/components/src/combobox-control/index.js
@@ -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 }
diff --git a/packages/components/src/form-token-field/test/index.tsx b/packages/components/src/form-token-field/test/index.tsx
index 2dc08d02fa4f88..c0926a9a630e5c 100644
--- a/packages/components/src/form-token-field/test/index.tsx
+++ b/packages/components/src/form-token-field/test/index.tsx
@@ -2057,7 +2057,12 @@ describe( 'FormTokenField', () => {
const suggestions = [ 'Pine', 'Pistachio', 'Sage' ];
- render( );
+ render(
+ <>
+
+
+ >
+ );
// No suggestions visible
const input = screen.getByRole( 'combobox' );
@@ -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]' );
diff --git a/packages/components/src/form-token-field/token-input.tsx b/packages/components/src/form-token-field/token-input.tsx
index 478cce3f3733cc..196ac03c799af8 100644
--- a/packages/components/src/form-token-field/token-input.tsx
+++ b/packages/components/src/form-token-field/token-input.tsx
@@ -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
@@ -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 > ) => {
@@ -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 (