feat(a11y): live-region announcements for errors and status - #1650
feat(a11y): live-region announcements for errors and status#1650AbhishekChorotiya wants to merge 1 commit into
Conversation
|
💡 Announcer.res error handling swallows exceptions silently The message handler in try {
let dict = ev.data->Utils.safeParse->Utils.getDictFromJson
// ... processing
} catch {
| _ => ()
}While this prevents crashes from malformed messages, it also hides legitimate errors during development. Consider logging parsing failures at least in development builds: catch {
| exn =>
if GlobalVars.env === "development" {
Console.log("Announcer: failed to process message", exn)
}
} |
7a20533 to
f13e8f7
Compare
f6035f0 to
7e98542
Compare
Global announcer (polite role=status + assertive role=alert, auto-clear); aria-busy on pay button; announce processing/failure (localized: processingPaymentText, paymentFailedText); announced validation errors via shared LiveError; accessible name for the VGS field.
7e98542 to
7636a92
Compare
f13e8f7 to
54c0f0f
Compare
Review SummaryPR: #1650 - feat(a11y): live-region announcements for errors and status Findings
No blocking issues. The live region implementation follows WCAG guidelines for status announcements. |
|
The new
This is particularly important for error states where multiple validations may fire in sequence. |
|
🚨 Bare catch block swallows all errors in announcer handler The Consider logging to the configured logger or at minimum document why errors must be suppressed. } catch {
| _ => ()
} |
| {React.string(errorStr->Belt.Option.getWithDefault(""))} | ||
| </div> | ||
| <LiveError | ||
| text={errorStr->Belt.Option.getWithDefault("")} |
There was a problem hiding this comment.
| text={errorStr->Belt.Option.getWithDefault("")} | |
| text={errorStr->Option.getOr("")} |
| if !(submitSuccessfulVal->JSON.Decode.bool->Option.getOr(false)) { | ||
| setIsPayNowButtonDisable(_ => false) | ||
| setShowLoader(_ => false) | ||
| announce(~assertive=true, localeString.paymentFailedText) |
There was a problem hiding this comment.
PayNowButton.res — calls announce(~assertive=true, localeString.paymentFailedText) (generic message)
Then Announcer.res— message listener extracts the specific error message from the response payload
Both listen for the same window "message" event with submitSuccessful === false. The Announcer registers its listener at mount time (in useEffect), while PayNowButton registers via addSmartEventListener at click time. Listeners fire in registration order, so Announcer fires first (sets specific error), then PayNowButton fires (overwrites with generic message)

Type of Change
Description
This PR improves live-region behavior for payment status and error states. It keeps announcement regions stable in the page, uses concise assertive messaging for failed submit states, and reuses the shared visually-hidden utility instead of duplicating screen-reader-only styling.
The user impact is more predictable screen-reader feedback during submit and validation flows, while avoiding extra noise during ordinary navigation or payment-method switching.
This PR is stacked after the keyboard and focus PR because it depends on the shared accessibility helper module already present in the stack.
Closes #1649
How did you test it?
Validated as part of the completed accessibility stack. The checks cover the combined flow after all stacked PRs are applied, and live-region behavior was checked in the local payment-element accessibility smoke flow.
npm run re:buildon the completed accessibility stack.npm run test:hookson the completed accessibility stack.npm run buildon the completed accessibility stack.Checklist
npm run re:build