@ornery/ui-grid-react 0.1.9 → 1.0.0
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/demo/main.tsx +4 -2
- package/dist/UiGrid.d.ts +6 -5
- package/dist/UiGrid.d.ts.map +1 -1
- package/dist/gridStateMath.d.ts +2 -2
- package/dist/gridStateMath.d.ts.map +1 -1
- package/dist/index.d.ts +2 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +159 -2020
- package/dist/index.mjs +153 -2106
- package/dist/mountUiGrid.d.ts +2 -0
- package/dist/mountUiGrid.d.ts.map +1 -1
- package/dist/ui-grid.css +94 -15
- package/dist/useGridState.d.ts +5 -0
- package/dist/useGridState.d.ts.map +1 -1
- package/package.json +5 -3
- package/src/UiGrid.test.tsx +64 -235
- package/src/UiGrid.tsx +178 -683
- package/src/gridStateMath.test.ts +3 -3
- package/src/gridStateMath.ts +7 -4
- package/src/index.ts +2 -18
- package/src/mountUiGrid.tsx +26 -0
- package/src/test-setup.ts +13 -0
- package/src/ui-grid.css +94 -15
- package/src/useGridState.ts +173 -24
- package/tsconfig.dts.json +3 -2
- package/tsconfig.json +2 -1
- package/vitest.config.ts +2 -0
package/dist/mountUiGrid.d.ts
CHANGED
|
@@ -6,4 +6,6 @@ export declare function mountUiGrid(container: Element | DocumentFragment, props
|
|
|
6
6
|
export declare function updateUiGrid(root: Root, props: UiGridProps): void;
|
|
7
7
|
/** Create a styled <span> React node — usable from non-TSX contexts (e.g. Angular). */
|
|
8
8
|
export declare function styledCell(text: string, color: string, extraStyle?: React.CSSProperties): React.ReactNode;
|
|
9
|
+
/** Create a date <input> React node — usable from non-TSX contexts. */
|
|
10
|
+
export declare function datePickerCell(value: string, onChange?: (newValue: string) => void, extraStyle?: React.CSSProperties): React.ReactNode;
|
|
9
11
|
//# sourceMappingURL=mountUiGrid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mountUiGrid.d.ts","sourceRoot":"","sources":["../src/mountUiGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEpD,wBAAgB,WAAW,CAAC,SAAS,EAAE,OAAO,GAAG,gBAAgB,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAI3F;AAED,uFAAuF;AACvF,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAEjE;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,KAAK,CAAC,aAAa,GAC/B,KAAK,CAAC,SAAS,CAMjB"}
|
|
1
|
+
{"version":3,"file":"mountUiGrid.d.ts","sourceRoot":"","sources":["../src/mountUiGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAc,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEpD,wBAAgB,WAAW,CAAC,SAAS,EAAE,OAAO,GAAG,gBAAgB,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAI3F;AAED,uFAAuF;AACvF,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAEjE;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,KAAK,CAAC,aAAa,GAC/B,KAAK,CAAC,SAAS,CAMjB;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACrC,UAAU,CAAC,EAAE,KAAK,CAAC,aAAa,GAC/B,KAAK,CAAC,SAAS,CAmBjB"}
|
package/dist/ui-grid.css
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
.ui-grid-host {
|
|
2
|
+
/* Stretch to fill the parent so the autoresize observer can measure a
|
|
3
|
+
deterministic height. Consumers can override these properties to opt
|
|
4
|
+
back into intrinsic sizing. */
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
min-height: 0;
|
|
8
|
+
height: 100%;
|
|
2
9
|
--_ui-grid-border-color: var(--ui-grid-border-color, var(--app-ui-grid-border-color, #d4d4d8));
|
|
3
10
|
--_ui-grid-header-background: var(
|
|
4
11
|
--ui-grid-header-background,
|
|
@@ -221,6 +228,12 @@
|
|
|
221
228
|
border: 1px solid var(--ui-grid-border-color);
|
|
222
229
|
box-shadow: var(--ui-grid-shadow);
|
|
223
230
|
overflow: hidden;
|
|
231
|
+
/* Allow the frame to grow inside the flexed host so the body grid can fill
|
|
232
|
+
the available height when no explicit viewportHeight is set. */
|
|
233
|
+
display: flex;
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
flex: 1 1 auto;
|
|
236
|
+
min-height: 0;
|
|
224
237
|
}
|
|
225
238
|
|
|
226
239
|
.metrics-strip {
|
|
@@ -266,37 +279,70 @@
|
|
|
266
279
|
color: var(--ui-grid-muted-color);
|
|
267
280
|
}
|
|
268
281
|
|
|
282
|
+
/*
|
|
283
|
+
* Grid table layout. Header + filter rows live in their own
|
|
284
|
+
* non-user-scrollable strips (real scroll containers so `position: sticky`
|
|
285
|
+
* on pinned cells anchors correctly); the body has its own viewport with
|
|
286
|
+
* visible scrollbars. Horizontal scroll on the body is mirrored onto the
|
|
287
|
+
* strips imperatively via React's onScroll/onWheel handlers so the columns
|
|
288
|
+
* always stay aligned.
|
|
289
|
+
*/
|
|
269
290
|
.grid-table {
|
|
270
|
-
|
|
291
|
+
position: relative;
|
|
292
|
+
display: flex;
|
|
293
|
+
flex-direction: column;
|
|
271
294
|
min-height: 0;
|
|
272
295
|
width: 100%;
|
|
273
296
|
min-width: 0;
|
|
274
|
-
overflow-
|
|
275
|
-
overflow-y: hidden;
|
|
297
|
+
overflow-anchor: none;
|
|
276
298
|
}
|
|
277
299
|
|
|
278
|
-
.header-
|
|
279
|
-
.filter-
|
|
280
|
-
|
|
281
|
-
display: grid;
|
|
282
|
-
width: max-content;
|
|
283
|
-
min-width: 100%;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
.header-grid,
|
|
287
|
-
.filter-grid {
|
|
300
|
+
.grid-header-strip,
|
|
301
|
+
.grid-filter-strip {
|
|
302
|
+
flex: 0 0 auto;
|
|
288
303
|
position: sticky;
|
|
289
304
|
z-index: 3;
|
|
305
|
+
background: var(--ui-grid-surface);
|
|
306
|
+
/* Real scroll container so pinned sticky cells anchor to something,
|
|
307
|
+
* but we don't want the user scrolling it directly. JS drives the
|
|
308
|
+
* scrollLeft, the wheel handler forwards gestures to the body, and
|
|
309
|
+
* the scrollbar is hidden. */
|
|
310
|
+
overflow-x: scroll;
|
|
311
|
+
overflow-y: hidden;
|
|
312
|
+
scrollbar-width: none;
|
|
313
|
+
touch-action: pan-y;
|
|
314
|
+
overscroll-behavior-x: none;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.grid-header-strip::-webkit-scrollbar,
|
|
318
|
+
.grid-filter-strip::-webkit-scrollbar {
|
|
319
|
+
display: none;
|
|
290
320
|
}
|
|
291
321
|
|
|
292
|
-
.header-
|
|
322
|
+
.grid-header-strip {
|
|
293
323
|
top: 0;
|
|
294
324
|
}
|
|
295
325
|
|
|
296
|
-
.filter-
|
|
326
|
+
.grid-filter-strip {
|
|
297
327
|
top: var(--ui-grid-header-sticky-top, 0px);
|
|
298
328
|
}
|
|
299
329
|
|
|
330
|
+
.grid-body-viewport {
|
|
331
|
+
flex: 1 1 auto;
|
|
332
|
+
min-height: 0;
|
|
333
|
+
overflow-x: auto;
|
|
334
|
+
overflow-anchor: none;
|
|
335
|
+
overscroll-behavior-x: none;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.header-grid,
|
|
339
|
+
.filter-grid,
|
|
340
|
+
.body-grid {
|
|
341
|
+
display: grid;
|
|
342
|
+
width: max-content;
|
|
343
|
+
min-width: 100%;
|
|
344
|
+
}
|
|
345
|
+
|
|
300
346
|
.header-cell,
|
|
301
347
|
.filter-cell,
|
|
302
348
|
.body-cell,
|
|
@@ -313,6 +359,7 @@
|
|
|
313
359
|
background: var(--ui-grid-header-background);
|
|
314
360
|
font-weight: var(--ui-grid-header-weight);
|
|
315
361
|
align-items: center;
|
|
362
|
+
position: relative;
|
|
316
363
|
}
|
|
317
364
|
|
|
318
365
|
.header-cell[draggable='true'] {
|
|
@@ -337,6 +384,38 @@
|
|
|
337
384
|
z-index: var(--ui-grid-pin-menu-open-z-index);
|
|
338
385
|
}
|
|
339
386
|
|
|
387
|
+
.column-resizer {
|
|
388
|
+
position: absolute;
|
|
389
|
+
top: 0;
|
|
390
|
+
right: -4px;
|
|
391
|
+
width: 8px;
|
|
392
|
+
height: 100%;
|
|
393
|
+
padding: 0;
|
|
394
|
+
border: 0;
|
|
395
|
+
background: transparent;
|
|
396
|
+
cursor: col-resize;
|
|
397
|
+
z-index: 5;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.column-resizer::after {
|
|
401
|
+
content: '';
|
|
402
|
+
position: absolute;
|
|
403
|
+
top: 20%;
|
|
404
|
+
bottom: 20%;
|
|
405
|
+
left: 50%;
|
|
406
|
+
width: 2px;
|
|
407
|
+
transform: translateX(-50%);
|
|
408
|
+
border-radius: 999px;
|
|
409
|
+
background: color-mix(in srgb, var(--ui-grid-border-color, #888) 80%, transparent);
|
|
410
|
+
opacity: 0;
|
|
411
|
+
transition: opacity 120ms ease;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.header-cell:hover .column-resizer::after,
|
|
415
|
+
.column-resizer:focus-visible::after {
|
|
416
|
+
opacity: 1;
|
|
417
|
+
}
|
|
418
|
+
|
|
340
419
|
.header-label {
|
|
341
420
|
min-width: 0;
|
|
342
421
|
display: block;
|
package/dist/useGridState.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export interface UseGridStateResult {
|
|
|
30
30
|
paginationSelectedPageSize: number;
|
|
31
31
|
rowSize: number;
|
|
32
32
|
viewportHeightPx: string;
|
|
33
|
+
autoViewportHeight: number | null;
|
|
33
34
|
headerLabel: (column: GridColumnDef) => string;
|
|
34
35
|
isGroupItem: (item: DisplayItem) => item is GroupItem;
|
|
35
36
|
isExpandableItem: (item: DisplayItem) => item is ExpandableItem;
|
|
@@ -45,6 +46,7 @@ export interface UseGridStateResult {
|
|
|
45
46
|
groupDisclosureLabel: (item: GroupItem) => string;
|
|
46
47
|
displayValue: (row: GridRow, column: GridColumnDef) => string;
|
|
47
48
|
isFocusedCell: (row: GridRow, column: GridColumnDef) => boolean;
|
|
49
|
+
isFocusedRow: (row: GridRow) => boolean;
|
|
48
50
|
isEditingCell: (row: GridRow, column: GridColumnDef) => boolean;
|
|
49
51
|
editorInputType: (column: GridColumnDef) => string;
|
|
50
52
|
cellContext: (row: GridRow, column: GridColumnDef) => GridCellTemplateContext;
|
|
@@ -100,6 +102,9 @@ export interface UseGridStateResult {
|
|
|
100
102
|
toggleTreeRow: (row: GridRow, event?: React.MouseEvent) => void;
|
|
101
103
|
moveColumn: (fromIndex: number, toIndex: number) => void;
|
|
102
104
|
moveVisibleColumn: (columnName: string, targetColumnName: string) => void;
|
|
105
|
+
canResizeColumns: () => boolean;
|
|
106
|
+
handleHeaderResizeMouseDown: (column: GridColumnDef, event: React.MouseEvent) => void;
|
|
107
|
+
autoSizeColumn: (column: GridColumnDef, event: React.MouseEvent) => void;
|
|
103
108
|
nextPage: () => void;
|
|
104
109
|
previousPage: () => void;
|
|
105
110
|
onPageSizeChange: (value: string) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGridState.d.ts","sourceRoot":"","sources":["../src/useGridState.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,SAAS,EAET,WAAW,EACX,aAAa,EACb,OAAO,EAEP,mBAAmB,EACnB,gBAAgB,EAChB,UAAU,EACV,SAAS,
|
|
1
|
+
{"version":3,"file":"useGridState.d.ts","sourceRoot":"","sources":["../src/useGridState.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ornery/ui-grid-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "React wrapper for @ornery/ui-grid-core",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
"./styles": "./dist/ui-grid.css"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
|
-
"@ornery/ui-grid-core": "0.
|
|
17
|
+
"@ornery/ui-grid-core": "1.0.0",
|
|
18
|
+
"@ornery/ui-grid-vanilla": "1.0.0",
|
|
18
19
|
"react": "^18.0.0 || ^19.0.0",
|
|
19
20
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"@ornery/ui-grid-core": "file:../ui-grid-core",
|
|
24
|
+
"@ornery/ui-grid-vanilla": "file:../ui-grid-vanilla",
|
|
23
25
|
"@testing-library/react": "^16.0.0",
|
|
24
26
|
"@types/react": "^19.0.0",
|
|
25
27
|
"@types/react-dom": "^19.0.0",
|
|
@@ -34,7 +36,7 @@
|
|
|
34
36
|
},
|
|
35
37
|
"scripts": {
|
|
36
38
|
"start": "vite serve demo --config demo/vite.config.ts",
|
|
37
|
-
"build": "npm run build --prefix ../ui-grid-core && tsup src/index.ts --format esm,cjs --tsconfig tsconfig.build.json --external react --external react-dom --external @ornery/ui-grid-core && tsc -p tsconfig.dts.json && node -e \"require('fs').copyFileSync('src/ui-grid.css','dist/ui-grid.css')\"",
|
|
39
|
+
"build": "npm run build --prefix ../ui-grid-core && npm run build --prefix ../ui-grid-vanilla && tsup src/index.ts --format esm,cjs --tsconfig tsconfig.build.json --external react --external react-dom --external @ornery/ui-grid-core --external @ornery/ui-grid-vanilla && tsc -p tsconfig.dts.json && node -e \"require('fs').copyFileSync('src/ui-grid.css','dist/ui-grid.css')\"",
|
|
38
40
|
"test": "vitest run",
|
|
39
41
|
"test:watch": "vitest"
|
|
40
42
|
}
|
package/src/UiGrid.test.tsx
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { render,
|
|
3
|
-
import { describe, it, expect, vi,
|
|
2
|
+
import { render, act, waitFor } from '@testing-library/react';
|
|
3
|
+
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
4
4
|
import { UiGrid } from './UiGrid';
|
|
5
5
|
import type { UiGridProps } from './UiGrid';
|
|
6
6
|
import type {
|
|
7
|
-
GridHeaderTemplateContext,
|
|
8
7
|
GridOptions,
|
|
9
8
|
UiGridApi,
|
|
10
|
-
GridExpandableTemplateContext,
|
|
11
9
|
} from '@ornery/ui-grid-core';
|
|
12
10
|
import { SORT_DIRECTIONS, FILTER_CONDITIONS } from '@ornery/ui-grid-core';
|
|
13
11
|
|
|
@@ -75,21 +73,33 @@ function createOptions(
|
|
|
75
73
|
};
|
|
76
74
|
}
|
|
77
75
|
|
|
78
|
-
function
|
|
76
|
+
function getShadowRoot(container: HTMLElement): ShadowRoot {
|
|
77
|
+
const el = container.querySelector('ui-grid-element');
|
|
78
|
+
if (!el?.shadowRoot) throw new Error('Shadow root not found');
|
|
79
|
+
return el.shadowRoot;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function renderGrid(
|
|
79
83
|
overrides: Partial<GridOptions> = {},
|
|
80
84
|
props: Partial<Omit<UiGridProps, 'options'>> = {},
|
|
81
|
-
): { container: HTMLElement; gridApi: UiGridApi } {
|
|
85
|
+
): Promise<{ container: HTMLElement; gridApi: UiGridApi; shadowRoot: ShadowRoot }> {
|
|
82
86
|
let gridApi!: UiGridApi;
|
|
87
|
+
let resolveApi: () => void;
|
|
88
|
+
const apiReady = new Promise<void>((r) => { resolveApi = r; });
|
|
83
89
|
const options = createOptions(overrides, (api) => {
|
|
84
90
|
gridApi = api;
|
|
85
91
|
props.onRegisterApi?.(api);
|
|
92
|
+
resolveApi();
|
|
86
93
|
});
|
|
87
94
|
|
|
88
95
|
const { container } = render(
|
|
89
96
|
<UiGrid options={options} onRegisterApi={options.onRegisterApi as any} {...props} />,
|
|
90
97
|
);
|
|
91
98
|
|
|
92
|
-
|
|
99
|
+
await act(async () => { await apiReady; });
|
|
100
|
+
|
|
101
|
+
const shadowRoot = getShadowRoot(container);
|
|
102
|
+
return { container, gridApi, shadowRoot };
|
|
93
103
|
}
|
|
94
104
|
|
|
95
105
|
describe('UiGrid React component', () => {
|
|
@@ -98,13 +108,13 @@ describe('UiGrid React component', () => {
|
|
|
98
108
|
vi.useRealTimers();
|
|
99
109
|
});
|
|
100
110
|
|
|
101
|
-
it('registers the API and renders headers and rows', () => {
|
|
102
|
-
const {
|
|
111
|
+
it('registers the API and renders headers and rows', async () => {
|
|
112
|
+
const { shadowRoot, gridApi } = await renderGrid();
|
|
103
113
|
|
|
104
|
-
const headers = Array.from(
|
|
114
|
+
const headers = Array.from(shadowRoot.querySelectorAll('.header-label')).map((el) =>
|
|
105
115
|
el.textContent?.trim(),
|
|
106
116
|
);
|
|
107
|
-
const bodyCells = Array.from(
|
|
117
|
+
const bodyCells = Array.from(shadowRoot.querySelectorAll('.body-cell')).map((el) =>
|
|
108
118
|
el.textContent?.trim(),
|
|
109
119
|
);
|
|
110
120
|
|
|
@@ -114,11 +124,10 @@ describe('UiGrid React component', () => {
|
|
|
114
124
|
expect(bodyCells).toContain('$300');
|
|
115
125
|
expect(bodyCells).toContain('Mina Patel');
|
|
116
126
|
expect(bodyCells).toContain('Gamma-badge');
|
|
117
|
-
expect(container.querySelector('.grid-viewport')).toBeNull();
|
|
118
127
|
});
|
|
119
128
|
|
|
120
|
-
it('filters rows
|
|
121
|
-
const {
|
|
129
|
+
it('filters rows via the API', async () => {
|
|
130
|
+
const { gridApi } = await renderGrid();
|
|
122
131
|
|
|
123
132
|
const filterChanged = vi.fn();
|
|
124
133
|
gridApi.core.on.filterChanged(filterChanged);
|
|
@@ -134,50 +143,11 @@ describe('UiGrid React component', () => {
|
|
|
134
143
|
act(() => {
|
|
135
144
|
gridApi.core.setFilter('status', 'Missing');
|
|
136
145
|
});
|
|
137
|
-
|
|
138
146
|
expect(gridApi.core.getVisibleRows()).toEqual([]);
|
|
139
|
-
expect(container.querySelector('.empty-state strong')?.textContent).toContain(
|
|
140
|
-
'Nothing to show',
|
|
141
|
-
);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('renders custom header content from the React headerRenderer prop', () => {
|
|
145
|
-
const headerRenderer = ({ value, column }: GridHeaderTemplateContext) => (
|
|
146
|
-
<span>{`${value}:${column.name}`}</span>
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
const { container } = renderGrid({}, { headerRenderer });
|
|
150
|
-
|
|
151
|
-
const headers = Array.from(container.querySelectorAll('.header-label')).map((el) =>
|
|
152
|
-
el.textContent?.trim(),
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
expect(headers).toEqual([
|
|
156
|
-
'Customer:name',
|
|
157
|
-
'Status:status',
|
|
158
|
-
'Revenue:revenue',
|
|
159
|
-
'Owner:owner',
|
|
160
|
-
'Badge:badge',
|
|
161
|
-
]);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('renders custom header content from column headerRenderer when no React headerRenderer is provided', () => {
|
|
165
|
-
const { container } = renderGrid({
|
|
166
|
-
columnDefs: [
|
|
167
|
-
{ name: 'name', displayName: 'Customer', headerRenderer: ({ value }) => `[${value}]` },
|
|
168
|
-
{ name: 'status' },
|
|
169
|
-
],
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
const headers = Array.from(container.querySelectorAll('.header-label')).map((el) =>
|
|
173
|
-
el.textContent?.trim(),
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
expect(headers).toEqual(['[Customer]', 'Status']);
|
|
177
147
|
});
|
|
178
148
|
|
|
179
|
-
it('sorts rows
|
|
180
|
-
const {
|
|
149
|
+
it('sorts rows via the API', async () => {
|
|
150
|
+
const { gridApi } = await renderGrid();
|
|
181
151
|
|
|
182
152
|
const sortChanged = vi.fn();
|
|
183
153
|
gridApi.core.on.sortChanged(sortChanged);
|
|
@@ -191,62 +161,10 @@ describe('UiGrid React component', () => {
|
|
|
191
161
|
'Gamma',
|
|
192
162
|
]);
|
|
193
163
|
expect(sortChanged).toHaveBeenLastCalledWith('name', SORT_DIRECTIONS.asc);
|
|
194
|
-
|
|
195
|
-
const headerButton = container.querySelector('.header-action') as HTMLButtonElement;
|
|
196
|
-
act(() => {
|
|
197
|
-
headerButton.click();
|
|
198
|
-
});
|
|
199
|
-
expect(sortChanged).toHaveBeenLastCalledWith('name', SORT_DIRECTIONS.desc);
|
|
200
|
-
expect(gridApi.core.getVisibleRows().map((row) => row.entity['name'])).toEqual([
|
|
201
|
-
'Gamma',
|
|
202
|
-
'Beta',
|
|
203
|
-
'alpha',
|
|
204
|
-
]);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('reorders visible columns by header drag and drop without shifting hidden columns', () => {
|
|
208
|
-
const { container } = renderGrid({
|
|
209
|
-
columnDefs: [
|
|
210
|
-
{ name: 'id', visible: false },
|
|
211
|
-
{ name: 'name', displayName: 'Customer' },
|
|
212
|
-
{ name: 'status' },
|
|
213
|
-
{ name: 'revenue' },
|
|
214
|
-
{ name: 'owner', field: 'account.owner' },
|
|
215
|
-
{ name: 'badge' },
|
|
216
|
-
],
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
const dataTransfer = {
|
|
220
|
-
effectAllowed: 'all',
|
|
221
|
-
dropEffect: 'none',
|
|
222
|
-
store: new Map<string, string>(),
|
|
223
|
-
setData(type: string, value: string) {
|
|
224
|
-
this.store.set(type, value);
|
|
225
|
-
},
|
|
226
|
-
getData(type: string) {
|
|
227
|
-
return this.store.get(type) ?? '';
|
|
228
|
-
},
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
const sourceHeader = container.querySelectorAll('.header-cell')[4] as HTMLElement;
|
|
232
|
-
const targetHeader = container.querySelectorAll('.header-cell')[1] as HTMLElement;
|
|
233
|
-
|
|
234
|
-
act(() => {
|
|
235
|
-
fireEvent.dragStart(sourceHeader, { dataTransfer });
|
|
236
|
-
fireEvent.dragOver(targetHeader, { dataTransfer });
|
|
237
|
-
fireEvent.drop(targetHeader, { dataTransfer });
|
|
238
|
-
fireEvent.dragEnd(sourceHeader, { dataTransfer });
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
const headers = Array.from(container.querySelectorAll('.header-label')).map((el) =>
|
|
242
|
-
el.textContent?.trim(),
|
|
243
|
-
);
|
|
244
|
-
|
|
245
|
-
expect(headers).toEqual(['Customer', 'Badge', 'Status', 'Revenue', 'Owner']);
|
|
246
164
|
});
|
|
247
165
|
|
|
248
|
-
it('groups rows
|
|
249
|
-
const {
|
|
166
|
+
it('groups rows via the API', async () => {
|
|
167
|
+
const { shadowRoot, gridApi } = await renderGrid();
|
|
250
168
|
|
|
251
169
|
const groupingChanged = vi.fn();
|
|
252
170
|
gridApi.core.on.groupingChanged(groupingChanged);
|
|
@@ -255,31 +173,20 @@ describe('UiGrid React component', () => {
|
|
|
255
173
|
gridApi.core.groupByColumn('status');
|
|
256
174
|
});
|
|
257
175
|
|
|
258
|
-
const
|
|
176
|
+
const groups = shadowRoot.querySelectorAll('.group-row');
|
|
259
177
|
expect(groupingChanged).toHaveBeenLastCalledWith(['status']);
|
|
260
|
-
expect(
|
|
261
|
-
expect(container.querySelectorAll('.body-cell')).toHaveLength(15);
|
|
262
|
-
|
|
263
|
-
const activeGroup = Array.from(initialGroups).find((node) =>
|
|
264
|
-
node.textContent?.includes('status: Active'),
|
|
265
|
-
);
|
|
266
|
-
expect(activeGroup).toBeTruthy();
|
|
267
|
-
|
|
268
|
-
act(() => {
|
|
269
|
-
(activeGroup as HTMLButtonElement).click();
|
|
270
|
-
});
|
|
271
|
-
expect(container.querySelectorAll('.body-cell')).toHaveLength(5);
|
|
178
|
+
expect(groups).toHaveLength(2);
|
|
272
179
|
});
|
|
273
180
|
|
|
274
181
|
it('exports visible rows as CSV', async () => {
|
|
275
|
-
const { gridApi } = renderGrid();
|
|
182
|
+
const { gridApi } = await renderGrid();
|
|
276
183
|
|
|
277
184
|
const anchor = document.createElement('a');
|
|
278
185
|
const originalCreateElement = document.createElement.bind(document);
|
|
279
186
|
const clickSpy = vi.spyOn(anchor, 'click').mockImplementation(() => {});
|
|
280
187
|
vi.spyOn(document, 'createElement').mockImplementation(((tagName: string) =>
|
|
281
188
|
tagName === 'a' ? anchor : originalCreateElement(tagName)) as typeof document.createElement);
|
|
282
|
-
|
|
189
|
+
vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:spec-grid');
|
|
283
190
|
vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => {});
|
|
284
191
|
|
|
285
192
|
act(() => {
|
|
@@ -287,37 +194,11 @@ describe('UiGrid React component', () => {
|
|
|
287
194
|
});
|
|
288
195
|
|
|
289
196
|
expect(clickSpy).toHaveBeenCalledTimes(1);
|
|
290
|
-
expect(anchor.download).
|
|
291
|
-
|
|
292
|
-
const blob = createObjectUrlSpy.mock.calls[0][0] as Blob;
|
|
293
|
-
const csv = await new Promise<string>((resolve) => {
|
|
294
|
-
const reader = new FileReader();
|
|
295
|
-
reader.onload = () => resolve(reader.result as string);
|
|
296
|
-
reader.readAsText(blob);
|
|
297
|
-
});
|
|
298
|
-
expect(csv).toContain('Customer,Status,Revenue,Owner,Badge');
|
|
299
|
-
expect(csv).toContain('Gamma,Pilot,$300,Mina Patel,Gamma-badge');
|
|
197
|
+
expect(anchor.download).toMatch(/\.csv$/);
|
|
300
198
|
});
|
|
301
199
|
|
|
302
|
-
it('
|
|
303
|
-
const {
|
|
304
|
-
virtualizationThreshold: 1,
|
|
305
|
-
data: Array.from({ length: 5 }, (_, index) => ({
|
|
306
|
-
id: `virtual-${index}`,
|
|
307
|
-
name: `Row ${index}`,
|
|
308
|
-
status: index % 2 === 0 ? 'Active' : 'Pilot',
|
|
309
|
-
revenue: index * 100,
|
|
310
|
-
account: { owner: `Owner ${index}` },
|
|
311
|
-
})),
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
expect(gridApi.core.getVisibleRows()).toHaveLength(5);
|
|
315
|
-
expect(container.querySelector('.grid-virtual-spacer')).not.toBeNull();
|
|
316
|
-
expect(container.querySelector('.grid-virtual-body')).not.toBeNull();
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
it('paginates rows', () => {
|
|
320
|
-
const { container, gridApi } = renderGrid({
|
|
200
|
+
it('paginates rows via the API', async () => {
|
|
201
|
+
const { gridApi } = await renderGrid({
|
|
321
202
|
enablePagination: true,
|
|
322
203
|
enablePaginationControls: true,
|
|
323
204
|
paginationPageSizes: [1, 2],
|
|
@@ -341,90 +222,44 @@ describe('UiGrid React component', () => {
|
|
|
341
222
|
gridApi.pagination.setPageSize(2);
|
|
342
223
|
});
|
|
343
224
|
expect(gridApi.core.getVisibleRows().map((row) => row.id)).toEqual(['row-1', 'row-2']);
|
|
344
|
-
|
|
345
|
-
expect(container.querySelector('.pagination-bar')?.textContent).toContain('1-2 of 3');
|
|
346
225
|
});
|
|
347
226
|
|
|
348
|
-
it('
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const beginCellEdit = vi.fn();
|
|
360
|
-
const afterCellEdit = vi.fn();
|
|
361
|
-
const cancelCellEdit = vi.fn();
|
|
362
|
-
gridApi.edit.on.beginCellEdit(beginCellEdit);
|
|
363
|
-
gridApi.edit.on.afterCellEdit(afterCellEdit);
|
|
364
|
-
gridApi.edit.on.cancelCellEdit(cancelCellEdit);
|
|
365
|
-
|
|
366
|
-
const firstNameCell = container.querySelector(
|
|
367
|
-
'.body-cell[data-row-id="row-1"][data-col-name="name"]',
|
|
368
|
-
) as HTMLElement;
|
|
369
|
-
|
|
370
|
-
await act(async () => {
|
|
371
|
-
firstNameCell.focus();
|
|
372
|
-
fireEvent.keyDown(firstNameCell, { key: 'Z' });
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
let editor = container.querySelector(
|
|
376
|
-
'.cell-editor[data-row-id="row-1"][data-col-name="name"]',
|
|
377
|
-
) as HTMLInputElement;
|
|
378
|
-
expect(editor).toBeTruthy();
|
|
379
|
-
expect(beginCellEdit).toHaveBeenCalled();
|
|
380
|
-
|
|
381
|
-
await act(async () => {
|
|
382
|
-
fireEvent.keyDown(editor, { key: 'Tab' });
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
expect(gridApi.core.getVisibleRows()[0]?.entity['name']).toBe('Z');
|
|
386
|
-
expect(afterCellEdit).toHaveBeenCalled();
|
|
387
|
-
|
|
388
|
-
const ownerCell = container.querySelector(
|
|
389
|
-
'.body-cell[data-row-id="row-1"][data-col-name="owner"]',
|
|
390
|
-
) as HTMLElement;
|
|
391
|
-
|
|
392
|
-
await act(async () => {
|
|
393
|
-
ownerCell.focus();
|
|
394
|
-
fireEvent.keyDown(ownerCell, { key: 'F2' });
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
editor = container.querySelector(
|
|
398
|
-
'.cell-editor[data-row-id="row-1"][data-col-name="owner"]',
|
|
399
|
-
) as HTMLInputElement;
|
|
400
|
-
expect(editor).toBeTruthy();
|
|
227
|
+
it('renders cell renderers via portals', async () => {
|
|
228
|
+
const statusRenderer = vi.fn((ctx) => `pill-${ctx.value}`);
|
|
229
|
+
const { container } = await renderGrid(
|
|
230
|
+
{
|
|
231
|
+
columnDefs: [
|
|
232
|
+
{ name: 'name', displayName: 'Customer' },
|
|
233
|
+
{ name: 'status' },
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
{ cellRenderers: { status: statusRenderer } },
|
|
237
|
+
);
|
|
401
238
|
|
|
239
|
+
// The vanilla element's framework slot flush may not fire in jsdom.
|
|
240
|
+
// Manually dispatch a cellSlotsChanged event to exercise the portal path.
|
|
241
|
+
const el = container.querySelector('ui-grid-element')!;
|
|
402
242
|
await act(async () => {
|
|
403
|
-
|
|
404
|
-
|
|
243
|
+
el.dispatchEvent(new CustomEvent('cellSlotsChanged', {
|
|
244
|
+
detail: {
|
|
245
|
+
added: [{
|
|
246
|
+
slotName: 'cell--status--row-1',
|
|
247
|
+
columnName: 'status',
|
|
248
|
+
rowId: 'row-1',
|
|
249
|
+
context: { $implicit: 'Pilot', value: 'Pilot', row: baseData[0], column: { name: 'status' }, rowIndex: 0 },
|
|
250
|
+
}],
|
|
251
|
+
removed: [],
|
|
252
|
+
},
|
|
253
|
+
}));
|
|
405
254
|
});
|
|
406
255
|
|
|
407
|
-
expect(
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
expect(cancelCellEdit).toHaveBeenCalled();
|
|
256
|
+
expect(statusRenderer).toHaveBeenCalled();
|
|
257
|
+
const portalContent = container.querySelectorAll('[slot]');
|
|
258
|
+
expect(portalContent.length).toBeGreaterThan(0);
|
|
411
259
|
});
|
|
412
260
|
|
|
413
|
-
it('
|
|
414
|
-
const {
|
|
415
|
-
labels: {
|
|
416
|
-
sortDefault: 'Trier',
|
|
417
|
-
sortAsc: 'Tri croissant',
|
|
418
|
-
paginationNext: 'Suivant',
|
|
419
|
-
},
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
const sortButton = container.querySelector('.header-action') as HTMLButtonElement;
|
|
423
|
-
expect(sortButton.getAttribute('aria-label')).toBe('Trier');
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
it('feature flags disable unused template sections', () => {
|
|
427
|
-
const { container, gridApi } = renderGrid({
|
|
261
|
+
it('feature flags disable columns via visible:false', async () => {
|
|
262
|
+
const { gridApi } = await renderGrid({
|
|
428
263
|
enableSorting: false,
|
|
429
264
|
enableFiltering: false,
|
|
430
265
|
enableGrouping: false,
|
|
@@ -437,12 +272,6 @@ describe('UiGrid React component', () => {
|
|
|
437
272
|
],
|
|
438
273
|
});
|
|
439
274
|
|
|
440
|
-
const headers = Array.from(container.querySelectorAll('.header-label')).map((el) =>
|
|
441
|
-
el.textContent?.trim(),
|
|
442
|
-
);
|
|
443
|
-
expect(headers).toEqual(['Status', 'Owner']);
|
|
444
|
-
expect(container.querySelector('.filter-grid')).toBeNull();
|
|
445
|
-
expect(container.querySelector('.chip-action')).toBeNull();
|
|
446
275
|
expect(gridApi.core.getVisibleRows()).toHaveLength(3);
|
|
447
276
|
});
|
|
448
277
|
});
|