// @vitest-environment jsdom // // The outside-press half of useDismiss. The hook subscribes its `mousedown` listener once per // open/close, which is what keeps a popover from re-subscribing on every parent render — but it // means the listener must not CAPTURE the caller's handler or the ref LIST, or it keeps using the // versions that existed when the popover opened. Both are read through refs (E27 S2); these tests // are what says so. import { useRef, useState } from "react"; import { describe, it, expect, vi, afterEach } from "vitest"; import { render, screen, fireEvent, cleanup } from "@testing-library/react"; import { useDismiss } from "./useDismiss"; afterEach(cleanup); /** A panel that stays open while the caller swaps the handler it wants called. */ function SwappableHandler({ first, second }: { first: () => void; second: () => void }) { const panel = useRef(null); const [useSecond, setUseSecond] = useState(false); useDismiss(true, useSecond ? second : first, panel); return (
panel
outside
); } /** A panel that starts out registering one element and adds a second while it is open. */ function GrowingRefs({ onClose }: { onClose: () => void }) { const panel = useRef(null); const trigger = useRef(null); const [withTrigger, setWithTrigger] = useState(false); useDismiss(true, onClose, withTrigger ? [panel, trigger] : [panel]); return (
panel
outside
); } describe("useDismiss · outside press", () => { it("closes on a press outside and stays put on a press inside", () => { const onClose = vi.fn(); render(); fireEvent.mouseDown(screen.getByText("panel")); expect(onClose).not.toHaveBeenCalled(); fireEvent.mouseDown(screen.getByTestId("outside")); expect(onClose).toHaveBeenCalledTimes(1); }); it("calls the handler the caller has NOW, not the one it had when the popover opened", () => { const first = vi.fn(); const second = vi.fn(); render(); fireEvent.click(screen.getByText("swap")); fireEvent.mouseDown(screen.getByTestId("outside")); expect(second).toHaveBeenCalledTimes(1); expect(first).not.toHaveBeenCalled(); }); it("honours a ref the caller started passing after the popover opened", () => { const onClose = vi.fn(); render(); fireEvent.mouseDown(screen.getByText("trigger")); expect(onClose).toHaveBeenCalledTimes(1); // not registered yet — outside onClose.mockClear(); fireEvent.click(screen.getByText("register trigger")); fireEvent.mouseDown(screen.getByText("trigger")); expect(onClose).not.toHaveBeenCalled(); // now it is one of ours fireEvent.mouseDown(screen.getByTestId("outside")); expect(onClose).toHaveBeenCalledTimes(1); }); });