Skip to content
File

Blob: tests/client/components/editor/controllers/menu/navigation.dom.test.tsx

typescript73 lines
1import { act } from "react";
2import { createRoot, type Root } from "react-dom/client";
3import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
4import { useMenuNavigation } from "@/client/components/editor/controllers/menu/navigation";
5 
6interface NavigationHandle {
7 onKeyDown: (event: KeyboardEvent) => boolean;
8 selectedIndex: number;
9 setSelectedIndex: (index: number) => void;
10}
11 
12beforeEach(() => {
13 Object.defineProperty(Element.prototype, "scrollIntoView", {
14 configurable: true,
15 value: vi.fn(),
16 });
17});
18 
19afterEach(() => {
20 vi.restoreAllMocks();
21});
22 
23describe("useMenuNavigation", () => {
24 it("resets selection for a new filtered item set before Enter selects", async () => {
25 let navigation: NavigationHandle | null = null;
26 const onSelect = vi.fn();
27 
28 function Probe({ items }: { items: string[] }) {
29 navigation = useMenuNavigation({
30 items,
31 listRef: { current: null },
32 onSelect,
33 });
34 return <output>{navigation.selectedIndex}</output>;
35 }
36 
37 const host = document.createElement("div");
38 document.body.appendChild(host);
39 let root: Root | null = createRoot(host);
40 
41 try {
42 const initialItems = ["Alpha", "Beta", "Gamma"];
43 await act(async () => {
44 root!.render(<Probe items={initialItems} />);
45 });
46 
47 act(() => {
48 navigation!.setSelectedIndex(2);
49 });
50 expect(host.textContent).toBe("2");
51 
52 const filteredItems = ["Beta"];
53 await act(async () => {
54 root!.render(<Probe items={filteredItems} />);
55 });
56 
57 expect(host.textContent).toBe("0");
58 
59 act(() => {
60 navigation!.onKeyDown(new KeyboardEvent("keydown", { key: "Enter" }));
61 });
62 
63 expect(onSelect).toHaveBeenCalledWith("Beta");
64 } finally {
65 await act(async () => {
66 root?.unmount();
67 });
68 root = null;
69 host.remove();
70 }
71 });
72});