From 613cf0cc81ee5d65a3045d04f197e2a1921a8745 Mon Sep 17 00:00:00 2001 From: Kendall Gassner Date: Thu, 6 Feb 2025 13:28:19 -0800 Subject: [PATCH] [Accessibility][Autocomplete] Announce "no selectable options" (#5662) * Announce no selectable options * changeset * change Announce strategy do debounce * add missing useEffect dep --- .changeset/chilled-rules-yell.md | 5 +++++ package-lock.json | 8 ++++++++ package.json | 1 + .../react/src/Autocomplete/AutocompleteMenu.tsx | 16 ++++++++++++++++ 4 files changed, 30 insertions(+) create mode 100644 .changeset/chilled-rules-yell.md diff --git a/.changeset/chilled-rules-yell.md b/.changeset/chilled-rules-yell.md new file mode 100644 index 00000000000..f4752fc2976 --- /dev/null +++ b/.changeset/chilled-rules-yell.md @@ -0,0 +1,5 @@ +--- +"@primer/react": patch +--- + +Autocomplete: Use aria-live to announce "no selectable options". diff --git a/package-lock.json b/package-lock.json index 593f075c140..7bac08e44dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@changesets/cli": "2.27.1", "@github/axe-github": "0.6.1", "@github/markdownlint-github": "^0.6.0", + "@github/mini-throttle": "2.1.1", "@github/prettier-config": "0.0.6", "@mdx-js/react": "1.6.22", "@playwright/test": "^1.50.0", @@ -4655,6 +4656,13 @@ "lodash": "^4.17.15" } }, + "node_modules/@github/mini-throttle": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@github/mini-throttle/-/mini-throttle-2.1.1.tgz", + "integrity": "sha512-KtOPaB+FiKJ6jcKm9UKyaM5fPURHGf+xcp+b4Mzoi81hOc6M1sIGpMZMAVbNzfa2lW5+RPGKq888Px0j76OZ/A==", + "dev": true, + "license": "MIT" + }, "node_modules/@github/prettier-config": { "version": "0.0.6", "dev": true, diff --git a/package.json b/package.json index c28876e714e..c09916ba8cf 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@changesets/changelog-github": "0.5.0", "@changesets/cli": "2.27.1", "@github/axe-github": "0.6.1", + "@github/mini-throttle": "2.1.1", "@github/markdownlint-github": "^0.6.0", "@github/prettier-config": "0.0.6", "@mdx-js/react": "1.6.22", diff --git a/packages/react/src/Autocomplete/AutocompleteMenu.tsx b/packages/react/src/Autocomplete/AutocompleteMenu.tsx index 3aa77cf5301..0e4551a713d 100644 --- a/packages/react/src/Autocomplete/AutocompleteMenu.tsx +++ b/packages/react/src/Autocomplete/AutocompleteMenu.tsx @@ -1,4 +1,6 @@ import React, {useContext, useEffect, useMemo, useRef, useState} from 'react' +import {debounce} from '@github/mini-throttle' +import {announce} from '@primer/live-region-element' import {scrollIntoView} from '@primer/behaviors' import type {ScrollIntoViewOptions} from '@primer/behaviors' import type {ActionListItemProps} from '../ActionList' @@ -122,6 +124,14 @@ export type AutocompleteMenuInternalProps = { const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_staff' +/** + * Announces a message to screen readers at a slowed-down rate. This is useful when you want to announce don't want to + * overwhelm the user with too many announcements in rapid succession. + */ +const debounceAnnouncement = debounce((announcement: string) => { + announce(announcement) +}, 250) + function AutocompleteMenu(props: AutocompleteMenuInternalProps) { const autocompleteContext = useContext(AutocompleteContext) if (autocompleteContext === null) { @@ -267,6 +277,12 @@ function AutocompleteMenu(props: AutocompleteMe allItemsToRenderRef.current = allItemsToRender }) + React.useEffect(() => { + if (allItemsToRender.length === 0) { + debounceAnnouncement(emptyStateText as string) + } + }, [allItemsToRender, emptyStateText]) + useFocusZone( { containerRef: listContainerRef,