argent-grid 0.1.0 → 0.2.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/.github/workflows/ci.yml +69 -0
- package/.github/workflows/pages.yml +6 -12
- package/.storybook/main.ts +20 -0
- package/.storybook/preview.ts +18 -0
- package/.storybook/tsconfig.json +24 -0
- package/AGENTS.md +2 -2
- package/README.md +51 -34
- package/angular.json +66 -0
- package/biome.json +66 -0
- package/demo-app/e2e/selection-screenshot.spec.ts +20 -0
- package/docs/AG-GRID-COMPARISON.md +725 -0
- package/docs/CELL-RENDERER-GUIDE.md +241 -0
- package/docs/CONTEXT-MENU-GUIDE.md +371 -0
- package/docs/LIVE-DATA-OPTIMIZATIONS.md +497 -0
- package/docs/PERFORMANCE-OPTIMIZATIONS-PHASE1.md +162 -0
- package/docs/PERFORMANCE-REVIEW.md +571 -0
- package/docs/RESEARCH-STATUS.md +234 -0
- package/docs/STATE-PERSISTENCE-GUIDE.md +370 -0
- package/docs/STORYBOOK-REFACTOR.md +215 -0
- package/docs/STORYBOOK-STATUS.md +156 -0
- package/docs/TEST-COVERAGE-REPORT.md +276 -0
- package/docs/THEME-API-GUIDE.md +445 -0
- package/docs/THEME-API-PLAN.md +364 -0
- package/e2e/advanced.spec.ts +109 -0
- package/e2e/argentgrid.spec.ts +65 -0
- package/e2e/benchmark.spec.ts +52 -0
- package/e2e/screenshots.spec.ts +52 -0
- package/e2e/theming.spec.ts +35 -0
- package/e2e/visual.spec.ts +91 -0
- package/e2e/visual.spec.ts-snapshots/grid-default.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-empty-state.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-filter-popup.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-scroll-borders.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-sidebar-buttons.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-text-filter.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-with-selection.png +0 -0
- package/package.json +20 -6
- package/plan.md +50 -18
- package/playwright.config.ts +38 -0
- package/setup-vitest.ts +10 -13
- package/src/lib/argent-grid.module.ts +10 -12
- package/src/lib/components/argent-grid.component.css +327 -76
- package/src/lib/components/argent-grid.component.html +186 -64
- package/src/lib/components/argent-grid.component.spec.ts +120 -160
- package/src/lib/components/argent-grid.component.ts +642 -189
- package/src/lib/components/argent-grid.selection.spec.ts +132 -0
- package/src/lib/components/set-filter/set-filter.component.ts +302 -0
- package/src/lib/directives/ag-grid-compatibility.directive.ts +16 -26
- package/src/lib/directives/click-outside.directive.ts +19 -0
- package/src/lib/rendering/canvas-renderer.spec.ts +366 -0
- package/src/lib/rendering/canvas-renderer.ts +418 -305
- package/src/lib/rendering/live-data-handler.ts +110 -0
- package/src/lib/rendering/live-data-optimizations.ts +133 -0
- package/src/lib/rendering/render/blit.spec.ts +16 -27
- package/src/lib/rendering/render/blit.ts +48 -36
- package/src/lib/rendering/render/cells.spec.ts +132 -0
- package/src/lib/rendering/render/cells.ts +46 -24
- package/src/lib/rendering/render/column-utils.ts +73 -0
- package/src/lib/rendering/render/hit-test.ts +55 -0
- package/src/lib/rendering/render/index.ts +79 -76
- package/src/lib/rendering/render/lines.ts +43 -43
- package/src/lib/rendering/render/primitives.ts +161 -0
- package/src/lib/rendering/render/theme.spec.ts +8 -12
- package/src/lib/rendering/render/theme.ts +7 -10
- package/src/lib/rendering/render/types.ts +2 -2
- package/src/lib/rendering/render/walk.spec.ts +35 -38
- package/src/lib/rendering/render/walk.ts +60 -50
- package/src/lib/rendering/utils/damage-tracker.spec.ts +8 -7
- package/src/lib/rendering/utils/damage-tracker.ts +6 -18
- package/src/lib/rendering/utils/index.ts +1 -1
- package/src/lib/services/grid.service.set-filter.spec.ts +219 -0
- package/src/lib/services/grid.service.spec.ts +1165 -201
- package/src/lib/services/grid.service.ts +819 -187
- package/src/lib/themes/parts/color-schemes.ts +132 -0
- package/src/lib/themes/parts/icon-sets.ts +258 -0
- package/src/lib/themes/theme-builder.ts +347 -0
- package/src/lib/themes/theme-quartz.ts +72 -0
- package/src/lib/themes/types.ts +238 -0
- package/src/lib/types/ag-grid-types.ts +73 -14
- package/src/public-api.ts +39 -9
- package/src/stories/Advanced.stories.ts +188 -0
- package/src/stories/ArgentGrid.stories.ts +277 -0
- package/src/stories/Benchmark.stories.ts +74 -0
- package/src/stories/CellRenderers.stories.ts +221 -0
- package/src/stories/Filtering.stories.ts +252 -0
- package/src/stories/Grouping.stories.ts +217 -0
- package/src/stories/Theming.stories.ts +124 -0
- package/src/stories/benchmark-wrapper.component.ts +315 -0
- package/tsconfig.storybook.json +10 -0
- package/vitest.config.ts +9 -9
- package/demo-app/README.md +0 -70
- package/demo-app/angular.json +0 -78
- package/demo-app/e2e/benchmark.spec.ts +0 -53
- package/demo-app/e2e/demo-page.spec.ts +0 -77
- package/demo-app/e2e/grid-features.spec.ts +0 -269
- package/demo-app/package-lock.json +0 -14023
- package/demo-app/package.json +0 -36
- package/demo-app/playwright-test-menu.js +0 -19
- package/demo-app/playwright.config.ts +0 -23
- package/demo-app/src/app/app.component.ts +0 -10
- package/demo-app/src/app/app.config.ts +0 -13
- package/demo-app/src/app/app.routes.ts +0 -7
- package/demo-app/src/app/demo-page/demo-page.component.css +0 -313
- package/demo-app/src/app/demo-page/demo-page.component.html +0 -124
- package/demo-app/src/app/demo-page/demo-page.component.ts +0 -366
- package/demo-app/src/index.html +0 -19
- package/demo-app/src/main.ts +0 -6
- package/demo-app/tsconfig.json +0 -31
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { TestBed } from '@angular/core/testing';
|
|
2
1
|
import { provideExperimentalZonelessChangeDetection } from '@angular/core';
|
|
2
|
+
import { TestBed } from '@angular/core/testing';
|
|
3
|
+
import { ColDef, FilterModel, GridApi, GridState } from '../types/ag-grid-types';
|
|
3
4
|
import { GridService } from './grid.service';
|
|
4
|
-
import { GridApi, ColDef, FilterModel, IRowNode } from '../types/ag-grid-types';
|
|
5
5
|
|
|
6
6
|
interface TestData {
|
|
7
7
|
id: number;
|
|
@@ -13,26 +13,23 @@ interface TestData {
|
|
|
13
13
|
describe('GridService', () => {
|
|
14
14
|
let service: GridService<TestData>;
|
|
15
15
|
let api: GridApi<TestData>;
|
|
16
|
-
|
|
17
|
-
const testColumnDefs:
|
|
16
|
+
|
|
17
|
+
const testColumnDefs: ColDef<TestData>[] = [
|
|
18
18
|
{ colId: 'id', field: 'id', headerName: 'ID', width: 100 },
|
|
19
19
|
{ colId: 'name', field: 'name', headerName: 'Name', width: 150 },
|
|
20
20
|
{ colId: 'age', field: 'age', headerName: 'Age', width: 80, sortable: true },
|
|
21
|
-
{ colId: 'email', field: 'email', headerName: 'Email', width: 200 }
|
|
21
|
+
{ colId: 'email', field: 'email', headerName: 'Email', width: 200 },
|
|
22
22
|
];
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
const testRowData: TestData[] = [
|
|
25
25
|
{ id: 1, name: 'John Doe', age: 30, email: 'john@example.com' },
|
|
26
26
|
{ id: 2, name: 'Jane Smith', age: 25, email: 'jane@example.com' },
|
|
27
|
-
{ id: 3, name: 'Bob Johnson', age: 35, email: 'bob@example.com' }
|
|
27
|
+
{ id: 3, name: 'Bob Johnson', age: 35, email: 'bob@example.com' },
|
|
28
28
|
];
|
|
29
29
|
|
|
30
30
|
beforeEach(() => {
|
|
31
31
|
TestBed.configureTestingModule({
|
|
32
|
-
providers: [
|
|
33
|
-
GridService,
|
|
34
|
-
provideExperimentalZonelessChangeDetection()
|
|
35
|
-
]
|
|
32
|
+
providers: [GridService, provideExperimentalZonelessChangeDetection()],
|
|
36
33
|
});
|
|
37
34
|
service = TestBed.inject(GridService);
|
|
38
35
|
api = service.createApi(testColumnDefs, [...testRowData]);
|
|
@@ -74,7 +71,7 @@ describe('GridService', () => {
|
|
|
74
71
|
const sortApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
75
72
|
// Sort by age descending
|
|
76
73
|
sortApi.setSortModel([{ colId: 'age', sort: 'desc' }]);
|
|
77
|
-
|
|
74
|
+
|
|
78
75
|
const sortedData = sortApi.getRowData();
|
|
79
76
|
expect(sortedData[0].age).toBe(35);
|
|
80
77
|
expect(sortedData[2].age).toBe(25);
|
|
@@ -84,7 +81,7 @@ describe('GridService', () => {
|
|
|
84
81
|
const sortApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
85
82
|
// Sort by age ascending
|
|
86
83
|
sortApi.setSortModel([{ colId: 'age', sort: 'asc' }]);
|
|
87
|
-
|
|
84
|
+
|
|
88
85
|
const sortedData = sortApi.getRowData();
|
|
89
86
|
expect(sortedData[0].age).toBe(25);
|
|
90
87
|
expect(sortedData[2].age).toBe(35);
|
|
@@ -93,14 +90,14 @@ describe('GridService', () => {
|
|
|
93
90
|
it('should handle transaction - add rows and respect sorting', () => {
|
|
94
91
|
const sortApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
95
92
|
sortApi.setSortModel([{ colId: 'name', sort: 'asc' }]);
|
|
96
|
-
|
|
93
|
+
|
|
97
94
|
// Initial alpha: Bob Johnson (35), Jane Smith (25), John Doe (30)
|
|
98
95
|
expect(sortApi.getDisplayedRowAtIndex(0)?.data.name).toBe('Bob Johnson');
|
|
99
96
|
|
|
100
97
|
sortApi.applyTransaction({
|
|
101
|
-
add: [{ id: 4, name: 'Alice', age: 28, email: 'alice@example.com' }]
|
|
98
|
+
add: [{ id: 4, name: 'Alice', age: 28, email: 'alice@example.com' }],
|
|
102
99
|
});
|
|
103
|
-
|
|
100
|
+
|
|
104
101
|
// Alice should now be first
|
|
105
102
|
expect(sortApi.getDisplayedRowAtIndex(0)?.data.name).toBe('Alice');
|
|
106
103
|
expect(sortApi.getDisplayedRowCount()).toBe(4);
|
|
@@ -109,14 +106,14 @@ describe('GridService', () => {
|
|
|
109
106
|
it('should handle transaction - update rows and respect filtering', () => {
|
|
110
107
|
const filterApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
111
108
|
filterApi.setFilterModel({
|
|
112
|
-
age: { filterType: 'number', type: 'greaterThan', filter: 30 }
|
|
109
|
+
age: { filterType: 'number', type: 'greaterThan', filter: 30 },
|
|
113
110
|
});
|
|
114
|
-
|
|
111
|
+
|
|
115
112
|
expect(filterApi.getDisplayedRowCount()).toBe(1); // Only Bob (35)
|
|
116
113
|
|
|
117
114
|
// Update Jane (25) to be 40
|
|
118
115
|
filterApi.applyTransaction({
|
|
119
|
-
update: [{ id: 2, name: 'Jane Smith', age: 40, email: 'jane@example.com' }]
|
|
116
|
+
update: [{ id: 2, name: 'Jane Smith', age: 40, email: 'jane@example.com' }],
|
|
120
117
|
});
|
|
121
118
|
|
|
122
119
|
expect(filterApi.getDisplayedRowCount()).toBe(2); // Bob and Jane
|
|
@@ -127,9 +124,14 @@ describe('GridService', () => {
|
|
|
127
124
|
const firstNode = api.getDisplayedRowAtIndex(0);
|
|
128
125
|
if (!firstNode || !firstNode.id) return;
|
|
129
126
|
|
|
130
|
-
const removeData: TestData = {
|
|
127
|
+
const removeData: TestData = {
|
|
128
|
+
id: firstNode.data.id,
|
|
129
|
+
name: firstNode.data.name,
|
|
130
|
+
age: firstNode.data.age,
|
|
131
|
+
email: firstNode.data.email,
|
|
132
|
+
};
|
|
131
133
|
const result = api.applyTransaction({
|
|
132
|
-
remove: [removeData]
|
|
134
|
+
remove: [removeData],
|
|
133
135
|
});
|
|
134
136
|
|
|
135
137
|
expect(result?.remove.length).toBe(1);
|
|
@@ -138,7 +140,7 @@ describe('GridService', () => {
|
|
|
138
140
|
|
|
139
141
|
it('should get and set filter model', () => {
|
|
140
142
|
const filterModel: FilterModel = {
|
|
141
|
-
name: { filterType: 'text', type: 'contains', filter: 'John' }
|
|
143
|
+
name: { filterType: 'text', type: 'contains', filter: 'John' },
|
|
142
144
|
};
|
|
143
145
|
|
|
144
146
|
api.setFilterModel(filterModel);
|
|
@@ -149,7 +151,7 @@ describe('GridService', () => {
|
|
|
149
151
|
it('should apply text filter - contains', () => {
|
|
150
152
|
const filterApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
151
153
|
filterApi.setFilterModel({
|
|
152
|
-
name: { filterType: 'text', type: 'contains', filter: 'John' }
|
|
154
|
+
name: { filterType: 'text', type: 'contains', filter: 'John' },
|
|
153
155
|
});
|
|
154
156
|
|
|
155
157
|
const data = filterApi.getRowData();
|
|
@@ -160,12 +162,12 @@ describe('GridService', () => {
|
|
|
160
162
|
it('should apply text filter - starts with', () => {
|
|
161
163
|
const filterApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
162
164
|
filterApi.setFilterModel({
|
|
163
|
-
name: { filterType: 'text', type: 'startsWith', filter: 'J' }
|
|
165
|
+
name: { filterType: 'text', type: 'startsWith', filter: 'J' },
|
|
164
166
|
});
|
|
165
167
|
|
|
166
168
|
const data = filterApi.getRowData();
|
|
167
169
|
// Should match 'John Doe' and 'Jane Smith'
|
|
168
|
-
data.forEach(row => {
|
|
170
|
+
data.forEach((row) => {
|
|
169
171
|
expect(row.name.startsWith('J')).toBe(true);
|
|
170
172
|
});
|
|
171
173
|
});
|
|
@@ -173,11 +175,11 @@ describe('GridService', () => {
|
|
|
173
175
|
it('should apply text filter - ends with', () => {
|
|
174
176
|
const filterApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
175
177
|
filterApi.setFilterModel({
|
|
176
|
-
name: { filterType: 'text', type: 'endsWith', filter: 'e' }
|
|
178
|
+
name: { filterType: 'text', type: 'endsWith', filter: 'e' },
|
|
177
179
|
});
|
|
178
180
|
|
|
179
181
|
const data = filterApi.getRowData();
|
|
180
|
-
data.forEach(row => {
|
|
182
|
+
data.forEach((row) => {
|
|
181
183
|
expect(row.name.endsWith('e')).toBe(true);
|
|
182
184
|
});
|
|
183
185
|
});
|
|
@@ -185,7 +187,7 @@ describe('GridService', () => {
|
|
|
185
187
|
it('should apply text filter - equals', () => {
|
|
186
188
|
const filterApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
187
189
|
filterApi.setFilterModel({
|
|
188
|
-
name: { filterType: 'text', type: 'equals', filter: 'John Doe' }
|
|
190
|
+
name: { filterType: 'text', type: 'equals', filter: 'John Doe' },
|
|
189
191
|
});
|
|
190
192
|
|
|
191
193
|
const data = filterApi.getRowData();
|
|
@@ -196,11 +198,11 @@ describe('GridService', () => {
|
|
|
196
198
|
it('should apply number filter - greater than', () => {
|
|
197
199
|
const filterApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
198
200
|
filterApi.setFilterModel({
|
|
199
|
-
age: { filterType: 'number', type: 'greaterThan', filter: 28 }
|
|
201
|
+
age: { filterType: 'number', type: 'greaterThan', filter: 28 },
|
|
200
202
|
});
|
|
201
203
|
|
|
202
204
|
const data = filterApi.getRowData();
|
|
203
|
-
data.forEach(row => {
|
|
205
|
+
data.forEach((row) => {
|
|
204
206
|
expect(row.age).toBeGreaterThan(28);
|
|
205
207
|
});
|
|
206
208
|
});
|
|
@@ -208,11 +210,11 @@ describe('GridService', () => {
|
|
|
208
210
|
it('should apply number filter - less than', () => {
|
|
209
211
|
const filterApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
210
212
|
filterApi.setFilterModel({
|
|
211
|
-
age: { filterType: 'number', type: 'lessThan', filter: 32 }
|
|
213
|
+
age: { filterType: 'number', type: 'lessThan', filter: 32 },
|
|
212
214
|
});
|
|
213
215
|
|
|
214
216
|
const data = filterApi.getRowData();
|
|
215
|
-
data.forEach(row => {
|
|
217
|
+
data.forEach((row) => {
|
|
216
218
|
expect(row.age).toBeLessThan(32);
|
|
217
219
|
});
|
|
218
220
|
});
|
|
@@ -220,11 +222,11 @@ describe('GridService', () => {
|
|
|
220
222
|
it('should apply number filter - in range', () => {
|
|
221
223
|
const filterApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
222
224
|
filterApi.setFilterModel({
|
|
223
|
-
age: { filterType: 'number', type: 'inRange', filter: 26, filterTo: 34 }
|
|
225
|
+
age: { filterType: 'number', type: 'inRange', filter: 26, filterTo: 34 },
|
|
224
226
|
});
|
|
225
227
|
|
|
226
228
|
const data = filterApi.getRowData();
|
|
227
|
-
data.forEach(row => {
|
|
229
|
+
data.forEach((row) => {
|
|
228
230
|
expect(row.age).toBeGreaterThanOrEqual(26);
|
|
229
231
|
expect(row.age).toBeLessThanOrEqual(34);
|
|
230
232
|
});
|
|
@@ -234,17 +236,17 @@ describe('GridService', () => {
|
|
|
234
236
|
const dateData: any[] = [
|
|
235
237
|
{ id: 1, name: 'Event 1', date: '2024-01-15' },
|
|
236
238
|
{ id: 2, name: 'Event 2', date: '2024-06-20' },
|
|
237
|
-
{ id: 3, name: 'Event 3', date: '2024-12-01' }
|
|
239
|
+
{ id: 3, name: 'Event 3', date: '2024-12-01' },
|
|
238
240
|
];
|
|
239
241
|
const dateColumnDefs: any[] = [
|
|
240
242
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
241
243
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
242
|
-
{ colId: 'date', field: 'date', headerName: 'Date', filter: 'agDateColumnFilter' }
|
|
244
|
+
{ colId: 'date', field: 'date', headerName: 'Date', filter: 'agDateColumnFilter' },
|
|
243
245
|
];
|
|
244
246
|
|
|
245
247
|
const filterApi = service.createApi(dateColumnDefs, dateData);
|
|
246
248
|
filterApi.setFilterModel({
|
|
247
|
-
date: { filterType: 'date', type: 'greaterThan', filter: '2024-03-01' }
|
|
249
|
+
date: { filterType: 'date', type: 'greaterThan', filter: '2024-03-01' },
|
|
248
250
|
});
|
|
249
251
|
|
|
250
252
|
const data = filterApi.getRowData();
|
|
@@ -255,7 +257,7 @@ describe('GridService', () => {
|
|
|
255
257
|
it('should clear filter when model is empty', () => {
|
|
256
258
|
const filterApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
257
259
|
filterApi.setFilterModel({
|
|
258
|
-
name: { filterType: 'text', type: 'contains', filter: 'John' }
|
|
260
|
+
name: { filterType: 'text', type: 'contains', filter: 'John' },
|
|
259
261
|
});
|
|
260
262
|
expect(api.isFilterPresent()).toBe(true);
|
|
261
263
|
|
|
@@ -267,12 +269,12 @@ describe('GridService', () => {
|
|
|
267
269
|
const filterApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
268
270
|
filterApi.setFilterModel({
|
|
269
271
|
name: { filterType: 'text', type: 'startsWith', filter: 'J' },
|
|
270
|
-
age: { filterType: 'number', type: 'lessThan', filter: 30 }
|
|
272
|
+
age: { filterType: 'number', type: 'lessThan', filter: 30 },
|
|
271
273
|
});
|
|
272
274
|
|
|
273
275
|
const data = filterApi.getRowData();
|
|
274
276
|
// Should match 'Jane Smith' (starts with J and age < 30)
|
|
275
|
-
data.forEach(row => {
|
|
277
|
+
data.forEach((row) => {
|
|
276
278
|
expect(row.name.startsWith('J')).toBe(true);
|
|
277
279
|
expect(row.age).toBeLessThan(30);
|
|
278
280
|
});
|
|
@@ -284,16 +286,16 @@ describe('GridService', () => {
|
|
|
284
286
|
{ id: 1, name: 'John', department: 'Engineering', salary: 80000 },
|
|
285
287
|
{ id: 2, name: 'Jane', department: 'Engineering', salary: 90000 },
|
|
286
288
|
{ id: 3, name: 'Bob', department: 'Sales', salary: 70000 },
|
|
287
|
-
{ id: 4, name: 'Alice', department: 'Sales', salary: 75000 }
|
|
289
|
+
{ id: 4, name: 'Alice', department: 'Sales', salary: 75000 },
|
|
288
290
|
];
|
|
289
291
|
const groupColumnDefs: ColDef[] = [
|
|
290
292
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
291
293
|
{ colId: 'department', field: 'department', headerName: 'Department', rowGroup: true },
|
|
292
|
-
{ colId: 'salary', field: 'salary', headerName: 'Salary' }
|
|
294
|
+
{ colId: 'salary', field: 'salary', headerName: 'Salary' },
|
|
293
295
|
];
|
|
294
296
|
|
|
295
297
|
const groupApi = service.createApi(groupColumnDefs, groupData);
|
|
296
|
-
|
|
298
|
+
|
|
297
299
|
// With groups collapsed, should show 2 group rows
|
|
298
300
|
const displayedCount = groupApi.getDisplayedRowCount();
|
|
299
301
|
expect(displayedCount).toBe(2); // 2 groups (Engineering, Sales)
|
|
@@ -303,17 +305,17 @@ describe('GridService', () => {
|
|
|
303
305
|
const groupData: any[] = [
|
|
304
306
|
{ id: 1, name: 'John', department: 'Engineering' },
|
|
305
307
|
{ id: 2, name: 'Jane', department: 'Engineering' },
|
|
306
|
-
{ id: 3, name: 'Bob', department: 'Sales' }
|
|
308
|
+
{ id: 3, name: 'Bob', department: 'Sales' },
|
|
307
309
|
];
|
|
308
310
|
const groupColumnDefs: ColDef[] = [
|
|
309
311
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
310
|
-
{ colId: 'department', field: 'department', headerName: 'Department', rowGroup: true }
|
|
312
|
+
{ colId: 'department', field: 'department', headerName: 'Department', rowGroup: true },
|
|
311
313
|
];
|
|
312
314
|
|
|
313
315
|
const groupApi = service.createApi(groupColumnDefs, groupData);
|
|
314
|
-
|
|
316
|
+
|
|
315
317
|
// Initially groups are collapsed
|
|
316
|
-
|
|
318
|
+
const displayedCount = groupApi.getDisplayedRowCount();
|
|
317
319
|
expect(displayedCount).toBe(2); // 2 groups
|
|
318
320
|
});
|
|
319
321
|
|
|
@@ -321,16 +323,16 @@ describe('GridService', () => {
|
|
|
321
323
|
const groupData: any[] = [
|
|
322
324
|
{ id: 1, name: 'John', department: 'Engineering', salary: 80000 },
|
|
323
325
|
{ id: 2, name: 'Jane', department: 'Engineering', salary: 90000 },
|
|
324
|
-
{ id: 3, name: 'Bob', department: 'Sales', salary: 70000 }
|
|
326
|
+
{ id: 3, name: 'Bob', department: 'Sales', salary: 70000 },
|
|
325
327
|
];
|
|
326
328
|
const groupColumnDefs: ColDef[] = [
|
|
327
329
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
328
330
|
{ colId: 'department', field: 'department', headerName: 'Department', rowGroup: true },
|
|
329
|
-
{ colId: 'salary', field: 'salary', headerName: 'Salary', aggFunc: 'sum' }
|
|
331
|
+
{ colId: 'salary', field: 'salary', headerName: 'Salary', aggFunc: 'sum' },
|
|
330
332
|
];
|
|
331
333
|
|
|
332
334
|
const groupApi = service.createApi(groupColumnDefs, groupData);
|
|
333
|
-
|
|
335
|
+
|
|
334
336
|
// Verify grouping works
|
|
335
337
|
const displayedCount = groupApi.getDisplayedRowCount();
|
|
336
338
|
expect(displayedCount).toBeGreaterThanOrEqual(2); // At least 2 groups
|
|
@@ -340,16 +342,16 @@ describe('GridService', () => {
|
|
|
340
342
|
const groupData: any[] = [
|
|
341
343
|
{ id: 1, name: 'John', department: 'Engineering', level: 'Senior' },
|
|
342
344
|
{ id: 2, name: 'Jane', department: 'Engineering', level: 'Junior' },
|
|
343
|
-
{ id: 3, name: 'Bob', department: 'Sales', level: 'Senior' }
|
|
345
|
+
{ id: 3, name: 'Bob', department: 'Sales', level: 'Senior' },
|
|
344
346
|
];
|
|
345
347
|
const groupColumnDefs: ColDef[] = [
|
|
346
348
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
347
349
|
{ colId: 'department', field: 'department', headerName: 'Department', rowGroup: true },
|
|
348
|
-
{ colId: 'level', field: 'level', headerName: 'Level', rowGroup: true }
|
|
350
|
+
{ colId: 'level', field: 'level', headerName: 'Level', rowGroup: true },
|
|
349
351
|
];
|
|
350
352
|
|
|
351
353
|
const groupApi = service.createApi(groupColumnDefs, groupData);
|
|
352
|
-
|
|
354
|
+
|
|
353
355
|
// Should have hierarchical groups (Engineering/Senior, Engineering/Junior, Sales/Senior)
|
|
354
356
|
const displayedCount = groupApi.getDisplayedRowCount();
|
|
355
357
|
expect(displayedCount).toBe(2); // Engineering and Sales top level
|
|
@@ -368,11 +370,11 @@ describe('GridService', () => {
|
|
|
368
370
|
if (!firstNode) return;
|
|
369
371
|
|
|
370
372
|
const originalName = firstNode.data.name;
|
|
371
|
-
|
|
373
|
+
|
|
372
374
|
// Simulate cell edit via transaction
|
|
373
375
|
const newValue = 'Updated Name';
|
|
374
376
|
editApi.applyTransaction({
|
|
375
|
-
update: [{ ...firstNode.data, name: newValue }]
|
|
377
|
+
update: [{ ...firstNode.data, name: newValue }],
|
|
376
378
|
});
|
|
377
379
|
|
|
378
380
|
const updatedNode = editApi.getDisplayedRowAtIndex(0);
|
|
@@ -383,9 +385,9 @@ describe('GridService', () => {
|
|
|
383
385
|
it('should support read-only cells', () => {
|
|
384
386
|
const readOnlyColumnDefs: ColDef[] = [
|
|
385
387
|
{ colId: 'id', field: 'id', headerName: 'ID', editable: false },
|
|
386
|
-
{ colId: 'name', field: 'name', headerName: 'Name', editable: true }
|
|
388
|
+
{ colId: 'name', field: 'name', headerName: 'Name', editable: true },
|
|
387
389
|
];
|
|
388
|
-
|
|
390
|
+
|
|
389
391
|
expect(readOnlyColumnDefs[0].editable).toBe(false);
|
|
390
392
|
expect(readOnlyColumnDefs[1].editable).toBe(true);
|
|
391
393
|
});
|
|
@@ -393,23 +395,31 @@ describe('GridService', () => {
|
|
|
393
395
|
it('should support valueParser on column', () => {
|
|
394
396
|
const parserColumnDefs: ColDef[] = [
|
|
395
397
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
396
|
-
{
|
|
397
|
-
|
|
398
|
+
{
|
|
399
|
+
colId: 'value',
|
|
400
|
+
field: 'value',
|
|
401
|
+
headerName: 'Value',
|
|
402
|
+
valueParser: (params: any) => Number(params.newValue),
|
|
403
|
+
},
|
|
398
404
|
];
|
|
399
|
-
|
|
405
|
+
|
|
400
406
|
expect(parserColumnDefs[1].valueParser).toBeDefined();
|
|
401
407
|
});
|
|
402
408
|
|
|
403
409
|
it('should support valueSetter on column', () => {
|
|
404
410
|
const setterColumnDefs: ColDef[] = [
|
|
405
411
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
406
|
-
{
|
|
412
|
+
{
|
|
413
|
+
colId: 'name',
|
|
414
|
+
field: 'name',
|
|
415
|
+
headerName: 'Name',
|
|
407
416
|
valueSetter: (params: any) => {
|
|
408
417
|
params.data.name = params.newValue.toUpperCase();
|
|
409
418
|
return true;
|
|
410
|
-
}
|
|
419
|
+
},
|
|
420
|
+
},
|
|
411
421
|
];
|
|
412
|
-
|
|
422
|
+
|
|
413
423
|
expect(setterColumnDefs[1].valueSetter).toBeDefined();
|
|
414
424
|
});
|
|
415
425
|
|
|
@@ -418,13 +428,13 @@ describe('GridService', () => {
|
|
|
418
428
|
const pinColumnDefs: ColDef[] = [
|
|
419
429
|
{ colId: 'id', field: 'id', headerName: 'ID', pinned: 'left' },
|
|
420
430
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
421
|
-
{ colId: 'value', field: 'value', headerName: 'Value' }
|
|
431
|
+
{ colId: 'value', field: 'value', headerName: 'Value' },
|
|
422
432
|
];
|
|
423
|
-
|
|
433
|
+
|
|
424
434
|
const pinApi = service.createApi(pinColumnDefs, [...testRowData]);
|
|
425
435
|
const columns = pinApi.getAllColumns();
|
|
426
|
-
|
|
427
|
-
const pinnedCol = columns.find(c => c.pinned === 'left');
|
|
436
|
+
|
|
437
|
+
const pinnedCol = columns.find((c) => c.pinned === 'left');
|
|
428
438
|
expect(pinnedCol).toBeDefined();
|
|
429
439
|
expect(pinnedCol?.colId).toBe('id');
|
|
430
440
|
});
|
|
@@ -433,13 +443,13 @@ describe('GridService', () => {
|
|
|
433
443
|
const pinColumnDefs: ColDef[] = [
|
|
434
444
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
435
445
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
436
|
-
{ colId: 'value', field: 'value', headerName: 'Value', pinned: 'right' }
|
|
446
|
+
{ colId: 'value', field: 'value', headerName: 'Value', pinned: 'right' },
|
|
437
447
|
];
|
|
438
|
-
|
|
448
|
+
|
|
439
449
|
const pinApi = service.createApi(pinColumnDefs, [...testRowData]);
|
|
440
450
|
const columns = pinApi.getAllColumns();
|
|
441
|
-
|
|
442
|
-
const pinnedCol = columns.find(c => c.pinned === 'right');
|
|
451
|
+
|
|
452
|
+
const pinnedCol = columns.find((c) => c.pinned === 'right');
|
|
443
453
|
expect(pinnedCol).toBeDefined();
|
|
444
454
|
expect(pinnedCol?.colId).toBe('value');
|
|
445
455
|
});
|
|
@@ -448,12 +458,12 @@ describe('GridService', () => {
|
|
|
448
458
|
const pinColumnDefs: ColDef[] = [
|
|
449
459
|
{ colId: 'id', field: 'id', headerName: 'ID', pinned: 'left' },
|
|
450
460
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
451
|
-
{ colId: 'value', field: 'value', headerName: 'Value', pinned: 'right' }
|
|
461
|
+
{ colId: 'value', field: 'value', headerName: 'Value', pinned: 'right' },
|
|
452
462
|
];
|
|
453
|
-
|
|
463
|
+
|
|
454
464
|
const pinApi = service.createApi(pinColumnDefs, [...testRowData]);
|
|
455
465
|
const state = pinApi.getState();
|
|
456
|
-
|
|
466
|
+
|
|
457
467
|
expect(state.columnPinning).toBeDefined();
|
|
458
468
|
expect(state.columnPinning?.left).toContain('id');
|
|
459
469
|
expect(state.columnPinning?.right).toContain('value');
|
|
@@ -464,15 +474,15 @@ describe('GridService', () => {
|
|
|
464
474
|
const rowData: any[] = [
|
|
465
475
|
{ id: 1, name: 'Row 1' },
|
|
466
476
|
{ id: 2, name: 'Row 2', pinned: 'top' },
|
|
467
|
-
{ id: 3, name: 'Row 3' }
|
|
477
|
+
{ id: 3, name: 'Row 3' },
|
|
468
478
|
];
|
|
469
|
-
|
|
479
|
+
|
|
470
480
|
const pinApi = service.createApi(testColumnDefs.slice(0, 2), rowData);
|
|
471
481
|
const displayedCount = pinApi.getDisplayedRowCount();
|
|
472
|
-
|
|
482
|
+
|
|
473
483
|
// Should have 3 rows total
|
|
474
484
|
expect(displayedCount).toBe(3);
|
|
475
|
-
|
|
485
|
+
|
|
476
486
|
// First row should be the pinned top one
|
|
477
487
|
const firstRow = pinApi.getDisplayedRowAtIndex(0);
|
|
478
488
|
expect(firstRow?.rowPinned).toBe('top');
|
|
@@ -482,11 +492,11 @@ describe('GridService', () => {
|
|
|
482
492
|
const rowData: any[] = [
|
|
483
493
|
{ id: 1, name: 'Row 1' },
|
|
484
494
|
{ id: 2, name: 'Row 2', pinned: 'bottom' },
|
|
485
|
-
{ id: 3, name: 'Row 3' }
|
|
495
|
+
{ id: 3, name: 'Row 3' },
|
|
486
496
|
];
|
|
487
|
-
|
|
497
|
+
|
|
488
498
|
const pinApi = service.createApi(testColumnDefs.slice(0, 2), rowData);
|
|
489
|
-
|
|
499
|
+
|
|
490
500
|
// Last row should be the pinned bottom one
|
|
491
501
|
const lastRowIndex = pinApi.getDisplayedRowCount() - 1;
|
|
492
502
|
const lastRow = pinApi.getDisplayedRowAtIndex(lastRowIndex);
|
|
@@ -499,19 +509,19 @@ describe('GridService', () => {
|
|
|
499
509
|
{ id: 2, name: 'Top 1', pinned: 'top' },
|
|
500
510
|
{ id: 3, name: 'Normal 2' },
|
|
501
511
|
{ id: 4, name: 'Bottom 1', pinned: 'bottom' },
|
|
502
|
-
{ id: 5, name: 'Top 2', pinned: 'top' }
|
|
512
|
+
{ id: 5, name: 'Top 2', pinned: 'top' },
|
|
503
513
|
];
|
|
504
|
-
|
|
514
|
+
|
|
505
515
|
const pinApi = service.createApi(testColumnDefs.slice(0, 2), rowData);
|
|
506
|
-
|
|
516
|
+
|
|
507
517
|
// Pinned top rows should come first
|
|
508
518
|
expect(pinApi.getDisplayedRowAtIndex(0)?.rowPinned).toBe('top');
|
|
509
519
|
expect(pinApi.getDisplayedRowAtIndex(1)?.rowPinned).toBe('top');
|
|
510
|
-
|
|
520
|
+
|
|
511
521
|
// Normal rows in middle
|
|
512
522
|
expect(pinApi.getDisplayedRowAtIndex(2)?.rowPinned).toBe(false);
|
|
513
523
|
expect(pinApi.getDisplayedRowAtIndex(3)?.rowPinned).toBe(false);
|
|
514
|
-
|
|
524
|
+
|
|
515
525
|
// Pinned bottom rows at end
|
|
516
526
|
expect(pinApi.getDisplayedRowAtIndex(4)?.rowPinned).toBe('bottom');
|
|
517
527
|
});
|
|
@@ -526,27 +536,27 @@ describe('GridService', () => {
|
|
|
526
536
|
const selectApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
527
537
|
const firstRow = selectApi.getDisplayedRowAtIndex(0);
|
|
528
538
|
if (!firstRow) return;
|
|
529
|
-
|
|
539
|
+
|
|
530
540
|
firstRow.selected = true;
|
|
531
541
|
const selected = selectApi.getSelectedRows();
|
|
532
|
-
|
|
542
|
+
|
|
533
543
|
expect(selected.length).toBe(1);
|
|
534
544
|
expect(selected[0].data.id).toBe(1);
|
|
535
545
|
});
|
|
536
546
|
|
|
537
547
|
it('should select multiple rows with Ctrl key', () => {
|
|
538
548
|
const selectApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
539
|
-
|
|
549
|
+
|
|
540
550
|
// Select first row
|
|
541
551
|
const firstRow = selectApi.getDisplayedRowAtIndex(0);
|
|
542
552
|
if (!firstRow) return;
|
|
543
553
|
firstRow.selected = true;
|
|
544
|
-
|
|
554
|
+
|
|
545
555
|
// Ctrl+click to select third row (multi-select)
|
|
546
556
|
const thirdRow = selectApi.getDisplayedRowAtIndex(2);
|
|
547
557
|
if (!thirdRow) return;
|
|
548
558
|
thirdRow.selected = true;
|
|
549
|
-
|
|
559
|
+
|
|
550
560
|
const selected = selectApi.getSelectedRows();
|
|
551
561
|
expect(selected.length).toBe(2);
|
|
552
562
|
});
|
|
@@ -554,7 +564,7 @@ describe('GridService', () => {
|
|
|
554
564
|
it('should select all rows', () => {
|
|
555
565
|
const selectApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
556
566
|
selectApi.selectAll();
|
|
557
|
-
|
|
567
|
+
|
|
558
568
|
const selected = selectApi.getSelectedRows();
|
|
559
569
|
expect(selected.length).toBe(3);
|
|
560
570
|
});
|
|
@@ -563,7 +573,7 @@ describe('GridService', () => {
|
|
|
563
573
|
const selectApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
564
574
|
selectApi.selectAll();
|
|
565
575
|
expect(selectApi.getSelectedRows().length).toBe(3);
|
|
566
|
-
|
|
576
|
+
|
|
567
577
|
selectApi.deselectAll();
|
|
568
578
|
expect(selectApi.getSelectedRows().length).toBe(0);
|
|
569
579
|
});
|
|
@@ -572,10 +582,10 @@ describe('GridService', () => {
|
|
|
572
582
|
const selectApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
573
583
|
const row = selectApi.getDisplayedRowAtIndex(0);
|
|
574
584
|
if (!row) return;
|
|
575
|
-
|
|
585
|
+
|
|
576
586
|
row.selected = true;
|
|
577
587
|
expect(selectApi.getSelectedRows().length).toBe(1);
|
|
578
|
-
|
|
588
|
+
|
|
579
589
|
row.selected = false;
|
|
580
590
|
expect(selectApi.getSelectedRows().length).toBe(0);
|
|
581
591
|
});
|
|
@@ -583,19 +593,50 @@ describe('GridService', () => {
|
|
|
583
593
|
it('should get selected row count', () => {
|
|
584
594
|
const selectApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
585
595
|
selectApi.selectAll();
|
|
586
|
-
|
|
596
|
+
|
|
587
597
|
const selectedCount = selectApi.getSelectedRows().length;
|
|
588
598
|
expect(selectedCount).toBe(3);
|
|
589
599
|
});
|
|
590
600
|
|
|
591
601
|
it('should support row selection with checkbox', () => {
|
|
592
602
|
const selectionColumnDefs: ColDef[] = [
|
|
593
|
-
{ colId: '
|
|
594
|
-
{ colId: '
|
|
595
|
-
{ colId: 'name', field: 'name', headerName: 'Name' }
|
|
603
|
+
{ colId: 'id', field: 'id', headerName: 'ID', checkboxSelection: true },
|
|
604
|
+
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
596
605
|
];
|
|
597
|
-
|
|
598
|
-
|
|
606
|
+
|
|
607
|
+
const selectionApi = service.createApi(selectionColumnDefs, [...testRowData]);
|
|
608
|
+
const columns = selectionApi.getAllColumns();
|
|
609
|
+
|
|
610
|
+
// Should have 3 columns: Dedicated Selection + ID + Name
|
|
611
|
+
expect(columns.length).toBe(3);
|
|
612
|
+
expect(columns[0].colId).toBe('ag-Grid-SelectionColumn');
|
|
613
|
+
expect(columns[0].pinned).toBe('left');
|
|
614
|
+
expect(columns[0].width).toBe(50);
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
describe('Dedicated Selection Column', () => {
|
|
618
|
+
it('should be created if checkboxSelection is true in any column', () => {
|
|
619
|
+
const defs: ColDef[] = [{ field: 'id', checkboxSelection: true }];
|
|
620
|
+
const api = service.createApi(defs, []);
|
|
621
|
+
expect(api.getAllColumns().some((c) => c.colId === 'ag-Grid-SelectionColumn')).toBe(true);
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
it('should be created if checkboxSelection is true in a nested column', () => {
|
|
625
|
+
const defs: any[] = [
|
|
626
|
+
{
|
|
627
|
+
headerName: 'Group',
|
|
628
|
+
children: [{ field: 'id', checkboxSelection: true }],
|
|
629
|
+
},
|
|
630
|
+
];
|
|
631
|
+
const api = service.createApi(defs, []);
|
|
632
|
+
expect(api.getAllColumns().some((c) => c.colId === 'ag-Grid-SelectionColumn')).toBe(true);
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('should not be created if no checkboxSelection is present', () => {
|
|
636
|
+
const defs: ColDef[] = [{ field: 'id' }];
|
|
637
|
+
const api = service.createApi(defs, []);
|
|
638
|
+
expect(api.getAllColumns().some((c) => c.colId === 'ag-Grid-SelectionColumn')).toBe(false);
|
|
639
|
+
});
|
|
599
640
|
});
|
|
600
641
|
|
|
601
642
|
// Aggregation Tests
|
|
@@ -603,112 +644,112 @@ describe('GridService', () => {
|
|
|
603
644
|
const aggData: any[] = [
|
|
604
645
|
{ id: 1, name: 'Item 1', value: 100 },
|
|
605
646
|
{ id: 2, name: 'Item 2', value: 200 },
|
|
606
|
-
{ id: 3, name: 'Item 3', value: 300 }
|
|
647
|
+
{ id: 3, name: 'Item 3', value: 300 },
|
|
607
648
|
];
|
|
608
649
|
const aggColumnDefs: ColDef[] = [
|
|
609
650
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
610
651
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
611
|
-
{ colId: 'value', field: 'value', headerName: 'Value', aggFunc: 'sum' }
|
|
652
|
+
{ colId: 'value', field: 'value', headerName: 'Value', aggFunc: 'sum' },
|
|
612
653
|
];
|
|
613
|
-
|
|
654
|
+
|
|
614
655
|
service.createApi(aggColumnDefs, aggData);
|
|
615
656
|
const agg = service.calculateColumnAggregations(aggData);
|
|
616
|
-
expect(agg
|
|
657
|
+
expect(agg.value).toBe(600);
|
|
617
658
|
});
|
|
618
659
|
|
|
619
660
|
it('should calculate average aggregation', () => {
|
|
620
661
|
const aggData: any[] = [
|
|
621
662
|
{ id: 1, name: 'Item 1', value: 100 },
|
|
622
663
|
{ id: 2, name: 'Item 2', value: 200 },
|
|
623
|
-
{ id: 3, name: 'Item 3', value: 300 }
|
|
664
|
+
{ id: 3, name: 'Item 3', value: 300 },
|
|
624
665
|
];
|
|
625
666
|
const aggColumnDefs: ColDef[] = [
|
|
626
667
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
627
|
-
{ colId: 'value', field: 'value', headerName: 'Value', aggFunc: 'avg' }
|
|
668
|
+
{ colId: 'value', field: 'value', headerName: 'Value', aggFunc: 'avg' },
|
|
628
669
|
];
|
|
629
|
-
|
|
670
|
+
|
|
630
671
|
service.createApi(aggColumnDefs, aggData);
|
|
631
672
|
const agg = service.calculateColumnAggregations(aggData);
|
|
632
|
-
expect(agg
|
|
673
|
+
expect(agg.value).toBe(200);
|
|
633
674
|
});
|
|
634
675
|
|
|
635
676
|
it('should calculate min/max aggregation', () => {
|
|
636
677
|
const aggData: any[] = [
|
|
637
678
|
{ id: 1, name: 'Item 1', value: 100 },
|
|
638
679
|
{ id: 2, name: 'Item 2', value: 50 },
|
|
639
|
-
{ id: 3, name: 'Item 3', value: 300 }
|
|
680
|
+
{ id: 3, name: 'Item 3', value: 300 },
|
|
640
681
|
];
|
|
641
682
|
const aggColumnDefs: ColDef[] = [
|
|
642
|
-
{ colId: 'value', field: 'value', headerName: 'Value', aggFunc: 'min' }
|
|
683
|
+
{ colId: 'value', field: 'value', headerName: 'Value', aggFunc: 'min' },
|
|
643
684
|
];
|
|
644
|
-
|
|
685
|
+
|
|
645
686
|
service.createApi(aggColumnDefs, aggData);
|
|
646
687
|
const agg = service.calculateColumnAggregations(aggData);
|
|
647
|
-
expect(agg
|
|
688
|
+
expect(agg.value).toBe(50);
|
|
648
689
|
});
|
|
649
690
|
|
|
650
691
|
it('should calculate max aggregation', () => {
|
|
651
692
|
const aggData: any[] = [
|
|
652
693
|
{ id: 1, name: 'Item 1', value: 100 },
|
|
653
694
|
{ id: 2, name: 'Item 2', value: 50 },
|
|
654
|
-
{ id: 3, name: 'Item 3', value: 300 }
|
|
695
|
+
{ id: 3, name: 'Item 3', value: 300 },
|
|
655
696
|
];
|
|
656
697
|
const aggColumnDefs: ColDef[] = [
|
|
657
|
-
{ colId: 'value', field: 'value', headerName: 'Value', aggFunc: 'max' }
|
|
698
|
+
{ colId: 'value', field: 'value', headerName: 'Value', aggFunc: 'max' },
|
|
658
699
|
];
|
|
659
|
-
|
|
700
|
+
|
|
660
701
|
service.createApi(aggColumnDefs, aggData);
|
|
661
702
|
const agg = service.calculateColumnAggregations(aggData);
|
|
662
|
-
expect(agg
|
|
703
|
+
expect(agg.value).toBe(300);
|
|
663
704
|
});
|
|
664
705
|
|
|
665
706
|
it('should calculate count aggregation', () => {
|
|
666
707
|
const aggData: any[] = [
|
|
667
708
|
{ id: 1, name: 'Item 1' },
|
|
668
709
|
{ id: 2, name: 'Item 2' },
|
|
669
|
-
{ id: 3, name: 'Item 3' }
|
|
710
|
+
{ id: 3, name: 'Item 3' },
|
|
670
711
|
];
|
|
671
712
|
const aggColumnDefs: ColDef[] = [
|
|
672
|
-
{ colId: 'id', field: 'id', headerName: 'ID', aggFunc: 'count' }
|
|
713
|
+
{ colId: 'id', field: 'id', headerName: 'ID', aggFunc: 'count' },
|
|
673
714
|
];
|
|
674
|
-
|
|
715
|
+
|
|
675
716
|
service.createApi(aggColumnDefs, aggData);
|
|
676
717
|
const agg = service.calculateColumnAggregations(aggData);
|
|
677
|
-
expect(agg
|
|
718
|
+
expect(agg.id).toBe(3);
|
|
678
719
|
});
|
|
679
720
|
|
|
680
721
|
it('should support custom aggregation function', () => {
|
|
681
722
|
const aggData: any[] = [
|
|
682
723
|
{ id: 1, value: 100 },
|
|
683
724
|
{ id: 2, value: 200 },
|
|
684
|
-
{ id: 3, value: 300 }
|
|
725
|
+
{ id: 3, value: 300 },
|
|
685
726
|
];
|
|
686
727
|
const customAggFunc = (params: any) => {
|
|
687
728
|
return params.values.reduce((sum: number, v: number) => sum + v, 0) * 2;
|
|
688
729
|
};
|
|
689
730
|
const aggColumnDefs: ColDef[] = [
|
|
690
|
-
{ colId: 'value', field: 'value', headerName: 'Value', aggFunc: customAggFunc }
|
|
731
|
+
{ colId: 'value', field: 'value', headerName: 'Value', aggFunc: customAggFunc },
|
|
691
732
|
];
|
|
692
|
-
|
|
733
|
+
|
|
693
734
|
service.createApi(aggColumnDefs, aggData);
|
|
694
735
|
const agg = service.calculateColumnAggregations(aggData);
|
|
695
|
-
expect(agg
|
|
736
|
+
expect(agg.value).toBe(1200); // (100+200+300) * 2
|
|
696
737
|
});
|
|
697
738
|
|
|
698
739
|
// Excel Export Tests
|
|
699
740
|
it('should export data as CSV', () => {
|
|
700
741
|
const exportData: any[] = [
|
|
701
742
|
{ id: 1, name: 'John', email: 'john@example.com' },
|
|
702
|
-
{ id: 2, name: 'Jane', email: 'jane@example.com' }
|
|
743
|
+
{ id: 2, name: 'Jane', email: 'jane@example.com' },
|
|
703
744
|
];
|
|
704
745
|
const exportColumnDefs: ColDef[] = [
|
|
705
746
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
706
747
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
707
|
-
{ colId: 'email', field: 'email', headerName: 'Email' }
|
|
748
|
+
{ colId: 'email', field: 'email', headerName: 'Email' },
|
|
708
749
|
];
|
|
709
|
-
|
|
750
|
+
|
|
710
751
|
const exportApi = service.createApi(exportColumnDefs, exportData);
|
|
711
|
-
|
|
752
|
+
|
|
712
753
|
// Mock downloadFile to avoid browser API issues in tests
|
|
713
754
|
(service as any).downloadFile = vi.fn();
|
|
714
755
|
expect(() => exportApi.exportDataAsCsv()).not.toThrow();
|
|
@@ -719,12 +760,12 @@ describe('GridService', () => {
|
|
|
719
760
|
const exportData: any[] = [{ id: 1, name: 'Test' }];
|
|
720
761
|
const exportColumnDefs: ColDef[] = [
|
|
721
762
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
722
|
-
{ colId: 'name', field: 'name', headerName: 'Name' }
|
|
763
|
+
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
723
764
|
];
|
|
724
|
-
|
|
765
|
+
|
|
725
766
|
const exportApi = service.createApi(exportColumnDefs, exportData);
|
|
726
767
|
(service as any).downloadFile = vi.fn();
|
|
727
|
-
|
|
768
|
+
|
|
728
769
|
exportApi.exportDataAsCsv({ fileName: 'custom-export.csv' });
|
|
729
770
|
expect((service as any).downloadFile).toHaveBeenCalledWith(
|
|
730
771
|
expect.any(String),
|
|
@@ -736,17 +777,17 @@ describe('GridService', () => {
|
|
|
736
777
|
it('should export only selected columns', () => {
|
|
737
778
|
const exportData: any[] = [
|
|
738
779
|
{ id: 1, name: 'John', email: 'john@example.com' },
|
|
739
|
-
{ id: 2, name: 'Jane', email: 'jane@example.com' }
|
|
780
|
+
{ id: 2, name: 'Jane', email: 'jane@example.com' },
|
|
740
781
|
];
|
|
741
782
|
const exportColumnDefs: ColDef[] = [
|
|
742
783
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
743
784
|
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
744
|
-
{ colId: 'email', field: 'email', headerName: 'Email' }
|
|
785
|
+
{ colId: 'email', field: 'email', headerName: 'Email' },
|
|
745
786
|
];
|
|
746
|
-
|
|
787
|
+
|
|
747
788
|
const exportApi = service.createApi(exportColumnDefs, exportData);
|
|
748
789
|
(service as any).downloadFile = vi.fn();
|
|
749
|
-
|
|
790
|
+
|
|
750
791
|
exportApi.exportDataAsCsv({ columnKeys: ['id', 'name'] });
|
|
751
792
|
const csvContent = (service as any).downloadFile.mock.calls[0][0];
|
|
752
793
|
// Should not contain email column
|
|
@@ -757,12 +798,12 @@ describe('GridService', () => {
|
|
|
757
798
|
const exportData: any[] = [{ id: 1, name: 'Test' }];
|
|
758
799
|
const exportColumnDefs: ColDef[] = [
|
|
759
800
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
760
|
-
{ colId: 'name', field: 'name', headerName: 'Name' }
|
|
801
|
+
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
761
802
|
];
|
|
762
|
-
|
|
803
|
+
|
|
763
804
|
const exportApi = service.createApi(exportColumnDefs, exportData);
|
|
764
805
|
(service as any).downloadFile = vi.fn();
|
|
765
|
-
|
|
806
|
+
|
|
766
807
|
exportApi.exportDataAsCsv({ skipHeader: true });
|
|
767
808
|
const csvContent = (service as any).downloadFile.mock.calls[0][0];
|
|
768
809
|
// First line should be data, not header
|
|
@@ -773,11 +814,11 @@ describe('GridService', () => {
|
|
|
773
814
|
const exportData: any[] = [{ id: 1, name: 'Test' }];
|
|
774
815
|
const exportColumnDefs: ColDef[] = [
|
|
775
816
|
{ colId: 'id', field: 'id', headerName: 'ID' },
|
|
776
|
-
{ colId: 'name', field: 'name', headerName: 'Name' }
|
|
817
|
+
{ colId: 'name', field: 'name', headerName: 'Name' },
|
|
777
818
|
];
|
|
778
|
-
|
|
819
|
+
|
|
779
820
|
const exportApi = service.createApi(exportColumnDefs, exportData);
|
|
780
|
-
|
|
821
|
+
|
|
781
822
|
// Mock URL methods
|
|
782
823
|
if (typeof URL.createObjectURL === 'undefined') {
|
|
783
824
|
URL.createObjectURL = vi.fn().mockReturnValue('blob:test');
|
|
@@ -785,7 +826,7 @@ describe('GridService', () => {
|
|
|
785
826
|
if (typeof URL.revokeObjectURL === 'undefined') {
|
|
786
827
|
URL.revokeObjectURL = vi.fn();
|
|
787
828
|
}
|
|
788
|
-
|
|
829
|
+
|
|
789
830
|
// We expect it not to throw during the setup phase
|
|
790
831
|
expect(() => exportApi.exportDataAsExcel()).not.toThrow();
|
|
791
832
|
});
|
|
@@ -793,7 +834,7 @@ describe('GridService', () => {
|
|
|
793
834
|
it('should get displayed row at index', () => {
|
|
794
835
|
const sortedApi = service.createApi(testColumnDefs, [
|
|
795
836
|
{ id: 10, name: 'First', age: 20, email: 'first@example.com' },
|
|
796
|
-
{ id: 20, name: 'Second', age: 25, email: 'second@example.com' }
|
|
837
|
+
{ id: 20, name: 'Second', age: 25, email: 'second@example.com' },
|
|
797
838
|
]);
|
|
798
839
|
const row = sortedApi.getDisplayedRowAtIndex(1);
|
|
799
840
|
expect(row).toBeTruthy();
|
|
@@ -821,12 +862,12 @@ describe('GridService', () => {
|
|
|
821
862
|
it('should support custom getRowId in gridOptions', () => {
|
|
822
863
|
const data = [
|
|
823
864
|
{ customId: 'A', name: 'John' },
|
|
824
|
-
{ customId: 'B', name: 'Jane' }
|
|
865
|
+
{ customId: 'B', name: 'Jane' },
|
|
825
866
|
];
|
|
826
867
|
const customApi = service.createApi(testColumnDefs, data, {
|
|
827
|
-
getRowId: (params) => params.data.customId
|
|
868
|
+
getRowId: (params) => params.data.customId,
|
|
828
869
|
});
|
|
829
|
-
|
|
870
|
+
|
|
830
871
|
expect(customApi.getDisplayedRowCount()).toBe(2);
|
|
831
872
|
expect(customApi.getRowNode('A')).toBeTruthy();
|
|
832
873
|
expect(customApi.getRowNode('B')).toBeTruthy();
|
|
@@ -836,11 +877,11 @@ describe('GridService', () => {
|
|
|
836
877
|
it('should handle rows with missing fields', () => {
|
|
837
878
|
const data = [
|
|
838
879
|
{ id: 1, name: 'John' },
|
|
839
|
-
{ id: 2, age: 30 }
|
|
880
|
+
{ id: 2, age: 30 },
|
|
840
881
|
];
|
|
841
882
|
const missingApi = service.createApi(testColumnDefs, data);
|
|
842
883
|
expect(missingApi.getDisplayedRowCount()).toBe(2);
|
|
843
|
-
|
|
884
|
+
|
|
844
885
|
// Test sorting on missing field
|
|
845
886
|
missingApi.setSortModel([{ colId: 'age', sort: 'asc' }]);
|
|
846
887
|
// John (undefined age) should be at the end according to compareValues
|
|
@@ -851,12 +892,12 @@ describe('GridService', () => {
|
|
|
851
892
|
it('should handle duplicate IDs (last one wins in map)', () => {
|
|
852
893
|
const data = [
|
|
853
894
|
{ id: 'dup', name: 'First' },
|
|
854
|
-
{ id: 'dup', name: 'Second' }
|
|
895
|
+
{ id: 'dup', name: 'Second' },
|
|
855
896
|
];
|
|
856
897
|
const dupApi = service.createApi(testColumnDefs, data);
|
|
857
|
-
|
|
898
|
+
|
|
858
899
|
expect(dupApi.getDisplayedRowCount()).toBe(2);
|
|
859
|
-
|
|
900
|
+
|
|
860
901
|
const node = dupApi.getRowNode('dup');
|
|
861
902
|
expect(node?.data.name).toBe('Second');
|
|
862
903
|
});
|
|
@@ -864,15 +905,18 @@ describe('GridService', () => {
|
|
|
864
905
|
it('should handle update transaction for non-existent row', () => {
|
|
865
906
|
const api = service.createApi(testColumnDefs, [{ id: 1, name: 'John' }]);
|
|
866
907
|
const result = api.applyTransaction({
|
|
867
|
-
update: [{ id: 99, name: 'Missing' }]
|
|
908
|
+
update: [{ id: 99, name: 'Missing' }],
|
|
868
909
|
});
|
|
869
|
-
|
|
910
|
+
|
|
870
911
|
expect(result?.update.length).toBe(0);
|
|
871
912
|
expect(api.getDisplayedRowCount()).toBe(1);
|
|
872
913
|
});
|
|
873
914
|
|
|
874
915
|
it('should handle sorting on non-existent column', () => {
|
|
875
|
-
const api = service.createApi(testColumnDefs, [
|
|
916
|
+
const api = service.createApi(testColumnDefs, [
|
|
917
|
+
{ id: 1, name: 'John' },
|
|
918
|
+
{ id: 2, name: 'Jane' },
|
|
919
|
+
]);
|
|
876
920
|
// Should not crash
|
|
877
921
|
api.setSortModel([{ colId: 'invalid', sort: 'asc' }]);
|
|
878
922
|
expect(api.getDisplayedRowCount()).toBe(2);
|
|
@@ -881,14 +925,14 @@ describe('GridService', () => {
|
|
|
881
925
|
it('should preserve selection across transactions', () => {
|
|
882
926
|
const api = service.createApi(testColumnDefs, [
|
|
883
927
|
{ id: 1, name: 'John' },
|
|
884
|
-
{ id: 2, name: 'Jane' }
|
|
928
|
+
{ id: 2, name: 'Jane' },
|
|
885
929
|
]);
|
|
886
|
-
|
|
930
|
+
|
|
887
931
|
const node1 = api.getRowNode('1')!;
|
|
888
932
|
node1.selected = true;
|
|
889
|
-
|
|
933
|
+
|
|
890
934
|
api.applyTransaction({ add: [{ id: 3, name: 'Bob' }] });
|
|
891
|
-
|
|
935
|
+
|
|
892
936
|
const sameNode1 = api.getRowNode('1')!;
|
|
893
937
|
expect(sameNode1.selected).toBe(true);
|
|
894
938
|
expect(api.getSelectedNodes().length).toBe(1);
|
|
@@ -901,53 +945,58 @@ describe('GridService', () => {
|
|
|
901
945
|
{ field: 'name', headerName: 'Name' },
|
|
902
946
|
{ field: 'dept', headerName: 'Dept', rowGroup: true },
|
|
903
947
|
{ field: 'location', headerName: 'Location', pivot: true },
|
|
904
|
-
{ field: 'salary', headerName: 'Salary', aggFunc: 'sum' }
|
|
948
|
+
{ field: 'salary', headerName: 'Salary', aggFunc: 'sum' },
|
|
905
949
|
];
|
|
906
|
-
|
|
950
|
+
|
|
907
951
|
const pivotData: any[] = [
|
|
908
952
|
{ id: 1, name: 'John', dept: 'Engineering', location: 'NY', salary: 1000 },
|
|
909
953
|
{ id: 2, name: 'Jane', dept: 'Engineering', location: 'SF', salary: 2000 },
|
|
910
954
|
{ id: 3, name: 'Bob', dept: 'Sales', location: 'NY', salary: 1500 },
|
|
911
955
|
{ id: 4, name: 'Alice', dept: 'Sales', location: 'SF', salary: 2500 },
|
|
912
|
-
{ id: 5, name: 'Charlie', dept: 'Engineering', location: 'NY', salary: 1200 }
|
|
956
|
+
{ id: 5, name: 'Charlie', dept: 'Engineering', location: 'NY', salary: 1200 },
|
|
913
957
|
];
|
|
914
958
|
|
|
915
959
|
it('should generate pivot columns correctly', () => {
|
|
916
960
|
const api = service.createApi(pivotColumnDefs, pivotData, { pivotMode: true });
|
|
917
961
|
const columns = api.getAllColumns();
|
|
918
|
-
const visibleColumns = columns.filter(c => c.visible);
|
|
919
|
-
|
|
962
|
+
const visibleColumns = columns.filter((c) => c.visible);
|
|
963
|
+
|
|
920
964
|
// Auto Group + 2 pivot columns (NY, SF) = 3
|
|
921
965
|
expect(visibleColumns.length).toBe(3);
|
|
922
|
-
expect(visibleColumns.find(c => c.colId.includes('NY'))).toBeTruthy();
|
|
923
|
-
expect(visibleColumns.find(c => c.colId.includes('SF'))).toBeTruthy();
|
|
966
|
+
expect(visibleColumns.find((c) => c.colId.includes('NY'))).toBeTruthy();
|
|
967
|
+
expect(visibleColumns.find((c) => c.colId.includes('SF'))).toBeTruthy();
|
|
924
968
|
});
|
|
925
969
|
|
|
926
970
|
it('should calculate pivoted values correctly', () => {
|
|
927
971
|
const api = service.createApi(pivotColumnDefs, pivotData, { pivotMode: true });
|
|
928
|
-
|
|
972
|
+
|
|
929
973
|
let engNode = null;
|
|
930
974
|
for (let i = 0; i < api.getDisplayedRowCount(); i++) {
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
975
|
+
const node = api.getDisplayedRowAtIndex(i);
|
|
976
|
+
if (node?.group && node.data.dept === 'Engineering') {
|
|
977
|
+
engNode = node;
|
|
978
|
+
break;
|
|
979
|
+
}
|
|
936
980
|
}
|
|
937
|
-
|
|
981
|
+
|
|
938
982
|
expect(engNode).toBeTruthy();
|
|
939
|
-
expect((engNode?.data as any).pivotData
|
|
940
|
-
expect((engNode?.data as any).pivotData
|
|
983
|
+
expect((engNode?.data as any).pivotData.NY.salary).toBe(2200);
|
|
984
|
+
expect((engNode?.data as any).pivotData.SF.salary).toBe(2000);
|
|
941
985
|
});
|
|
942
986
|
|
|
943
987
|
it('should toggle pivot mode via API', () => {
|
|
944
988
|
const api = service.createApi(pivotColumnDefs, pivotData, { pivotMode: false });
|
|
945
989
|
expect(api.isPivotMode()).toBe(false);
|
|
946
|
-
|
|
990
|
+
|
|
947
991
|
api.setPivotMode(true);
|
|
948
992
|
expect(api.isPivotMode()).toBe(true);
|
|
949
|
-
expect(
|
|
950
|
-
|
|
993
|
+
expect(
|
|
994
|
+
api
|
|
995
|
+
.getAllColumns()
|
|
996
|
+
.filter((c) => c.visible)
|
|
997
|
+
.some((c) => c.colId.startsWith('pivot_'))
|
|
998
|
+
).toBe(true);
|
|
999
|
+
|
|
951
1000
|
api.setPivotMode(false);
|
|
952
1001
|
expect(api.isPivotMode()).toBe(false);
|
|
953
1002
|
});
|
|
@@ -956,41 +1005,41 @@ describe('GridService', () => {
|
|
|
956
1005
|
describe('Master/Detail', () => {
|
|
957
1006
|
const mdColumnDefs: ColDef[] = [
|
|
958
1007
|
{ field: 'id', headerName: 'ID' },
|
|
959
|
-
{ field: 'name', headerName: 'Name' }
|
|
1008
|
+
{ field: 'name', headerName: 'Name' },
|
|
960
1009
|
];
|
|
961
|
-
|
|
1010
|
+
|
|
962
1011
|
const mdData: any[] = [
|
|
963
1012
|
{ id: 1, name: 'John' },
|
|
964
|
-
{ id: 2, name: 'Jane' }
|
|
1013
|
+
{ id: 2, name: 'Jane' },
|
|
965
1014
|
];
|
|
966
1015
|
|
|
967
1016
|
it('should identify master rows correctly', () => {
|
|
968
|
-
const api = service.createApi(mdColumnDefs, mdData, {
|
|
1017
|
+
const api = service.createApi(mdColumnDefs, mdData, {
|
|
969
1018
|
masterDetail: true,
|
|
970
|
-
isRowMaster: (data) => data.id === 1
|
|
1019
|
+
isRowMaster: (data) => data.id === 1,
|
|
971
1020
|
});
|
|
972
|
-
|
|
1021
|
+
|
|
973
1022
|
const node1 = api.getRowNode('1');
|
|
974
1023
|
const node2 = api.getRowNode('2');
|
|
975
|
-
|
|
1024
|
+
|
|
976
1025
|
expect(node1?.master).toBe(true);
|
|
977
1026
|
expect(node2?.master).toBe(false);
|
|
978
1027
|
});
|
|
979
1028
|
|
|
980
1029
|
it('should insert detail row when master is expanded', () => {
|
|
981
|
-
const api = service.createApi(mdColumnDefs, mdData, {
|
|
1030
|
+
const api = service.createApi(mdColumnDefs, mdData, {
|
|
982
1031
|
masterDetail: true,
|
|
983
|
-
isRowMaster: (data) => data.id === 1
|
|
1032
|
+
isRowMaster: (data) => data.id === 1,
|
|
984
1033
|
});
|
|
985
|
-
|
|
1034
|
+
|
|
986
1035
|
expect(api.getDisplayedRowCount()).toBe(2);
|
|
987
|
-
|
|
1036
|
+
|
|
988
1037
|
const node1 = api.getRowNode('1')!;
|
|
989
1038
|
api.setRowNodeExpanded(node1, true);
|
|
990
|
-
|
|
1039
|
+
|
|
991
1040
|
// Should now have 3 rows: Master 1, Detail 1, Master 2
|
|
992
1041
|
expect(api.getDisplayedRowCount()).toBe(3);
|
|
993
|
-
|
|
1042
|
+
|
|
994
1043
|
const detailNode = api.getDisplayedRowAtIndex(1);
|
|
995
1044
|
expect(detailNode?.detail).toBe(true);
|
|
996
1045
|
expect(detailNode?.id).toBe('1-detail');
|
|
@@ -998,42 +1047,957 @@ describe('GridService', () => {
|
|
|
998
1047
|
});
|
|
999
1048
|
|
|
1000
1049
|
it('should remove detail row when master is collapsed', () => {
|
|
1001
|
-
const api = service.createApi(mdColumnDefs, mdData, {
|
|
1050
|
+
const api = service.createApi(mdColumnDefs, mdData, {
|
|
1002
1051
|
masterDetail: true,
|
|
1003
|
-
isRowMaster: (data) => data.id === 1
|
|
1052
|
+
isRowMaster: (data) => data.id === 1,
|
|
1004
1053
|
});
|
|
1005
|
-
|
|
1054
|
+
|
|
1006
1055
|
const node1 = api.getRowNode('1')!;
|
|
1007
1056
|
api.setRowNodeExpanded(node1, true);
|
|
1008
1057
|
expect(api.getDisplayedRowCount()).toBe(3);
|
|
1009
|
-
|
|
1058
|
+
|
|
1010
1059
|
api.setRowNodeExpanded(node1, false);
|
|
1011
1060
|
expect(api.getDisplayedRowCount()).toBe(2);
|
|
1012
1061
|
});
|
|
1013
1062
|
|
|
1014
1063
|
it('should calculate correct Y positions for variable heights', () => {
|
|
1015
|
-
const api = service.createApi(mdColumnDefs, mdData, {
|
|
1064
|
+
const api = service.createApi(mdColumnDefs, mdData, {
|
|
1016
1065
|
masterDetail: true,
|
|
1017
1066
|
isRowMaster: (data) => data.id === 1,
|
|
1018
1067
|
rowHeight: 30,
|
|
1019
|
-
detailRowHeight: 100
|
|
1068
|
+
detailRowHeight: 100,
|
|
1020
1069
|
});
|
|
1021
|
-
|
|
1070
|
+
|
|
1022
1071
|
const node1 = api.getRowNode('1')!;
|
|
1023
1072
|
api.setRowNodeExpanded(node1, true);
|
|
1024
|
-
|
|
1073
|
+
|
|
1025
1074
|
// Row 0: Master (Y=0, H=30)
|
|
1026
1075
|
// Row 1: Detail (Y=30, H=100)
|
|
1027
1076
|
// Row 2: Master (Y=130, H=30)
|
|
1028
|
-
|
|
1077
|
+
|
|
1029
1078
|
expect(api.getRowY(0)).toBe(0);
|
|
1030
1079
|
expect(api.getRowY(1)).toBe(30);
|
|
1031
1080
|
expect(api.getRowY(2)).toBe(130);
|
|
1032
1081
|
expect(api.getTotalHeight()).toBe(160);
|
|
1033
|
-
|
|
1082
|
+
|
|
1034
1083
|
expect(api.getRowAtY(15)).toBe(0);
|
|
1035
1084
|
expect(api.getRowAtY(50)).toBe(1);
|
|
1036
1085
|
expect(api.getRowAtY(140)).toBe(2);
|
|
1037
1086
|
});
|
|
1038
1087
|
});
|
|
1088
|
+
|
|
1089
|
+
describe('Export Functions', () => {
|
|
1090
|
+
it('should export as CSV with default params', () => {
|
|
1091
|
+
const exportApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
1092
|
+
// Mock downloadFile
|
|
1093
|
+
const _originalDownload = (exportApi as any).downloadFile;
|
|
1094
|
+
(exportApi as any).downloadFile = vi.fn();
|
|
1095
|
+
|
|
1096
|
+
exportApi.exportDataAsCsv();
|
|
1097
|
+
|
|
1098
|
+
expect((exportApi as any).downloadFile).toHaveBeenCalled();
|
|
1099
|
+
const callArgs = (exportApi as any).downloadFile.mock.calls[0];
|
|
1100
|
+
expect(callArgs[2]).toBe('text/csv;charset=utf-8;');
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
it('should export as CSV with custom params', () => {
|
|
1104
|
+
const exportApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
1105
|
+
const _originalDownload = (exportApi as any).downloadFile;
|
|
1106
|
+
(exportApi as any).downloadFile = vi.fn();
|
|
1107
|
+
|
|
1108
|
+
exportApi.exportDataAsCsv({
|
|
1109
|
+
fileName: 'custom.csv',
|
|
1110
|
+
delimiter: ';',
|
|
1111
|
+
skipHeader: true,
|
|
1112
|
+
columnKeys: ['id', 'name'],
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
expect((exportApi as any).downloadFile).toHaveBeenCalled();
|
|
1116
|
+
const callArgs = (exportApi as any).downloadFile.mock.calls[0];
|
|
1117
|
+
expect(callArgs[1]).toBe('custom.csv');
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
it('should export as CSV with skipped columns', () => {
|
|
1121
|
+
const exportApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
1122
|
+
const _originalDownload = (exportApi as any).downloadFile;
|
|
1123
|
+
(exportApi as any).downloadFile = vi.fn();
|
|
1124
|
+
|
|
1125
|
+
exportApi.exportDataAsCsv({
|
|
1126
|
+
columnKeys: ['email'],
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
expect((exportApi as any).downloadFile).toHaveBeenCalled();
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
it('should handle CSV special characters', () => {
|
|
1133
|
+
const specialData = [
|
|
1134
|
+
{ id: 1, name: 'John, Jr.', age: 30, email: 'john@example.com' },
|
|
1135
|
+
{ id: 2, name: 'Jane "The Boss"', age: 25, email: 'jane@example.com' },
|
|
1136
|
+
{ id: 3, name: 'Bob\nJohnson', age: 35, email: 'bob@example.com' },
|
|
1137
|
+
];
|
|
1138
|
+
const exportApi = service.createApi(testColumnDefs, specialData);
|
|
1139
|
+
const _originalDownload = (exportApi as any).downloadFile;
|
|
1140
|
+
(exportApi as any).downloadFile = vi.fn();
|
|
1141
|
+
|
|
1142
|
+
exportApi.exportDataAsCsv();
|
|
1143
|
+
|
|
1144
|
+
expect((exportApi as any).downloadFile).toHaveBeenCalled();
|
|
1145
|
+
const csvContent = (exportApi as any).downloadFile.mock.calls[0][0];
|
|
1146
|
+
expect(csvContent).toContain('"John, Jr."');
|
|
1147
|
+
expect(csvContent).toContain('"Jane ""The Boss"""');
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
it('should export as Excel', () => {
|
|
1151
|
+
const exportApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
1152
|
+
// Excel export is async, just verify it doesn't throw
|
|
1153
|
+
expect(() => exportApi.exportDataAsExcel()).not.toThrow();
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
it('should export as Excel with custom params', () => {
|
|
1157
|
+
const exportApi = service.createApi(testColumnDefs, [...testRowData]);
|
|
1158
|
+
expect(() =>
|
|
1159
|
+
exportApi.exportDataAsExcel({
|
|
1160
|
+
fileName: 'custom.xlsx',
|
|
1161
|
+
sheetName: 'Data',
|
|
1162
|
+
skipHeader: true,
|
|
1163
|
+
columnKeys: ['id', 'name'],
|
|
1164
|
+
})
|
|
1165
|
+
).not.toThrow();
|
|
1166
|
+
});
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
describe('Column Operations', () => {
|
|
1170
|
+
it('should move column', () => {
|
|
1171
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1172
|
+
const columns = api.getAllColumns();
|
|
1173
|
+
expect(columns[0].colId).toBe('id');
|
|
1174
|
+
|
|
1175
|
+
api.moveColumn(columns[0], 2);
|
|
1176
|
+
|
|
1177
|
+
const movedColumns = api.getAllColumns();
|
|
1178
|
+
expect(movedColumns[2].colId).toBe('id');
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
it('should set column width', () => {
|
|
1182
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1183
|
+
const column = api.getColumn('name');
|
|
1184
|
+
|
|
1185
|
+
api.setColumnWidth(column!, 200);
|
|
1186
|
+
|
|
1187
|
+
expect(column?.width).toBe(200);
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
it('should set column pinned', () => {
|
|
1191
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1192
|
+
const column = api.getColumn('id');
|
|
1193
|
+
|
|
1194
|
+
api.setColumnPinned(column!, 'left');
|
|
1195
|
+
|
|
1196
|
+
expect(column?.pinned).toBe('left');
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
it('should set column visible', () => {
|
|
1200
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1201
|
+
const column = api.getColumn('name');
|
|
1202
|
+
|
|
1203
|
+
api.setColumnVisible(column!, false);
|
|
1204
|
+
|
|
1205
|
+
expect(column?.visible).toBe(false);
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
it('should set column sort', () => {
|
|
1209
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1210
|
+
const column = api.getColumn('age');
|
|
1211
|
+
|
|
1212
|
+
api.setColumnSort(column!, 'asc', false);
|
|
1213
|
+
|
|
1214
|
+
expect(column?.sort).toBe('asc');
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
it('should auto-size columns', () => {
|
|
1218
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1219
|
+
const columns = api.getAllColumns();
|
|
1220
|
+
|
|
1221
|
+
api.autoSizeColumns(columns.map((c) => c.colId));
|
|
1222
|
+
|
|
1223
|
+
// Columns should have been resized
|
|
1224
|
+
expect(columns[0].width).toBeGreaterThan(0);
|
|
1225
|
+
});
|
|
1226
|
+
|
|
1227
|
+
it('should get column state', () => {
|
|
1228
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1229
|
+
const state = api.getColumnState();
|
|
1230
|
+
|
|
1231
|
+
expect(state).toBeDefined();
|
|
1232
|
+
expect(state.length).toBeGreaterThan(0);
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
it('should apply column state', () => {
|
|
1236
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1237
|
+
const state = api.getColumnState();
|
|
1238
|
+
|
|
1239
|
+
// Modify state
|
|
1240
|
+
state[0].width = 300;
|
|
1241
|
+
|
|
1242
|
+
api.applyColumnState({ state, applyOrder: true });
|
|
1243
|
+
|
|
1244
|
+
expect(state[0].width).toBe(300);
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
it('should reset column state', () => {
|
|
1248
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1249
|
+
|
|
1250
|
+
api.resetColumnState();
|
|
1251
|
+
|
|
1252
|
+
// Should not throw
|
|
1253
|
+
});
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
describe('Clipboard Operations', () => {
|
|
1257
|
+
it('should copy to clipboard', () => {
|
|
1258
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1259
|
+
// Mock navigator.clipboard
|
|
1260
|
+
const originalClipboard = navigator.clipboard;
|
|
1261
|
+
Object.assign(navigator, {
|
|
1262
|
+
clipboard: { writeText: vi.fn().mockResolvedValue(undefined) },
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
api.copyToClipboard();
|
|
1266
|
+
|
|
1267
|
+
expect(navigator.clipboard.writeText).toHaveBeenCalled();
|
|
1268
|
+
|
|
1269
|
+
// Restore
|
|
1270
|
+
Object.assign(navigator, { clipboard: originalClipboard });
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
it('should paste from clipboard', () => {
|
|
1274
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1275
|
+
// Mock navigator.clipboard
|
|
1276
|
+
const originalClipboard = navigator.clipboard;
|
|
1277
|
+
Object.assign(navigator, {
|
|
1278
|
+
clipboard: { readText: vi.fn().mockResolvedValue('test\tpaste') },
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
api.pasteFromClipboard();
|
|
1282
|
+
|
|
1283
|
+
expect(navigator.clipboard.readText).toHaveBeenCalled();
|
|
1284
|
+
|
|
1285
|
+
// Restore
|
|
1286
|
+
Object.assign(navigator, { clipboard: originalClipboard });
|
|
1287
|
+
});
|
|
1288
|
+
});
|
|
1289
|
+
|
|
1290
|
+
describe('Cell Editing', () => {
|
|
1291
|
+
it('should start editing cell', () => {
|
|
1292
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1293
|
+
api.startEditingCell({ rowIndex: 0, colKey: 'name' });
|
|
1294
|
+
// Should not throw
|
|
1295
|
+
});
|
|
1296
|
+
|
|
1297
|
+
it('should stop editing', () => {
|
|
1298
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1299
|
+
api.stopEditing();
|
|
1300
|
+
// Should not throw
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
it('should get editing cells', () => {
|
|
1304
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1305
|
+
const cells = api.getEditingCells();
|
|
1306
|
+
expect(Array.isArray(cells)).toBe(true);
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
it('should flash cells', () => {
|
|
1310
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1311
|
+
api.flashCells({
|
|
1312
|
+
rowNodes: [api.getDisplayedRowAtIndex(0)!],
|
|
1313
|
+
columns: ['name'],
|
|
1314
|
+
flashTime: 500,
|
|
1315
|
+
});
|
|
1316
|
+
// Should not throw
|
|
1317
|
+
});
|
|
1318
|
+
});
|
|
1319
|
+
|
|
1320
|
+
describe('Row Operations', () => {
|
|
1321
|
+
it('should refresh rows', () => {
|
|
1322
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1323
|
+
const node = api.getDisplayedRowAtIndex(0);
|
|
1324
|
+
api.refreshRows({ rowNodes: [node!] });
|
|
1325
|
+
// Should not throw
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
it('should refresh cells', () => {
|
|
1329
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1330
|
+
api.refreshCells({
|
|
1331
|
+
rowNodes: [api.getDisplayedRowAtIndex(0)!],
|
|
1332
|
+
columns: ['name'],
|
|
1333
|
+
});
|
|
1334
|
+
// Should not throw
|
|
1335
|
+
});
|
|
1336
|
+
|
|
1337
|
+
it('should refresh header', () => {
|
|
1338
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1339
|
+
api.refreshHeader();
|
|
1340
|
+
// Should not throw
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
it('should reset row heights', () => {
|
|
1344
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1345
|
+
api.resetRowHeights();
|
|
1346
|
+
// Should not throw
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1349
|
+
it('should get row height for row', () => {
|
|
1350
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1351
|
+
const height = api.getRowHeightForRow(0);
|
|
1352
|
+
expect(height).toBe(32);
|
|
1353
|
+
});
|
|
1354
|
+
});
|
|
1355
|
+
|
|
1356
|
+
describe('Scroll Operations', () => {
|
|
1357
|
+
it('should set scroll position', () => {
|
|
1358
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1359
|
+
api.setScrollPosition({ top: 100, left: 50 });
|
|
1360
|
+
// Should not throw
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
it('should get scroll position', () => {
|
|
1364
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1365
|
+
const pos = api.getScrollPosition();
|
|
1366
|
+
expect(pos).toBeDefined();
|
|
1367
|
+
});
|
|
1368
|
+
|
|
1369
|
+
it('should ensure column visible', () => {
|
|
1370
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1371
|
+
api.ensureColumnVisible('name');
|
|
1372
|
+
// Should not throw
|
|
1373
|
+
});
|
|
1374
|
+
|
|
1375
|
+
it('should ensure index visible', () => {
|
|
1376
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1377
|
+
api.ensureIndexVisible(10);
|
|
1378
|
+
// Should not throw
|
|
1379
|
+
});
|
|
1380
|
+
|
|
1381
|
+
it('should size columns to fit', () => {
|
|
1382
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1383
|
+
api.sizeColumnsToFit(800);
|
|
1384
|
+
// Should not throw
|
|
1385
|
+
});
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
describe('Pivot Mode', () => {
|
|
1389
|
+
it('should set pivot mode', () => {
|
|
1390
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1391
|
+
api.setPivotMode(true);
|
|
1392
|
+
expect(api.isPivotMode()).toBe(true);
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1395
|
+
it('should get pivot columns', () => {
|
|
1396
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1397
|
+
const pivotCols = api.getPivotColumns();
|
|
1398
|
+
expect(Array.isArray(pivotCols)).toBe(true);
|
|
1399
|
+
});
|
|
1400
|
+
|
|
1401
|
+
it('should get value columns', () => {
|
|
1402
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1403
|
+
const valueCols = api.getValueColumns();
|
|
1404
|
+
expect(Array.isArray(valueCols)).toBe(true);
|
|
1405
|
+
});
|
|
1406
|
+
|
|
1407
|
+
it('should get row group columns', () => {
|
|
1408
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1409
|
+
const groupCols = api.getRowGroupColumns();
|
|
1410
|
+
expect(Array.isArray(groupCols)).toBe(true);
|
|
1411
|
+
});
|
|
1412
|
+
|
|
1413
|
+
it('should get group display type', () => {
|
|
1414
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1415
|
+
const type = api.getGroupDisplayType();
|
|
1416
|
+
expect(type).toBe('singleColumn');
|
|
1417
|
+
});
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
describe('Tool Panels', () => {
|
|
1421
|
+
it('should set side bar visible', () => {
|
|
1422
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1423
|
+
api.setSideBarVisible(true);
|
|
1424
|
+
// Should not throw
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
it('should open tool panel', () => {
|
|
1428
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1429
|
+
api.openToolPanel('columns');
|
|
1430
|
+
// Should not throw
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
it('should close tool panel', () => {
|
|
1434
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1435
|
+
api.closeToolPanel();
|
|
1436
|
+
// Should not throw
|
|
1437
|
+
});
|
|
1438
|
+
|
|
1439
|
+
it('should enable filter tool panel', () => {
|
|
1440
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1441
|
+
api.enableFilterToolPanel();
|
|
1442
|
+
// Should not throw
|
|
1443
|
+
});
|
|
1444
|
+
|
|
1445
|
+
it('should enable columns tool panel', () => {
|
|
1446
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1447
|
+
api.enableColumnsToolPanel();
|
|
1448
|
+
// Should not throw
|
|
1449
|
+
});
|
|
1450
|
+
|
|
1451
|
+
it('should get tool panel', () => {
|
|
1452
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1453
|
+
const _panel = api.getToolPanel('columns');
|
|
1454
|
+
// May be null if not initialized
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
it('should check if tool panel is showing', () => {
|
|
1458
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1459
|
+
const showing = api.isToolPanelShowing();
|
|
1460
|
+
expect(typeof showing).toBe('boolean');
|
|
1461
|
+
});
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
describe('Context Menu', () => {
|
|
1465
|
+
it('should get context menu items', () => {
|
|
1466
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1467
|
+
const items = api.getContextMenuItems();
|
|
1468
|
+
expect(Array.isArray(items)).toBe(true);
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
it('should get main menu items', () => {
|
|
1472
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1473
|
+
const items = api.getMainMenuItems();
|
|
1474
|
+
expect(Array.isArray(items)).toBe(true);
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
it('should get header context menu items', () => {
|
|
1478
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1479
|
+
const items = api.getHeaderContextMenuItems();
|
|
1480
|
+
expect(Array.isArray(items)).toBe(true);
|
|
1481
|
+
});
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
describe('Focus Management', () => {
|
|
1485
|
+
it('should get focused cell', () => {
|
|
1486
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1487
|
+
const _cell = api.getFocusedCell();
|
|
1488
|
+
// May be null
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
it('should set focused cell', () => {
|
|
1492
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1493
|
+
api.setFocusedCell({
|
|
1494
|
+
rowIndex: 0,
|
|
1495
|
+
colKey: 'name',
|
|
1496
|
+
rowPinned: null,
|
|
1497
|
+
forceBrowserFocus: false,
|
|
1498
|
+
});
|
|
1499
|
+
// Should not throw
|
|
1500
|
+
});
|
|
1501
|
+
});
|
|
1502
|
+
|
|
1503
|
+
describe('Event Handling', () => {
|
|
1504
|
+
it('should add event listener', () => {
|
|
1505
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1506
|
+
const listener = vi.fn();
|
|
1507
|
+
api.addEventListener('rowClicked', listener);
|
|
1508
|
+
// Should not throw
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1511
|
+
it('should remove event listener', () => {
|
|
1512
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1513
|
+
const listener = vi.fn();
|
|
1514
|
+
api.addEventListener('rowClicked', listener);
|
|
1515
|
+
api.removeEventListener('rowClicked', listener);
|
|
1516
|
+
// Should not throw
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1519
|
+
it('should dispatch event', () => {
|
|
1520
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1521
|
+
api.dispatchEvent('rowClicked', { data: {} });
|
|
1522
|
+
// Should not throw
|
|
1523
|
+
});
|
|
1524
|
+
|
|
1525
|
+
it('should get event path', () => {
|
|
1526
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1527
|
+
const path = api.getEventPath();
|
|
1528
|
+
expect(Array.isArray(path)).toBe(true);
|
|
1529
|
+
});
|
|
1530
|
+
});
|
|
1531
|
+
|
|
1532
|
+
describe('Rendering', () => {
|
|
1533
|
+
it('should get rendered nodes', () => {
|
|
1534
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1535
|
+
const nodes = api.getRenderedNodes();
|
|
1536
|
+
expect(Array.isArray(nodes)).toBe(true);
|
|
1537
|
+
});
|
|
1538
|
+
|
|
1539
|
+
it('should get first rendered row', () => {
|
|
1540
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1541
|
+
const row = api.getFirstRenderedRow();
|
|
1542
|
+
expect(typeof row).toBe('number');
|
|
1543
|
+
});
|
|
1544
|
+
|
|
1545
|
+
it('should get last rendered row', () => {
|
|
1546
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1547
|
+
const row = api.getLastRenderedRow();
|
|
1548
|
+
expect(typeof row).toBe('number');
|
|
1549
|
+
});
|
|
1550
|
+
|
|
1551
|
+
it('should get vertical pixel range', () => {
|
|
1552
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1553
|
+
const range = api.getVerticalPixelRange();
|
|
1554
|
+
expect(range).toBeDefined();
|
|
1555
|
+
});
|
|
1556
|
+
|
|
1557
|
+
it('should get horizontal pixel range', () => {
|
|
1558
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1559
|
+
const range = api.getHorizontalPixelRange();
|
|
1560
|
+
expect(range).toBeDefined();
|
|
1561
|
+
});
|
|
1562
|
+
|
|
1563
|
+
it('should get pinned width', () => {
|
|
1564
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1565
|
+
const width = api.getPinnedWidth();
|
|
1566
|
+
expect(typeof width).toBe('number');
|
|
1567
|
+
});
|
|
1568
|
+
|
|
1569
|
+
it('should get right pinned width', () => {
|
|
1570
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1571
|
+
const width = api.getRightPinnedWidth();
|
|
1572
|
+
expect(typeof width).toBe('number');
|
|
1573
|
+
});
|
|
1574
|
+
|
|
1575
|
+
it('should get H scroll position', () => {
|
|
1576
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1577
|
+
const pos = api.getHScrollPosition();
|
|
1578
|
+
expect(typeof pos).toBe('number');
|
|
1579
|
+
});
|
|
1580
|
+
|
|
1581
|
+
it('should get V scroll position', () => {
|
|
1582
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1583
|
+
const pos = api.getVScrollPosition();
|
|
1584
|
+
expect(typeof pos).toBe('number');
|
|
1585
|
+
});
|
|
1586
|
+
});
|
|
1587
|
+
|
|
1588
|
+
describe('Localization', () => {
|
|
1589
|
+
it('should get locale text', () => {
|
|
1590
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1591
|
+
const text = api.getLocaleText();
|
|
1592
|
+
expect(typeof text).toBe('string');
|
|
1593
|
+
});
|
|
1594
|
+
|
|
1595
|
+
it('should set locale text', () => {
|
|
1596
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1597
|
+
api.setLocaleText('en', { apply: 'Apply' });
|
|
1598
|
+
// Should not throw
|
|
1599
|
+
});
|
|
1600
|
+
});
|
|
1601
|
+
|
|
1602
|
+
describe('Charts', () => {
|
|
1603
|
+
it('should get chart models', () => {
|
|
1604
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1605
|
+
const models = api.getChartModels();
|
|
1606
|
+
expect(Array.isArray(models)).toBe(true);
|
|
1607
|
+
});
|
|
1608
|
+
|
|
1609
|
+
it('should get chart toolbar items', () => {
|
|
1610
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1611
|
+
const items = api.getChartToolbarItems();
|
|
1612
|
+
expect(Array.isArray(items)).toBe(true);
|
|
1613
|
+
});
|
|
1614
|
+
|
|
1615
|
+
it('should hide popup', () => {
|
|
1616
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1617
|
+
api.hidePopup();
|
|
1618
|
+
// Should not throw
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1621
|
+
it('should get sparkline options', () => {
|
|
1622
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1623
|
+
const options = api.getSparklineOptions();
|
|
1624
|
+
expect(Array.isArray(options)).toBe(true);
|
|
1625
|
+
});
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
describe('Grid State', () => {
|
|
1629
|
+
it('should get grid panel', () => {
|
|
1630
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1631
|
+
const _panel = api.getGridPanel();
|
|
1632
|
+
// May be null
|
|
1633
|
+
});
|
|
1634
|
+
|
|
1635
|
+
it('should get row container element', () => {
|
|
1636
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1637
|
+
const _element = api.getRowContainerElement();
|
|
1638
|
+
// May be null
|
|
1639
|
+
});
|
|
1640
|
+
|
|
1641
|
+
it('should get body element', () => {
|
|
1642
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1643
|
+
const _element = api.getBodyElement();
|
|
1644
|
+
// May be null
|
|
1645
|
+
});
|
|
1646
|
+
|
|
1647
|
+
it('should get header elements', () => {
|
|
1648
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1649
|
+
const elements = api.getHeaderElements();
|
|
1650
|
+
expect(Array.isArray(elements)).toBe(true);
|
|
1651
|
+
});
|
|
1652
|
+
|
|
1653
|
+
it('should get center elements', () => {
|
|
1654
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1655
|
+
const elements = api.getCenterElements();
|
|
1656
|
+
expect(Array.isArray(elements)).toBe(true);
|
|
1657
|
+
});
|
|
1658
|
+
|
|
1659
|
+
it('should get left elements', () => {
|
|
1660
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1661
|
+
const elements = api.getLeftElements();
|
|
1662
|
+
expect(Array.isArray(elements)).toBe(true);
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
it('should get right elements', () => {
|
|
1666
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1667
|
+
const elements = api.getRightElements();
|
|
1668
|
+
expect(Array.isArray(elements)).toBe(true);
|
|
1669
|
+
});
|
|
1670
|
+
});
|
|
1671
|
+
|
|
1672
|
+
describe('Disabled State', () => {
|
|
1673
|
+
it('should set disabled', () => {
|
|
1674
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1675
|
+
api.setDisabled(true);
|
|
1676
|
+
// Should not throw
|
|
1677
|
+
});
|
|
1678
|
+
|
|
1679
|
+
it('should check if disabled', () => {
|
|
1680
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1681
|
+
const disabled = api.isDisabled();
|
|
1682
|
+
expect(typeof disabled).toBe('boolean');
|
|
1683
|
+
});
|
|
1684
|
+
});
|
|
1685
|
+
|
|
1686
|
+
describe('Row Information', () => {
|
|
1687
|
+
it('should get row position', () => {
|
|
1688
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1689
|
+
const pos = api.getRowPosition(0);
|
|
1690
|
+
expect(typeof pos).toBe('number');
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
it('should get row style', () => {
|
|
1694
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1695
|
+
const _style = api.getRowStyle(0);
|
|
1696
|
+
// May be null
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
it('should get row class', () => {
|
|
1700
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1701
|
+
const _cls = api.getRowClass(0);
|
|
1702
|
+
// May be null
|
|
1703
|
+
});
|
|
1704
|
+
|
|
1705
|
+
it('should get row id', () => {
|
|
1706
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1707
|
+
const node = api.getDisplayedRowAtIndex(0);
|
|
1708
|
+
const id = api.getRowId(node!);
|
|
1709
|
+
expect(id).toBe('1'); // First row has id: 1 in test data
|
|
1710
|
+
});
|
|
1711
|
+
|
|
1712
|
+
it('should check if row is master', () => {
|
|
1713
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1714
|
+
const node = api.getDisplayedRowAtIndex(0);
|
|
1715
|
+
const isMaster = api.isRowMaster(node!);
|
|
1716
|
+
expect(typeof isMaster).toBe('boolean');
|
|
1717
|
+
});
|
|
1718
|
+
|
|
1719
|
+
it('should get row group columns', () => {
|
|
1720
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1721
|
+
const cols = api.getRowGroupColumns();
|
|
1722
|
+
expect(Array.isArray(cols)).toBe(true);
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
it('should get column groups', () => {
|
|
1726
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1727
|
+
const groups = api.getColumnGroups();
|
|
1728
|
+
expect(Array.isArray(groups)).toBe(true);
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
it('should get column group', () => {
|
|
1732
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1733
|
+
const _group = api.getColumnGroup();
|
|
1734
|
+
// May be null
|
|
1735
|
+
});
|
|
1736
|
+
});
|
|
1737
|
+
|
|
1738
|
+
describe('Aggregation', () => {
|
|
1739
|
+
it('should refresh aggregated cols', () => {
|
|
1740
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1741
|
+
api.refreshAggregatedCols();
|
|
1742
|
+
// Should not throw
|
|
1743
|
+
});
|
|
1744
|
+
});
|
|
1745
|
+
|
|
1746
|
+
describe('ForEach Operations', () => {
|
|
1747
|
+
it('should forEach node', () => {
|
|
1748
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1749
|
+
const callback = vi.fn();
|
|
1750
|
+
api.forEachNode(callback);
|
|
1751
|
+
expect(callback).toHaveBeenCalled();
|
|
1752
|
+
});
|
|
1753
|
+
|
|
1754
|
+
it('should forEach node after filter', () => {
|
|
1755
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1756
|
+
const callback = vi.fn();
|
|
1757
|
+
api.forEachNodeAfterFilter(callback);
|
|
1758
|
+
expect(callback).toHaveBeenCalled();
|
|
1759
|
+
});
|
|
1760
|
+
|
|
1761
|
+
it('should forEach node after filter and sort', () => {
|
|
1762
|
+
const api = service.createApi(testColumnDefs, [...testRowData]);
|
|
1763
|
+
const callback = vi.fn();
|
|
1764
|
+
api.forEachNodeAfterFilterAndSort(callback);
|
|
1765
|
+
expect(callback).toHaveBeenCalled();
|
|
1766
|
+
});
|
|
1767
|
+
});
|
|
1768
|
+
|
|
1769
|
+
// ============================================================================
|
|
1770
|
+
// STATE PERSISTENCE TESTS
|
|
1771
|
+
// ============================================================================
|
|
1772
|
+
|
|
1773
|
+
describe('State Persistence', () => {
|
|
1774
|
+
beforeEach(() => {
|
|
1775
|
+
// Clear localStorage before each test
|
|
1776
|
+
localStorage.clear();
|
|
1777
|
+
});
|
|
1778
|
+
|
|
1779
|
+
describe('getState', () => {
|
|
1780
|
+
it('should return grid state object', () => {
|
|
1781
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1782
|
+
const state = service.getState();
|
|
1783
|
+
|
|
1784
|
+
expect(state).toBeDefined();
|
|
1785
|
+
expect(state.columnOrder).toBeDefined();
|
|
1786
|
+
expect(state.filter).toBeDefined();
|
|
1787
|
+
expect(state.sort).toBeDefined();
|
|
1788
|
+
});
|
|
1789
|
+
|
|
1790
|
+
it('should include column order, width, and visibility', () => {
|
|
1791
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1792
|
+
const state = service.getState();
|
|
1793
|
+
|
|
1794
|
+
expect(state.columnOrder).toHaveLength(4);
|
|
1795
|
+
expect(state.columnOrder?.[0]).toHaveProperty('colId');
|
|
1796
|
+
expect(state.columnOrder?.[0]).toHaveProperty('width');
|
|
1797
|
+
expect(state.columnOrder?.[0]).toHaveProperty('hide');
|
|
1798
|
+
});
|
|
1799
|
+
|
|
1800
|
+
it('should include filter model', () => {
|
|
1801
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1802
|
+
const state = service.getState();
|
|
1803
|
+
|
|
1804
|
+
expect(state.filter).toEqual({});
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1807
|
+
it('should include sort model', () => {
|
|
1808
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1809
|
+
const state = service.getState();
|
|
1810
|
+
|
|
1811
|
+
expect(state.sort).toEqual({ sortModel: [] });
|
|
1812
|
+
});
|
|
1813
|
+
});
|
|
1814
|
+
|
|
1815
|
+
describe('setState', () => {
|
|
1816
|
+
it('should restore column state', () => {
|
|
1817
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1818
|
+
const state: GridState = {
|
|
1819
|
+
columnOrder: [
|
|
1820
|
+
{ colId: 'id', width: 150, hide: false, pinned: false },
|
|
1821
|
+
{ colId: 'name', width: 200, hide: true, pinned: 'left' },
|
|
1822
|
+
],
|
|
1823
|
+
};
|
|
1824
|
+
|
|
1825
|
+
expect(() => service.setState(state)).not.toThrow();
|
|
1826
|
+
});
|
|
1827
|
+
|
|
1828
|
+
it('should restore filter state', () => {
|
|
1829
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1830
|
+
const state: GridState = {
|
|
1831
|
+
filter: { id: { filterType: 'number', type: 'greaterThan', filter: 1 } },
|
|
1832
|
+
};
|
|
1833
|
+
|
|
1834
|
+
expect(() => service.setState(state)).not.toThrow();
|
|
1835
|
+
});
|
|
1836
|
+
|
|
1837
|
+
it('should restore sort state', () => {
|
|
1838
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1839
|
+
const state: GridState = {
|
|
1840
|
+
sort: [{ colId: 'name', sort: 'asc' }],
|
|
1841
|
+
};
|
|
1842
|
+
|
|
1843
|
+
expect(() => service.setState(state)).not.toThrow();
|
|
1844
|
+
});
|
|
1845
|
+
|
|
1846
|
+
it('should emit state change event', () => {
|
|
1847
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1848
|
+
const stateChangeSpy = vi.fn();
|
|
1849
|
+
service.gridStateChanged$.subscribe(stateChangeSpy);
|
|
1850
|
+
|
|
1851
|
+
service.setState({ columnOrder: [] });
|
|
1852
|
+
|
|
1853
|
+
expect(stateChangeSpy).toHaveBeenCalledWith({ type: 'state-restored' });
|
|
1854
|
+
});
|
|
1855
|
+
});
|
|
1856
|
+
|
|
1857
|
+
describe('saveState', () => {
|
|
1858
|
+
it('should save state to localStorage', () => {
|
|
1859
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1860
|
+
service.saveState('test-key');
|
|
1861
|
+
|
|
1862
|
+
const saved = localStorage.getItem('test-key');
|
|
1863
|
+
expect(saved).not.toBeNull();
|
|
1864
|
+
expect(() => JSON.parse(saved!)).not.toThrow();
|
|
1865
|
+
});
|
|
1866
|
+
|
|
1867
|
+
it('should use default key if not provided', () => {
|
|
1868
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1869
|
+
service.saveState();
|
|
1870
|
+
|
|
1871
|
+
const saved = localStorage.getItem('argent-grid-state');
|
|
1872
|
+
expect(saved).not.toBeNull();
|
|
1873
|
+
});
|
|
1874
|
+
|
|
1875
|
+
it('should emit state saved event', () => {
|
|
1876
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1877
|
+
const stateChangeSpy = vi.fn();
|
|
1878
|
+
service.gridStateChanged$.subscribe(stateChangeSpy);
|
|
1879
|
+
|
|
1880
|
+
service.saveState('test-key');
|
|
1881
|
+
|
|
1882
|
+
expect(stateChangeSpy).toHaveBeenCalledWith({ type: 'state-saved', key: 'test-key' });
|
|
1883
|
+
});
|
|
1884
|
+
|
|
1885
|
+
it('should handle errors gracefully without throwing', () => {
|
|
1886
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1887
|
+
|
|
1888
|
+
// Just verify the method doesn't throw even if localStorage fails
|
|
1889
|
+
// (We can't easily mock localStorage in jsdom environment)
|
|
1890
|
+
expect(() => service.saveState('test-key')).not.toThrow();
|
|
1891
|
+
});
|
|
1892
|
+
});
|
|
1893
|
+
|
|
1894
|
+
describe('restoreState', () => {
|
|
1895
|
+
it('should restore state from localStorage', () => {
|
|
1896
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1897
|
+
const state: GridState = {
|
|
1898
|
+
columnOrder: [{ colId: 'id', width: 150, hide: false, pinned: false }],
|
|
1899
|
+
};
|
|
1900
|
+
localStorage.setItem('test-key', JSON.stringify(state));
|
|
1901
|
+
|
|
1902
|
+
const result = service.restoreState('test-key');
|
|
1903
|
+
|
|
1904
|
+
expect(result).toBe(true);
|
|
1905
|
+
});
|
|
1906
|
+
|
|
1907
|
+
it('should return false if no state exists', () => {
|
|
1908
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1909
|
+
const result = service.restoreState('non-existent-key');
|
|
1910
|
+
|
|
1911
|
+
expect(result).toBe(false);
|
|
1912
|
+
});
|
|
1913
|
+
|
|
1914
|
+
it('should use default key if not provided', () => {
|
|
1915
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1916
|
+
const state: GridState = { columnOrder: [] };
|
|
1917
|
+
localStorage.setItem('argent-grid-state', JSON.stringify(state));
|
|
1918
|
+
|
|
1919
|
+
const result = service.restoreState();
|
|
1920
|
+
|
|
1921
|
+
expect(result).toBe(true);
|
|
1922
|
+
});
|
|
1923
|
+
|
|
1924
|
+
it('should emit state restored event', () => {
|
|
1925
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1926
|
+
const state: GridState = { columnOrder: [] };
|
|
1927
|
+
localStorage.setItem('test-key', JSON.stringify(state));
|
|
1928
|
+
|
|
1929
|
+
const stateChangeSpy = vi.fn();
|
|
1930
|
+
service.gridStateChanged$.subscribe(stateChangeSpy);
|
|
1931
|
+
|
|
1932
|
+
service.restoreState('test-key');
|
|
1933
|
+
|
|
1934
|
+
expect(stateChangeSpy).toHaveBeenCalledWith({ type: 'state-restored', key: 'test-key' });
|
|
1935
|
+
});
|
|
1936
|
+
|
|
1937
|
+
it('should handle invalid JSON gracefully', () => {
|
|
1938
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1939
|
+
const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
1940
|
+
|
|
1941
|
+
localStorage.setItem('test-key', 'invalid-json');
|
|
1942
|
+
const result = service.restoreState('test-key');
|
|
1943
|
+
|
|
1944
|
+
expect(result).toBe(false);
|
|
1945
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
1946
|
+
|
|
1947
|
+
consoleSpy.mockRestore();
|
|
1948
|
+
});
|
|
1949
|
+
});
|
|
1950
|
+
|
|
1951
|
+
describe('clearState', () => {
|
|
1952
|
+
it('should remove state from localStorage', () => {
|
|
1953
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1954
|
+
localStorage.setItem('test-key', JSON.stringify({ columnOrder: [] }));
|
|
1955
|
+
|
|
1956
|
+
service.clearState('test-key');
|
|
1957
|
+
|
|
1958
|
+
expect(localStorage.getItem('test-key')).toBeNull();
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
it('should use default key if not provided', () => {
|
|
1962
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1963
|
+
localStorage.setItem('argent-grid-state', JSON.stringify({ columnOrder: [] }));
|
|
1964
|
+
|
|
1965
|
+
service.clearState();
|
|
1966
|
+
|
|
1967
|
+
expect(localStorage.getItem('argent-grid-state')).toBeNull();
|
|
1968
|
+
});
|
|
1969
|
+
|
|
1970
|
+
it('should emit state cleared event', () => {
|
|
1971
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1972
|
+
const stateChangeSpy = vi.fn();
|
|
1973
|
+
service.gridStateChanged$.subscribe(stateChangeSpy);
|
|
1974
|
+
|
|
1975
|
+
service.clearState('test-key');
|
|
1976
|
+
|
|
1977
|
+
expect(stateChangeSpy).toHaveBeenCalledWith({ type: 'state-cleared', key: 'test-key' });
|
|
1978
|
+
});
|
|
1979
|
+
});
|
|
1980
|
+
|
|
1981
|
+
describe('hasState', () => {
|
|
1982
|
+
it('should return true if state exists', () => {
|
|
1983
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1984
|
+
localStorage.setItem('test-key', JSON.stringify({ columnOrder: [] }));
|
|
1985
|
+
|
|
1986
|
+
expect(service.hasState('test-key')).toBe(true);
|
|
1987
|
+
});
|
|
1988
|
+
|
|
1989
|
+
it('should return false if state does not exist', () => {
|
|
1990
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1991
|
+
|
|
1992
|
+
expect(service.hasState('non-existent-key')).toBe(false);
|
|
1993
|
+
});
|
|
1994
|
+
|
|
1995
|
+
it('should use default key if not provided', () => {
|
|
1996
|
+
service.createApi(testColumnDefs, [...testRowData]);
|
|
1997
|
+
localStorage.setItem('argent-grid-state', JSON.stringify({ columnOrder: [] }));
|
|
1998
|
+
|
|
1999
|
+
expect(service.hasState()).toBe(true);
|
|
2000
|
+
});
|
|
2001
|
+
});
|
|
2002
|
+
});
|
|
1039
2003
|
});
|