Repository navigation
refactor: use ariaNotify over @primer/live-region-element #8641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Draft
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
a6bb6c4
refactor: use ariaNotify for Announce
joshblack 2f026ce
test: update AriaAlert and AriaStatus tests
joshblack 17492fb
refactor: replace live-region-element usage with useAnnounce
joshblack 32955d8
refactor: use ariaNotify for SelectPanel, FilteredActionList
joshblack b74f810
test: update tests to use ariaNotify over live-region-element
joshblack f917df8
chore(deps): remove @primer/live-region-element
joshblack 5a1c236
chore: re-order sideEffects array
joshblack f217809
chore: auto-fix lint and formatting issues
joshblack f6a5d82
docs(skills): add live-regions skill
joshblack 0735ebf
refactor: change polyfill location
joshblack 67d3196
Merge branch 'refactor/use-aria-notify' of github.com:primer/react in…
joshblack 55a7fa7
refactor(storybook): update live-region-element usage in stories
joshblack a288309
Refactor announcement logic in useAnnounce and Announce
joshblack File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| --- | ||
| name: live-regions | ||
| description: 'Use when adding, modifying, reviewing, or testing screen reader announcements, including ariaNotify, AriaStatus, AriaAlert, and useAnnounce. Do not use for static accessible names or descriptions, or changes unrelated to dynamic announcements.' | ||
| --- | ||
|
|
||
| # Live Regions | ||
|
|
||
| ## Choose the announcement API | ||
|
|
||
| - Default to `ariaNotify` for imperative announcements in response to user actions. | ||
| - If code would otherwise use `role="status"`, use the internal `AriaStatus` | ||
| component instead. | ||
| - If code would otherwise use `role="alert"`, use the internal `AriaAlert` | ||
| component instead. | ||
| - Use `useAnnounce` to debounce calls to `ariaNotify`. Do not implement another | ||
| announcement timer or queue at the call site. | ||
| - Do not use `@primer/live-region-element` or query `<live-region>` elements. | ||
|
|
||
| The implementations live in `packages/react/src/live-region/`. Within Primer | ||
| React, import the components and hook from the relative `live-region` module. | ||
|
|
||
| ## Announce in response to an action, not rendering | ||
|
|
||
| By default, announcements must not happen on initial page render. Call | ||
| `ariaNotify` from an event handler or in response to a user action, including | ||
| completion of asynchronous work initiated by that action. | ||
|
|
||
| Do not announce merely because a component renders, an effect runs, or props | ||
| change. An effect observing the completion of a user-initiated operation can be | ||
| appropriate, but mounting alone is not a reason to announce. | ||
|
|
||
| If a component appears in response to an action, its appearance can justify an | ||
| announcement. Either make the imperative announcement as part of that response | ||
| or use `announceOnShow` on `AriaStatus` or `AriaAlert`. Do not announce the same | ||
| message through both paths. | ||
|
|
||
| For content present on initial page render, explicitly use | ||
| `announceOnShow={false}`. `AriaStatus` currently defaults to `false`, but | ||
| `AriaAlert` currently defaults to `true`; do not rely on that default when the | ||
| alert is present on initial render. | ||
|
|
||
| ## Import the polyfill | ||
|
|
||
| Code calling `ariaNotify`, directly or through `useAnnounce`, must import | ||
| `packages/react/src/polyfills/ariaNotify`. For a typical sibling component: | ||
|
|
||
| ```tsx | ||
| import '../polyfills/ariaNotify' | ||
| ``` | ||
|
|
||
| Adjust the relative path for the file. The shared module imports | ||
| `@github/arianotify-polyfill` and provides the TypeScript declarations. Do not | ||
| redeclare the API or assume native browser support. | ||
|
|
||
| Use `document.ariaNotify(message, {priority: 'normal'})` for an ordinary status | ||
| announcement. Use `priority: 'high'` only for an urgent announcement that | ||
| corresponds to assertive behavior. `ariaNotify` takes a string, not a React node. | ||
|
|
||
| When an announcement belongs to a particular element or modal, use the relevant | ||
| element's `ariaNotify` so the announcement has the appropriate scope. | ||
|
|
||
| ## Debounce with `useAnnounce` | ||
|
|
||
| For results of a user-initiated search: | ||
|
|
||
| ```tsx | ||
| import '../polyfills/ariaNotify' | ||
| import {useAnnounce} from '../live-region' | ||
|
|
||
| function SearchButton({ | ||
| onSearch, | ||
| }: { | ||
| onSearch: () => Promise<number> | ||
| }) { | ||
| const {announce} = useAnnounce() | ||
|
|
||
| return ( | ||
| <button | ||
| type="button" | ||
| onClick={async () => { | ||
| const resultCount = await onSearch() | ||
| announce(`${resultCount} results available`, { | ||
| delayMs: 250, | ||
| }) | ||
| }} | ||
| > | ||
| Search | ||
| </button> | ||
| ) | ||
| } | ||
| ``` | ||
|
|
||
| Each hook instance owns its pending announcement. A subsequent call replaces | ||
| that instance's pending message, and unmounting cancels it. High-priority | ||
| announcements are immediate rather than debounced. | ||
|
|
||
| The hook also returns `cancel()`. Call it when the pending message becomes stale, | ||
| such as when loading finishes, matching results return, or a panel closes. | ||
| Cancellation only affects work not yet handed to the browser; it cannot retract | ||
| an announcement already sent through `ariaNotify`. | ||
|
|
||
| Keep ownership local. Each component cancels its own stale announcements using | ||
| the state it already receives. Do not recreate a global queue or share a | ||
| controller through context just to reproduce the old live-region clearing | ||
| behavior. | ||
|
|
||
| ## Test announcements at the API boundary | ||
|
|
||
| Follow the [component-testing skill](../component-testing/SKILL.md). | ||
|
|
||
| - Spy on `Element.prototype.ariaNotify` for `Announce`, `AriaStatus`, and | ||
| `AriaAlert`; spy on `document.ariaNotify` for `useAnnounce`. | ||
| - Give the spy a no-op implementation and restore it after each test. A spy | ||
| without an implementation still calls the browser API or polyfill. | ||
| - Assert the message and priority, not live-region DOM or polyfill internals. | ||
| There is no `getMessage` equivalent to inspect. | ||
| - Use polling for eventual calls with real timers. For debounce behavior, use | ||
| fake timers advanced inside RTL's `act` and assert before and at the deadline. | ||
| - Flush pending work before negative assertions so they do not pass merely | ||
| because the announcement has not fired yet. | ||
| - Cover relevant cancellation and StrictMode behavior without expecting duplicate | ||
| mount announcements. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@primer/react': patch | ||
| --- | ||
|
|
||
| SelectPanel: Prevent stale loading and list announcements after results arrive or the panel closes | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot add a separate changeset for the
minorbump that you're describing