Open History to see the last commit
1import { expect, it, vi } from "vite-plus/test";
2import { page, userEvent } from "vite-plus/test/browser";
3
4import "~/index.css";
5import { act, fakeTimers, render } from "../../../test/render";
6import {
7 Tooltip,
8 TooltipPopup,
9 TooltipProvider,
10 TooltipScrollDismissArea,
11 TooltipTrigger,
12} from "./tooltip";
13
14it.each([
15 { focus: "mouse", keptOpen: false },
16 { focus: "keyboard", keptOpen: true },
17])("scrolling with a $focus-focused hovered trigger keeps it open: $keptOpen", async (c) => {
18 const onScroll = vi.fn();
19 await using screen = await render(
20 <TooltipScrollDismissArea
21 data-testid="timeline"
22 onScrollCapture={onScroll}
23 className="h-32 overflow-y-auto"
24 >
25 <Tooltip>
26 <TooltipTrigger>Timeline action</TooltipTrigger>
27 <TooltipPopup>Description</TooltipPopup>
28 </Tooltip>
29 <div className="h-96" />
30 </TooltipScrollDismissArea>,
31 );
32 const trigger = screen.getByRole("button", { name: "Timeline action" });
33 if (c.focus === "mouse") {
34 // A press closes the tooltip; hovering again reopens it with mouse focus kept.
35 await userEvent.click(trigger);
36 await userEvent.unhover(trigger);
37 } else {
38 await userEvent.keyboard("{Tab}");
39 }
40 await userEvent.hover(trigger);
41 await expect.element(trigger).toHaveFocus();
42 await expect.element(page.getByText("Description")).toBeVisible();
43
44 screen.getByTestId("timeline").element().scrollTop = 20;
45 await vi.waitFor(() => expect(onScroll).toHaveBeenCalledOnce());
46
47 if (c.keptOpen) await expect.element(page.getByText("Description")).toBeVisible();
48 else await expect.element(page.getByText("Description")).not.toBeInTheDocument();
49});
50
51it("blocks sidebar hover cards until scrolling settles", async () => {
52 const onOpenChange = vi.fn();
53 await using screen = await render(
54 <TooltipScrollDismissArea suppressHoverWhileScrolling>
55 <div data-testid="viewport">
56 <TooltipProvider delay={50} closeDelay={0} timeout={400}>
57 <Tooltip>
58 <TooltipTrigger>First row</TooltipTrigger>
59 <TooltipPopup>First details</TooltipPopup>
60 </Tooltip>
61 <Tooltip onOpenChange={onOpenChange}>
62 <TooltipTrigger>Second row</TooltipTrigger>
63 <TooltipPopup>Second details</TooltipPopup>
64 </Tooltip>
65 </TooltipProvider>
66 </div>
67 </TooltipScrollDismissArea>,
68 );
69 using _timers = fakeTimers();
70 await act(() => screen.getByTestId("viewport").element().dispatchEvent(new Event("scroll")));
71 await screen.getByRole("button", { name: "Second row" }).hover();
72 await act(() => vi.advanceTimersByTime(50));
73 await expect.element(page.getByText("Second details")).not.toBeInTheDocument();
74 expect(onOpenChange.mock.calls.some(([open, details]) => open && !details.isCanceled)).toBe(
75 false,
76 );
77
78 await act(() => vi.advanceTimersByTime(200));
79 await screen.getByRole("button", { name: "First row" }).hover();
80 await screen.getByRole("button", { name: "Second row" }).hover();
81 await act(() => vi.advanceTimersByTime(600));
82 await expect.element(page.getByText("Second details")).toBeInTheDocument();
83});