skrewww

Industries · Banking

Banking Transaction Row

Banking Transaction Row is a single financial transaction entry — merchant, date, amount, and status — with a Popover for full transaction detail.

Status (success/warning/error) — drives Badge variant and amount color, no top-level variants of its own

betaReact AvailableFigma UnavailableDocs Partial
React last updated
2026-07-25
Documentation last updated
2026-07-25
Accessibility target
WCAG 2.2 AA (target)
Version
0.1.0-beta

Known open questions

  • No Figma reference exists for this component or for Industry Systems generally — confirmed absent via full file search on 2026-07-25, not an oversight.
  • List Item gained aria-expanded/aria-haspopup/aria-controls passthrough for this component's disclosure trigger — a genuine Layer 2 extension, not a Banking-specific workaround (see components/ui/ListItem.tsx).
  • Currency/amount formatting is the consumer's responsibility — amount is a pre-formatted display string, not a number with a built-in formatter, consistent with how Account Card and Balance Summary also take pre-formatted figures.

Recent activity

Click any row to open its detail Popover. Status drives both the Badge variant and the amount's color, using the existing semantic/feedback/success, semantic/feedback/warning, and semantic/action/danger tokens — no new colors.

Everyday Checking •••• 4821

Purpose

Banking Transaction Row is a single transaction entry in a financial activity list — merchant, date, amount, and status, with a Popover for full detail. The first Layer 4 Industry Systems pilot component; React-first, no Figma reference exists yet for Industry Systems.

Anatomy

Composes: List Item (row shell) + Avatar (merchant logo/initials) + Badge (status) + Popover (detail trigger, anchored via PopoverAnchor since List Item does not forward a ref).

Variants and states

success · warning · error

When to use

Listing individual financial transactions (purchases, transfers, deposits) where each row needs its own status and an optional detail view without leaving the list.

When not to use

A generic, non-financial row — use List Item directly. A transaction that doesn't need a status or detail view — a plain List Item composition is simpler.

Accessibility

The whole row is a single native button (List Item's action mode) with aria-expanded/aria-haspopup="dialog"/aria-controls reflecting the anchored Popover's open state — added to List Item itself for this, not layered on top of it.

Keyboard behavior

The row is a single native button (List Item's action mode) — Enter/Space toggles the anchored Popover open/closed, matching native button semantics. No composite keyboard model beyond that.

Common mistakes

Wrapping List Item in PopoverTrigger instead of PopoverAnchor — List Item doesn't forward a ref, so PopoverTrigger's clone-based ref assignment silently fails to attach to a real DOM node. Inventing new status colors instead of reusing the existing semantic/feedback/success, semantic/feedback/warning, and semantic/action/danger tokens Alert already establishes for exactly this purpose.

Properties

merchant, merchantLogoSrc?, merchantInitials?, date, amount, status (success/warning/error), statusLabel, detail (ReactNode, typically BankingTransactionDetailRow items).

Why Popover instead of Drawer for the detail view?

Popover's own documented purpose ("non-modal floating panel for supplementary or lightly interactive content anchored to a trigger") precisely matches viewing a handful of read-only detail fields for one row without leaving the transaction list. Drawer's placement is currently left-edge-only, an unconventional position for a per-row detail panel, and Drawer's own description ("supplementary settings, filters, or secondary forms") targets a heavier, more form-like use case than this.

Why PopoverAnchor instead of PopoverTrigger?

PopoverTrigger clones its child and injects a ref for position tracking — List Item does not forward a ref to its underlying interactive element, so that ref would silently fail to attach. PopoverAnchor wrapping a plain div is the same pattern Combobox already uses for its own non-button trigger (its text input).

Tokens used

semantic/feedback/successsemantic/feedback/warningsemantic/action/dangersemantic/text/primarysemantic/text/secondary

Component API

PropTypeDefaultDescription
merchantstringMerchant or counterparty name — also the row's title and the Avatar's accessible label.
merchantLogoSrcstringOptional merchant logo image URL.
merchantInitialsstringFallback initials shown when merchantLogoSrc is absent or fails to load.
datestringDisplay date/time string — formatting is the consumer's responsibility.
amountstringPre-formatted amount string (e.g. "-$42.50") — currency formatting is the consumer's responsibility.
status"success" | "warning" | "error"Drives Badge variant and amount color via existing semantic status tokens.
statusLabelstringVisible status text (e.g. "Completed", "Pending", "Declined").
detailReactNodeContent shown in the anchored Popover — typically BankingTransactionDetailRow items.

React example

Copy React example
import {
  BankingTransactionRow,
  BankingTransactionDetailRow,
} from "@/components/ui/BankingTransactionRow";

export function Example() {
  return (
    <ul>
      <BankingTransactionRow
        merchant="Coffee Collective"
        merchantInitials="CC"
        date="Jan 12"
        amount="-$4.75"
        status="success"
        statusLabel="Completed"
        detail={
          <>
            <BankingTransactionDetailRow label="Category" value="Dining" />
            <BankingTransactionDetailRow label="Transaction ID" value="TX-48213" />
          </>
        }
      />
    </ul>
  );
}