@alaarab/ogrid-angular-radix 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/dist/esm/column-chooser/column-chooser.component.js +199 -0
- package/dist/esm/column-header-filter/column-header-filter.component.js +497 -0
- package/dist/esm/datagrid-table/datagrid-table.component.js +573 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/ogrid/ogrid.component.js +77 -0
- package/dist/esm/pagination-controls/pagination-controls.component.js +189 -0
- package/dist/types/column-chooser/column-chooser.component.d.ts +26 -0
- package/dist/types/column-header-filter/column-header-filter.component.d.ts +67 -0
- package/dist/types/datagrid-table/datagrid-table.component.d.ts +131 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/ogrid/ogrid.component.d.ts +14 -0
- package/dist/types/pagination-controls/pagination-controls.component.d.ts +15 -0
- package/jest-mocks/angular-cdk-overlay.cjs.js +38 -0
- package/jest-mocks/style-mock.js +1 -0
- package/jest.config.js +43 -0
- package/package.json +37 -0
- package/scripts/compile-styles.js +53 -0
- package/src/__tests__/column-chooser.component.spec.ts.skip +195 -0
- package/src/__tests__/column-header-filter.component.spec.ts.skip +401 -0
- package/src/__tests__/datagrid-table.component.spec.ts.skip +417 -0
- package/src/__tests__/exports.test.ts +54 -0
- package/src/__tests__/ogrid.component.spec.ts.skip +236 -0
- package/src/__tests__/pagination-controls.component.spec.ts.skip +190 -0
- package/src/column-chooser/column-chooser.component.ts +204 -0
- package/src/column-header-filter/column-header-filter.component.ts +528 -0
- package/src/datagrid-table/datagrid-table.component.scss +289 -0
- package/src/datagrid-table/datagrid-table.component.ts +636 -0
- package/src/index.ts +16 -0
- package/src/ogrid/ogrid.component.ts +78 -0
- package/src/pagination-controls/pagination-controls.component.ts +187 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
import { DebugElement } from '@angular/core';
|
|
3
|
+
import { By } from '@angular/platform-browser';
|
|
4
|
+
import { DataGridTableComponent } from '../datagrid-table/datagrid-table.component';
|
|
5
|
+
import type { IColumnDefinition, IOGridDataGridProps } from '@alaarab/ogrid-angular';
|
|
6
|
+
|
|
7
|
+
interface TestRow {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
status: string;
|
|
11
|
+
value: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('DataGridTableComponent', () => {
|
|
15
|
+
let component: DataGridTableComponent<TestRow>;
|
|
16
|
+
let fixture: ComponentFixture<DataGridTableComponent<TestRow>>;
|
|
17
|
+
|
|
18
|
+
const mockData: TestRow[] = [
|
|
19
|
+
{ id: '1', name: 'Alpha', status: 'Active', value: 100 },
|
|
20
|
+
{ id: '2', name: 'Beta', status: 'Closed', value: 200 },
|
|
21
|
+
{ id: '3', name: 'Gamma', status: 'Active', value: 150 },
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const mockColumns: IColumnDefinition[] = [
|
|
25
|
+
{ columnId: 'name', name: 'Name' },
|
|
26
|
+
{ columnId: 'status', name: 'Status' },
|
|
27
|
+
{ columnId: 'value', name: 'Value' },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const getBasicProps = (): IOGridDataGridProps<TestRow> => ({
|
|
31
|
+
columns: mockColumns,
|
|
32
|
+
items: mockData,
|
|
33
|
+
getRowId: (row) => row.id,
|
|
34
|
+
visibleColumns: new Set(['name', 'status', 'value']),
|
|
35
|
+
filters: {},
|
|
36
|
+
filterOptions: {},
|
|
37
|
+
loadingFilterOptions: {},
|
|
38
|
+
onColumnSort: jest.fn(),
|
|
39
|
+
onFilterChange: jest.fn(),
|
|
40
|
+
sortBy: undefined,
|
|
41
|
+
sortDirection: 'asc',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
beforeEach(async () => {
|
|
45
|
+
await TestBed.configureTestingModule({
|
|
46
|
+
imports: [DataGridTableComponent],
|
|
47
|
+
}).compileComponents();
|
|
48
|
+
|
|
49
|
+
fixture = TestBed.createComponent(DataGridTableComponent<TestRow>);
|
|
50
|
+
component = fixture.componentInstance;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should create', () => {
|
|
54
|
+
expect(component).toBeTruthy();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('Basic rendering', () => {
|
|
58
|
+
it('should render table element', () => {
|
|
59
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
60
|
+
fixture.detectChanges();
|
|
61
|
+
|
|
62
|
+
const table = fixture.debugElement.query(By.css('table'));
|
|
63
|
+
expect(table).toBeTruthy();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should render table headers for visible columns', () => {
|
|
67
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
68
|
+
fixture.detectChanges();
|
|
69
|
+
|
|
70
|
+
const headers = fixture.debugElement.queryAll(By.css('thead th'));
|
|
71
|
+
expect(headers.length).toBe(3);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should render all rows', () => {
|
|
75
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
76
|
+
fixture.detectChanges();
|
|
77
|
+
|
|
78
|
+
const rows = fixture.debugElement.queryAll(By.css('tbody tr'));
|
|
79
|
+
expect(rows.length).toBe(3);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should render cells for visible columns', () => {
|
|
83
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
84
|
+
fixture.detectChanges();
|
|
85
|
+
|
|
86
|
+
const firstRow = fixture.debugElement.queryAll(By.css('tbody tr'))[0];
|
|
87
|
+
const cells = firstRow.queryAll(By.css('td'));
|
|
88
|
+
expect(cells.length).toBe(3);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should hide columns not in visibleColumns', () => {
|
|
92
|
+
const props = {
|
|
93
|
+
...getBasicProps(),
|
|
94
|
+
visibleColumns: new Set(['name']),
|
|
95
|
+
};
|
|
96
|
+
fixture.componentRef.setInput('props', props);
|
|
97
|
+
fixture.detectChanges();
|
|
98
|
+
|
|
99
|
+
const headers = fixture.debugElement.queryAll(By.css('thead th'));
|
|
100
|
+
expect(headers.length).toBe(1);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should set aria-label when provided', () => {
|
|
104
|
+
const props = {
|
|
105
|
+
...getBasicProps(),
|
|
106
|
+
'aria-label': 'Projects grid',
|
|
107
|
+
};
|
|
108
|
+
fixture.componentRef.setInput('props', props);
|
|
109
|
+
fixture.detectChanges();
|
|
110
|
+
|
|
111
|
+
const region = fixture.debugElement.query(By.css('[role="region"]'));
|
|
112
|
+
expect(region.nativeElement.getAttribute('aria-label')).toBe('Projects grid');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should set aria-labelledby when provided', () => {
|
|
116
|
+
const props = {
|
|
117
|
+
...getBasicProps(),
|
|
118
|
+
'aria-labelledby': 'grid-heading',
|
|
119
|
+
};
|
|
120
|
+
fixture.componentRef.setInput('props', props);
|
|
121
|
+
fixture.detectChanges();
|
|
122
|
+
|
|
123
|
+
const region = fixture.debugElement.query(By.css('[role="region"]'));
|
|
124
|
+
expect(region.nativeElement.getAttribute('aria-labelledby')).toBe('grid-heading');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('Sorting', () => {
|
|
129
|
+
it('should show sort indicator on sorted column', () => {
|
|
130
|
+
const props = {
|
|
131
|
+
...getBasicProps(),
|
|
132
|
+
sortBy: 'name',
|
|
133
|
+
sortDirection: 'asc' as const,
|
|
134
|
+
};
|
|
135
|
+
fixture.componentRef.setInput('props', props);
|
|
136
|
+
fixture.detectChanges();
|
|
137
|
+
|
|
138
|
+
const nameHeader = fixture.debugElement.query(
|
|
139
|
+
By.css('column-header-filter')
|
|
140
|
+
);
|
|
141
|
+
expect(nameHeader).toBeTruthy();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should call onColumnSort when sortable header clicked', () => {
|
|
145
|
+
const onColumnSortMock = jest.fn();
|
|
146
|
+
const props = {
|
|
147
|
+
...getBasicProps(),
|
|
148
|
+
onColumnSort: onColumnSortMock,
|
|
149
|
+
};
|
|
150
|
+
fixture.componentRef.setInput('props', props);
|
|
151
|
+
fixture.detectChanges();
|
|
152
|
+
|
|
153
|
+
const sortButton = fixture.debugElement.query(
|
|
154
|
+
By.css('button[aria-label="Sort by Name"]')
|
|
155
|
+
);
|
|
156
|
+
sortButton.nativeElement.click();
|
|
157
|
+
|
|
158
|
+
expect(onColumnSortMock).toHaveBeenCalledWith('name');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should not call onColumnSort for non-sortable column', () => {
|
|
162
|
+
const onColumnSortMock = jest.fn();
|
|
163
|
+
const props = {
|
|
164
|
+
...getBasicProps(),
|
|
165
|
+
onColumnSort: onColumnSortMock,
|
|
166
|
+
};
|
|
167
|
+
fixture.componentRef.setInput('props', props);
|
|
168
|
+
fixture.detectChanges();
|
|
169
|
+
|
|
170
|
+
// Value column is not sortable
|
|
171
|
+
const headers = fixture.debugElement.queryAll(By.css('column-header-filter'));
|
|
172
|
+
const valueHeader = headers[2];
|
|
173
|
+
|
|
174
|
+
const sortButton = valueHeader.query(
|
|
175
|
+
By.css('button[aria-label^="Sort"]')
|
|
176
|
+
);
|
|
177
|
+
expect(sortButton).toBeNull();
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
describe('Filtering', () => {
|
|
182
|
+
it('should show filter icon for filterable columns', () => {
|
|
183
|
+
const props = {
|
|
184
|
+
...getBasicProps(),
|
|
185
|
+
columns: [
|
|
186
|
+
{ columnId: 'name', name: 'Name', sortable: true, filterable: { type: 'text' } },
|
|
187
|
+
{ columnId: 'status', name: 'Status', sortable: true, filterable: { type: 'multiSelect', filterField: 'status' } },
|
|
188
|
+
],
|
|
189
|
+
};
|
|
190
|
+
fixture.componentRef.setInput('props', props);
|
|
191
|
+
fixture.detectChanges();
|
|
192
|
+
|
|
193
|
+
const filterButtons = fixture.debugElement.queryAll(
|
|
194
|
+
By.css('button[aria-label^="Filter"]')
|
|
195
|
+
);
|
|
196
|
+
expect(filterButtons.length).toBe(2);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
it('should call onFilterChange when filter applied', () => {
|
|
200
|
+
const onFilterChangeMock = jest.fn();
|
|
201
|
+
const props = {
|
|
202
|
+
...getBasicProps(),
|
|
203
|
+
columns: [
|
|
204
|
+
{ columnId: 'name', name: 'Name', sortable: true, filterable: { type: 'text' } },
|
|
205
|
+
],
|
|
206
|
+
onFilterChange: onFilterChangeMock,
|
|
207
|
+
};
|
|
208
|
+
fixture.componentRef.setInput('props', props);
|
|
209
|
+
fixture.detectChanges();
|
|
210
|
+
|
|
211
|
+
// Open filter popover
|
|
212
|
+
const filterButton = fixture.debugElement.query(
|
|
213
|
+
By.css('button[aria-label="Filter Name"]')
|
|
214
|
+
);
|
|
215
|
+
filterButton.nativeElement.click();
|
|
216
|
+
fixture.detectChanges();
|
|
217
|
+
|
|
218
|
+
// Enter text and apply
|
|
219
|
+
const input = fixture.debugElement.query(
|
|
220
|
+
By.css('input[placeholder="Enter search term..."]')
|
|
221
|
+
);
|
|
222
|
+
input.nativeElement.value = 'test';
|
|
223
|
+
input.nativeElement.dispatchEvent(new Event('input'));
|
|
224
|
+
fixture.detectChanges();
|
|
225
|
+
|
|
226
|
+
const applyButton = fixture.debugElement.query(
|
|
227
|
+
By.css('.ogrid-header-filter__action-btn--primary')
|
|
228
|
+
);
|
|
229
|
+
applyButton.nativeElement.click();
|
|
230
|
+
|
|
231
|
+
expect(onFilterChangeMock).toHaveBeenCalled();
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('Empty state', () => {
|
|
236
|
+
it('should show empty state when no items', () => {
|
|
237
|
+
const props = {
|
|
238
|
+
...getBasicProps(),
|
|
239
|
+
items: [],
|
|
240
|
+
emptyState: {
|
|
241
|
+
title: 'No data',
|
|
242
|
+
description: 'There are no items to display',
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
fixture.componentRef.setInput('props', props);
|
|
246
|
+
fixture.detectChanges();
|
|
247
|
+
|
|
248
|
+
const emptyState = fixture.nativeElement.textContent;
|
|
249
|
+
expect(emptyState).toContain('No data');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should show default empty state when no custom provided', () => {
|
|
253
|
+
const props = {
|
|
254
|
+
...getBasicProps(),
|
|
255
|
+
items: [],
|
|
256
|
+
};
|
|
257
|
+
fixture.componentRef.setInput('props', props);
|
|
258
|
+
fixture.detectChanges();
|
|
259
|
+
|
|
260
|
+
const rows = fixture.debugElement.queryAll(By.css('tbody tr'));
|
|
261
|
+
expect(rows.length).toBe(0);
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
describe('Row selection', () => {
|
|
266
|
+
it('should show checkbox column when selectionMode is multiple', () => {
|
|
267
|
+
const props = {
|
|
268
|
+
...getBasicProps(),
|
|
269
|
+
selectionMode: 'multiple' as const,
|
|
270
|
+
selectedRowKeys: new Set<string>(),
|
|
271
|
+
onSelectionChange: jest.fn(),
|
|
272
|
+
};
|
|
273
|
+
fixture.componentRef.setInput('props', props);
|
|
274
|
+
fixture.detectChanges();
|
|
275
|
+
|
|
276
|
+
const checkboxes = fixture.debugElement.queryAll(
|
|
277
|
+
By.css('input[type="checkbox"]')
|
|
278
|
+
);
|
|
279
|
+
expect(checkboxes.length).toBeGreaterThan(0);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should call onSelectionChange when row checkbox clicked', () => {
|
|
283
|
+
const onSelectionChangeMock = jest.fn();
|
|
284
|
+
const props = {
|
|
285
|
+
...getBasicProps(),
|
|
286
|
+
selectionMode: 'multiple' as const,
|
|
287
|
+
selectedRowKeys: new Set<string>(),
|
|
288
|
+
onSelectionChange: onSelectionChangeMock,
|
|
289
|
+
};
|
|
290
|
+
fixture.componentRef.setInput('props', props);
|
|
291
|
+
fixture.detectChanges();
|
|
292
|
+
|
|
293
|
+
const checkboxes = fixture.debugElement.queryAll(
|
|
294
|
+
By.css('tbody input[type="checkbox"]')
|
|
295
|
+
);
|
|
296
|
+
checkboxes[0].nativeElement.click();
|
|
297
|
+
|
|
298
|
+
expect(onSelectionChangeMock).toHaveBeenCalled();
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should highlight selected rows', () => {
|
|
302
|
+
const props = {
|
|
303
|
+
...getBasicProps(),
|
|
304
|
+
selectionMode: 'multiple' as const,
|
|
305
|
+
selectedRowKeys: new Set(['1']),
|
|
306
|
+
onSelectionChange: jest.fn(),
|
|
307
|
+
};
|
|
308
|
+
fixture.componentRef.setInput('props', props);
|
|
309
|
+
fixture.detectChanges();
|
|
310
|
+
|
|
311
|
+
const firstRow = fixture.debugElement.queryAll(By.css('tbody tr'))[0];
|
|
312
|
+
expect(firstRow.nativeElement.classList.contains('ogrid-datagrid-table__row--selected')).toBe(true);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
describe('Column groups', () => {
|
|
317
|
+
it('should render column groups header row', () => {
|
|
318
|
+
const props = {
|
|
319
|
+
...getBasicProps(),
|
|
320
|
+
columnGroups: [
|
|
321
|
+
{ label: 'Group 1', columns: ['name', 'status'] },
|
|
322
|
+
],
|
|
323
|
+
};
|
|
324
|
+
fixture.componentRef.setInput('props', props);
|
|
325
|
+
fixture.detectChanges();
|
|
326
|
+
|
|
327
|
+
const groupRow = fixture.debugElement.query(By.css('thead tr:first-child'));
|
|
328
|
+
expect(groupRow).toBeTruthy();
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
describe('Loading state', () => {
|
|
333
|
+
it('should show loading indicator when isLoading is true', () => {
|
|
334
|
+
const props = {
|
|
335
|
+
...getBasicProps(),
|
|
336
|
+
isLoading: true,
|
|
337
|
+
};
|
|
338
|
+
fixture.componentRef.setInput('props', props);
|
|
339
|
+
fixture.detectChanges();
|
|
340
|
+
|
|
341
|
+
// Loading state may be shown in different ways
|
|
342
|
+
// Just verify component renders without error
|
|
343
|
+
const table = fixture.debugElement.query(By.css('table'));
|
|
344
|
+
expect(table).toBeTruthy();
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
describe('Custom cell rendering', () => {
|
|
349
|
+
it('should use custom cellValue when provided', () => {
|
|
350
|
+
const props = {
|
|
351
|
+
...getBasicProps(),
|
|
352
|
+
columns: [
|
|
353
|
+
{
|
|
354
|
+
columnId: 'name',
|
|
355
|
+
name: 'Name',
|
|
356
|
+
sortable: true,
|
|
357
|
+
cellValue: (row: TestRow) => `Custom: ${row.name}`,
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
visibleColumns: new Set(['name']),
|
|
361
|
+
};
|
|
362
|
+
fixture.componentRef.setInput('props', props);
|
|
363
|
+
fixture.detectChanges();
|
|
364
|
+
|
|
365
|
+
const firstCell = fixture.debugElement.query(By.css('tbody tr td'));
|
|
366
|
+
expect(firstCell.nativeElement.textContent.trim()).toBe('Custom: Alpha');
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe('Accessibility', () => {
|
|
371
|
+
it('should have proper table role', () => {
|
|
372
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
373
|
+
fixture.detectChanges();
|
|
374
|
+
|
|
375
|
+
const table = fixture.debugElement.query(By.css('table'));
|
|
376
|
+
expect(table.nativeElement.getAttribute('role')).toBe('table');
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('should have proper row roles', () => {
|
|
380
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
381
|
+
fixture.detectChanges();
|
|
382
|
+
|
|
383
|
+
const rows = fixture.debugElement.queryAll(By.css('tbody tr'));
|
|
384
|
+
rows.forEach((row) => {
|
|
385
|
+
expect(row.nativeElement.getAttribute('role')).toBe('row');
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should have proper cell roles', () => {
|
|
390
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
391
|
+
fixture.detectChanges();
|
|
392
|
+
|
|
393
|
+
const cells = fixture.debugElement.queryAll(By.css('tbody td'));
|
|
394
|
+
cells.forEach((cell) => {
|
|
395
|
+
expect(cell.nativeElement.getAttribute('role')).toBe('cell');
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
describe('Column pinning', () => {
|
|
401
|
+
it('should apply sticky positioning to pinned columns', () => {
|
|
402
|
+
const props = {
|
|
403
|
+
...getBasicProps(),
|
|
404
|
+
columns: [
|
|
405
|
+
{ columnId: 'name', name: 'Name', sortable: true, pinned: 'left' as const },
|
|
406
|
+
{ columnId: 'status', name: 'Status', sortable: true },
|
|
407
|
+
],
|
|
408
|
+
};
|
|
409
|
+
fixture.componentRef.setInput('props', props);
|
|
410
|
+
fixture.detectChanges();
|
|
411
|
+
|
|
412
|
+
const firstHeader = fixture.debugElement.query(By.css('thead th'));
|
|
413
|
+
const styles = firstHeader.nativeElement.style;
|
|
414
|
+
expect(styles.position).toBe('sticky');
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
describe('@alaarab/ogrid-angular-radix exports', () => {
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3
|
+
let mod: any;
|
|
4
|
+
|
|
5
|
+
beforeAll(() => {
|
|
6
|
+
mod = require('../index');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// Angular Radix UI components
|
|
10
|
+
it('exports OGridComponent', () => {
|
|
11
|
+
expect(mod.OGridComponent).toBeDefined();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('exports DataGridTableComponent', () => {
|
|
15
|
+
expect(mod.DataGridTableComponent).toBeDefined();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('exports ColumnHeaderFilterComponent', () => {
|
|
19
|
+
expect(mod.ColumnHeaderFilterComponent).toBeDefined();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('exports ColumnChooserComponent', () => {
|
|
23
|
+
expect(mod.ColumnChooserComponent).toBeDefined();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('exports PaginationControlsComponent', () => {
|
|
27
|
+
expect(mod.PaginationControlsComponent).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Re-exports from @alaarab/ogrid-angular
|
|
31
|
+
it('re-exports OGridService from angular adapter', () => {
|
|
32
|
+
expect(mod.OGridService).toBeDefined();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('re-exports DataGridStateService from angular adapter', () => {
|
|
36
|
+
expect(mod.DataGridStateService).toBeDefined();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('re-exports headless components from angular adapter', () => {
|
|
40
|
+
expect(mod.OGridLayoutComponent).toBeDefined();
|
|
41
|
+
expect(mod.StatusBarComponent).toBeDefined();
|
|
42
|
+
expect(mod.GridContextMenuComponent).toBeDefined();
|
|
43
|
+
expect(mod.SideBarComponent).toBeDefined();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('re-exports core utility functions', () => {
|
|
47
|
+
expect(mod.flattenColumns).toBeDefined();
|
|
48
|
+
expect(typeof mod.flattenColumns).toBe('function');
|
|
49
|
+
expect(mod.processClientSideData).toBeDefined();
|
|
50
|
+
expect(typeof mod.processClientSideData).toBe('function');
|
|
51
|
+
expect(mod.exportToCsv).toBeDefined();
|
|
52
|
+
expect(typeof mod.exportToCsv).toBe('function');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
import { DebugElement } from '@angular/core';
|
|
3
|
+
import { By } from '@angular/platform-browser';
|
|
4
|
+
import { OGridComponent } from '../ogrid/ogrid.component';
|
|
5
|
+
import type { IOGridProps } from '@alaarab/ogrid-angular';
|
|
6
|
+
|
|
7
|
+
interface TestRow {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
status: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe('OGridComponent', () => {
|
|
14
|
+
let component: OGridComponent<TestRow>;
|
|
15
|
+
let fixture: ComponentFixture<OGridComponent<TestRow>>;
|
|
16
|
+
|
|
17
|
+
const mockData: TestRow[] = [
|
|
18
|
+
{ id: '1', name: 'Alpha', status: 'Active' },
|
|
19
|
+
{ id: '2', name: 'Beta', status: 'Closed' },
|
|
20
|
+
{ id: '3', name: 'Gamma', status: 'Active' },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const getBasicProps = (): IOGridProps<TestRow> => ({
|
|
24
|
+
columns: [
|
|
25
|
+
{ columnId: 'name', name: 'Name', sortable: true },
|
|
26
|
+
{ columnId: 'status', name: 'Status', sortable: true },
|
|
27
|
+
],
|
|
28
|
+
data: mockData,
|
|
29
|
+
getRowId: (row) => row.id,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
beforeEach(async () => {
|
|
33
|
+
await TestBed.configureTestingModule({
|
|
34
|
+
imports: [OGridComponent],
|
|
35
|
+
}).compileComponents();
|
|
36
|
+
|
|
37
|
+
fixture = TestBed.createComponent(OGridComponent<TestRow>);
|
|
38
|
+
component = fixture.componentInstance;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should create', () => {
|
|
42
|
+
expect(component).toBeTruthy();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should create OGridService instance', () => {
|
|
46
|
+
expect(component.ogridService).toBeTruthy();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should render OGridLayoutComponent', () => {
|
|
50
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
51
|
+
fixture.detectChanges();
|
|
52
|
+
|
|
53
|
+
const layout = fixture.debugElement.query(By.css('ogrid-layout'));
|
|
54
|
+
expect(layout).toBeTruthy();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should render DataGridTableComponent', () => {
|
|
58
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
59
|
+
fixture.detectChanges();
|
|
60
|
+
|
|
61
|
+
const dataGrid = fixture.debugElement.query(By.css('ogrid-datagrid-table'));
|
|
62
|
+
expect(dataGrid).toBeTruthy();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should render PaginationControlsComponent', () => {
|
|
66
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
67
|
+
fixture.detectChanges();
|
|
68
|
+
|
|
69
|
+
const pagination = fixture.debugElement.query(By.css('ogrid-pagination-controls'));
|
|
70
|
+
expect(pagination).toBeTruthy();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should show ColumnChooser when columnChooser is toolbar', () => {
|
|
74
|
+
const props = {
|
|
75
|
+
...getBasicProps(),
|
|
76
|
+
columnChooser: 'toolbar' as const,
|
|
77
|
+
};
|
|
78
|
+
fixture.componentRef.setInput('props', props);
|
|
79
|
+
fixture.detectChanges();
|
|
80
|
+
|
|
81
|
+
const columnChooser = fixture.debugElement.query(By.css('ogrid-column-chooser'));
|
|
82
|
+
expect(columnChooser).toBeTruthy();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should not show ColumnChooser when columnChooser is false', () => {
|
|
86
|
+
const props = {
|
|
87
|
+
...getBasicProps(),
|
|
88
|
+
columnChooser: false,
|
|
89
|
+
};
|
|
90
|
+
fixture.componentRef.setInput('props', props);
|
|
91
|
+
fixture.detectChanges();
|
|
92
|
+
|
|
93
|
+
const columnChooser = fixture.debugElement.query(By.css('ogrid-column-chooser'));
|
|
94
|
+
expect(columnChooser).toBeNull();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should pass dataGridProps to DataGridTable', () => {
|
|
98
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
99
|
+
fixture.detectChanges();
|
|
100
|
+
|
|
101
|
+
const dataGridProps = component.dataGridProps();
|
|
102
|
+
expect(dataGridProps).toBeDefined();
|
|
103
|
+
expect(dataGridProps.columns).toBeDefined();
|
|
104
|
+
expect(dataGridProps.items).toBeDefined();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should configure OGridService with props', () => {
|
|
108
|
+
const props = getBasicProps();
|
|
109
|
+
fixture.componentRef.setInput('props', props);
|
|
110
|
+
fixture.detectChanges();
|
|
111
|
+
|
|
112
|
+
const spy = jest.spyOn(component.ogridService, 'configure');
|
|
113
|
+
component.dataGridProps();
|
|
114
|
+
expect(spy).toHaveBeenCalledWith(props);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should handle page size change and reset to page 1', () => {
|
|
118
|
+
fixture.componentRef.setInput('props', getBasicProps());
|
|
119
|
+
fixture.detectChanges();
|
|
120
|
+
|
|
121
|
+
const setPageSizeSpy = jest.spyOn(component.ogridService.pagination(), 'setPageSize');
|
|
122
|
+
const setPageSpy = jest.spyOn(component.ogridService.pagination(), 'setPage');
|
|
123
|
+
|
|
124
|
+
component.onPageSizeChange(25);
|
|
125
|
+
|
|
126
|
+
expect(setPageSizeSpy).toHaveBeenCalledWith(25);
|
|
127
|
+
expect(setPageSpy).toHaveBeenCalledWith(1);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should support custom className', () => {
|
|
131
|
+
const props = {
|
|
132
|
+
...getBasicProps(),
|
|
133
|
+
className: 'custom-grid-class',
|
|
134
|
+
};
|
|
135
|
+
fixture.componentRef.setInput('props', props);
|
|
136
|
+
fixture.detectChanges();
|
|
137
|
+
|
|
138
|
+
const layout = fixture.debugElement.query(By.css('ogrid-layout'));
|
|
139
|
+
// className is passed as input, verify it's set
|
|
140
|
+
expect(component.ogridService.className()).toBe('custom-grid-class');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should support initial sorting', () => {
|
|
144
|
+
const props = {
|
|
145
|
+
...getBasicProps(),
|
|
146
|
+
initialSortBy: 'name',
|
|
147
|
+
initialSortDirection: 'desc' as const,
|
|
148
|
+
};
|
|
149
|
+
fixture.componentRef.setInput('props', props);
|
|
150
|
+
fixture.detectChanges();
|
|
151
|
+
|
|
152
|
+
// Trigger computation
|
|
153
|
+
component.dataGridProps();
|
|
154
|
+
|
|
155
|
+
const sort = component.ogridService.sort();
|
|
156
|
+
expect(sort.field).toBe('name');
|
|
157
|
+
expect(sort.direction).toBe('desc');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should support initial page size', () => {
|
|
161
|
+
const props = {
|
|
162
|
+
...getBasicProps(),
|
|
163
|
+
initialPageSize: 25,
|
|
164
|
+
};
|
|
165
|
+
fixture.componentRef.setInput('props', props);
|
|
166
|
+
fixture.detectChanges();
|
|
167
|
+
|
|
168
|
+
component.dataGridProps();
|
|
169
|
+
|
|
170
|
+
const pagination = component.ogridService.pagination();
|
|
171
|
+
expect(pagination.pageSize).toBe(25);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should support custom pagination options', () => {
|
|
175
|
+
const props = {
|
|
176
|
+
...getBasicProps(),
|
|
177
|
+
pageSizeOptions: [10, 25, 50, 100],
|
|
178
|
+
};
|
|
179
|
+
fixture.componentRef.setInput('props', props);
|
|
180
|
+
fixture.detectChanges();
|
|
181
|
+
|
|
182
|
+
component.dataGridProps();
|
|
183
|
+
|
|
184
|
+
const pagination = component.ogridService.pagination();
|
|
185
|
+
expect(pagination.pageSizeOptions).toEqual([10, 25, 50, 100]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should support custom entity label', () => {
|
|
189
|
+
const props = {
|
|
190
|
+
...getBasicProps(),
|
|
191
|
+
entityLabelPlural: 'projects',
|
|
192
|
+
};
|
|
193
|
+
fixture.componentRef.setInput('props', props);
|
|
194
|
+
fixture.detectChanges();
|
|
195
|
+
|
|
196
|
+
component.dataGridProps();
|
|
197
|
+
|
|
198
|
+
const pagination = component.ogridService.pagination();
|
|
199
|
+
expect(pagination.entityLabelPlural).toBe('projects');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('should support disabling pagination', () => {
|
|
203
|
+
const props = {
|
|
204
|
+
...getBasicProps(),
|
|
205
|
+
pagination: false,
|
|
206
|
+
};
|
|
207
|
+
fixture.componentRef.setInput('props', props);
|
|
208
|
+
fixture.detectChanges();
|
|
209
|
+
|
|
210
|
+
// Component should still render but pagination controls won't be shown
|
|
211
|
+
const layout = fixture.debugElement.query(By.css('ogrid-layout'));
|
|
212
|
+
expect(layout).toBeTruthy();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should support toolbar content', () => {
|
|
216
|
+
const props = {
|
|
217
|
+
...getBasicProps(),
|
|
218
|
+
toolbar: 'Custom Toolbar Content',
|
|
219
|
+
};
|
|
220
|
+
fixture.componentRef.setInput('props', props);
|
|
221
|
+
fixture.detectChanges();
|
|
222
|
+
|
|
223
|
+
expect(component.ogridService.toolbar()).toBe('Custom Toolbar Content');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('should support toolbarBelow content', () => {
|
|
227
|
+
const props = {
|
|
228
|
+
...getBasicProps(),
|
|
229
|
+
toolbarBelow: 'Toolbar Below Content',
|
|
230
|
+
};
|
|
231
|
+
fixture.componentRef.setInput('props', props);
|
|
232
|
+
fixture.detectChanges();
|
|
233
|
+
|
|
234
|
+
expect(component.ogridService.toolbarBelow()).toBe('Toolbar Below Content');
|
|
235
|
+
});
|
|
236
|
+
});
|