diff --git a/apps/app/src/components/qr-modal/index.test.tsx b/apps/app/src/components/qr-modal/index.test.tsx new file mode 100644 index 0000000..0a43114 --- /dev/null +++ b/apps/app/src/components/qr-modal/index.test.tsx @@ -0,0 +1,32 @@ +import { afterEach, expect, it, vi } from "vitest"; +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import "@testing-library/jest-dom/vitest"; +import { QrModal } from "./index"; +afterEach(() => { cleanup(); vi.unstubAllGlobals(); }); +function renderDialog() { + vi.stubGlobal("fetch", vi.fn(() => Promise.resolve(new Response("{}")))); + const onClose = vi.fn(); + const view = render(); + const heading = screen.getByText("Scan to Pick a Visualization"); + const panel = heading.parentElement!.parentElement!; + const foreground = panel.parentElement!; + return { ...view, panel, foreground, onClose }; +} +it("dismisses when clicking the foreground outside the panel", () => { + const { foreground, onClose } = renderDialog(); + fireEvent.click(foreground); + expect(onClose).toHaveBeenCalledExactlyOnceWith(); +}); +it("keeps clicks inside the panel open", () => { + const { panel, onClose } = renderDialog(); + fireEvent.click(panel); + expect(onClose).not.toHaveBeenCalled(); +}); +it("dismisses with Escape and removes the listener when closed", () => { + const { rerender, onClose } = renderDialog(); + fireEvent.keyDown(document, { key: "Escape" }); + expect(onClose).toHaveBeenCalledExactlyOnceWith(); + rerender(); + fireEvent.keyDown(document, { key: "Escape" }); + expect(onClose).toHaveBeenCalledTimes(1); +}); diff --git a/apps/app/src/components/qr-modal/index.tsx b/apps/app/src/components/qr-modal/index.tsx index 756f037..37467a3 100644 --- a/apps/app/src/components/qr-modal/index.tsx +++ b/apps/app/src/components/qr-modal/index.tsx @@ -47,6 +47,15 @@ export function QrModal({ }) { const pickUrl = usePickUrl(sessionId); + useEffect(() => { + if (!isOpen) return; + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") onClose(); + }; + document.addEventListener("keydown", handleKeyDown); + return () => document.removeEventListener("keydown", handleKeyDown); + }, [isOpen, onClose]); + if (!isOpen) return null; return ( @@ -65,6 +74,9 @@ export function QrModal({ {/* Modal */}
{ + if (event.target === event.currentTarget) onClose(); + }} style={{ position: "fixed", inset: 0,