fix: announce sending status to screen readers via live region#5781
fix: announce sending status to screen readers via live region#5781isherstneva wants to merge 4 commits intomicrosoft:mainfrom
Conversation
When a user submits a message, the activity enters the "sending" state while awaiting server acknowledgement. The visual "Sending" indicator was rendered next to the activity but not inside an ARIA live region, so screen readers never announced it. Adds LiveRegionSendSending component (mirroring LiveRegionSendFailed) that watches for activities newly entering the "sending" state and queues the localized "Sending message." string into the polite live region. Also adds TRANSCRIPT_LIVE_REGION_SEND_SENDING_ALT localization key and a corresponding integration test.
|
@isherstneva please read the following Contributor License Agreement(CLA). If you agree with the CLA, please reply with the following information.
Contributor License AgreementContribution License AgreementThis Contribution License Agreement (“Agreement”) is agreed to by the party signing below (“You”),
|
There was a problem hiding this comment.
Pull request overview
Adds ARIA live-region announcements for the “sending” state so screen readers narrate “Sending message.” when an outgoing activity is awaiting server acknowledgement.
Changes:
- Added
LiveRegionSendSendingcomponent and mounted it inLiveRegionTranscript. - Introduced new localization key
TRANSCRIPT_LIVE_REGION_SEND_SENDING_ALT. - Added an accessibility integration test covering the new live-region narration.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/component/src/Transcript/LiveRegionTranscript.tsx | Mounts the new sending live-region component alongside the existing send-failed narration. |
| packages/component/src/Transcript/LiveRegion/SendSending.tsx | Implements detection of activities newly entering the SENDING state and queues localized narration. |
| packages/api/src/localization/en-US.json | Adds the localized “Sending message.” string and comment for translators. |
| tests/html2/accessibility/liveRegion/activityStatus.sendSending.html | Adds an integration test asserting the live region narrates “Sending message.” for unacknowledged outgoing activities. |
| CHANGELOG.md | Documents the accessibility fix in the release notes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const prevActivityKeysOfSending = usePrevious(activityKeysOfSending); | ||
|
|
||
| /** True, if one or more non-presentational activities newly entered the "sending" state, otherwise false. */ | ||
| const hasNewSending = useMemo<boolean>(() => { | ||
| if (activityKeysOfSending === prevActivityKeysOfSending) { | ||
| return false; | ||
| } | ||
|
|
||
| for (const key of activityKeysOfSending.keys()) { | ||
| if (!prevActivityKeysOfSending.has(key)) { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| }, [activityKeysOfSending, prevActivityKeysOfSending]); | ||
|
|
||
| useLiveRegion(() => hasNewSending && liveRegionSendSendingAlt, [hasNewSending, liveRegionSendSendingAlt]); |
There was a problem hiding this comment.
prevActivityKeysOfSending.has(key) will throw if usePrevious returns undefined on the initial render (common for “previous value” hooks). Consider defaulting prevActivityKeysOfSending to an empty Set (or handling the undefined case inside the memo) so the first render cannot crash. Also, useLiveRegion(() => hasNewSending && liveRegionSendSendingAlt, ...) returns false when not sending; if useLiveRegion expects a string/undefined, prefer returning undefined/null rather than a boolean to avoid accidental narration/queuing of a non-string value.
| () => | ||
| Array.from(sendStatusByActivityKey).reduce( | ||
| (activityKeysOfSending, [key, sendStatus]) => | ||
| sendStatus === SENDING && !isPresentational(getActivityByKey(key)) | ||
| ? activityKeysOfSending.add(key) | ||
| : activityKeysOfSending, | ||
| new Set<string>() | ||
| ), |
There was a problem hiding this comment.
Array.from(sendStatusByActivityKey) allocates an intermediate array on every recomputation. Since sendStatusByActivityKey is already iterable, iterating it directly (e.g., a for...of loop) avoids the extra allocation and can reduce overhead for large transcripts.
| () => | |
| Array.from(sendStatusByActivityKey).reduce( | |
| (activityKeysOfSending, [key, sendStatus]) => | |
| sendStatus === SENDING && !isPresentational(getActivityByKey(key)) | |
| ? activityKeysOfSending.add(key) | |
| : activityKeysOfSending, | |
| new Set<string>() | |
| ), | |
| () => { | |
| const activityKeysOfSending = new Set<string>(); | |
| for (const [key, sendStatus] of sendStatusByActivityKey) { | |
| if (sendStatus === SENDING && !isPresentational(getActivityByKey(key))) { | |
| activityKeysOfSending.add(key); | |
| } | |
| } | |
| return activityKeysOfSending; | |
| }, |
| "_TRANSCRIPT_LIVE_REGION_SUGGESTED_ACTIONS_WITH_ACCESS_KEY_LABEL_ALT.comment": "$1 will be \"ACCESS_KEY_ALT\".", | ||
| "TRANSCRIPT_LIVE_REGION_SEND_FAILED_ALT": "Failed to send message.", | ||
| "TRANSCRIPT_LIVE_REGION_SEND_SENDING_ALT": "Sending message.", | ||
| "_TRANSCRIPT_LIVE_REGION_SEND_SENDING_ALT.comment": "This is for screen reader. When the user sends a message, the live region will announce this string to indicate the message is being sent.", |
There was a problem hiding this comment.
Correct the wording “screen reader” → “screen readers” for grammatical correctness in the translator comment.
| "_TRANSCRIPT_LIVE_REGION_SEND_SENDING_ALT.comment": "This is for screen reader. When the user sends a message, the live region will announce this string to indicate the message is being sent.", | |
| "_TRANSCRIPT_LIVE_REGION_SEND_SENDING_ALT.comment": "This is for screen readers. When the user sends a message, the live region will announce this string to indicate the message is being sent.", |
When a user submits a message, the activity enters the "sending" state while awaiting server acknowledgement. The visual "Sending" indicator was rendered next to the activity but not inside an ARIA live region, so screen readers never announced it.
Adds LiveRegionSendSending component (mirroring LiveRegionSendFailed) that watches for activities newly entering the "sending" state and queues the localized "Sending message." string into the polite live region. Also adds TRANSCRIPT_LIVE_REGION_SEND_SENDING_ALT localization key and a corresponding integration test.
Bug ticket: https://msazure.visualstudio.com/CCI/_workitems/edit/36003607/
Changelog Entry
Description
Design
Specific Changes
CHANGELOG.mdReview Checklist
z-index)package.jsonandpackage-lock.jsonreviewed