@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.
Files changed (33) hide show
  1. package/README.md +76 -0
  2. package/dist/esm/column-chooser/column-chooser.component.js +199 -0
  3. package/dist/esm/column-header-filter/column-header-filter.component.js +497 -0
  4. package/dist/esm/datagrid-table/datagrid-table.component.js +573 -0
  5. package/dist/esm/index.js +14 -0
  6. package/dist/esm/ogrid/ogrid.component.js +77 -0
  7. package/dist/esm/pagination-controls/pagination-controls.component.js +189 -0
  8. package/dist/types/column-chooser/column-chooser.component.d.ts +26 -0
  9. package/dist/types/column-header-filter/column-header-filter.component.d.ts +67 -0
  10. package/dist/types/datagrid-table/datagrid-table.component.d.ts +131 -0
  11. package/dist/types/index.d.ts +12 -0
  12. package/dist/types/ogrid/ogrid.component.d.ts +14 -0
  13. package/dist/types/pagination-controls/pagination-controls.component.d.ts +15 -0
  14. package/jest-mocks/angular-cdk-overlay.cjs.js +38 -0
  15. package/jest-mocks/style-mock.js +1 -0
  16. package/jest.config.js +43 -0
  17. package/package.json +37 -0
  18. package/scripts/compile-styles.js +53 -0
  19. package/src/__tests__/column-chooser.component.spec.ts.skip +195 -0
  20. package/src/__tests__/column-header-filter.component.spec.ts.skip +401 -0
  21. package/src/__tests__/datagrid-table.component.spec.ts.skip +417 -0
  22. package/src/__tests__/exports.test.ts +54 -0
  23. package/src/__tests__/ogrid.component.spec.ts.skip +236 -0
  24. package/src/__tests__/pagination-controls.component.spec.ts.skip +190 -0
  25. package/src/column-chooser/column-chooser.component.ts +204 -0
  26. package/src/column-header-filter/column-header-filter.component.ts +528 -0
  27. package/src/datagrid-table/datagrid-table.component.scss +289 -0
  28. package/src/datagrid-table/datagrid-table.component.ts +636 -0
  29. package/src/index.ts +16 -0
  30. package/src/ogrid/ogrid.component.ts +78 -0
  31. package/src/pagination-controls/pagination-controls.component.ts +187 -0
  32. package/tsconfig.build.json +9 -0
  33. 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
+ });