| ... | @@ -147,6 +147,48 @@ describe("sidebar pointer lifecycle", () => { | ... | @@ -147,6 +147,48 @@ describe("sidebar pointer lifecycle", () => { |
| 147 | expect(inside.onCancel).not.toHaveBeenCalled(); | 147 | expect(inside.onCancel).not.toHaveBeenCalled(); |
| 148 | }); | 148 | }); |
| 149 | | 149 | |
| | 150 | it("drops context when a zero-button move precedes pointerup", () => { |
| | 151 | const onDrop = vi.fn((point: { x: number; y: number }) => point.x > 50); |
| | 152 | const drag = gesture({ onMove: (point) => point.x > 50, onDrop }); |
| | 153 | document.dispatchEvent(pointer("pointermove", { clientY: 20 })); |
| | 154 | document.dispatchEvent(pointer("pointermove", { clientX: 90, clientY: 30 })); |
| | 155 | |
| | 156 | // Native desktop releases can report the released button on the final move. |
| | 157 | document.dispatchEvent(pointer("pointermove", { buttons: 0, clientX: 94, clientY: 32 })); |
| | 158 | document.dispatchEvent(pointer("pointerup", { buttons: 0, clientX: 94, clientY: 32 })); |
| | 159 | document.dispatchEvent(pointer("pointermove", { buttons: 0, clientX: 100, clientY: 40 })); |
| | 160 | |
| | 161 | expect(onDrop).toHaveBeenCalledExactlyOnceWith({ x: 94, y: 32 }); |
| | 162 | expect(drag.onCancel).toHaveBeenCalledOnce(); |
| | 163 | expect(drag.onEnd).not.toHaveBeenCalled(); |
| | 164 | expect(drag.onFinish).toHaveBeenCalledExactlyOnceWith(true); |
| | 165 | }); |
| | 166 | |
| | 167 | it("cancels a missed sidebar release without committing a reorder", () => { |
| | 168 | const onDrop = vi.fn((point: { x: number; y: number }) => point.x > 50); |
| | 169 | const drag = gesture({ onMove: (point) => point.x > 50, onDrop }); |
| | 170 | document.dispatchEvent(pointer("pointermove", { clientX: 90, clientY: 30 })); |
| | 171 | document.dispatchEvent(pointer("pointermove", { buttons: 0, clientX: 30, clientY: 40 })); |
| | 172 | document.dispatchEvent(pointer("pointerup", { buttons: 0, clientX: 30, clientY: 40 })); |
| | 173 | |
| | 174 | expect(onDrop).toHaveBeenCalledExactlyOnceWith({ x: 30, y: 40 }); |
| | 175 | expect(drag.onCancel).toHaveBeenCalledOnce(); |
| | 176 | expect(drag.onEnd).not.toHaveBeenCalled(); |
| | 177 | expect(drag.onFinish).toHaveBeenCalledExactlyOnceWith(true); |
| | 178 | }); |
| | 179 | |
| | 180 | it("does not drop context when the button is released before activation", () => { |
| | 181 | const onDrop = vi.fn(() => true); |
| | 182 | const drag = gesture({ onDrop }); |
| | 183 | document.dispatchEvent(pointer("pointermove", { buttons: 0, clientX: 90, clientY: 30 })); |
| | 184 | document.dispatchEvent(pointer("pointerup", { buttons: 0, clientX: 90, clientY: 30 })); |
| | 185 | |
| | 186 | expect(onDrop).not.toHaveBeenCalled(); |
| | 187 | expect(drag.onStart).not.toHaveBeenCalled(); |
| | 188 | expect(drag.onAbort).toHaveBeenCalledOnce(); |
| | 189 | expect(drag.onFinish).toHaveBeenCalledExactlyOnceWith(false); |
| | 190 | }); |
| | 191 | |
| 150 | it("resumes reordering when a context drag returns to the sidebar", () => { | 192 | it("resumes reordering when a context drag returns to the sidebar", () => { |
| 151 | const drag = gesture({ | 193 | const drag = gesture({ |
| 152 | onMove: (point) => point.x > 50, | 194 | onMove: (point) => point.x > 50, |