Skip to content
Source
Sign inSign up

useResizableWidth.browser.test.tsx

85 lines3.6 KB

1import { describe, expect, it, vi } from "vite-plus/test";
2​
3import { act, render } from "../../test/render";
4import { RESIZABLE_WIDTH_PROPERTY, useResizableWidth } from "./useResizableWidth";
5​
6const STORAGE_KEY = "test:responsive-panel-width";
7​
8function Panel({ defaultWidth }: { defaultWidth: number }) {
9 const { width, setWidth, handlers } = useResizableWidth({
10 storageKey: STORAGE_KEY,
11 defaultWidth,
12 minWidth: 160,
13 maxWidth: 560,
14 edge: "left",
15 });
16 return (
17 <>
18 <button onClick={() => setWidth(350)}>{width}</button>
19 <div role="separator" style={{ width: 8, height: 40 }} {...handlers} />
20 </>
21 );
23​
24describe("useResizableWidth", () => {
25 it("follows a responsive default until the user saves a width", async () => {
26 const previous = localStorage.getItem(STORAGE_KEY);
27 localStorage.removeItem(STORAGE_KEY);
28 try {
29 // This hook only needs browser storage and React; no navigation is involved.
30 await using screen = await render(<Panel defaultWidth={320} />, { router: false });
31 expect(screen.container.textContent).toBe("320");
32 await screen.rerender(<Panel defaultWidth={200} />);
33 expect(screen.container.textContent).toBe("200");
34​
35 await act(() => screen.container.querySelector("button")!.click());
36 await screen.rerender(<Panel defaultWidth={180} />);
37 expect(screen.container.textContent).toBe("350");
38​
39 await screen.unmount();
40 await using restored = await render(<Panel defaultWidth={200} />, { router: false });
41 expect(restored.container.textContent).toBe("350");
42 } finally {
43 if (previous === null) localStorage.removeItem(STORAGE_KEY);
44 else localStorage.setItem(STORAGE_KEY, previous);
45 }
46 });
47​
48 it("keeps the default responsive after a stationary click and persists an actual drag", async () => {
49 const previous = localStorage.getItem(STORAGE_KEY);
50 localStorage.removeItem(STORAGE_KEY);
51 try {
52 // This hook only needs browser storage and React; no navigation is involved.
53 await using screen = await render(<Panel defaultWidth={240} />, { router: false });
54 await screen.getByRole("separator").click();
55 expect(localStorage.getItem(STORAGE_KEY)).toBeNull();
56 await screen.rerender(<Panel defaultWidth={256} />);
57 expect(screen.container.textContent).toBe("256");
58​
59 const handle = screen.container.querySelector<HTMLElement>('[role="separator"]')!;
60 // Synthetic drag events have no active browser pointer to capture.
61 using _capture = vi.spyOn(handle, "setPointerCapture").mockImplementation(() => {});
62 const pointer = { bubbles: true, pointerId: 1, button: 0 };
63 await act(() => {
64 handle.dispatchEvent(new PointerEvent("pointerdown", { ...pointer, clientX: 500 }));
65 });
66 await screen.rerender(<Panel defaultWidth={280} />);
67 await act(() => {
68 handle.dispatchEvent(new PointerEvent("pointerup", { ...pointer, clientX: 500 }));
69 });
70 expect(localStorage.getItem(STORAGE_KEY)).toBeNull();
71 expect(screen.container.style.getPropertyValue(RESIZABLE_WIDTH_PROPERTY)).toBe("280px");
72​
73 await act(() => {
74 handle.dispatchEvent(new PointerEvent("pointerdown", { ...pointer, clientX: 500 }));
75 handle.dispatchEvent(new PointerEvent("pointerup", { ...pointer, clientX: 450 }));
76 });
77 expect(localStorage.getItem(STORAGE_KEY)).toBe("330");
78 await screen.rerender(<Panel defaultWidth={200} />);
79 expect(screen.container.textContent).toBe("330");
80 } finally {
81 if (previous === null) localStorage.removeItem(STORAGE_KEY);
82 else localStorage.setItem(STORAGE_KEY, previous);
83 }
84 });
85});