-
Notifications
You must be signed in to change notification settings - Fork 662
fix: debounce list update announcements to prevent race conditions during rapid filtering #7745
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
base: main
Are you sure you want to change the base?
Changes from 4 commits
737cd3b
b6d17cb
81610c9
ae34e23
5fa2176
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@primer/react': patch | ||
| --- | ||
|
|
||
| Debounce list update announcements in `useAnnouncements` to prevent race conditions during rapid filtering. This ensures screen readers don't overwhelm users with intermediate states during fast typing. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -105,40 +105,44 @@ export const useAnnouncements = ( | |
| function announceListUpdates() { | ||
| if (isFirstRender) return // ignore on first render as announceInitialFocus will also announce | ||
|
|
||
| liveRegion?.clear() // clear previous announcements | ||
| const timeoutId = window.setTimeout(() => { | ||
| liveRegion?.clear() // clear previous announcements | ||
|
|
||
| if (items.length === 0 && !loading) { | ||
| announce(`${message?.title}. ${message?.description}`, {delayMs}) | ||
| return | ||
| } | ||
| if (items.length === 0 && !loading) { | ||
| announce(`${message?.title}. ${message?.description}`, {delayMs}) | ||
| return | ||
| } | ||
|
|
||
| if (usingRovingTabindex) { | ||
| const announcementText = `${items.length} item${items.length > 1 ? 's' : ''} available, ${selectedItems} selected.` | ||
|
|
||
| announce(announcementText, { | ||
| delayMs, | ||
| from: liveRegion ? liveRegion : undefined, | ||
| }) | ||
| } else { | ||
| // give @primer/behaviors a moment to update active-descendant | ||
| window.requestAnimationFrame(() => { | ||
| const activeItem = getItemWithActiveDescendant(listContainerRef, items) | ||
| if (!activeItem) return | ||
| const {index, text, selected} = activeItem | ||
|
|
||
| const announcementText = [ | ||
| `List updated`, | ||
| `Focused item: ${text}`, | ||
| `${selected ? 'selected' : 'not selected'}`, | ||
| `${index + 1} of ${items.length}`, | ||
| ].join(', ') | ||
| if (usingRovingTabindex) { | ||
| const announcementText = `${items.length} item${items.length > 1 ? 's' : ''} available, ${selectedItems} selected.` | ||
|
|
||
| announce(announcementText, { | ||
| delayMs, | ||
| from: liveRegion ? liveRegion : undefined, // announce will create a liveRegion if it doesn't find one | ||
| from: liveRegion ? liveRegion : undefined, | ||
| }) | ||
| }) | ||
| } | ||
| } else { | ||
| // give @primer/behaviors a moment to update active-descendant | ||
| window.requestAnimationFrame(() => { | ||
| const activeItem = getItemWithActiveDescendant(listContainerRef, items) | ||
|
Comment on lines
+125
to
+127
|
||
| if (!activeItem) return | ||
| const {index, text, selected} = activeItem | ||
|
|
||
| const announcementText = [ | ||
| `List updated`, | ||
| `Focused item: ${text}`, | ||
| `${selected ? 'selected' : 'not selected'}`, | ||
| `${index + 1} of ${items.length}`, | ||
| ].join(', ') | ||
|
|
||
| announce(announcementText, { | ||
| delayMs, | ||
| from: liveRegion ? liveRegion : undefined, // announce will create a liveRegion if it doesn't find one | ||
| }) | ||
| }) | ||
| } | ||
| }, delayMs) | ||
|
|
||
| return () => window.clearTimeout(timeoutId) | ||
| }, | ||
| [ | ||
| announce, | ||
|
|
||
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.
delayMsis already passed toannounce(...)to intentionally delay announcements (see file-level comment). Using the samedelayMsas thesetTimeoutdebounce means list updates are delayed twice (timeout + announce delay), likely ~1000ms instead of the previous 500ms. Consider separating debounce timing from the announcement delay (e.g.,debounceMsfor the outer timeout, and keepdelayMsonly forannounce, or vice-versa).This issue also appears in the following locations of the same file:
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.
I think this makes sense, perhaps we should remove the delay for this function in lieu of the added debounce
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.
Updated the changes in pr, please validate