diff --git a/src/components/TransactionStatus.patterns.test.tsx b/src/components/TransactionStatus.patterns.test.tsx index 70e8118..f1c70a7 100644 --- a/src/components/TransactionStatus.patterns.test.tsx +++ b/src/components/TransactionStatus.patterns.test.tsx @@ -47,6 +47,7 @@ const BASE: Omit = { const pending: Transaction = { ...BASE, status: "pending" }; const success: Transaction = { ...BASE, status: "success" }; const error: Transaction = { ...BASE, status: "error", message: "Insufficient funds" }; +const stale: Transaction = { ...BASE, status: "stale" }; // ─── Helper ────────────────────────────────────────────────────────────────── @@ -139,7 +140,27 @@ describe("TransactionStatus — pattern fills (WCAG 1.4.1)", () => { }); }); - // ─── 4. data-status attribute ────────────────────────────────────────────── + + // ─── 3.5 Stale ───────────────────────────────────────────────────────────── + describe("stale status", () => { + it("3.5a. icon-bg carries the colour-tint class (dc-status-icon-bg--accent)", () => { + render( {}} />); + expect(getIconBg("stale")).toHaveClass("dc-status-icon-bg--accent"); + }); + + it("3.5b. icon-bg carries the geometry-pattern class (dc-status-icon-bg--pattern-pending)", () => { + render( {}} />); + expect(getIconBg("stale")).toHaveClass("dc-status-icon-bg--pattern-pending"); + }); + + it("3.5c. renders the refresh button when onRefresh is provided", () => { + const { unmount } = render( {}} onRefresh={() => {}} />); + expect(screen.getByRole("button", { name: /refresh status/i })).toBeInTheDocument(); + unmount(); + }); + }); + +// ─── 4. data-status attribute ────────────────────────────────────────────── describe("data-status attribute", () => { it("4a. pending transaction sets data-status='pending'", () => { @@ -156,6 +177,11 @@ describe("TransactionStatus — pattern fills (WCAG 1.4.1)", () => { render( {}} />); expect(document.querySelector("[data-status='error']")).toBeInTheDocument(); }); + + it("4d. stale transaction sets data-status='stale'", () => { + render( {}} />); + expect(document.querySelector("[data-status='stale']")).toBeInTheDocument(); + }); }); // ─── 5. Screen-reader / WCAG 4.1.3 status message ───────────────────────── @@ -200,6 +226,11 @@ describe("TransactionStatus — pattern fills (WCAG 1.4.1)", () => { render( {}} />); expect(screen.getByText(/an error occurred/i)).toBeInTheDocument(); }); + + it("6f. stale renders 'Status Unknown' heading", () => { + render( {}} />); + expect(screen.getByRole("heading", { name: /status unknown/i })).toBeInTheDocument(); + }); }); // ─── 7. Icon aria-hidden ─────────────────────────────────────────────────── diff --git a/src/components/TransactionStatus.tsx b/src/components/TransactionStatus.tsx index afd2399..0de1d4c 100644 --- a/src/components/TransactionStatus.tsx +++ b/src/components/TransactionStatus.tsx @@ -1,37 +1,3 @@ -/** - * TransactionStatus - * - * Step 4 (final) of the draw-credit flow. Shows the outcome of the draw - * request: pending / success / error — with a transaction detail card and a - * "Make Another Draw" reset button. - * - * Design-token classes used (all from `src/index.css` `.dc-*` block): - * dc-spinner-wrap (reused for centred text layout), - * dc-status-icon-bg, dc-status-icon-bg--accent/success/error, - * dc-status-icon, dc-status-icon--accent/success/error, - * dc-step__title, dc-step__subtitle, - * dc-status-detail-card, dc-status-detail-row, - * dc-status-detail-row__label, dc-status-detail-row__value, - * dc-status-detail-row__value--mono, dc-status-detail-row__value--large, - * dc-success-notice, - * dc-btn, dc-btn--primary, dc-btn--full, dc-btn--icon-gap - * - * Color-blind accessibility (WCAG 2.1 SC 1.4.1): - * The icon circle uses both a color-tint class AND a pattern-fill class - * (from src/styles/patterns.css) so each status is identifiable by shape, - * not color alone: - * pending → concentric dots (dc-status-icon-bg--pattern-pending) - * success → diagonal stripes (dc-status-icon-bg--pattern-success) - * error → crosshatch (dc-status-icon-bg--pattern-error) - * - * Accessibility: - * - The outer div has role="status" + aria-live="polite" so the result is - * announced when it replaces the loading spinner. - * - Icons are aria-hidden="true"; all meaningful state info is in text. - * - forced-colors overrides in patterns.css preserve pattern geometry in - * Windows High Contrast mode. - */ - import { Transaction } from "@/types/draw-credit.types"; import { CheckCircle2, AlertCircle, Clock, RotateCcw } from "lucide-react"; import "@/styles/patterns.css"; @@ -39,23 +5,14 @@ import "@/styles/patterns.css"; interface TransactionStatusProps { transaction: Transaction; onNewDraw: () => void; + onRefresh?: () => void; } -/** - * Maps a transaction status to the CSS modifier and icon for that outcome. - * All colour references go through the dc-status-icon-bg--* and - * dc-status-icon--* token classes (var(--accent/success/error)). - * - * patternMod maps to the dc-status-icon-bg--pattern-* classes in - * src/styles/patterns.css, providing a geometry cue beyond colour alone - * (WCAG 2.1 SC 1.4.1 – Use of Color). - */ const STATUS_CONFIG = { pending: { Icon: Clock, title: "Processing", colorMod: "accent", - /** Concentric dots — conveys cyclical "in progress" motion. */ patternMod: "pending", message: "Your draw request is being processed.", }, @@ -63,7 +20,6 @@ const STATUS_CONFIG = { Icon: CheckCircle2, title: "Draw Successful", colorMod: "success", - /** Diagonal stripes (45°, upward sweep) — positive direction cue. */ patternMod: "success", message: "Funds have been disbursed to your account.", }, @@ -71,15 +27,22 @@ const STATUS_CONFIG = { Icon: AlertCircle, title: "Draw Failed", colorMod: "error", - /** Crosshatch (45° + 135°) — dense warning texture, distinct from success. */ patternMod: "error", - message: null, // filled from transaction.message at render time + message: null, + }, + stale: { + Icon: Clock, + title: "Status Unknown", + colorMod: "accent", + patternMod: "pending", + message: "The transaction status is unclear. Please refresh to check again.", }, } as const; export function TransactionStatus({ transaction, onNewDraw, + onRefresh, }: TransactionStatusProps) { const config = STATUS_CONFIG[transaction.status]; const { Icon, title, colorMod, patternMod } = config; @@ -90,16 +53,11 @@ export function TransactionStatus({ : config.message; return ( - /* - * role="status" + aria-live="polite" ensures the result is announced when - * this replaces the loading spinner (which was removed from the DOM). - */
- {/* Status icon circle — colour tint + pattern fill for color-blind accessibility */}
- {/* Title + message */}

{title}

{message}

- {/* Transaction detail card */}
- {/* Transaction ID */}

Transaction ID

{transaction.id}

- - {/* Amount drawn */}

Amount Drawn

${transaction.amount.toLocaleString()}

- - {/* Timestamp (optional) */} {transaction.timestamp && (

Time

@@ -147,19 +98,26 @@ export function TransactionStatus({ )}
- {/* Success notice — uses var(--success-tint/border) via dc-success-notice */} {transaction.status === "success" && (
-

- Funds will be deposited to your account within 1-2 business days. -

+

Funds will be deposited to your account within 1-2 business days.

)} - {/* Reset CTA */} + {transaction.status === "stale" && onRefresh && ( + + )} +