profileShare

rasmusjy / tasteprint

Read-only snapshot

No repository description.

main default branch 169 files Expires Sep 13, 2026, 9:06 AM
useModalDialog.test.tsx 1,696 bytes
1 import { useState } from "react";
2 import { render, screen, waitFor } from "@testing-library/react";
3 import userEvent from "@testing-library/user-event";
4 import { expect, it, vi } from "vitest";
5 import { useModalDialog } from "./useModalDialog";
6
7 function TestDialog({ onClose }: { onClose: () => void }) {
8 const dialogRef = useModalDialog(onClose);
9 return (
10 <section ref={dialogRef} role="dialog" aria-label="Test dialog">
11 <button type="button" data-initial-focus>First</button>
12 <button type="button">Last</button>
13 </section>
14 );
15 }
16
17 function Harness({ onClose }: { onClose: () => void }) {
18 const [open, setOpen] = useState(false);
19 return (
20 <>
21 <button type="button" onClick={() => setOpen(true)}>Open</button>
22 {open && <TestDialog onClose={() => { onClose(); setOpen(false); }} />}
23 </>
24 );
25 }
26
27 it("traps focus, closes with Escape, and restores the previous focus", async () => {
28 const user = userEvent.setup();
29 const onClose = vi.fn();
30 render(<Harness onClose={onClose} />);
31
32 const opener = screen.getByRole("button", { name: "Open" });
33 await user.click(opener);
34 const first = screen.getByRole("button", { name: "First" });
35 const last = screen.getByRole("button", { name: "Last" });
36 await waitFor(() => expect(first).toHaveFocus());
37 expect(document.body.style.overflow).toBe("hidden");
38
39 await user.tab({ shift: true });
40 expect(last).toHaveFocus();
41 await user.tab();
42 expect(first).toHaveFocus();
43
44 await user.keyboard("{Escape}");
45 expect(onClose).toHaveBeenCalledOnce();
46 expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
47 expect(opener).toHaveFocus();
48 expect(document.body.style.overflow).toBe("");
49 });
50