@cdx-ui/data-grid 0.3.11 → 0.3.13
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/README.md +3 -1
- package/coverage.svg +1 -1
- package/lib/commonjs/DataGrid.js +490 -81
- package/lib/commonjs/DataGrid.js.map +1 -1
- package/lib/commonjs/DataGridFilterPanel.js +547 -0
- package/lib/commonjs/DataGridFilterPanel.js.map +1 -0
- package/lib/commonjs/adapter/columnTypes.js +256 -0
- package/lib/commonjs/adapter/columnTypes.js.map +1 -0
- package/lib/commonjs/adapter/columns.js +140 -4
- package/lib/commonjs/adapter/columns.js.map +1 -1
- package/lib/commonjs/adapter/filterOperators.js +422 -0
- package/lib/commonjs/adapter/filterOperators.js.map +1 -0
- package/lib/commonjs/adapter/pinning.js +105 -0
- package/lib/commonjs/adapter/pinning.js.map +1 -0
- package/lib/commonjs/adapter/selectColumn.js +11 -0
- package/lib/commonjs/adapter/selectColumn.js.map +1 -1
- package/lib/commonjs/adapter/treeData.js +195 -0
- package/lib/commonjs/adapter/treeData.js.map +1 -0
- package/lib/commonjs/index.js +26 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/numberCells.js +280 -0
- package/lib/commonjs/numberCells.js.map +1 -0
- package/lib/commonjs/styles.js +107 -1
- package/lib/commonjs/styles.js.map +1 -1
- package/lib/commonjs/types.js +39 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/useDataGridTable.js +234 -18
- package/lib/commonjs/useDataGridTable.js.map +1 -1
- package/lib/module/DataGrid.js +497 -88
- package/lib/module/DataGrid.js.map +1 -1
- package/lib/module/DataGridFilterPanel.js +540 -0
- package/lib/module/DataGridFilterPanel.js.map +1 -0
- package/lib/module/adapter/columnTypes.js +240 -0
- package/lib/module/adapter/columnTypes.js.map +1 -0
- package/lib/module/adapter/columns.js +137 -4
- package/lib/module/adapter/columns.js.map +1 -1
- package/lib/module/adapter/filterOperators.js +406 -0
- package/lib/module/adapter/filterOperators.js.map +1 -0
- package/lib/module/adapter/pinning.js +94 -0
- package/lib/module/adapter/pinning.js.map +1 -0
- package/lib/module/adapter/selectColumn.js +11 -1
- package/lib/module/adapter/selectColumn.js.map +1 -1
- package/lib/module/adapter/treeData.js +187 -0
- package/lib/module/adapter/treeData.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/numberCells.js +275 -0
- package/lib/module/numberCells.js.map +1 -0
- package/lib/module/styles.js +106 -0
- package/lib/module/styles.js.map +1 -1
- package/lib/module/types.js +38 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/useDataGridTable.js +238 -22
- package/lib/module/useDataGridTable.js.map +1 -1
- package/lib/typescript/DataGrid.d.ts.map +1 -1
- package/lib/typescript/DataGridFilterPanel.d.ts +52 -0
- package/lib/typescript/DataGridFilterPanel.d.ts.map +1 -0
- package/lib/typescript/adapter/columnTypes.d.ts +32 -0
- package/lib/typescript/adapter/columnTypes.d.ts.map +1 -0
- package/lib/typescript/adapter/columns.d.ts +14 -5
- package/lib/typescript/adapter/columns.d.ts.map +1 -1
- package/lib/typescript/adapter/filterOperators.d.ts +43 -0
- package/lib/typescript/adapter/filterOperators.d.ts.map +1 -0
- package/lib/typescript/adapter/pinning.d.ts +26 -0
- package/lib/typescript/adapter/pinning.d.ts.map +1 -0
- package/lib/typescript/adapter/selectColumn.d.ts +8 -0
- package/lib/typescript/adapter/selectColumn.d.ts.map +1 -1
- package/lib/typescript/adapter/treeData.d.ts +36 -0
- package/lib/typescript/adapter/treeData.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +4 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/numberCells.d.ts +46 -0
- package/lib/typescript/numberCells.d.ts.map +1 -0
- package/lib/typescript/styles.d.ts +32 -0
- package/lib/typescript/styles.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +154 -7
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/useDataGridTable.d.ts +40 -4
- package/lib/typescript/useDataGridTable.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/DataGrid.tsx +745 -95
- package/src/DataGridFilterPanel.tsx +648 -0
- package/src/adapter/columnTypes.ts +327 -0
- package/src/adapter/columns.test.ts +75 -1
- package/src/adapter/columns.ts +192 -3
- package/src/adapter/filterOperators.test.ts +292 -0
- package/src/adapter/filterOperators.ts +524 -0
- package/src/adapter/pinning.ts +134 -0
- package/src/adapter/selectColumn.ts +12 -1
- package/src/adapter/treeData.ts +228 -0
- package/src/index.ts +19 -0
- package/src/numberCells.tsx +379 -0
- package/src/styles.ts +131 -0
- package/src/types.ts +190 -7
- package/src/useDataGridTable.test.ts +69 -0
- package/src/useDataGridTable.ts +338 -17
package/src/styles.ts
CHANGED
|
@@ -38,6 +38,51 @@ export const dataGridMainVariants = cva(
|
|
|
38
38
|
},
|
|
39
39
|
);
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Opaque sticky fill so pinned cells cover scrolled content.
|
|
43
|
+
* Body hover matches the row's 50% secondary tint, mixed over the primary surface
|
|
44
|
+
* so the cell stays opaque while content scrolls underneath.
|
|
45
|
+
*/
|
|
46
|
+
export const dataGridPinnedSurfaceVariants = cva(
|
|
47
|
+
Platform.select({
|
|
48
|
+
web: 'bg-surface-primary',
|
|
49
|
+
default: '',
|
|
50
|
+
}),
|
|
51
|
+
{
|
|
52
|
+
variants: {
|
|
53
|
+
header: {
|
|
54
|
+
true: 'bg-neutral-50',
|
|
55
|
+
false: Platform.select({
|
|
56
|
+
web: 'web:[tr:hover_&]:bg-[color-mix(in_srgb,var(--color-surface-secondary)_50%,var(--color-surface-primary))]',
|
|
57
|
+
default: '',
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
defaultVariants: {
|
|
62
|
+
header: false,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
export const dataGridPinnedEdgeVariants = cva('', {
|
|
68
|
+
variants: {
|
|
69
|
+
edge: {
|
|
70
|
+
start: Platform.select({
|
|
71
|
+
web: 'border-e border-stroke-secondary',
|
|
72
|
+
default: '',
|
|
73
|
+
}),
|
|
74
|
+
end: Platform.select({
|
|
75
|
+
web: 'border-s border-stroke-secondary',
|
|
76
|
+
default: '',
|
|
77
|
+
}),
|
|
78
|
+
none: '',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
defaultVariants: {
|
|
82
|
+
edge: 'none',
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
41
86
|
export const dataGridToolbarVariants = cva(
|
|
42
87
|
'shrink-0 flex-row items-center gap-3 border-b border-stroke-secondary px-4 py-4',
|
|
43
88
|
);
|
|
@@ -135,6 +180,49 @@ export const dataGridBodyVariants = cva(
|
|
|
135
180
|
|
|
136
181
|
export const dataGridFilterInputVariants = cva('min-w-24');
|
|
137
182
|
|
|
183
|
+
// The toolbar panel sits at a fixed spot in the grid root. A panel opened from a column is anchored
|
|
184
|
+
// to its opener in a portal, so its position and width cap come from the anchored overlay instead.
|
|
185
|
+
export const dataGridFilterPanelVariants = cva(
|
|
186
|
+
'w-max gap-3 rounded-lg border border-stroke-secondary bg-surface-primary px-3 pb-2 pt-3 shadow-lg',
|
|
187
|
+
{
|
|
188
|
+
variants: {
|
|
189
|
+
anchored: {
|
|
190
|
+
true: '',
|
|
191
|
+
false: 'absolute start-4 top-16 z-30 max-w-[calc(100%-2rem)]',
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
defaultVariants: {
|
|
195
|
+
anchored: false,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// The panel is as wide as one unwrapped row, capped at the grid's width. Below that, fields wrap
|
|
201
|
+
// onto new lines and grow to fill them, so the layout follows the grid's width, not the viewport.
|
|
202
|
+
export const dataGridFilterPanelRowVariants = cva('flex-row flex-wrap items-end gap-2');
|
|
203
|
+
|
|
204
|
+
export const dataGridFilterPanelLogicVariants = cva('min-h-8 w-24 shrink-0 justify-center');
|
|
205
|
+
|
|
206
|
+
export const dataGridFilterPanelFieldVariants = cva('min-w-0 shrink grow gap-1', {
|
|
207
|
+
variants: {
|
|
208
|
+
size: {
|
|
209
|
+
column: 'w-40',
|
|
210
|
+
operator: 'w-40',
|
|
211
|
+
value: 'w-56',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
export const dataGridFilterPanelColumnVariants = cva('w-full');
|
|
217
|
+
|
|
218
|
+
export const dataGridFilterPanelOperatorVariants = cva('w-full');
|
|
219
|
+
|
|
220
|
+
export const dataGridFilterPanelValueVariants = cva('w-full');
|
|
221
|
+
|
|
222
|
+
export const dataGridFilterPanelFooterVariants = cva(
|
|
223
|
+
'flex-row items-center justify-between gap-2 border-t border-stroke-secondary pt-2',
|
|
224
|
+
);
|
|
225
|
+
|
|
138
226
|
export const dataGridHeadVariants = cva([
|
|
139
227
|
'h-12 bg-neutral-50 px-4',
|
|
140
228
|
Platform.select({
|
|
@@ -159,6 +247,49 @@ export const dataGridCellVariants = cva([
|
|
|
159
247
|
}),
|
|
160
248
|
]);
|
|
161
249
|
|
|
250
|
+
export const dataGridDetailToggleCellVariants = cva([
|
|
251
|
+
'w-12 px-2',
|
|
252
|
+
Platform.select({
|
|
253
|
+
web: 'border-b border-stroke-secondary',
|
|
254
|
+
default: '',
|
|
255
|
+
}),
|
|
256
|
+
]);
|
|
257
|
+
|
|
258
|
+
// Header labels and depth-0 content line up because both reserve the toggle slot.
|
|
259
|
+
export const dataGridTreeCellInnerVariants = cva('min-w-0 flex-1 flex-row items-center gap-2');
|
|
260
|
+
|
|
261
|
+
export const dataGridTreeToggleSlotVariants = cva('size-8 shrink-0 items-center justify-center');
|
|
262
|
+
|
|
263
|
+
export const dataGridTreeContentVariants = cva('min-w-0 flex-1 flex-row items-center');
|
|
264
|
+
|
|
265
|
+
export const dataGridDetailPanelCellVariants = cva([
|
|
266
|
+
'px-4 py-3',
|
|
267
|
+
Platform.select({
|
|
268
|
+
web: 'border-b border-stroke-secondary bg-surface-primary',
|
|
269
|
+
default: '',
|
|
270
|
+
}),
|
|
271
|
+
]);
|
|
272
|
+
|
|
273
|
+
// Same `w-0 min-w-full` trick as the header label: a declared column width truncates the value
|
|
274
|
+
// with an ellipsis instead of wrapping it onto a second line and growing the fixed-height row.
|
|
275
|
+
export const dataGridCellTextVariants = cva('min-w-0', {
|
|
276
|
+
variants: {
|
|
277
|
+
align: {
|
|
278
|
+
start: 'text-start',
|
|
279
|
+
center: 'text-center',
|
|
280
|
+
end: 'text-end',
|
|
281
|
+
},
|
|
282
|
+
fitToColumn: {
|
|
283
|
+
true: 'w-0 min-w-full',
|
|
284
|
+
false: '',
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
defaultVariants: {
|
|
288
|
+
align: 'start',
|
|
289
|
+
fitToColumn: false,
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
|
|
162
293
|
export const dataGridSelectCellVariants = cva([
|
|
163
294
|
'w-14 px-4',
|
|
164
295
|
Platform.select({
|
package/src/types.ts
CHANGED
|
@@ -28,21 +28,92 @@ export interface GridRowSelectionModel {
|
|
|
28
28
|
ids: Set<GridRowId>;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Filter operators. Names match MUI Data Grid Community so server handlers written for
|
|
33
|
+
* MUI keep working. Which operators a column accepts depends on its `type`.
|
|
34
|
+
*/
|
|
35
|
+
export type GridFilterOperator =
|
|
36
|
+
| 'contains'
|
|
37
|
+
| 'doesNotContain'
|
|
38
|
+
| 'equals'
|
|
39
|
+
| 'doesNotEqual'
|
|
40
|
+
| 'startsWith'
|
|
41
|
+
| 'endsWith'
|
|
42
|
+
| 'isEmpty'
|
|
43
|
+
| 'isNotEmpty'
|
|
44
|
+
| 'isAnyOf'
|
|
45
|
+
| '='
|
|
46
|
+
| '!='
|
|
47
|
+
| '>'
|
|
48
|
+
| '>='
|
|
49
|
+
| '<'
|
|
50
|
+
| '<='
|
|
51
|
+
| 'is'
|
|
52
|
+
| 'not'
|
|
53
|
+
| 'after'
|
|
54
|
+
| 'onOrAfter'
|
|
55
|
+
| 'before'
|
|
56
|
+
| 'onOrBefore';
|
|
57
|
+
|
|
31
58
|
export interface GridFilterItem {
|
|
32
59
|
field: string;
|
|
33
|
-
|
|
60
|
+
/** Defaults to `'contains'`, a case-insensitive substring match on the displayed text. */
|
|
61
|
+
operator?: GridFilterOperator;
|
|
62
|
+
/**
|
|
63
|
+
* Operand for `operator`: a string for text, a number for `number`, a `Date` for `date` /
|
|
64
|
+
* `dateTime`, a boolean for `boolean`, a `singleSelect` option value, or an array for
|
|
65
|
+
* `isAnyOf`. `isEmpty` and `isNotEmpty` ignore it.
|
|
66
|
+
*/
|
|
67
|
+
value?: unknown;
|
|
34
68
|
}
|
|
35
69
|
|
|
36
70
|
export type GridFilterModel = GridFilterItem[];
|
|
37
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Columns pinned to the logical start and end edges while the grid scrolls
|
|
74
|
+
* horizontally.
|
|
75
|
+
*
|
|
76
|
+
* `left` and `right` are deprecated compatibility aliases for `start` and `end`
|
|
77
|
+
* (same mapping as `GridColDef.align`).
|
|
78
|
+
*/
|
|
79
|
+
export interface GridPinnedColumnFields {
|
|
80
|
+
start?: string[];
|
|
81
|
+
end?: string[];
|
|
82
|
+
/**
|
|
83
|
+
* @deprecated Use `start` instead.
|
|
84
|
+
*/
|
|
85
|
+
left?: string[];
|
|
86
|
+
/**
|
|
87
|
+
* @deprecated Use `end` instead.
|
|
88
|
+
*/
|
|
89
|
+
right?: string[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** How `filterModel` items combine: every item must match (`'and'`) or any one item (`'or'`). */
|
|
93
|
+
export type GridLogicOperator = 'and' | 'or';
|
|
94
|
+
|
|
38
95
|
export interface GridRenderCellParams<R extends GridValidRowModel = GridValidRowModel> {
|
|
39
96
|
value: unknown;
|
|
40
97
|
row: R;
|
|
41
98
|
field: string;
|
|
42
99
|
id: GridRowId;
|
|
100
|
+
/** The column being rendered, including props spread after a column preset. */
|
|
101
|
+
col: GridColDef<R>;
|
|
43
102
|
}
|
|
44
103
|
|
|
45
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Built-in column types. Names and defaults match MUI Data Grid Community.
|
|
106
|
+
* Omit `type` to keep untyped display, alignment, and sort.
|
|
107
|
+
*/
|
|
108
|
+
export type GridColType = 'string' | 'number' | 'date' | 'dateTime' | 'boolean' | 'singleSelect';
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* A `singleSelect` choice: a primitive (value and label are the same) or an
|
|
112
|
+
* explicit `{ value, label }` pair.
|
|
113
|
+
*/
|
|
114
|
+
export type GridValueOption = string | number | { value: string | number; label: string };
|
|
115
|
+
|
|
116
|
+
export interface GridBaseColDef<R extends GridValidRowModel = GridValidRowModel> {
|
|
46
117
|
field: string;
|
|
47
118
|
headerName?: string;
|
|
48
119
|
width?: number;
|
|
@@ -52,28 +123,74 @@ export interface GridColDef<R extends GridValidRowModel = GridValidRowModel> {
|
|
|
52
123
|
* Logical cell-content alignment.
|
|
53
124
|
*
|
|
54
125
|
* `left` and `right` are deprecated compatibility aliases for `start` and `end`.
|
|
55
|
-
*
|
|
126
|
+
* When omitted, `number` defaults to `end`, `boolean` to `center`, and every
|
|
127
|
+
* other type (including omitted `type`) to `start`.
|
|
56
128
|
*/
|
|
57
129
|
align?: GridAlignment;
|
|
58
130
|
/**
|
|
59
|
-
* Logical header-content alignment. Defaults to `align
|
|
131
|
+
* Logical header-content alignment. Defaults to `align`, then the type default.
|
|
60
132
|
*
|
|
61
133
|
* `left` and `right` are deprecated compatibility aliases for `start` and `end`.
|
|
62
134
|
*/
|
|
63
135
|
headerAlign?: GridAlignment;
|
|
64
136
|
sortable?: boolean;
|
|
65
137
|
filterable?: boolean;
|
|
66
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Column value type. Drives default alignment, display formatting, and client
|
|
140
|
+
* sort/filter. Omitted `type` keeps the untyped defaults from the current release.
|
|
141
|
+
*/
|
|
142
|
+
type?: Exclude<GridColType, 'singleSelect'>;
|
|
67
143
|
valueGetter?: (value: unknown, row: R, col: GridColDef<R>) => unknown;
|
|
68
144
|
valueFormatter?: (value: unknown, row: R, col: GridColDef<R>) => ReactNode | string;
|
|
69
145
|
renderCell?: (params: GridRenderCellParams<R>) => ReactNode;
|
|
70
146
|
renderHeader?: () => ReactNode;
|
|
71
147
|
}
|
|
72
148
|
|
|
149
|
+
export interface GridSingleSelectColDef<
|
|
150
|
+
R extends GridValidRowModel = GridValidRowModel,
|
|
151
|
+
> extends Omit<GridBaseColDef<R>, 'type'> {
|
|
152
|
+
type: 'singleSelect';
|
|
153
|
+
/**
|
|
154
|
+
* Choices for a `singleSelect` column. Use `{ value, label }` when the stored
|
|
155
|
+
* value differs from the displayed text. A function receives the row when labels
|
|
156
|
+
* depend on other fields.
|
|
157
|
+
*/
|
|
158
|
+
valueOptions: readonly GridValueOption[] | ((params: { row?: R }) => readonly GridValueOption[]);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type GridColDef<R extends GridValidRowModel = GridValidRowModel> =
|
|
162
|
+
| GridBaseColDef<R>
|
|
163
|
+
| GridSingleSelectColDef<R>;
|
|
164
|
+
|
|
73
165
|
export type GridFeatureMode = 'client' | 'server';
|
|
74
166
|
|
|
75
167
|
export type GridPaginationMode = GridFeatureMode;
|
|
76
168
|
|
|
169
|
+
/** Maps a column `field` to `false` to hide it. Fields that are missing or `true` stay visible. */
|
|
170
|
+
export type GridColumnVisibilityModel = Record<string, boolean>;
|
|
171
|
+
|
|
172
|
+
export interface GridRowParams<R extends GridValidRowModel = GridValidRowModel> {
|
|
173
|
+
id: GridRowId;
|
|
174
|
+
row: R;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface GridTreeNodeParams<
|
|
178
|
+
R extends GridValidRowModel = GridValidRowModel,
|
|
179
|
+
> extends GridRowParams<R> {
|
|
180
|
+
/** `0` for top-level rows. */
|
|
181
|
+
depth: number;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* How a selection change spreads through `treeData`. Both default to `true`, as in MUI.
|
|
186
|
+
*/
|
|
187
|
+
export interface GridRowSelectionPropagation {
|
|
188
|
+
/** Selecting or deselecting a parent applies to its descendants that pass the current filter. */
|
|
189
|
+
descendants?: boolean;
|
|
190
|
+
/** A parent is selected exactly when all of its children are selected. */
|
|
191
|
+
parents?: boolean;
|
|
192
|
+
}
|
|
193
|
+
|
|
77
194
|
export interface DataGridProps<R extends GridValidRowModel = GridValidRowModel> {
|
|
78
195
|
rows: GridRowsProp<R>;
|
|
79
196
|
columns: readonly GridColDef<R>[];
|
|
@@ -114,14 +231,72 @@ export interface DataGridProps<R extends GridValidRowModel = GridValidRowModel>
|
|
|
114
231
|
filterModel?: GridFilterModel;
|
|
115
232
|
defaultFilterModel?: GridFilterModel;
|
|
116
233
|
onFilterModelChange?: (model: GridFilterModel) => void;
|
|
234
|
+
/** Combines `filterModel` items. Defaults to `'and'`. The quick filter always applies on top. */
|
|
235
|
+
filterLogicOperator?: GridLogicOperator;
|
|
236
|
+
defaultFilterLogicOperator?: GridLogicOperator;
|
|
237
|
+
onFilterLogicOperatorChange?: (operator: GridLogicOperator) => void;
|
|
117
238
|
quickFilter?: string;
|
|
118
239
|
defaultQuickFilter?: string;
|
|
119
240
|
onQuickFilterChange?: (value: string) => void;
|
|
120
|
-
/** Shows the toolbar search field. Off by default, same as MUI Community. */
|
|
241
|
+
/** Shows the toolbar search field and filter button. Off by default, same as MUI Community. */
|
|
121
242
|
showToolbar?: boolean;
|
|
122
|
-
/** Consumer-owned toolbar controls beside search (export, density, toggles, etc.). Wire actions to app state that drives grid props
|
|
243
|
+
/** Consumer-owned toolbar controls beside search and the filter button (export, density, toggles, etc.). Wire actions to app state that drives grid props. */
|
|
123
244
|
toolbarActions?: ReactNode;
|
|
245
|
+
/**
|
|
246
|
+
* Pins columns to the logical start and/or end edge while the grid scrolls
|
|
247
|
+
* horizontally. Replaces MUI X Pro `pinnedColumns` (including deprecated
|
|
248
|
+
* `left` / `right` aliases). MUI X `initialState.pinnedColumns` maps here.
|
|
249
|
+
*
|
|
250
|
+
* MUI X Community ships no column pinning; this is a Pro feature.
|
|
251
|
+
*/
|
|
252
|
+
pinnedColumns?: GridPinnedColumnFields;
|
|
253
|
+
/**
|
|
254
|
+
* Hides columns without removing them from `columns`. Hidden columns still sort, filter, and
|
|
255
|
+
* match the quick filter, and the filter panel still lists them. The grid has no built-in
|
|
256
|
+
* control that changes visibility, so keep the model in your own state.
|
|
257
|
+
*/
|
|
258
|
+
columnVisibilityModel?: GridColumnVisibilityModel;
|
|
124
259
|
headerFilters?: boolean;
|
|
260
|
+
/**
|
|
261
|
+
* Renders `rows` as a tree. Each row's position comes from `getTreeDataPath`. The first column
|
|
262
|
+
* becomes the tree column: it indents by depth and holds the expand toggle and, with
|
|
263
|
+
* `checkboxSelection`, the row checkbox. Search and filters keep the ancestors of every match
|
|
264
|
+
* and expand them while the filter is active.
|
|
265
|
+
*
|
|
266
|
+
* Client mode only: server pagination, sorting, and filtering are not supported with tree data.
|
|
267
|
+
* Pagination counts top-level rows, so a group never splits across pages.
|
|
268
|
+
* @default false
|
|
269
|
+
*/
|
|
270
|
+
treeData?: boolean;
|
|
271
|
+
/**
|
|
272
|
+
* Path from the root to the row, for example `['Accounts', 'OAuth V1', 'List accounts']`.
|
|
273
|
+
* Paths must be unique. A row whose parent path has no row attaches to its nearest existing
|
|
274
|
+
* ancestor. Keep the function reference stable.
|
|
275
|
+
*/
|
|
276
|
+
getTreeDataPath?: (row: R) => readonly string[];
|
|
277
|
+
/**
|
|
278
|
+
* Overrides the tree column (the first column) when `treeData` is on: header name, width,
|
|
279
|
+
* `renderCell`, and so on. `field` stays the first column's field.
|
|
280
|
+
*/
|
|
281
|
+
groupingColDef?: Partial<Omit<GridBaseColDef<R>, 'field'>>;
|
|
282
|
+
/** Depth expanded on first render. `0` collapses every group; `-1` expands all. @default 0 */
|
|
283
|
+
defaultGroupingExpansionDepth?: number;
|
|
284
|
+
/** Takes priority over `defaultGroupingExpansionDepth` for the initial state of a group. */
|
|
285
|
+
isGroupExpandedByDefault?: (node: GridTreeNodeParams<R>) => boolean;
|
|
286
|
+
/**
|
|
287
|
+
* How selection spreads between parents and children with `treeData`. Unlike MUI, rows hidden
|
|
288
|
+
* by a filter stay selected, so a search-then-pick form keeps earlier picks.
|
|
289
|
+
*/
|
|
290
|
+
rowSelectionPropagation?: GridRowSelectionPropagation;
|
|
291
|
+
/**
|
|
292
|
+
* Content of the expandable panel under a row (master-detail). Adds a toggle column at the
|
|
293
|
+
* start. Rows for which this returns `null` or `undefined` get no toggle.
|
|
294
|
+
*/
|
|
295
|
+
getDetailPanelContent?: (params: GridRowParams<R>) => ReactNode;
|
|
296
|
+
/** Ids of rows whose detail panel is open (controlled). */
|
|
297
|
+
detailPanelExpandedRowIds?: ReadonlySet<GridRowId>;
|
|
298
|
+
defaultDetailPanelExpandedRowIds?: ReadonlySet<GridRowId>;
|
|
299
|
+
onDetailPanelExpandedRowIdsChange?: (ids: Set<GridRowId>) => void;
|
|
125
300
|
/** Replaces the default centered "No rows" message. Shown when `rows` is empty and not loading. */
|
|
126
301
|
noRowsOverlay?: ReactNode;
|
|
127
302
|
/** Replaces the default skeleton rows shown when `loading` is true. */
|
|
@@ -135,6 +310,14 @@ export interface DataGridProps<R extends GridValidRowModel = GridValidRowModel>
|
|
|
135
310
|
|
|
136
311
|
export const SELECT_COLUMN_ID = '__select__';
|
|
137
312
|
|
|
313
|
+
/** Same id as MUI X's master-detail toggle column. */
|
|
314
|
+
export const DETAIL_PANEL_TOGGLE_ID = '__detail_panel_toggle__';
|
|
315
|
+
|
|
316
|
+
export const EMPTY_ROW_ID_SET: ReadonlySet<GridRowId> = new Set();
|
|
317
|
+
|
|
318
|
+
/** MUI X Data Grid checkbox-column field; accepted as an alias for {@link SELECT_COLUMN_ID}. */
|
|
319
|
+
export const MUI_CHECKBOX_FIELD = '__check__';
|
|
320
|
+
|
|
138
321
|
export const DEFAULT_PAGE_SIZE_OPTIONS = [25, 50, 100] as const;
|
|
139
322
|
|
|
140
323
|
export const DEFAULT_PAGINATION_MODEL: GridPaginationModel = {
|
|
@@ -2,10 +2,79 @@ import type { RowSelectionState } from '@tanstack/react-table';
|
|
|
2
2
|
import type { GridRowId } from './types';
|
|
3
3
|
import {
|
|
4
4
|
columnFiltersToFilterModel,
|
|
5
|
+
filterModelToColumnFilters,
|
|
5
6
|
rowSelectionToModel,
|
|
6
7
|
selectionModelToRowSelection,
|
|
7
8
|
} from './useDataGridTable';
|
|
8
9
|
|
|
10
|
+
describe('filterModelToColumnFilters', () => {
|
|
11
|
+
it('keeps items without an operator as text filters and drops blank ones', () => {
|
|
12
|
+
expect(
|
|
13
|
+
filterModelToColumnFilters([
|
|
14
|
+
{ field: 'name', value: 'alice' },
|
|
15
|
+
{ field: 'status', value: ' ' },
|
|
16
|
+
{ field: 'age' },
|
|
17
|
+
]),
|
|
18
|
+
).toEqual([{ id: 'name', value: 'alice' }]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('groups several items on one field and round-trips them', () => {
|
|
22
|
+
const model = [
|
|
23
|
+
{ field: 'amount', operator: '>' as const, value: 1 },
|
|
24
|
+
{ field: 'amount', operator: '<' as const, value: 9 },
|
|
25
|
+
];
|
|
26
|
+
const state = filterModelToColumnFilters(model);
|
|
27
|
+
|
|
28
|
+
expect(state).toEqual([
|
|
29
|
+
{
|
|
30
|
+
id: 'amount',
|
|
31
|
+
value: {
|
|
32
|
+
items: [
|
|
33
|
+
{ operator: '>', value: 1 },
|
|
34
|
+
{ operator: '<', value: 9 },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
]);
|
|
39
|
+
expect(columnFiltersToFilterModel(state)).toEqual(model);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('carries the operator and typed operand', () => {
|
|
43
|
+
expect(filterModelToColumnFilters([{ field: 'amount', operator: '>', value: 100 }])).toEqual([
|
|
44
|
+
{ id: 'amount', value: { operator: '>', value: 100 } },
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('keeps isEmpty and isNotEmpty without a value', () => {
|
|
49
|
+
expect(
|
|
50
|
+
filterModelToColumnFilters([
|
|
51
|
+
{ field: 'a', operator: 'isEmpty' },
|
|
52
|
+
{ field: 'b', operator: 'isNotEmpty', value: '' },
|
|
53
|
+
]),
|
|
54
|
+
).toEqual([
|
|
55
|
+
{ id: 'a', value: { operator: 'isEmpty', value: undefined } },
|
|
56
|
+
{ id: 'b', value: { operator: 'isNotEmpty', value: '' } },
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('drops value operators whose operand is blank', () => {
|
|
61
|
+
expect(
|
|
62
|
+
filterModelToColumnFilters([
|
|
63
|
+
{ field: 'a', operator: '>' },
|
|
64
|
+
{ field: 'b', operator: 'isAnyOf', value: [] },
|
|
65
|
+
{ field: 'c', operator: 'contains', value: ' ' },
|
|
66
|
+
]),
|
|
67
|
+
).toEqual([]);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('columnFiltersToFilterModel with operators', () => {
|
|
72
|
+
it('round-trips operator filters', () => {
|
|
73
|
+
const model = [{ field: 'amount', operator: '<=' as const, value: 5 }];
|
|
74
|
+
expect(columnFiltersToFilterModel(filterModelToColumnFilters(model))).toEqual(model);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
9
78
|
describe('columnFiltersToFilterModel', () => {
|
|
10
79
|
it('keeps string filter values', () => {
|
|
11
80
|
expect(columnFiltersToFilterModel([{ id: 'name', value: 'alice' }])).toEqual([
|