Skip to content

fix(frontend): unify modal focus traps, guard confirmation backdrops, dedupe routes - #1648

Merged
Junirezz merged 1 commit into
Junirezz:mainfrom
trinnode:frontend-modal-focus-fixes
Oct 18, 2026
Merged

Junirezz merged 1 commit into
Junirezz:mainfrom
trinnode:frontend-modal-focus-fixes

Conversation

@trinnode

Copy link
Copy Markdown
Contributor

Summary

Resolves four frontend accessibility/maintenance issues in one pass.

#1620 — Confirmation modal backdrop + a11y

  • ConfirmationModal and WithdrawalConfirmationModal now use the shared useFocusTrap hook instead of ad-hoc focus handling.
  • Backdrop clicks and Escape are ignored while isProcessing is true, so a transaction can't be dismissed mid-flight.
  • Both dialogs expose role="dialog", aria-modal, aria-labelledby and aria-describedby, and all copy/labels go through t().
  • Tests cover open/close, Escape, backdrop clicks (idle + processing), focus-into-dialog, focus restore, and Tab wrapping.

#1621 — One focus trap for every modal surface

  • Drawer adopts useFocusTrap, so focus is restored on close and unmount (previously it could be lost on unmount).
  • ToastCenter closes on Escape, moves focus into the panel on open, and is configured as a non-modal popover (trapTab: false, lockScroll: false) so Tab can still leave it and page scroll is untouched.
  • useFocusTrap gains optional trapTab / lockScroll options (backward compatible — defaults preserve existing behavior).

#1629 — Remove stale tracked artifacts

  • Deleted 11 committed build/test artifacts (bundle-stats.html, lint-output*.txt, test-output.txt, test-results.txt, test_output.json, vitest-results*.json).
  • Extended frontend/.gitignore and documented the generated reports in frontend/README.md.

#1564 — Deduplicate route definitions

  • Removed a duplicate generic ErrorBoundary block and a duplicate /admin route in App.tsx.
  • Wrapped the wildcard route in RouteErrorBoundary routeName="not-found".
  • Added a route-table smoke test asserting each path deep-links to exactly one route.

CI plumbing

  • Repaired the invalid root package.json (duplicate "test" key + trailing comma) that caused a JSON parse failure and blocked the frontend env-validation/version gates.

Test plan

  • npm run lint — clean for all touched files
  • npx vitest run on the new/updated suites — 48 passing
  • Full suite: only pre-existing failures remain (unrelated files: SessionExpiryWarning, VaultDashboard, VirtualizedDataTable, ToastContext, optimisticVaultCache, vaultHealthApi, Portfolio, TransactionHistory, network-banner-stack); all were confirmed failing on main before this branch.

Closes #1620
Closes #1621
Closes #1629
Closes #1564

… dedupe routes

- ConfirmationModal & WithdrawalConfirmationModal now use the shared
  useFocusTrap hook, ignore backdrop clicks and Escape while processing,
  expose dialog/aria-modal/aria-labelledby, and route all copy through t()
- Drawer adopts useFocusTrap so focus is restored on both close and unmount
- ToastCenter closes on Escape, moves focus into the panel, and stays a
  non-modal, non-scroll-locking popover
- useFocusTrap gains trapTab and lockScroll options (backward compatible)
- App.tsx drops duplicate route/ErrorBoundary definitions and wraps the
  not-found route in RouteErrorBoundary
- Remove stale tracked build/test artifacts and extend .gitignore + README
- Repair invalid root package.json (duplicate test script + trailing comma)
- Add modal, drawer, toast-center and routing smoke tests

Closes Junirezz#1620
Closes Junirezz#1621
Closes Junirezz#1629
Closes Junirezz#1564
@drips-wave

drips-wave Bot commented Oct 17, 2026

Copy link
Copy Markdown

@trinnode 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

@trinnode

Copy link
Copy Markdown
Contributor Author

Grouped these four issues since they all touch the same modal/route surfaces, so one review pass covers them.

Notes for review:

  • useFocusTrap keeps its existing defaults; the new trapTab / lockScroll flags are opt-in. ToastCenter is the only caller passing them, which is what makes it a non-modal popover (Tab can leave, page scroll is untouched).
  • The confirmation modals still fully block dismissal while isProcessing — backdrop clicks and Escape are no-ops in that state, so an in-flight deposit/withdraw can't be closed by accident.
  • Drawer moved to the shared hook, which also fixes the case where focus was lost when the drawer unmounted rather than closed.
  • The root package.json had a duplicate test key plus a trailing comma, so JSON.parse threw and took down the pnpm-version gate and the frontend env-validation job before anything else could run. Fixed here.

The failing checks that remain on this repo (backend, Rust/CodeQL, coverage-threshold, dependency scans, plus a handful of pre-existing frontend test failures) are unrelated to these files — I confirmed they fail on main too.

@Junirezz
Junirezz merged commit 1f5ab07 into Junirezz:main Oct 18, 2026
14 of 26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment