Skip to content

[Gap-Audit] Stability: PaymentConsentModal Firefox focus timing + imperative/declarative hybrid #11

Description

@TortoiseWolfe

Summary

PaymentConsentModal.tsx:46-57 calls dialog.showModal() then immediately calls acceptButtonRef.current?.focus() synchronously. Firefox doesn't guarantee the dialog is focusable until the next frame. This is the same shape as the Firefox CI regression that caused revert 3e67772.

Additionally, the component manages show/hide via two conflicting mechanisms: it returns null when !showModal AND imperatively calls dialog.close() in a useEffect. When showModal flips false, the cleanup of the cancel-event-listener effect can be skipped, leaving orphaned event listeners on the dialog element.

Files

  • src/components/payment/PaymentConsentModal/PaymentConsentModal.tsx:46-57 — focus-after-showModal timing
  • src/components/payment/PaymentConsentModal/PaymentConsentModal.tsx:50-86 — imperative/declarative hybrid

Fix

  1. Wrap focus call in requestAnimationFrame(() => acceptButtonRef.current?.focus())
  2. Pick ONE mode: either keep dialog mounted with open attribute (declarative), OR remove the return null and let dialog stay mounted-but-closed (imperative). Don't mix.

Why this matters

Firefox revert 3e67772 already happened for this exact symptom. The fix is the same shape; just hasn't been applied here.

Related

  • Code review findings: CR-018, CR-019
  • Revert: 3e67772

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggap-auditIdentified during 2026-04-25 planned-vs-shipped auditpriority:p0Critical — fix immediately (blocking, security, post-remake regression root cause)remake-regressionCode-review finding tied to post-remake regression patterns

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions