@opencorestack/opengridx 2.1.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +399 -0
- package/README.md +95 -72
- package/dist/index.d.ts +590 -131
- package/dist/opengridx.css +1 -1
- package/dist/opengridx.es.js +7891 -5631
- package/dist/opengridx.es.js.map +1 -1
- package/dist/opengridx.umd.js +17 -23
- package/dist/opengridx.umd.js.map +1 -1
- package/docs/API_REFERENCE.md +348 -166
- package/docs/README.md +6 -1
- package/docs/architecture/datagrid-orchestration.md +81 -0
- package/docs/architecture/grid-row-meta.md +102 -0
- package/docs/architecture/use-grid-columns.md +128 -0
- package/docs/architecture/use-grid-controlled-state.md +157 -0
- package/docs/architecture/use-grid-row-pipeline.md +120 -0
- package/docs/architecture/use-grid-scroll-sync.md +111 -0
- package/docs/architecture/use-grid-state-snapshot.md +90 -0
- package/docs/architecture/use-grid-virtualization.md +115 -0
- package/docs/architecture/use-grid-visible-rows.md +85 -0
- package/docs/assets/banner-alt.png +0 -0
- package/docs/assets/banner.png +0 -0
- package/docs/assets/hero.png +0 -0
- package/docs/assets/logo.png +0 -0
- package/docs/components/aggregation-footer.md +15 -10
- package/docs/components/cell.md +16 -6
- package/docs/components/column-group-header.md +55 -28
- package/docs/components/column-resize.md +17 -13
- package/docs/components/column-visibility.md +6 -7
- package/docs/components/datagrid.md +15 -13
- package/docs/components/empty-state.md +2 -1
- package/docs/components/error-overlay.md +12 -7
- package/docs/components/filter-panel.md +28 -8
- package/docs/components/header.md +28 -9
- package/docs/components/pagination.md +14 -8
- package/docs/components/row.md +21 -5
- package/docs/components/toolbar.md +27 -9
- package/docs/components/tooltip.md +16 -7
- package/docs/customization/slots-api.md +90 -38
- package/docs/customization/theming.md +66 -18
- package/docs/features/aggregation-pivot.md +75 -9
- package/docs/features/cell-spanning.md +80 -125
- package/docs/features/clipboard.md +41 -16
- package/docs/features/custom-pagination.md +38 -18
- package/docs/features/data-source.md +36 -13
- package/docs/features/editing-reordering.md +125 -16
- package/docs/features/export-guide.md +126 -39
- package/docs/features/filtering.md +44 -6
- package/docs/features/infinite-scroll.md +54 -46
- package/docs/features/keyboard-navigation.md +83 -0
- package/docs/features/list-view.md +58 -5
- package/docs/features/loading-states.md +18 -20
- package/docs/features/master-detail.md +18 -5
- package/docs/features/pdf-export.md +72 -19
- package/docs/features/pinning.md +24 -5
- package/docs/features/selection.md +50 -10
- package/docs/features/sorting-pagination.md +64 -14
- package/docs/features/state-persistence.md +78 -10
- package/docs/features/toolbar-customization.md +26 -17
- package/docs/features/tree-data-grouping.md +36 -8
- package/docs/features/virtualization.md +37 -14
- package/docs/migration/v1-to-v2.md +115 -0
- package/docs/migration/v2-to-v3.md +1233 -0
- package/docs/performance.md +124 -0
- package/docs/roadmap.md +5 -5
- package/lib/components/Cell/Cell.css +17 -1
- package/lib/components/Cell/Cell.tsx +129 -57
- package/lib/components/Cell/CellErrorBoundary.tsx +12 -6
- package/lib/components/Cell/GridEditInputCell.tsx +114 -41
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.css +7 -1
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.css +15 -2
- package/lib/components/ColumnResizeHandle/ColumnResizeHandle.tsx +104 -38
- package/lib/components/ColumnResizeHandle/clampResizeWidth.ts +13 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.css +6 -0
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.tsx +21 -13
- package/lib/components/DataGrid/DataGrid.css +85 -12
- package/lib/components/DataGrid/DataGrid.tsx +582 -876
- package/lib/components/DataGrid/GridAggregationFooter.tsx +53 -17
- package/lib/components/DataGrid/GridErrorOverlay.tsx +15 -12
- package/lib/components/DataGrid/GridListView.tsx +165 -24
- package/lib/components/DataGrid/GridLiveRegion.tsx +31 -0
- package/lib/components/DataGrid/GridLoadingOverlay.tsx +27 -0
- package/lib/components/DataGrid/GridPaginationArea.tsx +44 -0
- package/lib/components/DataGrid/GridPinnedRows.tsx +62 -11
- package/lib/components/DataGrid/GridStandaloneColumnPanel.tsx +40 -38
- package/lib/components/DataGrid/GridToolbarSlot.tsx +51 -0
- package/lib/components/DataGrid/GridVirtualRows.tsx +40 -24
- package/lib/components/DetailPanel/DetailPanel.css +0 -6
- package/lib/components/DetailPanel/DetailPanel.tsx +34 -5
- package/lib/components/DetailPanel/DetailPanelContent.tsx +17 -0
- package/lib/components/FilterPanel/FilterPanel.css +13 -2
- package/lib/components/FilterPanel/FilterPanel.tsx +272 -99
- package/lib/components/Header/ColumnMenu.tsx +91 -39
- package/lib/components/Header/Header.css +6 -3
- package/lib/components/Header/Header.tsx +181 -160
- package/lib/components/ListView/ListView.css +25 -0
- package/lib/components/ListView/ListViewRow.tsx +76 -10
- package/lib/components/Pagination/Pagination.tsx +15 -7
- package/lib/components/Row/Row.tsx +177 -77
- package/lib/components/Toolbar/GlobalSearch.css +6 -3
- package/lib/components/Toolbar/GlobalSearch.tsx +26 -27
- package/lib/components/Toolbar/GridToolbar.css +12 -11
- package/lib/components/Toolbar/GridToolbar.tsx +133 -89
- package/lib/components/Toolbar/PivotPanel.tsx +89 -49
- package/lib/components/Tooltip/Tooltip.css +20 -0
- package/lib/components/Tooltip/Tooltip.tsx +68 -35
- package/lib/components/ui/Button.tsx +3 -0
- package/lib/components/ui/Checkbox.tsx +2 -1
- package/lib/components/ui/ExpandIcon.css +0 -9
- package/lib/components/ui/ExpandIcon.tsx +4 -1
- package/lib/hooks/core/gridApiPlaceholder.ts +34 -0
- package/lib/hooks/core/gridToolbarHostContext.ts +14 -0
- package/lib/hooks/core/useDataGrid.ts +115 -173
- package/lib/hooks/core/useGridAggregationApi.ts +72 -0
- package/lib/hooks/core/useGridApiMethods.ts +115 -0
- package/lib/hooks/core/useGridApiRef.ts +19 -4
- package/lib/hooks/core/useGridApiRefBinding.ts +18 -0
- package/lib/hooks/core/useGridAriaRows.ts +38 -0
- package/lib/hooks/core/useGridClipboardApi.ts +38 -0
- package/lib/hooks/core/useGridColumnLookup.ts +30 -0
- package/lib/hooks/core/useGridColumns.tsx +279 -76
- package/lib/hooks/core/useGridColumnsPanel.ts +77 -0
- package/lib/hooks/core/useGridControlledState.ts +99 -6
- package/lib/hooks/core/useGridDetailPanel.ts +51 -0
- package/lib/hooks/core/useGridDevWarnings.ts +104 -5
- package/lib/hooks/core/useGridFocusHandlers.ts +50 -0
- package/lib/hooks/core/useGridGroupingColumn.ts +62 -0
- package/lib/hooks/core/useGridHeaderHandlers.ts +92 -0
- package/lib/hooks/core/useGridHierarchy.ts +81 -0
- package/lib/hooks/core/useGridKeyboardNavigation.ts +447 -243
- package/lib/hooks/core/useGridLiveRowSelection.ts +67 -0
- package/lib/hooks/core/useGridPageCorrection.ts +55 -0
- package/lib/hooks/core/useGridRowIdOf.ts +32 -0
- package/lib/hooks/core/useGridRowInteractions.ts +81 -0
- package/lib/hooks/core/useGridRowPipeline.ts +69 -39
- package/lib/hooks/core/useGridRowSelection.ts +91 -0
- package/lib/hooks/core/useGridScrollSync.ts +80 -32
- package/lib/hooks/core/useGridScrollToIndexesApi.ts +45 -0
- package/lib/hooks/core/useGridStateSnapshot.ts +40 -5
- package/lib/hooks/core/useGridThemeDimensions.ts +49 -0
- package/lib/hooks/core/useGridToolbarProps.ts +97 -0
- package/lib/hooks/core/useGridViewport.ts +38 -0
- package/lib/hooks/core/useGridViewportSize.ts +26 -0
- package/lib/hooks/core/useGridVisibleRows.ts +17 -5
- package/lib/hooks/core/useLayout.ts +215 -195
- package/lib/hooks/core/useRowsScrollEndLatch.ts +41 -0
- package/lib/hooks/core/useStableColumns.ts +34 -0
- package/lib/hooks/features/useAggregation.ts +82 -51
- package/lib/hooks/features/useColumnGroupReorderGuard.ts +34 -0
- package/lib/hooks/features/useDetailPanelHeights.ts +29 -0
- package/lib/hooks/features/useGridClipboard.ts +133 -73
- package/lib/hooks/features/useGridDataSource.ts +354 -128
- package/lib/hooks/features/useGridEditing.ts +211 -51
- package/lib/hooks/features/useGridPivot.ts +92 -0
- package/lib/hooks/features/useGridSpanRenderWindow.ts +104 -0
- package/lib/hooks/features/useGridSpanning.ts +410 -306
- package/lib/hooks/features/usePivot.ts +0 -0
- package/lib/hooks/features/useServerTreeChildren.ts +50 -0
- package/lib/hooks/useColumnReorder.ts +38 -44
- package/lib/hooks/useRowGrouping.ts +298 -160
- package/lib/hooks/useRowReorder.ts +62 -31
- package/lib/hooks/useTreeData.ts +287 -220
- package/lib/index.ts +28 -2
- package/lib/state/index.ts +1 -0
- package/lib/state/types.ts +7 -1
- package/lib/state/useGridStateStorage.ts +88 -29
- package/lib/theme/DataGridThemeProvider.tsx +143 -38
- package/lib/theme/gridThemeContext.ts +5 -0
- package/lib/theme/index.ts +5 -0
- package/lib/theme/presets.ts +5 -4
- package/lib/theme/types.ts +40 -2
- package/lib/types/index.ts +313 -116
- package/lib/utils/aggregation/index.ts +119 -24
- package/lib/utils/aria/index.ts +40 -0
- package/lib/utils/columnGroups.ts +110 -0
- package/lib/utils/columnLookup.ts +33 -0
- package/lib/utils/columnWidth.ts +22 -0
- package/lib/utils/dataSource/index.ts +46 -0
- package/lib/utils/detailPanel.ts +66 -0
- package/lib/utils/dragData.ts +44 -0
- package/lib/utils/editing/index.ts +30 -0
- package/lib/utils/export/exportShared.ts +197 -0
- package/lib/utils/export/exportToExcelAdvanced.ts +278 -139
- package/lib/utils/export/exportToPdf.ts +143 -92
- package/lib/utils/export/groupLabel.ts +14 -0
- package/lib/utils/export/index.ts +278 -272
- package/lib/utils/export/pdfText.ts +50 -0
- package/lib/utils/filtering/index.ts +277 -148
- package/lib/utils/focus/index.ts +115 -0
- package/lib/utils/gridApiRows.ts +35 -0
- package/lib/utils/gridModes.ts +77 -0
- package/lib/utils/grouping/groupedExportRows.ts +91 -0
- package/lib/utils/pagination/index.ts +56 -0
- package/lib/utils/pinning/index.ts +77 -33
- package/lib/utils/pivot/index.ts +368 -0
- package/lib/utils/scroll/index.ts +55 -5
- package/lib/utils/scroll/scrollEnd.ts +18 -0
- package/lib/utils/sorting/index.ts +154 -30
- package/lib/utils/syntheticRows.ts +12 -0
- package/lib/utils/values.ts +206 -0
- package/lib/utils/viewport.ts +14 -0
- package/llms.txt +261 -164
- package/package.json +13 -5
- package/lib/components/ColumnGroupHeader/ColumnGroupHeader.tsx +0 -220
- package/lib/components/ColumnVisibilityPanel/ColumnVisibilityPanel.test.tsx +0 -85
- package/lib/components/DataGrid/DataGrid.defects.test.tsx +0 -103
- package/lib/components/DataGrid/DataGrid.regression.test.tsx +0 -132
- package/lib/components/DataGrid/DataGrid.smoke.test.tsx +0 -58
- package/lib/components/DataGrid/DataGrid.v2.test.tsx +0 -127
- package/lib/components/DataGrid/DataGrid.virtualization.browser.test.tsx +0 -183
- package/lib/components/Header/Header.test.tsx +0 -135
- package/lib/components/Pagination/Pagination.test.tsx +0 -64
- package/lib/hooks/core/useDataGrid.regression.test.ts +0 -66
- package/lib/hooks/core/useGridControlledState.test.ts +0 -86
- package/lib/hooks/core/useGridKeyboardNavigation.test.ts +0 -176
- package/lib/hooks/core/useGridRowPipeline.test.ts +0 -117
- package/lib/hooks/features/useAggregation.test.ts +0 -175
- package/lib/hooks/features/usePivot.test.ts +0 -202
- package/lib/hooks/useRowGrouping.test.ts +0 -298
- package/lib/hooks/useTreeData.test.ts +0 -83
- package/lib/utils/aggregation/aggregation.test.ts +0 -16
- package/lib/utils/export/exportToExcelAdvanced.test.ts +0 -109
- package/lib/utils/export/exportToPdf.test.ts +0 -120
- package/lib/utils/export/groupedExport.test.ts +0 -144
- package/lib/utils/filtering/filtering.test.ts +0 -178
- package/lib/utils/sorting/sorting.test.ts +0 -88
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opencorestack/opengridx",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "OpenGridX: High-performance React data infrastructure. Unlock advanced Row Grouping, Excel Export, and Column Pinning without the usual \"Pro\" gatekeeping. Built for speed, scale, and complete architectural freedom. Fully open, virtualization-ready, and feature-complete.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/opengridx.umd.js",
|
|
@@ -20,11 +20,18 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"dist",
|
|
22
22
|
"lib",
|
|
23
|
+
"!lib/**/*.test.ts",
|
|
24
|
+
"!lib/**/*.test.tsx",
|
|
25
|
+
"!lib/**/__screenshots__",
|
|
26
|
+
"docs/architecture",
|
|
27
|
+
"docs/assets",
|
|
23
28
|
"docs/components",
|
|
24
29
|
"docs/customization",
|
|
25
30
|
"docs/features",
|
|
31
|
+
"docs/migration",
|
|
26
32
|
"docs/API_REFERENCE.md",
|
|
27
33
|
"docs/README.md",
|
|
34
|
+
"docs/performance.md",
|
|
28
35
|
"docs/roadmap.md",
|
|
29
36
|
"README.md",
|
|
30
37
|
"LICENSE",
|
|
@@ -38,8 +45,9 @@
|
|
|
38
45
|
"scripts": {
|
|
39
46
|
"dev": "vite",
|
|
40
47
|
"build": "tsc --project tsconfig.build.json && vite build",
|
|
41
|
-
"build:lib": "tsc --project tsconfig.build.json && vite build --config vite.config.lib.js && cp lib/styles/opengridx.css.d.ts dist/opengridx.css.d.ts",
|
|
48
|
+
"build:lib": "tsc --project tsconfig.build.json && vite build --config vite.config.lib.js && cp lib/styles/opengridx.css.d.ts dist/opengridx.css.d.ts && node scripts/check-bundle.mjs",
|
|
42
49
|
"lint": "eslint .",
|
|
50
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
43
51
|
"preview": "vite preview",
|
|
44
52
|
"test": "vitest run --project unit",
|
|
45
53
|
"test:browser": "vitest run --project browser",
|
|
@@ -105,8 +113,8 @@
|
|
|
105
113
|
"@types/react": "^19.2.5",
|
|
106
114
|
"@types/react-dom": "^19.2.3",
|
|
107
115
|
"@vitejs/plugin-react": "^5.1.1",
|
|
108
|
-
"@vitest/browser-playwright": "^4.
|
|
109
|
-
"@vitest/coverage-v8": "^4.
|
|
116
|
+
"@vitest/browser-playwright": "^4.1.11",
|
|
117
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
110
118
|
"eslint": "^9.39.1",
|
|
111
119
|
"eslint-plugin-react-hooks": "^7.0.1",
|
|
112
120
|
"eslint-plugin-react-refresh": "^0.4.24",
|
|
@@ -122,6 +130,6 @@
|
|
|
122
130
|
"typescript-eslint": "^8.65.0",
|
|
123
131
|
"vite": "^7.2.4",
|
|
124
132
|
"vite-plugin-dts": "^4.5.4",
|
|
125
|
-
"vitest": "^4.
|
|
133
|
+
"vitest": "^4.1.11"
|
|
126
134
|
}
|
|
127
135
|
}
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import type { GridColumnGroup, GridColumnGroupingModel, GridColDef, GridRowModel } from '../../types';
|
|
2
|
-
import './ColumnGroupHeader.css';
|
|
3
|
-
|
|
4
|
-
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
5
|
-
|
|
6
|
-
/** All leaf field names reachable from a group node (recursive). */
|
|
7
|
-
function collectFields(group: GridColumnGroup): string[] {
|
|
8
|
-
const fields: string[] = [];
|
|
9
|
-
for (const child of group.children) {
|
|
10
|
-
if (typeof child === 'string') {
|
|
11
|
-
fields.push(child);
|
|
12
|
-
} else {
|
|
13
|
-
fields.push(...collectFields(child));
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return fields;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** Maximum nesting depth of any group tree. */
|
|
20
|
-
function maxDepth(groups: GridColumnGroupingModel): number {
|
|
21
|
-
let d = 0;
|
|
22
|
-
function walk(g: GridColumnGroup, depth: number) {
|
|
23
|
-
d = Math.max(d, depth);
|
|
24
|
-
for (const child of g.children) {
|
|
25
|
-
if (typeof child !== 'string') walk(child, depth + 1);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
groups.forEach(g => walk(g, 1));
|
|
29
|
-
return d;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Flattened cell descriptor for one row of group headers.
|
|
34
|
-
* `span` is the pixel width, `level` is 0-indexed depth.
|
|
35
|
-
*/
|
|
36
|
-
interface GroupCell {
|
|
37
|
-
key: string;
|
|
38
|
-
label: string;
|
|
39
|
-
width: number; // total pixel width spanned
|
|
40
|
-
isGroup: boolean; // false → empty "filler" cell for ungrouped columns
|
|
41
|
-
groupId?: string;
|
|
42
|
-
headerClassName?: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Build one row of GroupCell descriptors for a given depth level. */
|
|
46
|
-
function buildRow(
|
|
47
|
-
groups: GridColumnGroupingModel,
|
|
48
|
-
leafColumns: string[],
|
|
49
|
-
columnWidths: Record<string, number>,
|
|
50
|
-
level: number,
|
|
51
|
-
): GroupCell[] {
|
|
52
|
-
const cells: GroupCell[] = [];
|
|
53
|
-
|
|
54
|
-
// Assign each leaf column to the group (at `level`) that contains it, if any.
|
|
55
|
-
// Consecutive columns in the same group merge into one cell.
|
|
56
|
-
|
|
57
|
-
// Build a map: field → group-at-this-level
|
|
58
|
-
const fieldToGroup = new Map<string, GridColumnGroup | null>();
|
|
59
|
-
|
|
60
|
-
function findGroupAtLevel(
|
|
61
|
-
groups: GridColumnGroupingModel,
|
|
62
|
-
field: string,
|
|
63
|
-
currentLevel: number,
|
|
64
|
-
): GridColumnGroup | null {
|
|
65
|
-
for (const g of groups) {
|
|
66
|
-
const leafFields = collectFields(g);
|
|
67
|
-
if (!leafFields.includes(field)) continue;
|
|
68
|
-
if (currentLevel === level) return g;
|
|
69
|
-
// Go deeper
|
|
70
|
-
for (const child of g.children) {
|
|
71
|
-
if (typeof child !== 'string') {
|
|
72
|
-
const found = findGroupAtLevel([child], field, currentLevel + 1);
|
|
73
|
-
if (found) return found;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
for (const f of leafColumns) {
|
|
82
|
-
fieldToGroup.set(f, findGroupAtLevel(groups, f, 0));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Merge consecutive columns belonging to the same group
|
|
86
|
-
let i = 0;
|
|
87
|
-
while (i < leafColumns.length) {
|
|
88
|
-
const field = leafColumns[i];
|
|
89
|
-
const group = fieldToGroup.get(field) ?? null;
|
|
90
|
-
|
|
91
|
-
if (!group) {
|
|
92
|
-
// Ungrouped column — filler cell spanning just this column
|
|
93
|
-
cells.push({
|
|
94
|
-
key: `filler-${field}-${level}`,
|
|
95
|
-
label: '',
|
|
96
|
-
width: columnWidths[field] ?? 100,
|
|
97
|
-
isGroup: false,
|
|
98
|
-
});
|
|
99
|
-
i++;
|
|
100
|
-
} else {
|
|
101
|
-
// Collect all consecutive columns that belong to the same group instance
|
|
102
|
-
let totalWidth = 0;
|
|
103
|
-
let j = i;
|
|
104
|
-
while (j < leafColumns.length && fieldToGroup.get(leafColumns[j]) === group) {
|
|
105
|
-
totalWidth += columnWidths[leafColumns[j]] ?? 100;
|
|
106
|
-
j++;
|
|
107
|
-
}
|
|
108
|
-
cells.push({
|
|
109
|
-
key: `${group.groupId}-${level}`,
|
|
110
|
-
label: group.headerName,
|
|
111
|
-
width: totalWidth,
|
|
112
|
-
isGroup: true,
|
|
113
|
-
groupId: group.groupId,
|
|
114
|
-
headerClassName: group.headerClassName,
|
|
115
|
-
});
|
|
116
|
-
i = j;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return cells;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// ─── Component ────────────────────────────────────────────────────────────────
|
|
124
|
-
|
|
125
|
-
export interface ColumnGroupHeaderProps<R extends GridRowModel = GridRowModel> {
|
|
126
|
-
columnGroupingModel: GridColumnGroupingModel;
|
|
127
|
-
/** The ordered, effective columns (already filtered for visibility). */
|
|
128
|
-
columns: GridColDef<R>[];
|
|
129
|
-
/** Resolved pixel widths for every column. */
|
|
130
|
-
columnWidths: Record<string, number>;
|
|
131
|
-
/** Whether a checkbox column is prepended. */
|
|
132
|
-
checkboxSelection?: boolean;
|
|
133
|
-
/** Whether a row-reorder handle column is prepended. */
|
|
134
|
-
rowReordering?: boolean;
|
|
135
|
-
/** Whether a detail-panel expand column is prepended. */
|
|
136
|
-
hasDetailPanel?: boolean;
|
|
137
|
-
/** height (px) of each group header row */
|
|
138
|
-
groupHeaderHeight?: number;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export function ColumnGroupHeader<R extends GridRowModel = GridRowModel>({
|
|
142
|
-
columnGroupingModel,
|
|
143
|
-
columns,
|
|
144
|
-
columnWidths,
|
|
145
|
-
checkboxSelection = false,
|
|
146
|
-
rowReordering = false,
|
|
147
|
-
hasDetailPanel = false,
|
|
148
|
-
groupHeaderHeight = 36,
|
|
149
|
-
}: ColumnGroupHeaderProps<R>) {
|
|
150
|
-
|
|
151
|
-
const levels = maxDepth(columnGroupingModel);
|
|
152
|
-
if (levels === 0 || columns.length === 0) return null;
|
|
153
|
-
|
|
154
|
-
// Ordered leaf fields (visible columns only)
|
|
155
|
-
const leafFields = columns.map(c => c.field);
|
|
156
|
-
|
|
157
|
-
// Width of any prepended utility columns
|
|
158
|
-
const CHECKBOX_W = 52;
|
|
159
|
-
const REORDER_W = 40;
|
|
160
|
-
const EXPAND_W = 52;
|
|
161
|
-
const prefixWidth =
|
|
162
|
-
(checkboxSelection ? CHECKBOX_W : 0) +
|
|
163
|
-
(rowReordering ? REORDER_W : 0) +
|
|
164
|
-
(hasDetailPanel ? EXPAND_W : 0);
|
|
165
|
-
|
|
166
|
-
return (
|
|
167
|
-
<>
|
|
168
|
-
{Array.from({ length: levels }, (_, level) => {
|
|
169
|
-
const row = buildRow(
|
|
170
|
-
columnGroupingModel,
|
|
171
|
-
leafFields,
|
|
172
|
-
columnWidths,
|
|
173
|
-
level,
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
return (
|
|
177
|
-
<div
|
|
178
|
-
key={`group-row-${level}`}
|
|
179
|
-
className="ogx-col-group-row"
|
|
180
|
-
style={{ height: groupHeaderHeight }}
|
|
181
|
-
role="row"
|
|
182
|
-
aria-rowindex={level + 1}
|
|
183
|
-
>
|
|
184
|
-
{/* Spacer for checkbox / reorder / expand columns */}
|
|
185
|
-
{prefixWidth > 0 && (
|
|
186
|
-
<div
|
|
187
|
-
className="ogx-col-group-cell ogx-col-group-cell--filler"
|
|
188
|
-
style={{ width: prefixWidth, minWidth: prefixWidth }}
|
|
189
|
-
role="columnheader"
|
|
190
|
-
/>
|
|
191
|
-
)}
|
|
192
|
-
|
|
193
|
-
{row.map((cell) => (
|
|
194
|
-
<div
|
|
195
|
-
key={cell.key}
|
|
196
|
-
role="columnheader"
|
|
197
|
-
aria-colspan={undefined /* pixel-based layout */}
|
|
198
|
-
className={[
|
|
199
|
-
'ogx-col-group-cell',
|
|
200
|
-
cell.isGroup
|
|
201
|
-
? 'ogx-col-group-cell--group'
|
|
202
|
-
: 'ogx-col-group-cell--filler',
|
|
203
|
-
cell.headerClassName ?? '',
|
|
204
|
-
].join(' ').trim()}
|
|
205
|
-
style={{ width: cell.width, minWidth: cell.width }}
|
|
206
|
-
title={cell.isGroup ? cell.label : undefined}
|
|
207
|
-
>
|
|
208
|
-
{cell.isGroup && (
|
|
209
|
-
<span className="ogx-col-group-cell__label">
|
|
210
|
-
{cell.label}
|
|
211
|
-
</span>
|
|
212
|
-
)}
|
|
213
|
-
</div>
|
|
214
|
-
))}
|
|
215
|
-
</div>
|
|
216
|
-
);
|
|
217
|
-
})}
|
|
218
|
-
</>
|
|
219
|
-
);
|
|
220
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { render, screen } from '@testing-library/react';
|
|
3
|
-
import { ColumnVisibilityPanel } from './ColumnVisibilityPanel';
|
|
4
|
-
import type { GridColDef } from '../../types';
|
|
5
|
-
|
|
6
|
-
const columns: GridColDef[] = [
|
|
7
|
-
{ field: 'name', headerName: 'Name' },
|
|
8
|
-
{ field: 'department', headerName: 'Department' },
|
|
9
|
-
{ field: 'groupKey', headerName: 'Group Key', hideable: false },
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
const visibleColumns = new Set(['name', 'department', 'groupKey']);
|
|
13
|
-
|
|
14
|
-
const noop = () => {};
|
|
15
|
-
|
|
16
|
-
describe('ColumnVisibilityPanel', () => {
|
|
17
|
-
it('excludes hideable:false columns from the list by default', () => {
|
|
18
|
-
render(
|
|
19
|
-
<ColumnVisibilityPanel
|
|
20
|
-
columns={columns}
|
|
21
|
-
visibleColumns={visibleColumns}
|
|
22
|
-
onVisibilityChange={noop}
|
|
23
|
-
onShowAll={noop}
|
|
24
|
-
onHideAll={noop}
|
|
25
|
-
/>
|
|
26
|
-
);
|
|
27
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
28
|
-
expect(screen.getByText('Department')).toBeInTheDocument();
|
|
29
|
-
expect(screen.queryByText('Group Key')).not.toBeInTheDocument();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('shows hideable:false columns as disabled when showNonHideableColumns is true', () => {
|
|
33
|
-
render(
|
|
34
|
-
<ColumnVisibilityPanel
|
|
35
|
-
columns={columns}
|
|
36
|
-
visibleColumns={visibleColumns}
|
|
37
|
-
onVisibilityChange={noop}
|
|
38
|
-
onShowAll={noop}
|
|
39
|
-
onHideAll={noop}
|
|
40
|
-
showNonHideableColumns
|
|
41
|
-
/>
|
|
42
|
-
);
|
|
43
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
44
|
-
expect(screen.getByText('Department')).toBeInTheDocument();
|
|
45
|
-
expect(screen.getByText('Group Key')).toBeInTheDocument();
|
|
46
|
-
|
|
47
|
-
// 3 columns shown + 1 "Show/Hide All" checkbox in footer = 4 checkboxes
|
|
48
|
-
// The 3rd column (Group Key, hideable:false) must be the disabled one
|
|
49
|
-
const checkboxes = screen.getAllByRole('checkbox');
|
|
50
|
-
const disabledCheckboxes = checkboxes.filter(cb => (cb as HTMLInputElement).disabled);
|
|
51
|
-
expect(disabledCheckboxes).toHaveLength(1);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('does not render a disabled checkbox for hideable:false columns by default', () => {
|
|
55
|
-
render(
|
|
56
|
-
<ColumnVisibilityPanel
|
|
57
|
-
columns={columns}
|
|
58
|
-
visibleColumns={visibleColumns}
|
|
59
|
-
onVisibilityChange={noop}
|
|
60
|
-
onShowAll={noop}
|
|
61
|
-
onHideAll={noop}
|
|
62
|
-
/>
|
|
63
|
-
);
|
|
64
|
-
const checkboxes = screen.getAllByRole('checkbox');
|
|
65
|
-
const disabledCheckboxes = checkboxes.filter(cb => (cb as HTMLInputElement).disabled);
|
|
66
|
-
expect(disabledCheckboxes).toHaveLength(0);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('never shows hideable:false columns even when search would match them', () => {
|
|
70
|
-
// Without showNonHideableColumns, hideable:false columns are not in the base list
|
|
71
|
-
// so they cannot appear regardless of search. This test verifies the base list,
|
|
72
|
-
// not a dynamic search interaction.
|
|
73
|
-
render(
|
|
74
|
-
<ColumnVisibilityPanel
|
|
75
|
-
columns={columns}
|
|
76
|
-
visibleColumns={visibleColumns}
|
|
77
|
-
onVisibilityChange={noop}
|
|
78
|
-
onShowAll={noop}
|
|
79
|
-
onHideAll={noop}
|
|
80
|
-
/>
|
|
81
|
-
);
|
|
82
|
-
// "Group Key" is hideable:false — must not appear in the default panel
|
|
83
|
-
expect(screen.queryByText('Group Key')).not.toBeInTheDocument();
|
|
84
|
-
});
|
|
85
|
-
});
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
-
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
-
import { DataGrid } from './DataGrid';
|
|
4
|
-
import type { GridColDef } from '../../types';
|
|
5
|
-
|
|
6
|
-
// Regression tests for the OpenGridX 2.0.4 consumer defect report.
|
|
7
|
-
|
|
8
|
-
type Row = { id: number; region: string; amount: number };
|
|
9
|
-
|
|
10
|
-
const ROWS: Row[] = [
|
|
11
|
-
{ id: 1, region: 'North', amount: 1200 },
|
|
12
|
-
{ id: 2, region: 'North', amount: 800 },
|
|
13
|
-
{ id: 3, region: 'South', amount: 450 },
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
const COLUMNS: GridColDef<Row>[] = [
|
|
17
|
-
{ field: 'region', headerName: 'Region', width: 150 },
|
|
18
|
-
{ field: 'amount', headerName: 'Amount', width: 150, valueFormatter: ({ value }) => `$${String(value)}` },
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
describe('D3 — valueFormatter survives row grouping', () => {
|
|
22
|
-
it('renders formatted values without grouping', () => {
|
|
23
|
-
render(<DataGrid rows={ROWS} columns={COLUMNS} />);
|
|
24
|
-
expect(screen.getByText('$1200')).toBeInTheDocument();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('renders formatted values for leaf rows when rowGroupingModel is active', () => {
|
|
28
|
-
render(<DataGrid rows={ROWS} columns={COLUMNS} rowGroupingModel={['region']} defaultGroupingExpansionDepth={-1} />);
|
|
29
|
-
expect(screen.getByText('$1200')).toBeInTheDocument();
|
|
30
|
-
expect(screen.queryByText('1200')).not.toBeInTheDocument();
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('passes formattedValue to a consumer renderCell', () => {
|
|
34
|
-
const renderCell = vi.fn(() => null);
|
|
35
|
-
const cols: GridColDef<Row>[] = [COLUMNS[0], { ...COLUMNS[1], renderCell }];
|
|
36
|
-
render(<DataGrid rows={ROWS} columns={cols} />);
|
|
37
|
-
expect(renderCell).toHaveBeenCalledWith(expect.objectContaining({ value: 1200, formattedValue: '$1200' }));
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('D2 — slots that were typed but never rendered', () => {
|
|
42
|
-
it('renders slots.footer with grid state, replacing the default pagination', () => {
|
|
43
|
-
const Footer = vi.fn((props: Record<string, unknown>) => <div data-testid="footer">rows:{String(props.rowCount)} note:{String(props.note)}</div>);
|
|
44
|
-
render(
|
|
45
|
-
<DataGrid
|
|
46
|
-
rows={ROWS}
|
|
47
|
-
columns={COLUMNS}
|
|
48
|
-
pagination
|
|
49
|
-
aggregationModel={{ amount: 'sum' }}
|
|
50
|
-
slots={{ footer: Footer }}
|
|
51
|
-
slotProps={{ footer: { note: 'hi' } }}
|
|
52
|
-
/>
|
|
53
|
-
);
|
|
54
|
-
expect(screen.getByTestId('footer')).toHaveTextContent('rows:3 note:hi');
|
|
55
|
-
expect(Footer).toHaveBeenCalledWith(expect.objectContaining({ aggregationResult: { amount: 2450 } }), undefined);
|
|
56
|
-
expect(document.querySelector('.ogx-pagination, .ogx__pagination')).toBeNull();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('renders slots.noRowsOverlay when there are no rows', () => {
|
|
60
|
-
render(<DataGrid rows={[]} columns={COLUMNS} slots={{ noRowsOverlay: () => <div>Nothing here</div> }} />);
|
|
61
|
-
expect(screen.getByText('Nothing here')).toBeInTheDocument();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('renders slots.loadingOverlay while loading with no rows', () => {
|
|
65
|
-
render(<DataGrid rows={[]} columns={COLUMNS} loading slots={{ loadingOverlay: () => <div>Fetching…</div> }} />);
|
|
66
|
-
expect(screen.getByText('Fetching…')).toBeInTheDocument();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('D6 — onRowDoubleClick', () => {
|
|
71
|
-
it('fires with the row params on double-click', () => {
|
|
72
|
-
const onRowDoubleClick = vi.fn();
|
|
73
|
-
render(<DataGrid rows={ROWS} columns={COLUMNS} onRowDoubleClick={onRowDoubleClick} />);
|
|
74
|
-
fireEvent.doubleClick(screen.getByText('$800'));
|
|
75
|
-
expect(onRowDoubleClick).toHaveBeenCalledWith(expect.objectContaining({ id: 2, row: ROWS[1] }));
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('fires in list view as well', () => {
|
|
79
|
-
const onRowDoubleClick = vi.fn();
|
|
80
|
-
render(
|
|
81
|
-
<DataGrid rows={ROWS} columns={COLUMNS} listView
|
|
82
|
-
listViewColumn={{ field: 'card', renderCell: ({ row }) => <span>card-{row.id}</span> }}
|
|
83
|
-
onRowDoubleClick={onRowDoubleClick} />
|
|
84
|
-
);
|
|
85
|
-
fireEvent.doubleClick(screen.getByText('card-3'));
|
|
86
|
-
expect(onRowDoubleClick).toHaveBeenCalledWith(expect.objectContaining({ id: 3, row: ROWS[2] }));
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('Row grouping — expansion survives row edits', () => {
|
|
91
|
-
it('keeps a user-expanded group open when the rows prop changes', () => {
|
|
92
|
-
const { rerender, container } = render(<DataGrid rows={ROWS} columns={COLUMNS} rowGroupingModel={['region']} />);
|
|
93
|
-
const rowCount = () => container.querySelectorAll('.ogx__rows [role="row"]').length;
|
|
94
|
-
expect(rowCount()).toBe(2);
|
|
95
|
-
|
|
96
|
-
fireEvent.click(container.querySelector('.ogx__rows [role="row"]')!);
|
|
97
|
-
expect(rowCount()).toBe(4);
|
|
98
|
-
|
|
99
|
-
rerender(<DataGrid rows={ROWS.map(r => (r.id === 1 ? { ...r, amount: 1300 } : r))} columns={COLUMNS} rowGroupingModel={['region']} />);
|
|
100
|
-
expect(rowCount()).toBe(4);
|
|
101
|
-
expect(screen.getByText('$1300')).toBeInTheDocument();
|
|
102
|
-
});
|
|
103
|
-
});
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Regression tests for bugs fixed in the DataGrid render layer.
|
|
3
|
-
*
|
|
4
|
-
* Tests cover:
|
|
5
|
-
* - getVisibleRows() respects pagination (not returning all rows)
|
|
6
|
-
* - Row DOM nodes are not remounted when column widths change
|
|
7
|
-
*
|
|
8
|
-
* Not covered here (require a real browser / ResizeObserver):
|
|
9
|
-
* - translateY offset — depends on computed cumulativeHeights, which requires
|
|
10
|
-
* a real viewport; jsdom reports all dimensions as 0
|
|
11
|
-
* - scroll-ref RAF batching — requires scroll events that jsdom fires but
|
|
12
|
-
* requestAnimationFrame does not execute by default
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import React from 'react';
|
|
16
|
-
import { describe, it, expect } from 'vitest';
|
|
17
|
-
import { render, act } from '@testing-library/react';
|
|
18
|
-
import { DataGrid } from './DataGrid';
|
|
19
|
-
import { useGridApiRef } from '../../hooks/core/useGridApiRef';
|
|
20
|
-
import type { GridColDef } from '../../types';
|
|
21
|
-
|
|
22
|
-
const COLS: GridColDef[] = [
|
|
23
|
-
{ field: 'id', headerName: 'ID', width: 80 },
|
|
24
|
-
{ field: 'name', headerName: 'Name', width: 150 },
|
|
25
|
-
{ field: 'age', headerName: 'Age', width: 100 },
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
// 10 rows so pagination actually clips the visible set
|
|
29
|
-
const ROWS = Array.from({ length: 10 }, (_, i) => ({
|
|
30
|
-
id: i + 1,
|
|
31
|
-
name: `User ${i + 1}`,
|
|
32
|
-
age: 20 + i,
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
36
|
-
// Bug: getVisibleRows() was returning sortedUnpinnedRows (all rows) regardless
|
|
37
|
-
// of whether pagination was active. Fixed: now returns paginatedUnpinnedRows
|
|
38
|
-
// when a paginationModel is set.
|
|
39
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
40
|
-
|
|
41
|
-
describe('getVisibleRows respects pagination', () => {
|
|
42
|
-
it('returns only the current page rows, not all rows', () => {
|
|
43
|
-
const assigningRef = React.createRef<import('../../types').GridApi>();
|
|
44
|
-
|
|
45
|
-
function GridWithRef() {
|
|
46
|
-
const ref = useGridApiRef();
|
|
47
|
-
// expose ref to test scope after render
|
|
48
|
-
React.useEffect(() => {
|
|
49
|
-
(assigningRef as React.MutableRefObject<import('../../types').GridApi>).current = ref.current;
|
|
50
|
-
});
|
|
51
|
-
return (
|
|
52
|
-
<DataGrid
|
|
53
|
-
rows={ROWS}
|
|
54
|
-
columns={COLS}
|
|
55
|
-
apiRef={ref}
|
|
56
|
-
pagination
|
|
57
|
-
paginationModel={{ page: 0, pageSize: 3 }}
|
|
58
|
-
/>
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
act(() => {
|
|
63
|
-
render(<GridWithRef />);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// pageSize=3, total=10 → page 0 should expose 3 rows, not 10.
|
|
67
|
-
const visible = assigningRef.current?.getVisibleRows() ?? [];
|
|
68
|
-
expect(visible.length).toBe(3);
|
|
69
|
-
expect(visible.map(r => r.id)).toEqual([1, 2, 3]);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('returns all rows when no pagination is provided', () => {
|
|
73
|
-
const assigningRef = React.createRef<import('../../types').GridApi>();
|
|
74
|
-
|
|
75
|
-
function GridNoPagination() {
|
|
76
|
-
const ref = useGridApiRef();
|
|
77
|
-
React.useEffect(() => {
|
|
78
|
-
(assigningRef as React.MutableRefObject<import('../../types').GridApi>).current = ref.current;
|
|
79
|
-
});
|
|
80
|
-
return <DataGrid rows={ROWS} columns={COLS} apiRef={ref} />;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
act(() => {
|
|
84
|
-
render(<GridNoPagination />);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const visible = assigningRef.current?.getVisibleRows() ?? [];
|
|
88
|
-
// Without pagination the full sorted set is returned.
|
|
89
|
-
expect(visible.length).toBe(ROWS.length);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
94
|
-
// Bug: row keys were `${row.id}-${virtualization.totalWidth}`, so every column
|
|
95
|
-
// resize unmounted and remounted every DOM row node.
|
|
96
|
-
// Fixed: key is now simply `row.id`.
|
|
97
|
-
//
|
|
98
|
-
// Test strategy: capture DOM element identity before and after a column prop
|
|
99
|
-
// change. If the key includes totalWidth the elements will be new nodes; if
|
|
100
|
-
// the key is stable they will be the exact same DOM nodes.
|
|
101
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
102
|
-
|
|
103
|
-
describe('row DOM nodes are stable across column width changes', () => {
|
|
104
|
-
it('does not remount rows when column widths change', () => {
|
|
105
|
-
const { rerender, container } = render(
|
|
106
|
-
<DataGrid rows={ROWS.slice(0, 3)} columns={COLS} />
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
// Collect the row cell elements before the update.
|
|
110
|
-
const cellsBefore = Array.from(
|
|
111
|
-
container.querySelectorAll('[role="gridcell"]')
|
|
112
|
-
);
|
|
113
|
-
expect(cellsBefore.length).toBeGreaterThan(0);
|
|
114
|
-
|
|
115
|
-
// Update the Name column width — simulates a resize.
|
|
116
|
-
const updatedCols: GridColDef[] = COLS.map(c =>
|
|
117
|
-
c.field === 'name' ? { ...c, width: 250 } : c
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
rerender(<DataGrid rows={ROWS.slice(0, 3)} columns={updatedCols} />);
|
|
121
|
-
|
|
122
|
-
const cellsAfter = Array.from(
|
|
123
|
-
container.querySelectorAll('[role="gridcell"]')
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// Same number of cells, and critically the same DOM node instances.
|
|
127
|
-
expect(cellsAfter.length).toBe(cellsBefore.length);
|
|
128
|
-
cellsBefore.forEach((node, i) => {
|
|
129
|
-
expect(cellsAfter[i]).toBe(node);
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
});
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { describe, it, expect } from 'vitest';
|
|
3
|
-
import { render, screen } from '@testing-library/react';
|
|
4
|
-
import { DataGrid } from './DataGrid';
|
|
5
|
-
import type { GridColDef } from '../../types';
|
|
6
|
-
|
|
7
|
-
const COLUMNS: GridColDef[] = [
|
|
8
|
-
{ field: 'id', headerName: 'ID', width: 80 },
|
|
9
|
-
{ field: 'name', headerName: 'Name', width: 150 },
|
|
10
|
-
{ field: 'age', headerName: 'Age', width: 100, type: 'number' },
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
const ROWS = [
|
|
14
|
-
{ id: 1, name: 'Alice', age: 30 },
|
|
15
|
-
{ id: 2, name: 'Bob', age: 25 },
|
|
16
|
-
{ id: 3, name: 'Charlie', age: 35 },
|
|
17
|
-
];
|
|
18
|
-
|
|
19
|
-
describe('DataGrid — smoke tests', () => {
|
|
20
|
-
it('renders without throwing', () => {
|
|
21
|
-
expect(() =>
|
|
22
|
-
render(<DataGrid rows={ROWS} columns={COLUMNS} />)
|
|
23
|
-
).not.toThrow();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('renders column headers', () => {
|
|
27
|
-
render(<DataGrid rows={ROWS} columns={COLUMNS} />);
|
|
28
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
29
|
-
expect(screen.getByText('Age')).toBeInTheDocument();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('renders row data', () => {
|
|
33
|
-
render(<DataGrid rows={ROWS} columns={COLUMNS} />);
|
|
34
|
-
expect(screen.getByText('Alice')).toBeInTheDocument();
|
|
35
|
-
expect(screen.getByText('Bob')).toBeInTheDocument();
|
|
36
|
-
expect(screen.getByText('Charlie')).toBeInTheDocument();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('renders with empty rows without throwing', () => {
|
|
40
|
-
expect(() =>
|
|
41
|
-
render(<DataGrid rows={[]} columns={COLUMNS} />)
|
|
42
|
-
).not.toThrow();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('renders with empty columns without throwing', () => {
|
|
46
|
-
expect(() =>
|
|
47
|
-
render(<DataGrid rows={ROWS} columns={[]} />)
|
|
48
|
-
).not.toThrow();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('renders with checkboxSelection enabled', () => {
|
|
52
|
-
const { container } = render(
|
|
53
|
-
<DataGrid rows={ROWS} columns={COLUMNS} checkboxSelection />
|
|
54
|
-
);
|
|
55
|
-
const checkboxes = container.querySelectorAll('input[type="checkbox"]');
|
|
56
|
-
expect(checkboxes.length).toBeGreaterThan(0);
|
|
57
|
-
});
|
|
58
|
-
});
|