Skip to content

feat(frontend): 2FA optimistic rollback, keyboard nav, tests, and accessible Analytics Cards focus trap - #1583

Open
omarima-10 wants to merge 1 commit into
emdevelopa:mainfrom
omarima-10:feature/hillwisdom-assigned-batch
Open

omarima-10 wants to merge 1 commit into
emdevelopa:mainfrom
omarima-10:feature/hillwisdom-assigned-batch

Conversation

@omarima-10

Copy link
Copy Markdown

Summary

  • TwoFactorAuthSetup: on a network failure during code verification, the QR/manual key and the entered code are preserved instead of being discarded, a distinct network-failure message is shown with a retry action, and network failures are distinguished from invalid-code rejections (which still clear the code, since that's a normal retry case, not a connectivity problem).
  • TwoFactorAuthSetup: the code input now submits on Enter and clears on Escape, and step transitions are announced to keyboard/screen-reader users via a live region (the step dots themselves have no valid "jump back" action, since each step is driven by an async call rather than being freely navigable).
  • TwoFactorAuthSetup: added unit and snapshot coverage for the idle/enabling/scan/error/success states and the new rollback/retry/keyboard behavior. While adding this I found and fixed a pre-existing bug in the test file itself: several tests awaited waitFor(...) while fake timers were active, which deadlocks because waitFor polls via setTimeout, frozen once vi.useFakeTimers() runs, until Vitest's own test timeout. Fixed by following this repo's own established pattern elsewhere (e.g. KycSubmissionForm.test.tsx): flush state synchronously via act(async () => vi.runAllTimers()) and assert directly, no waitFor under fake timers.
  • AnalyticsCards: each card is now a button that opens an accessible detail dialog (reusing the shared ui/Modal component) with the metric restated in plain language. While wiring this up I found the shared Modal component's focus trap was silently inert: dialogRef was declared and read in the trap's useEffect, but never attached via ref={} to any element, so dialogRef.current was always null and the trap's own early-return made it a no-op on every open. Fixed by attaching the ref and adding the missing role="dialog", aria-modal="true", and aria-labelledby (matching the convention PaymentDetailModal.tsx already uses), so the focus trap is now real for every consumer of the shared Modal, not just AnalyticsCards.

Test plan

  • TwoFactorAuthSetup.test.tsx: 31/31 pass (26 pre-existing, rewritten to fix the fake-timer deadlock, plus 5 new snapshots)
  • AnalyticsCards.test.tsx (new): 11/11 pass, including focus-trap and dialog-labelling assertions
  • FiatOnrampModal.test.tsx (existing consumer of the shared Modal): 7/7 pass, confirming the Modal ARIA/ref fix is non-breaking
  • npx tsc --noEmit: identical pre-existing error count to a clean main baseline (6 errors, all in OnboardingProgressTracker.tsx, unrelated)
  • Full frontend npx vitest run: verified suite-by-suite against a clean main baseline, zero new failures; TwoFactorAuthSetup.test.tsx moved from failing to fully passing

closes #1519
closes #1520
closes #1521
closes #1522

…ble focus trap

- TwoFactorAuthSetup: preserve QR/manual key and entered code on a network
  failure during verification instead of discarding setup progress, add a
  retry action, and distinguish network failures from invalid-code
  rejections (closes emdevelopa#1519)
- TwoFactorAuthSetup: submit on Enter, clear on Escape, and announce step
  transitions via a live region for keyboard/screen-reader users (closes emdevelopa#1520)
- TwoFactorAuthSetup: add unit and snapshot test coverage across all steps,
  including the new rollback/retry and keyboard behavior; fix a pre-existing
  deadlock where waitFor was awaited while fake timers were active (closes emdevelopa#1521)
- AnalyticsCards: make each card an interactive detail dialog reusing the
  shared Modal component; fix Modal's focus trap, which was silently
  inert because its ref was never attached to the dialog element, and add
  the missing role="dialog"/aria-modal/aria-labelledby (closes emdevelopa#1522)
@vercel

vercel Bot commented Sep 26, 2026

Copy link
Copy Markdown

@omarima-10 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

@omarima-10 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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment