diff --git a/src/app/examples/grid-state.component.ts b/src/app/examples/grid-state.component.ts index 9c0fd35ab..2a7de31d1 100644 --- a/src/app/examples/grid-state.component.ts +++ b/src/app/examples/grid-state.component.ts @@ -157,7 +157,11 @@ export class GridStateComponent implements OnInit, OnDestroy { hideForceFitButton: true }, gridMenu: { - hideForceFitButton: true + hideForceFitButton: true, + hideClearFrozenColumnsCommand: false, + }, + headerMenu: { + hideFreezeColumnsCommand: false, }, enablePagination: true, pagination: { diff --git a/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts b/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts index e3d1cb1e3..4bde8c8dd 100644 --- a/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts +++ b/src/app/modules/angular-slickgrid/components/__tests__/angular-slickgrid-constructor.spec.ts @@ -21,7 +21,7 @@ import { SortService, TreeDataService, } from '../../services'; -import { Column, CurrentFilter, CurrentSorter, GraphqlPaginatedResult, GraphqlServiceApi, GraphqlServiceOption, GridOption, GridState, GridStateChange, GridStateType, Pagination, ServicePagination } from '../../models'; +import { Column, CurrentFilter, CurrentPinning, CurrentSorter, GraphqlPaginatedResult, GraphqlServiceApi, GraphqlServiceOption, GridOption, GridState, GridStateChange, GridStateType, Pagination, ServicePagination } from '../../models'; import { Filters } from '../../filters'; import { Editors } from '../../editors'; import * as utilities from '../../services/backend-utilities'; @@ -847,6 +847,15 @@ describe('Angular-Slickgrid Custom Component instantiated via Constructor', () = expect(backendSpy).toHaveBeenCalledWith(mockColumnFilter, false); }); + it('should override frozen grid options when "pinning" is defined in the "presets" property', () => { + const pinningMock = { frozenBottom: false, frozenColumn: -1, frozenRow: -1 } as CurrentPinning; + + component.gridOptions.presets = { pinning: pinningMock }; + component.ngAfterViewInit(); + + expect(component.gridOptions).toEqual({ ...component.gridOptions, ...pinningMock }); + }); + it('should call the "updateFilters" method when filters are defined in the "presets" property', () => { const spy = jest.spyOn(mockGraphqlService, 'updateFilters'); const mockFilters = [{ columnId: 'company', searchTerms: ['xyz'], operator: 'IN' }] as CurrentFilter[]; diff --git a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts index 285533494..831e2a71d 100644 --- a/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts +++ b/src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts @@ -849,6 +849,11 @@ export class AngularSlickgridComponent implements AfterViewInit, OnDestroy, OnIn this.sharedService.visibleColumns = this._columnDefinitions; this.extensionService.createExtensionsBeforeGridCreation(this._columnDefinitions, this.gridOptions); + // if user entered some Pinning/Frozen "presets", we need to apply them in the grid options + if (this.gridOptions.presets?.pinning) { + this.gridOptions = { ...this.gridOptions, ...this.gridOptions.presets.pinning }; + } + // build SlickGrid Grid, also user might optionally pass a custom dataview (e.g. remote model) this.grid = new Slick.Grid(`#${this.gridId}`, this.customDataView || this.dataView, this._columnDefinitions, this.gridOptions); diff --git a/src/app/modules/angular-slickgrid/constants.ts b/src/app/modules/angular-slickgrid/constants.ts index db0d8803f..d832fc3ac 100644 --- a/src/app/modules/angular-slickgrid/constants.ts +++ b/src/app/modules/angular-slickgrid/constants.ts @@ -8,7 +8,7 @@ export class Constants { TEXT_CLEAR_ALL_FILTERS: 'Clear all Filters', TEXT_CLEAR_ALL_GROUPING: 'Clear all Grouping', TEXT_CLEAR_ALL_SORTING: 'Clear all Sorting', - TEXT_CLEAR_FROZEN_COLUMNS: 'Clear Frozen Columns', + TEXT_CLEAR_PINNING: 'Unfreeze Columns/Rows', TEXT_COLLAPSE_ALL_GROUPS: 'Collapse all Groups', TEXT_CONTAINS: 'Contains', TEXT_COLUMNS: 'Columns', diff --git a/src/app/modules/angular-slickgrid/extensions/__tests__/gridMenuExtension.spec.ts b/src/app/modules/angular-slickgrid/extensions/__tests__/gridMenuExtension.spec.ts index 56fedcc93..26999b6e8 100644 --- a/src/app/modules/angular-slickgrid/extensions/__tests__/gridMenuExtension.spec.ts +++ b/src/app/modules/angular-slickgrid/extensions/__tests__/gridMenuExtension.spec.ts @@ -5,7 +5,7 @@ import { GridOption } from '../../models/gridOption.interface'; import { GridMenuExtension } from '../gridMenuExtension'; import { ExtensionUtility } from '../extensionUtility'; import { SharedService } from '../../services/shared.service'; -import { Column, DelimiterType, FileType } from '../../models'; +import { BackendServiceApi, Column, DelimiterType, FileType, GridMenu } from '../../models'; import { ExcelExportService, ExportService, FilterService, SortService } from '../../services'; declare const Slick: any; @@ -138,7 +138,7 @@ describe('gridMenuExtension', () => { TITLE: 'Titre', CLEAR_ALL_FILTERS: 'Supprimer tous les filtres', CLEAR_ALL_SORTING: 'Supprimer tous les tris', - CLEAR_FROZEN_COLUMNS: 'Libérer les colonnes gelées', + CLEAR_PINNING: 'Dégeler les colonnes/rangées', EXPORT_TO_CSV: 'Exporter en format CSV', EXPORT_TO_EXCEL: 'Exporter vers Excel', EXPORT_TO_TAB_DELIMITED: 'Exporter en format texte (délimité par tabulation)', @@ -176,7 +176,7 @@ describe('gridMenuExtension', () => { }); it('should register the addon', () => { - const onRegisteredSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onExtensionRegistered'); + const onRegisteredSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onExtensionRegistered'); const instance = extension.register(); const addonInstance = extension.getAddonInstance(); @@ -189,11 +189,11 @@ describe('gridMenuExtension', () => { it('should call internal event handler subscribe and expect the "onColumnsChanged" option to be called when addon notify is called', () => { const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe'); - const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onColumnsChanged'); - const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onAfterMenuShow'); - const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onBeforeMenuShow'); - const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onMenuClose'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onColumnsChanged'); + const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onAfterMenuShow'); + const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onBeforeMenuShow'); + const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onMenuClose'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const visibleColsSpy = jest.spyOn(SharedService.prototype, 'visibleColumns', 'set'); const readjustSpy = jest.spyOn(extensionUtility, 'readjustFrozenColumnIndexWhenNeeded'); @@ -216,29 +216,29 @@ describe('gridMenuExtension', () => { it(`should call internal event handler subscribe and expect the "onColumnsChanged" option to be called and it should override "visibleColumns" when array passed as arguments is bigger than previous visible columns`, () => { - const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe'); - const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onColumnsChanged'); - const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onAfterMenuShow'); - const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onBeforeMenuShow'); - const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onMenuClose'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); - const visibleColsSpy = jest.spyOn(SharedService.prototype, 'visibleColumns', 'set'); - - const instance = extension.register(); - instance.onColumnsChanged.notify({ columns: columnsMock, grid: gridStub }, new Slick.EventData(), gridStub); - - expect(handlerSpy).toHaveBeenCalledTimes(5); - expect(handlerSpy).toHaveBeenCalledWith( - { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), }, - expect.anything() - ); - expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), { columns: columnsMock, grid: gridStub }); - expect(onAfterSpy).not.toHaveBeenCalled(); - expect(onBeforeSpy).not.toHaveBeenCalled(); - expect(onCloseSpy).not.toHaveBeenCalled(); - expect(onCommandSpy).not.toHaveBeenCalled(); - expect(visibleColsSpy).toHaveBeenCalledWith(columnsMock); - }); + const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe'); + const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onColumnsChanged'); + const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onAfterMenuShow'); + const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onBeforeMenuShow'); + const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onMenuClose'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); + const visibleColsSpy = jest.spyOn(SharedService.prototype, 'visibleColumns', 'set'); + + const instance = extension.register(); + instance.onColumnsChanged.notify({ columns: columnsMock, grid: gridStub }, new Slick.EventData(), gridStub); + + expect(handlerSpy).toHaveBeenCalledTimes(5); + expect(handlerSpy).toHaveBeenCalledWith( + { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), }, + expect.anything() + ); + expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), { columns: columnsMock, grid: gridStub }); + expect(onAfterSpy).not.toHaveBeenCalled(); + expect(onBeforeSpy).not.toHaveBeenCalled(); + expect(onCloseSpy).not.toHaveBeenCalled(); + expect(onCommandSpy).not.toHaveBeenCalled(); + expect(visibleColsSpy).toHaveBeenCalledWith(columnsMock); + }); it('should call internal "onColumnsChanged" event and expect "readjustFrozenColumnIndexWhenNeeded" method to be called when the grid is detected to be a frozen grid', () => { gridOptionsMock.frozenColumn = 0; @@ -258,11 +258,11 @@ describe('gridMenuExtension', () => { it('should call internal event handler subscribe and expect the "onBeforeMenuShow" option to be called when addon notify is called', () => { const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe'); - const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onColumnsChanged'); - const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onAfterMenuShow'); - const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onBeforeMenuShow'); - const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onMenuClose'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onColumnsChanged'); + const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onAfterMenuShow'); + const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onBeforeMenuShow'); + const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onMenuClose'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onBeforeMenuShow.notify({ grid: gridStub, menu: {} }, new Slick.EventData(), gridStub); @@ -281,11 +281,11 @@ describe('gridMenuExtension', () => { it('should call internal event handler subscribe and expect the "onAfterMenuShow" option to be called when addon notify is called', () => { const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe'); - const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onColumnsChanged'); - const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onAfterMenuShow'); - const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onBeforeMenuShow'); - const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onMenuClose'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onColumnsChanged'); + const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onAfterMenuShow'); + const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onBeforeMenuShow'); + const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onMenuClose'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onAfterMenuShow.notify({ grid: gridStub, menu: {} }, new Slick.EventData(), gridStub); @@ -304,11 +304,11 @@ describe('gridMenuExtension', () => { it('should call internal event handler subscribe and expect the "onMenuClose" option to be called when addon notify is called', () => { const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe'); - const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onColumnsChanged'); - const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onAfterMenuShow'); - const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onBeforeMenuShow'); - const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onMenuClose'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onColumnsChanged'); + const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onAfterMenuShow'); + const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onBeforeMenuShow'); + const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onMenuClose'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onMenuClose.notify({ grid: gridStub, menu: {} }, new Slick.EventData(), gridStub); @@ -327,11 +327,11 @@ describe('gridMenuExtension', () => { it('should call internal event handler subscribe and expect the "onCommand" option to be called when addon notify is called', () => { const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe'); - const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onColumnsChanged'); - const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onAfterMenuShow'); - const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onBeforeMenuShow'); - const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onMenuClose'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onColumnsChanged'); + const onAfterSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onAfterMenuShow'); + const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onBeforeMenuShow'); + const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onMenuClose'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onCommand.notify({ grid: gridStub, command: 'help' }, new Slick.EventData(), gridStub); @@ -350,8 +350,8 @@ describe('gridMenuExtension', () => { it('should call "autosizeColumns" method when the "onMenuClose" event was triggered and the columns are different', () => { const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe'); - const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onColumnsChanged'); - const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onMenuClose'); + const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onColumnsChanged'); + const onCloseSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onMenuClose'); const autoSizeSpy = jest.spyOn(gridStub, 'autosizeColumns'); const instance = extension.register(); @@ -381,16 +381,16 @@ describe('gridMenuExtension', () => { it('should expect an empty "customItems" array when both Filter & Sort are disabled', () => { extension.register(); - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([]); + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([]); }); - it('should expect menu related to "Clear Frozen Columns"', () => { + it('should expect menu related to "Unfreeze Columns/Rows"', () => { const copyGridOptionsMock = { ...gridOptionsMock, gridMenu: { hideClearFrozenColumnsCommand: false, } } as unknown as GridOption; jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); extension.register(); // calling 2x register to make sure it doesn't duplicate commands - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ - { iconCssClass: 'fa fa-times', title: 'Libérer les colonnes gelées', disabled: false, command: 'clear-frozen-columns', positionOrder: 49 }, + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ + { iconCssClass: 'fa fa-times', title: 'Dégeler les colonnes/rangées', disabled: false, command: 'clear-pinning', positionOrder: 52 }, ]); }); @@ -399,10 +399,10 @@ describe('gridMenuExtension', () => { jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); extension.register(); // calling 2x register to make sure it doesn't duplicate commands - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ { iconCssClass: 'fa fa-filter text-danger', title: 'Supprimer tous les filtres', disabled: false, command: 'clear-filter', positionOrder: 50 }, - { iconCssClass: 'fa fa-random', title: 'Basculer la ligne des filtres', disabled: false, command: 'toggle-filter', positionOrder: 52 }, - { iconCssClass: 'fa fa-refresh', title: 'Rafraîchir les données', disabled: false, command: 'refresh-dataset', positionOrder: 56 } + { iconCssClass: 'fa fa-random', title: 'Basculer la ligne des filtres', disabled: false, command: 'toggle-filter', positionOrder: 53 }, + { iconCssClass: 'fa fa-refresh', title: 'Rafraîchir les données', disabled: false, command: 'refresh-dataset', positionOrder: 57 } ]); }); @@ -411,7 +411,7 @@ describe('gridMenuExtension', () => { jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); extension.register(); // calling 2x register to make sure it doesn't duplicate commands - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ { iconCssClass: 'fa fa-filter text-danger', title: 'Supprimer tous les filtres', disabled: false, command: 'clear-filter', positionOrder: 50 } ]); }); @@ -421,8 +421,8 @@ describe('gridMenuExtension', () => { jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); extension.register(); // calling 2x register to make sure it doesn't duplicate commands - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ - { iconCssClass: 'fa fa-random', title: 'Basculer la ligne des filtres', disabled: false, command: 'toggle-filter', positionOrder: 52 }, + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ + { iconCssClass: 'fa fa-random', title: 'Basculer la ligne des filtres', disabled: false, command: 'toggle-filter', positionOrder: 53 }, ]); }); @@ -431,8 +431,8 @@ describe('gridMenuExtension', () => { jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); extension.register(); // calling 2x register to make sure it doesn't duplicate commands - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ - { iconCssClass: 'fa fa-refresh', title: 'Rafraîchir les données', disabled: false, command: 'refresh-dataset', positionOrder: 56 } + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ + { iconCssClass: 'fa fa-refresh', title: 'Rafraîchir les données', disabled: false, command: 'refresh-dataset', positionOrder: 57 } ]); }); @@ -441,8 +441,8 @@ describe('gridMenuExtension', () => { jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); extension.register(); // calling 2x register to make sure it doesn't duplicate commands - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ - { iconCssClass: 'fa fa-random', title: 'Basculer la ligne de pré-en-tête', disabled: false, command: 'toggle-preheader', positionOrder: 52 } + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ + { iconCssClass: 'fa fa-random', title: 'Basculer la ligne de pré-en-tête', disabled: false, command: 'toggle-preheader', positionOrder: 53 } ]); }); @@ -450,7 +450,7 @@ describe('gridMenuExtension', () => { const copyGridOptionsMock = { ...gridOptionsMock, showPreHeaderPanel: true, gridMenu: { hideClearFrozenColumnsCommand: true, hideTogglePreHeaderCommand: true } } as unknown as GridOption; jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([]); + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([]); }); it('should have the "clear-sorting" menu command when "enableSorting" is set', () => { @@ -458,7 +458,7 @@ describe('gridMenuExtension', () => { jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); extension.register(); // calling 2x register to make sure it doesn't duplicate commands - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ { iconCssClass: 'fa fa-unsorted text-danger', title: 'Supprimer tous les tris', disabled: false, command: 'clear-sorting', positionOrder: 51 } ]); }); @@ -467,7 +467,7 @@ describe('gridMenuExtension', () => { const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true, gridMenu: { hideClearFrozenColumnsCommand: true, hideClearAllSortingCommand: true } } as unknown as GridOption; jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([]); + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([]); }); it('should have the "export-csv" menu command when "enableExport" is set', () => { @@ -475,8 +475,8 @@ describe('gridMenuExtension', () => { jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); extension.register(); // calling 2x register to make sure it doesn't duplicate commands - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ - { iconCssClass: 'fa fa-download', title: 'Exporter en format CSV', disabled: false, command: 'export-csv', positionOrder: 53 } + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ + { iconCssClass: 'fa fa-download', title: 'Exporter en format CSV', disabled: false, command: 'export-csv', positionOrder: 54 } ]); }); @@ -484,7 +484,7 @@ describe('gridMenuExtension', () => { const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideClearFrozenColumnsCommand: true, hideExportExcelCommand: true, hideExportCsvCommand: true, hideExportTextDelimitedCommand: true } } as unknown as GridOption; jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([]); + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([]); }); it('should have the "export-excel" menu command when "enableExport" is set', () => { @@ -492,8 +492,8 @@ describe('gridMenuExtension', () => { jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); extension.register(); // calling 2x register to make sure it doesn't duplicate commands - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ - { iconCssClass: 'fa fa-file-excel-o text-success', title: 'Exporter vers Excel', disabled: false, command: 'export-excel', positionOrder: 54 } + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ + { iconCssClass: 'fa fa-file-excel-o text-success', title: 'Exporter vers Excel', disabled: false, command: 'export-excel', positionOrder: 55 } ]); }); @@ -502,8 +502,8 @@ describe('gridMenuExtension', () => { jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); extension.register(); // calling 2x register to make sure it doesn't duplicate commands - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ - { iconCssClass: 'fa fa-download', title: 'Exporter en format texte (délimité par tabulation)', disabled: false, command: 'export-text-delimited', positionOrder: 55 } + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ + { iconCssClass: 'fa fa-download', title: 'Exporter en format texte (délimité par tabulation)', disabled: false, command: 'export-text-delimited', positionOrder: 56 } ]); }); @@ -511,7 +511,7 @@ describe('gridMenuExtension', () => { const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideClearFrozenColumnsCommand: true, hideExportExcelCommand: true, hideExportCsvCommand: true, hideExportTextDelimitedCommand: true } } as unknown as GridOption; jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock); extension.register(); - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([]); + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([]); }); }); @@ -556,9 +556,9 @@ describe('gridMenuExtension', () => { it('should have user grid menu custom items', () => { extension.register(); - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ - { command: 'export-csv', disabled: false, iconCssClass: 'fa fa-download', positionOrder: 53, title: 'Exporter en format CSV' }, - // { command: 'export-excel', disabled: false, iconCssClass: 'fa fa-file-excel-o text-success', positionOrder: 54, title: 'Exporter vers Excel' }, + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ + { command: 'export-csv', disabled: false, iconCssClass: 'fa fa-download', positionOrder: 54, title: 'Exporter en format CSV' }, + // { command: 'export-excel', disabled: false, iconCssClass: 'fa fa-file-excel-o text-success', positionOrder: 55, title: 'Exporter vers Excel' }, { command: 'help', disabled: false, iconCssClass: 'fa fa-question-circle', positionOrder: 99, title: 'Aide', titleKey: 'HELP' }, ]); }); @@ -566,8 +566,8 @@ describe('gridMenuExtension', () => { it('should have same user grid menu custom items even when grid menu extension is registered multiple times', () => { extension.register(); extension.register(); - expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([ - { command: 'export-csv', disabled: false, iconCssClass: 'fa fa-download', positionOrder: 53, title: 'Exporter en format CSV' }, + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).customItems).toEqual([ + { command: 'export-csv', disabled: false, iconCssClass: 'fa fa-download', positionOrder: 54, title: 'Exporter en format CSV' }, { command: 'help', disabled: false, iconCssClass: 'fa fa-question-circle', positionOrder: 99, title: 'Aide', titleKey: 'HELP' }, ]); }); @@ -591,11 +591,11 @@ describe('gridMenuExtension', () => { data: { users: { nodes: [] }, pageInfo: { hasNextPage: true }, totalCount: 0 }, metrics: { startTime: now, endTime: now, executionTime: 0, totalItemCount: 0 } }; - const preSpy = jest.spyOn(gridOptionsMock.backendServiceApi, 'preProcess'); - const postSpy = jest.spyOn(gridOptionsMock.backendServiceApi, 'postProcess'); + const preSpy = jest.spyOn(gridOptionsMock.backendServiceApi as BackendServiceApi, 'preProcess'); + const postSpy = jest.spyOn(gridOptionsMock.backendServiceApi as BackendServiceApi, 'postProcess'); const promise = new Promise((resolve) => setTimeout(() => resolve(processResult), 1)); - const processSpy = jest.spyOn(gridOptionsMock.backendServiceApi, 'process').mockReturnValue(promise); - jest.spyOn(gridOptionsMock.backendServiceApi.service, 'buildQuery').mockReturnValue(query); + const processSpy = jest.spyOn(gridOptionsMock.backendServiceApi as BackendServiceApi, 'process').mockReturnValue(promise); + jest.spyOn((gridOptionsMock.backendServiceApi as BackendServiceApi).service, 'buildQuery').mockReturnValue(query); extension.refreshBackendDataset({ enableAddRow: true }); expect(preSpy).toHaveBeenCalled(); expect(processSpy).toHaveBeenCalled(); @@ -613,25 +613,25 @@ describe('gridMenuExtension', () => { mockGridMenuAddon.onCommand = new Slick.Event(); }); - it('should call "clearFrozenColumns" when the command triggered is "clear-frozen-columns"', () => { + it('should call "clearFrozenColumns" when the command triggered is "clear-pinning"', () => { const setOptionsSpy = jest.spyOn(gridStub, 'setOptions'); const setColumnsSpy = jest.spyOn(gridStub, 'setColumns'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); jest.spyOn(SharedService.prototype, 'allColumns', 'get').mockReturnValue(columnsMock); jest.spyOn(SharedService.prototype, 'visibleColumns', 'get').mockReturnValue(columnsMock.slice(0, 1)); const instance = extension.register(); - instance.onCommand.notify({ grid: gridStub, command: 'clear-frozen-columns' }, new Slick.EventData(), gridStub); + instance.onCommand.notify({ grid: gridStub, command: 'clear-pinning' }, new Slick.EventData(), gridStub); expect(onCommandSpy).toHaveBeenCalled(); expect(setColumnsSpy).toHaveBeenCalled(); - expect(setOptionsSpy).toHaveBeenCalledWith({ frozenColumn: -1, enableMouseWheelScrollHandler: false }); + expect(setOptionsSpy).toHaveBeenCalledWith({ frozenColumn: -1, frozenRow: -1, frozenBottom: false, enableMouseWheelScrollHandler: false }); }); it('should call "clearFilters" and dataview refresh when the command triggered is "clear-filter"', () => { const filterSpy = jest.spyOn(filterServiceStub, 'clearFilters'); const refreshSpy = jest.spyOn(SharedService.prototype.dataView, 'refresh'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onCommand.notify({ grid: gridStub, command: 'clear-filter' }, new Slick.EventData(), gridStub); @@ -644,7 +644,7 @@ describe('gridMenuExtension', () => { it('should call "clearSorting" and dataview refresh when the command triggered is "clear-sorting"', () => { const sortSpy = jest.spyOn(sortServiceStub, 'clearSorting'); const refreshSpy = jest.spyOn(SharedService.prototype.dataView, 'refresh'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onCommand.notify({ grid: gridStub, command: 'clear-sorting' }, new Slick.EventData(), gridStub); @@ -656,7 +656,7 @@ describe('gridMenuExtension', () => { it('should call "exportToExcel" when the command triggered is "export-excel"', () => { const excelExportSpy = jest.spyOn(excelExportServiceStub, 'exportToExcel'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onCommand.notify({ grid: gridStub, command: 'export-excel' }, new Slick.EventData(), gridStub); @@ -667,7 +667,7 @@ describe('gridMenuExtension', () => { it('should call "exportToFile" with CSV set when the command triggered is "export-csv"', () => { const exportSpy = jest.spyOn(exportServiceStub, 'exportToFile'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onCommand.notify({ grid: gridStub, command: 'export-csv' }, new Slick.EventData(), gridStub); @@ -681,7 +681,7 @@ describe('gridMenuExtension', () => { it('should call "exportToFile" with CSV set when the command triggered is "export-text-delimited"', () => { const exportSpy = jest.spyOn(exportServiceStub, 'exportToFile'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onCommand.notify({ grid: gridStub, command: 'export-text-delimited' }, new Slick.EventData(), gridStub); @@ -696,7 +696,7 @@ describe('gridMenuExtension', () => { it('should call the grid "setHeaderRowVisibility" method when the command triggered is "toggle-filter"', () => { gridOptionsMock.showHeaderRow = false; const gridSpy = jest.spyOn(SharedService.prototype.grid, 'setHeaderRowVisibility'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const setColumnSpy = jest.spyOn(SharedService.prototype.grid, 'setColumns'); const instance = extension.register(); @@ -717,7 +717,7 @@ describe('gridMenuExtension', () => { it('should call the grid "setTopPanelVisibility" method when the command triggered is "toggle-toppanel"', () => { gridOptionsMock.showTopPanel = false; const gridSpy = jest.spyOn(SharedService.prototype.grid, 'setTopPanelVisibility'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onCommand.notify({ grid: gridStub, command: 'toggle-toppanel' }, new Slick.EventData(), gridStub); @@ -735,7 +735,7 @@ describe('gridMenuExtension', () => { it('should call the grid "setPreHeaderPanelVisibility" method when the command triggered is "toggle-preheader"', () => { gridOptionsMock.showPreHeaderPanel = false; const gridSpy = jest.spyOn(SharedService.prototype.grid, 'setPreHeaderPanelVisibility'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onCommand.notify({ grid: gridStub, command: 'toggle-preheader' }, new Slick.EventData(), gridStub); @@ -752,7 +752,7 @@ describe('gridMenuExtension', () => { it('should call "refreshBackendDataset" method when the command triggered is "refresh-dataset"', () => { const refreshSpy = jest.spyOn(extension, 'refreshBackendDataset'); - const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onCommand'); + const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand'); const instance = extension.register(); instance.onCommand.notify({ grid: gridStub, command: 'refresh-dataset' }, new Slick.EventData(), gridStub); @@ -774,9 +774,9 @@ describe('gridMenuExtension', () => { expect(utilitySpy).toHaveBeenCalled(); expect(translateSpy).toHaveBeenCalled(); expect(updateColsSpy).toHaveBeenCalledWith(SharedService.prototype.gridOptions.gridMenu); - expect(SharedService.prototype.gridOptions.gridMenu.columnTitle).toBe('Colonnes'); - expect(SharedService.prototype.gridOptions.gridMenu.forceFitTitle).toBe('Ajustement forcé des colonnes'); - expect(SharedService.prototype.gridOptions.gridMenu.syncResizeTitle).toBe('Redimension synchrone'); + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).columnTitle).toBe('Colonnes'); + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).forceFitTitle).toBe('Ajustement forcé des colonnes'); + expect((SharedService.prototype.gridOptions.gridMenu as GridMenu).syncResizeTitle).toBe('Redimension synchrone'); expect(columnsMock).toEqual([ { id: 'field1', field: 'field1', width: 100, name: 'Titre', nameKey: 'TITLE' }, { id: 'field2', field: 'field2', width: 75 } @@ -789,7 +789,7 @@ describe('gridMenuExtension', () => { const instance = extension.register(); const showSpy = jest.spyOn(instance, 'showGridMenu'); - extension.showGridMenu(null); + extension.showGridMenu(null as any); expect(showSpy).toHaveBeenCalled(); }); @@ -798,7 +798,7 @@ describe('gridMenuExtension', () => { describe('without ngx-translate', () => { beforeEach(() => { - translate = null; + translate = null as any; extension = new GridMenuExtension({} as ExcelExportService, {} as ExportService, {} as ExtensionUtility, {} as FilterService, { gridOptions: { enableTranslate: true } } as SharedService, {} as SortService, translate); }); diff --git a/src/app/modules/angular-slickgrid/extensions/gridMenuExtension.ts b/src/app/modules/angular-slickgrid/extensions/gridMenuExtension.ts index 2dc5e4f2a..5353c9dad 100644 --- a/src/app/modules/angular-slickgrid/extensions/gridMenuExtension.ts +++ b/src/app/modules/angular-slickgrid/extensions/gridMenuExtension.ts @@ -216,17 +216,17 @@ export class GridMenuExtension implements Extension { const gridOptions = this.sharedService.gridOptions; const translationPrefix = getTranslationPrefix(gridOptions); - // show grid menu: Clear Frozen Columns + // show grid menu: Unfreeze Columns/Rows if (this.sharedService.gridOptions && this._gridMenuOptions && !this._gridMenuOptions.hideClearFrozenColumnsCommand) { - const commandName = 'clear-frozen-columns'; + const commandName = 'clear-pinning'; if (!originalCustomItems.find(item => item !== 'divider' && item.hasOwnProperty('command') && item.command === commandName)) { gridMenuCustomItems.push( { iconCssClass: this._gridMenuOptions.iconClearFrozenColumnsCommand || 'fa fa-times', - title: this.sharedService.gridOptions.enableTranslate ? this.translate.instant(`${translationPrefix}CLEAR_FROZEN_COLUMNS`) : this._locales && this._locales.TEXT_CLEAR_FROZEN_COLUMNS, + title: this.sharedService.gridOptions.enableTranslate ? this.translate.instant(`${translationPrefix}CLEAR_PINNING`) : this._locales && this._locales.TEXT_CLEAR_PINNING, disabled: false, command: commandName, - positionOrder: 49 + positionOrder: 52 } ); } @@ -259,7 +259,7 @@ export class GridMenuExtension implements Extension { title: this.sharedService.gridOptions.enableTranslate ? this.translate.instant(`${translationPrefix}TOGGLE_FILTER_ROW`) : this._locales && this._locales.TEXT_TOGGLE_FILTER_ROW, disabled: false, command: commandName, - positionOrder: 52 + positionOrder: 53 } ); } @@ -275,7 +275,7 @@ export class GridMenuExtension implements Extension { title: this.sharedService.gridOptions.enableTranslate ? this.translate.instant(`${translationPrefix}REFRESH_DATASET`) : this._locales && this._locales.TEXT_REFRESH_DATASET, disabled: false, command: commandName, - positionOrder: 56 + positionOrder: 57 } ); } @@ -293,7 +293,7 @@ export class GridMenuExtension implements Extension { title: this.sharedService.gridOptions.enableTranslate ? this.translate.instant(`${translationPrefix}TOGGLE_PRE_HEADER_ROW`) : this._locales && this._locales.TEXT_TOGGLE_PRE_HEADER_ROW, disabled: false, command: commandName, - positionOrder: 52 + positionOrder: 53 } ); } @@ -328,7 +328,7 @@ export class GridMenuExtension implements Extension { title: this.sharedService.gridOptions.enableTranslate ? this.translate.instant(`${translationPrefix}EXPORT_TO_CSV`) : this._locales && this._locales.TEXT_EXPORT_TO_CSV, disabled: false, command: commandName, - positionOrder: 53 + positionOrder: 54 } ); } @@ -344,7 +344,7 @@ export class GridMenuExtension implements Extension { title: this.sharedService.gridOptions.enableTranslate ? this.translate.instant(`${translationPrefix}EXPORT_TO_EXCEL`) : this._locales && this._locales.TEXT_EXPORT_TO_EXCEL, disabled: false, command: commandName, - positionOrder: 54 + positionOrder: 55 } ); } @@ -360,7 +360,7 @@ export class GridMenuExtension implements Extension { title: this.sharedService.gridOptions.enableTranslate ? this.translate.instant(`${translationPrefix}EXPORT_TO_TAB_DELIMITED`) : this._locales && this._locales.TEXT_EXPORT_TO_TAB_DELIMITED, disabled: false, command: commandName, - positionOrder: 55 + positionOrder: 56 } ); } @@ -383,15 +383,25 @@ export class GridMenuExtension implements Extension { private executeGridMenuInternalCustomCommands(e: Event, args: GridMenuItem) { if (args && args.command) { switch (args.command) { - case 'clear-frozen-columns': + case 'clear-pinning': const visibleColumns = [...this.sharedService.visibleColumns]; - this.sharedService.grid.setOptions({ frozenColumn: -1, enableMouseWheelScrollHandler: false }); + const newGridOptions = { frozenColumn: -1, frozenRow: -1, frozenBottom: false, enableMouseWheelScrollHandler: false }; + this.sharedService.grid.setOptions(newGridOptions); + this.sharedService.gridOptions.frozenColumn = newGridOptions.frozenColumn; + this.sharedService.gridOptions.frozenRow = newGridOptions.frozenRow; + this.sharedService.gridOptions.frozenBottom = newGridOptions.frozenBottom; + this.sharedService.gridOptions.enableMouseWheelScrollHandler = newGridOptions.enableMouseWheelScrollHandler; // SlickGrid seems to be somehow resetting the columns to their original positions, // so let's re-fix them to the position we kept as reference if (Array.isArray(visibleColumns)) { this.sharedService.grid.setColumns(visibleColumns); } + + // we also need to autosize columns if the option is enabled + if (this.sharedService.gridOptions.enableAutoSizeColumns) { + this.sharedService.grid.autosizeColumns(); + } break; case 'clear-filter': this.filterService.clearFilters(); diff --git a/src/app/modules/angular-slickgrid/extensions/headerMenuExtension.ts b/src/app/modules/angular-slickgrid/extensions/headerMenuExtension.ts index 23da63ef3..27eabe741 100644 --- a/src/app/modules/angular-slickgrid/extensions/headerMenuExtension.ts +++ b/src/app/modules/angular-slickgrid/extensions/headerMenuExtension.ts @@ -344,7 +344,10 @@ export class HeaderMenuExtension implements Extension { case 'freeze-columns': const visibleColumns = [...this.sharedService.visibleColumns]; const columnPosition = visibleColumns.findIndex((col) => col.id === args.column.id); - this.sharedService.grid.setOptions({ frozenColumn: columnPosition, enableMouseWheelScrollHandler: true } as GridOption); + const newGridOptions = { frozenColumn: columnPosition, enableMouseWheelScrollHandler: true }; + this.sharedService.grid.setOptions(newGridOptions); + this.sharedService.gridOptions.frozenColumn = newGridOptions.frozenColumn; + this.sharedService.gridOptions.enableMouseWheelScrollHandler = newGridOptions.enableMouseWheelScrollHandler; this.sharedService.frozenVisibleColumnId = args.column.id; // to freeze columns, we need to take only the visible columns and we also need to use setColumns() when some of them are hidden @@ -352,6 +355,11 @@ export class HeaderMenuExtension implements Extension { if (this.sharedService.hasColumnsReordered || (Array.isArray(this.sharedService.allColumns) && visibleColumns.length !== this.sharedService.allColumns.length)) { this.sharedService.grid.setColumns(visibleColumns); } + + // we also need to autosize columns if the option is enabled + if (this.sharedService.gridOptions.enableAutoSizeColumns) { + this.sharedService.grid.autosizeColumns(); + } break; case 'sort-asc': case 'sort-desc': diff --git a/src/app/modules/angular-slickgrid/models/currentPinning.interface.ts b/src/app/modules/angular-slickgrid/models/currentPinning.interface.ts new file mode 100644 index 000000000..8287414c1 --- /dev/null +++ b/src/app/modules/angular-slickgrid/models/currentPinning.interface.ts @@ -0,0 +1,10 @@ +export interface CurrentPinning { + /** Defaults to false, do we want to freeze (pin) the bottom portion instead of the top */ + frozenBottom?: boolean; + + /** Number of column index(es) to freeze (pin) in the grid */ + frozenColumn?: number; + + /** Number of row index(es) to freeze (pin) in the grid */ + frozenRow?: number; +} diff --git a/src/app/modules/angular-slickgrid/models/gridMenu.interface.ts b/src/app/modules/angular-slickgrid/models/gridMenu.interface.ts index 2768c69d3..1fc1fdee5 100644 --- a/src/app/modules/angular-slickgrid/models/gridMenu.interface.ts +++ b/src/app/modules/angular-slickgrid/models/gridMenu.interface.ts @@ -37,7 +37,7 @@ export interface GridMenu { /** Defaults to false, which will hide the "Clear all Sorting" command in the Grid Menu (Grid Option "enableSorting: true" has to be enabled) */ hideClearAllSortingCommand?: boolean; - /** Defaults to true, which will hide the "Clear Frozen Columns" command in the Grid Menu */ + /** Defaults to true, which will hide the "Unfreeze Columns/Rows" command in the Grid Menu */ hideClearFrozenColumnsCommand?: boolean; /** Defaults to false, which will hide the "Export to CSV" command in the Grid Menu (Grid Option "enableExport: true" has to be enabled) */ @@ -73,7 +73,7 @@ export interface GridMenu { /** icon for the "Clear all Sorting" command */ iconClearAllSortingCommand?: string; - /** icon for the "Clear Frozen Columns" command */ + /** icon for the "Unfreeze Columns/Rows" command */ iconClearFrozenColumnsCommand?: string; /** icon for the "Export to CSV" command */ diff --git a/src/app/modules/angular-slickgrid/models/gridOption.interface.ts b/src/app/modules/angular-slickgrid/models/gridOption.interface.ts index f9f66b920..e975fc6a9 100644 --- a/src/app/modules/angular-slickgrid/models/gridOption.interface.ts +++ b/src/app/modules/angular-slickgrid/models/gridOption.interface.ts @@ -333,10 +333,10 @@ export interface GridOption { /** Defaults to false, do we want to freeze (pin) the bottom portion instead of the top */ frozenBottom?: boolean; - /** Number of column(s) to freeze (pin) in the grid */ + /** Number of column index(es) to freeze (pin) in the grid */ frozenColumn?: number; - /** Number of row(s) to freeze (pin) in the grid */ + /** Number of row index(es) to freeze (pin) in the grid */ frozenRow?: number; /** Defaults to false, which leads to have row with full width */ diff --git a/src/app/modules/angular-slickgrid/models/gridState.interface.ts b/src/app/modules/angular-slickgrid/models/gridState.interface.ts index 6e2ba3b07..f52d505c8 100644 --- a/src/app/modules/angular-slickgrid/models/gridState.interface.ts +++ b/src/app/modules/angular-slickgrid/models/gridState.interface.ts @@ -1,4 +1,4 @@ -import { CurrentColumn, CurrentFilter, CurrentPagination, CurrentRowSelection, CurrentSorter } from './index'; +import { CurrentColumn, CurrentFilter, CurrentPagination, CurrentPinning, CurrentRowSelection, CurrentSorter } from './index'; export interface GridState { /** Columns (and their state: visibility/position) that are currently applied in the grid */ @@ -13,6 +13,9 @@ export interface GridState { /** Pagination (and it's state, pageNumber, pageSize) that are currently applied in the grid */ pagination?: CurrentPagination | null; + /** Pinning (frozen) column & row position */ + pinning?: CurrentPinning; + /** Row Selections (by their dataContext IDs and/or grid row indexes) */ rowSelection?: CurrentRowSelection | null; } diff --git a/src/app/modules/angular-slickgrid/models/gridStateChange.interface.ts b/src/app/modules/angular-slickgrid/models/gridStateChange.interface.ts index 91c73c761..9af5be8e3 100644 --- a/src/app/modules/angular-slickgrid/models/gridStateChange.interface.ts +++ b/src/app/modules/angular-slickgrid/models/gridStateChange.interface.ts @@ -1,10 +1,10 @@ -import { CurrentColumn, CurrentFilter, CurrentPagination, CurrentRowSelection, CurrentSorter, GridState, GridStateType } from './index'; +import { CurrentColumn, CurrentFilter, CurrentPagination, CurrentPinning, CurrentRowSelection, CurrentSorter, GridState, GridStateType } from './index'; export interface GridStateChange { /** Last Grid State Change that was triggered (only 1 type of change at a time) */ change?: { /** Grid State change, the values of the new change */ - newValues: CurrentColumn[] | CurrentFilter[] | CurrentSorter[] | CurrentPagination | CurrentRowSelection; + newValues: CurrentColumn[] | CurrentFilter[] | CurrentSorter[] | CurrentPagination | CurrentPinning | CurrentRowSelection; /** The Grid State Type of change that was made (filter/sorter/...) */ type: GridStateType; diff --git a/src/app/modules/angular-slickgrid/models/gridStateType.enum.ts b/src/app/modules/angular-slickgrid/models/gridStateType.enum.ts index a26fd3b37..d02576847 100644 --- a/src/app/modules/angular-slickgrid/models/gridStateType.enum.ts +++ b/src/app/modules/angular-slickgrid/models/gridStateType.enum.ts @@ -2,6 +2,7 @@ export enum GridStateType { columns = 'columns', filter = 'filter', pagination = 'pagination', + pinning = 'pinning', sorter = 'sorter', rowSelection = 'rowSelection', } diff --git a/src/app/modules/angular-slickgrid/models/index.ts b/src/app/modules/angular-slickgrid/models/index.ts index fecc74e56..0fbb9dcbd 100644 --- a/src/app/modules/angular-slickgrid/models/index.ts +++ b/src/app/modules/angular-slickgrid/models/index.ts @@ -28,6 +28,7 @@ export * from './contextMenu.interface'; export * from './currentColumn.interface'; export * from './currentFilter.interface'; export * from './currentPagination.interface'; +export * from './currentPinning.interface'; export * from './currentRowSelection.interface'; export * from './currentSorter.interface'; export * from './customFooterOption.interface'; diff --git a/src/app/modules/angular-slickgrid/models/locale.interface.ts b/src/app/modules/angular-slickgrid/models/locale.interface.ts index be289d3db..07dc98288 100644 --- a/src/app/modules/angular-slickgrid/models/locale.interface.ts +++ b/src/app/modules/angular-slickgrid/models/locale.interface.ts @@ -17,8 +17,8 @@ export interface Locale { /** Text "Clear all Sorting" shown in Header Menu */ TEXT_CLEAR_ALL_SORTING: string; - /** Text "Clear Frozen Columns" shown in Grid Menu */ - TEXT_CLEAR_FROZEN_COLUMNS: string; + /** Text "Unfreeze Columns/Rows" shown in Grid Menu */ + TEXT_CLEAR_PINNING: string; /** Text "Columns" title displayed in the Column Picker & Grid Menu (when enabled) */ TEXT_COLUMNS: string; diff --git a/src/app/modules/angular-slickgrid/services/__tests__/grid.service.spec.ts b/src/app/modules/angular-slickgrid/services/__tests__/grid.service.spec.ts index 69de5ca1f..786d9b092 100644 --- a/src/app/modules/angular-slickgrid/services/__tests__/grid.service.spec.ts +++ b/src/app/modules/angular-slickgrid/services/__tests__/grid.service.spec.ts @@ -1,6 +1,6 @@ import { TestBed } from '@angular/core/testing'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { GridService, ExtensionService, FilterService, GridStateService, SortService, SharedService } from '..'; +import { GridService, FilterService, GridStateService, SortService, SharedService } from '..'; import { CellArgs, Column, OnEventArgs, GridOption } from './../../models'; declare const Slick: any; @@ -15,10 +15,6 @@ const mockSelectionModelImplementation = jest.fn().mockImplementation(() => mock jest.mock('slickgrid/plugins/slick.rowselectionmodel', () => mockSelectionModelImplementation); Slick.RowSelectionModel = mockSelectionModelImplementation; -const extensionServiceStub = { - getAllColumns: jest.fn(), -} as unknown as ExtensionService; - const filterServiceStub = { clearFilters: jest.fn(), } as unknown as FilterService; @@ -65,6 +61,7 @@ const gridStub = { navigateTop: jest.fn(), render: jest.fn(), setColumns: jest.fn(), + setOptions: jest.fn(), setSelectedRows: jest.fn(), scrollRowIntoView: jest.fn(), updateRow: jest.fn(), @@ -80,7 +77,6 @@ describe('Grid Service', () => { beforeEach(() => { TestBed.configureTestingModule({ providers: [ - { provide: ExtensionService, useValue: extensionServiceStub }, { provide: FilterService, useValue: filterServiceStub }, { provide: GridStateService, useValue: gridStateServiceStub }, { provide: SharedService, useValue: sharedService }, @@ -310,7 +306,7 @@ describe('Grid Service', () => { it('should throw an error when calling "upsertItemById" without a valid "id"', () => { const mockItem = { id: 0, user: { firstName: 'John', lastName: 'Doe' } }; - expect(() => service.upsertItemById(undefined, mockItem)).toThrowError('Calling Upsert of an item requires the item to include a valid and unique "id" property'); + expect(() => service.upsertItemById(undefined as any, mockItem)).toThrowError('Calling Upsert of an item requires the item to include a valid and unique "id" property'); }); it('should call the "upsertItemById" method and expect it to call the "addItem" with default boolean flags', () => { @@ -492,7 +488,7 @@ describe('Grid Service', () => { it('should throw an error when calling "updateItemById" without a valid "id"', () => { const mockItem = { id: 0, user: { firstName: 'John', lastName: 'Doe' } }; - expect(() => service.updateItemById(undefined, mockItem)).toThrowError('Cannot update a row without a valid "id"'); + expect(() => service.updateItemById(undefined as any, mockItem)).toThrowError('Cannot update a row without a valid "id"'); }); it('should throw an error when calling "updateItemById" and not finding the item in the grid', () => { @@ -755,8 +751,8 @@ describe('Grid Service', () => { }); it('should throw an error when calling "deleteItemById" without a valid "id" as argument', () => { - expect(() => service.deleteItemById(null)).toThrowError('Cannot delete a row without a valid "id"'); - expect(() => service.deleteItemById(undefined)).toThrowError('Cannot delete a row without a valid "id"'); + expect(() => service.deleteItemById(null as any)).toThrowError('Cannot delete a row without a valid "id"'); + expect(() => service.deleteItemById(undefined as any)).toThrowError('Cannot delete a row without a valid "id"'); }); it('should expect the service to call "deleteItemById" method when calling "deleteItem" with an item', () => { @@ -950,9 +946,54 @@ describe('Grid Service', () => { }); }); + describe('Pinning methods', () => { + const columnsMock: Column[] = [{ id: 'field1', field: 'field1', width: 100, nameKey: 'TITLE' }, { id: 'field2', field: 'field2', width: 75 }]; + + it('should call "clearPinning" and expect SlickGrid "setOptions" and "setColumns" to be called with frozen options being reset', () => { + const setOptionsSpy = jest.spyOn(gridStub, 'setOptions'); + const setColumnsSpy = jest.spyOn(gridStub, 'setColumns'); + jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub); + jest.spyOn(SharedService.prototype, 'allColumns', 'get').mockReturnValue(columnsMock); + jest.spyOn(SharedService.prototype, 'visibleColumns', 'get').mockReturnValue(columnsMock.slice(0, 1)); + + service.clearPinning(); + + expect(setColumnsSpy).toHaveBeenCalled(); + expect(setOptionsSpy).toHaveBeenCalledWith({ frozenBottom: false, frozenColumn: -1, frozenRow: -1, enableMouseWheelScrollHandler: false }); + }); + + it('should call "setPinning" and expect SlickGrid "setOptions" be called with new frozen options and "autosizeColumns" also be called', () => { + const mockPinning = { frozenBottom: true, frozenColumn: 1, frozenRow: 2 }; + jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub); + const setOptionsSpy = jest.spyOn(gridStub, 'setOptions'); + const autosizeColumnsSpy = jest.spyOn(gridStub, 'autosizeColumns'); + const gridOptionSetterSpy = jest.spyOn(SharedService.prototype, 'gridOptions', 'set'); + + service.setPinning(mockPinning); + + expect(setOptionsSpy).toHaveBeenCalledWith(mockPinning); + expect(gridOptionSetterSpy).toHaveBeenCalledWith(mockPinning); + expect(autosizeColumnsSpy).toHaveBeenCalled(); + }); + + it('should call "setPinning" and expect SlickGrid "setOptions" be called with new frozen options and "autosizeColumns" not being called when passing False as 2nd argument', () => { + const mockPinning = { frozenBottom: true, frozenColumn: 1, frozenRow: 2 }; + jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub); + const setOptionsSpy = jest.spyOn(gridStub, 'setOptions'); + const autosizeColumnsSpy = jest.spyOn(gridStub, 'autosizeColumns'); + const gridOptionSetterSpy = jest.spyOn(SharedService.prototype, 'gridOptions', 'set'); + + service.setPinning(mockPinning, false); + + expect(setOptionsSpy).toHaveBeenCalledWith(mockPinning); + expect(gridOptionSetterSpy).toHaveBeenCalledWith(mockPinning); + expect(autosizeColumnsSpy).not.toHaveBeenCalled(); + }); + }); + describe('getColumnFromEventArguments method', () => { it('should throw an error when slickgrid getColumns method is not available', () => { - gridStub.getColumns = undefined; + gridStub.getColumns = undefined as any; expect(() => service.getColumnFromEventArguments({} as CellArgs)) .toThrowError('To get the column definition and data, we need to have these arguments passed as objects (row, cell, grid)'); @@ -960,7 +1001,7 @@ describe('Grid Service', () => { }); it('should throw an error when slickgrid getDataItem method is not available', () => { - gridStub.getDataItem = undefined; + gridStub.getDataItem = undefined as any; expect(() => service.getColumnFromEventArguments({} as CellArgs)) .toThrowError('To get the column definition and data, we need to have these arguments passed as objects (row, cell, grid)'); @@ -983,7 +1024,7 @@ describe('Grid Service', () => { describe('getDataItemByRowNumber method', () => { it('should throw an error when slickgrid "getDataItem" method is not available', () => { - gridStub.getDataItem = undefined; + gridStub.getDataItem = undefined as any; expect(() => service.getDataItemByRowNumber(0)).toThrowError(`We could not find SlickGrid Grid object or it's "getDataItem" method`); gridStub.getDataItem = jest.fn(); // put it back as a valid mock for later tests }); @@ -1068,16 +1109,16 @@ describe('Grid Service', () => { it(`should return an Item Metadata object with filled "cssClasses" property including a row number in the string when the column definition has a "rowClass" property and when callback provided already returns a "cssClasses" property`, () => { - const rowNumber = 1; - const dataviewSpy = jest.spyOn(dataviewStub, 'getItem').mockReturnValue(columnDefinitions[rowNumber]); + const rowNumber = 1; + const dataviewSpy = jest.spyOn(dataviewStub, 'getItem').mockReturnValue(columnDefinitions[rowNumber]); - const callback = service.getItemRowMetadataToHighlight(mockItemMetadataFn); - const output = callback(rowNumber); // execute callback with a row number + const callback = service.getItemRowMetadataToHighlight(mockItemMetadataFn); + const output = callback(rowNumber); // execute callback with a row number - expect(dataviewSpy).toHaveBeenCalled(); - expect(typeof callback === 'function').toBe(true); - expect(output).toEqual({ cssClasses: ' red row1' }); - }); + expect(dataviewSpy).toHaveBeenCalled(); + expect(typeof callback === 'function').toBe(true); + expect(output).toEqual({ cssClasses: ' red row1' }); + }); }); describe('highlightRowByMetadata method', () => { @@ -1105,7 +1146,7 @@ describe('Grid Service', () => { }); it('should throw an error when the grid "getDataItem" method is not available', () => { - gridStub.getDataItem = undefined; + gridStub.getDataItem = undefined as any; expect(() => service.getDataItemByRowIndex(0)) .toThrowError('We could not find SlickGrid Grid object and/or "getDataItem" method'); }); @@ -1127,7 +1168,7 @@ describe('Grid Service', () => { }); it('should throw an error when the grid "getDataItem" method is not available', () => { - gridStub.getDataItem = undefined; + gridStub.getDataItem = undefined as any; expect(() => service.getDataItemByRowIndexes([0])) .toThrowError('We could not find SlickGrid Grid object and/or "getDataItem" method'); }); @@ -1149,7 +1190,7 @@ describe('Grid Service', () => { }); it('should throw an error when the grid "getSelectedRows" method is not available', () => { - gridStub.getSelectedRows = undefined; + gridStub.getSelectedRows = undefined as any; expect(() => service.getSelectedRows()) .toThrowError('We could not find SlickGrid Grid object and/or "getSelectedRows" method'); }); @@ -1169,7 +1210,7 @@ describe('Grid Service', () => { }); it('should throw an error when the grid "getSelectedRows" method is not available', () => { - gridStub.getSelectedRows = undefined; + gridStub.getSelectedRows = undefined as any; expect(() => service.getSelectedRowsDataItem()) .toThrowError('We could not find SlickGrid Grid object and/or "getSelectedRows" method'); }); @@ -1462,32 +1503,34 @@ describe('Grid Service', () => { it('should call a reset and expect a few grid methods to be called', () => { const mockColumns = [{ id: 'field1', width: 100 }, { id: 'field2', width: 150 }, { id: 'field3', field: 'field3' }] as Column[]; jest.spyOn(gridStub, 'getOptions').mockReturnValue({ enableAutoResize: true, enableAutoSizeColumns: true } as GridOption); - const extensionSpy = jest.spyOn(extensionServiceStub, 'getAllColumns').mockReturnValue(mockColumns); + const allColumnSpy = jest.spyOn(SharedService.prototype, 'allColumns', 'get').mockReturnValue(mockColumns); const setColSpy = jest.spyOn(gridStub, 'setColumns'); const autosizeSpy = jest.spyOn(gridStub, 'autosizeColumns'); const gridStateSpy = jest.spyOn(gridStateServiceStub, 'resetColumns'); const filterSpy = jest.spyOn(filterServiceStub, 'clearFilters'); const sortSpy = jest.spyOn(sortServiceStub, 'clearSorting'); + const clearPinningSpy = jest.spyOn(service, 'clearPinning'); service.resetGrid(); - expect(extensionSpy).toHaveBeenCalled(); + expect(allColumnSpy).toHaveBeenCalled(); expect(setColSpy).toHaveBeenCalled(); expect(autosizeSpy).toHaveBeenCalled(); expect(gridStateSpy).toHaveBeenCalled(); expect(filterSpy).toHaveBeenCalled(); expect(sortSpy).toHaveBeenCalled(); + expect(clearPinningSpy).toHaveBeenCalled(); }); it('should call a reset and expect the grid "resetColumns" method to be called with the column definitions provided to the method', () => { const mockColumns = [{ id: 'field1', width: 100 }, { id: 'field2', width: 150 }, { id: 'field3', field: 'field3' }] as Column[]; jest.spyOn(gridStub, 'getOptions').mockReturnValue({ enableAutoResize: true, enableAutoSizeColumns: true } as GridOption); - const extensionSpy = jest.spyOn(extensionServiceStub, 'getAllColumns').mockReturnValue(mockColumns); + const allColumnSpy = jest.spyOn(SharedService.prototype, 'allColumns', 'get').mockReturnValue(mockColumns); const gridStateSpy = jest.spyOn(gridStateServiceStub, 'resetColumns'); service.resetGrid(mockColumns); - expect(extensionSpy).toHaveBeenCalled(); + expect(allColumnSpy).toHaveBeenCalled(); expect(gridStateSpy).toHaveBeenCalledWith(mockColumns); }); }); @@ -1498,7 +1541,7 @@ describe('Grid Service', () => { }); it('should be able to highlight first row at zero index', () => { - const mockRowMetadata = (rowNumber) => ({ cssClasses: `row-${rowNumber}` }); + const mockRowMetadata = (rowNumber: number) => ({ cssClasses: `row-${rowNumber}` }); const mockItem = { id: 0, firstName: 'John', lastName: 'Doe' }; jest.spyOn(service, 'getItemRowMetadataToHighlight').mockReturnValue(mockRowMetadata); jest.spyOn(dataviewStub, 'getItem').mockReturnValue(mockItem); diff --git a/src/app/modules/angular-slickgrid/services/__tests__/gridState.service.spec.ts b/src/app/modules/angular-slickgrid/services/__tests__/gridState.service.spec.ts index 264e8f4ea..f9c313263 100644 --- a/src/app/modules/angular-slickgrid/services/__tests__/gridState.service.spec.ts +++ b/src/app/modules/angular-slickgrid/services/__tests__/gridState.service.spec.ts @@ -9,6 +9,7 @@ import { BackendService, CurrentFilter, CurrentPagination, + CurrentPinning, CurrentRowSelection, CurrentSorter, Column, @@ -23,7 +24,10 @@ import { declare const Slick: any; const gridOptionMock = { - enableAutoResize: true + enableAutoResize: true, + frozenBottom: false, + frozenColumn: -1, + frozenRow: -1, } as GridOption; const backendServiceStub = { @@ -49,6 +53,7 @@ const gridStub = { getSelectedRows: jest.fn(), setColumns: jest.fn(), setSelectedRows: jest.fn(), + onSetOptions: new Slick.Event(), onColumnsReordered: new Slick.Event(), onColumnsResized: new Slick.Event(), onSelectedRowsChanged: new Slick.Event(), @@ -88,7 +93,7 @@ describe('GridStateService', () => { }); describe('init method', () => { - let slickgridEvent; + let slickgridEvent: any; beforeEach(() => { slickgridEvent = new Slick.Event(); @@ -215,7 +220,7 @@ describe('GridStateService', () => { }); }); - describe('bindSlickGridEventToGridStateChange tests', () => { + describe('bindSlickGridColumnChangeEventToGridStateChange tests', () => { it('should subscribe to some SlickGrid events and expect the event to be triggered when a notify is triggered after service was initialized', () => { const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[]; const associatedColumnsMock = [{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 }] as CurrentColumn[]; @@ -239,6 +244,23 @@ describe('GridStateService', () => { expect(rxOnChangeSpy).toHaveBeenCalledWith(stateChangeMock); }); }); + + describe('bindSlickGridOnSetOptionsEventToGridStateChange tests', () => { + it('should subscribe to some SlickGrid events and expect the event to be triggered when a notify is triggered after service was initialized', () => { + const mockGridOptionsBefore = { frozenBottom: false, frozenColumn: -1, frozenRow: -1 } as GridOption; + const mockGridOptionsAfter = { frozenBottom: true, frozenColumn: 1, frozenRow: 1 } as GridOption; + const gridStateMock = { pinning: mockGridOptionsBefore, columns: [], filters: [], sorters: [] } as GridState; + const stateChangeMock = { change: { newValues: mockGridOptionsAfter, type: GridStateType.pinning }, gridState: gridStateMock } as GridStateChange; + const rxOnChangeSpy = jest.spyOn(service.onGridStateChanged, 'next'); + const gridStateSpy = jest.spyOn(service, 'getCurrentGridState').mockReturnValue(gridStateMock); + + service.init(gridStub, dataViewStub); + gridStub.onSetOptions.notify({ optionsBefore: mockGridOptionsBefore, optionsAfter: mockGridOptionsAfter, grid: gridStub }, new Slick.EventData()); + + expect(gridStateSpy).toHaveBeenCalled(); + expect(rxOnChangeSpy).toHaveBeenCalledWith(stateChangeMock); + }); + }); }); describe('getAssociatedCurrentColumns method', () => { @@ -331,11 +353,14 @@ describe('GridStateService', () => { }); it('should call "getCurrentGridState" method and return Pagination', () => { + const gridOptionsMock = { enablePagination: true, frozenBottom: false, frozenColumn: -1, frozenRow: -1 } as GridOption; const paginationMock = { pageNumber: 2, pageSize: 50 } as CurrentPagination; const columnMock = [{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 }] as CurrentColumn[]; const filterMock = [{ columnId: 'field1', operator: 'EQ', searchTerms: [] }] as CurrentFilter[]; const sorterMock = [{ columnId: 'field1', direction: 'ASC' }, { columnId: 'field2', direction: 'DESC' }] as CurrentSorter[]; + const pinningMock = { frozenBottom: false, frozenColumn: -1, frozenRow: -1 } as CurrentPinning; + jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock); const columnSpy = jest.spyOn(service, 'getCurrentColumns').mockReturnValue(columnMock); const filterSpy = jest.spyOn(service, 'getCurrentFilters').mockReturnValue(filterMock); const sorterSpy = jest.spyOn(service, 'getCurrentSorters').mockReturnValue(sorterMock); @@ -347,17 +372,20 @@ describe('GridStateService', () => { expect(filterSpy).toHaveBeenCalled(); expect(sorterSpy).toHaveBeenCalled(); expect(paginationSpy).toHaveBeenCalled(); - expect(output).toEqual({ columns: columnMock, filters: filterMock, sorters: sorterMock, pagination: paginationMock } as GridState); + expect(output).toEqual({ columns: columnMock, filters: filterMock, sorters: sorterMock, pagination: paginationMock, pinning: pinningMock, } as GridState); }); }); describe('getCurrentRowSelections method', () => { + let pinningMock: CurrentPinning; + beforeEach(() => { jest.clearAllMocks(); + pinningMock = { frozenBottom: false, frozenColumn: -1, frozenRow: -1 } as CurrentPinning; }); it('should return null when "enableCheckboxSelector" flag is disabled', () => { - const gridOptionsMock = { enableCheckboxSelector: false, enableRowSelection: false } as GridOption; + const gridOptionsMock = { enableCheckboxSelector: false, enableRowSelection: false, ...pinningMock } as GridOption; jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock); const output = service.getCurrentRowSelections(); @@ -368,7 +396,7 @@ describe('GridStateService', () => { it('should call "getCurrentGridState" method and return the Row Selection when either "enableCheckboxSelector" or "enableRowSelection" flag is enabled', () => { const selectedGridRows = [2]; const selectedRowIds = [99]; - const gridOptionsMock = { enableCheckboxSelector: true } as GridOption; + const gridOptionsMock = { enableCheckboxSelector: true, ...pinningMock } as GridOption; jest.spyOn(gridStub, 'getSelectedRows').mockReturnValue(selectedGridRows); jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock); const columnMock = [{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 }] as CurrentColumn[]; @@ -387,7 +415,7 @@ describe('GridStateService', () => { expect(filterSpy).toHaveBeenCalled(); expect(sorterSpy).toHaveBeenCalled(); expect(selectionSpy).toHaveBeenCalled(); - expect(output).toEqual({ columns: columnMock, filters: filterMock, sorters: sorterMock, rowSelection: selectionMock } as GridState); + expect(output).toEqual({ columns: columnMock, filters: filterMock, sorters: sorterMock, rowSelection: selectionMock, pinning: pinningMock, } as GridState); }); it('should call the "mapIdsToRows" from the DataView and get the data IDs from the "selectedRowDataContextIds" array', () => { @@ -408,13 +436,17 @@ describe('GridStateService', () => { }); describe('Row Selection - bindSlickGridRowSelectionToGridStateChange method', () => { + let pinningMock: CurrentPinning; + let gridOptionsMock: GridOption; + beforeEach(() => { jest.clearAllMocks(); }); describe('without Pagination', () => { beforeEach(() => { - const gridOptionsMock = { enablePagination: false, enableRowSelection: true } as GridOption; + pinningMock = { frozenBottom: false, frozenColumn: -1, frozenRow: -1 } as CurrentPinning; + gridOptionsMock = { enablePagination: false, enableRowSelection: true, ...pinningMock } as GridOption; jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock); }); @@ -446,6 +478,7 @@ describe('GridStateService', () => { columns: columnMock, filters: filterMock, sorters: sorterMock, + pinning: pinningMock, rowSelection: { gridRowIndexes: mockRowIndexes, dataContextIds: mockRowIds, filteredDataContextIds: mockRowIds }, }, }); @@ -455,11 +488,15 @@ describe('GridStateService', () => { }); describe('with Pagination (bindSlickGridRowSelectionWithPaginationToGridStateChange)', () => { + let pinningMock: CurrentPinning; + beforeEach(() => { jest.clearAllMocks(); service.dispose(); - const gridOptionsMock = { enablePagination: true, enableRowSelection: true } as GridOption; + pinningMock = { frozenBottom: false, frozenColumn: -1, frozenRow: -1 } as CurrentPinning; + const gridOptionsMock = { enablePagination: true, enableRowSelection: true, ...pinningMock } as GridOption; jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock); + jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock); }); it('should call the "onGridStateChanged" event with the row selection when Pagination is disabled and "onSelectedRowsChanged" is triggered', (done) => { @@ -497,6 +534,7 @@ describe('GridStateService', () => { columns: columnMock, filters: filterMock, sorters: sorterMock, + pinning: pinningMock, pagination: paginationMock, rowSelection: { gridRowIndexes: mockRowIndexes, dataContextIds: mockRowIds, filteredDataContextIds: mockRowIds }, }, @@ -572,6 +610,7 @@ describe('GridStateService', () => { columns: columnMock, filters: null, sorters: null, + pinning: pinningMock, pagination: paginationMock, rowSelection: { gridRowIndexes: shouldBeSelectedRowIndexes, dataContextIds: mockRowIds, filteredDataContextIds: mockRowIds }, }, @@ -671,6 +710,7 @@ describe('GridStateService', () => { columns: columnMock, filters: filterMock, sorters: sorterMock, + pinning: pinningMock, pagination: paginationMock, rowSelection: { gridRowIndexes: mockRowIndexes, dataContextIds: mockRowIds, filteredDataContextIds: mockRowIds }, } @@ -682,6 +722,7 @@ describe('GridStateService', () => { columns: columnMock, filters: filterMock, sorters: sorterMock, + pinning: pinningMock, pagination: paginationMock, rowSelection: { gridRowIndexes: mockRowIndexes, dataContextIds: mockRowIds, filteredDataContextIds: mockFilterSearchTerms }, }, @@ -739,7 +780,7 @@ describe('GridStateService', () => { }); it('should return null when no BackendService is used and FilterService is missing the "getCurrentLocalFilters" method', () => { - gridStub.getOptions = undefined; + gridStub.getOptions = undefined as any; const output = service.getCurrentFilters(); expect(output).toBeNull(); }); @@ -791,7 +832,7 @@ describe('GridStateService', () => { describe('needToPreserveRowSelection method', () => { it('should return false when there are no "dataView" property defined in the grid options', () => { - const gridOptionsMock = { dataView: null } as GridOption; + const gridOptionsMock = { dataView: null as any } as GridOption; jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock); const output = service.needToPreserveRowSelection(); @@ -800,7 +841,7 @@ describe('GridStateService', () => { }); it('should return false when "dataView" property is defined in the grid options with "syncGridSelection" property', () => { - const gridOptionsMock = { dataView: null } as GridOption; + const gridOptionsMock = { dataView: null as any } as GridOption; jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock); const output = service.needToPreserveRowSelection(); @@ -877,18 +918,18 @@ describe('GridStateService', () => { it(`should call the method with column definitions and expect "onGridStateChanged" to be triggered with "newValues" property being the columns and still empty "gridState" property`, () => { - const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[]; - const currentColumnsMock = [{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 }] as CurrentColumn[]; - const gridStateMock = { columns: [], filters: [], sorters: [] } as GridState; - const stateChangeMock = { change: { newValues: currentColumnsMock, type: GridStateType.columns }, gridState: gridStateMock } as GridStateChange; - const onChangeSpy = jest.spyOn(service.onGridStateChanged, 'next'); - const serviceSpy = jest.spyOn(service, 'getCurrentGridState').mockReturnValue(gridStateMock); + const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[]; + const currentColumnsMock = [{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 }] as CurrentColumn[]; + const gridStateMock = { columns: [], filters: [], sorters: [] } as GridState; + const stateChangeMock = { change: { newValues: currentColumnsMock, type: GridStateType.columns }, gridState: gridStateMock } as GridStateChange; + const onChangeSpy = jest.spyOn(service.onGridStateChanged, 'next'); + const serviceSpy = jest.spyOn(service, 'getCurrentGridState').mockReturnValue(gridStateMock); - service.resetColumns(columnsMock); + service.resetColumns(columnsMock); - expect(serviceSpy).toHaveBeenCalled(); - expect(onChangeSpy).toHaveBeenCalledWith(stateChangeMock); - }); + expect(serviceSpy).toHaveBeenCalled(); + expect(onChangeSpy).toHaveBeenCalledWith(stateChangeMock); + }); }); describe('resetToOriginalColumns method', () => { @@ -944,10 +985,13 @@ describe('GridStateService', () => { let currentColumnsMock: CurrentColumn[]; let filterMock: CurrentFilter[]; let sorterMock: CurrentSorter[]; + let pinningMock: CurrentPinning; beforeEach(() => { - const gridOptionsMock = { enablePagination: false, enableCheckboxSelector: false } as GridOption; + pinningMock = { frozenBottom: false, frozenColumn: -1, frozenRow: -1 } as CurrentPinning; + const gridOptionsMock = { enablePagination: false, enableCheckboxSelector: false, ...pinningMock } as GridOption; jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock); + jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock); columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[]; filterMock = [{ columnId: 'field1', operator: 'EQ', searchTerms: [] }, { columnId: 'field2', operator: '>=', searchTerms: [2] }] as CurrentFilter[]; @@ -959,7 +1003,7 @@ describe('GridStateService', () => { }); it('should trigger a "onGridStateChanged" event when "onFilterChanged" is triggered', () => { - const gridStateMock = { columns: currentColumnsMock, filters: filterMock, sorters: sorterMock } as GridState; + const gridStateMock = { columns: currentColumnsMock, filters: filterMock, sorters: sorterMock, pinning: pinningMock } as GridState; const stateChangeMock = { change: { newValues: filterMock, type: GridStateType.filter }, gridState: gridStateMock } as GridStateChange; const rxOnChangeSpy = jest.spyOn(service.onGridStateChanged, 'next'); @@ -968,7 +1012,7 @@ describe('GridStateService', () => { }); it('should trigger a "onGridStateChanged" event when "onFilterCleared" is triggered', () => { - const gridStateMock = { columns: currentColumnsMock, filters: filterMock, sorters: sorterMock } as GridState; + const gridStateMock = { columns: currentColumnsMock, filters: filterMock, sorters: sorterMock, pinning: pinningMock } as GridState; const stateChangeMock = { change: { newValues: [], type: GridStateType.filter }, gridState: gridStateMock } as GridStateChange; const rxOnChangeSpy = jest.spyOn(service.onGridStateChanged, 'next'); @@ -977,7 +1021,7 @@ describe('GridStateService', () => { }); it('should trigger a "onGridStateChanged" event when "onSortChanged" is triggered', () => { - const gridStateMock = { columns: currentColumnsMock, filters: filterMock, sorters: sorterMock } as GridState; + const gridStateMock = { columns: currentColumnsMock, filters: filterMock, sorters: sorterMock, pinning: pinningMock } as GridState; const stateChangeMock = { change: { newValues: sorterMock, type: GridStateType.sorter }, gridState: gridStateMock } as GridStateChange; const rxOnChangeSpy = jest.spyOn(service.onGridStateChanged, 'next'); @@ -986,7 +1030,7 @@ describe('GridStateService', () => { }); it('should trigger a "onGridStateChanged" event when "onSortCleared" is triggered', (done) => { - const gridStateMock = { columns: currentColumnsMock, filters: filterMock, sorters: sorterMock } as GridState; + const gridStateMock = { columns: currentColumnsMock, filters: filterMock, sorters: sorterMock, pinning: pinningMock } as GridState; const stateChangeMock = { change: { newValues: [], type: GridStateType.sorter }, gridState: gridStateMock } as GridStateChange; const rxOnChangeSpy = jest.spyOn(service.onGridStateChanged, 'next'); diff --git a/src/app/modules/angular-slickgrid/services/grid.service.ts b/src/app/modules/angular-slickgrid/services/grid.service.ts index d0bea2e71..014452766 100644 --- a/src/app/modules/angular-slickgrid/services/grid.service.ts +++ b/src/app/modules/angular-slickgrid/services/grid.service.ts @@ -4,6 +4,7 @@ import { Subject } from 'rxjs'; import { CellArgs, Column, + CurrentPinning, GridOption, GridServiceDeleteOption, GridServiceInsertOption, @@ -11,7 +12,6 @@ import { HideColumnOption, OnEventArgs } from './../models/index'; -import { ExtensionService } from './extension.service'; import { FilterService } from './filter.service'; import { GridStateService } from './gridState.service'; import { SharedService } from './shared.service'; @@ -39,7 +39,6 @@ export class GridService { onColumnsChanged = new Subject(); constructor( - private extensionService: ExtensionService, private filterService: FilterService, private gridStateService: GridStateService, private sharedService: SharedService, @@ -73,6 +72,32 @@ export class GridService { } } + /** Clear all the pinning (frozen) options */ + clearPinning() { + const visibleColumns = [...this.sharedService.visibleColumns]; + this.sharedService.grid.setOptions({ frozenColumn: -1, frozenRow: -1, frozenBottom: false, enableMouseWheelScrollHandler: false }); + + // SlickGrid seems to be somehow resetting the columns to their original positions, + // so let's re-fix them to the position we kept as reference + if (Array.isArray(visibleColumns)) { + this.sharedService.grid.setColumns(visibleColumns); + } + } + + /** + * Set pinning (frozen) grid options + * @param pinningOptions - which pinning/frozen options to modify + * @param shouldAutosizeColumns - defaults to True, should we call an autosizeColumns after the pinning is done? + */ + setPinning(pinningOptions: CurrentPinning, shouldAutosizeColumns = true) { + this.sharedService.grid.setOptions(pinningOptions); + this.sharedService.gridOptions = { ...this.sharedService.gridOptions, ...pinningOptions }; + + if (shouldAutosizeColumns) { + this.sharedService.grid.autosizeColumns(); + } + } + /** * Get all column set in the grid, that is all visible/hidden columns * and also include any extra columns used by some plugins (like Row Selection, Row Detail, ...) @@ -366,7 +391,7 @@ export class GridService { resetGrid(columnDefinitions?: Column[]) { // reset columns to original states & refresh the grid if (this._grid && this._dataView) { - const originalColumns = this.extensionService.getAllColumns(); + const originalColumns = this.sharedService.allColumns || []; if (Array.isArray(originalColumns) && originalColumns.length > 0) { // set the grid columns to it's original column definitions @@ -378,6 +403,9 @@ export class GridService { } } + // clear any Pinning/Frozen columns/rows + this.clearPinning(); + if (this.filterService && this.filterService.clearFilters) { this.filterService.clearFilters(); } diff --git a/src/app/modules/angular-slickgrid/services/gridState.service.ts b/src/app/modules/angular-slickgrid/services/gridState.service.ts index a9aa92657..06e7ddc7b 100644 --- a/src/app/modules/angular-slickgrid/services/gridState.service.ts +++ b/src/app/modules/angular-slickgrid/services/gridState.service.ts @@ -96,10 +96,12 @@ export class GridStateService { * @return grid state */ getCurrentGridState(args?: { requestRefreshRowFilteredRow?: boolean }): GridState { + const { frozenColumn, frozenRow, frozenBottom } = this.sharedService.gridOptions; const gridState: GridState = { columns: this.getCurrentColumns(), filters: this.getCurrentFilters(), sorters: this.getCurrentSorters(), + pinning: { frozenColumn, frozenRow, frozenBottom }, }; const currentPagination = this.getCurrentPagination(); @@ -394,6 +396,7 @@ export class GridStateService { // subscribe to Column Resize & Reordering this.bindSlickGridColumnChangeEventToGridStateChange('onColumnsReordered', grid); this.bindSlickGridColumnChangeEventToGridStateChange('onColumnsResized', grid); + this.bindSlickGridOnSetOptionsEventToGridStateChange(grid); // subscribe to Row Selection changes (when enabled) if (this._gridOptions.enableRowSelection || this._gridOptions.enableCheckboxSelector) { @@ -433,8 +436,8 @@ export class GridStateService { /** * Bind a Grid Event (of Column changes) to a Grid State change event - * @param event name - * @param grid + * @param event - event name + * @param grid - SlickGrid object */ private bindSlickGridColumnChangeEventToGridStateChange(eventName: string, grid: any) { const slickGridEvent = grid && grid[eventName]; @@ -448,6 +451,23 @@ export class GridStateService { } } + /** + * Bind a Grid Event (of grid option changes) to a Grid State change event, if we detect that any of the pinning (frozen) options changes then we'll trigger a Grid State change + * @param grid - SlickGrid object + */ + private bindSlickGridOnSetOptionsEventToGridStateChange(grid: any) { + this._eventHandler.subscribe(grid.onSetOptions, (_e: Event, args: any) => { + const { frozenBottom: frozenBottomBefore, frozenColumn: frozenColumnBefore, frozenRow: frozenRowBefore } = args.optionsBefore; + const { frozenBottom: frozenBottomAfter, frozenColumn: frozenColumnAfter, frozenRow: frozenRowAfter } = args.optionsAfter; + + if ((frozenBottomBefore !== frozenBottomAfter) || (frozenColumnBefore !== frozenColumnAfter) || (frozenRowBefore !== frozenRowAfter)) { + const newValues = { frozenBottom: frozenBottomAfter, frozenColumn: frozenColumnAfter, frozenRow: frozenRowAfter }; + const currentGridState = this.getCurrentGridState(); + this.onGridStateChanged.next({ change: { newValues, type: GridStateType.pinning }, gridState: currentGridState }); + } + }); + } + /** * Bind a Grid Event of Row Selection change to a Grid State change event * For the row selection, we can't just use the getSelectedRows() since this will only return the visible rows shown in the UI which is not enough. diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index d20391890..612076e41 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -4,7 +4,7 @@ "CLEAR_ALL_FILTERS": "Clear all Filters", "CLEAR_ALL_GROUPING": "Clear all Grouping", "CLEAR_ALL_SORTING": "Clear all Sorting", - "CLEAR_FROZEN_COLUMNS": "Clear Frozen Columns", + "CLEAR_PINNING": "Unfreeze Columns/Rows", "COLLAPSE_ALL_GROUPS": "Collapse all Groups", "COLUMNS": "Columns", "COMMANDS": "Commands", diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 4cc76e998..c4136ee86 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -4,7 +4,7 @@ "CLEAR_ALL_FILTERS": "Supprimer tous les filtres", "CLEAR_ALL_GROUPING": "Supprimer tous les groupes", "CLEAR_ALL_SORTING": "Supprimer tous les tris", - "CLEAR_FROZEN_COLUMNS": "Libérer les colonnes gelées", + "CLEAR_PINNING": "Dégeler les colonnes/rangées", "COLLAPSE_ALL_GROUPS": "Réduire tous les groupes", "COLUMNS": "Colonnes", "COMMANDS": "Commandes", diff --git a/test/cypress/integration/example15.spec.js b/test/cypress/integration/example15.spec.js index c9e2cc083..5709e0532 100644 --- a/test/cypress/integration/example15.spec.js +++ b/test/cypress/integration/example15.spec.js @@ -5,7 +5,7 @@ function removeExtraSpaces(textS) { } describe('Example 15 - Column Span & Header Grouping', () => { - // NOTE: everywhere there's a * 2 is because we have a top+bottom (frozen rows) containers even after clear frozen columns + // NOTE: everywhere there's a * 2 is because we have a top+bottom (frozen rows) containers even after Unfreeze Columns/Rows const fullPreTitles = ['', 'Common Factor', 'Period', 'Analysis']; const fullTitles = ['#', 'Title', 'Duration', 'Start', 'Finish', '% Complete', 'Effort Driven']; @@ -105,12 +105,12 @@ describe('Example 15 - Column Span & Header Grouping', () => { .each(($child, index) => expect($child.text()).to.eq(fullTitles[index])); }); - it('should click on the Grid Menu command "Clear Frozen Columns" to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => { + it('should click on the Grid Menu command "Unfreeze Columns/Rows" to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => { cy.get('#grid2') .find('button.slick-gridmenu-button') .click({ force: true }); - cy.contains('Clear Frozen Columns') + cy.contains('Unfreeze Columns/Rows') .click({ force: true }); cy.get('#grid2').find('[style="top:0px"]').should('have.length', 1); diff --git a/test/cypress/integration/example16.spec.js b/test/cypress/integration/example16.spec.js index 4942bcc46..794365688 100644 --- a/test/cypress/integration/example16.spec.js +++ b/test/cypress/integration/example16.spec.js @@ -178,7 +178,7 @@ describe('Example 16: Grid State & Presets using Local Storage', () => { cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(2)') + .children('.slick-header-menuitem:nth-child(4)') .children('.slick-header-menucontent') .should('contain', 'Sort Descending') .click(); @@ -405,7 +405,7 @@ describe('Example 16: Grid State & Presets using Local Storage', () => { cy.get('.slick-header-menu') .should('be.visible') - .children('.slick-header-menuitem:nth-child(6)') + .children('.slick-header-menuitem:nth-child(8)') .children('.slick-header-menucontent') .should('contain', 'Cacher la colonne') .click(); @@ -416,6 +416,23 @@ describe('Example 16: Grid State & Presets using Local Storage', () => { .each(($child, index) => expect($child.find('.slick-column-name').text()).to.eq(expectedTitles[index])); }); + it('should be able to freeze "Description" column', () => { + cy.get('.slick-header-columns') + .children('.slick-header-column:nth(1)') + .trigger('mouseover') + .children('.slick-header-menubutton') + .should('be.hidden') + .invoke('show') + .click(); + + cy.get('.slick-header-menu') + .should('be.visible') + .children('.slick-header-menuitem:nth-child(1)') + .children('.slick-header-menucontent') + .should('contain', 'Geler les colonnes') + .click(); + }); + it('should reload the page', () => { cy.reload().wait(50); }); @@ -479,4 +496,10 @@ describe('Example 16: Grid State & Presets using Local Storage', () => { expect($child.attr('class')).to.contain('selected'); }); }); + + it('should have a persisted frozen column after "Description" and a grid with 4 containers on page load with 2 columns on the left and 3 columns on the right', () => { + cy.get('[style="top:0px"]').should('have.length', 2); + cy.get('.grid-canvas-left > [style="top:0px"]').children().should('have.length', 2); + cy.get('.grid-canvas-right > [style="top:0px"]').children().should('have.length', 3); + }); }); diff --git a/test/cypress/integration/example20.spec.js b/test/cypress/integration/example20.spec.js index 61c9b90d2..28de8498f 100644 --- a/test/cypress/integration/example20.spec.js +++ b/test/cypress/integration/example20.spec.js @@ -5,7 +5,7 @@ function removeExtraSpaces(textS) { } describe('Example 20 - Frozen Grid', () => { - // NOTE: everywhere there's a * 2 is because we have a top+bottom (frozen rows) containers even after clear frozen columns + // NOTE: everywhere there's a * 2 is because we have a top+bottom (frozen rows) containers even after Unfreeze Columns/Rows const fullTitles = ['#', 'Title', '% Complete', 'Start', 'Finish', 'Cost | Duration', 'Effort Driven', 'Title 1', 'Title 2', 'Title 3', 'Title 4']; @@ -207,16 +207,16 @@ describe('Example 20 - Frozen Grid', () => { .each(($child, index) => expect($child.text()).to.eq(fullTitles[index])); }); - it('should click on the Grid Menu command "Clear Frozen Columns" to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => { + it('should click on the Grid Menu command "Unfreeze Columns/Rows" to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => { cy.get('#grid20') .find('button.slick-gridmenu-button') .click({ force: true }); - cy.contains('Clear Frozen Columns') + cy.contains('Unfreeze Columns/Rows') .click({ force: true }); - cy.get('[style="top:0px"]').should('have.length', 1 * 2); - cy.get('.grid-canvas-left > [style="top:0px"]').children().should('have.length', 11 * 2); + cy.get('[style="top:0px"]').should('have.length', 1); + cy.get('.grid-canvas-left > [style="top:0px"]').children().should('have.length', 11); cy.get('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(0)').should('contain', '0'); cy.get('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(1)').should('contain', 'Task 0'); diff --git a/test/translateServiceStub.ts b/test/translateServiceStub.ts index 553cdb9f5..bb7ac75d8 100644 --- a/test/translateServiceStub.ts +++ b/test/translateServiceStub.ts @@ -16,7 +16,7 @@ export class TranslateServiceStub { case 'CLEAR_ALL_GROUPING': output = this.currentLang === 'en' ? 'Clear all Grouping' : 'Supprimer tous les groupes'; break; case 'CLEAR_ALL_FILTERS': output = this.currentLang === 'en' ? 'Clear all Filters' : 'Supprimer tous les filtres'; break; case 'CLEAR_ALL_SORTING': output = this.currentLang === 'en' ? 'Clear all Sorting' : 'Supprimer tous les tris'; break; - case 'CLEAR_FROZEN_COLUMNS': output = this.currentLang === 'en' ? 'Clear Frozen Columns' : 'Libérer les colonnes gelées'; break; + case 'CLEAR_PINNING': output = this.currentLang === 'en' ? 'Unfreeze Columns/Rows' : 'Dégeler les colonnes/rangées'; break; case 'COLUMNS': output = this.currentLang === 'en' ? 'Columns' : 'Colonnes'; break; case 'COMMANDS': output = this.currentLang === 'en' ? 'Commands' : 'Commandes'; break; case 'COLLAPSE_ALL_GROUPS': output = this.currentLang === 'en' ? 'Collapse all Groups' : 'Réduire tous les groupes'; break;