Skip to content

Commit 4e0b528

Browse files
Ghislain BeaulacGhislain Beaulac
Ghislain Beaulac
authored and
Ghislain Beaulac
committed
fix(presets): Grid State & Presets stopped working for columns
- there was mainly 3 problems 1. ColumnPicker & GridMenu were re-registered but their instances were not overwritten and because of that, the onColumnsChanged in the GridState was never triggering anymore because the subscribe was on the instance that no longer existed 2. we were using the setColumns with allColumns, that was a regression introduced by previous commit, this in terms was cancelling any presets of columns from coming in 3. header menu was not triggering any changes, hiding a column and/or sorting a column had no effect on the Grid State. - Example 15 is the reference for Grid State & Presets
1 parent d9a2325 commit 4e0b528

23 files changed

+571
-104
lines changed

.circleci/config.yml

+8-8
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ jobs:
77
steps:
88
- checkout
99
- restore_cache:
10-
key: angular-slickgrid-build-{{ .Branch }}-{{ checksum "package.json" }}
11-
- run: yarn install
10+
key: angular-slickgrid-build-{{ .Branch }}-{{ checksum "yarn.lock" }}
11+
- run: yarn install --frozen-lockfile
1212
- run:
1313
name: Install Jest JUnit coverage reporter
1414
command: yarn add --dev jest-junit
1515
- save_cache:
16-
key: angular-slickgrid-build-{{ .Branch }}-{{ checksum "package.json" }}
16+
key: angular-slickgrid-build-{{ .Branch }}-{{ checksum "yarn.lock" }}
1717
paths:
18-
- "node_modules"
18+
- ~/.cache/yarn
1919
- run:
2020
name: Run Jest tests with JUnit as reporter
2121
command: ./node_modules/.bin/jest --config test/jest.config.js --ci --runInBand --collectCoverage=true --reporters=default --reporters=jest-junit
@@ -31,17 +31,17 @@ jobs:
3131
- restore_cache:
3232
name: Restoring Cache for Cypress
3333
keys:
34-
- e2e-tests-{{ .Branch }}-{{ checksum "package.json" }}
34+
- e2e-tests-{{ .Branch }}-{{ checksum "yarn.lock" }}
3535
- run:
36-
name: Installing Cypress dependencies with yarn
36+
name: Installing Cypress dependencies
3737
command: |
3838
cd test/cypress
3939
yarn install --frozen-lockfile
4040
- save_cache:
4141
name: Saving Cache for Cypress
42-
key: e2e-tests-{{ .Branch }}-{{ checksum "package.json" }}
42+
key: e2e-tests-{{ .Branch }}-{{ checksum "yarn.lock" }}
4343
paths:
44-
- "test/cypress/node_modules"
44+
- ~/.cache/yarn
4545
- run:
4646
name: Running Cypress E2E tests with JUnit reporter
4747
command: |

src/app/examples/grid-menu.component.html

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ <h2>{{title}}</h2>
1515
<i class="fa fa-language"></i>
1616
Switch Language
1717
</button>
18+
<b>Locale:</b> <span style="font-style: italic"
19+
data-test="selected-locale">{{selectedLanguage + '.json'}}</span>
1820

1921
<div class="col-sm-12">
2022
<angular-slickgrid gridId="grid9"

src/app/examples/grid-menu.component.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ export class GridMenuComponent implements OnInit {
2929
visibleColumns: Column[];
3030

3131
constructor(private translate: TranslateService) {
32-
this.selectedLanguage = this.translate.getDefaultLang();
32+
// always start with English for Cypress E2E tests to be consistent
33+
const defaultLang = 'en';
34+
this.translate.use(defaultLang);
35+
this.selectedLanguage = defaultLang;
3336
}
3437

3538
ngOnInit(): void {
@@ -157,8 +160,8 @@ export class GridMenuComponent implements OnInit {
157160
}
158161

159162
switchLanguage() {
160-
this.selectedLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en';
161-
this.translate.use(this.selectedLanguage);
163+
const nextLocale = (this.selectedLanguage === 'en') ? 'fr' : 'en';
164+
this.translate.use(nextLocale).subscribe(() => this.selectedLanguage = nextLocale);
162165
}
163166

164167
toggleGridMenu(e) {
+26-19
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
<div class="container-fluid">
2-
<h2>{{title}}</h2>
3-
<div class="subtitle" [innerHTML]="subTitle"></div>
2+
<h2>{{title}}</h2>
3+
<div class="subtitle"
4+
[innerHTML]="subTitle"></div>
45

5-
<button class="btn btn-default btn-sm" (click)="clearGridStateFromLocalStorage()">
6-
<i class="fa fa-times"></i>
7-
Clear Grid State from Local Storage &amp; Reset Grid
8-
</button>
9-
<button class="btn btn-default btn-sm" (click)="switchLanguage()">
10-
<i class="fa fa-language"></i>
11-
Switch Language
12-
</button>
6+
<button class="btn btn-default btn-sm"
7+
(click)="clearGridStateFromLocalStorage()"
8+
data-test="reset-button">
9+
<i class="fa fa-times"></i>
10+
Clear Grid State from Local Storage &amp; Reset Grid
11+
</button>
12+
<button class="btn btn-default btn-sm"
13+
(click)="switchLanguage()"
14+
data-test="language-button">
15+
<i class="fa fa-language"></i>
16+
Switch Language
17+
</button>
18+
<b>Locale:</b> <span style="font-style: italic"
19+
data-test="selected-locale">{{selectedLanguage + '.json'}}</span>
1320

14-
<angular-slickgrid gridId="grid16"
15-
[columnDefinitions]="columnDefinitions"
16-
[gridOptions]="gridOptions"
17-
[dataset]="dataset"
18-
(onAngularGridCreated)="angularGridReady($event)"
19-
(onGridStateChanged)="gridStateChanged($event)"
20-
(onBeforeGridDestroy)="saveCurrentGridState($event)">
21-
</angular-slickgrid>
22-
</div>
21+
<angular-slickgrid gridId="grid16"
22+
[columnDefinitions]="columnDefinitions"
23+
[gridOptions]="gridOptions"
24+
[dataset]="dataset"
25+
(onAngularGridCreated)="angularGridReady($event)"
26+
(onGridStateChanged)="gridStateChanged($event)"
27+
(onBeforeGridDestroy)="saveCurrentGridState($event)">
28+
</angular-slickgrid>
29+
</div>

src/app/examples/grid-state.component.ts

+15-6
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,14 @@ export class GridStateComponent implements OnInit {
5151
ngOnInit(): void {
5252
const presets = JSON.parse(localStorage[LOCAL_STORAGE_KEY] || null);
5353

54-
// use some Grid State preset defaults if you wish
54+
// use some Grid State preset defaults if you wish or just restore from Locale Storage
5555
// presets = presets || this.useDefaultPresets();
56-
5756
this.defineGrid(presets);
57+
58+
// always start with English for Cypress E2E tests to be consistent
59+
const defaultLang = 'en';
60+
this.translate.use(defaultLang);
61+
this.selectedLanguage = defaultLang;
5862
}
5963

6064
/** Clear the Grid State from Local Storage and reset the grid to it's original state */
@@ -100,7 +104,6 @@ export class GridStateComponent implements OnInit {
100104
filter: {
101105
collection: multiSelectFilterArray,
102106
model: Filters.multipleSelect,
103-
searchTerms: [1, 33, 44, 50, 66], // default selection
104107
// we could add certain option(s) to the "multiple-select" plugin
105108
filterOptions: {
106109
maxHeight: 250,
@@ -141,7 +144,13 @@ export class GridStateComponent implements OnInit {
141144
enableCheckboxSelector: true,
142145
enableFiltering: true,
143146
enableTranslate: true,
144-
i18n: this.translate
147+
i18n: this.translate,
148+
columnPicker: {
149+
hideForceFitButton: true
150+
},
151+
gridMenu: {
152+
hideForceFitButton: true
153+
},
145154
};
146155

147156
// reload the Grid State with the grid options presets
@@ -196,8 +205,8 @@ export class GridStateComponent implements OnInit {
196205
}
197206

198207
switchLanguage() {
199-
this.selectedLanguage = (this.selectedLanguage === 'en') ? 'fr' : 'en';
200-
this.translate.use(this.selectedLanguage);
208+
const nextLocale = (this.selectedLanguage === 'en') ? 'fr' : 'en';
209+
this.translate.use(nextLocale).subscribe(() => this.selectedLanguage = nextLocale);
201210
}
202211

203212
useDefaultPresets() {

src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
326326
this.bindBackendCallbackFunctions(this.gridOptions);
327327
}
328328

329-
this.gridStateService.init(this.grid, this.extensionService, this.filterService, this.sortService);
329+
this.gridStateService.init(this.grid);
330330

331331
this.onAngularGridCreated.emit({
332332
// Slick Grid & DataView objects

src/app/modules/angular-slickgrid/extensions/__tests__/headerMenuExtension.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const filterServiceStub = {
1515

1616
const sortServiceStub = {
1717
clearSorting: jest.fn(),
18+
emitSortChanged: jest.fn(),
1819
getCurrentColumnSorts: jest.fn(),
1920
onBackendSortChanged: jest.fn(),
2021
onLocalSortChanged: jest.fn(),

src/app/modules/angular-slickgrid/extensions/columnPickerExtension.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class ColumnPickerExtension implements Extension {
4646
this.sharedService.gridOptions.columnPicker.columnTitle = this.sharedService.gridOptions.columnPicker.columnTitle || columnTitle;
4747
this.sharedService.gridOptions.columnPicker.forceFitTitle = this.sharedService.gridOptions.columnPicker.forceFitTitle || forceFitTitle;
4848
this.sharedService.gridOptions.columnPicker.syncResizeTitle = this.sharedService.gridOptions.columnPicker.syncResizeTitle || syncResizeTitle;
49-
this._addon = new Slick.Controls.ColumnPicker(this.sharedService.columnDefinitions, this.sharedService.grid, this.sharedService.gridOptions);
49+
this._addon = new Slick.Controls.ColumnPicker(this.sharedService.allColumns, this.sharedService.grid, this.sharedService.gridOptions);
5050

5151
if (this.sharedService.grid && this.sharedService.gridOptions.enableColumnPicker) {
5252
if (this.sharedService.gridOptions.columnPicker.onExtensionRegistered) {

src/app/modules/angular-slickgrid/extensions/extensionUtility.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ export class ExtensionUtility {
1515
* @param array input
1616
* @param index
1717
*/
18-
arrayRemoveItemByIndex(array: any[], index: number) {
19-
return array.filter((el: any, i: number) => index !== i);
18+
arrayRemoveItemByIndex<T>(array: T[], index: number): T[] {
19+
return array.filter((el: T, i: number) => index !== i);
2020
}
2121

2222
/**

src/app/modules/angular-slickgrid/extensions/gridMenuExtension.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export class GridMenuExtension implements Extension {
8888
this.extensionUtility.translateItems(this.sharedService.gridOptions.gridMenu.customItems, 'titleKey', 'title');
8989
this.extensionUtility.sortItems(this.sharedService.gridOptions.gridMenu.customItems, 'positionOrder');
9090

91-
this._addon = new Slick.Controls.GridMenu(this.sharedService.columnDefinitions, this.sharedService.grid, this.sharedService.gridOptions);
91+
this._addon = new Slick.Controls.GridMenu(this.sharedService.allColumns, this.sharedService.grid, this.sharedService.gridOptions);
9292

9393
// hook all events
9494
if (this.sharedService.grid && this.sharedService.gridOptions.gridMenu) {

src/app/modules/angular-slickgrid/extensions/headerMenuExtension.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Constants } from '../constants';
44
import {
55
Column,
66
ColumnSort,
7+
CurrentSorter,
8+
EmitterType,
79
Extension,
810
ExtensionName,
911
GridOption,
@@ -192,10 +194,11 @@ export class HeaderMenuExtension implements Extension {
192194
hideColumn(column: Column) {
193195
if (this.sharedService.grid && this.sharedService.grid.getColumns && this.sharedService.grid.setColumns && this.sharedService.grid.getColumnIndex) {
194196
const columnIndex = this.sharedService.grid.getColumnIndex(column.id);
195-
const currentColumns = this.sharedService.grid.getColumns();
197+
const currentColumns = this.sharedService.grid.getColumns() as Column[];
196198
const visibleColumns = this.extensionUtility.arrayRemoveItemByIndex(currentColumns, columnIndex);
197199
this.sharedService.visibleColumns = visibleColumns;
198200
this.sharedService.grid.setColumns(visibleColumns);
201+
this.sharedService.onColumnsChanged.next(visibleColumns);
199202
}
200203
}
201204

@@ -333,12 +336,16 @@ export class HeaderMenuExtension implements Extension {
333336
// get previously sorted columns
334337
const sortedColsWithoutCurrent: ColumnSort[] = this.sortService.getCurrentColumnSorts(args.column.id + '');
335338

339+
let emitterType: EmitterType;
340+
336341
// add to the column array, the column sorted by the header menu
337342
sortedColsWithoutCurrent.push({ sortCol: args.column, sortAsc: isSortingAsc });
338343
if (this.sharedService.gridOptions.backendServiceApi) {
339344
this.sortService.onBackendSortChanged(event, { multiColumnSort: true, sortCols: sortedColsWithoutCurrent, grid: this.sharedService.grid });
345+
emitterType = EmitterType.remote;
340346
} else if (this.sharedService.dataView) {
341347
this.sortService.onLocalSortChanged(this.sharedService.grid, this.sharedService.dataView, sortedColsWithoutCurrent);
348+
emitterType = EmitterType.local;
342349
} else {
343350
// when using customDataView, we will simply send it as a onSort event with notify
344351
const isMultiSort = this.sharedService && this.sharedService.gridOptions && this.sharedService.gridOptions.multiColumnSort || false;
@@ -354,7 +361,23 @@ export class HeaderMenuExtension implements Extension {
354361
sortCol: col && col.sortCol,
355362
};
356363
});
357-
this.sharedService.grid.setSortColumns(newSortColumns); // add sort icon in UI
364+
365+
// add sort icon in UI
366+
this.sharedService.grid.setSortColumns(newSortColumns);
367+
368+
// if we have an emitter type set, we will emit a sort changed
369+
// for the Grid State Service to see the change.
370+
// We also need to pass current sorters changed to the emitSortChanged method
371+
if (emitterType) {
372+
const currentLocalSorters: CurrentSorter[] = [];
373+
newSortColumns.forEach((sortCol) => {
374+
currentLocalSorters.push({
375+
columnId: sortCol.columnId + '',
376+
direction: sortCol.sortAsc ? 'ASC' : 'DESC'
377+
});
378+
});
379+
this.sortService.emitSortChanged(emitterType, currentLocalSorters);
380+
}
358381
}
359382
}
360383
}

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

+13-1
Original file line numberDiff line numberDiff line change
@@ -588,13 +588,17 @@ describe('ExtensionService', () => {
588588
});
589589

590590
it('should re-register the Column Picker when enable and method is called with new column definition collection provided as argument', () => {
591+
const instanceMock = { onColumnsChanged: jest.fn() };
592+
const extensionMock = { name: ExtensionName.columnPicker, addon: null, instance: null, class: null } as ExtensionModel;
593+
const expectedExtension = { name: ExtensionName.columnPicker, addon: instanceMock, instance: instanceMock, class: null } as ExtensionModel;
591594
const gridOptionsMock = { enableColumnPicker: true } as GridOption;
592595
const columnsMock = [
593596
{ id: 'field1', field: 'field1', headerKey: 'HELLO' },
594597
{ id: 'field2', field: 'field2', headerKey: 'WORLD' }
595598
] as Column[];
596599
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
597600
jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub);
601+
const spyGetExt = jest.spyOn(service, 'getExtensionByName').mockReturnValue(extensionMock);
598602
const spyCpDispose = jest.spyOn(extensionColumnPickerStub, 'dispose');
599603
const spyCpRegister = jest.spyOn(extensionColumnPickerStub, 'register');
600604
const spyAllCols = jest.spyOn(SharedService.prototype, 'allColumns', 'set');
@@ -609,20 +613,28 @@ describe('ExtensionService', () => {
609613
});
610614

611615
it('should re-register the Grid Menu when enable and method is called with new column definition collection provided as argument', () => {
616+
const instanceMock = { onColumnsChanged: jest.fn() };
617+
const extensionMock = { name: ExtensionName.gridMenu, addon: null, instance: null, class: null } as ExtensionModel;
618+
const expectedExtension = { name: ExtensionName.gridMenu, addon: instanceMock, instance: instanceMock, class: null } as ExtensionModel;
612619
const gridOptionsMock = { enableGridMenu: true } as GridOption;
613620
const columnsMock = [
614621
{ id: 'field1', field: 'field1', headerKey: 'HELLO' },
615622
{ id: 'field2', field: 'field2', headerKey: 'WORLD' }
616623
] as Column[];
617624
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
618625
jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub);
626+
const spyGetExt = jest.spyOn(service, 'getExtensionByName').mockReturnValue(extensionMock);
619627
const spyGmDispose = jest.spyOn(extensionGridMenuStub, 'dispose');
620-
const spyGmRegister = jest.spyOn(extensionGridMenuStub, 'register');
628+
const spyGmRegister = jest.spyOn(extensionGridMenuStub, 'register').mockReturnValue(instanceMock);
621629
const spyAllCols = jest.spyOn(SharedService.prototype, 'allColumns', 'set');
622630
const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
623631

624632
service.renderColumnHeaders(columnsMock);
625633

634+
expect(expectedExtension).toEqual(expectedExtension);
635+
expect(spyGetExt).toHaveBeenCalled();
636+
expect(expectedExtension).toEqual(expectedExtension);
637+
expect(spyGetExt).toHaveBeenCalled();
626638
expect(spyGmDispose).toHaveBeenCalled();
627639
expect(spyGmRegister).toHaveBeenCalled();
628640
expect(spyAllCols).toHaveBeenCalledWith(columnsMock);

0 commit comments

Comments
 (0)