feat(batchdiff): remit rows drill to RemitDrawer
This commit is contained in:
@@ -29,6 +29,7 @@ vi.mock("@/lib/api", () => ({
|
|||||||
isConfigured: true,
|
isConfigured: true,
|
||||||
listBatches: vi.fn(),
|
listBatches: vi.fn(),
|
||||||
getBatchDiff: vi.fn(),
|
getBatchDiff: vi.fn(),
|
||||||
|
getRemittance: vi.fn(),
|
||||||
},
|
},
|
||||||
ApiError: class ApiError extends Error {
|
ApiError: class ApiError extends Error {
|
||||||
constructor(public status: number, message: string) {
|
constructor(public status: number, message: string) {
|
||||||
@@ -180,6 +181,13 @@ function makeDiffPayload(): BatchDiffResponse {
|
|||||||
describe("BatchDiff page", () => {
|
describe("BatchDiff page", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
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(() => {
|
afterEach(() => {
|
||||||
@@ -189,6 +197,42 @@ describe("BatchDiff page", () => {
|
|||||||
.happyDOM.setURL("http://localhost/batch-diff");
|
.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 () => {
|
it("renders the awaiting-picks empty state when no batches are selected", async () => {
|
||||||
(api.listBatches as unknown as ReturnType<typeof vi.fn>).mockResolvedValue([
|
(api.listBatches as unknown as ReturnType<typeof vi.fn>).mockResolvedValue([
|
||||||
BATCH_A, BATCH_B,
|
BATCH_A, BATCH_B,
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ import {
|
|||||||
BatchDiffView,
|
BatchDiffView,
|
||||||
BatchDiffViewSkeleton,
|
BatchDiffViewSkeleton,
|
||||||
} from "@/components/BatchDiffView";
|
} from "@/components/BatchDiffView";
|
||||||
|
import { RemitDrawer } from "@/components/RemitDrawer";
|
||||||
import { useBatches } from "@/hooks/useBatches";
|
import { useBatches } from "@/hooks/useBatches";
|
||||||
import { useBatchDiff } from "@/hooks/useBatchDiff";
|
import { useBatchDiff } from "@/hooks/useBatchDiff";
|
||||||
|
import { useRemitDrawerUrlState } from "@/hooks/useRemitDrawerUrlState";
|
||||||
import type { BatchSummary as ApiBatchSummary } from "@/lib/api";
|
import type { BatchSummary as ApiBatchSummary } from "@/lib/api";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
@@ -269,6 +271,14 @@ export function BatchDiff() {
|
|||||||
// BrowserRouter (production) symmetric: both update the
|
// BrowserRouter (production) symmetric: both update the
|
||||||
// useSearchParams hook on every navigation.
|
// useSearchParams hook on every navigation.
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
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(
|
const { a, b } = useMemo(
|
||||||
() => readIdsFromParams(searchParams),
|
() => readIdsFromParams(searchParams),
|
||||||
[searchParams],
|
[searchParams],
|
||||||
@@ -835,6 +845,24 @@ export function BatchDiff() {
|
|||||||
<span>{ready ? "A vs B" : "awaiting picks"}</span>
|
<span>{ready ? "A vs B" : "awaiting picks"}</span>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user