Skip to content

feat(frontend): implement WCAG 2.1 AA accessible focus trap and ARIA attributes in Fiat Onramp Modal - #1584

Merged
emdevelopa merged 1 commit into
emdevelopa:mainfrom
Chucks1093:fix/1484-1485-1486-1502
Sep 26, 2026
Merged

emdevelopa merged 1 commit into
emdevelopa:mainfrom
Chucks1093:fix/1484-1485-1486-1502

Conversation

@Chucks1093

Copy link
Copy Markdown
Contributor

Closes #1484
Closes #1485
Closes #1486
Closes #1502

Changes

frontend/src/components/ui/Modal.tsx

  • Added role="dialog" and aria-modal="true" to the dialog container so screen readers announce the modal boundary correctly (WCAG 2.1 AA, success criterion 4.1.2)
  • Added aria-labelledby wired to the existing title id so the modal name is announced on focus
  • Added optional aria-describedby prop forwarded from the consumer
  • Added ref={dialogRef} and tabIndex={-1} on the container — previously the ref was declared but never attached, so dialog.focus() was a no-op and the focus trap never initialised
  • Added focus:outline-none so the programmatic focus on open does not show an unwanted ring on the container

frontend/src/components/FiatOnrampModal.tsx

  • Added id={descriptionId} to the description paragraph and passed descriptionId to <Modal> via the new prop so aria-describedby is populated on the dialog element
  • Added aria-busy={isBusy} to the submit button so assistive technology announces the in-progress state without relying on visual-only feedback

frontend/src/components/FiatOnrampModal.test.tsx

  • Added a WCAG 2.1 AA — ARIA attributes describe block with 4 new tests:
    • role=dialog and aria-modal=true present
    • aria-labelledby resolves to the title text
    • aria-describedby resolves to the description paragraph
    • aria-busy reflects idle and busy states on the submit button
    • Close button has a descriptive aria-label

@vercel

vercel Bot commented Sep 26, 2026

Copy link
Copy Markdown

@Chucks1093 is attempting to deploy a commit to the Emmanuel's projects Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave

drips-wave Bot commented Sep 26, 2026

Copy link
Copy Markdown

@Chucks1093 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@emdevelopa
emdevelopa merged commit bd7adf4 into emdevelopa:main Sep 26, 2026
1 of 5 checks passed
Mawuli-tech added a commit to Mawuli-tech/Stellar_Payment_API that referenced this pull request Sep 26, 2026
…lopa#1512)

Investigated the actual webhook-logs feature (page.tsx -> WebhookLogs.tsx
-> WebhookDetailModal.tsx -> the shared ui/Modal.tsx) rather than assuming
the issue's "focus trap" framing applied to the log table itself. The
plain, read-only <table> listing deliveries doesn't need a focus trap.

The originally-planned fix for this issue also wired up Modal.tsx's
dead dialogRef (never attached via ref={dialogRef}, so its existing
Tab-cycling focus-trap implementation never ran) and added
tabIndex/role="button"/keyboard handling to each WebhookLogs row, both
genuinely missing at the time. Rebasing onto main found both already
fixed independently: Modal.tsx already has ref={dialogRef},
role="dialog", aria-modal, and aria-labelledby (merged via emdevelopa#1584, for
Fiat Onramp Modal's own focus-trap issue), and WebhookLogs.tsx's rows
already have tabIndex={0}/role="button"/Enter+Space handling (merged
via emdevelopa#1582, WebhookLogs' own keyboard-navigation issue), both fixing
the exact same underlying dead code and accessibility gaps this issue
also targeted, just filed and closed under different issue numbers.
Kept upstream's versions as-is rather than reintroducing equivalent
but differently-worded duplicates.

What's left, and genuinely new: the <table> itself had no accessible
name and its <th> cells had no scope="col", and neither of the other
two merged PRs touched this (no sortable columns exist here, so
aria-sort doesn't apply, unlike RecentPayments.tsx elsewhere in this
codebase). Added aria-label to the table and scope="col" to every
header cell.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment