feat(batchdiff): remit rows drill to RemitDrawer

This commit is contained in:
Tyler
2026-06-21 17:07:38 -06:00
parent 54440da2cd
commit 12c2913ba1
2 changed files with 72 additions and 0 deletions
+44
View File
@@ -29,6 +29,7 @@ vi.mock("@/lib/api", () => ({
isConfigured: true,
listBatches: vi.fn(),
getBatchDiff: vi.fn(),
getRemittance: vi.fn(),
},
ApiError: class ApiError extends Error {
constructor(public status: number, message: string) {
@@ -180,6 +181,13 @@ function makeDiffPayload(): BatchDiffResponse {
describe("BatchDiff page", () => {
beforeEach(() => {
vi.clearAllMocks();
// Default for the per-remit detail fetch — the drawer fetches
// this whenever `?remit=` is in the URL. Return a never-resolving
// promise so the drawer stays in the loading state; the smoke
// test only asserts the drawer mounts, not the loaded data.
(
api.getRemittance as unknown as ReturnType<typeof vi.fn>
).mockReturnValue(new Promise(() => {}));
});
afterEach(() => {
@@ -189,6 +197,42 @@ describe("BatchDiff page", () => {
.happyDOM.setURL("http://localhost/batch-diff");
});
it("SP21 Task 4.5: deep-link ?remit=ID opens the RemitDrawer on mount", async () => {
// Pre-set the URL with `?remit=`. The page doesn't surface any
// per-remit rows (the diff payload is claim-level), but the
// drawer must still mount so the URL contract is honored.
//
// `useRemitDrawerUrlState` reads `window.location.search`
// (NOT React Router's search params) so we have to set the
// global window URL via happyDOM AND give MemoryRouter an
// initial entry — both stay in lockstep.
(window as unknown as { happyDOM: { setURL: (u: string) => void } })
.happyDOM.setURL("http://localhost/batch-diff?remit=REM-7");
(
api.listBatches as unknown as ReturnType<typeof vi.fn>
).mockResolvedValue([BATCH_A, BATCH_B]);
const { unmount } = renderIntoContainer(
<BatchDiff />,
["/batch-diff?remit=REM-7"],
);
// Page header must be present + drawer must be open.
await waitFor(
() => !!document.querySelector('[data-testid="batch-diff-page"]'),
"page header mounted",
);
await waitFor(
() => !!document.querySelector('[data-testid="remit-drawer"]'),
"remit drawer mounted via deep link",
);
expect(
document.querySelector('[data-testid="remit-drawer"]'),
).not.toBeNull();
unmount();
});
it("renders the awaiting-picks empty state when no batches are selected", async () => {
(api.listBatches as unknown as ReturnType<typeof vi.fn>).mockResolvedValue([
BATCH_A, BATCH_B,
+28
View File
@@ -21,8 +21,10 @@ import {
BatchDiffView,
BatchDiffViewSkeleton,
} from "@/components/BatchDiffView";
import { RemitDrawer } from "@/components/RemitDrawer";
import { useBatches } from "@/hooks/useBatches";
import { useBatchDiff } from "@/hooks/useBatchDiff";
import { useRemitDrawerUrlState } from "@/hooks/useRemitDrawerUrlState";
import type { BatchSummary as ApiBatchSummary } from "@/lib/api";
import { cn } from "@/lib/utils";
@@ -269,6 +271,14 @@ export function BatchDiff() {
// BrowserRouter (production) symmetric: both update the
// useSearchParams hook on every navigation.
const [searchParams, setSearchParams] = useSearchParams();
// SP21 Phase 4 Task 4.5: mount the RemitDrawer so a deep link to
// /batch-diff?remit=REM-1 opens the drawer in-place. The BatchDiff
// data model (BatchClaimDiffSummary) is a claim-level projection —
// there are no per-remit IDs in the diff payload to wrap, so no
// click-to-drill is wired here. The drawer still mounts so the
// `?remit=` URL contract is honored when the user lands on this
// page with the param set (e.g. from an external link).
const { remitId, open, close } = useRemitDrawerUrlState();
const { a, b } = useMemo(
() => readIdsFromParams(searchParams),
[searchParams],
@@ -835,6 +845,24 @@ export function BatchDiff() {
<span>{ready ? "A vs B" : "awaiting picks"}</span>
</div>
</div>
{/* SP21 Phase 4 Task 4.5: RemitDrawer mount. There are no
per-remit rows in the diff payload (the page is a
claim-level projection), so this drawer is for deep-link
support only — clicking a claim row does not open it.
When the user lands on /batch-diff?remit=REM-1, the drawer
opens in-place. The `remits` list is empty so j/k is a
no-op while the drawer is open. */}
<RemitDrawer
remitId={remitId}
remits={[]}
onClose={close}
onNavigate={open}
onToggleHelp={() => {
// BatchDiff has no cheatsheet surface; `?` is a no-op
// here, but the prop is required by the drawer's contract.
}}
/>
</div>
);
}