| ... | ... | @@ -147,6 +147,48 @@ describe("sidebar pointer lifecycle", () => { |
| 147 | 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 | 192 | it("resumes reordering when a context drag returns to the sidebar", () => { |
| 151 | 193 | const drag = gesture({ |
| 152 | 194 | onMove: (point) => point.x > 50, |