feat: complete shared platform UI contracts
This commit is contained in:
@@ -129,3 +129,21 @@ const externalShortcutMarkup = renderToStaticMarkup(
|
||||
);
|
||||
assertEqual(externalShortcutMarkup.includes("Target alpha"), true, "an external count shortcut synchronizes the grid query");
|
||||
assertEqual(externalShortcutMarkup.includes("Ordinary row"), false, "the synchronized query does not disagree with visible rows");
|
||||
|
||||
const fixedCoverMarkup = renderToStaticMarkup(
|
||||
<DataGrid
|
||||
id="fixed-cover-regression"
|
||||
rows={[{ id: "row", first: "First", second: "Second" }]}
|
||||
columns={[
|
||||
{ id: "first", header: "First", width: 100, value: (row) => row.first },
|
||||
{ id: "second", header: "Second", width: 200, value: (row) => row.second },
|
||||
{ id: "actions", header: "Actions", width: 72, sticky: "end", value: () => "" }
|
||||
]}
|
||||
getRowKey={(row) => row.id}
|
||||
/>
|
||||
);
|
||||
assertEqual(
|
||||
fixedCoverMarkup.includes("data-grid-buffer-cell"),
|
||||
false,
|
||||
"cover grids never render a synthetic blank column"
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
distributeDataGridResizeAmount,
|
||||
effectiveDataGridColumnMinWidth,
|
||||
fitDataGridColumns,
|
||||
resizeDataGridColumn,
|
||||
type DataGridSizingColumn
|
||||
} from "../src/components/table/dataGridSizing";
|
||||
|
||||
@@ -45,7 +46,7 @@ assertWidths(
|
||||
{ fixed: 100, primary: 200, secondary: 300 },
|
||||
"fraction tracks receive residual width by declared weight"
|
||||
);
|
||||
assertEqual(weightedLayout.bufferWidth, 0, "fraction tracks cover the available container");
|
||||
assertEqual(weightedLayout.underflowWidth, 0, "fraction tracks cover the available container");
|
||||
|
||||
const boundedLayout = fitDataGridColumns([
|
||||
{ id: "fixed", width: 100 },
|
||||
@@ -54,10 +55,10 @@ const boundedLayout = fitDataGridColumns([
|
||||
], 600);
|
||||
assertWidths(
|
||||
boundedLayout.widths,
|
||||
{ fixed: 100, primary: 150, secondary: 200 },
|
||||
"fraction tracks stop at their declared maxima"
|
||||
{ fixed: 250, primary: 150, secondary: 200 },
|
||||
"real columns absorb cover space after fraction tracks reach their preferred maxima"
|
||||
);
|
||||
assertEqual(boundedLayout.bufferWidth, 150, "space beyond column maxima becomes the synthetic buffer");
|
||||
assertEqual(boundedLayout.underflowWidth, 0, "cover layouts never create a synthetic filler track");
|
||||
|
||||
const overflowLayout = fitDataGridColumns([
|
||||
{ id: "fixed", width: 200 },
|
||||
@@ -66,10 +67,10 @@ const overflowLayout = fitDataGridColumns([
|
||||
], 600);
|
||||
assertWidths(
|
||||
overflowLayout.widths,
|
||||
{ fixed: 200, primary: 320, secondary: 260 },
|
||||
"columns never shrink below declared minima"
|
||||
{ fixed: 72, primary: 320, secondary: 260 },
|
||||
"ordinary fixed tracks also shrink before a cover layout overflows"
|
||||
);
|
||||
assertEqual(overflowLayout.overflowWidth, 180, "minimum-width excess remains horizontal overflow");
|
||||
assertEqual(overflowLayout.overflowWidth, 52, "hard minimum-width excess remains horizontal overflow");
|
||||
|
||||
const percentageLayout = fitDataGridColumns([
|
||||
{ id: "fixed", width: 100 },
|
||||
@@ -78,10 +79,10 @@ const percentageLayout = fitDataGridColumns([
|
||||
], 1000);
|
||||
assertWidths(
|
||||
percentageLayout.widths,
|
||||
{ fixed: 100, primary: 300, secondary: 200 },
|
||||
"percentage tracks retain their preferred container share"
|
||||
{ fixed: 100, primary: 540, secondary: 360 },
|
||||
"percentage tracks absorb residual cover space proportionally"
|
||||
);
|
||||
assertEqual(percentageLayout.bufferWidth, 400, "unused percentage space remains a neutral buffer");
|
||||
assertEqual(percentageLayout.underflowWidth, 0, "percentage layouts cover the complete container");
|
||||
|
||||
const overriddenLayout = fitDataGridColumns(weightedColumns, 800, {}, { primary: 450 });
|
||||
assertWidths(
|
||||
@@ -93,20 +94,20 @@ assertWidths(
|
||||
const overriddenOverflowLayout = fitDataGridColumns(weightedColumns, 500, {}, { primary: 450 });
|
||||
assertWidths(
|
||||
overriddenOverflowLayout.widths,
|
||||
{ fixed: 100, primary: 450, secondary: 100 },
|
||||
"a narrow viewport preserves the user override without squashing peers below their minima"
|
||||
{ fixed: 72, primary: 328, secondary: 100 },
|
||||
"a cover layout treats a persisted user width as preferred under viewport pressure"
|
||||
);
|
||||
assertEqual(
|
||||
overriddenOverflowLayout.overflowWidth,
|
||||
150,
|
||||
"a user override that cannot fit becomes horizontal overflow"
|
||||
0,
|
||||
"a persisted user preference does not create avoidable horizontal overflow"
|
||||
);
|
||||
|
||||
const clampedOverrideLayout = fitDataGridColumns(weightedColumns, 800, {}, { primary: 900 });
|
||||
const clampedOverrideLayout = fitDataGridColumns(weightedColumns, 800, {}, { primary: 900 }, "free");
|
||||
assertEqual(
|
||||
clampedOverrideLayout.widths.primary,
|
||||
500,
|
||||
"user overrides remain bounded by the column maximum"
|
||||
"free-layout user overrides remain bounded by the column maximum"
|
||||
);
|
||||
|
||||
const redistributedGrowth = distributeDataGridResizeAmount(300, [
|
||||
@@ -141,6 +142,120 @@ assertWidths(
|
||||
"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),
|
||||
|
||||
Reference in New Issue
Block a user