Skip to content
This repository was archived by the owner on Jun 1, 2025. It is now read-only.

Commit 719a06d

Browse files
Ghislain BeaulacGhislain Beaulac
authored andcommitted
fix(gridState): columnPicker & gridMenu not triggering gridState change
- add GridState Service full test suite
1 parent f105efc commit 719a06d

File tree

2 files changed

+220
-19
lines changed

2 files changed

+220
-19
lines changed

src/app/modules/angular-slickgrid/services/__tests__/gridState.service.spec.ts

Lines changed: 207 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { TestBed } from '@angular/core/testing';
2-
import { BackendServiceApi, BackendService, GridOption, CurrentPagination, CurrentSorter, CurrentFilter } from '../../models';
2+
import { BackendServiceApi, BackendService, GridOption, CurrentPagination, CurrentSorter, CurrentFilter, Column, CurrentColumn, GridStateChange, GridStateType, GridState, ExtensionName } from '../../models';
33
import { FilterService } from '../filter.service';
44
import { GridStateService } from '../gridState.service';
55
import { ExtensionService } from '../extension.service';
66
import { SortService } from '../sort.service';
77
import { of } from 'rxjs';
88

9+
declare var Slick: any;
10+
911
const gridOptionMock = {
1012
enableAutoResize: true
1113
} as GridOption;
@@ -19,7 +21,11 @@ const backendServiceStub = {
1921
const gridStub = {
2022
autosizeColumns: jest.fn(),
2123
getScrollbarDimensions: jest.fn(),
22-
getOptions: () => gridOptionMock
24+
getOptions: () => gridOptionMock,
25+
getColumns: jest.fn(),
26+
onColumnsReordered: new Slick.Event(),
27+
onColumnsResized: new Slick.Event(),
28+
setSelectedRows: jest.fn(),
2329
};
2430

2531
const extensionServiceStub = {
@@ -55,16 +61,155 @@ describe('GridStateService', () => {
5561
expect(service).toBeTruthy();
5662
});
5763

58-
it('should have called the "subscribeToAllGridChanges" method while initializing', () => {
59-
const gridStateSpy = jest.spyOn(service, 'subscribeToAllGridChanges');
60-
const filterSpy = jest.spyOn(filterServiceStub.onFilterChanged, 'subscribe');
61-
const sortSpy = jest.spyOn(sortServiceStub.onSortChanged, 'subscribe');
64+
describe('init method', () => {
65+
let slickgridEvent;
66+
beforeEach(() => {
67+
slickgridEvent = new Slick.Event();
68+
});
69+
afterEach(() => {
70+
slickgridEvent.unsubscribe();
71+
});
72+
73+
it('should have called the "subscribeToAllGridChanges" method while initializing', () => {
74+
const gridStateSpy = jest.spyOn(service, 'subscribeToAllGridChanges');
75+
const filterSpy = jest.spyOn(filterServiceStub.onFilterChanged, 'subscribe');
76+
const sortSpy = jest.spyOn(sortServiceStub.onSortChanged, 'subscribe');
6277

63-
service.init(gridStub, extensionServiceStub, filterServiceStub, sortServiceStub);
78+
service.init(gridStub, extensionServiceStub, filterServiceStub, sortServiceStub);
79+
80+
expect(gridStateSpy).toHaveBeenCalled();
81+
expect(filterSpy).toHaveBeenCalled();
82+
expect(sortSpy).toHaveBeenCalled();
83+
});
84+
85+
describe('getCurrentColumns method', () => {
86+
it('should call "getCurrentColumns" and return empty array when no columns is defined', () => {
87+
const output = service.getCurrentColumns();
88+
expect(output).toEqual([]);
89+
});
90+
91+
it('should call "getCurrentColumns" and return Columns when the method is called', () => {
92+
const columnsMock = [
93+
{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' },
94+
{ id: 'field2', field: 'field2', width: 150, headerCssClass: 'blue' },
95+
{ id: 'field3', field: 'field3' },
96+
] as Column[];
97+
const gridSpy = jest.spyOn(gridStub, 'getColumns').mockReturnValue(columnsMock);
98+
99+
const output = service.getCurrentColumns();
100+
101+
expect(gridSpy).toHaveBeenCalled();
102+
expect(output).toEqual([
103+
{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 },
104+
{ columnId: 'field2', cssClass: '', headerCssClass: 'blue', width: 150 },
105+
{ columnId: 'field3', cssClass: '', headerCssClass: '', width: 0 },
106+
] as CurrentColumn[]);
107+
});
108+
});
64109

65-
expect(gridStateSpy).toHaveBeenCalled();
66-
expect(filterSpy).toHaveBeenCalled();
67-
expect(sortSpy).toHaveBeenCalled();
110+
describe('bindExtensionAddonEventToGridStateChange tests', () => {
111+
it('should subscribe to some Extension Addon SlickGrid events and expect the event to be triggered when a notify is triggered after service was initialized', () => {
112+
const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[];
113+
const associatedColumnsMock = [{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 }] as CurrentColumn[];
114+
const extensionMock = { name: ExtensionName.columnPicker, addon: { onColumnsChanged: slickgridEvent }, class: null };
115+
const gridStateMock = { columns: associatedColumnsMock, filters: [], sorters: [] } as GridState;
116+
const stateChangeMock = { change: { newValues: associatedColumnsMock, type: GridStateType.columns }, gridState: gridStateMock } as GridStateChange;
117+
118+
const gridStateSpy = jest.spyOn(service, 'getCurrentGridState').mockReturnValue(gridStateMock);
119+
const extensionSpy = jest.spyOn(extensionServiceStub, 'getExtensionByName').mockReturnValue(extensionMock);
120+
const rxOnChangeSpy = jest.spyOn(service.onGridStateChanged, 'next');
121+
122+
service.init(gridStub, extensionServiceStub, filterServiceStub, sortServiceStub);
123+
slickgridEvent.notify({ columns: columnsMock }, new Slick.EventData(), gridStub);
124+
125+
expect(gridStateSpy).toHaveBeenCalled();
126+
expect(rxOnChangeSpy).toHaveBeenCalledWith(stateChangeMock);
127+
expect(extensionSpy).toHaveBeenCalledWith(ExtensionName.columnPicker);
128+
expect(extensionSpy).toHaveBeenLastCalledWith(ExtensionName.gridMenu);
129+
});
130+
});
131+
132+
describe('bindSlickGridEventToGridStateChange tests', () => {
133+
it('should subscribe to some SlickGrid events and expect the event to be triggered when a notify is triggered after service was initialized', () => {
134+
const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[];
135+
const associatedColumnsMock = [{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 }] as CurrentColumn[];
136+
const gridStateMock = { columns: associatedColumnsMock, filters: [], sorters: [] } as GridState;
137+
const stateChangeMock = { change: { newValues: associatedColumnsMock, type: GridStateType.columns }, gridState: gridStateMock } as GridStateChange;
138+
139+
const gridColumnSpy = jest.spyOn(gridStub, 'getColumns').mockReturnValue(columnsMock);
140+
const gridColumnReorderSpy = jest.spyOn(gridStub.onColumnsReordered, 'subscribe');
141+
const gridColumnResizeSpy = jest.spyOn(gridStub.onColumnsResized, 'subscribe');
142+
const gridStateSpy = jest.spyOn(service, 'getCurrentGridState').mockReturnValue(gridStateMock);
143+
const rxOnChangeSpy = jest.spyOn(service.onGridStateChanged, 'next');
144+
145+
service.init(gridStub, extensionServiceStub, filterServiceStub, sortServiceStub);
146+
gridStub.onColumnsReordered.notify({ impactedColumns: columnsMock }, new Slick.EventData(), gridStub);
147+
service.resetColumns();
148+
149+
expect(gridColumnSpy).toHaveBeenCalled();
150+
expect(gridColumnReorderSpy).toHaveBeenCalled();
151+
expect(gridColumnResizeSpy).toHaveBeenCalled();
152+
expect(gridStateSpy).toHaveBeenCalled();
153+
expect(rxOnChangeSpy).toHaveBeenCalledWith(stateChangeMock);
154+
});
155+
});
156+
});
157+
158+
describe('getAssociatedCurrentColumns method', () => {
159+
it('should call "getAssociatedCurrentColumns" and expect "getCurrentColumns" to return current cached Columns', () => {
160+
const columnsMock = [
161+
{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' },
162+
{ id: 'field2', field: 'field2', width: 150, headerCssClass: 'blue' },
163+
{ id: 'field3', field: 'field3' },
164+
] as Column[];
165+
const associatedColumnsMock = [
166+
{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 },
167+
{ columnId: 'field2', cssClass: '', headerCssClass: 'blue', width: 150 },
168+
{ columnId: 'field3', cssClass: '', headerCssClass: '', width: 0 },
169+
] as CurrentColumn[];
170+
171+
const associatedColumns = service.getAssociatedCurrentColumns(columnsMock);
172+
const currentColumns = service.getCurrentColumns();
173+
174+
expect(associatedColumns).toEqual(associatedColumnsMock);
175+
expect(currentColumns).toEqual(associatedColumnsMock);
176+
});
177+
});
178+
179+
describe('getAssociatedGridColumns method', () => {
180+
it('should call "getAssociatedGridColumns" and return empty array when empty array is provided as current columns', () => {
181+
const associatedGridColumns = service.getAssociatedGridColumns(gridStub, []);
182+
const columns = service.getColumns();
183+
184+
expect(associatedGridColumns).toEqual([]);
185+
expect(columns).toEqual([]);
186+
});
187+
188+
it('should call "getAssociatedGridColumns" and return empty array when empty array is provided as current columns', () => {
189+
const columnsMock = [
190+
{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' },
191+
{ id: 'field2', field: 'field2', width: 150, headerCssClass: 'blue' },
192+
{ id: 'field3', field: 'field3' },
193+
] as Column[];
194+
const columnsWithClassesMock = [
195+
{ id: 'field1', field: 'field1', width: 100, cssClass: 'red', headerCssClass: '' },
196+
{ id: 'field2', field: 'field2', width: 150, cssClass: '', headerCssClass: 'blue' },
197+
{ id: 'field3', field: 'field3', width: 0, cssClass: '', headerCssClass: '' },
198+
] as Column[];
199+
const currentColumnsMock = [
200+
{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 },
201+
{ columnId: 'field2', cssClass: '', headerCssClass: 'blue', width: 150 },
202+
{ columnId: 'field3', cssClass: '', headerCssClass: '', width: 0 },
203+
] as CurrentColumn[];
204+
const gridSpy = jest.spyOn(gridStub, 'getColumns').mockReturnValue(columnsMock);
205+
206+
const associatedGridColumns = service.getAssociatedGridColumns(gridStub, currentColumnsMock);
207+
const columns = service.getColumns();
208+
209+
expect(gridSpy).toHaveBeenCalled();
210+
expect(associatedGridColumns).toEqual(columnsWithClassesMock);
211+
expect(columns).toEqual(columnsWithClassesMock);
212+
});
68213
});
69214

70215
describe('getCurrentPagination method', () => {
@@ -126,7 +271,7 @@ describe('GridStateService', () => {
126271

127272
describe('getCurrentFilters method', () => {
128273
it('should return null when no BackendService is used and FilterService is missing the "getCurrentLocalFilters" method', () => {
129-
const gridSpy = jest.spyOn(gridStub, 'getOptions').mockReturnValue({});
274+
const gridSpy = jest.spyOn(gridStub, 'getOptions');
130275

131276
const output = service.getCurrentFilters();
132277

@@ -160,4 +305,55 @@ describe('GridStateService', () => {
160305
expect(output).toBe(filterMock);
161306
});
162307
});
308+
309+
describe('resetColumns method', () => {
310+
it('should call the method without any column definitions and expect "onGridStateChanged" to be triggered with empty changes', () => {
311+
const gridStateMock = { columns: [], filters: [], sorters: [] } as GridState;
312+
const stateChangeMock = { change: { newValues: [], type: GridStateType.columns }, gridState: gridStateMock } as GridStateChange;
313+
const onChangeSpy = jest.spyOn(service.onGridStateChanged, 'next');
314+
const serviceSpy = jest.spyOn(service, 'getCurrentGridState').mockReturnValue(gridStateMock);
315+
316+
service.resetColumns();
317+
318+
expect(serviceSpy).toHaveBeenCalled();
319+
expect(onChangeSpy).toHaveBeenCalledWith(stateChangeMock);
320+
});
321+
322+
it(`should call the method with column definitions and expect "onGridStateChanged" to be triggered
323+
with "newValues" property being the columns and still empty "gridState" property`, () => {
324+
const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[];
325+
const currentColumnsMock = [{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 }] as CurrentColumn[];
326+
const gridStateMock = { columns: [], filters: [], sorters: [] } as GridState;
327+
const stateChangeMock = { change: { newValues: currentColumnsMock, type: GridStateType.columns }, gridState: gridStateMock } as GridStateChange;
328+
const onChangeSpy = jest.spyOn(service.onGridStateChanged, 'next');
329+
const serviceSpy = jest.spyOn(service, 'getCurrentGridState').mockReturnValue(gridStateMock);
330+
331+
service.resetColumns(columnsMock);
332+
333+
expect(serviceSpy).toHaveBeenCalled();
334+
expect(onChangeSpy).toHaveBeenCalledWith(stateChangeMock);
335+
});
336+
});
337+
338+
describe('resetRowSelection method', () => {
339+
it('should call the method and do nothing when row selection is not in use', () => {
340+
const gridSpy = jest.spyOn(gridStub, 'setSelectedRows');
341+
service.resetRowSelection();
342+
expect(gridSpy).not.toHaveBeenCalled();
343+
});
344+
345+
it('should call the method and call the grid selection reset when the selection extension is used', () => {
346+
const extensionMock = { name: ExtensionName.rowSelection, addon: {}, class: null };
347+
const gridOptionsMock = { enableRowSelection: true } as GridOption;
348+
const gridOptionSpy = jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
349+
const setSelectionSpy = jest.spyOn(gridStub, 'setSelectedRows');
350+
const extensionSpy = jest.spyOn(extensionServiceStub, 'getExtensionByName').mockReturnValue(extensionMock);
351+
352+
service.resetRowSelection();
353+
354+
expect(gridOptionSpy).toHaveBeenCalled();
355+
expect(extensionSpy).toHaveBeenCalledWith(ExtensionName.rowSelection);
356+
expect(setSelectionSpy).toHaveBeenCalled();
357+
});
358+
});
163359
});

src/app/modules/angular-slickgrid/services/gridState.service.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ export class GridStateService {
6262
}
6363
});
6464
this.subscriptions = [];
65+
this._currentColumns = [];
66+
this._columns = [];
6567
}
6668

6769
/**
@@ -87,7 +89,7 @@ export class GridStateService {
8789
* @return current columns
8890
*/
8991
getColumns(): Column[] {
90-
return this._columns || this._grid.getColumns();
92+
return this._columns;
9193
}
9294

9395
/**
@@ -256,8 +258,8 @@ export class GridStateService {
256258
);
257259

258260
// Subscribe to ColumnPicker and/or GridMenu for show/hide Columns visibility changes
259-
this.bindExtensionEventToGridStateChange(ExtensionName.columnPicker, 'onColumnsChanged');
260-
this.bindExtensionEventToGridStateChange(ExtensionName.gridMenu, 'onColumnsChanged');
261+
this.bindExtensionAddonEventToGridStateChange(ExtensionName.columnPicker, 'onColumnsChanged');
262+
this.bindExtensionAddonEventToGridStateChange(ExtensionName.gridMenu, 'onColumnsChanged');
261263

262264
// subscribe to Column Resize & Reordering
263265
this.bindSlickGridEventToGridStateChange('onColumnsReordered', grid);
@@ -273,11 +275,12 @@ export class GridStateService {
273275
* @param extension name
274276
* @param grid
275277
*/
276-
private bindExtensionEventToGridStateChange(extensionName: ExtensionName, eventName: string) {
278+
private bindExtensionAddonEventToGridStateChange(extensionName: ExtensionName, eventName: string) {
277279
const extension = this.extensionService && this.extensionService.getExtensionByName && this.extensionService.getExtensionByName(extensionName);
280+
const slickEvent = extension && extension.addon && extension.addon[eventName];
278281

279-
if (extension && extension.class && extension.class[eventName] && extension.class[eventName].subscribe) {
280-
this._eventHandler.subscribe(extension.class[eventName], (e: Event, args: any) => {
282+
if (slickEvent && slickEvent.subscribe) {
283+
this._eventHandler.subscribe(slickEvent, (e: Event, args: any) => {
281284
const columns: Column[] = args && args.columns;
282285
const currentColumns: CurrentColumn[] = this.getAssociatedCurrentColumns(columns);
283286
this.onGridStateChanged.next({ change: { newValues: currentColumns, type: GridStateType.columns }, gridState: this.getCurrentGridState() });
@@ -291,8 +294,10 @@ export class GridStateService {
291294
* @param grid
292295
*/
293296
private bindSlickGridEventToGridStateChange(eventName: string, grid: any) {
294-
if (grid && grid[eventName] && grid[eventName].subscribe) {
295-
this._eventHandler.subscribe(grid[eventName], (e: Event, args: any) => {
297+
const slickGridEvent = grid && grid[eventName];
298+
299+
if (slickGridEvent && slickGridEvent.subscribe) {
300+
this._eventHandler.subscribe(slickGridEvent, (e: Event, args: any) => {
296301
const columns: Column[] = grid.getColumns();
297302
const currentColumns: CurrentColumn[] = this.getAssociatedCurrentColumns(columns);
298303
this.onGridStateChanged.next({ change: { newValues: currentColumns, type: GridStateType.columns }, gridState: this.getCurrentGridState() });

0 commit comments

Comments
 (0)