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

Commit 07d190b

Browse files
authored
Merge pull request #642 from ghiscoding/bugfix/hiding-showing-frozen-column-index
fix(core): showing/hiding column shouldn't affect its freezing position
2 parents e609ad3 + 7907cb8 commit 07d190b

File tree

14 files changed

+373
-14
lines changed

14 files changed

+373
-14
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,4 @@
178178
"yargs": "^15.4.1",
179179
"zone.js": "~0.9.1"
180180
}
181-
}
181+
}

src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,16 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () =
346346
expect(component.gridOptions.enableMouseWheelScrollHandler).toBeTrue();
347347
});
348348

349+
it('should keep frozen column index reference (via frozenVisibleColumnId) when grid is a frozen grid', () => {
350+
const sharedFrozenIndexSpy = jest.spyOn(SharedService.prototype, 'frozenVisibleColumnId', 'set');
351+
component.gridOptions.frozenColumn = 0;
352+
353+
component.ngOnInit();
354+
component.ngAfterViewInit();
355+
356+
expect(sharedFrozenIndexSpy).toHaveBeenCalledWith('name');
357+
});
358+
349359
describe('initialization method', () => {
350360
describe('columns definitions changed', () => {
351361
it('should expect "translateColumnHeaders" being called when "enableTranslate" is set', () => {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,12 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn
838838
// emit the Grid & DataView object to make them available in parent component
839839
this.onGridCreated.emit(this.grid);
840840

841+
// when it's a frozen grid, we need to keep the frozen column id for reference if we ever show/hide column from ColumnPicker/GridMenu afterward
842+
const frozenColumnIndex = this.gridOptions.frozenColumn !== undefined ? this.gridOptions.frozenColumn : -1;
843+
if (frozenColumnIndex >= 0 && frozenColumnIndex <= this._columnDefinitions.length) {
844+
this.sharedService.frozenVisibleColumnId = this._columnDefinitions[frozenColumnIndex].id || '';
845+
}
846+
841847
// initialize the SlickGrid grid
842848
this.grid.init();
843849

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

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ declare const Slick: any;
1111
const gridStub = {
1212
getOptions: jest.fn(),
1313
registerPlugin: jest.fn(),
14+
setColumns: jest.fn(),
15+
setOptions: jest.fn(),
1416
};
1517

1618
const mockAddon = jest.fn().mockImplementation(() => ({
@@ -80,41 +82,59 @@ describe('columnPickerExtension', () => {
8082
expect(mockAddon).toHaveBeenCalledWith(columnsMock, gridStub, gridOptionsMock);
8183
});
8284

83-
it('should call internal event handler subscribe and expect the "onColumnSpy" option to be called when addon notify is called', () => {
85+
it('should call internal event handler subscribe and expect the "onColumnsChanged" grid option to be called when addon notify is called', () => {
8486
const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
8587
const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.columnPicker, 'onColumnsChanged');
8688
const visibleColsSpy = jest.spyOn(SharedService.prototype, 'visibleColumns', 'set');
89+
const readjustSpy = jest.spyOn(extensionUtility, 'readjustFrozenColumnIndexWhenNeeded');
8790

8891
const instance = extension.register();
89-
instance.onColumnsChanged.notify({ columns: columnsMock.slice(0, 1), grid: gridStub }, new Slick.EventData(), gridStub);
92+
instance.onColumnsChanged.notify({ columnId: 'field1', showing: false, columns: columnsMock.slice(0, 1), grid: gridStub }, new Slick.EventData(), gridStub);
9093

94+
expect(readjustSpy).not.toHaveBeenCalled();
9195
expect(handlerSpy).toHaveBeenCalledTimes(1);
9296
expect(handlerSpy).toHaveBeenCalledWith(
9397
{ notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
9498
expect.anything()
9599
);
96-
expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), { columns: columnsMock.slice(0, 1), grid: gridStub });
100+
expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), { columnId: 'field1', showing: false, columns: columnsMock.slice(0, 1), grid: gridStub });
97101
expect(visibleColsSpy).not.toHaveBeenCalled();
98102
});
99103

100-
it(`should call internal event handler subscribe and expect the "onColumnSpy" option to be called when addon notify is called
104+
it(`should call internal event handler subscribe and expect the "onColumnsChanged" grid option to be called when addon notify is called
101105
and it should override "visibleColumns" when array passed as arguments is bigger than previous visible columns`, () => {
102106
const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
103107
const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.columnPicker, 'onColumnsChanged');
104108
const visibleColsSpy = jest.spyOn(SharedService.prototype, 'visibleColumns', 'set');
105109

106110
const instance = extension.register();
107-
instance.onColumnsChanged.notify({ columns: columnsMock, grid: gridStub }, new Slick.EventData(), gridStub);
111+
instance.onColumnsChanged.notify({ columnId: 'field1', showing: true, columns: columnsMock, grid: gridStub }, new Slick.EventData(), gridStub);
108112

109113
expect(handlerSpy).toHaveBeenCalledTimes(1);
110114
expect(handlerSpy).toHaveBeenCalledWith(
111115
{ notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
112116
expect.anything()
113117
);
114-
expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), { columns: columnsMock, grid: gridStub });
118+
expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), { columnId: 'field1', showing: true, columns: columnsMock, grid: gridStub });
115119
expect(visibleColsSpy).toHaveBeenCalledWith(columnsMock);
116120
});
117121

122+
it('should call internal "onColumnsChanged" event and expect "readjustFrozenColumnIndexWhenNeeded" method to be called when the grid is detected to be a frozen grid', () => {
123+
gridOptionsMock.frozenColumn = 0;
124+
const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
125+
const readjustSpy = jest.spyOn(extensionUtility, 'readjustFrozenColumnIndexWhenNeeded');
126+
127+
const instance = extension.register();
128+
instance.onColumnsChanged.notify({ columnId: 'field1', showing: false, allColumns: columnsMock, columns: columnsMock.slice(0, 1), grid: gridStub }, new Slick.EventData(), gridStub);
129+
130+
expect(handlerSpy).toHaveBeenCalledTimes(1);
131+
expect(handlerSpy).toHaveBeenCalledWith(
132+
{ notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
133+
expect.anything()
134+
);
135+
expect(readjustSpy).toHaveBeenCalledWith('field1', 0, false, columnsMock, columnsMock.slice(0, 1));
136+
});
137+
118138
it('should dispose of the addon', () => {
119139
const instance = extension.register();
120140
const destroySpy = jest.spyOn(instance, 'destroy');

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

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ import { TestBed } from '@angular/core/testing';
22
import { TranslateService, TranslateModule } from '@ngx-translate/core';
33

44
import { ExtensionUtility } from '../extensionUtility';
5-
import { ExtensionName, GridOption } from '../../models';
5+
import { Column, ExtensionName, GridOption, SlickGrid } from '../../models';
66
import { SharedService } from '../../services/shared.service';
77

88
declare let Slick: any;
99

10+
const gridStub = {
11+
getOptions: jest.fn(),
12+
setColumns: jest.fn(),
13+
setOptions: jest.fn(),
14+
registerPlugin: jest.fn(),
15+
} as unknown as SlickGrid;
16+
1017
const mockAddon = jest.fn().mockImplementation(() => ({
1118
init: jest.fn(),
1219
destroy: jest.fn()
@@ -227,6 +234,71 @@ describe('ExtensionUtility', () => {
227234
expect(output).toBe('Commandes');
228235
});
229236
});
237+
238+
describe('readjustFrozenColumnIndexWhenNeeded method', () => {
239+
let gridOptionsMock: GridOption;
240+
241+
beforeEach(() => {
242+
gridOptionsMock = { frozenColumn: 1 } as GridOption;
243+
jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub);
244+
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
245+
jest.spyOn(SharedService.prototype, 'frozenVisibleColumnId', 'get').mockReturnValue('field2');
246+
});
247+
248+
afterEach(() => {
249+
jest.clearAllMocks();
250+
});
251+
252+
it('should increase "frozenColumn" from 0 to 1 when showing a column that was previously hidden and its index is lower or equal to provided argument (2nd arg, frozenColumnIndex)', () => {
253+
const allColumns = [{ id: 'field1' }, { id: 'field2' }, { id: 'field3' }] as Column[];
254+
const visibleColumns = [{ id: 'field1' }, { id: 'field2' }] as Column[];
255+
const setOptionSpy = jest.spyOn(SharedService.prototype.grid, 'setOptions');
256+
257+
utility.readjustFrozenColumnIndexWhenNeeded('field1', 0, true, allColumns, visibleColumns);
258+
259+
expect(setOptionSpy).toHaveBeenCalledWith({ frozenColumn: 1 });
260+
});
261+
262+
it('should keep "frozenColumn" at 0 when showing a column that was previously hidden and its index is greater than provided argument (2nd arg, frozenColumnIndex)', () => {
263+
const allColumns = [{ id: 'field1' }, { id: 'field2' }, { id: 'field3' }] as Column[];
264+
const visibleColumns = [{ id: 'field1' }, { id: 'field2' }, { id: 'field3' }] as Column[];
265+
const setOptionSpy = jest.spyOn(SharedService.prototype.grid, 'setOptions');
266+
267+
utility.readjustFrozenColumnIndexWhenNeeded('field3', 0, true, allColumns, visibleColumns);
268+
269+
expect(setOptionSpy).not.toHaveBeenCalled();
270+
});
271+
272+
it('should decrease "frozenColumn" from 1 to 0 when hiding a column that was previously shown and its index is lower or equal to provided argument (2nd arg, frozenColumnIndex)', () => {
273+
const allColumns = [{ id: 'field1' }, { id: 'field2' }, { id: 'field3' }] as Column[];
274+
const visibleColumns = [{ id: 'field1' }, { id: 'field2' }] as Column[];
275+
const setOptionSpy = jest.spyOn(SharedService.prototype.grid, 'setOptions');
276+
277+
utility.readjustFrozenColumnIndexWhenNeeded('field1', 1, false, allColumns, visibleColumns);
278+
279+
expect(setOptionSpy).toHaveBeenCalledWith({ frozenColumn: 0 });
280+
});
281+
282+
it('should keep "frozenColumn" at 1 when hiding a column that was previously hidden and its index is greater than provided argument (2nd arg, frozenColumnIndex)', () => {
283+
const allColumns = [{ id: 'field1' }, { id: 'field2' }, { id: 'field3' }] as Column[];
284+
const visibleColumns = [{ id: 'field1' }, { id: 'field2' }] as Column[];
285+
const setOptionSpy = jest.spyOn(SharedService.prototype.grid, 'setOptions');
286+
287+
utility.readjustFrozenColumnIndexWhenNeeded('field3', 1, false, allColumns, visibleColumns);
288+
289+
expect(setOptionSpy).not.toHaveBeenCalled();
290+
});
291+
292+
it('should not change "frozenColumn" when showing a column that was not found in the visibleColumns columns array', () => {
293+
const allColumns = [{ id: 'field1' }, { id: 'field2' }, { id: 'field3' }] as Column[];
294+
const visibleColumns = [{ id: 'field1' }, { field: 'field2' }] as Column[];
295+
const setOptionSpy = jest.spyOn(SharedService.prototype.grid, 'setOptions');
296+
297+
utility.readjustFrozenColumnIndexWhenNeeded('fiel3', 0, true, allColumns, visibleColumns);
298+
299+
expect(setOptionSpy).not.toHaveBeenCalled();
300+
});
301+
});
230302
});
231303

232304
describe('without ngx-translate', () => {

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,16 +195,18 @@ describe('gridMenuExtension', () => {
195195
const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onMenuClose');
196196
const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand');
197197
const visibleColsSpy = jest.spyOn(SharedService.prototype, 'visibleColumns', 'set');
198+
const readjustSpy = jest.spyOn(extensionUtility, 'readjustFrozenColumnIndexWhenNeeded');
198199

199200
const instance = extension.register();
200-
instance.onColumnsChanged.notify({ columns: columnsMock.slice(0, 1), grid: gridStub }, new Slick.EventData(), gridStub);
201+
instance.onColumnsChanged.notify({ columnId: 'field1', showing: false, columns: columnsMock.slice(0, 1), grid: gridStub }, new Slick.EventData(), gridStub);
201202

203+
expect(readjustSpy).not.toHaveBeenCalled();
202204
expect(handlerSpy).toHaveBeenCalledTimes(5);
203205
expect(handlerSpy).toHaveBeenCalledWith(
204206
{ notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
205207
expect.anything()
206208
);
207-
expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), { columns: columnsMock.slice(0, 1), grid: gridStub });
209+
expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), { columnId: 'field1', showing: false, columns: columnsMock.slice(0, 1), grid: gridStub });
208210
expect(onAfterSpy).not.toHaveBeenCalled();
209211
expect(onBeforeSpy).not.toHaveBeenCalled();
210212
expect(onCloseSpy).not.toHaveBeenCalled();
@@ -238,6 +240,22 @@ describe('gridMenuExtension', () => {
238240
expect(visibleColsSpy).toHaveBeenCalledWith(columnsMock);
239241
});
240242

243+
it('should call internal "onColumnsChanged" event and expect "readjustFrozenColumnIndexWhenNeeded" method to be called when the grid is detected to be a frozen grid', () => {
244+
gridOptionsMock.frozenColumn = 0;
245+
const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
246+
const readjustSpy = jest.spyOn(extensionUtility, 'readjustFrozenColumnIndexWhenNeeded');
247+
248+
const instance = extension.register();
249+
instance.onColumnsChanged.notify({ columnId: 'field1', showing: false, allColumns: columnsMock, columns: columnsMock.slice(0, 1), grid: gridStub }, new Slick.EventData(), gridStub);
250+
251+
expect(handlerSpy).toHaveBeenCalledTimes(5);
252+
expect(handlerSpy).toHaveBeenCalledWith(
253+
{ notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
254+
expect.anything()
255+
);
256+
expect(readjustSpy).toHaveBeenCalledWith('field1', 0, false, columnsMock, columnsMock.slice(0, 1));
257+
});
258+
241259
it('should call internal event handler subscribe and expect the "onBeforeMenuShow" option to be called when addon notify is called', () => {
242260
const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
243261
const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onColumnsChanged');

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ describe('headerMenuExtension', () => {
341341
jest.spyOn(gridStub, 'getColumnIndex').mockReturnValue(1);
342342
jest.spyOn(gridStub, 'getColumns').mockReturnValue(columnsMock);
343343
const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
344+
const setOptionSpy = jest.spyOn(gridStub, 'setOptions');
344345
const visibleSpy = jest.spyOn(SharedService.prototype, 'visibleColumns', 'set');
345346
const updatedColumnsMock = [{
346347
id: 'field1', field: 'field1', nameKey: 'TITLE', width: 100,
@@ -357,6 +358,35 @@ describe('headerMenuExtension', () => {
357358

358359
extension.hideColumn(columnsMock[1]);
359360

361+
expect(setOptionSpy).not.toHaveBeenCalled();
362+
expect(visibleSpy).toHaveBeenCalledWith(updatedColumnsMock);
363+
expect(setColumnsSpy).toHaveBeenCalledWith(updatedColumnsMock);
364+
});
365+
366+
it('should call hideColumn and expect "setOptions" to be called with new "frozenColumn" index when the grid is detected to be a frozen grid', () => {
367+
gridOptionsMock.frozenColumn = 1;
368+
jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub);
369+
jest.spyOn(gridStub, 'getColumnIndex').mockReturnValue(1);
370+
jest.spyOn(gridStub, 'getColumns').mockReturnValue(columnsMock);
371+
const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
372+
const setOptionSpy = jest.spyOn(gridStub, 'setOptions');
373+
const visibleSpy = jest.spyOn(SharedService.prototype, 'visibleColumns', 'set');
374+
const updatedColumnsMock = [{
375+
id: 'field1', field: 'field1', nameKey: 'TITLE', width: 100,
376+
header: {
377+
menu: {
378+
items: [
379+
{ iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 48 },
380+
{ divider: true, command: '', positionOrder: 49 },
381+
{ command: 'hide', iconCssClass: 'fa fa-times', positionOrder: 55, title: 'Cacher la colonne' }
382+
]
383+
}
384+
}
385+
}] as Column[];
386+
387+
extension.hideColumn(columnsMock[1]);
388+
389+
expect(setOptionSpy).toHaveBeenCalledWith({ frozenColumn: 0 });
360390
expect(visibleSpy).toHaveBeenCalledWith(updatedColumnsMock);
361391
expect(setColumnsSpy).toHaveBeenCalledWith(updatedColumnsMock);
362392
});

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@angular/core';
2-
import { ColumnPicker, Extension, ExtensionName, SlickEventHandler } from '../models/index';
2+
import { Column, ColumnPicker, Extension, ExtensionName, SlickEventHandler } from '../models/index';
33
import { ExtensionUtility } from './extensionUtility';
44
import { SharedService } from '../services/shared.service';
55

@@ -56,13 +56,20 @@ export class ColumnPickerExtension implements Extension {
5656
if (this._columnPicker.onExtensionRegistered) {
5757
this._columnPicker.onExtensionRegistered(this._addon);
5858
}
59-
this._eventHandler.subscribe(this._addon.onColumnsChanged, (e: any, args: { columns: any, grid: any }) => {
59+
this._eventHandler.subscribe(this._addon.onColumnsChanged, (e: any, args: { columnId: string; showing: boolean; columns: Column[]; allColumns: Column[]; grid: any; }) => {
6060
if (this._columnPicker && typeof this._columnPicker.onColumnsChanged === 'function') {
6161
this._columnPicker.onColumnsChanged(e, args);
6262
}
6363
if (args && Array.isArray(args.columns) && args.columns.length !== this.sharedService.visibleColumns.length) {
6464
this.sharedService.visibleColumns = args.columns;
6565
}
66+
// if we're using frozen columns, we need to readjust pinning when the new hidden column becomes visible again on the left pinning container
67+
// we need to readjust frozenColumn index because SlickGrid freezes by index and has no knowledge of the columns themselves
68+
const frozenColumnIndex = this.sharedService.gridOptions.frozenColumn !== undefined ? this.sharedService.gridOptions.frozenColumn : -1;
69+
if (frozenColumnIndex >= 0) {
70+
const { showing: isColumnShown, columnId, allColumns, columns: visibleColumns } = args;
71+
this.extensionUtility.readjustFrozenColumnIndexWhenNeeded(columnId, frozenColumnIndex, isColumnShown, allColumns, visibleColumns);
72+
}
6673
});
6774
}
6875
return this._addon;

0 commit comments

Comments
 (0)