// @vitest-environment jsdom // // The shared chip-overflow measure core. jsdom has no layout engine — every offset* / clientWidth // is 0 — so we stub those on the real ref'd nodes and assert the hook (a) reads the geometry off them // (chip right edges, pill width, the gap between the last chip and the pill, and the lane's available // width), (b) returns the caller's `decide` result, and (c) re-measures when `deps` changes. import { describe, it, expect, afterEach } from "vitest"; import { useRef } from "react"; import { render, screen, cleanup, act } from "@testing-library/react"; import { useChipOverflowFit, type ChipRowGeometry } from "./useChipOverflowFit"; function stub( el: HTMLElement, props: Partial>, ) { for (const [k, v] of Object.entries(props)) { Object.defineProperty(el, k, { configurable: true, get: () => v }); } } let lastGeom: ChipRowGeometry | null = null; function Harness({ deps }: { deps: string }) { const rowRef = useRef(null); const chipRefs = useRef>([]); const pillRef = useRef(null); const visible = useChipOverflowFit({ rowRef, chipsRef: chipRefs, pillRef, deps, initial: -1, decide: (g) => { lastGeom = g; // A trivial policy: count the chips whose right edge fits the lane. return g.chipRights.filter((r) => r <= g.avail).length; }, }); return (
{ chipRefs.current[0] = el; }} data-testid="chip0" /> { chipRefs.current[1] = el; }} data-testid="chip1" />
{visible}
); } afterEach(() => { cleanup(); lastGeom = null; }); describe("useChipOverflowFit", () => { it("reads geometry off the refs and returns the decide() result; re-measures on deps change", () => { const { rerender } = render(); // First pass ran against jsdom's 0-sized boxes. expect(lastGeom).toEqual({ avail: 0, chipRights: [0, 0], pillW: 0, gap: 0 }); // Give the boxes real geometry, then bump deps to force a re-measure. stub(screen.getByTestId("row"), { clientWidth: 100 }); stub(screen.getByTestId("chip0"), { offsetLeft: 0, offsetWidth: 30 }); // right edge 30 stub(screen.getByTestId("chip1"), { offsetLeft: 36, offsetWidth: 30 }); // right edge 66 stub(screen.getByTestId("pill"), { offsetLeft: 72, offsetWidth: 20 }); // gap = 72 - 66 = 6 act(() => { rerender(); }); expect(lastGeom).toEqual({ avail: 100, chipRights: [30, 66], pillW: 20, gap: 6 }); // decide counted both chips (30 and 66 both <= 100). expect(screen.getByTestId("visible").textContent).toBe("2"); }); it("drops a chip whose right edge exceeds the lane, via the caller's policy", () => { const { rerender } = render(); stub(screen.getByTestId("row"), { clientWidth: 40 }); stub(screen.getByTestId("chip0"), { offsetLeft: 0, offsetWidth: 30 }); // right edge 30 <= 40 stub(screen.getByTestId("chip1"), { offsetLeft: 36, offsetWidth: 30 }); // right edge 66 > 40 stub(screen.getByTestId("pill"), { offsetLeft: 72, offsetWidth: 20 }); act(() => { rerender(); }); expect(lastGeom).toEqual({ avail: 40, chipRights: [30, 66], pillW: 20, gap: 6 }); // Only chip0 (right edge 30) fits the 40-wide lane; chip1 (66) is dropped by the policy. expect(screen.getByTestId("visible").textContent).toBe("1"); }); });