From f56182e50d2ffcedac78b39f83932117833e0c5c Mon Sep 17 00:00:00 2001 From: chuks68 Date: Tue, 21 Jul 2026 15:40:48 -0400 Subject: [PATCH] Add responsive card layout for SettlementTable with mobile view and extend tests for card layout --- src/components/SettlementTable.test.tsx | 17 ++ src/components/SettlementTable.tsx | 211 +++++++++++++++--------- 2 files changed, 151 insertions(+), 77 deletions(-) diff --git a/src/components/SettlementTable.test.tsx b/src/components/SettlementTable.test.tsx index 0204453..376c1cb 100644 --- a/src/components/SettlementTable.test.tsx +++ b/src/components/SettlementTable.test.tsx @@ -2,6 +2,7 @@ import { describe, it, expect } from "vitest"; import { render, screen, fireEvent, within } from "@testing-library/react"; import { SettlementTable } from "./SettlementTable"; import { Settlement } from "@/lib/types"; +import { formatAmount } from "@/lib/format"; function settlement(overrides: Partial): Settlement { return { @@ -77,3 +78,19 @@ describe("SettlementTable sorting", () => { expect(header).toHaveAttribute("aria-sort", "descending"); }); }); + +describe("SettlementTable mobile layout", () => { + it("renders a card for each settlement with correct data", () => { + render(); + const cards = screen.getAllByTestId("settlement-card"); + expect(cards).toHaveLength(settlements.length); + settlements.forEach((s) => { + const card = screen.getByText(`Settlement #${s.id}`).closest("div"); + expect(card).toBeInTheDocument(); + expect(within(card!).getByText(s.anchor)).toBeInTheDocument(); + expect(within(card!).getByText(s.asset)).toBeInTheDocument(); + expect(within(card!).getByText(formatAmount(s.amount))).toBeInTheDocument(); + expect(within(card!).getByText(formatAmount(s.fee))).toBeInTheDocument(); + }); + }); +}); diff --git a/src/components/SettlementTable.tsx b/src/components/SettlementTable.tsx index e39b0d2..acd45e5 100644 --- a/src/components/SettlementTable.tsx +++ b/src/components/SettlementTable.tsx @@ -43,86 +43,143 @@ export function SettlementTable({ ); return ( - - - - - - - - - - {actionable ? - - + <> + {/* Table view for larger screens */} +
#AssetFee : null} -
+ + + + + + + + + {actionable ? + + + {sorted.map((s) => ( + + + + + + + + {actionable ? ( + + ) : null} + + ))} + + + + + + + + +
#AssetFee : null} +
+ + {s.id} + + {s.anchor}{s.asset}{formatAmount(s.amount)}{formatAmount(s.fee)} + + + {s.status === "pending" ? ( + + {onExecute ? ( + + ) : null} + {onCancel ? ( + + ) : null} + + ) : null} +
+ Total (visible rows) + {formatAmount(totals.amount)}{formatAmount(totals.fee)} + {actionable ? : null} +
+ + {/* Card view for small screens */} +
{sorted.map((s) => ( - - - - {s.id} +
+
+ + Settlement #{s.id} - - {s.anchor} - {s.asset} - {formatAmount(s.amount)} - {formatAmount(s.fee)} - - - {actionable ? ( - - {s.status === "pending" ? ( - - {onExecute ? ( - - ) : null} - {onCancel ? ( - - ) : null} - - ) : null} - - ) : null} - +
+
+
Anchor
+
{s.anchor}
+
Asset
+
{s.asset}
+
Amount
+
{formatAmount(s.amount)}
+
Fee
+
{formatAmount(s.fee)}
+
+ {actionable && s.status === "pending" && ( +
+ {onExecute && ( + + )} + {onCancel && ( + + )} +
+ )} +
))} - - - - - Total (visible rows) - - {formatAmount(totals.amount)} - {formatAmount(totals.fee)} - - {actionable ? : null} - - - + {/* Totals card */} +
+
Total (visible rows)
+
+
Amount
+
{formatAmount(totals.amount)}
+
Fee
+
{formatAmount(totals.fee)}
+
+
+
+ ); }