diff --git a/src/components/BatchDiffView.tsx b/src/components/BatchDiffView.tsx index a7b3311..df0f3c6 100644 --- a/src/components/BatchDiffView.tsx +++ b/src/components/BatchDiffView.tsx @@ -5,6 +5,7 @@ import { Plus, type LucideIcon, } from "lucide-react"; +import { useNavigate } from "react-router-dom"; import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; import { @@ -15,6 +16,7 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; +import { DrillableCell } from "@/components/drill/DrillableCell"; import { fmt } from "@/lib/format"; import { cn } from "@/lib/utils"; import type { @@ -263,13 +265,28 @@ function RowIndicator({ // --------------------------------------------------------------------------- function ClaimIdCell({ id }: { id: string }) { + // SP21 Phase 5 Task 5.6: each claim id is drillable to + // /claims?claim=ID so the operator can jump straight from a + // "Removed from A" or "Changed" row into the ClaimDrawer. + // DrillableCell handles e.stopPropagation internally — important + // if these rows ever get a row-level onClick. For removed claims + // that no longer exist in the DB, the ClaimDrawer's 404 state + // takes over (verified in Phase 2 testing). + const navigate = useNavigate(); return ( - + navigate(`/claims?claim=${encodeURIComponent(id)}`) + } + ariaLabel={`View claim ${id} in detail`} > - {id} - + + {id} + + ); } diff --git a/src/pages/BatchDiff.test.tsx b/src/pages/BatchDiff.test.tsx index c58c612..8829605 100644 --- a/src/pages/BatchDiff.test.tsx +++ b/src/pages/BatchDiff.test.tsx @@ -481,8 +481,184 @@ describe("BatchDiff page", () => { ).toContain("pick a batch"); unmount(); }); + + it("SP21 Task 5.6: clicking an added-claim id navigates to /claims?claim=ID", async () => { + // Each ClaimIdCell wraps its id text with DrillableCell, whose + // onClick navigates to /claims?claim=ID. The MemoryRouter gives + // us a router context so the navigation completes; we observe + // it via the rendered pathname on a LocationTracker spy. + (api.listBatches as unknown as ReturnType).mockResolvedValue([ + BATCH_A, BATCH_B, + ]); + (api.getBatchDiff as unknown as ReturnType).mockResolvedValue( + makeDiffPayload(), + ); + + const captured: { pathname: string; search: string } = { + pathname: "/batch-diff", + search: "", + }; + const Tracker = () => { + const loc = useLocationSafe(); + React.useEffect(() => { + captured.pathname = loc.pathname; + captured.search = loc.search; + }, [loc.pathname, loc.search]); + return null; + }; + + const { unmount } = renderIntoContainer( + <> + + + , + [`/batch-diff?a=${BATCH_A.id}&b=${BATCH_B.id}`], + ); + + await waitFor( + () => !!document.querySelector('[data-testid="diff-added-row-CLM-3"]'), + "added row rendered", + ); + + const cell = document.querySelector( + '[data-testid="diff-added-row-CLM-3"] [data-testid="diff-claim-id"]', + ); + expect(cell).not.toBeNull(); + // DrillableCell wraps the id text in its own