import { dataGridLayoutSignature, dataGridWidthsForLayout, distributeDataGridResizeAmount, effectiveDataGridColumnMinWidth, fitDataGridColumns, resizeDataGridColumn, type DataGridSizingColumn } from "../src/components/table/dataGridSizing"; function assertEqual(actual: T, expected: T, message: string): void { if (actual !== expected) throw new Error(`${message}: expected ${String(expected)}, got ${String(actual)}`); } function assertWidths( actual: Record, expected: Record, message: string ): void { for (const [columnId, width] of Object.entries(expected)) { assertEqual(actual[columnId], width, `${message} (${columnId})`); } } const minmaxColumn: DataGridSizingColumn = { id: "recipients", width: "minmax(320px, 1.4fr)", resizable: true, sortable: true, filterable: true }; assertEqual( effectiveDataGridColumnMinWidth(minmaxColumn), 320, "a minmax lower bound remains the resize minimum" ); const weightedColumns: DataGridSizingColumn[] = [ { id: "fixed", width: 100 }, { id: "primary", width: "minmax(100px, 1fr)", minWidth: 100, maxWidth: 500 }, { id: "secondary", width: "minmax(100px, 2fr)", minWidth: 100, maxWidth: 500 } ]; const weightedLayout = fitDataGridColumns(weightedColumns, 600); assertWidths( weightedLayout.widths, { fixed: 100, primary: 200, secondary: 300 }, "fraction tracks receive residual width by declared weight" ); assertEqual(weightedLayout.underflowWidth, 0, "fraction tracks cover the available container"); const boundedLayout = fitDataGridColumns([ { id: "fixed", width: 100 }, { id: "primary", width: "minmax(100px, 1fr)", minWidth: 100, maxWidth: 150 }, { id: "secondary", width: "minmax(100px, 2fr)", minWidth: 100, maxWidth: 200 } ], 600); assertWidths( boundedLayout.widths, { fixed: 250, primary: 150, secondary: 200 }, "real columns absorb cover space after fraction tracks reach their preferred maxima" ); assertEqual(boundedLayout.underflowWidth, 0, "cover layouts never create a synthetic filler track"); const overflowLayout = fitDataGridColumns([ { id: "fixed", width: 200 }, { id: "primary", width: "minmax(320px, 1fr)", resizable: true }, { id: "secondary", width: "minmax(260px, 1fr)", resizable: true } ], 600); assertWidths( overflowLayout.widths, { fixed: 72, primary: 320, secondary: 260 }, "ordinary fixed tracks also shrink before a cover layout overflows" ); assertEqual(overflowLayout.overflowWidth, 52, "hard minimum-width excess remains horizontal overflow"); const percentageLayout = fitDataGridColumns([ { id: "fixed", width: 100 }, { id: "primary", width: "30%", minWidth: 100 }, { id: "secondary", width: "20%", minWidth: 100 } ], 1000); assertWidths( percentageLayout.widths, { fixed: 100, primary: 540, secondary: 360 }, "percentage tracks absorb residual cover space proportionally" ); assertEqual(percentageLayout.underflowWidth, 0, "percentage layouts cover the complete container"); const overriddenLayout = fitDataGridColumns(weightedColumns, 800, {}, { primary: 450 }); assertWidths( overriddenLayout.widths, { fixed: 100, primary: 450, secondary: 250 }, "a user-resized track stays fixed while responsive peers absorb container changes" ); const overriddenOverflowLayout = fitDataGridColumns(weightedColumns, 500, {}, { primary: 450 }); assertWidths( overriddenOverflowLayout.widths, { fixed: 72, primary: 328, secondary: 100 }, "a cover layout treats a persisted user width as preferred under viewport pressure" ); assertEqual( overriddenOverflowLayout.overflowWidth, 0, "a persisted user preference does not create avoidable horizontal overflow" ); const clampedOverrideLayout = fitDataGridColumns(weightedColumns, 800, {}, { primary: 900 }, "free"); assertEqual( clampedOverrideLayout.widths.primary, 500, "free-layout user overrides remain bounded by the column maximum" ); const redistributedGrowth = distributeDataGridResizeAmount(300, [ { columnId: "bounded", startWidth: 100, minWidth: 100, maxWidth: 150, weight: 1 }, { columnId: "remaining", startWidth: 100, minWidth: 100, maxWidth: 500, weight: 1 } ]); assertWidths( redistributedGrowth.amounts, { bounded: 50, remaining: 250 }, "unused growth is redistributed after a peer reaches its maximum" ); assertEqual(redistributedGrowth.applied, 300, "bounded weighted growth applies the complete available amount"); const boundedShrink = distributeDataGridResizeAmount(-300, [ { columnId: "primary", startWidth: 300, minWidth: 200, maxWidth: 500, weight: 1 }, { columnId: "secondary", startWidth: 300, minWidth: 250, maxWidth: 500, weight: 1 } ]); assertWidths( boundedShrink.amounts, { primary: -100, secondary: -50 }, "shrink distribution stops every peer at its minimum" ); assertEqual(boundedShrink.applied, -150, "unavailable shrink remains unapplied instead of squashing columns"); const legacyFillLayout = fitDataGridColumns([ { id: "fixed", width: 100 }, { id: "fill", width: 200, minWidth: 120, maxWidth: 450, fill: true } ], 500); assertWidths( legacyFillLayout.widths, { fixed: 100, fill: 400 }, "legacy fill columns remain stable under the explicit sizing contract" ); const fixedCoverColumns: DataGridSizingColumn[] = [ { id: "first", width: 100 }, { id: "second", width: 200 }, { id: "actions", width: 80, sticky: "end" } ]; const fixedCoverLayout = fitDataGridColumns(fixedCoverColumns, 760); assertWidths( fixedCoverLayout.widths, { first: 226.67, second: 453.33, actions: 80 }, "all-fixed cover grids stretch ordinary columns proportionally" ); assertEqual(fixedCoverLayout.underflowWidth, 0, "all-fixed cover grids leave no blank track"); const softMaximumLayout = fitDataGridColumns([ { id: "first", width: 100, maxWidth: 100 }, { id: "second", width: 200, maxWidth: 200 }, { id: "actions", width: 80, sticky: "end" } ], 680); assertWidths( softMaximumLayout.widths, { first: 200, second: 400, actions: 80 }, "coverage may exceed preferred maxima instead of rendering a filler column" ); const resizeColumns: DataGridSizingColumn[] = [ { id: "first", width: 200, minWidth: 100, maxWidth: 500, resizable: true }, { id: "middle", width: 300, minWidth: 100, maxWidth: 500, resizable: true }, { id: "last", width: 300, minWidth: 100, maxWidth: 500, resizable: true }, { id: "actions", width: 100, sticky: "end" } ]; const resizeBase = { first: 200, middle: 300, last: 300, actions: 100 }; const resizedFirst = resizeDataGridColumn(resizeColumns, resizeBase, "first", 120, "cover"); assertWidths( resizedFirst.widths, { first: 320, middle: 240, last: 240, actions: 100 }, "right-side peers compensate an earlier cover-column resize" ); const resizedLast = resizeDataGridColumn(resizeColumns, resizeBase, "last", 120, "cover"); assertWidths( resizedLast.widths, { first: 140, middle: 240, last: 420, actions: 100 }, "left-side peers compensate the last resizable cover column" ); const committedLayout = fitDataGridColumns( resizeColumns, 900, {}, resizedLast.widths, "cover" ); assertWidths( committedLayout.widths, resizedLast.widths, "committing a cover resize preserves the drag layout without a mouse-up snap" ); const fixedPeerResize = resizeDataGridColumn([ { id: "editable", width: 200, minWidth: 100, maxWidth: 500, resizable: true }, { id: "fixed", width: 300 }, { id: "actions", width: 100, sticky: "end" } ], { editable: 200, fixed: 300, actions: 100 }, "editable", 80, "cover"); assertWidths( fixedPeerResize.widths, { editable: 280, fixed: 220, actions: 100 }, "non-resizable tracks may compensate layout without exposing a resize handle" ); const coverBeyondPassiveMax = resizeDataGridColumn([ { id: "first", width: 200, minWidth: 100, maxWidth: 200, resizable: true }, { id: "second", width: 300, minWidth: 100, maxWidth: 300, resizable: true }, { id: "actions", width: 100, sticky: "end" } ], { first: 200, second: 300, actions: 100 }, "first", -80, "cover"); assertWidths( coverBeyondPassiveMax.widths, { first: 120, second: 380, actions: 100 }, "cover compensation may exceed a peer's preferred maximum to retain full coverage" ); const constrainedAtPassiveMax = resizeDataGridColumn([ { id: "first", width: 200, minWidth: 100, maxWidth: 200, resizable: true }, { id: "second", width: 300, minWidth: 100, maxWidth: 300, resizable: true }, { id: "actions", width: 100, sticky: "end" } ], { first: 200, second: 300, actions: 100 }, "first", -80, "constrained"); assertWidths( constrainedAtPassiveMax.widths, { first: 200, second: 300, actions: 100 }, "constrained compensation stops when every peer is at its hard maximum" ); const initiallyExpandedColumn = resizeDataGridColumn([ { id: "first", width: 100, minWidth: 72, maxWidth: 100, resizable: true }, { id: "second", width: 200, minWidth: 72, resizable: true } ], { first: 180, second: 320 }, "first", 20, "cover"); assertEqual( initiallyExpandedColumn.widths.first, 180, "a cover-expanded active track does not snap backwards on a growth drag" ); const freeResize = resizeDataGridColumn( resizeColumns, resizeBase, "last", 120, "free" ); assertWidths( freeResize.widths, { first: 200, middle: 300, last: 420, actions: 100 }, "free resizing changes only the active track" ); const originalSignature = dataGridLayoutSignature(weightedColumns, "container", "cover"); const changedSignature = dataGridLayoutSignature( weightedColumns.map((column) => column.id === "primary" ? { ...column, maxWidth: 420 } : column), "container", "cover" ); assertEqual( dataGridWidthsForLayout(originalSignature, originalSignature, { primary: 450 })?.primary, 450, "matching layout signatures restore user widths" ); assertEqual( dataGridWidthsForLayout(originalSignature, changedSignature, { primary: 450 }), undefined, "stale layout signatures discard user widths" ); assertEqual( dataGridLayoutSignature(weightedColumns, "container", "free") === originalSignature, false, "free and cover layouts keep independent user overrides" );