@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,195 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { DebugElement } from '@angular/core';
3
+ import { By } from '@angular/platform-browser';
4
+ import { ColumnChooserComponent } from '../column-chooser/column-chooser.component';
5
+ import type { IColumnDefinition } from '@alaarab/ogrid-angular';
6
+
7
+ describe('ColumnChooserComponent', () => {
8
+ let component: ColumnChooserComponent;
9
+ let fixture: ComponentFixture<ColumnChooserComponent>;
10
+
11
+ const mockColumns: IColumnDefinition[] = [
12
+ { columnId: 'name', name: 'Name' },
13
+ { columnId: 'status', name: 'Status' },
14
+ { columnId: 'priority', name: 'Priority' },
15
+ ];
16
+
17
+ beforeEach(async () => {
18
+ await TestBed.configureTestingModule({
19
+ imports: [ColumnChooserComponent],
20
+ }).compileComponents();
21
+
22
+ fixture = TestBed.createComponent(ColumnChooserComponent);
23
+ component = fixture.componentInstance;
24
+ });
25
+
26
+ it('should create', () => {
27
+ expect(component).toBeTruthy();
28
+ });
29
+
30
+ it('should display column count in trigger button', () => {
31
+ fixture.componentRef.setInput('columns', mockColumns);
32
+ fixture.componentRef.setInput('visibleColumns', new Set(['name', 'status']));
33
+ fixture.detectChanges();
34
+
35
+ const trigger = fixture.debugElement.query(By.css('.ogrid-column-chooser__trigger'));
36
+ expect(trigger.nativeElement.textContent).toContain('Columns (2/3)');
37
+ });
38
+
39
+ it('should toggle dropdown when trigger clicked', () => {
40
+ fixture.componentRef.setInput('columns', mockColumns);
41
+ fixture.componentRef.setInput('visibleColumns', new Set(['name']));
42
+ fixture.detectChanges();
43
+
44
+ const trigger = fixture.debugElement.query(By.css('.ogrid-column-chooser__trigger'));
45
+
46
+ // Initially closed
47
+ expect(component.isOpen()).toBe(false);
48
+ let dropdown = fixture.debugElement.query(By.css('.ogrid-column-chooser__dropdown'));
49
+ expect(dropdown).toBeNull();
50
+
51
+ // Click to open
52
+ trigger.nativeElement.click();
53
+ fixture.detectChanges();
54
+ expect(component.isOpen()).toBe(true);
55
+ dropdown = fixture.debugElement.query(By.css('.ogrid-column-chooser__dropdown'));
56
+ expect(dropdown).toBeTruthy();
57
+
58
+ // Click to close
59
+ trigger.nativeElement.click();
60
+ fixture.detectChanges();
61
+ expect(component.isOpen()).toBe(false);
62
+ });
63
+
64
+ it('should show aria-expanded attribute', () => {
65
+ fixture.componentRef.setInput('columns', mockColumns);
66
+ fixture.componentRef.setInput('visibleColumns', new Set(['name']));
67
+ fixture.detectChanges();
68
+
69
+ const trigger = fixture.debugElement.query(By.css('.ogrid-column-chooser__trigger'));
70
+
71
+ expect(trigger.nativeElement.getAttribute('aria-expanded')).toBe('false');
72
+
73
+ component.toggle();
74
+ fixture.detectChanges();
75
+
76
+ expect(trigger.nativeElement.getAttribute('aria-expanded')).toBe('true');
77
+ });
78
+
79
+ it('should render all columns as checkboxes', () => {
80
+ fixture.componentRef.setInput('columns', mockColumns);
81
+ fixture.componentRef.setInput('visibleColumns', new Set(['name', 'status']));
82
+ component.isOpen.set(true);
83
+ fixture.detectChanges();
84
+
85
+ const items = fixture.debugElement.queryAll(By.css('.ogrid-column-chooser__item'));
86
+ expect(items.length).toBe(3);
87
+
88
+ const checkboxes = fixture.debugElement.queryAll(By.css('input[type="checkbox"]'));
89
+ expect(checkboxes.length).toBe(3);
90
+ });
91
+
92
+ it('should check visible columns', () => {
93
+ fixture.componentRef.setInput('columns', mockColumns);
94
+ fixture.componentRef.setInput('visibleColumns', new Set(['name', 'priority']));
95
+ component.isOpen.set(true);
96
+ fixture.detectChanges();
97
+
98
+ const checkboxes = fixture.debugElement.queryAll(By.css('input[type="checkbox"]'));
99
+ expect(checkboxes[0].nativeElement.checked).toBe(true); // name
100
+ expect(checkboxes[1].nativeElement.checked).toBe(false); // status
101
+ expect(checkboxes[2].nativeElement.checked).toBe(true); // priority
102
+ });
103
+
104
+ it('should emit visibilityChange when checkbox toggled', () => {
105
+ fixture.componentRef.setInput('columns', mockColumns);
106
+ fixture.componentRef.setInput('visibleColumns', new Set(['name']));
107
+ component.isOpen.set(true);
108
+ fixture.detectChanges();
109
+
110
+ const visibilityChangeSpy = jest.fn();
111
+ component.visibilityChange.subscribe(visibilityChangeSpy);
112
+
113
+ const checkboxes = fixture.debugElement.queryAll(By.css('input[type="checkbox"]'));
114
+
115
+ // Toggle status checkbox (currently unchecked)
116
+ checkboxes[1].nativeElement.checked = true;
117
+ checkboxes[1].nativeElement.dispatchEvent(new Event('change'));
118
+ fixture.detectChanges();
119
+
120
+ expect(visibilityChangeSpy).toHaveBeenCalledWith({
121
+ columnKey: 'status',
122
+ visible: true,
123
+ });
124
+ });
125
+
126
+ it('should emit visibilityChange for all columns when selectAll clicked', () => {
127
+ fixture.componentRef.setInput('columns', mockColumns);
128
+ fixture.componentRef.setInput('visibleColumns', new Set(['name']));
129
+ component.isOpen.set(true);
130
+ fixture.detectChanges();
131
+
132
+ const visibilityChangeSpy = jest.fn();
133
+ component.visibilityChange.subscribe(visibilityChangeSpy);
134
+
135
+ const selectAllBtn = fixture.debugElement.queryAll(By.css('.ogrid-column-chooser__btn'))[1];
136
+ selectAllBtn.nativeElement.click();
137
+ fixture.detectChanges();
138
+
139
+ // Should emit for status and priority (not already visible)
140
+ expect(visibilityChangeSpy).toHaveBeenCalledTimes(2);
141
+ expect(visibilityChangeSpy).toHaveBeenCalledWith({ columnKey: 'status', visible: true });
142
+ expect(visibilityChangeSpy).toHaveBeenCalledWith({ columnKey: 'priority', visible: true });
143
+ });
144
+
145
+ it('should emit visibilityChange for all columns when clearAll clicked', () => {
146
+ fixture.componentRef.setInput('columns', mockColumns);
147
+ fixture.componentRef.setInput('visibleColumns', new Set(['name', 'status', 'priority']));
148
+ component.isOpen.set(true);
149
+ fixture.detectChanges();
150
+
151
+ const visibilityChangeSpy = jest.fn();
152
+ component.visibilityChange.subscribe(visibilityChangeSpy);
153
+
154
+ const clearAllBtn = fixture.debugElement.queryAll(By.css('.ogrid-column-chooser__btn'))[0];
155
+ clearAllBtn.nativeElement.click();
156
+ fixture.detectChanges();
157
+
158
+ // Should emit for all 3 columns
159
+ expect(visibilityChangeSpy).toHaveBeenCalledTimes(3);
160
+ expect(visibilityChangeSpy).toHaveBeenCalledWith({ columnKey: 'name', visible: false });
161
+ expect(visibilityChangeSpy).toHaveBeenCalledWith({ columnKey: 'status', visible: false });
162
+ expect(visibilityChangeSpy).toHaveBeenCalledWith({ columnKey: 'priority', visible: false });
163
+ });
164
+
165
+ it('should close dropdown when clicking outside', () => {
166
+ fixture.componentRef.setInput('columns', mockColumns);
167
+ fixture.componentRef.setInput('visibleColumns', new Set(['name']));
168
+ component.isOpen.set(true);
169
+ fixture.detectChanges();
170
+
171
+ // Simulate click outside
172
+ const outsideElement = document.createElement('div');
173
+ document.body.appendChild(outsideElement);
174
+
175
+ const event = new MouseEvent('click', { bubbles: true });
176
+ Object.defineProperty(event, 'target', { value: outsideElement, enumerable: true });
177
+
178
+ component.onDocumentClick(event);
179
+ fixture.detectChanges();
180
+
181
+ expect(component.isOpen()).toBe(false);
182
+
183
+ document.body.removeChild(outsideElement);
184
+ });
185
+
186
+ it('should display correct header text', () => {
187
+ fixture.componentRef.setInput('columns', mockColumns);
188
+ fixture.componentRef.setInput('visibleColumns', new Set(['name', 'status']));
189
+ component.isOpen.set(true);
190
+ fixture.detectChanges();
191
+
192
+ const header = fixture.debugElement.query(By.css('.ogrid-column-chooser__header'));
193
+ expect(header.nativeElement.textContent).toContain('Select Columns (2 of 3)');
194
+ });
195
+ });
@@ -0,0 +1,401 @@
1
+ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
2
+ import { DebugElement } from '@angular/core';
3
+ import { By } from '@angular/platform-browser';
4
+ import { ColumnHeaderFilterComponent } from '../column-header-filter/column-header-filter.component';
5
+
6
+ describe('ColumnHeaderFilterComponent', () => {
7
+ let component: ColumnHeaderFilterComponent;
8
+ let fixture: ComponentFixture<ColumnHeaderFilterComponent>;
9
+
10
+ beforeEach(async () => {
11
+ await TestBed.configureTestingModule({
12
+ imports: [ColumnHeaderFilterComponent],
13
+ }).compileComponents();
14
+
15
+ fixture = TestBed.createComponent(ColumnHeaderFilterComponent);
16
+ component = fixture.componentInstance;
17
+ });
18
+
19
+ it('should create', () => {
20
+ expect(component).toBeTruthy();
21
+ });
22
+
23
+ it('should render column name', () => {
24
+ fixture.componentRef.setInput('columnKey', 'name');
25
+ fixture.componentRef.setInput('columnName', 'Full Name');
26
+ fixture.componentRef.setInput('filterType', 'none');
27
+ fixture.detectChanges();
28
+
29
+ const headerLabel = fixture.debugElement.query(By.css('[data-header-label]'));
30
+ expect(headerLabel.nativeElement.textContent.trim()).toBe('Full Name');
31
+ });
32
+
33
+ describe('Sort button', () => {
34
+ it('should show sort button when onSort provided', () => {
35
+ fixture.componentRef.setInput('columnKey', 'name');
36
+ fixture.componentRef.setInput('columnName', 'Name');
37
+ fixture.componentRef.setInput('filterType', 'none');
38
+ fixture.componentRef.setInput('onSort', jest.fn());
39
+ fixture.detectChanges();
40
+
41
+ const sortButton = fixture.debugElement.query(
42
+ By.css('button[aria-label="Sort by Name"]')
43
+ );
44
+ expect(sortButton).toBeTruthy();
45
+ });
46
+
47
+ it('should not show sort button when onSort not provided', () => {
48
+ fixture.componentRef.setInput('columnKey', 'name');
49
+ fixture.componentRef.setInput('columnName', 'Name');
50
+ fixture.componentRef.setInput('filterType', 'none');
51
+ fixture.detectChanges();
52
+
53
+ const sortButton = fixture.debugElement.query(
54
+ By.css('button[aria-label="Sort by Name"]')
55
+ );
56
+ expect(sortButton).toBeNull();
57
+ });
58
+
59
+ it('should call onSort when sort button clicked', () => {
60
+ const onSortMock = jest.fn();
61
+ fixture.componentRef.setInput('columnKey', 'name');
62
+ fixture.componentRef.setInput('columnName', 'Name');
63
+ fixture.componentRef.setInput('filterType', 'none');
64
+ fixture.componentRef.setInput('onSort', onSortMock);
65
+ fixture.detectChanges();
66
+
67
+ const sortButton = fixture.debugElement.query(
68
+ By.css('button[aria-label="Sort by Name"]')
69
+ );
70
+ sortButton.nativeElement.click();
71
+
72
+ expect(onSortMock).toHaveBeenCalled();
73
+ });
74
+
75
+ it('should show ascending indicator when sorted ascending', () => {
76
+ fixture.componentRef.setInput('columnKey', 'name');
77
+ fixture.componentRef.setInput('columnName', 'Name');
78
+ fixture.componentRef.setInput('filterType', 'none');
79
+ fixture.componentRef.setInput('onSort', jest.fn());
80
+ fixture.componentRef.setInput('isSorted', true);
81
+ fixture.componentRef.setInput('isSortedDescending', false);
82
+ fixture.detectChanges();
83
+
84
+ const sortButton = fixture.debugElement.query(
85
+ By.css('button[aria-label="Sort by Name"]')
86
+ );
87
+ expect(sortButton.nativeElement.textContent.trim()).toBe('▲');
88
+ });
89
+
90
+ it('should show descending indicator when sorted descending', () => {
91
+ fixture.componentRef.setInput('columnKey', 'name');
92
+ fixture.componentRef.setInput('columnName', 'Name');
93
+ fixture.componentRef.setInput('filterType', 'none');
94
+ fixture.componentRef.setInput('onSort', jest.fn());
95
+ fixture.componentRef.setInput('isSorted', true);
96
+ fixture.componentRef.setInput('isSortedDescending', true);
97
+ fixture.detectChanges();
98
+
99
+ const sortButton = fixture.debugElement.query(
100
+ By.css('button[aria-label="Sort by Name"]')
101
+ );
102
+ expect(sortButton.nativeElement.textContent.trim()).toBe('▼');
103
+ });
104
+ });
105
+
106
+ describe('Filter button', () => {
107
+ it('should show filter button when filterType is not none', () => {
108
+ fixture.componentRef.setInput('columnKey', 'name');
109
+ fixture.componentRef.setInput('columnName', 'Name');
110
+ fixture.componentRef.setInput('filterType', 'text');
111
+ fixture.detectChanges();
112
+
113
+ const filterButton = fixture.debugElement.query(
114
+ By.css('button[aria-label="Filter Name"]')
115
+ );
116
+ expect(filterButton).toBeTruthy();
117
+ });
118
+
119
+ it('should not show filter button when filterType is none', () => {
120
+ fixture.componentRef.setInput('columnKey', 'name');
121
+ fixture.componentRef.setInput('columnName', 'Name');
122
+ fixture.componentRef.setInput('filterType', 'none');
123
+ fixture.detectChanges();
124
+
125
+ const filterButton = fixture.debugElement.query(
126
+ By.css('button[aria-label="Filter Name"]')
127
+ );
128
+ expect(filterButton).toBeNull();
129
+ });
130
+
131
+ it('should toggle popover when filter button clicked', () => {
132
+ fixture.componentRef.setInput('columnKey', 'name');
133
+ fixture.componentRef.setInput('columnName', 'Name');
134
+ fixture.componentRef.setInput('filterType', 'text');
135
+ fixture.detectChanges();
136
+
137
+ const filterButton = fixture.debugElement.query(
138
+ By.css('button[aria-label="Filter Name"]')
139
+ );
140
+
141
+ // Open popover
142
+ filterButton.nativeElement.click();
143
+ fixture.detectChanges();
144
+ expect(component.isFilterOpen()).toBe(true);
145
+ let popover = fixture.debugElement.query(By.css('.ogrid-header-filter__popover'));
146
+ expect(popover).toBeTruthy();
147
+
148
+ // Close popover
149
+ filterButton.nativeElement.click();
150
+ fixture.detectChanges();
151
+ expect(component.isFilterOpen()).toBe(false);
152
+ popover = fixture.debugElement.query(By.css('.ogrid-header-filter__popover'));
153
+ expect(popover).toBeNull();
154
+ });
155
+
156
+ it('should show active indicator when filter has value', () => {
157
+ fixture.componentRef.setInput('columnKey', 'name');
158
+ fixture.componentRef.setInput('columnName', 'Name');
159
+ fixture.componentRef.setInput('filterType', 'text');
160
+ fixture.componentRef.setInput('textValue', 'test');
161
+ fixture.detectChanges();
162
+
163
+ const filterButton = fixture.debugElement.query(
164
+ By.css('button[aria-label="Filter Name"]')
165
+ );
166
+ const dot = filterButton.query(By.css('.ogrid-header-filter__dot'));
167
+ expect(dot).toBeTruthy();
168
+ });
169
+ });
170
+
171
+ describe('Text filter', () => {
172
+ beforeEach(() => {
173
+ fixture.componentRef.setInput('columnKey', 'name');
174
+ fixture.componentRef.setInput('columnName', 'Name');
175
+ fixture.componentRef.setInput('filterType', 'text');
176
+ fixture.componentRef.setInput('textValue', '');
177
+ fixture.componentRef.setInput('onTextChange', jest.fn());
178
+ });
179
+
180
+ it('should render text input when popover open', () => {
181
+ component.isFilterOpen.set(true);
182
+ fixture.detectChanges();
183
+
184
+ const input = fixture.debugElement.query(
185
+ By.css('input[type="text"][placeholder="Enter search term..."]')
186
+ );
187
+ expect(input).toBeTruthy();
188
+ });
189
+
190
+ it('should call onTextChange when Apply clicked', () => {
191
+ const onTextChangeMock = jest.fn();
192
+ fixture.componentRef.setInput('onTextChange', onTextChangeMock);
193
+ component.isFilterOpen.set(true);
194
+ fixture.detectChanges();
195
+
196
+ const input = fixture.debugElement.query(
197
+ By.css('input[type="text"][placeholder="Enter search term..."]')
198
+ );
199
+ input.nativeElement.value = 'test value';
200
+ input.nativeElement.dispatchEvent(new Event('input'));
201
+ fixture.detectChanges();
202
+
203
+ const applyButton = fixture.debugElement.queryAll(
204
+ By.css('.ogrid-header-filter__action-btn--primary')
205
+ )[0];
206
+ applyButton.nativeElement.click();
207
+
208
+ expect(onTextChangeMock).toHaveBeenCalledWith('test value');
209
+ });
210
+
211
+ it('should call onTextChange with empty string when Clear clicked', () => {
212
+ const onTextChangeMock = jest.fn();
213
+ fixture.componentRef.setInput('onTextChange', onTextChangeMock);
214
+ fixture.componentRef.setInput('textValue', 'existing');
215
+ component.isFilterOpen.set(true);
216
+ fixture.detectChanges();
217
+
218
+ const clearButton = fixture.debugElement.queryAll(
219
+ By.css('.ogrid-header-filter__action-btn')
220
+ )[0];
221
+ clearButton.nativeElement.click();
222
+
223
+ expect(onTextChangeMock).toHaveBeenCalledWith('');
224
+ });
225
+
226
+ it('should disable Clear button when no text value', () => {
227
+ fixture.componentRef.setInput('textValue', '');
228
+ component.isFilterOpen.set(true);
229
+ fixture.detectChanges();
230
+
231
+ const clearButton = fixture.debugElement.queryAll(
232
+ By.css('.ogrid-header-filter__action-btn')
233
+ )[0];
234
+ expect(clearButton.nativeElement.disabled).toBe(true);
235
+ });
236
+ });
237
+
238
+ describe('MultiSelect filter', () => {
239
+ const options = ['Option 1', 'Option 2', 'Option 3'];
240
+
241
+ beforeEach(() => {
242
+ fixture.componentRef.setInput('columnKey', 'status');
243
+ fixture.componentRef.setInput('columnName', 'Status');
244
+ fixture.componentRef.setInput('filterType', 'multiSelect');
245
+ fixture.componentRef.setInput('options', options);
246
+ fixture.componentRef.setInput('selectedValues', []);
247
+ fixture.componentRef.setInput('onFilterChange', jest.fn());
248
+ });
249
+
250
+ it('should render options list when popover open', () => {
251
+ component.isFilterOpen.set(true);
252
+ fixture.detectChanges();
253
+
254
+ const optionLabels = fixture.debugElement.queryAll(
255
+ By.css('.ogrid-header-filter__option')
256
+ );
257
+ expect(optionLabels.length).toBe(3);
258
+ });
259
+
260
+ it('should show loading state', () => {
261
+ fixture.componentRef.setInput('isLoadingOptions', true);
262
+ component.isFilterOpen.set(true);
263
+ fixture.detectChanges();
264
+
265
+ const loading = fixture.debugElement.query(By.css('.ogrid-header-filter__loading'));
266
+ expect(loading).toBeTruthy();
267
+ expect(loading.nativeElement.textContent).toContain('Loading');
268
+ });
269
+
270
+ it('should filter options based on search text', () => {
271
+ component.isFilterOpen.set(true);
272
+ fixture.detectChanges();
273
+
274
+ const searchInput = fixture.debugElement.query(
275
+ By.css('input[type="text"][placeholder="Search..."]')
276
+ );
277
+ searchInput.nativeElement.value = '2';
278
+ searchInput.nativeElement.dispatchEvent(new Event('input'));
279
+ fixture.detectChanges();
280
+
281
+ const optionLabels = fixture.debugElement.queryAll(
282
+ By.css('.ogrid-header-filter__option')
283
+ );
284
+ expect(optionLabels.length).toBe(1);
285
+ expect(optionLabels[0].nativeElement.textContent).toContain('Option 2');
286
+ });
287
+
288
+ it('should show empty state when no options match search', () => {
289
+ component.isFilterOpen.set(true);
290
+ fixture.detectChanges();
291
+
292
+ const searchInput = fixture.debugElement.query(
293
+ By.css('input[type="text"][placeholder="Search..."]')
294
+ );
295
+ searchInput.nativeElement.value = 'nonexistent';
296
+ searchInput.nativeElement.dispatchEvent(new Event('input'));
297
+ fixture.detectChanges();
298
+
299
+ const empty = fixture.debugElement.query(By.css('.ogrid-header-filter__empty'));
300
+ expect(empty).toBeTruthy();
301
+ expect(empty.nativeElement.textContent).toContain('No options found');
302
+ });
303
+
304
+ it('should check selected options', () => {
305
+ fixture.componentRef.setInput('selectedValues', ['Option 1', 'Option 3']);
306
+ component.isFilterOpen.set(true);
307
+ fixture.detectChanges();
308
+
309
+ const checkboxes = fixture.debugElement.queryAll(
310
+ By.css('.ogrid-header-filter__option input[type="checkbox"]')
311
+ );
312
+ expect(checkboxes[0].nativeElement.checked).toBe(true);
313
+ expect(checkboxes[1].nativeElement.checked).toBe(false);
314
+ expect(checkboxes[2].nativeElement.checked).toBe(true);
315
+ });
316
+
317
+ it('should call onFilterChange when Apply clicked', () => {
318
+ const onFilterChangeMock = jest.fn();
319
+ fixture.componentRef.setInput('onFilterChange', onFilterChangeMock);
320
+ component.isFilterOpen.set(true);
321
+ fixture.detectChanges();
322
+
323
+ // Toggle first checkbox
324
+ const checkboxes = fixture.debugElement.queryAll(
325
+ By.css('.ogrid-header-filter__option input[type="checkbox"]')
326
+ );
327
+ checkboxes[0].nativeElement.checked = true;
328
+ checkboxes[0].nativeElement.dispatchEvent(new Event('change'));
329
+ fixture.detectChanges();
330
+
331
+ const applyButton = fixture.debugElement.queryAll(
332
+ By.css('.ogrid-header-filter__action-btn--primary')
333
+ )[0];
334
+ applyButton.nativeElement.click();
335
+
336
+ expect(onFilterChangeMock).toHaveBeenCalledWith(['Option 1']);
337
+ });
338
+
339
+ it('should select all filtered options when Select All clicked', () => {
340
+ const onFilterChangeMock = jest.fn();
341
+ fixture.componentRef.setInput('onFilterChange', onFilterChangeMock);
342
+ component.isFilterOpen.set(true);
343
+ fixture.detectChanges();
344
+
345
+ const selectAllButton = fixture.debugElement.queryAll(
346
+ By.css('.ogrid-header-filter__select-actions .ogrid-header-filter__action-btn')
347
+ )[0];
348
+ selectAllButton.nativeElement.click();
349
+ fixture.detectChanges();
350
+
351
+ const applyButton = fixture.debugElement.queryAll(
352
+ By.css('.ogrid-header-filter__action-btn--primary')
353
+ )[0];
354
+ applyButton.nativeElement.click();
355
+
356
+ expect(onFilterChangeMock).toHaveBeenCalledWith(['Option 1', 'Option 2', 'Option 3']);
357
+ });
358
+
359
+ it('should clear selection when Clear clicked in multiSelect actions', () => {
360
+ fixture.componentRef.setInput('selectedValues', ['Option 1', 'Option 2']);
361
+ component.isFilterOpen.set(true);
362
+ fixture.detectChanges();
363
+
364
+ const clearButton = fixture.debugElement.queryAll(
365
+ By.css('.ogrid-header-filter__select-actions .ogrid-header-filter__action-btn')
366
+ )[1];
367
+ clearButton.nativeElement.click();
368
+ fixture.detectChanges();
369
+
370
+ const checkboxes = fixture.debugElement.queryAll(
371
+ By.css('.ogrid-header-filter__option input[type="checkbox"]')
372
+ );
373
+ expect(checkboxes.every((cb) => !cb.nativeElement.checked)).toBe(true);
374
+ });
375
+ });
376
+
377
+ describe('Date filter', () => {
378
+ beforeEach(() => {
379
+ fixture.componentRef.setInput('columnKey', 'createdDate');
380
+ fixture.componentRef.setInput('columnName', 'Created Date');
381
+ fixture.componentRef.setInput('filterType', 'date');
382
+ fixture.componentRef.setInput('onDateChange', jest.fn());
383
+ });
384
+
385
+ it('should render date operator dropdown', () => {
386
+ component.isFilterOpen.set(true);
387
+ fixture.detectChanges();
388
+
389
+ const select = fixture.debugElement.query(By.css('select'));
390
+ expect(select).toBeTruthy();
391
+ });
392
+
393
+ it('should render date input fields', () => {
394
+ component.isFilterOpen.set(true);
395
+ fixture.detectChanges();
396
+
397
+ const dateInputs = fixture.debugElement.queryAll(By.css('input[type="date"]'));
398
+ expect(dateInputs.length).toBeGreaterThan(0);
399
+ });
400
+ });
401
+ });