diff --git a/src/Form/FormAutosuggest.jsx b/src/Form/FormAutosuggest.jsx
index 5fb689b2e8..499f8444ab 100644
--- a/src/Form/FormAutosuggest.jsx
+++ b/src/Form/FormAutosuggest.jsx
@@ -2,6 +2,7 @@ import React, {
useEffect, useState,
} from 'react';
import PropTypes from 'prop-types';
+import { v4 as uuidv4 } from 'uuid';
import { useIntl } from 'react-intl';
import { KeyboardArrowUp, KeyboardArrowDown } from '../../icons';
import Icon from '../Icon';
@@ -38,6 +39,11 @@ function FormAutosuggest({
errorMessage: '',
dropDownItems: [],
});
+ const [activeMenuItemId, setActiveMenuItemId] = useState(null);
+
+ const handleMenuItemFocus = (menuItemId) => {
+ setActiveMenuItemId(menuItemId);
+ };
const handleItemClick = (e, onClick) => {
const clickedValue = e.currentTarget.getAttribute('data-value');
@@ -63,12 +69,15 @@ function FormAutosuggest({
let childrenOpt = React.Children.map(children, (child) => {
// eslint-disable-next-line no-shadow
const { children, onClick, ...rest } = child.props;
+ const menuItemId = uuidv4();
return React.cloneElement(child, {
...rest,
children,
'data-value': children,
onClick: (e) => handleItemClick(e, onClick),
+ id: menuItemId,
+ onFocus: () => handleMenuItemFocus(menuItemId),
});
});
@@ -219,6 +228,9 @@ function FormAutosuggest({
return (
+
+ {`${state.dropDownItems.length} options found`}
+
0).toString()}
@@ -228,6 +240,7 @@ function FormAutosuggest({
autoComplete="off"
value={state.displayValue}
aria-invalid={state.errorMessage}
+ aria-activedescendant={activeMenuItemId}
onChange={handleOnChange}
onClick={handleClick}
trailingElement={iconToggle}
diff --git a/src/Form/tests/FormAutosuggest.test.jsx b/src/Form/tests/FormAutosuggest.test.jsx
index 8eaafb0e8d..27548704a3 100644
--- a/src/Form/tests/FormAutosuggest.test.jsx
+++ b/src/Form/tests/FormAutosuggest.test.jsx
@@ -85,6 +85,33 @@ describe('render behavior', () => {
expect(formControlFeedback).toBeInTheDocument();
});
+
+ it('renders component with options that all have IDs', () => {
+ const { getByTestId, getAllByTestId } = render();
+ const input = getByTestId('autosuggest-textbox-input');
+
+ userEvent.click(input);
+ const optionItemIds = getAllByTestId('autosuggest-optionitem').map(item => item.id);
+
+ expect(optionItemIds).not.toContain(null);
+ expect(optionItemIds).not.toContain(undefined);
+ });
+
+ it('confirms that the value of the aria-live attribute on the wrapper component is assertive', () => {
+ const { getByTestId } = render();
+
+ expect(getByTestId('autosuggest-screen-reader-options-count').getAttribute('aria-live')).toEqual('assertive');
+ });
+
+ it('displays correct amount of options found to screen readers', () => {
+ const { getByText, getByTestId } = render();
+ const input = getByTestId('autosuggest-textbox-input');
+
+ expect(getByText('0 options found')).toBeInTheDocument();
+ userEvent.click(input);
+
+ expect(getByText('3 options found')).toBeInTheDocument();
+ });
});
describe('controlled behavior', () => {
@@ -136,6 +163,17 @@ describe('controlled behavior', () => {
expect(onClick).toHaveBeenCalledTimes(0);
});
+ it('should set the correct activedescendant', () => {
+ const { getByTestId, getAllByTestId } = render();
+ const input = getByTestId('autosuggest-textbox-input');
+
+ userEvent.click(input);
+ const expectedOptionId = getAllByTestId('autosuggest-optionitem')[0].id;
+ userEvent.keyboard('{arrowdown}');
+
+ expect(input.getAttribute('aria-activedescendant')).toEqual(expectedOptionId);
+ });
+
it('filters dropdown based on typed field value with one match', () => {
const { getByTestId, queryAllByTestId } = render();
const input = getByTestId('autosuggest-textbox-input');
@@ -187,4 +225,19 @@ describe('controlled behavior', () => {
const updatedList = queryAllByTestId('autosuggest-optionitem');
expect(updatedList.length).toBe(0);
});
+
+ it('updates screen reader option count based on typed field value with multiple matches', () => {
+ const { getByText, getByTestId } = render();
+ const input = getByTestId('autosuggest-textbox-input');
+
+ expect(getByText('0 options found')).toBeInTheDocument();
+ userEvent.click(input);
+
+ expect(getByText('3 options found')).toBeInTheDocument();
+
+ userEvent.click(input);
+ userEvent.type(input, '1');
+
+ expect(getByText('2 options found')).toBeInTheDocument();
+ });
});