@ornery/ui-grid-react 0.1.10 → 1.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.
@@ -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
@@ -279,37 +279,70 @@
279
279
  color: var(--ui-grid-muted-color);
280
280
  }
281
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
+ */
282
290
  .grid-table {
283
- display: grid;
291
+ position: relative;
292
+ display: flex;
293
+ flex-direction: column;
284
294
  min-height: 0;
285
295
  width: 100%;
286
296
  min-width: 0;
287
- overflow-x: auto;
288
- overflow-y: hidden;
297
+ overflow-anchor: none;
289
298
  }
290
299
 
291
- .header-grid,
292
- .filter-grid,
293
- .body-grid {
294
- display: grid;
295
- width: max-content;
296
- min-width: 100%;
297
- }
298
-
299
- .header-grid,
300
- .filter-grid {
300
+ .grid-header-strip,
301
+ .grid-filter-strip {
302
+ flex: 0 0 auto;
301
303
  position: sticky;
302
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;
303
320
  }
304
321
 
305
- .header-grid {
322
+ .grid-header-strip {
306
323
  top: 0;
307
324
  }
308
325
 
309
- .filter-grid {
326
+ .grid-filter-strip {
310
327
  top: var(--ui-grid-header-sticky-top, 0px);
311
328
  }
312
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
+
313
346
  .header-cell,
314
347
  .filter-cell,
315
348
  .body-cell,
@@ -1 +1 @@
1
- {"version":3,"file":"useGridState.d.ts","sourceRoot":"","sources":["../src/useGridState.ts"],"names":[],"mappings":"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"}
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.1.10",
3
+ "version": "1.0.1",
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.1.10",
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
  }
@@ -1,13 +1,11 @@
1
1
  import React from 'react';
2
- import { render, screen, fireEvent, act } from '@testing-library/react';
3
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
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 renderGrid(
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
- return { container, gridApi };
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 { container, gridApi } = renderGrid();
111
+ it('registers the API and renders headers and rows', async () => {
112
+ const { shadowRoot, gridApi } = await renderGrid();
103
113
 
104
- const headers = Array.from(container.querySelectorAll('.header-label')).map((el) =>
114
+ const headers = Array.from(shadowRoot.querySelectorAll('.header-label')).map((el) =>
105
115
  el.textContent?.trim(),
106
116
  );
107
- const bodyCells = Array.from(container.querySelectorAll('.body-cell')).map((el) =>
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 and renders empty state', () => {
121
- const { container, gridApi } = renderGrid();
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 and cycles sort state from header button', () => {
180
- const { container, gridApi } = renderGrid();
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 and collapses groups', () => {
249
- const { container, gridApi } = renderGrid();
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 initialGroups = container.querySelectorAll('.group-row');
176
+ const groups = shadowRoot.querySelectorAll('.group-row');
259
177
  expect(groupingChanged).toHaveBeenLastCalledWith(['status']);
260
- expect(initialGroups).toHaveLength(2);
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
- const createObjectUrlSpy = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:spec-grid');
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).toBe('spec-grid.csv');
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('virtualizes rows when count crosses threshold', () => {
303
- const { container, gridApi } = renderGrid({
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('keyboard cell editing: commit, navigate, cancel', async () => {
349
- const { container, gridApi } = renderGrid({
350
- enableGrouping: false,
351
- enableCellEditOnFocus: true,
352
- columnDefs: [
353
- { name: 'name', displayName: 'Customer', enableCellEdit: true },
354
- { name: 'status' },
355
- { name: 'owner', field: 'account.owner', enableCellEdit: true },
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
- fireEvent.change(editor, { target: { value: 'Taylor Morgan' } });
404
- fireEvent.keyDown(editor, { key: 'Escape' });
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(gridApi.core.getVisibleRows()[0]?.entity['account']).toEqual({
408
- owner: 'Mina Patel',
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('resolves custom i18n label overrides', () => {
414
- const { container } = renderGrid({
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
  });