From f027f12cdacfa99882b54746452246747176b79d Mon Sep 17 00:00:00 2001 From: Liang Xu Date: Sat, 7 Nov 2026 03:40:29 +0800 Subject: [PATCH 1/5] fix(table-core): end a column resize when its mouseup was missed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dragging a resize handle installs `mousemove` and `mouseup` listeners on the document. If the button release never reaches the page — switching tabs, taking a screenshot, any focus steal — the `mouseup` listener never fires: the listeners stay attached and `isResizingColumn` keeps the dragged column, so the handle follows the pointer with no button held. The touch path already handles its equivalent, where the browser sends `touchcancel` instead of `touchend`. A mouse has no such event, but the next `mousemove` reports `buttons === 0`, which is the same information: no button is down, so the gesture is over. Run the same cleanup and commit there. The existing tests that simulate a drag now pass `buttons: 1` on their `mousemove` events, which is what a real drag reports; without it they would describe a move with no button held. --- .../columnResizingFeature.utils.ts | 13 +++- .../columnResizingFeature.utils.test.ts | 77 ++++++++++++++++--- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts b/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts index 6f066fa322..97f87adda2 100644 --- a/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts +++ b/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts @@ -240,7 +240,18 @@ export function header_getResizeHandler< _contextDocument || (typeof document !== 'undefined' ? document : null) const mouseEvents = { - moveHandler: (e: MouseEvent) => onMove(e.clientX), + moveHandler: (e: MouseEvent) => { + // A resize can lose its `mouseup`: switching tabs, taking a screenshot, + // or any other focus steal stops the release from reaching the page, + // and the listeners stay on the document with the drag still active. + // The next move is the only place left to notice — no button is down, + // so the gesture it belongs to is over. + if (e.buttons === 0) { + mouseEvents.upHandler(e) + return + } + onMove(e.clientX) + }, upHandler: (e: MouseEvent) => { contextDocument?.removeEventListener( 'mousemove', diff --git a/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts b/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts index 3240f13c3a..0f1be4b2b3 100644 --- a/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts +++ b/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts @@ -328,7 +328,7 @@ describe('header_getResizeHandler', () => { handler({ type: 'mousedown', clientX: 100 }) // Simulate mouse move - const moveEvent = new MouseEvent('mousemove', { clientX: 150 }) + const moveEvent = new MouseEvent('mousemove', { clientX: 150, buttons: 1 }) document.dispatchEvent(moveEvent) expect(onColumnSizingChange).toHaveBeenCalled() @@ -379,7 +379,7 @@ describe('header_getResizeHandler', () => { const handler = header_getResizeHandler(header as any, document) handler({ type: 'mousedown', clientX: 100 }) - const moveEvent = new MouseEvent('mousemove', { clientX: 150 }) + const moveEvent = new MouseEvent('mousemove', { clientX: 150, buttons: 1 }) document.dispatchEvent(moveEvent) const lastUpdate = sizingUpdates[sizingUpdates.length - 1] @@ -426,7 +426,7 @@ describe('header_getResizeHandler', () => { const handler = header_getResizeHandler(header as any, document) handler({ type: 'mousedown', clientX: 100 }) - const moveEvent = new MouseEvent('mousemove', { clientX: 150 }) + const moveEvent = new MouseEvent('mousemove', { clientX: 150, buttons: 1 }) document.dispatchEvent(moveEvent) const lastResizing = resizingUpdates[resizingUpdates.length - 1] @@ -460,9 +460,15 @@ describe('header_getResizeHandler', () => { const handler = header_getResizeHandler(header as any, document) handler({ type: 'mousedown', clientX: 100 }) - document.dispatchEvent(new MouseEvent('mousemove', { clientX: 110 })) - document.dispatchEvent(new MouseEvent('mousemove', { clientX: 120 })) - document.dispatchEvent(new MouseEvent('mousemove', { clientX: 130 })) + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 110, buttons: 1 }), + ) + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 120, buttons: 1 }), + ) + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 130, buttons: 1 }), + ) // leading edge applies immediately; later moves wait for the frame expect(onColumnSizingChange).toHaveBeenCalledTimes(1) @@ -508,8 +514,12 @@ describe('header_getResizeHandler', () => { const handler = header_getResizeHandler(header as any, document) handler({ type: 'mousedown', clientX: 100 }) - document.dispatchEvent(new MouseEvent('mousemove', { clientX: 110 })) - document.dispatchEvent(new MouseEvent('mousemove', { clientX: 120 })) + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 110, buttons: 1 }), + ) + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 120, buttons: 1 }), + ) expect(sizingUpdates).toHaveLength(1) // mouseup before the frame fires: pending frame is canceled and the end @@ -538,7 +548,9 @@ describe('header_getResizeHandler', () => { const afterDown = notifications.length // a move writes columnResizing AND columnSizing, batched into one flush - document.dispatchEvent(new MouseEvent('mousemove', { clientX: 150 })) + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 150, buttons: 1 }), + ) const afterMove = notifications.length // drag end: 'end' commit (two writes) + resizing reset, one flush total @@ -569,7 +581,9 @@ describe('header_getResizeHandler', () => { // single move: the rAF throttle applies the first move of a frame // immediately, later moves wait for the next frame - document.dispatchEvent(new MouseEvent('mousemove', { clientX: 140 })) + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 140, buttons: 1 }), + ) expect(sizingUpdates).toHaveLength(0) // transient resize info still tracks the drag during moves @@ -660,6 +674,49 @@ describe('header_getResizeHandler', () => { removeEventListenerSpy.mockRestore() }) + + it('should end the resize when a move arrives with no button held', () => { + // A resize started with the mouse can lose its `mouseup`: switching tabs, + // taking a screenshot, or any other focus steal stops the button release + // from reaching the page. The next move reports `buttons: 0`, which is the + // only signal left that the gesture is over. + const table = makeTable(1, { + columnResizeMode: 'onChange', + }) + const sizingUpdates: Array> = [] + table.options.onColumnSizingChange = (updater) => { + sizingUpdates.push( + typeof updater === 'function' + ? updater(table.atoms.columnSizing.get()) + : updater, + ) + } + + const header = createTestResizeHeader(table) + const handler = header_getResizeHandler(header as any, document) + handler({ type: 'mousedown', clientX: 100 }) + + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 150, buttons: 1 }), + ) + expect(sizingUpdates).toHaveLength(1) + expect(table.atoms.columnResizing.get().isResizingColumn).toBe('firstName') + + // the release happened while the page was not listening, so the next move + // carries the truth: no button is down + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 150, buttons: 0 }), + ) + + expect(table.atoms.columnResizing.get().isResizingColumn).toBe(false) + + // the listeners are gone, so later moves cannot resurrect the drag + const updatesAfterEnd = sizingUpdates.length + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 400, buttons: 0 }), + ) + expect(sizingUpdates).toHaveLength(updatesAfterEnd) + }) }) describe('passiveEventSupported', () => { From 97a69641386b0ac50e58a7b32cafe5e74ff2f53b Mon Sep 17 00:00:00 2001 From: Liang Xu Date: Sat, 7 Nov 2026 03:41:12 +0800 Subject: [PATCH 2/5] chore: add changeset for the column resize fix --- .changeset/quiet-mice-resize.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/quiet-mice-resize.md diff --git a/.changeset/quiet-mice-resize.md b/.changeset/quiet-mice-resize.md new file mode 100644 index 0000000000..9f6498aec8 --- /dev/null +++ b/.changeset/quiet-mice-resize.md @@ -0,0 +1,5 @@ +--- +'@tanstack/table-core': patch +--- + +End a column resize when its `mouseup` was missed. Dragging a resize handle installs `mousemove` and `mouseup` listeners on the document, and a release that never reaches the page — switching tabs, taking a screenshot, any focus steal — left those listeners attached with the dragged column still active, so the handle followed the pointer with no button held. The next `mousemove` reports `buttons === 0`, which says the gesture is over; the resize now commits and cleans up there, matching what the touch path already does on `touchcancel`. From 39ad0bf8ce18dea9c11d479606fd3fd481573a73 Mon Sep 17 00:00:00 2001 From: Liang Xu <24490353+lx3133584@users.noreply.github.com> Date: Mon, 9 Nov 2026 15:53:48 +0800 Subject: [PATCH 3/5] fix(table-core): end a missed-mouseup resize at the last held-button position When a resize loses its mouseup, the next mousemove reports buttons: 0 and that is how the handler notices the gesture is over. It then committed the position carried by that very move, but the pointer keeps travelling after the button comes up, so the column was sized by how far it drifted rather than by where the drag ended. The end now uses the last position observed while the button was still down, which is the position a real mouseup would have reported. Normal mouseup keeps using the event's own position. --- .../columnResizingFeature.utils.ts | 15 ++++++- .../columnResizingFeature.utils.test.ts | 40 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts b/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts index 97f87adda2..28469abe06 100644 --- a/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts +++ b/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts @@ -246,12 +246,25 @@ export function header_getResizeHandler< // and the listeners stay on the document with the drag still active. // The next move is the only place left to notice — no button is down, // so the gesture it belongs to is over. + // + // That move carries a position of its own, but it is not where the drag + // ended: the pointer travelled on after the button came up. Ending at it + // would size the column by how far the pointer drifted, so end at the + // last position observed while the button was still held. if (e.buttons === 0) { - mouseEvents.upHandler(e) + mouseEvents.endAtLastHeldPosition() return } onMove(e.clientX) }, + endAtLastHeldPosition: () => { + contextDocument?.removeEventListener( + 'mousemove', + mouseEvents.moveHandler, + ) + contextDocument?.removeEventListener('mouseup', mouseEvents.upHandler) + onEnd() + }, upHandler: (e: MouseEvent) => { contextDocument?.removeEventListener( 'mousemove', diff --git a/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts b/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts index 0f1be4b2b3..7023db69b8 100644 --- a/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts +++ b/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts @@ -717,6 +717,46 @@ describe('header_getResizeHandler', () => { ) expect(sizingUpdates).toHaveLength(updatesAfterEnd) }) + + it('should commit the last held-button position when the release was missed', () => { + // The move that reveals the missed release is a move of its own: the pointer + // travelled on after the button came up, and its position says nothing about + // where the drag actually ended. The size has to come from the last move made + // while the button was still down, the same position a real `mouseup` would + // have reported. + const table = makeTable(1, { + columnResizeMode: 'onChange', + }) + const sizingUpdates: Array> = [] + table.options.onColumnSizingChange = (updater) => { + sizingUpdates.push( + typeof updater === 'function' + ? updater(table.atoms.columnSizing.get()) + : updater, + ) + } + + const header = createTestResizeHeader(table) + const handler = header_getResizeHandler(header as any, document) + handler({ type: 'mousedown', clientX: 100 }) + + // the drag itself ends here, at 150 + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 150, buttons: 1 }), + ) + const sizeAtRelease = sizingUpdates[sizingUpdates.length - 1]!['firstName'] + + // the button came up unobserved, and the pointer wandered to 400 before the + // next move reported it + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 400, buttons: 0 }), + ) + + expect(table.atoms.columnResizing.get().isResizingColumn).toBe(false) + expect(sizingUpdates[sizingUpdates.length - 1]!['firstName']).toBe( + sizeAtRelease, + ) + }) }) describe('passiveEventSupported', () => { From b1e9467cc05e6974b24608f35169b0b3ddb56744 Mon Sep 17 00:00:00 2001 From: Liang Xu <24490353+lx3133584@users.noreply.github.com> Date: Fri, 13 Nov 2026 20:49:11 +0800 Subject: [PATCH 4/5] fix(table-core): end a resize when the initiating button is released MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `buttons` is a bitmask, so a nonzero value does not mean the drag is still alive. When the button that started a resize comes up while another one stays down — the same focus steal that hides the `mouseup` — the next move reports a nonzero mask and the handle keeps following the pointer. Track the bit the gesture began with and end the resize when that bit is clear. Signed-off-by: Liang Xu <24490353+lx3133584@users.noreply.github.com> --- .changeset/quiet-mice-resize.md | 2 + .../columnResizingFeature.utils.ts | 14 +++++-- .../columnResizingFeature.utils.test.ts | 39 +++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/.changeset/quiet-mice-resize.md b/.changeset/quiet-mice-resize.md index 9f6498aec8..a08c6ea72f 100644 --- a/.changeset/quiet-mice-resize.md +++ b/.changeset/quiet-mice-resize.md @@ -3,3 +3,5 @@ --- End a column resize when its `mouseup` was missed. Dragging a resize handle installs `mousemove` and `mouseup` listeners on the document, and a release that never reaches the page — switching tabs, taking a screenshot, any focus steal — left those listeners attached with the dragged column still active, so the handle followed the pointer with no button held. The next `mousemove` reports `buttons === 0`, which says the gesture is over; the resize now commits and cleans up there, matching what the touch path already does on `touchcancel`. + +`buttons` is a bitmask, so a nonzero value alone does not prove the drag is alive: the button that started the resize can be released while a different one stays down. The gesture now ends when the initiating button's bit is clear, so a move that reports another button no longer keeps the handle attached. diff --git a/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts b/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts index 28469abe06..126a769a27 100644 --- a/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts +++ b/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts @@ -121,6 +121,14 @@ export function header_getResizeHandler< ? Math.round(event.touches[0]!.clientX) : (event as MouseEvent).clientX + // `buttons` is a bitmask, so a nonzero value does not mean this drag is + // still alive: the button that started it can be released while another one + // stays down. Remember which bit the gesture began with and require that + // same bit on every move. + const startButtonMask = isTouchStartEvent(event) + ? 0 + : 1 << (event as MouseEvent).button + const newColumnSizing: ColumnSizingState = makeObjectMap() const updateOffset = (eventType: 'move' | 'end', clientXPos?: number) => { @@ -244,14 +252,14 @@ export function header_getResizeHandler< // A resize can lose its `mouseup`: switching tabs, taking a screenshot, // or any other focus steal stops the release from reaching the page, // and the listeners stay on the document with the drag still active. - // The next move is the only place left to notice — no button is down, - // so the gesture it belongs to is over. + // The next move is the only place left to notice — the button that + // started the gesture is no longer held, so the gesture is over. // // That move carries a position of its own, but it is not where the drag // ended: the pointer travelled on after the button came up. Ending at it // would size the column by how far the pointer drifted, so end at the // last position observed while the button was still held. - if (e.buttons === 0) { + if ((e.buttons & startButtonMask) === 0) { mouseEvents.endAtLastHeldPosition() return } diff --git a/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts b/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts index 7023db69b8..efee9fd174 100644 --- a/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts +++ b/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts @@ -757,6 +757,45 @@ describe('header_getResizeHandler', () => { sizeAtRelease, ) }) + + it('should end the resize when the released button is not the one that started it', () => { + // `buttons` is a bitmask: 0 means nothing is held, but a nonzero value does + // not mean the drag is still alive. If the left button was released + // unobserved and the same move reports the right button instead, the mask is + // 2 while the gesture that started the resize is long over. + const table = makeTable(1, { + columnResizeMode: 'onChange', + }) + const sizingUpdates: Array> = [] + table.options.onColumnSizingChange = (updater) => { + sizingUpdates.push( + typeof updater === 'function' + ? updater(table.atoms.columnSizing.get()) + : updater, + ) + } + + const header = createTestResizeHeader(table) + const handler = header_getResizeHandler(header as any, document) + handler({ type: 'mousedown', clientX: 100, button: 0 }) + + // the drag itself ends here, at 150 + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 150, buttons: 1 }), + ) + const sizeAtRelease = sizingUpdates[sizingUpdates.length - 1]!['firstName'] + + // the left button came up unobserved; the pointer keeps moving with only the + // right button held + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 400, buttons: 2 }), + ) + + expect(table.atoms.columnResizing.get().isResizingColumn).toBe(false) + expect(sizingUpdates[sizingUpdates.length - 1]!['firstName']).toBe( + sizeAtRelease, + ) + }) }) describe('passiveEventSupported', () => { From 12d37cd7d904acb983abc2471605345353b67603 Mon Sep 17 00:00:00 2001 From: Liang Xu <24490353+lx3133584@users.noreply.github.com> Date: Fri, 13 Nov 2026 21:59:54 +0800 Subject: [PATCH 5/5] fix(table-core): map the initiating button to its own `buttons` bit `button` and `buttons` order the non-primary buttons differently: the middle button is `button: 1` but `buttons: 4`, and the right one is `button: 2` with `buttons: 2`. Shifting the button value gave the wrong mask for both, so a middle-button drag ended on its first move and a right-button drag ended while the button was still held. Look the bit up instead, and treat a `mousedown` without a button value as the primary one. Signed-off-by: Liang Xu <24490353+lx3133584@users.noreply.github.com> --- .changeset/quiet-mice-resize.md | 2 + .../columnResizingFeature.utils.ts | 19 +++++++- .../columnResizingFeature.utils.test.ts | 44 +++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/.changeset/quiet-mice-resize.md b/.changeset/quiet-mice-resize.md index a08c6ea72f..73745d8595 100644 --- a/.changeset/quiet-mice-resize.md +++ b/.changeset/quiet-mice-resize.md @@ -5,3 +5,5 @@ End a column resize when its `mouseup` was missed. Dragging a resize handle installs `mousemove` and `mouseup` listeners on the document, and a release that never reaches the page — switching tabs, taking a screenshot, any focus steal — left those listeners attached with the dragged column still active, so the handle followed the pointer with no button held. The next `mousemove` reports `buttons === 0`, which says the gesture is over; the resize now commits and cleans up there, matching what the touch path already does on `touchcancel`. `buttons` is a bitmask, so a nonzero value alone does not prove the drag is alive: the button that started the resize can be released while a different one stays down. The gesture now ends when the initiating button's bit is clear, so a move that reports another button no longer keeps the handle attached. + +`button` and `buttons` do not order the non-primary buttons the same way (middle is `button: 1` with `buttons: 4`, right is `button: 2` with `buttons: 2`), so the initiating button's mask is looked up rather than shifted from its value; a middle- or right-button drag now stays attached. diff --git a/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts b/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts index 126a769a27..b19e43b4bb 100644 --- a/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts +++ b/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts @@ -12,6 +12,20 @@ import type { Column_Internal } from '../../types/Column' import type { ColumnSizingState } from '../column-sizing/columnSizingFeature.types' import type { columnResizingState } from './columnResizingFeature.types' +/** + * The `buttons` bit of each `button` value. The two properties do not share an + * order: the primary button is bit 0 in both, but the middle button is + * `button: 1` with `buttons: 4` and the secondary one `button: 2` with + * `buttons: 2`, so shifting the button value maps those two the wrong way. + */ +const BUTTON_MASK: Readonly> = { + 0: 1, // primary + 1: 4, // auxiliary (middle) + 2: 2, // secondary (right) + 3: 8, // fourth (back) + 4: 16, // fifth (forward) +} + /** * Creates the default transient column resizing state. * @@ -124,10 +138,11 @@ export function header_getResizeHandler< // `buttons` is a bitmask, so a nonzero value does not mean this drag is // still alive: the button that started it can be released while another one // stays down. Remember which bit the gesture began with and require that - // same bit on every move. + // same bit on every move. A `mousedown` without a button value is the + // primary one. const startButtonMask = isTouchStartEvent(event) ? 0 - : 1 << (event as MouseEvent).button + : (BUTTON_MASK[(event as MouseEvent).button] ?? BUTTON_MASK[0]!) const newColumnSizing: ColumnSizingState = makeObjectMap() diff --git a/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts b/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts index efee9fd174..b3fb8b6e4b 100644 --- a/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts +++ b/packages/table-core/tests/unit/features/column-resizing/columnResizingFeature.utils.test.ts @@ -796,6 +796,50 @@ describe('header_getResizeHandler', () => { sizeAtRelease, ) }) + + it.each([ + ['middle', 1, 4], + ['right', 2, 2], + ])( + 'should keep resizing while a %s-button drag holds its own button', + (_name, button, mask) => { + // `button` and `buttons` order the non-primary buttons differently: the + // middle button is `button: 1` but `buttons: 4`, the right one `button: 2` + // and `buttons: 2`. A mask derived by shifting the button value would end + // a middle-button drag on its first move and a right-button drag on one + // that still holds it. + const table = makeTable(1, { + columnResizeMode: 'onChange', + }) + const sizingUpdates: Array> = [] + table.options.onColumnSizingChange = (updater) => { + sizingUpdates.push( + typeof updater === 'function' + ? updater(table.atoms.columnSizing.get()) + : updater, + ) + } + + const header = createTestResizeHeader(table) + const handler = header_getResizeHandler(header as any, document) + handler({ type: 'mousedown', clientX: 100, button }) + + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 400, buttons: mask }), + ) + expect(table.atoms.columnResizing.get().isResizingColumn).toBe( + 'firstName', + ) + // the drag is still tracking the pointer, not merely still flagged as active + expect(table.atoms.columnResizing.get().deltaOffset).toBe(300) + + // the initiating button comes up; nothing of the gesture is left + document.dispatchEvent( + new MouseEvent('mousemove', { clientX: 600, buttons: 0 }), + ) + expect(table.atoms.columnResizing.get().isResizingColumn).toBe(false) + }, + ) }) describe('passiveEventSupported', () => {