From ce1e3463f4c0f1811459bde83bf3101163a258ba Mon Sep 17 00:00:00 2001 From: Shaurya Singh Date: Thu, 4 Jun 2026 16:10:14 -0700 Subject: [PATCH 1/2] fix(table-core): clamp column size to minSize/maxSize during resize drag The getResizeHandler drag loop computed new column widths without ever checking the column definition's minSize or maxSize constraints. Dragging a resize handle far left or right could push a column outside the bounds declared in columnDef, making the constraints ineffective at runtime even though column_getSize (used for committed state reads) does apply the clamp. Fix by capturing each leaf column's minSize/maxSize at drag-start time into a closure map (columnSizingConstraints), then applying Math.min(maxSize, Math.max(minSize, rawSize)) before writing into newColumnSizing. Two new tests confirm the bounds are respected for both directions. --- .../columnResizingFeature.utils.ts | 27 ++++- .../columnResizingFeature.utils.test.ts | 112 ++++++++++++++++++ 2 files changed, 133 insertions(+), 6 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 75c278c23c..b195a9a46d 100644 --- a/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts +++ b/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts @@ -1,5 +1,6 @@ import { column_getSize, + getDefaultColumnSizingColumnDef, header_getSize, table_setColumnSizing, } from '../column-sizing/columnSizingFeature.utils' @@ -119,6 +120,18 @@ export function header_getResizeHandler< column_getSize(leafHeader.column), ]) + const defaultSizes = getDefaultColumnSizingColumnDef() + const columnSizingConstraints: Record< + string, + { minSize: number; maxSize: number } + > = {} + header.getLeafHeaders().forEach((leafHeader) => { + columnSizingConstraints[leafHeader.column.id] = { + minSize: leafHeader.column.columnDef.minSize ?? defaultSizes.minSize, + maxSize: leafHeader.column.columnDef.maxSize ?? defaultSizes.maxSize, + } + }) + const clientX = isTouchStartEvent(event) ? Math.round(event.touches[0]!.clientX) : (event as MouseEvent).clientX @@ -142,14 +155,16 @@ export function header_getResizeHandler< ) old.columnSizingStart.forEach(([columnId, headerSize]) => { + const rawSize = + headerSize > 0 + ? headerSize + headerSize * deltaPercentage + : deltaOffset / old.columnSizingStart.length + const constraints = columnSizingConstraints[columnId] + const minSize = constraints?.minSize ?? 0 + const maxSize = constraints?.maxSize ?? Number.MAX_SAFE_INTEGER newColumnSizing[columnId] = Math.round( - Math.max( - headerSize > 0 - ? headerSize + headerSize * deltaPercentage - : deltaOffset / old.columnSizingStart.length, - 0, - ) * 100, + Math.min(maxSize, Math.max(minSize, rawSize)) * 100, ) / 100 }) 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 2e9355e0d8..88fceb01bf 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 @@ -422,6 +422,118 @@ describe('header_getResizeHandler', () => { removeEventListenerSpy.mockRestore() }) + + it('should not resize a column below its minSize', () => { + const table = generateTestTableWithData(1, { + columnResizeMode: 'onChange', + }) + + let resizingState = getDefaultColumnResizingState() + table.options.onColumnResizingChange = (updater: any) => { + resizingState = + typeof updater === 'function' ? updater(resizingState) : updater + ;(table.store.state as any).columnResizing = resizingState + } + + const sizingUpdates: Record[] = [] + table.options.onColumnSizingChange = (updater: any) => { + if (typeof updater === 'function') { + const result = updater(table.atoms.columnSizing?.get() ?? {}) + sizingUpdates.push(result) + } else { + sizingUpdates.push(updater) + } + } + + // Column starts at 100px, minSize is 50px + const constrainedColumn = { + ...table.getAllColumns()[0], + id: 'firstName', + columnDef: { enableResizing: true, minSize: 50, size: 100 }, + table, + } + const header = createTestResizeHeader(table, { + getSize: () => 100, + getLeafHeaders: () => [ + { + column: constrainedColumn, + getSize: () => 100, + subHeaders: [], + }, + ], + }) + + const handler = header_getResizeHandler(header as any, document) + handler({ type: 'mousedown', clientX: 200 }) + + // Drag far left — would put column at ~10px without the clamp + const moveEvent = new MouseEvent('mousemove', { clientX: 10 }) + document.dispatchEvent(moveEvent) + + const lastUpdate = sizingUpdates[sizingUpdates.length - 1] + expect(lastUpdate).toBeDefined() + const newSize = lastUpdate!['firstName'] + expect(newSize).toBeGreaterThanOrEqual(50) + + const upEvent = new MouseEvent('mouseup', { clientX: 10 }) + document.dispatchEvent(upEvent) + }) + + it('should not resize a column above its maxSize', () => { + const table = generateTestTableWithData(1, { + columnResizeMode: 'onChange', + }) + + let resizingState = getDefaultColumnResizingState() + table.options.onColumnResizingChange = (updater: any) => { + resizingState = + typeof updater === 'function' ? updater(resizingState) : updater + ;(table.store.state as any).columnResizing = resizingState + } + + const sizingUpdates: Record[] = [] + table.options.onColumnSizingChange = (updater: any) => { + if (typeof updater === 'function') { + const result = updater(table.atoms.columnSizing?.get() ?? {}) + sizingUpdates.push(result) + } else { + sizingUpdates.push(updater) + } + } + + // Column starts at 100px, maxSize is 150px + const constrainedColumn = { + ...table.getAllColumns()[0], + id: 'firstName', + columnDef: { enableResizing: true, maxSize: 150, size: 100 }, + table, + } + const header = createTestResizeHeader(table, { + getSize: () => 100, + getLeafHeaders: () => [ + { + column: constrainedColumn, + getSize: () => 100, + subHeaders: [], + }, + ], + }) + + const handler = header_getResizeHandler(header as any, document) + handler({ type: 'mousedown', clientX: 100 }) + + // Drag far right — would put column at ~500px without the clamp + const moveEvent = new MouseEvent('mousemove', { clientX: 600 }) + document.dispatchEvent(moveEvent) + + const lastUpdate = sizingUpdates[sizingUpdates.length - 1] + expect(lastUpdate).toBeDefined() + const newSize = lastUpdate!['firstName'] + expect(newSize).toBeLessThanOrEqual(150) + + const upEvent = new MouseEvent('mouseup', { clientX: 600 }) + document.dispatchEvent(upEvent) + }) }) describe('passiveEventSupported', () => { From 9df8c5ad28a7f20d5c3c285a874887fa9416a774 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 21:03:24 +0000 Subject: [PATCH 2/2] ci: apply automated fixes --- .../features/column-resizing/columnResizingFeature.utils.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 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 b195a9a46d..e7e7f0c9c9 100644 --- a/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts +++ b/packages/table-core/src/features/column-resizing/columnResizingFeature.utils.ts @@ -163,9 +163,8 @@ export function header_getResizeHandler< const minSize = constraints?.minSize ?? 0 const maxSize = constraints?.maxSize ?? Number.MAX_SAFE_INTEGER newColumnSizing[columnId] = - Math.round( - Math.min(maxSize, Math.max(minSize, rawSize)) * 100, - ) / 100 + Math.round(Math.min(maxSize, Math.max(minSize, rawSize)) * 100) / + 100 }) return {