diff --git a/package.json b/package.json
index 0c58c30f4..997c8bf55 100644
--- a/package.json
+++ b/package.json
@@ -101,7 +101,7 @@
"lodash.isequal": "^4.5.0",
"moment-mini": "^2.22.1",
"rxjs": "^6.3.3",
- "slickgrid": "^2.4.24",
+ "slickgrid": "^2.4.25",
"text-encoding-utf-8": "^1.0.2"
},
"devDependencies": {
diff --git a/src/app/examples/grid-colspan.component.html b/src/app/examples/grid-colspan.component.html
index e57c4519d..47842fa49 100644
--- a/src/app/examples/grid-colspan.component.html
+++ b/src/app/examples/grid-colspan.component.html
@@ -14,11 +14,20 @@
Grid 1 (with Header Grouping & Colspan)
Grid 2 (with Header Grouping & Frozen/Pinned Columns)
+
+
+ Remove Frozen Columns
+
+
+ Set 3 Frozen Columns
+
+
+ gridWidth="800"
+ (onAngularGridCreated)="angularGridReady2($event)">
diff --git a/src/app/examples/grid-colspan.component.ts b/src/app/examples/grid-colspan.component.ts
index bc6c14203..b145b6270 100644
--- a/src/app/examples/grid-colspan.component.ts
+++ b/src/app/examples/grid-colspan.component.ts
@@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
-import { Column, FieldType, GridOption } from './../modules/angular-slickgrid';
+import { AngularGridInstance, Column, FieldType, GridOption } from './../modules/angular-slickgrid';
@Component({
templateUrl: './grid-colspan.component.html',
@@ -15,9 +15,15 @@ export class GridColspanComponent implements OnInit {
Header Grouping - (Wiki docs )
Note that you can add Sort but remember that it will sort by the data which the row contains, even if the data is visually hidden by colspan it will still sort it
+
+ Header Grouping spanning accross multiple columns is working but has some UI issues on window resize.
+ If anyone can fix it, probably some CSS issues, please let us know.
+
`;
+ angularGrid2: AngularGridInstance;
+ gridObj2: any;
columnDefinitions1: Column[];
columnDefinitions2: Column[];
gridOptions1: GridOption;
@@ -30,6 +36,11 @@ export class GridColspanComponent implements OnInit {
this.prepareGrid2();
}
+ angularGridReady2(angularGrid: AngularGridInstance) {
+ this.angularGrid2 = angularGrid;
+ this.gridObj2 = angularGrid.slickGrid;
+ }
+
prepareGrid1() {
this.columnDefinitions1 = [
{ id: 'title', name: 'Title', field: 'title', sortable: true, columnGroup: 'Common Factor' },
@@ -49,7 +60,7 @@ export class GridColspanComponent implements OnInit {
showPreHeaderPanel: true,
preHeaderPanelHeight: 28,
explicitInitialization: true,
- colspanCallback: this.renderDifferentColspan
+ colspanCallback: this.renderDifferentColspan,
};
this.dataset1 = this.getData(500);
@@ -57,6 +68,7 @@ export class GridColspanComponent implements OnInit {
prepareGrid2() {
this.columnDefinitions2 = [
+ { id: 'sel', name: '#', field: 'num', behavior: 'select', cssClass: 'cell-selection', width: 40, resizable: false, selectable: false },
{ id: 'title', name: 'Title', field: 'title', sortable: true, columnGroup: 'Common Factor' },
{ id: 'duration', name: 'Duration', field: 'duration', columnGroup: 'Common Factor' },
{ id: 'start', name: 'Start', field: 'start', columnGroup: 'Period' },
@@ -73,7 +85,9 @@ export class GridColspanComponent implements OnInit {
showPreHeaderPanel: true,
preHeaderPanelHeight: 25,
explicitInitialization: true,
- frozenColumn: 1,
+ frozenColumn: 2,
+ gridMenu: { hideClearFrozenColumnsCommand: false },
+ headerMenu: { hideFreezeColumnsCommand: false }
};
this.dataset2 = this.getData(500);
@@ -97,6 +111,11 @@ export class GridColspanComponent implements OnInit {
return mockDataset;
}
+ setFrozenColumns2(frozenCols: number) {
+ this.gridObj2.setOptions({ frozenColumn: frozenCols, alwaysShowVerticalScroll: false });
+ this.gridOptions2 = this.gridObj2.getOptions();
+ }
+
/**
* A callback to render different row column span
* Your callback will always have the "item" argument which you can use to decide on the colspan
diff --git a/src/app/examples/grid-frozen.component.html b/src/app/examples/grid-frozen.component.html
index 83b8d3224..0c8bd8370 100644
--- a/src/app/examples/grid-frozen.component.html
+++ b/src/app/examples/grid-frozen.component.html
@@ -19,6 +19,16 @@ {{title}}
Set
+
+
+
+
+ Remove Frozen Columns
+
+
+ Set 3 Frozen Columns
+
+
Toggle Pinned Rows
diff --git a/src/app/examples/grid-frozen.component.ts b/src/app/examples/grid-frozen.component.ts
index 8d4c98136..7d763d96b 100644
--- a/src/app/examples/grid-frozen.component.ts
+++ b/src/app/examples/grid-frozen.component.ts
@@ -77,14 +77,14 @@ export class GridFrozenComponent implements OnInit {
{
id: 'start', name: 'Start', field: 'start',
minWidth: 100, width: 120,
- filterable: true,
- sortable: true
+ filterable: true, sortable: true,
+ formatter: Formatters.dateIso
},
{
id: 'finish', name: 'Finish', field: 'finish',
minWidth: 100, width: 120,
- filterable: true,
- sortable: true
+ filterable: true, sortable: true,
+ formatter: Formatters.dateIso
},
{
id: 'cost', name: 'Cost | Duration', field: 'cost',
@@ -170,25 +170,25 @@ export class GridFrozenComponent implements OnInit {
sortable: true
},
{
- id: 'title1', name: 'Title1', field: 'title1',
+ id: 'title1', name: 'Title 1', field: 'title1',
minWidth: 100, width: 120,
filterable: true,
sortable: true
},
{
- id: 'title2', name: 'Title2', field: 'title2',
+ id: 'title2', name: 'Title 2', field: 'title2',
minWidth: 100, width: 120,
filterable: true,
sortable: true
},
{
- id: 'title3', name: 'Title3', field: 'title3',
+ id: 'title3', name: 'Title 3', field: 'title3',
minWidth: 100, width: 120,
filterable: true,
sortable: true
},
{
- id: 'title4', name: 'Title4', field: 'title4',
+ id: 'title4', name: 'Title 4', field: 'title4',
minWidth: 100, width: 120,
filterable: true,
sortable: true
@@ -209,6 +209,9 @@ export class GridFrozenComponent implements OnInit {
frozenColumn: this.frozenColumnCount,
frozenRow: this.frozenRowCount,
// frozenBottom: true, // if you want to freeze the bottom instead of the top, you can enable this property
+
+ gridMenu: { hideClearFrozenColumnsCommand: false },
+ headerMenu: { hideFreezeColumnsCommand: false }
};
// mock a dataset
@@ -219,18 +222,14 @@ export class GridFrozenComponent implements OnInit {
// Set up some test columns.
const mockDataset = [];
for (let i = 0; i < 500; i++) {
- const randomYear = 2000 + Math.floor(Math.random() * 10);
- const randomMonth = Math.floor(Math.random() * 11);
- const randomDay = Math.floor((Math.random() * 29));
-
mockDataset[i] = {
id: i,
title: 'Task ' + i,
cost: (i % 33 === 0) ? null : Math.random() * 10000,
duration: i % 8 ? (Math.round(Math.random() * 100) + '') : null,
percentComplete: Math.round(Math.random() * 100),
- start: new Date(randomYear, randomMonth, randomDay),
- finish: new Date(randomYear, (randomMonth + 1), randomDay),
+ start: new Date(2009, 0, 1),
+ finish: new Date(2009, 4, 5),
effortDriven: (i % 5 === 0),
title1: `Some Text ${Math.round(Math.random() * 25)}`,
title2: `Some Text ${Math.round(Math.random() * 25)}`,
@@ -276,6 +275,11 @@ export class GridFrozenComponent implements OnInit {
alert(args.validationResults.msg);
}
+ setFrozenColumns(frozenCols: number) {
+ this.gridObj.setOptions({ frozenColumn: frozenCols, alwaysShowVerticalScroll: false });
+ this.gridOptions = this.gridObj.getOptions();
+ }
+
/** toggle dynamically, through slickgrid "setOptions()" the top/bottom pinned location */
toggleFrozenBottomRows() {
if (this.gridObj && this.gridObj.setOptions) {
diff --git a/src/app/modules/angular-slickgrid/constants.ts b/src/app/modules/angular-slickgrid/constants.ts
index 598818b99..2a5356075 100644
--- a/src/app/modules/angular-slickgrid/constants.ts
+++ b/src/app/modules/angular-slickgrid/constants.ts
@@ -8,6 +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_COLLAPSE_ALL_GROUPS: 'Collapse all Groups',
TEXT_CONTAINS: 'Contains',
TEXT_COLUMNS: 'Columns',
@@ -20,6 +21,7 @@ export class Constants {
TEXT_EXPORT_TO_TEXT_FORMAT: 'Export in Text format (Tab delimited)',
TEXT_EXPORT_TO_EXCEL: 'Export to Excel',
TEXT_FORCE_FIT_COLUMNS: 'Force fit columns',
+ TEXT_FREEZE_COLUMNS: 'Freeze Columns',
TEXT_GROUP_BY: 'Group By',
TEXT_HIDE_COLUMN: 'Hide Column',
TEXT_ITEMS: 'items',
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 6f9891e42..945f919bb 100644
--- a/src/app/modules/angular-slickgrid/extensions/__tests__/gridMenuExtension.spec.ts
+++ b/src/app/modules/angular-slickgrid/extensions/__tests__/gridMenuExtension.spec.ts
@@ -42,6 +42,7 @@ const gridStub = {
getUID: () => gridUid,
registerPlugin: jest.fn(),
setColumns: jest.fn(),
+ setOptions: jest.fn(),
setHeaderRowVisibility: jest.fn(),
setTopPanelVisibility: jest.fn(),
setPreHeaderPanelVisibility: jest.fn(),
@@ -94,6 +95,7 @@ describe('gridMenuExtension', () => {
gridMenu: {
customItems: [],
hideClearAllFiltersCommand: false,
+ hideClearFrozenColumnsCommand: true,
hideForceFitButton: false,
hideSyncResizeButton: true,
onExtensionRegistered: jest.fn(),
@@ -136,6 +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',
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)',
@@ -363,6 +366,16 @@ describe('gridMenuExtension', () => {
expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([]);
});
+ it('should expect menu related to "Clear Frozen Columns"', () => {
+ 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 },
+ ]);
+ });
+
it('should expect all menu related to Filter when "enableFilering" is set', () => {
const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, showHeaderRow: true, } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
@@ -376,7 +389,7 @@ describe('gridMenuExtension', () => {
});
it('should have only 1 menu "clear-filter" when all other menus are defined as hidden & when "enableFilering" is set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, showHeaderRow: true, gridMenu: { hideToggleFilterCommand: true, hideRefreshDatasetCommand: true } } as unknown as GridOption;
+ const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, showHeaderRow: true, gridMenu: { hideClearFrozenColumnsCommand: true, hideToggleFilterCommand: true, hideRefreshDatasetCommand: true } } 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
@@ -386,7 +399,7 @@ describe('gridMenuExtension', () => {
});
it('should have only 1 menu "toggle-filter" when all other menus are defined as hidden & when "enableFilering" is set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, showHeaderRow: true, gridMenu: { hideClearAllFiltersCommand: true, hideRefreshDatasetCommand: true } } as unknown as GridOption;
+ const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, showHeaderRow: true, gridMenu: { hideClearFrozenColumnsCommand: true, hideClearAllFiltersCommand: true, hideRefreshDatasetCommand: true } } 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
@@ -396,7 +409,7 @@ describe('gridMenuExtension', () => {
});
it('should have only 1 menu "refresh-dataset" when all other menus are defined as hidden & when "enableFilering" is set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, showHeaderRow: true, gridMenu: { hideClearAllFiltersCommand: true, hideToggleFilterCommand: true } } as unknown as GridOption;
+ const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, showHeaderRow: true, gridMenu: { hideClearFrozenColumnsCommand: true, hideClearAllFiltersCommand: true, hideToggleFilterCommand: true } } 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
@@ -416,7 +429,7 @@ describe('gridMenuExtension', () => {
});
it('should not have the "toggle-preheader" menu command when "showPreHeaderPanel" and "hideTogglePreHeaderCommand" are set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, showPreHeaderPanel: true, gridMenu: { hideTogglePreHeaderCommand: true } } as unknown as GridOption;
+ 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([]);
@@ -433,14 +446,14 @@ describe('gridMenuExtension', () => {
});
it('should not have the "clear-sorting" menu command when "enableSorting" and "hideClearAllSortingCommand" are set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true, gridMenu: { hideClearAllSortingCommand: true } } as unknown as GridOption;
+ 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([]);
});
it('should have the "export-csv" menu command when "enableExport" is set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportExcelCommand: true, hideExportTextDelimitedCommand: true } } as unknown as GridOption;
+ const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideClearFrozenColumnsCommand: true, hideExportExcelCommand: true, hideExportTextDelimitedCommand: true } } 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
@@ -450,14 +463,14 @@ describe('gridMenuExtension', () => {
});
it('should not have the "export-csv" menu command when "enableExport" and "hideExportCsvCommand" are set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportExcelCommand: true, hideExportCsvCommand: true, hideExportTextDelimitedCommand: true } } as unknown as GridOption;
+ 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([]);
});
it('should have the "export-excel" menu command when "enableExport" is set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableExcelExport: true, enableExport: false, gridMenu: { hideExportCsvCommand: true, hideExportExcelCommand: false } } as unknown as GridOption;
+ const copyGridOptionsMock = { ...gridOptionsMock, enableExcelExport: true, enableExport: false, gridMenu: { hideClearFrozenColumnsCommand: true, hideExportCsvCommand: true, hideExportExcelCommand: 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
@@ -467,7 +480,7 @@ describe('gridMenuExtension', () => {
});
it('should have the "export-text-delimited" menu command when "enableExport" is set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportCsvCommand: true, hideExportExcelCommand: true } } as unknown as GridOption;
+ const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideClearFrozenColumnsCommand: true, hideExportCsvCommand: true, hideExportExcelCommand: true } } 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
@@ -477,7 +490,7 @@ describe('gridMenuExtension', () => {
});
it('should not have the "export-text-delimited" menu command when "enableExport" and "hideExportCsvCommand" are set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportExcelCommand: true, hideExportCsvCommand: true, hideExportTextDelimitedCommand: true } } as unknown as GridOption;
+ 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([]);
@@ -499,6 +512,7 @@ describe('gridMenuExtension', () => {
enableExport: true,
gridMenu: {
customItems: customItemsMock,
+ hideClearFrozenColumnsCommand: true,
hideExportCsvCommand: false,
hideExportExcelCommand: false,
hideExportTextDelimitedCommand: true,
@@ -581,6 +595,21 @@ describe('gridMenuExtension', () => {
mockGridMenuAddon.onCommand = new Slick.Event();
});
+ it('should call "clearFrozenColumns" when the command triggered is "clear-frozen-columns"', () => {
+ const setOptionsSpy = jest.spyOn(gridStub, 'setOptions');
+ const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.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);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(setColumnsSpy).toHaveBeenCalled();
+ expect(setOptionsSpy).toHaveBeenCalledWith({ frozenColumn: -1 });
+ });
+
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');
diff --git a/src/app/modules/angular-slickgrid/extensions/__tests__/headerMenuExtension.spec.ts b/src/app/modules/angular-slickgrid/extensions/__tests__/headerMenuExtension.spec.ts
index f200c516c..b3d139fd4 100644
--- a/src/app/modules/angular-slickgrid/extensions/__tests__/headerMenuExtension.spec.ts
+++ b/src/app/modules/angular-slickgrid/extensions/__tests__/headerMenuExtension.spec.ts
@@ -33,6 +33,7 @@ const gridStub = {
getOptions: jest.fn(),
registerPlugin: jest.fn(),
setColumns: jest.fn(),
+ setOptions: jest.fn(),
setHeaderRowVisibility: jest.fn(),
setTopPanelVisibility: jest.fn(),
setPreHeaderPanelVisibility: jest.fn(),
@@ -73,6 +74,7 @@ describe('headerMenuExtension', () => {
postProcess: jest.fn(),
},
headerMenu: {
+ hideFreezeColumnsCommand: false,
hideForceFitButton: false,
hideSyncResizeButton: true,
onExtensionRegistered: jest.fn(),
@@ -109,6 +111,7 @@ describe('headerMenuExtension', () => {
COMMANDS: 'Commandes',
COLUMNS: 'Colonnes',
FORCE_FIT_COLUMNS: 'Ajustement forcé des colonnes',
+ FREEZE_COLUMNS: 'Geler les colonnes',
SYNCHRONOUS_RESIZE: 'Redimension synchrone',
HIDE_COLUMN: 'Cacher la colonne',
REMOVE_FILTER: 'Supprimer le filtre',
@@ -121,6 +124,7 @@ describe('headerMenuExtension', () => {
COMMANDS: 'Commands',
COLUMNS: 'Columns',
FORCE_FIT_COLUMNS: 'Force fit columns',
+ FREEZE_COLUMNS: 'Freeze Columns',
SYNCHRONOUS_RESIZE: 'Synchronous resize',
HIDE_COLUMN: 'Hide Column',
REMOVE_FILTER: 'Remove Filter',
@@ -164,6 +168,7 @@ describe('headerMenuExtension', () => {
minWidth: 140,
hideColumnHideCommand: false,
hideForceFitButton: false,
+ hideFreezeColumnsCommand: false,
hideSyncResizeButton: true,
hideSortCommands: false,
title: '',
@@ -254,8 +259,21 @@ describe('headerMenuExtension', () => {
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
});
+ it('should have the commands "frozen-columns" and "hide" in the header menu list', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, headerMenu: { hideFreezeColumnsCommand: false } } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ extension.register();
+
+ expect(mockColumn.header.menu.items).not.toBeNull();
+ expect(mockColumn.header.menu.items).toEqual([
+ { iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 48 },
+ { divider: true, command: '', positionOrder: 49 },
+ { iconCssClass: 'fa fa-times', title: 'Cacher la colonne', command: 'hide', positionOrder: 55 }
+ ]);
+ });
+
it('should have the command "hide-column" in the header menu list', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, headerMenu: { hideColumnHideCommand: false } } as unknown as GridOption;
+ const copyGridOptionsMock = { ...gridOptionsMock, headerMenu: { hideFreezeColumnsCommand: true, hideColumnHideCommand: false } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();
@@ -266,7 +284,7 @@ describe('headerMenuExtension', () => {
});
it('should expect all menu related to Sorting when "enableSorting" is set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true, headerMenu: { hideColumnHideCommand: true } } as unknown as GridOption;
+ const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true, headerMenu: { hideFreezeColumnsCommand: true, hideColumnHideCommand: true } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();
@@ -295,12 +313,14 @@ describe('headerMenuExtension', () => {
expect(mockColumn.header.menu.items).not.toBeNull();
expect(mockColumn.header.menu.items).toEqual([
+ { iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 48 },
+ { divider: true, command: '', positionOrder: 49 },
{ iconCssClass: 'fa fa-times', title: 'Cacher la colonne', command: 'hide', positionOrder: 55 }
]);
});
it('should expect all menu related to Filtering when "enableFiltering" is set', () => {
- const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, headerMenu: { hideColumnHideCommand: true } } as unknown as GridOption;
+ const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true, headerMenu: { hideFreezeColumnsCommand: true, hideColumnHideCommand: true } } as unknown as GridOption;
jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
extension.register();
@@ -324,7 +344,15 @@ describe('headerMenuExtension', () => {
const visibleSpy = jest.spyOn(SharedService.prototype, 'visibleColumns', 'set');
const updatedColumnsMock = [{
id: 'field1', field: 'field1', nameKey: 'TITLE', width: 100,
- header: { menu: { items: [{ command: 'hide', iconCssClass: 'fa fa-times', positionOrder: 55, title: 'Cacher la colonne' }] } }
+ header: {
+ menu: {
+ items: [
+ { iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 48 },
+ { divider: true, command: '', positionOrder: 49 },
+ { command: 'hide', iconCssClass: 'fa fa-times', positionOrder: 55, title: 'Cacher la colonne' }
+ ]
+ }
+ }
}] as Column[];
extension.hideColumn(columnsMock[1]);
@@ -341,6 +369,8 @@ describe('headerMenuExtension', () => {
header: {
menu: {
items: [
+ { iconCssClass: 'fa fa-thumb-tack', title: 'Geler les colonnes', command: 'freeze-columns', positionOrder: 48 },
+ { divider: true, command: '', positionOrder: 49 },
{ iconCssClass: 'fa fa-sort-asc', title: 'Trier par ordre croissant', command: 'sort-asc', positionOrder: 50 },
{ iconCssClass: 'fa fa-sort-desc', title: 'Trier par ordre décroissant', command: 'sort-desc', positionOrder: 51 },
{ divider: true, command: '', positionOrder: 52 },
@@ -361,6 +391,8 @@ describe('headerMenuExtension', () => {
header: {
menu: {
items: [
+ { iconCssClass: 'fa fa-thumb-tack', title: 'Freeze Columns', command: 'freeze-columns', positionOrder: 48 },
+ { divider: true, command: '', positionOrder: 49 },
{ iconCssClass: 'fa fa-sort-asc', title: 'Sort Ascending', command: 'sort-asc', positionOrder: 50 },
{ iconCssClass: 'fa fa-sort-desc', title: 'Sort Descending', command: 'sort-desc', positionOrder: 51 },
{ divider: true, command: '', positionOrder: 52 },
@@ -375,6 +407,32 @@ describe('headerMenuExtension', () => {
});
describe('executeHeaderMenuInternalCommands method', () => {
+ it('should trigger the command "freeze-columns" and grid "setOptions" method to be called with current column position', () => {
+ const setOptionsSpy = jest.spyOn(gridStub, 'setOptions');
+ const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: columnsMock[0], grid: gridStub, command: 'freeze-columns' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(setOptionsSpy).toHaveBeenCalledWith({ frozenColumn: 0, alwaysShowVerticalScroll: false });
+ expect(setColumnsSpy).toHaveBeenCalled();
+ });
+
+ it('should trigger the command "freeze-columns" and grid "setOptions" method to be called with frozen column of -1 because the column found is not visible', () => {
+ const setOptionsSpy = jest.spyOn(gridStub, 'setOptions');
+ const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: columnsMock[1], grid: gridStub, command: 'freeze-columns' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(setOptionsSpy).toHaveBeenCalledWith({ frozenColumn: -1, alwaysShowVerticalScroll: false });
+ expect(setColumnsSpy).toHaveBeenCalled();
+ });
+
it('should trigger the command "hide" and expect the grid "autosizeColumns" method being called', () => {
const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
const autosizeSpy = jest.spyOn(SharedService.prototype.grid, 'autosizeColumns');
diff --git a/src/app/modules/angular-slickgrid/extensions/columnPickerExtension.ts b/src/app/modules/angular-slickgrid/extensions/columnPickerExtension.ts
index b15e78536..b15b4f1af 100644
--- a/src/app/modules/angular-slickgrid/extensions/columnPickerExtension.ts
+++ b/src/app/modules/angular-slickgrid/extensions/columnPickerExtension.ts
@@ -56,7 +56,7 @@ export class ColumnPickerExtension implements Extension {
if (this.sharedService.gridOptions.columnPicker && typeof this.sharedService.gridOptions.columnPicker.onColumnsChanged === 'function') {
this.sharedService.gridOptions.columnPicker.onColumnsChanged(e, args);
}
- if (args && Array.isArray(args.columns) && args.columns.length > this.sharedService.visibleColumns.length) {
+ if (args && Array.isArray(args.columns) && args.columns.length !== this.sharedService.visibleColumns.length) {
this.sharedService.visibleColumns = args.columns;
}
});
diff --git a/src/app/modules/angular-slickgrid/extensions/gridMenuExtension.ts b/src/app/modules/angular-slickgrid/extensions/gridMenuExtension.ts
index c40834ee4..a474a659b 100644
--- a/src/app/modules/angular-slickgrid/extensions/gridMenuExtension.ts
+++ b/src/app/modules/angular-slickgrid/extensions/gridMenuExtension.ts
@@ -201,6 +201,22 @@ export class GridMenuExtension implements Extension {
const gridOptions = this.sharedService.gridOptions;
const translationPrefix = getTranslationPrefix(gridOptions);
+ // show grid menu: Clear Frozen Columns
+ if (this.sharedService.gridOptions && this.sharedService.gridOptions.gridMenu && !this.sharedService.gridOptions.gridMenu.hideClearFrozenColumnsCommand) {
+ const commandName = 'clear-frozen-columns';
+ if (!originalCustomItems.find((item: GridMenuItem) => item.hasOwnProperty('command') && item.command === commandName)) {
+ gridMenuCustomItems.push(
+ {
+ iconCssClass: this.sharedService.gridOptions.gridMenu.iconClearFrozenColumnsCommand || 'fa fa-times',
+ title: this.sharedService.gridOptions.enableTranslate ? this.translate.instant(`${translationPrefix}CLEAR_FROZEN_COLUMNS`) : this._locales && this._locales.TEXT_CLEAR_FROZEN_COLUMNS,
+ disabled: false,
+ command: commandName,
+ positionOrder: 49
+ }
+ );
+ }
+ }
+
if (this.sharedService.gridOptions && (this.sharedService.gridOptions.enableFiltering && !this.sharedService.hideHeaderRowAfterPageLoad)) {
// show grid menu: Clear all Filters
if (this.sharedService.gridOptions && this.sharedService.gridOptions.gridMenu && !this.sharedService.gridOptions.gridMenu.hideClearAllFiltersCommand) {
@@ -352,6 +368,13 @@ export class GridMenuExtension implements Extension {
private executeGridMenuInternalCustomCommands(e: Event, args: GridMenuItem) {
if (args && args.command) {
switch (args.command) {
+ case 'clear-frozen-columns':
+ const visibleColumns = [...this.sharedService.visibleColumns];
+ this.sharedService.grid.setOptions({ frozenColumn: -1 });
+ if (Array.isArray(visibleColumns) && Array.isArray(this.sharedService.allColumns) && visibleColumns.length !== this.sharedService.allColumns.length) {
+ this.sharedService.grid.setColumns(visibleColumns);
+ }
+ break;
case 'clear-filter':
this.filterService.clearFilters();
this.sharedService.dataView.refresh();
diff --git a/src/app/modules/angular-slickgrid/extensions/headerMenuExtension.ts b/src/app/modules/angular-slickgrid/extensions/headerMenuExtension.ts
index 2c87f472a..e2ee439ab 100644
--- a/src/app/modules/angular-slickgrid/extensions/headerMenuExtension.ts
+++ b/src/app/modules/angular-slickgrid/extensions/headerMenuExtension.ts
@@ -133,6 +133,23 @@ export class HeaderMenuExtension implements Extension {
const columnHeaderMenuItems: Array = columnDef && columnDef.header && columnDef.header.menu && columnDef.header.menu.items || [];
+ // Freeze Column (pinning)
+ if (headerMenuOptions && !headerMenuOptions.hideFreezeColumnsCommand) {
+ if (columnHeaderMenuItems.filter((item: MenuCommandItem) => item.hasOwnProperty('command') && item.command === 'freeze-columns').length === 0) {
+ columnHeaderMenuItems.push({
+ iconCssClass: headerMenuOptions.iconFreezeColumns || 'fa fa-thumb-tack',
+ title: options.enableTranslate ? this.translate.instant(`${translationPrefix}FREEZE_COLUMNS`) : this._locales && this._locales.TEXT_FREEZE_COLUMNS,
+ command: 'freeze-columns',
+ positionOrder: 48
+ });
+ }
+
+ // add a divider (separator) between the top freeze columns commands and the rest of the commands
+ if (columnHeaderMenuItems.filter((item: MenuCommandItem) => item.positionOrder === 49).length === 0) {
+ columnHeaderMenuItems.push({ divider: true, command: '', positionOrder: 49 });
+ }
+ }
+
// Sorting Commands
if (options.enableSorting && columnDef.sortable && headerMenuOptions && !headerMenuOptions.hideSortCommands) {
if (columnHeaderMenuItems.filter((item: MenuCommandItem) => item.hasOwnProperty('command') && item.command === 'sort-asc').length === 0) {
@@ -254,6 +271,9 @@ export class HeaderMenuExtension implements Extension {
case 'clear-sort':
item.title = this.translate.instant(`${translationPrefix}REMOVE_SORT`) || this._locales && this._locales.TEXT_REMOVE_SORT;
break;
+ case 'freeze-columns':
+ item.title = this.translate.instant(`${translationPrefix}FREEZE_COLUMNS`) || this._locales && this._locales.TEXT_FREEZE_COLUMNS;
+ break;
case 'sort-asc':
item.title = this.translate.instant(`${translationPrefix}SORT_ASCENDING`) || this._locales && this._locales.TEXT_SORT_ASCENDING;
break;
@@ -306,6 +326,17 @@ export class HeaderMenuExtension implements Extension {
case 'clear-sort':
this.clearColumnSort(event, args);
break;
+ case 'freeze-columns':
+ const visibleColumns = [...this.sharedService.visibleColumns];
+ const columnPosition = visibleColumns.findIndex((col) => col.id === args.column.id);
+ this.sharedService.grid.setOptions({ frozenColumn: columnPosition, alwaysShowVerticalScroll: false });
+
+ // to freeze columns, we need to take only the visible columns and we also need to use setColumns() when some of them are hidden
+ // to make sure that we only use the visible columns, not doing this would show back some of the hidden columns
+ if (Array.isArray(visibleColumns) && Array.isArray(this.sharedService.allColumns) && visibleColumns.length !== this.sharedService.allColumns.length) {
+ this.sharedService.grid.setColumns(visibleColumns);
+ }
+ break;
case 'sort-asc':
case 'sort-desc':
const isSortingAsc = (args.command === 'sort-asc');
diff --git a/src/app/modules/angular-slickgrid/global-grid-options.ts b/src/app/modules/angular-slickgrid/global-grid-options.ts
index a329c510d..8de348a35 100644
--- a/src/app/modules/angular-slickgrid/global-grid-options.ts
+++ b/src/app/modules/angular-slickgrid/global-grid-options.ts
@@ -116,6 +116,7 @@ export const GlobalGridOptions: Partial = {
gridMenu: {
hideClearAllFiltersCommand: false,
hideClearAllSortingCommand: false,
+ hideClearFrozenColumnsCommand: true, // opt-in command
hideExportCsvCommand: false,
hideExportExcelCommand: false,
hideExportTextDelimitedCommand: true,
@@ -127,6 +128,7 @@ export const GlobalGridOptions: Partial = {
iconCssClass: 'fa fa-bars',
iconClearAllFiltersCommand: 'fa fa-filter text-danger',
iconClearAllSortingCommand: 'fa fa-unsorted text-danger',
+ iconClearFrozenColumnsCommand: 'fa fa-times',
iconExportCsvCommand: 'fa fa-download',
iconExportExcelCommand: 'fa fa-file-excel-o text-success',
iconExportTextDelimitedCommand: 'fa fa-download',
@@ -144,12 +146,14 @@ export const GlobalGridOptions: Partial = {
minWidth: 140,
iconClearFilterCommand: 'fa fa-filter text-danger',
iconClearSortCommand: 'fa fa-unsorted',
+ iconFreezeColumns: 'fa fa-thumb-tack',
iconSortAscCommand: 'fa fa-sort-amount-asc',
iconSortDescCommand: 'fa fa-sort-amount-desc',
iconColumnHideCommand: 'fa fa-times',
hideColumnHideCommand: false,
hideClearFilterCommand: false,
hideClearSortCommand: false,
+ hideFreezeColumnsCommand: true, // opt-in command
hideSortCommands: false
},
headerRowHeight: 35,
diff --git a/src/app/modules/angular-slickgrid/models/gridMenu.interface.ts b/src/app/modules/angular-slickgrid/models/gridMenu.interface.ts
index bff81413c..2768c69d3 100644
--- a/src/app/modules/angular-slickgrid/models/gridMenu.interface.ts
+++ b/src/app/modules/angular-slickgrid/models/gridMenu.interface.ts
@@ -37,6 +37,9 @@ 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 */
+ 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) */
hideExportCsvCommand?: boolean;
@@ -70,6 +73,9 @@ export interface GridMenu {
/** icon for the "Clear all Sorting" command */
iconClearAllSortingCommand?: string;
+ /** icon for the "Clear Frozen Columns" command */
+ iconClearFrozenColumnsCommand?: string;
+
/** icon for the "Export to CSV" command */
iconExportCsvCommand?: string;
@@ -121,7 +127,6 @@ export interface GridMenu {
/** Callback method that user can override the default behavior of enabling/disabling an item from the list. */
menuUsabilityOverride?: (args: MenuCallbackArgs) => boolean;
-
// --
// Events
diff --git a/src/app/modules/angular-slickgrid/models/headerMenu.interface.ts b/src/app/modules/angular-slickgrid/models/headerMenu.interface.ts
index dd2ef0011..e8663b9b2 100644
--- a/src/app/modules/angular-slickgrid/models/headerMenu.interface.ts
+++ b/src/app/modules/angular-slickgrid/models/headerMenu.interface.ts
@@ -29,9 +29,18 @@ export interface HeaderMenu {
/** Defaults to false, which will hide the Clear Filter command in the Header Menu (Grid Option "enableHeaderMenu: true" has to be enabled) */
hideFilterCommands?: boolean;
+ /** Defaults to true (opt-in feature), which will hide the "Freeze Columns" command in the Header Menu */
+ hideFreezeColumnsCommand?: boolean;
+
/** Defaults to false, which will hide Sort (Asc/Desc & Clear Sort) commands in the Header Menu (Grid Option "enableHeaderMenu: true" has to be enabled) */
hideSortCommands?: boolean;
+ /**
+ * Defaults to false, which will hide the Divider (separator) between the top sort commands and the other clear commands
+ * (Grid Option "enableHeaderMenu" and "enableSorting" have to be enabled)
+ */
+ hideSortCommandsDivider?: boolean;
+
/** Defaults to false, which will hide the "Hide Column" command in the Header Menu (Grid Option "enableHeaderMenu: true" has to be enabled) */
hideColumnHideCommand?: boolean;
@@ -50,6 +59,9 @@ export interface HeaderMenu {
/** icon for the "Hide Column" command */
iconColumnHideCommand?: string;
+ /** icon for the "Freeze Columns" command */
+ iconFreezeColumns?: string;
+
/** icon for the "Sort Ascending" command */
iconSortAscCommand?: string;
diff --git a/src/app/modules/angular-slickgrid/models/locale.interface.ts b/src/app/modules/angular-slickgrid/models/locale.interface.ts
index 1a20506d0..0d6703cf1 100644
--- a/src/app/modules/angular-slickgrid/models/locale.interface.ts
+++ b/src/app/modules/angular-slickgrid/models/locale.interface.ts
@@ -17,6 +17,9 @@ 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 "Columns" title displayed in the Column Picker & Grid Menu (when enabled) */
TEXT_COLUMNS: string;
@@ -53,6 +56,9 @@ export interface Locale {
/** Text "Force fit Columns" displayed in the Column Picker & Grid Menu (when enabled) */
TEXT_FORCE_FIT_COLUMNS: string;
+ /** Text "Freeze Columns" shown in Header Menu (when enabled) */
+ TEXT_FREEZE_COLUMNS?: string;
+
/** Text "Group by" shown in Export when using Grouping (when enabled) */
TEXT_GROUP_BY: string;
@@ -65,12 +71,12 @@ export interface Locale {
/** Text "items per page" displayed in the Pagination (when enabled) */
TEXT_ITEMS_PER_PAGE?: string;
- /** Text "of" displayed in the Pagination (when enabled) */
- TEXT_OF?: string;
-
/** Text "Last Update" displayed in the Footer (when enabled) */
TEXT_LAST_UPDATE?: string;
+ /** Text "of" displayed in the Pagination (when enabled) */
+ TEXT_OF?: string;
+
/** Text "OK" displayed in the Multiple Select Editor/Filter */
TEXT_OK: string;
diff --git a/src/app/modules/angular-slickgrid/models/slickGrid.interface.ts b/src/app/modules/angular-slickgrid/models/slickGrid.interface.ts
index 3dcdc22e9..cec1e45a6 100644
--- a/src/app/modules/angular-slickgrid/models/slickGrid.interface.ts
+++ b/src/app/modules/angular-slickgrid/models/slickGrid.interface.ts
@@ -479,6 +479,7 @@ export interface SlickGrid {
onViewportChanged: SlickEvent;
onRendered: SlickEvent;
onSelectedRowsChanged: SlickEvent;
+ onSetOptions: SlickEvent;
onScroll: SlickEvent;
onSort: SlickEvent;
}
diff --git a/src/app/modules/angular-slickgrid/services/__tests__/groupingAndColspan.service.spec.ts b/src/app/modules/angular-slickgrid/services/__tests__/groupingAndColspan.service.spec.ts
index a977cd894..3fed8c58f 100644
--- a/src/app/modules/angular-slickgrid/services/__tests__/groupingAndColspan.service.spec.ts
+++ b/src/app/modules/angular-slickgrid/services/__tests__/groupingAndColspan.service.spec.ts
@@ -49,9 +49,11 @@ const gridStub = {
invalidate: jest.fn(),
onColumnsResized: new Slick.Event(),
onColumnsReordered: new Slick.Event(),
+ onSetOptions: new Slick.Event(),
onSort: new Slick.Event(),
render: jest.fn(),
setColumns: jest.fn(),
+ setOptions: jest.fn(),
setSortColumns: jest.fn(),
};
@@ -338,5 +340,21 @@ describe('GroupingAndColspanService', () => {
expect(divHeaderColumns.length).toBeGreaterThan(2);
expect(divHeaderColumns[0].outerHTML).toEqual(`All your colums div here
`);
});
+
+ it('should render the pre-header row grouping title after changing "frozenColumn" with grid "setOptions"', () => {
+ const divHeaderColumns = document.getElementsByClassName('slick-header-columns');
+ jest.spyOn(gridStub, 'getColumns').mockReturnValue(mockColumns);
+ const spy = jest.spyOn(service, 'renderPreHeaderRowGroupingTitles');
+
+ service.init(gridStub, dataViewStub);
+ gridStub.onSetOptions.notify({ optionsBefore: { frozenColumn: -1 }, optionsAfter: { frozenColumn: 1 } }, new Slick.EventData(), gridStub);
+ jest.runAllTimers(); // fast-forward timer
+
+ expect(spy).toHaveBeenCalledTimes(2);
+ expect(setTimeout).toHaveBeenCalledTimes(2);
+ expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 0);
+ expect(divHeaderColumns.length).toBeGreaterThan(2);
+ expect(divHeaderColumns[0].outerHTML).toEqual(`All your colums div here
`);
+ });
});
});
diff --git a/src/app/modules/angular-slickgrid/services/groupingAndColspan.service.ts b/src/app/modules/angular-slickgrid/services/groupingAndColspan.service.ts
index 55775470c..2ed22c4a0 100644
--- a/src/app/modules/angular-slickgrid/services/groupingAndColspan.service.ts
+++ b/src/app/modules/angular-slickgrid/services/groupingAndColspan.service.ts
@@ -49,6 +49,13 @@ export class GroupingAndColspanService {
this._eventHandler.subscribe(dataView.onRowCountChanged, () => this.renderPreHeaderRowGroupingTitles());
this.resizerService.onGridAfterResize.subscribe(() => this.renderPreHeaderRowGroupingTitles());
+ this._eventHandler.subscribe(grid.onSetOptions, (_e, args) => {
+ // when user changes frozen columns dynamically (e.g. from header menu), we need to re-render the pre-header of the grouping titles
+ if (args && args.optionsBefore && args.optionsAfter && args.optionsBefore.frozenColumn !== args.optionsAfter.frozenColumn) {
+ setTimeout(() => this.renderPreHeaderRowGroupingTitles(), 0);
+ }
+ });
+
// for both picker (columnPicker/gridMenu) we also need to re-create after hiding/showing columns
const columnPickerExtension = this.extensionService.getExtensionByName(ExtensionName.columnPicker);
if (columnPickerExtension && columnPickerExtension.instance && columnPickerExtension.instance.onColumnsChanged) {
@@ -62,7 +69,7 @@ export class GroupingAndColspanService {
}
// also not sure why at this point, but it seems that I need to call the 1st create in a delayed execution
- // probably some kind of timing issues and delaying it until the grid is fully ready does help
+ // probably some kind of timing issues and delaying it until the grid is fully ready fixes this problem
setTimeout(() => this.renderPreHeaderRowGroupingTitles(), 50);
}
}
diff --git a/src/app/modules/angular-slickgrid/styles/material-svg-icons.scss b/src/app/modules/angular-slickgrid/styles/material-svg-icons.scss
index 49276e73c..87e29bd3f 100644
--- a/src/app/modules/angular-slickgrid/styles/material-svg-icons.scss
+++ b/src/app/modules/angular-slickgrid/styles/material-svg-icons.scss
@@ -20,6 +20,8 @@ $icon-color-mdi-help-circle: $icon-color !default;
$icon-color-mdi-help-circle-outline: $icon-color !default;
$icon-color-mdi-menu: $icon-color !default;
$icon-color-mdi-microsoft-excel: $icon-color !default;
+$icon-color-mdi-pin-off-outline: $icon-color !default;
+$icon-color-mdi-pin-outline: $icon-color !default;
$icon-color-mdi-plus: $icon-color !default;
$icon-color-mdi-sort-ascending: $icon-color !default;
$icon-color-mdi-sort-descending: $icon-color !default;
@@ -48,6 +50,8 @@ $icon-width-mdi-help-circle: $icon-width !default;
$icon-width-mdi-help-circle-outline: $icon-width !default;
$icon-width-mdi-menu: $icon-width !default;
$icon-width-mdi-microsoft-excel: $icon-width !default;
+$icon-width-mdi-pin-off-outline: $icon-width !default;
+$icon-width-mdi-pin-outline: $icon-width !default;
$icon-width-mdi-plus: $icon-width !default;
$icon-width-mdi-sort-ascending: $icon-width !default;
$icon-width-mdi-sort-descending: $icon-width !default;
@@ -146,6 +150,14 @@ $icon-width-mdi-sync: $icon-width !default;
width: $icon-width-mdi-microsoft-excel;
content: url('data:image/svg+xml, ');
}
+ &.mdi-pin-off-outline:before {
+ width: $icon-width-mdi-pin-off-outline;
+ content: url('data:image/svg+xml, ');
+ }
+ &.mdi-pin-outline:before {
+ width: $icon-width-mdi-pin-outline;
+ content: url('data:image/svg+xml, ');
+ }
&.mdi-plus:before {
width: $icon-width-mdi-plus;
content: url('data:image/svg+xml, ');
diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json
index b1b94d06c..ededaa3f1 100644
--- a/src/assets/i18n/en.json
+++ b/src/assets/i18n/en.json
@@ -4,6 +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",
"COLLAPSE_ALL_GROUPS": "Collapse all Groups",
"COLUMNS": "Columns",
"COMMANDS": "Commands",
@@ -16,6 +17,7 @@
"EXPORT_TO_EXCEL": "Export to Excel",
"EXPORT_TO_TAB_DELIMITED": "Export in Text format (Tab delimited)",
"EXPORT_TO_TEXT_FORMAT": "Export in Text format",
+ "FREEZE_COLUMNS": "Freeze Columns",
"FROM_TO_OF_TOTAL_ITEMS": "{{from}}-{{to}} of {{totalItems}} items",
"FORCE_FIT_COLUMNS": "Force fit columns",
"INVALID_FLOAT": "The number must be valid and have a maximum of {{maxDecimal}} decimals.",
diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json
index 1550a13af..8ad64d275 100644
--- a/src/assets/i18n/fr.json
+++ b/src/assets/i18n/fr.json
@@ -4,6 +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",
"COLLAPSE_ALL_GROUPS": "Réduire tous les groupes",
"COLUMNS": "Colonnes",
"COMMANDS": "Commandes",
@@ -16,6 +17,7 @@
"EXPORT_TO_EXCEL": "Exporter vers Excel",
"EXPORT_TO_TAB_DELIMITED": "Exporter en format texte (délimité par tabulation)",
"EXPORT_TO_TEXT_FORMAT": "Exporter en format texte",
+ "FREEZE_COLUMNS": "Geler les colonnes",
"FROM_TO_OF_TOTAL_ITEMS": "{{from}}-{{to}} de {{totalItems}} éléments",
"FORCE_FIT_COLUMNS": "Ajustement forcé des colonnes",
"GROUP_BY": "Grouper par",
diff --git a/test/cypress/integration/example15.spec.js b/test/cypress/integration/example15.spec.js
new file mode 100644
index 000000000..c9e2cc083
--- /dev/null
+++ b/test/cypress/integration/example15.spec.js
@@ -0,0 +1,125 @@
+///
+
+function removeExtraSpaces(textS) {
+ return `${textS}`.replace(/\s+/g, ' ').trim();
+}
+
+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
+ const fullPreTitles = ['', 'Common Factor', 'Period', 'Analysis'];
+ const fullTitles = ['#', 'Title', 'Duration', 'Start', 'Finish', '% Complete', 'Effort Driven'];
+
+ it('should display Example title', () => {
+ cy.visit(`${Cypress.config('baseExampleUrl')}/colspan`);
+ cy.get('h2').should('contain', 'Example 15: Column Span & Header Grouping');
+ });
+
+ it('should have exact Column Pre-Header & Column Header Titles in the grid', () => {
+ cy.get('#grid2')
+ .find('.slick-header-columns:nth(0)')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullPreTitles[index]));
+
+ cy.get('#grid2')
+ .find('.slick-header-columns:nth(1)')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullTitles[index]));
+ });
+
+ it('should have a frozen grid on page load with 3 columns on the left and 4 columns on the right', () => {
+ cy.get('#grid2').find('[style="top:0px"]').should('have.length', 2);
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"]').children().should('have.length', 3);
+ cy.get('#grid2').find('.grid-canvas-right > [style="top:0px"]').children().should('have.length', 4);
+
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(0)').should('contain', '0');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(1)').should('contain', 'Task 0');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(2)').should('contain', '5 days');
+
+ cy.get('#grid2').find('.grid-canvas-right > [style="top:0px"] > .slick-cell:nth(0)').should('contain', '01/01/2009');
+ cy.get('#grid2').find('.grid-canvas-right > [style="top:0px"] > .slick-cell:nth(1)').should('contain', '01/05/2009');
+ });
+
+ it('should have exact Column Pre-Header & Column Header Titles in the grid', () => {
+ cy.get('#grid2')
+ .find('.slick-header-columns:nth(0)')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullPreTitles[index]));
+
+ cy.get('#grid2')
+ .find('.slick-header-columns:nth(1)')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullTitles[index]));
+ });
+
+ it('should click on the "Remove Frozen Columns" button to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => {
+ cy.contains('Remove Frozen Columns')
+ .click({ force: true });
+
+ cy.get('#grid2').find('[style="top:0px"]').should('have.length', 1);
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"]').children().should('have.length', 7);
+
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(0)').should('contain', '0');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(1)').should('contain', 'Task 0');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(2)').should('contain', '5 days');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(3)').should('contain', '01/01/2009');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(4)').should('contain', '01/05/2009');
+ });
+
+ it('should have exact Column Pre-Header & Column Header Titles in the grid', () => {
+ cy.get('#grid2')
+ .find('.slick-header-columns:nth(0)')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullPreTitles[index]));
+
+ cy.get('#grid2')
+ .find('.slick-header-columns:nth(1)')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullTitles[index]));
+ });
+
+ it('should click on the "Set 3 Frozen Columns" button to switch frozen columns grid and expect 3 frozen columns on the left and 4 columns on the right', () => {
+ cy.contains('Set 3 Frozen Columns')
+ .click({ force: true });
+
+ cy.get('#grid2').find('[style="top:0px"]').should('have.length', 2);
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"]').children().should('have.length', 3);
+ cy.get('#grid2').find('.grid-canvas-right > [style="top:0px"]').children().should('have.length', 4);
+
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(0)').should('contain', '0');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(1)').should('contain', 'Task 0');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(2)').should('contain', '5 days');
+
+ cy.get('#grid2').find('.grid-canvas-right > [style="top:0px"] > .slick-cell:nth(0)').should('contain', '01/01/2009');
+ cy.get('#grid2').find('.grid-canvas-right > [style="top:0px"] > .slick-cell:nth(1)').should('contain', '01/05/2009');
+ });
+
+ it('should have exact Column Pre-Header & Column Header Titles in the grid', () => {
+ cy.get('#grid2')
+ .find('.slick-header-columns:nth(0)')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullPreTitles[index]));
+
+ cy.get('#grid2')
+ .find('.slick-header-columns:nth(1)')
+ .children()
+ .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', () => {
+ cy.get('#grid2')
+ .find('button.slick-gridmenu-button')
+ .click({ force: true });
+
+ cy.contains('Clear Frozen Columns')
+ .click({ force: true });
+
+ cy.get('#grid2').find('[style="top:0px"]').should('have.length', 1);
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"]').children().should('have.length', 7);
+
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(0)').should('contain', '0');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(1)').should('contain', 'Task 0');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(2)').should('contain', '5 days');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(3)').should('contain', '01/01/2009');
+ cy.get('#grid2').find('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(4)').should('contain', '01/05/2009');
+ });
+});
diff --git a/test/cypress/integration/example20.spec.js b/test/cypress/integration/example20.spec.js
new file mode 100644
index 000000000..276b0a3d4
--- /dev/null
+++ b/test/cypress/integration/example20.spec.js
@@ -0,0 +1,103 @@
+///
+
+function removeExtraSpaces(textS) {
+ return `${textS}`.replace(/\s+/g, ' ').trim();
+}
+
+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
+
+ const fullTitles = ['#', 'Title', '% Complete', 'Start', 'Finish', 'Cost | Duration', 'Effort Driven', 'Title 1', 'Title 2', 'Title 3', 'Title 4'];
+
+ it('should display Example title', () => {
+ cy.visit(`${Cypress.config('baseExampleUrl')}/frozen`);
+ cy.get('h2').should('contain', 'Example 20: Pinned (frozen) Columns/Rows');
+ });
+
+ it('should have exact column titles on 1st grid', () => {
+ cy.get('#grid20')
+ .find('.slick-header-columns')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullTitles[index]));
+ });
+
+ it('should have exact Column Header Titles in the grid', () => {
+ cy.get('#grid20')
+ .find('.slick-header-columns:nth(0)')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullTitles[index]));
+ });
+
+ it('should have a frozen grid with 4 containers on page load with 3 columns on the left and 4 columns on the right', () => {
+ cy.get('[style="top:0px"]').should('have.length', 2 * 2);
+ cy.get('.grid-canvas-left > [style="top:0px"]').children().should('have.length', 3 * 2);
+ cy.get('.grid-canvas-right > [style="top:0px"]').children().should('have.length', 8 * 2);
+
+ 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');
+
+ cy.get('.grid-canvas-right > [style="top:0px"] > .slick-cell:nth(0)').should('contain', '2009-01-01');
+ cy.get('.grid-canvas-right > [style="top:0px"] > .slick-cell:nth(1)').should('contain', '2009-05-05');
+ });
+
+ it('should click on the "Remove Frozen Columns" button to switch to a regular grid without frozen columns and expect 7 columns on the left container', () => {
+ cy.get('[data-test=remove-frozen-column-button]')
+ .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('.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');
+
+ cy.get('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(3)').should('contain', '2009-01-01');
+ cy.get('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(4)').should('contain', '2009-05-05');
+ });
+
+ it('should have exact Column Header Titles in the grid', () => {
+ cy.get('#grid20')
+ .find('.slick-header-columns:nth(0)')
+ .children()
+ .each(($child, index) => expect($child.text()).to.eq(fullTitles[index]));
+ });
+
+ it('should click on the "Set 3 Frozen Columns" button to switch frozen columns grid and expect 3 frozen columns on the left and 4 columns on the right', () => {
+ cy.get('[data-test=set-3frozen-columns]')
+ .click({ force: true });
+
+ cy.get('[style="top:0px"]').should('have.length', 2 * 2);
+ cy.get('.grid-canvas-left > [style="top:0px"]').children().should('have.length', 3 * 2);
+ cy.get('.grid-canvas-right > [style="top:0px"]').children().should('have.length', 8 * 2);
+
+ 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');
+
+ cy.get('.grid-canvas-right > [style="top:0px"] > .slick-cell:nth(0)').should('contain', '2009-01-01');
+ cy.get('.grid-canvas-right > [style="top:0px"] > .slick-cell:nth(1)').should('contain', '2009-05-05');
+ });
+
+ it('should have exact Column Header Titles in the grid', () => {
+ cy.get('#grid20')
+ .find('.slick-header-columns:nth(0)')
+ .children()
+ .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', () => {
+ cy.get('#grid20')
+ .find('button.slick-gridmenu-button')
+ .click({ force: true });
+
+ cy.contains('Clear Frozen Columns')
+ .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('.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');
+
+ cy.get('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(3)').should('contain', '2009-01-01');
+ cy.get('.grid-canvas-left > [style="top:0px"] > .slick-cell:nth(4)').should('contain', '2009-05-05');
+ });
+});