Skip to content
Source
Sign inSign up

Sidebar.pointer.browser.test.tsx

104 lines4.1 KB

1import type { SensorProps } from "@dnd-kit/core";
2import { describe, expect, it, vi } from "vite-plus/test";
3import { fakeTimers } from "../../test/render";
4import { SidebarPointerSensor } from "./Sidebar.pointer";
5​
6const sensors: SidebarPointerSensor[] = [];
7​
8function pointer(type: string, values: PointerEventInit = {}) {
9 return new PointerEvent(type, {
10 bubbles: true,
11 cancelable: true,
12 pointerId: 1,
13 clientX: 10,
14 clientY: 10,
15 buttons: 1,
16 ...values,
17 });
19​
20function gesture(
21 extraOptions: Partial<ConstructorParameters<typeof SidebarPointerSensor>[0]["options"]> = {},
22) {
23 const callbacks = {
24 onStart: vi.fn(),
25 onMove: vi.fn(),
26 onEnd: vi.fn(),
27 onCancel: vi.fn(),
28 onAbort: vi.fn(),
29 onPending: vi.fn(),
30 };
31 const onFinish = vi.fn();
32 const props = {
33 active: "thread",
34 event: pointer("pointerdown"),
35 options: { distance: 6, onAttach: vi.fn(), onFinish, ...extraOptions },
36 ...callbacks,
37 } as unknown as SensorProps<ConstructorParameters<typeof SidebarPointerSensor>[0]["options"]>;
38 const sensor = new SidebarPointerSensor(props);
39 sensors.push(sensor);
40 return { sensor, onFinish, ...callbacks };
42​
43function setup() {
44 const stack = new DisposableStack();
45 stack.use(fakeTimers());
46 stack.defer(() => {
47 for (const sensor of sensors.splice(0)) sensor.cancel();
48 // Cancelled sensors keep suppressing clicks on the shared document until the next press.
49 document.dispatchEvent(pointer("pointerdown"));
50 vi.runAllTimers();
51 });
52 return stack;
54​
55describe("sidebar pointer lifecycle", () => {
56 it("drops context when a zero-button move precedes pointerup", () => {
57 using _environment = setup();
58 const onDrop = vi.fn((point: { x: number; y: number }) => point.x > 50);
59 const drag = gesture({ onMove: (point) => point.x > 50, onDrop });
60 document.dispatchEvent(pointer("pointermove", { clientY: 20 }));
61 document.dispatchEvent(pointer("pointermove", { clientX: 90, clientY: 30 }));
62​
63 // Native desktop releases can report the released button on the final move.
64 document.dispatchEvent(pointer("pointermove", { buttons: 0, clientX: 94, clientY: 32 }));
65 document.dispatchEvent(pointer("pointerup", { buttons: 0, clientX: 94, clientY: 32 }));
66 document.dispatchEvent(pointer("pointermove", { buttons: 0, clientX: 100, clientY: 40 }));
67​
68 expect(onDrop).toHaveBeenCalledExactlyOnceWith({ x: 94, y: 32 });
69 expect(drag.onCancel).toHaveBeenCalledOnce();
70 expect(drag.onEnd).not.toHaveBeenCalled();
71 expect(drag.onFinish).toHaveBeenCalledExactlyOnceWith(true);
72 });
73​
74 // regression: #22058 a press that never became a drag must keep its click
75 it("keeps the click when a zero-button move precedes pointerup before activation", () => {
76 using _environment = setup();
77 const press = gesture();
78 document.dispatchEvent(pointer("pointermove", { clientX: 11 }));
79 document.dispatchEvent(pointer("pointermove", { buttons: 0, clientX: 12 }));
80 document.dispatchEvent(pointer("pointerup", { buttons: 0, clientX: 12 }));
81 const onClick = vi.fn();
82 document.body.addEventListener("click", onClick);
83 document.body.dispatchEvent(new MouseEvent("click", { bubbles: true }));
84 document.body.removeEventListener("click", onClick);
85​
86 expect(press.onCancel).not.toHaveBeenCalled();
87 expect(onClick).toHaveBeenCalledOnce();
88 });
89​
90 // regression: #17373 a zero-button release outside a drop target must cancel, not reorder
91 it("cancels a missed sidebar release without committing a reorder", () => {
92 using _environment = setup();
93 const onDrop = vi.fn((point: { x: number; y: number }) => point.x > 50);
94 const drag = gesture({ onMove: (point) => point.x > 50, onDrop });
95 document.dispatchEvent(pointer("pointermove", { clientX: 90, clientY: 30 }));
96 document.dispatchEvent(pointer("pointermove", { buttons: 0, clientX: 30, clientY: 40 }));
97 document.dispatchEvent(pointer("pointerup", { buttons: 0, clientX: 30, clientY: 40 }));
98​
99 expect(onDrop).toHaveBeenCalledExactlyOnceWith({ x: 30, y: 40 });
100 expect(drag.onCancel).toHaveBeenCalledOnce();
101 expect(drag.onEnd).not.toHaveBeenCalled();
102 expect(drag.onFinish).toHaveBeenCalledExactlyOnceWith(true);
103 });
104});