diff --git a/src/aurelia-slickgrid/extensions/__tests__/cellExternalCopyManagerExtension.spec.ts b/src/aurelia-slickgrid/extensions/__tests__/cellExternalCopyManagerExtension.spec.ts
index dccb75cda..3b5ac7d68 100644
--- a/src/aurelia-slickgrid/extensions/__tests__/cellExternalCopyManagerExtension.spec.ts
+++ b/src/aurelia-slickgrid/extensions/__tests__/cellExternalCopyManagerExtension.spec.ts
@@ -51,8 +51,8 @@ describe('cellExternalCopyManagerExtension', () => {
} as GridOption;
beforeEach(() => {
- extensionUtility = new ExtensionUtility({} as I18N, sharedService);
sharedService = new SharedService();
+ extensionUtility = new ExtensionUtility({} as I18N, sharedService);
extension = new CellExternalCopyManagerExtension(extensionUtility, sharedService);
});
@@ -73,11 +73,11 @@ describe('cellExternalCopyManagerExtension', () => {
it('should register the addon', () => {
const pluginSpy = jest.spyOn(SharedService.prototype.grid, 'registerPlugin');
- const optionSpy = jest.spyOn(SharedService.prototype.gridOptions.excelCopyBufferOptions, 'onExtensionRegistered');
+ const onRegisteredSpy = jest.spyOn(SharedService.prototype.gridOptions.excelCopyBufferOptions, 'onExtensionRegistered');
const instance = extension.register();
- expect(optionSpy).toHaveBeenCalledWith(instance);
+ expect(onRegisteredSpy).toHaveBeenCalledWith(instance);
expect(pluginSpy).toHaveBeenCalledWith(instance);
expect(mockSelectionModel).toHaveBeenCalled();
expect(mockAddon).toHaveBeenCalledWith({
diff --git a/src/aurelia-slickgrid/extensions/__tests__/columnPickerExtension.spec.ts b/src/aurelia-slickgrid/extensions/__tests__/columnPickerExtension.spec.ts
new file mode 100644
index 000000000..be798ea2a
--- /dev/null
+++ b/src/aurelia-slickgrid/extensions/__tests__/columnPickerExtension.spec.ts
@@ -0,0 +1,139 @@
+import { I18N } from 'aurelia-i18n';
+import { EventAggregator } from 'aurelia-event-aggregator';
+import { BindingSignaler } from 'aurelia-templating-resources';
+import { GridOption } from '../../models/gridOption.interface';
+import { ColumnPickerExtension } from '../columnPickerExtension';
+import { ExtensionUtility } from '../extensionUtility';
+import { SharedService } from '../../services/shared.service';
+import { Column } from '../../models';
+
+declare var Slick: any;
+
+const gridStub = {
+ getOptions: jest.fn(),
+ registerPlugin: jest.fn(),
+};
+
+const mockAddon = jest.fn().mockImplementation(() => ({
+ init: jest.fn(),
+ destroy: jest.fn(),
+ onColumnsChanged: new Slick.Event(),
+}));
+
+jest.mock('slickgrid/controls/slick.columnpicker', () => mockAddon);
+Slick.Controls = {
+ ColumnPicker: mockAddon
+};
+
+describe('columnPickerExtension', () => {
+ const columnsMock: Column[] = [{ id: 'field1', field: 'field1', width: 100, headerKey: 'TITLE' }, { id: 'field2', field: 'field2', width: 75 }];
+ let extensionUtility: ExtensionUtility;
+ let i18n: I18N;
+ let extension: ColumnPickerExtension;
+ let sharedService: SharedService;
+
+ const gridOptionsMock = {
+ enableColumnPicker: true,
+ enableTranslate: true,
+ columnPicker: {
+ hideForceFitButton: false,
+ hideSyncResizeButton: true,
+ onExtensionRegistered: jest.fn(),
+ onColumnsChanged: (e, args: { columns: Column[] }) => { }
+ },
+ } as GridOption;
+
+ beforeEach(() => {
+ sharedService = new SharedService();
+ i18n = new I18N(new EventAggregator(), new BindingSignaler());
+ extensionUtility = new ExtensionUtility(i18n, sharedService);
+ extension = new ColumnPickerExtension(extensionUtility, sharedService);
+ i18n.setup({
+ resources: {
+ en: {
+ translation: {
+ TITLE: 'Title', COMMANDS: 'Commands', COLUMNS: 'Columns', FORCE_FIT_COLUMNS: 'Force fit columns', SYNCHRONOUS_RESIZE: 'Synchronous resize'
+ }
+ },
+ fr: {
+ translation: {
+ TITLE: 'Titre', COMMANDS: 'Commandes', COLUMNS: 'Colonnes', FORCE_FIT_COLUMNS: 'Ajustement forcé des colonnes', SYNCHRONOUS_RESIZE: 'Redimension synchrone'
+ }
+ }
+ },
+ lng: '0',
+ fallbackLng: 'en',
+ debug: false
+ });
+ i18n.setLocale('fr');
+ });
+
+ it('should return null when either the grid object or the grid options is missing', () => {
+ const output = extension.register();
+ expect(output).toBeNull();
+ });
+
+ describe('registered addon', () => {
+ beforeEach(() => {
+ jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub);
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+ jest.spyOn(SharedService.prototype, 'allColumns', 'get').mockReturnValue(columnsMock);
+ jest.spyOn(SharedService.prototype, 'columnDefinitions', 'get').mockReturnValue(columnsMock);
+ });
+
+ it('should register the addon', () => {
+ const onRegisteredSpy = jest.spyOn(SharedService.prototype.gridOptions.columnPicker, 'onExtensionRegistered');
+ const instance = extension.register();
+
+ expect(instance).not.toBeNull();
+ expect(onRegisteredSpy).toHaveBeenCalledWith(instance);
+ expect(mockAddon).toHaveBeenCalledWith(columnsMock, gridStub, gridOptionsMock);
+ });
+
+ it('should call internal event handler subscribe and expect the "onColumnSpy" option to be called when addon notify is called', () => {
+ const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
+ const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.columnPicker, 'onColumnsChanged');
+
+ const instance = extension.register();
+ instance.onColumnsChanged.notify(columnsMock.slice(0, 1), new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalledTimes(1);
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), columnsMock.slice(0, 1));
+ });
+
+ it('should dispose of the addon', () => {
+ const instance = extension.register();
+ const destroySpy = jest.spyOn(instance, 'destroy');
+
+ extension.dispose();
+
+ expect(destroySpy).toHaveBeenCalled();
+ });
+ });
+
+ describe('translateColumnPicker method', () => {
+ it('should translate the column picker header titles', () => {
+ const utilitySpy = jest.spyOn(extensionUtility, 'getPickerTitleOutputString');
+ const translateSpy = jest.spyOn(extensionUtility, 'translateItems');
+
+ const instance = extension.register();
+ extension.translateColumnPicker();
+ const initSpy = jest.spyOn(instance, 'init');
+
+ expect(utilitySpy).toHaveBeenCalled();
+ expect(translateSpy).toHaveBeenCalled();
+ expect(initSpy).toHaveBeenCalledWith(gridStub);
+ expect(SharedService.prototype.gridOptions.columnPicker.columnTitle).toBe('Colonnes');
+ expect(SharedService.prototype.gridOptions.columnPicker.forceFitTitle).toBe('Ajustement forcé des colonnes');
+ expect(SharedService.prototype.gridOptions.columnPicker.syncResizeTitle).toBe('Redimension synchrone');
+ expect(columnsMock).toEqual([
+ { id: 'field1', field: 'field1', width: 100, name: 'Titre', headerKey: 'TITLE' },
+ { id: 'field2', field: 'field2', width: 75 }
+ ]);
+ });
+ });
+});
diff --git a/src/aurelia-slickgrid/extensions/__tests__/draggableGroupingExtension.spec.ts b/src/aurelia-slickgrid/extensions/__tests__/draggableGroupingExtension.spec.ts
new file mode 100644
index 000000000..234e0d243
--- /dev/null
+++ b/src/aurelia-slickgrid/extensions/__tests__/draggableGroupingExtension.spec.ts
@@ -0,0 +1,114 @@
+import { I18N } from 'aurelia-i18n';
+import { GridOption } from '../../models/gridOption.interface';
+import { DraggableGroupingExtension } from '../draggableGroupingExtension';
+import { ExtensionUtility } from '../extensionUtility';
+import { SharedService } from '../../services/shared.service';
+import { Grouping } from '../../models';
+
+declare var Slick: any;
+
+const gridStub = {
+ getOptions: jest.fn(),
+ registerPlugin: jest.fn(),
+};
+
+const mockAddon = jest.fn().mockImplementation(() => ({
+ init: jest.fn(),
+ destroy: jest.fn(),
+ onGroupChanged: new Slick.Event(),
+}));
+
+jest.mock('slickgrid/plugins/slick.draggablegrouping', () => mockAddon);
+Slick.DraggableGrouping = mockAddon;
+
+describe('draggableGroupingExtension', () => {
+ let extensionUtility: ExtensionUtility;
+ let sharedService: SharedService;
+ let extension: DraggableGroupingExtension;
+ const gridOptionsMock = {
+ enableDraggableGrouping: true,
+ draggableGrouping: {
+ deleteIconCssClass: 'class',
+ dropPlaceHolderText: 'test',
+ groupIconCssClass: 'group-class',
+ onExtensionRegistered: jest.fn(),
+ onGroupChanged: (e: Event, args: { caller?: string; groupColumns: Grouping[] }) => { },
+ }
+ } as GridOption;
+
+ beforeEach(() => {
+ sharedService = new SharedService();
+ extensionUtility = new ExtensionUtility({} as I18N, sharedService);
+ extension = new DraggableGroupingExtension(extensionUtility, sharedService);
+ });
+
+ it('should return null when either the grid object or the grid options is missing', () => {
+ const output = extension.register();
+ expect(output).toBeNull();
+ });
+
+ describe('registered addon', () => {
+ beforeEach(() => {
+ jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub);
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+ });
+
+ it('should register the addon', () => {
+ const onRegisteredSpy = jest.spyOn(SharedService.prototype.gridOptions.draggableGrouping, 'onExtensionRegistered');
+ const pluginSpy = jest.spyOn(SharedService.prototype.grid, 'registerPlugin');
+
+ const instance = extension.create(gridOptionsMock);
+ const addon = extension.register();
+
+ expect(addon).not.toBeNull();
+ expect(mockAddon).toHaveBeenCalledWith({
+ deleteIconCssClass: 'class',
+ dropPlaceHolderText: 'test',
+ groupIconCssClass: 'group-class',
+ onExtensionRegistered: expect.anything(),
+ onGroupChanged: expect.anything(),
+ });
+ expect(onRegisteredSpy).toHaveBeenCalledWith(instance);
+ expect(pluginSpy).toHaveBeenCalledWith(instance);
+ });
+
+ it('should dispose of the addon', () => {
+ const instance = extension.create(gridOptionsMock);
+ const destroySpy = jest.spyOn(instance, 'destroy');
+
+ extension.dispose();
+
+ expect(destroySpy).toHaveBeenCalled();
+ });
+
+ it('should provide addon options and expect them to be called in the addon constructor', () => {
+ const optionMock = {
+ deleteIconCssClass: 'different-class',
+ dropPlaceHolderText: 'different-test',
+ groupIconCssClass: 'different-group-class',
+ };
+ const addonOptions = { ...gridOptionsMock, draggableGrouping: optionMock };
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(addonOptions);
+
+ extension.create(addonOptions);
+ extension.register();
+
+ expect(mockAddon).toHaveBeenCalledWith(optionMock);
+ });
+
+ it('should call internal event handler subscribe and expect the "onGroupChanged" option to be called when addon notify is called', () => {
+ const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
+ const onColumnSpy = jest.spyOn(SharedService.prototype.gridOptions.draggableGrouping, 'onGroupChanged');
+
+ const instance = extension.create(gridOptionsMock);
+ extension.register();
+ instance.onGroupChanged.notify({ caller: 'clear-all', groupColumns: [] }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), { caller: 'clear-all', groupColumns: [] });
+ });
+ });
+});
diff --git a/src/aurelia-slickgrid/extensions/__tests__/extensionUtility.spec.ts b/src/aurelia-slickgrid/extensions/__tests__/extensionUtility.spec.ts
new file mode 100644
index 000000000..012649b45
--- /dev/null
+++ b/src/aurelia-slickgrid/extensions/__tests__/extensionUtility.spec.ts
@@ -0,0 +1,98 @@
+import { I18N } from 'aurelia-i18n';
+import { EventAggregator } from 'aurelia-event-aggregator';
+import { BindingSignaler } from 'aurelia-templating-resources';
+import { GridOption } from '../../models/gridOption.interface';
+import { ExtensionUtility } from '../extensionUtility';
+import { SharedService } from '../../services/shared.service';
+import { ExtensionName } from '../../models';
+
+declare var Slick: any;
+
+const mockAddon = jest.fn().mockImplementation(() => ({
+ init: jest.fn(),
+ destroy: jest.fn()
+}));
+
+jest.mock('slickgrid/slick.groupitemmetadataprovider', () => mockAddon);
+Slick.Data = {
+ GroupItemMetadataProvider: mockAddon
+};
+
+describe('extensionUtility', () => {
+ let i18n: I18N;
+ let sharedService: SharedService;
+ let utility: ExtensionUtility;
+
+ beforeEach(async () => {
+ sharedService = new SharedService();
+ i18n = new I18N(new EventAggregator(), new BindingSignaler());
+ utility = new ExtensionUtility(i18n, sharedService);
+ i18n.setup({
+ resources: { en: { translation: { TITLE: 'Title' } }, fr: { translation: { TITLE: 'Titre' } } },
+ lng: '0',
+ fallbackLng: 'en',
+ debug: false
+ });
+ await i18n.setLocale('fr');
+ });
+
+ describe('arrayRemoveItemByIndex method', () => {
+ it('should remove an item from the array', () => {
+ const input = [{ field: 'field1', name: 'Field 1' }, { field: 'field2', name: 'Field 2' }, { field: 'field3', name: 'Field 3' }];
+ const expected = [{ field: 'field1', name: 'Field 1' }, { field: 'field3', name: 'Field 3' }];
+
+ const output = utility.arrayRemoveItemByIndex(input, 1);
+ expect(output).toEqual(expected);
+ });
+ });
+
+ describe('loadExtensionDynamically method', () => {
+ // we can test only the GroupItemMetadataProvider which is not tested in any other extension test
+ it('should check that groupItemMetaProvider gets loaded', () => {
+ utility.loadExtensionDynamically(ExtensionName.groupItemMetaProvider);
+ const groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider();
+ expect(mockAddon).toHaveBeenCalled();
+ expect(groupItemMetadataProvider).not.toBeNull();
+ });
+ });
+
+ describe('getPickerTitleOutputString method', () => {
+ it('should translate titleKey when there is one', () => {
+ const gridOptionsMock = { enableTranslate: true, enableGridMenu: true, gridMenu: { hideForceFitButton: false, hideSyncResizeButton: true, columnTitleKey: 'TITLE' } } as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+
+ const output = utility.getPickerTitleOutputString('columnTitle', 'gridMenu');
+
+ expect(output).toEqual('Titre');
+ });
+
+ it('should return undefined when the given property is not found', () => {
+ const gridOptionsMock = { enableTranslate: true, enableGridMenu: true, gridMenu: { hideForceFitButton: false, hideSyncResizeButton: true } } as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+
+ const output = utility.getPickerTitleOutputString('unknown', 'gridMenu');
+
+ expect(output).toEqual(undefined);
+ });
+ });
+
+ describe('sortItems method', () => {
+ it('should sort the items by their order property', () => {
+ const inputArray = [{ field: 'field1', order: 3 }, { field: 'field2', order: 1 }, { field: 'field3', order: 2 }];
+ const expectedArray = [{ field: 'field2', order: 1 }, { field: 'field3', order: 2 }, { field: 'field1', order: 3 }];
+
+ utility.sortItems(inputArray, 'order');
+
+ expect(inputArray).toEqual(expectedArray);
+ });
+
+ it('should sort the items by their order property when found and then return the object without the property', () => {
+ const inputArray = [{ field: 'field1', order: 3 }, { field: 'field3', order: 2 }, { field: 'field2' }];
+ const expectedArray = [{ field: 'field3', order: 2 }, { field: 'field1', order: 3 }, { field: 'field2' }];
+
+ utility.sortItems(inputArray, 'order');
+
+ expect(inputArray).toEqual(expectedArray);
+ });
+ });
+});
diff --git a/src/aurelia-slickgrid/extensions/__tests__/gridMenuExtension.spec.ts b/src/aurelia-slickgrid/extensions/__tests__/gridMenuExtension.spec.ts
new file mode 100644
index 000000000..e484d5710
--- /dev/null
+++ b/src/aurelia-slickgrid/extensions/__tests__/gridMenuExtension.spec.ts
@@ -0,0 +1,590 @@
+import { I18N } from 'aurelia-i18n';
+import { EventAggregator } from 'aurelia-event-aggregator';
+import { BindingSignaler } from 'aurelia-templating-resources';
+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 { ExportService, FilterService, SortService } from '../../services';
+
+declare var Slick: any;
+jest.mock('flatpickr', () => { });
+
+const gridId = 'grid1';
+const gridUid = 'slickgrid_124343';
+const containerId = 'demo-container';
+const exportServiceStub = {
+ exportToFile: jest.fn(),
+} as unknown as ExportService;
+
+const filterServiceStub = {
+ clearFilters: jest.fn(),
+} as unknown as FilterService;
+
+const sortServiceStub = {
+ clearSorting: jest.fn(),
+} as unknown as SortService;
+
+const dataViewStub = {
+ refresh: jest.fn(),
+};
+
+const gridStub = {
+ autosizeColumns: jest.fn(),
+ getColumnIndex: jest.fn(),
+ getColumns: jest.fn(),
+ getOptions: jest.fn(),
+ getUID: () => gridUid,
+ registerPlugin: jest.fn(),
+ setColumns: jest.fn(),
+ setHeaderRowVisibility: jest.fn(),
+ setTopPanelVisibility: jest.fn(),
+ setPreHeaderPanelVisibility: jest.fn(),
+};
+
+const mockAddon = jest.fn().mockImplementation(() => ({
+ init: jest.fn(),
+ destroy: jest.fn(),
+ showGridMenu: jest.fn(),
+ onColumnsChanged: new Slick.Event(),
+ onCommand: new Slick.Event(),
+ onBeforeMenuShow: new Slick.Event(),
+ onMenuClose: new Slick.Event(),
+}));
+
+jest.mock('slickgrid/controls/slick.gridmenu', () => mockAddon);
+Slick.Controls = {
+ GridMenu: mockAddon
+};
+
+// define a
container to simulate the grid container
+const template =
+ `
`;
+
+describe('gridMenuExtension', () => {
+ const columnsMock: Column[] = [{ id: 'field1', field: 'field1', width: 100, headerKey: 'TITLE' }, { id: 'field2', field: 'field2', width: 75 }];
+ let extensionUtility: ExtensionUtility;
+ let i18n: I18N;
+ let extension: GridMenuExtension;
+ let sharedService: SharedService;
+
+ const gridOptionsMock = {
+ enableAutoSizeColumns: true,
+ enableGridMenu: true,
+ enableTranslate: true,
+ backendServiceApi: {
+ service: {
+ buildQuery: jest.fn(),
+ },
+ internalPostProcess: jest.fn(),
+ preProcess: jest.fn(),
+ process: jest.fn(),
+ postProcess: jest.fn(),
+ },
+ gridMenu: {
+ customItems: [],
+ hideClearAllFiltersCommand: false,
+ hideForceFitButton: false,
+ hideSyncResizeButton: true,
+ onExtensionRegistered: jest.fn(),
+ onCommand: (e, args: { command: any, item: any, grid: any }) => { },
+ onColumnsChanged: (e, args: { columns: Column[], grid: any }) => { },
+ onBeforeMenuShow: (e, args: { menu: any, grid: any }) => { },
+ onMenuClose: (e, args: { menu: any, grid: any }) => { },
+ },
+ pagination: {
+ totalItems: 0
+ },
+ showHeaderRow: false,
+ showTopPanel: false,
+ showPreHeaderPanel: false
+ } as unknown as GridOption;
+
+ beforeEach(() => {
+ const div = document.createElement('div');
+ div.innerHTML = template;
+ document.body.appendChild(div);
+
+ sharedService = new SharedService();
+ i18n = new I18N(new EventAggregator(), new BindingSignaler());
+ extensionUtility = new ExtensionUtility(i18n, sharedService);
+ extension = new GridMenuExtension(exportServiceStub, extensionUtility, filterServiceStub, i18n, sharedService, sortServiceStub);
+ i18n.setup({
+ resources: {
+ en: {
+ translation: {
+ TITLE: 'Title', COMMANDS: 'Commands', COLUMNS: 'Columns', FORCE_FIT_COLUMNS: 'Force fit columns', SYNCHRONOUS_RESIZE: 'Synchronous resize'
+ }
+ },
+ fr: {
+ translation: {
+ TITLE: 'Titre',
+ CLEAR_ALL_FILTERS: 'Supprimer tous les filtres',
+ CLEAR_ALL_SORTING: 'Supprimer tous les tris',
+ EXPORT_TO_CSV: 'Exporter en format CSV',
+ EXPORT_TO_TAB_DELIMITED: 'Exporter en format texte (délimité par tabulation)',
+ COMMANDS: 'Commandes',
+ COLUMNS: 'Colonnes',
+ FORCE_FIT_COLUMNS: 'Ajustement forcé des colonnes',
+ SYNCHRONOUS_RESIZE: 'Redimension synchrone',
+ TOGGLE_FILTER_ROW: 'Basculer la ligne des filtres',
+ REFRESH_DATASET: 'Rafraîchir les données',
+ TOGGLE_PRE_HEADER_ROW: 'Basculer la ligne de pré-en-tête',
+
+ }
+ }
+ },
+ lng: '0',
+ fallbackLng: 'en',
+ debug: false
+ });
+ i18n.setLocale('fr');
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should return null when either the grid object or the grid options is missing', () => {
+ const output = extension.register();
+ expect(output).toBeNull();
+ });
+
+ describe('registered addon', () => {
+ beforeEach(() => {
+ jest.spyOn(SharedService.prototype, 'dataView', 'get').mockReturnValue(dataViewStub);
+ jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub);
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+ jest.spyOn(SharedService.prototype, 'allColumns', 'get').mockReturnValue(columnsMock);
+ jest.spyOn(SharedService.prototype, 'visibleColumns', 'get').mockReturnValue(columnsMock);
+ jest.spyOn(SharedService.prototype, 'columnDefinitions', 'get').mockReturnValue(columnsMock);
+ });
+
+ it('should register the addon', () => {
+ const onRegisteredSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu, 'onExtensionRegistered');
+ const instance = extension.register();
+
+ expect(instance).not.toBeNull();
+ expect(onRegisteredSpy).toHaveBeenCalledWith(instance);
+ expect(mockAddon).toHaveBeenCalledWith(columnsMock, gridStub, gridOptionsMock);
+ });
+
+ 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 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 instance = extension.register();
+ instance.onColumnsChanged.notify(columnsMock.slice(0, 1), new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalledTimes(4);
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onColumnSpy).toHaveBeenCalledWith(expect.anything(), columnsMock.slice(0, 1));
+ expect(onBeforeSpy).not.toHaveBeenCalled();
+ expect(onCloseSpy).not.toHaveBeenCalled();
+ expect(onCommandSpy).not.toHaveBeenCalled();
+ });
+
+ 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 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 instance = extension.register();
+ instance.onBeforeMenuShow.notify({ grid: gridStub, menu: {} }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalledTimes(4);
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onBeforeSpy).toHaveBeenCalledWith(expect.anything(), { grid: gridStub, menu: {} });
+ expect(onColumnSpy).not.toHaveBeenCalled();
+ expect(onCloseSpy).not.toHaveBeenCalled();
+ expect(onCommandSpy).not.toHaveBeenCalled();
+ });
+
+ 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 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 instance = extension.register();
+ instance.onMenuClose.notify({ grid: gridStub, menu: {} }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalledTimes(4);
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onCloseSpy).toHaveBeenCalledWith(expect.anything(), { grid: gridStub, menu: {} });
+ expect(onColumnSpy).not.toHaveBeenCalled();
+ expect(onBeforeSpy).not.toHaveBeenCalled();
+ expect(onCommandSpy).not.toHaveBeenCalled();
+ });
+
+ 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 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 instance = extension.register();
+ instance.onCommand.notify({ grid: gridStub, command: 'help' }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalledTimes(4);
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onCommandSpy).toHaveBeenCalledWith(expect.anything(), { grid: gridStub, command: 'help' });
+ expect(onColumnSpy).not.toHaveBeenCalled();
+ expect(onBeforeSpy).not.toHaveBeenCalled();
+ expect(onCloseSpy).not.toHaveBeenCalled();
+ });
+
+ 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 autoSizeSpy = jest.spyOn(gridStub, 'autosizeColumns');
+
+ const instance = extension.register();
+ instance.onColumnsChanged.notify(columnsMock.slice(0, 1), new Slick.EventData(), gridStub);
+ instance.onMenuClose.notify({ grid: gridStub, menu: {} }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalled();
+ expect(onCloseSpy).toHaveBeenCalled();
+ expect(onColumnSpy).toHaveBeenCalled();
+ expect(autoSizeSpy).toHaveBeenCalled();
+ });
+
+ it('should dispose of the addon', () => {
+ const instance = extension.register();
+ const destroySpy = jest.spyOn(instance, 'destroy');
+
+ extension.dispose();
+
+ expect(destroySpy).toHaveBeenCalled();
+ });
+ });
+
+ describe('addGridMenuCustomCommands method', () => {
+ afterEach(() => {
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+ });
+
+ it('should expect an empty "customItems" array when both Filter & Sort are disabled', () => {
+ extension.register();
+ expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([]);
+ });
+
+ it('should expect all menu related to Filter when "enableFilering" is set', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, enableFiltering: true } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ extension.register();
+ expect(SharedService.prototype.gridOptions.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: 54 }
+ ]);
+ });
+
+ 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, gridMenu: { hideToggleFilterCommand: true, hideRefreshDatasetCommand: true } } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ extension.register();
+ expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([
+ { iconCssClass: 'fa fa-filter text-danger', title: 'Supprimer tous les filtres', disabled: false, command: 'clear-filter', positionOrder: 50 }
+ ]);
+ });
+
+ 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, gridMenu: { hideClearAllFiltersCommand: true, hideRefreshDatasetCommand: true } } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ extension.register();
+ expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([
+ { iconCssClass: 'fa fa-random', title: 'Basculer la ligne des filtres', disabled: false, command: 'toggle-filter', positionOrder: 52 },
+ ]);
+ });
+
+ 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, gridMenu: { hideClearAllFiltersCommand: true, hideToggleFilterCommand: true } } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ extension.register();
+ expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([
+ { iconCssClass: 'fa fa-refresh', title: 'Rafraîchir les données', disabled: false, command: 'refresh-dataset', positionOrder: 54 }
+ ]);
+ });
+
+ it('should have the "toggle-preheader" menu command when "showPreHeaderPanel" is set', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, showPreHeaderPanel: true } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ extension.register();
+ 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 }
+ ]);
+ });
+
+ 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;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ extension.register();
+ expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([]);
+ });
+
+ it('should have the "clear-sorting" menu command when "enableSorting" is set', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ extension.register();
+ expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([
+ { iconCssClass: 'fa fa-unsorted text-danger', title: 'Supprimer tous les tris', disabled: false, command: 'clear-sorting', positionOrder: 51 }
+ ]);
+ });
+
+ 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;
+ 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: { hideExportTextDelimitedCommand: true } } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ extension.register();
+ expect(SharedService.prototype.gridOptions.gridMenu.customItems).toEqual([
+ { iconCssClass: 'fa fa-download', title: 'Exporter en format CSV', disabled: false, command: 'export-csv', positionOrder: 53 }
+ ]);
+ });
+
+ it('should not have the "export-csv" menu command when "enableExport" and "hideExportCsvCommand" are set', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { 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-text-delimited" menu command when "enableExport" is set', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { hideExportCsvCommand: true } } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ extension.register();
+ 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: 54 }
+ ]);
+ });
+
+ it('should not have the "export-text-delimited" menu command when "enableExport" and "hideExportCsvCommand" are set', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, enableExport: true, gridMenu: { 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([]);
+ });
+ });
+
+ describe('refreshBackendDataset method', () => {
+ afterEach(() => {
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+ });
+
+ it('should throw an error when backendServiceApi is not provided in the grid options', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, backendServiceApi: {} } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ expect(() => extension.refreshBackendDataset()).toThrowError(`BackendServiceApi requires at least a "process" function and a "service" defined`);
+ });
+
+ it('should call the backend service API to refresh the dataset', (done) => {
+ const now = new Date();
+ const query = `query { users (first:20,offset:0) { totalCount, nodes { id,name,gender,company } } }`;
+ const processResult = {
+ data: { users: { nodes: [] }, pageInfo: { hasNextPage: true }, totalCount: 0 },
+ statistics: { startTime: now, endTime: now, executionTime: 0, totalItemCount: 0 }
+ };
+ const preSpy = jest.spyOn(gridOptionsMock.backendServiceApi, 'preProcess');
+ const postSpy = jest.spyOn(gridOptionsMock.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);
+ extension.refreshBackendDataset({ enableAddRow: true });
+ expect(preSpy).toHaveBeenCalled();
+ expect(processSpy).toHaveBeenCalled();
+ promise.then(() => {
+ expect(postSpy).toHaveBeenCalledWith(processResult);
+ done();
+ });
+ });
+ });
+
+ describe('executeGridMenuInternalCustomCommands method', () => {
+ 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 instance = extension.register();
+ instance.onCommand.notify({ grid: gridStub, command: 'clear-filter' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(filterSpy).toHaveBeenCalled();
+ expect(refreshSpy).toHaveBeenCalled();
+ });
+
+ 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 instance = extension.register();
+ instance.onCommand.notify({ grid: gridStub, command: 'clear-sorting' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(sortSpy).toHaveBeenCalled();
+ expect(refreshSpy).toHaveBeenCalled();
+ });
+
+ 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 instance = extension.register();
+ instance.onCommand.notify({ grid: gridStub, command: 'export-csv' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(exportSpy).toHaveBeenCalledWith({
+ delimiter: DelimiterType.comma,
+ filename: 'export',
+ format: FileType.csv,
+ useUtf8WithBom: true
+ });
+ });
+
+ 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 instance = extension.register();
+ instance.onCommand.notify({ grid: gridStub, command: 'export-text-delimited' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(exportSpy).toHaveBeenCalledWith({
+ delimiter: DelimiterType.tab,
+ filename: 'export',
+ format: FileType.txt,
+ useUtf8WithBom: true
+ });
+ });
+
+ 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 instance = extension.register();
+ instance.onCommand.notify({ grid: gridStub, command: 'toggle-filter' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(gridSpy).toHaveBeenCalledWith(true);
+
+ gridOptionsMock.showHeaderRow = true;
+ instance.onCommand.notify({ grid: gridStub, command: 'toggle-filter' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(gridSpy).toHaveBeenCalledWith(false);
+ });
+
+ 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 instance = extension.register();
+ instance.onCommand.notify({ grid: gridStub, command: 'toggle-toppanel' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(gridSpy).toHaveBeenCalledWith(true);
+
+ gridOptionsMock.showTopPanel = true;
+ instance.onCommand.notify({ grid: gridStub, command: 'toggle-toppanel' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(gridSpy).toHaveBeenCalledWith(false);
+ });
+
+ 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 instance = extension.register();
+ instance.onCommand.notify({ grid: gridStub, command: 'toggle-preheader' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(gridSpy).toHaveBeenCalledWith(true);
+
+ gridOptionsMock.showPreHeaderPanel = true;
+ instance.onCommand.notify({ grid: gridStub, command: 'toggle-preheader' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(gridSpy).toHaveBeenCalledWith(false);
+ });
+
+ 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 instance = extension.register();
+ instance.onCommand.notify({ grid: gridStub, command: 'refresh-dataset' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(refreshSpy).toHaveBeenCalled();
+ });
+ });
+
+ describe('translateGridMenu method', () => {
+ it('should translate the column picker header titles', () => {
+ const utilitySpy = jest.spyOn(extensionUtility, 'getPickerTitleOutputString');
+ const translateSpy = jest.spyOn(extensionUtility, 'translateItems');
+
+ const instance = extension.register();
+ extension.translateGridMenu();
+ const initSpy = jest.spyOn(instance, 'init');
+
+ expect(utilitySpy).toHaveBeenCalled();
+ expect(translateSpy).toHaveBeenCalled();
+ expect(initSpy).toHaveBeenCalledWith(gridStub);
+ 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(columnsMock).toEqual([
+ { id: 'field1', field: 'field1', width: 100, name: 'Titre', headerKey: 'TITLE' },
+ { id: 'field2', field: 'field2', width: 75 }
+ ]);
+ });
+ });
+
+ describe('showGridMenu method', () => {
+ it('should call the show grid menu', () => {
+ const instance = extension.register();
+
+ const showSpy = jest.spyOn(instance, 'showGridMenu');
+ extension.showGridMenu(null);
+
+ expect(showSpy).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/aurelia-slickgrid/extensions/__tests__/headerMenuExtension.spec.ts b/src/aurelia-slickgrid/extensions/__tests__/headerMenuExtension.spec.ts
new file mode 100644
index 000000000..e6adbbab8
--- /dev/null
+++ b/src/aurelia-slickgrid/extensions/__tests__/headerMenuExtension.spec.ts
@@ -0,0 +1,508 @@
+import { I18N } from 'aurelia-i18n';
+import { EventAggregator } from 'aurelia-event-aggregator';
+import { BindingSignaler } from 'aurelia-templating-resources';
+import { GridOption } from '../../models/gridOption.interface';
+import { HeaderMenuExtension } from '../headerMenuExtension';
+import { ExtensionUtility } from '../extensionUtility';
+import { SharedService } from '../../services/shared.service';
+import { Column, ColumnSort } from '../../models';
+import { ExportService, FilterService, SortService } from '../../services';
+
+declare var Slick: any;
+jest.mock('flatpickr', () => { });
+
+const exportServiceStub = {
+ exportToFile: jest.fn(),
+} as unknown as ExportService;
+
+const filterServiceStub = {
+ clearFilterByColumnId: jest.fn(),
+} as unknown as FilterService;
+
+const sortServiceStub = {
+ clearSorting: jest.fn(),
+ getPreviousColumnSorts: jest.fn(),
+ onBackendSortChanged: jest.fn(),
+ onLocalSortChanged: jest.fn(),
+} as unknown as SortService;
+
+const dataViewStub = {
+ refresh: jest.fn(),
+};
+
+const gridStub = {
+ autosizeColumns: jest.fn(),
+ getColumnIndex: jest.fn(),
+ getColumns: jest.fn(),
+ getOptions: jest.fn(),
+ registerPlugin: jest.fn(),
+ setColumns: jest.fn(),
+ setHeaderRowVisibility: jest.fn(),
+ setTopPanelVisibility: jest.fn(),
+ setPreHeaderPanelVisibility: jest.fn(),
+ setSortColumns: jest.fn(),
+ onSort: new Slick.Event(),
+};
+
+const mockAddon = jest.fn().mockImplementation(() => ({
+ init: jest.fn(),
+ destroy: jest.fn(),
+ onBeforeMenuShow: new Slick.Event(),
+ onColumnsChanged: new Slick.Event(),
+ onCommand: new Slick.Event(),
+}));
+
+jest.mock('slickgrid/plugins/slick.headermenu', () => mockAddon);
+Slick.Plugins = {
+ HeaderMenu: mockAddon
+};
+
+describe('headerMenuExtension', () => {
+ const columnsMock: Column[] = [{ id: 'field1', field: 'field1', width: 100, headerKey: 'TITLE' }, { id: 'field2', field: 'field2', width: 75 }];
+ let extensionUtility: ExtensionUtility;
+ let i18n: I18N;
+ let extension: HeaderMenuExtension;
+ let sharedService: SharedService;
+
+ const gridOptionsMock = {
+ enableAutoSizeColumns: true,
+ enableHeaderMenu: true,
+ enableTranslate: true,
+ backendServiceApi: {
+ service: {
+ buildQuery: jest.fn(),
+ },
+ internalPostProcess: jest.fn(),
+ preProcess: jest.fn(),
+ process: jest.fn(),
+ postProcess: jest.fn(),
+ },
+ headerMenu: {
+ hideForceFitButton: false,
+ hideSyncResizeButton: true,
+ onExtensionRegistered: jest.fn(),
+ onCommand: (e, args: { command: any, item: any, grid: any }) => { },
+ onBeforeMenuShow: (e, args: { menu: any, grid: any }) => { },
+ },
+ multiColumnSort: true,
+ pagination: {
+ totalItems: 0
+ },
+ showHeaderRow: false,
+ showTopPanel: false,
+ showPreHeaderPanel: false
+ } as unknown as GridOption;
+
+ beforeEach(() => {
+ sharedService = new SharedService();
+ i18n = new I18N(new EventAggregator(), new BindingSignaler());
+ extensionUtility = new ExtensionUtility(i18n, sharedService);
+ extension = new HeaderMenuExtension(extensionUtility, filterServiceStub, i18n, sharedService, sortServiceStub);
+ i18n.setup({
+ resources: {
+ en: {
+ translation: {
+ TITLE: 'Title',
+ COMMANDS: 'Commands',
+ COLUMNS: 'Columns',
+ FORCE_FIT_COLUMNS: 'Force fit columns',
+ SYNCHRONOUS_RESIZE: 'Synchronous resize',
+ HIDE_COLUMN: 'Hide Column',
+ REMOVE_FILTER: 'Remove Filter',
+ REMOVE_SORT: 'Remove Sort',
+ SORT_ASCENDING: 'Sort Ascending',
+ SORT_DESCENDING: 'Sort Descending',
+ }
+ },
+ fr: {
+ translation: {
+ TITLE: 'Titre',
+ COMMANDS: 'Commandes',
+ COLUMNS: 'Colonnes',
+ FORCE_FIT_COLUMNS: 'Ajustement forcé des colonnes',
+ SYNCHRONOUS_RESIZE: 'Redimension synchrone',
+ HIDE_COLUMN: 'Cacher la colonne',
+ REMOVE_FILTER: 'Supprimer le filtre',
+ REMOVE_SORT: 'Supprimer le tri',
+ SORT_ASCENDING: 'Trier par ordre croissant',
+ SORT_DESCENDING: 'Trier par ordre décroissant',
+ }
+ }
+ },
+ lng: '0',
+ fallbackLng: 'en',
+ debug: false
+ });
+ i18n.setLocale('fr');
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should return null when either the grid object or the grid options is missing', () => {
+ const output = extension.register();
+ expect(output).toBeNull();
+ });
+
+ describe('registered addon', () => {
+ beforeEach(() => {
+ jest.spyOn(SharedService.prototype, 'dataView', 'get').mockReturnValue(dataViewStub);
+ jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub);
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+ jest.spyOn(SharedService.prototype, 'allColumns', 'get').mockReturnValue(columnsMock);
+ jest.spyOn(SharedService.prototype, 'visibleColumns', 'get').mockReturnValue(columnsMock);
+ jest.spyOn(SharedService.prototype, 'columnDefinitions', 'get').mockReturnValue(columnsMock);
+ });
+
+ it('should register the addon', () => {
+ const pluginSpy = jest.spyOn(SharedService.prototype.grid, 'registerPlugin');
+ const onRegisteredSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onExtensionRegistered');
+
+ const instance = extension.register();
+
+ expect(mockAddon).toHaveBeenCalledWith({
+ autoAlignOffset: 12,
+ minWidth: 140,
+ hideColumnHideCommand: false,
+ hideForceFitButton: false,
+ hideSyncResizeButton: true,
+ hideSortCommands: false,
+ title: '',
+ onCommand: expect.anything(),
+ onBeforeMenuShow: expect.anything(),
+ onExtensionRegistered: expect.anything(),
+ });
+ expect(onRegisteredSpy).toHaveBeenCalledWith(instance);
+ expect(pluginSpy).toHaveBeenCalledWith(instance);
+ });
+
+ 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 onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onBeforeMenuShow');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+
+ const instance = extension.register();
+ instance.onBeforeMenuShow.notify({ grid: gridStub, menu: {} }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalledTimes(2);
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onBeforeSpy).toHaveBeenCalledWith(expect.anything(), { grid: gridStub, menu: {} });
+ expect(onCommandSpy).not.toHaveBeenCalled();
+ });
+
+ 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 onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onBeforeMenuShow');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ grid: gridStub, command: 'help' }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalledTimes(2);
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onCommandSpy).toHaveBeenCalledWith(expect.anything(), { grid: gridStub, command: 'help' });
+ expect(onBeforeSpy).not.toHaveBeenCalled();
+ });
+
+ it('should dispose of the addon', () => {
+ const instance = extension.register();
+ const destroySpy = jest.spyOn(instance, 'destroy');
+
+ extension.dispose();
+
+ expect(destroySpy).toHaveBeenCalled();
+ });
+ });
+
+ describe('addHeaderMenuCustomCommands method', () => {
+ const mockColumn = { id: 'field1', field: 'field1', width: 100, headerKey: 'TITLE', sortable: true, filterable: true } as any;
+
+ beforeEach(() => {
+ jest.spyOn(SharedService.prototype, 'columnDefinitions', 'get').mockReturnValue([mockColumn]);
+ });
+ afterEach(() => {
+ mockColumn.header = undefined;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+ });
+
+ it('should have the command "hide-column" in the header menu list', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, headerMenu: { hideColumnHideCommand: 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-times', title: 'Cacher la colonne', command: 'hide', positionOrder: 55 }
+ ]);
+ });
+
+ it('should expect all menu related to Sorting when "enableSorting" is set', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true, headerMenu: { hideColumnHideCommand: true } } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+
+ extension.register();
+
+ const headerMenuExpected = [
+ { 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 },
+ { iconCssClass: 'fa fa-unsorted', title: 'Supprimer le tri', command: 'clear-sort', positionOrder: 54 }
+ ];
+ expect(mockColumn.header.menu.items).not.toBeNull();
+ expect(mockColumn.header.menu.items).toEqual(headerMenuExpected);
+
+ // double-check that registering again won't add duplicate commands
+ extension.register();
+ expect(mockColumn.header.menu.items).toEqual(headerMenuExpected);
+ });
+
+ it('should expect only the "hide-column" command in the menu when "enableSorting" and "hideSortCommands" are set', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, enableSorting: true } as unknown as GridOption;
+ copyGridOptionsMock.headerMenu.hideColumnHideCommand = false;
+ copyGridOptionsMock.headerMenu.hideSortCommands = true;
+ 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-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;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+
+ extension.register();
+
+ const headerMenuExpected = [{ iconCssClass: 'fa fa-filter', title: 'Supprimer le filtre', command: 'clear-filter', positionOrder: 53 }];
+ expect(mockColumn.header.menu.items).not.toBeNull();
+ expect(mockColumn.header.menu.items).toEqual(headerMenuExpected);
+
+ // double-check that registering again won't add duplicate commands
+ extension.register();
+ expect(mockColumn.header.menu.items).toEqual(headerMenuExpected);
+ });
+ });
+
+ describe('hideColumn method', () => {
+ it('should call hideColumn and expect "visibleColumns" to be updated accordingly', () => {
+ jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub);
+ jest.spyOn(gridStub, 'getColumnIndex').mockReturnValue(1);
+ jest.spyOn(gridStub, 'getColumns').mockReturnValue(columnsMock);
+ const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
+ const visibleSpy = jest.spyOn(SharedService.prototype, 'visibleColumns', 'set');
+ const updatedColumnsMock = [{
+ id: 'field1', field: 'field1', headerKey: 'TITLE', width: 100,
+ header: { menu: { items: [{ command: 'hide', iconCssClass: 'fa fa-times', positionOrder: 55, title: 'Cacher la colonne' }] } }
+ }] as Column[];
+
+ extension.hideColumn(columnsMock[1]);
+
+ expect(visibleSpy).toHaveBeenCalledWith(updatedColumnsMock);
+ expect(setColumnsSpy).toHaveBeenCalledWith(updatedColumnsMock);
+ });
+ });
+
+ describe('translateHeaderMenu method', () => {
+ it('should call the resetHeaderMenuTranslations and have all header menu translated', () => {
+ const mockColumns: Column[] = [{
+ id: 'field1', field: 'field1', width: 100,
+ header: {
+ menu: {
+ items: [
+ { 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 },
+ { iconCssClass: 'fa fa-filter', title: 'Supprimer le filtre', command: 'clear-filter', positionOrder: 53 },
+ { iconCssClass: 'fa fa-unsorted', title: 'Supprimer le tri', command: 'clear-sort', positionOrder: 54 },
+ { iconCssClass: 'fa fa-times', command: 'hide', positionOrder: 55, title: 'Cacher la colonne' },
+ ]
+ }
+ }
+ }];
+ jest.spyOn(SharedService.prototype, 'visibleColumns', 'get').mockReturnValue(mockColumns);
+
+ i18n.setLocale('en');
+ extension.translateHeaderMenu();
+
+ expect(mockColumns).toEqual([{
+ id: 'field1', field: 'field1', width: 100,
+ header: {
+ menu: {
+ items: [
+ { 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 },
+ { iconCssClass: 'fa fa-filter', title: 'Remove Filter', command: 'clear-filter', positionOrder: 53 },
+ { iconCssClass: 'fa fa-unsorted', title: 'Remove Sort', command: 'clear-sort', positionOrder: 54 },
+ { iconCssClass: 'fa fa-times', command: 'hide', positionOrder: 55, title: 'Hide Column' },
+ ]
+ }
+ }
+ }]);
+ });
+ });
+
+ describe('executeHeaderMenuInternalCommands method', () => {
+ 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');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: columnsMock[0], grid: gridStub, command: 'hide' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(autosizeSpy).toHaveBeenCalled();
+ });
+
+ it('should trigger the command "clear-filter" and expect "clearColumnFilter" method being called with dataview refresh', () => {
+ const filterSpy = jest.spyOn(filterServiceStub, 'clearFilterByColumnId');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: columnsMock[0], grid: gridStub, command: 'clear-filter' }, new Slick.EventData(), gridStub);
+
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(filterSpy).toHaveBeenCalledWith(expect.anything(), columnsMock[0].id);
+ });
+
+ it('should trigger the command "clear-sort" and expect Sort Service to call "onBackendSortChanged" being called without the sorted column', () => {
+ const mockSortedCols: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: false, sortCol: { id: 'field2', field: 'field2' } }];
+ const previousSortSpy = jest.spyOn(sortServiceStub, 'getPreviousColumnSorts').mockReturnValue([mockSortedCols[1]]).mockReturnValueOnce(mockSortedCols).mockRejectedValueOnce([mockSortedCols[1]]);
+ const backendSortSpy = jest.spyOn(sortServiceStub, 'onBackendSortChanged');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+ const setSortSpy = jest.spyOn(SharedService.prototype.grid, 'setSortColumns');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: columnsMock[0], grid: gridStub, command: 'clear-sort' }, new Slick.EventData(), gridStub);
+
+ expect(previousSortSpy).toHaveBeenCalled();
+ expect(backendSortSpy).toHaveBeenCalledWith(expect.anything(), { multiColumnSort: true, sortCols: [mockSortedCols[1]], grid: gridStub });
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(setSortSpy).toHaveBeenCalled();
+ });
+
+ it('should trigger the command "clear-sort" and expect Sort Service to call "onLocalSortChanged" being called without the sorted column', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, backendServiceApi: undefined } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ const mockSortedCols: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: false, sortCol: { id: 'field2', field: 'field2' } }];
+ const previousSortSpy = jest.spyOn(sortServiceStub, 'getPreviousColumnSorts').mockReturnValue([mockSortedCols[1]]).mockReturnValueOnce(mockSortedCols).mockRejectedValueOnce([mockSortedCols[1]]);
+ const localSortSpy = jest.spyOn(sortServiceStub, 'onLocalSortChanged');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+ const setSortSpy = jest.spyOn(SharedService.prototype.grid, 'setSortColumns');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: columnsMock[0], grid: gridStub, command: 'clear-sort' }, new Slick.EventData(), gridStub);
+
+ expect(previousSortSpy).toHaveBeenCalled();
+ expect(localSortSpy).toHaveBeenCalledWith(gridStub, dataViewStub, [mockSortedCols[1]], true);
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(setSortSpy).toHaveBeenCalled();
+ });
+
+ it('should trigger the command "clear-sort" and expect "onSort" event triggered when no DataView is provided', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, backendServiceApi: undefined } as unknown as GridOption;
+ const mockSortedCols: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: false, sortCol: { id: 'field2', field: 'field2' } }];
+
+ jest.spyOn(SharedService.prototype, 'dataView', 'get').mockReturnValue(undefined);
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ const previousSortSpy = jest.spyOn(sortServiceStub, 'getPreviousColumnSorts').mockReturnValue([mockSortedCols[1]]).mockReturnValueOnce(mockSortedCols).mockRejectedValueOnce([mockSortedCols[1]]);
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+ const setSortSpy = jest.spyOn(SharedService.prototype.grid, 'setSortColumns');
+ const gridSortSpy = jest.spyOn(gridStub.onSort, 'notify');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: columnsMock[0], grid: gridStub, command: 'clear-sort' }, new Slick.EventData(), gridStub);
+
+ expect(previousSortSpy).toHaveBeenCalled();
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(setSortSpy).toHaveBeenCalled();
+ expect(gridSortSpy).toHaveBeenCalledWith([mockSortedCols[1]]);
+ });
+
+ it('should trigger the command "sort-asc" and expect Sort Service to call "onBackendSortChanged" being called without the sorted column', () => {
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+ const mockSortedCols: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: false, sortCol: { id: 'field2', field: 'field2' } }];
+ const mockSortedOuput: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: true, sortCol: { id: 'field2', field: 'field2' } }];
+ const previousSortSpy = jest.spyOn(sortServiceStub, 'getPreviousColumnSorts').mockReturnValue([mockSortedCols[0]]);
+ const backendSortSpy = jest.spyOn(sortServiceStub, 'onBackendSortChanged');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+ const setSortSpy = jest.spyOn(SharedService.prototype.grid, 'setSortColumns');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: mockSortedCols[1].sortCol, grid: gridStub, command: 'sort-asc' }, new Slick.EventData(), gridStub);
+
+ expect(previousSortSpy).toHaveBeenCalled();
+ expect(backendSortSpy).toHaveBeenCalledWith(expect.anything(), { multiColumnSort: true, sortCols: mockSortedOuput, grid: gridStub });
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(setSortSpy).toHaveBeenCalled();
+ });
+
+ it('should trigger the command "sort-desc" and expect Sort Service to call "onBackendSortChanged" being called without the sorted column', () => {
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+ const mockSortedCols: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: true, sortCol: { id: 'field2', field: 'field2' } }];
+ const mockSortedOuput: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: false, sortCol: { id: 'field2', field: 'field2' } }];
+ const previousSortSpy = jest.spyOn(sortServiceStub, 'getPreviousColumnSorts').mockReturnValue([mockSortedCols[0]]);
+ const backendSortSpy = jest.spyOn(sortServiceStub, 'onBackendSortChanged');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+ const setSortSpy = jest.spyOn(SharedService.prototype.grid, 'setSortColumns');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: mockSortedCols[1].sortCol, grid: gridStub, command: 'sort-desc' }, new Slick.EventData(), gridStub);
+
+ expect(previousSortSpy).toHaveBeenCalled();
+ expect(backendSortSpy).toHaveBeenCalledWith(expect.anything(), { multiColumnSort: true, sortCols: mockSortedOuput, grid: gridStub });
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(setSortSpy).toHaveBeenCalled();
+ });
+
+ it('should trigger the command "sort-desc" and expect Sort Service to call "onLocalSortChanged" being called without the sorted column', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, backendServiceApi: undefined } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'dataView', 'get').mockReturnValue(dataViewStub);
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ const mockSortedCols: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: true, sortCol: { id: 'field2', field: 'field2' } }];
+ const mockSortedOuput: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: false, sortCol: { id: 'field2', field: 'field2' } }];
+ const previousSortSpy = jest.spyOn(sortServiceStub, 'getPreviousColumnSorts').mockReturnValue([mockSortedCols[0]]);
+ const localSortSpy = jest.spyOn(sortServiceStub, 'onLocalSortChanged');
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+ const setSortSpy = jest.spyOn(SharedService.prototype.grid, 'setSortColumns');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: mockSortedCols[1].sortCol, grid: gridStub, command: 'sort-desc' }, new Slick.EventData(), gridStub);
+
+ expect(previousSortSpy).toHaveBeenCalled();
+ expect(localSortSpy).toHaveBeenCalledWith(gridStub, dataViewStub, mockSortedOuput);
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(setSortSpy).toHaveBeenCalled();
+ });
+
+ it('should trigger the command "sort-desc" and expect "onSort" event triggered when no DataView is provided', () => {
+ const copyGridOptionsMock = { ...gridOptionsMock, backendServiceApi: undefined } as unknown as GridOption;
+ jest.spyOn(SharedService.prototype, 'dataView', 'get').mockReturnValue(undefined);
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(copyGridOptionsMock);
+ const mockSortedCols: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: true, sortCol: { id: 'field2', field: 'field2' } }];
+ const mockSortedOuput: ColumnSort[] = [{ sortAsc: true, sortCol: { id: 'field1', field: 'field1' } }, { sortAsc: false, sortCol: { id: 'field2', field: 'field2' } }];
+ const previousSortSpy = jest.spyOn(sortServiceStub, 'getPreviousColumnSorts').mockReturnValue([mockSortedCols[0]]);
+ const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.headerMenu, 'onCommand');
+ const setSortSpy = jest.spyOn(SharedService.prototype.grid, 'setSortColumns');
+ const gridSortSpy = jest.spyOn(gridStub.onSort, 'notify');
+
+ const instance = extension.register();
+ instance.onCommand.notify({ column: mockSortedCols[1].sortCol, grid: gridStub, command: 'sort-desc' }, new Slick.EventData(), gridStub);
+
+ expect(previousSortSpy).toHaveBeenCalled();
+ expect(gridSortSpy).toHaveBeenCalledWith(mockSortedOuput);
+ expect(onCommandSpy).toHaveBeenCalled();
+ expect(setSortSpy).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/aurelia-slickgrid/extensions/__tests__/rowMoveManagerExtension.spec.ts b/src/aurelia-slickgrid/extensions/__tests__/rowMoveManagerExtension.spec.ts
new file mode 100644
index 000000000..6e2c36610
--- /dev/null
+++ b/src/aurelia-slickgrid/extensions/__tests__/rowMoveManagerExtension.spec.ts
@@ -0,0 +1,136 @@
+import { I18N } from 'aurelia-i18n';
+import { GridOption } from '../../models/gridOption.interface';
+import { RowMoveManagerExtension } from '../rowMoveManagerExtension';
+import { ExtensionUtility } from '../extensionUtility';
+import { SharedService } from '../../services/shared.service';
+
+declare var Slick: any;
+
+const gridStub = {
+ getOptions: jest.fn(),
+ getSelectionModel: jest.fn(),
+ registerPlugin: jest.fn(),
+ setSelectionModel: jest.fn(),
+};
+
+const mockAddon = jest.fn().mockImplementation(() => ({
+ init: jest.fn(),
+ destroy: jest.fn(),
+ onBeforeMoveRows: new Slick.Event(),
+ onMoveRows: new Slick.Event(),
+}));
+
+const mockSelectionModel = jest.fn().mockImplementation(() => ({
+ init: jest.fn(),
+ destroy: jest.fn()
+}));
+
+jest.mock('slickgrid/plugins/slick.rowmovemanager', () => mockAddon);
+Slick.RowMoveManager = mockAddon;
+
+jest.mock('slickgrid/plugins/slick.rowselectionmodel', () => mockSelectionModel);
+Slick.RowSelectionModel = mockSelectionModel;
+
+describe('rowMoveManagerExtension', () => {
+ let extensionUtility: ExtensionUtility;
+ let sharedService: SharedService;
+ let extension: RowMoveManagerExtension;
+ const gridOptionsMock = {
+ enableRowMoveManager: true,
+ rowMoveManager: {
+ onExtensionRegistered: jest.fn(),
+ onBeforeMoveRows: (e, args: { insertBefore: number; rows: number[]; }) => { },
+ onMoveRows: (e, args: { insertBefore: number; rows: number[]; }) => { },
+ },
+ } as GridOption;
+
+ beforeEach(() => {
+ sharedService = new SharedService();
+ extensionUtility = new ExtensionUtility({} as I18N, sharedService);
+ extension = new RowMoveManagerExtension(extensionUtility, sharedService);
+ });
+
+ it('should return null when either the grid object or the grid options is missing', () => {
+ const output = extension.register();
+ expect(output).toBeNull();
+ });
+
+ describe('registered addon', () => {
+ beforeEach(() => {
+ jest.spyOn(SharedService.prototype, 'grid', 'get').mockReturnValue(gridStub);
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(gridOptionsMock);
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should register the addon', () => {
+ const onRegisteredSpy = jest.spyOn(SharedService.prototype.gridOptions.rowMoveManager, 'onExtensionRegistered');
+ const pluginSpy = jest.spyOn(SharedService.prototype.grid, 'registerPlugin');
+
+ const instance = extension.register();
+
+ expect(mockAddon).toHaveBeenCalledWith({
+ onExtensionRegistered: expect.anything(),
+ onBeforeMoveRows: expect.anything(),
+ onMoveRows: expect.anything(),
+ });
+ expect(onRegisteredSpy).toHaveBeenCalledWith(instance);
+ expect(pluginSpy).toHaveBeenCalledWith(instance);
+ });
+
+ it('should dispose of the addon', () => {
+ const instance = extension.register();
+ const destroySpy = jest.spyOn(instance, 'destroy');
+
+ extension.dispose();
+
+ expect(destroySpy).toHaveBeenCalled();
+ });
+
+ it('should provide addon options and expect them to be called in the addon constructor', () => {
+ const optionMock = { cancelEditOnDrag: true };
+ const addonOptions = { ...gridOptionsMock, rowMoveManager: optionMock };
+ jest.spyOn(SharedService.prototype, 'gridOptions', 'get').mockReturnValue(addonOptions);
+
+ extension.register();
+
+ expect(mockAddon).toHaveBeenCalledWith(optionMock);
+ });
+
+ it('should call internal event handler subscribe and expect the "onBeforeMoveRows" option to be called when addon notify is called', () => {
+ const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
+ const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.rowMoveManager, 'onBeforeMoveRows');
+ const onMoveSpy = jest.spyOn(SharedService.prototype.gridOptions.rowMoveManager, 'onMoveRows');
+
+ const instance = extension.register();
+ instance.onBeforeMoveRows.notify({ insertBefore: 3, rows: [1] }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalledTimes(2);
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onBeforeSpy).toHaveBeenCalledWith(expect.anything(), { insertBefore: 3, rows: [1] });
+ expect(onMoveSpy).not.toHaveBeenCalled();
+ });
+
+ it('should call internal event handler subscribe and expect the "onMoveRows" option to be called when addon notify is called', () => {
+ const handlerSpy = jest.spyOn(extension.eventHandler, 'subscribe');
+ const onBeforeSpy = jest.spyOn(SharedService.prototype.gridOptions.rowMoveManager, 'onBeforeMoveRows');
+ const onMoveSpy = jest.spyOn(SharedService.prototype.gridOptions.rowMoveManager, 'onMoveRows');
+
+ const instance = extension.register();
+ instance.onMoveRows.notify({ insertBefore: 3, rows: [1] }, new Slick.EventData(), gridStub);
+
+ expect(handlerSpy).toHaveBeenCalledTimes(2);
+ expect(handlerSpy).toHaveBeenCalledWith(
+ { notify: expect.anything(), subscribe: expect.anything(), unsubscribe: expect.anything(), },
+ expect.anything()
+ );
+ expect(onMoveSpy).toHaveBeenCalledWith(expect.anything(), { insertBefore: 3, rows: [1] });
+ expect(onBeforeSpy).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/aurelia-slickgrid/extensions/columnPickerExtension.ts b/src/aurelia-slickgrid/extensions/columnPickerExtension.ts
index 3e5df70a8..3b509ed14 100644
--- a/src/aurelia-slickgrid/extensions/columnPickerExtension.ts
+++ b/src/aurelia-slickgrid/extensions/columnPickerExtension.ts
@@ -1,5 +1,5 @@
import { singleton, inject } from 'aurelia-framework';
-import { CellArgs, Extension, ExtensionName } from '../models/index';
+import { CellArgs, Extension, ExtensionName, SlickEventHandler } from '../models/index';
import { ExtensionUtility } from './extensionUtility';
import { SharedService } from '../services/shared.service';
@@ -9,20 +9,26 @@ declare var Slick: any;
@singleton(true)
@inject(ExtensionUtility, SharedService)
export class ColumnPickerExtension implements Extension {
- private _eventHandler: any = new Slick.EventHandler();
- private _extension: any;
+ private _addon: any;
+ private _eventHandler: SlickEventHandler;
constructor(
private extensionUtility: ExtensionUtility,
private sharedService: SharedService,
- ) { }
+ ) {
+ this._eventHandler = new Slick.EventHandler();
+ }
+
+ get eventHandler(): SlickEventHandler {
+ return this._eventHandler;
+ }
dispose() {
// unsubscribe all SlickGrid events
this._eventHandler.unsubscribeAll();
- if (this._extension && this._extension.destroy) {
- this._extension.destroy();
+ if (this._addon && this._addon.destroy) {
+ this._addon.destroy();
}
}
@@ -41,23 +47,27 @@ export class ColumnPickerExtension implements Extension {
this.sharedService.gridOptions.columnPicker.forceFitTitle = this.sharedService.gridOptions.columnPicker.forceFitTitle || forceFitTitle;
this.sharedService.gridOptions.columnPicker.syncResizeTitle = this.sharedService.gridOptions.columnPicker.syncResizeTitle || syncResizeTitle;
- this._extension = new Slick.Controls.ColumnPicker(this.sharedService.columnDefinitions, this.sharedService.grid, this.sharedService.gridOptions);
+ this._addon = new Slick.Controls.ColumnPicker(this.sharedService.columnDefinitions, this.sharedService.grid, this.sharedService.gridOptions);
if (this.sharedService.grid && this.sharedService.gridOptions.enableColumnPicker) {
- this._eventHandler.subscribe(this._extension.onColumnsChanged, (e: any, args: CellArgs) => {
+ if (this.sharedService.gridOptions.columnPicker.onExtensionRegistered) {
+ this.sharedService.gridOptions.columnPicker.onExtensionRegistered(this._addon);
+ }
+ this._eventHandler.subscribe(this._addon.onColumnsChanged, (e: any, args: CellArgs) => {
if (this.sharedService.gridOptions.columnPicker && typeof this.sharedService.gridOptions.columnPicker.onColumnsChanged === 'function') {
this.sharedService.gridOptions.columnPicker.onColumnsChanged(e, args);
}
});
}
- return this._extension;
+ return this._addon;
}
+ return null;
}
- /** Translate the Column Picker and it's last 2 checkboxes */
+ /** Translate the Column Picker headers and also the last 2 checkboxes */
translateColumnPicker() {
if (this.sharedService && this.sharedService.grid && this.sharedService.gridOptions) {
- // update the properties by pointers, that is the only way to get Grid Menu Control to see the new values
+ // update the properties by pointers, that is the only way to get Column Picker Control to see the new values
if (this.sharedService.gridOptions.columnPicker) {
this.emptyColumnPickerTitles();
@@ -66,13 +76,13 @@ export class ColumnPickerExtension implements Extension {
this.sharedService.gridOptions.columnPicker.syncResizeTitle = this.extensionUtility.getPickerTitleOutputString('syncResizeTitle', 'columnPicker');
}
- // translate all columns (including non-visible)
+ // translate all columns (including hidden columns)
this.extensionUtility.translateItems(this.sharedService.allColumns, 'headerKey', 'name');
// re-initialize the Column Picker, that will recreate all the list
// doing an "init()" won't drop any existing command attached
- if (this._extension.init) {
- this._extension.init(this.sharedService.grid);
+ if (this._addon.init) {
+ this._addon.init(this.sharedService.grid);
}
}
}
diff --git a/src/aurelia-slickgrid/extensions/draggableGroupingExtension.ts b/src/aurelia-slickgrid/extensions/draggableGroupingExtension.ts
index 247e0e342..7e1371ad3 100644
--- a/src/aurelia-slickgrid/extensions/draggableGroupingExtension.ts
+++ b/src/aurelia-slickgrid/extensions/draggableGroupingExtension.ts
@@ -1,6 +1,6 @@
import { singleton, inject } from 'aurelia-framework';
import { SharedService } from '../services/shared.service';
-import { Extension, ExtensionName, GridOption, Grouping } from '../models/index';
+import { Extension, ExtensionName, GridOption, Grouping, SlickEventHandler } from '../models/index';
import { ExtensionUtility } from './extensionUtility';
// using external non-typed js libraries
@@ -9,17 +9,23 @@ declare var Slick: any;
@singleton(true)
@inject(ExtensionUtility, SharedService)
export class DraggableGroupingExtension implements Extension {
- private _eventHandler: any = new Slick.EventHandler();
- private _extension: any;
+ private _addon: any;
+ private _eventHandler: SlickEventHandler;
- constructor(private extensionUtility: ExtensionUtility, private sharedService: SharedService) { }
+ constructor(private extensionUtility: ExtensionUtility, private sharedService: SharedService) {
+ this._eventHandler = new Slick.EventHandler();
+ }
+
+ get eventHandler(): SlickEventHandler {
+ return this._eventHandler;
+ }
dispose() {
// unsubscribe all SlickGrid events
this._eventHandler.unsubscribeAll();
- if (this._extension && this._extension.destroy) {
- this._extension.destroy();
+ if (this._addon && this._addon.destroy) {
+ this._addon.destroy();
}
}
@@ -31,29 +37,29 @@ export class DraggableGroupingExtension implements Extension {
// dynamically import the SlickGrid plugin (addon) with RequireJS
this.extensionUtility.loadExtensionDynamically(ExtensionName.draggableGrouping);
- if (!this._extension && gridOptions) {
- this._extension = new Slick.DraggableGrouping(gridOptions.draggableGrouping || {});
+ if (!this._addon && gridOptions) {
+ this._addon = new Slick.DraggableGrouping(gridOptions.draggableGrouping || {});
}
- return this._extension;
+ return this._addon;
}
register(): any {
if (this.sharedService && this.sharedService.grid && this.sharedService.gridOptions) {
- this.sharedService.grid.registerPlugin(this._extension);
+ this.sharedService.grid.registerPlugin(this._addon);
// Events
if (this.sharedService.grid && this.sharedService.gridOptions.draggableGrouping) {
if (this.sharedService.gridOptions.draggableGrouping.onExtensionRegistered) {
- this.sharedService.gridOptions.draggableGrouping.onExtensionRegistered(this._extension);
+ this.sharedService.gridOptions.draggableGrouping.onExtensionRegistered(this._addon);
}
- this._eventHandler.subscribe(this._extension.onGroupChanged, (e: any, args: { caller?: string; groupColumns: Grouping[] }) => {
+ this._eventHandler.subscribe(this._addon.onGroupChanged, (e: any, args: { caller?: string; groupColumns: Grouping[] }) => {
if (this.sharedService.gridOptions.draggableGrouping && typeof this.sharedService.gridOptions.draggableGrouping.onGroupChanged === 'function') {
this.sharedService.gridOptions.draggableGrouping.onGroupChanged(e, args);
}
});
}
- return this._extension;
+ return this._addon;
}
return null;
}
diff --git a/src/aurelia-slickgrid/extensions/extensionUtility.ts b/src/aurelia-slickgrid/extensions/extensionUtility.ts
index 82057f320..be81813b4 100644
--- a/src/aurelia-slickgrid/extensions/extensionUtility.ts
+++ b/src/aurelia-slickgrid/extensions/extensionUtility.ts
@@ -17,9 +17,7 @@ export class ExtensionUtility {
* @param index
*/
arrayRemoveItemByIndex(array: any[], index: number) {
- return array.filter((el: any, i: number) => {
- return index !== i;
- });
+ return array.filter((el: any, i: number) => index !== i);
}
/**
@@ -43,7 +41,7 @@ export class ExtensionUtility {
require('slickgrid/controls/slick.columnpicker');
break;
case ExtensionName.draggableGrouping:
- require('slickgrid/plugins/slick.draggablegrouping.js');
+ require('slickgrid/plugins/slick.draggablegrouping');
break;
case ExtensionName.gridMenu:
require('slickgrid/controls/slick.gridmenu');
@@ -61,10 +59,10 @@ export class ExtensionUtility {
require('slickgrid/plugins/slick.rowselectionmodel');
break;
case ExtensionName.rowDetailView:
- require('slickgrid/plugins/slick.rowdetailview.js');
+ require('slickgrid/plugins/slick.rowdetailview');
break;
case ExtensionName.rowMoveManager:
- require('slickgrid/plugins/slick.rowmovemanager.js');
+ require('slickgrid/plugins/slick.rowmovemanager');
break;
}
} catch (e) {
@@ -112,10 +110,9 @@ export class ExtensionUtility {
}
/**
- * Sort items in an array by a property name
+ * Sort items (by pointers) in an array by a property name
* @params items array
* @param property name to sort with
- * @return sorted array
*/
sortItems(items: any[], propertyName: string) {
// sort the custom items by their position in the list
@@ -129,9 +126,11 @@ export class ExtensionUtility {
/** Translate the an array of items from an input key and assign to the output key */
translateItems(items: any[], inputKey: string, outputKey: string) {
- for (const item of items) {
- if (item[inputKey]) {
- item[outputKey] = this.i18n.tr(item[inputKey]);
+ if (Array.isArray(items)) {
+ for (const item of items) {
+ if (item[inputKey]) {
+ item[outputKey] = this.i18n.tr(item[inputKey]);
+ }
}
}
}
diff --git a/src/aurelia-slickgrid/extensions/gridMenuExtension.ts b/src/aurelia-slickgrid/extensions/gridMenuExtension.ts
index 52a87e75b..0d6f1d8fd 100644
--- a/src/aurelia-slickgrid/extensions/gridMenuExtension.ts
+++ b/src/aurelia-slickgrid/extensions/gridMenuExtension.ts
@@ -3,8 +3,6 @@ import { I18N } from 'aurelia-i18n';
import { Constants } from '../constants';
import {
CellArgs,
- Column,
- ColumnSort,
DelimiterType,
ExtensionName,
Extension,
@@ -13,7 +11,7 @@ import {
GridMenu,
GridMenuItem,
GridOption,
- HeaderMenuOnCommandArgs,
+ SlickEventHandler,
} from '../models/index';
import { ExportService } from '../services/export.service';
import { ExtensionUtility } from './extensionUtility';
@@ -35,8 +33,8 @@ declare var Slick: any;
SortService,
)
export class GridMenuExtension implements Extension {
- private _eventHandler: any = new Slick.EventHandler();
- private _extension: any;
+ private _addon: any;
+ private _eventHandler: SlickEventHandler;
areVisibleColumnDifferent = false;
userOriginalGridMenu: GridMenu;
@@ -47,20 +45,26 @@ export class GridMenuExtension implements Extension {
private i18n: I18N,
private sharedService: SharedService,
private sortService: SortService,
- ) { }
+ ) {
+ this._eventHandler = new Slick.EventHandler();
+ }
+
+ get eventHandler(): SlickEventHandler {
+ return this._eventHandler;
+ }
dispose() {
// unsubscribe all SlickGrid events
this._eventHandler.unsubscribeAll();
- if (this._extension && this._extension.destroy) {
- this._extension.destroy();
+ if (this._addon && this._addon.destroy) {
+ this._addon.destroy();
}
}
/** Show the Grid Menu typically from a button click since we need to know the event */
showGridMenu(e: Event) {
- this._extension.showGridMenu(e);
+ this._addon.showGridMenu(e);
}
/** Create the Header Menu and expose all the available hooks that user can subscribe (onCommand, onBeforeMenuShow, ...) */
@@ -80,31 +84,31 @@ export class GridMenuExtension implements Extension {
this.extensionUtility.translateItems(this.sharedService.gridOptions.gridMenu.customItems, 'titleKey', 'title');
this.extensionUtility.sortItems(this.sharedService.gridOptions.gridMenu.customItems, 'positionOrder');
- this._extension = new Slick.Controls.GridMenu(this.sharedService.columnDefinitions, this.sharedService.grid, this.sharedService.gridOptions);
+ this._addon = new Slick.Controls.GridMenu(this.sharedService.columnDefinitions, this.sharedService.grid, this.sharedService.gridOptions);
// hook all events
if (this.sharedService.grid && this.sharedService.gridOptions.gridMenu) {
if (this.sharedService.gridOptions.gridMenu.onExtensionRegistered) {
- this.sharedService.gridOptions.gridMenu.onExtensionRegistered(this._extension);
+ this.sharedService.gridOptions.gridMenu.onExtensionRegistered(this._addon);
}
- this._eventHandler.subscribe(this._extension.onBeforeMenuShow, (e: any, args: CellArgs) => {
+ this._eventHandler.subscribe(this._addon.onBeforeMenuShow, (e: any, args: CellArgs) => {
if (this.sharedService.gridOptions.gridMenu && typeof this.sharedService.gridOptions.gridMenu.onBeforeMenuShow === 'function') {
this.sharedService.gridOptions.gridMenu.onBeforeMenuShow(e, args);
}
});
- this._eventHandler.subscribe(this._extension.onColumnsChanged, (e: any, args: CellArgs) => {
+ this._eventHandler.subscribe(this._addon.onColumnsChanged, (e: any, args: CellArgs) => {
this.areVisibleColumnDifferent = true;
if (this.sharedService.gridOptions.gridMenu && typeof this.sharedService.gridOptions.gridMenu.onColumnsChanged === 'function') {
this.sharedService.gridOptions.gridMenu.onColumnsChanged(e, args);
}
});
- this._eventHandler.subscribe(this._extension.onCommand, (e: any, args: any) => {
+ this._eventHandler.subscribe(this._addon.onCommand, (e: any, args: any) => {
this.executeGridMenuInternalCustomCommands(e, args);
if (this.sharedService.gridOptions.gridMenu && typeof this.sharedService.gridOptions.gridMenu.onCommand === 'function') {
this.sharedService.gridOptions.gridMenu.onCommand(e, args);
}
});
- this._eventHandler.subscribe(this._extension.onMenuClose, (e: any, args: CellArgs) => {
+ this._eventHandler.subscribe(this._addon.onMenuClose, (e: any, args: CellArgs) => {
if (this.sharedService.gridOptions.gridMenu && typeof this.sharedService.gridOptions.gridMenu.onMenuClose === 'function') {
this.sharedService.gridOptions.gridMenu.onMenuClose(e, args);
}
@@ -122,62 +126,11 @@ export class GridMenuExtension implements Extension {
}
});
}
- return this._extension;
+ return this._addon;
}
return null;
}
- /**
- * Execute the Grid Menu Custom command callback that was triggered by the onCommand subscribe
- * These are the default internal custom commands
- * @param event
- * @param GridMenuItem args
- */
- executeGridMenuInternalCustomCommands(e: Event, args: GridMenuItem) {
- if (args && args.command) {
- switch (args.command) {
- case 'clear-filter':
- this.filterService.clearFilters();
- this.sharedService.dataView.refresh();
- break;
- case 'clear-sorting':
- this.sortService.clearSorting();
- this.sharedService.dataView.refresh();
- break;
- case 'export-csv':
- this.exportService.exportToFile({
- delimiter: DelimiterType.comma,
- filename: 'export',
- format: FileType.csv,
- useUtf8WithBom: true
- });
- break;
- case 'export-text-delimited':
- this.exportService.exportToFile({
- delimiter: DelimiterType.tab,
- filename: 'export',
- format: FileType.txt,
- useUtf8WithBom: true
- });
- break;
- case 'toggle-filter':
- this.sharedService.grid.setHeaderRowVisibility(!this.sharedService.grid.getOptions().showHeaderRow);
- break;
- case 'toggle-toppanel':
- this.sharedService.grid.setTopPanelVisibility(!this.sharedService.grid.getOptions().showTopPanel);
- break;
- case 'toggle-preheader':
- this.sharedService.grid.setPreHeaderPanelVisibility(!this.sharedService.grid.getOptions().showPreHeaderPanel);
- break;
- case 'refresh-dataset':
- this.refreshBackendDataset();
- break;
- default:
- break;
- }
- }
- }
-
/** Refresh the dataset through the Backend Service */
refreshBackendDataset(gridOptions?: GridOption) {
let query = '';
@@ -232,6 +185,39 @@ export class GridMenuExtension implements Extension {
}
}
+ /** Translate the Grid Menu titles and column picker */
+ translateGridMenu() {
+ // update the properties by pointers, that is the only way to get Grid Menu Control to see the new values
+ // we also need to call the control init so that it takes the new Grid object with latest values
+ if (this.sharedService.gridOptions && this.sharedService.gridOptions.gridMenu) {
+ this.sharedService.gridOptions.gridMenu.customItems = [];
+ this.emptyGridMenuTitles();
+
+ // merge original user grid menu items with internal items
+ // then sort all Grid Menu Custom Items (sorted by pointer, no need to use the return)
+ this.sharedService.gridOptions.gridMenu.customItems = [...this.userOriginalGridMenu.customItems || [], ...this.addGridMenuCustomCommands()];
+ this.extensionUtility.translateItems(this.sharedService.gridOptions.gridMenu.customItems, 'titleKey', 'title');
+ this.extensionUtility.sortItems(this.sharedService.gridOptions.gridMenu.customItems, 'positionOrder');
+
+ this.sharedService.gridOptions.gridMenu.columnTitle = this.extensionUtility.getPickerTitleOutputString('columnTitle', 'gridMenu');
+ this.sharedService.gridOptions.gridMenu.forceFitTitle = this.extensionUtility.getPickerTitleOutputString('forceFitTitle', 'gridMenu');
+ this.sharedService.gridOptions.gridMenu.syncResizeTitle = this.extensionUtility.getPickerTitleOutputString('syncResizeTitle', 'gridMenu');
+
+ // translate all columns (including non-visible)
+ this.extensionUtility.translateItems(this.sharedService.allColumns, 'headerKey', 'name');
+
+ // re-initialize the Grid Menu, that will recreate all the menus & list
+ // doing an "init()" won't drop any existing command attached
+ if (this._addon.init) {
+ this._addon.init(this.sharedService.grid);
+ }
+ }
+ }
+
+ // --
+ // private functions
+ // ------------------
+
/** Create Grid Menu with Custom Commands if user has enabled Filters and/or uses a Backend Service (OData, GraphQL) */
private addGridMenuCustomCommands() {
const backendApi = this.sharedService.gridOptions.backendServiceApi || null;
@@ -341,37 +327,53 @@ export class GridMenuExtension implements Extension {
return gridMenuCustomItems;
}
- /** Execute the Header Menu Commands that was triggered by the onCommand subscribe */
- executeHeaderMenuInternalCommands(e: Event, args: HeaderMenuOnCommandArgs) {
+ /**
+ * Execute the Grid Menu Custom command callback that was triggered by the onCommand subscribe
+ * These are the default internal custom commands
+ * @param event
+ * @param GridMenuItem args
+ */
+ private executeGridMenuInternalCustomCommands(e: Event, args: GridMenuItem) {
if (args && args.command) {
switch (args.command) {
- case 'hide':
- this.hideColumn(args.column);
- if (this.sharedService.gridOptions && this.sharedService.gridOptions.enableAutoSizeColumns) {
- this.sharedService.grid.autosizeColumns();
- }
+ case 'clear-filter':
+ this.filterService.clearFilters();
+ this.sharedService.dataView.refresh();
break;
- case 'sort-asc':
- case 'sort-desc':
- // get previously sorted columns
- const cols: ColumnSort[] = this.sortService.getPreviousColumnSorts(args.column.id + '');
-
- // add to the column array, the column sorted by the header menu
- cols.push({ sortCol: args.column, sortAsc: (args.command === 'sort-asc') });
- if (this.sharedService.gridOptions.backendServiceApi) {
- this.sortService.onBackendSortChanged(e, { multiColumnSort: true, sortCols: cols, grid: this.sharedService.grid });
- } else {
- this.sortService.onLocalSortChanged(this.sharedService.grid, this.sharedService.dataView, cols);
- }
-
- // update the this.sharedService.gridObj sortColumns array which will at the same add the visual sort icon(s) on the UI
- const newSortColumns: ColumnSort[] = cols.map((col) => {
- return {
- columnId: col && col.sortCol && col.sortCol.id,
- sortAsc: col && col.sortAsc
- };
+ case 'clear-sorting':
+ this.sortService.clearSorting();
+ this.sharedService.dataView.refresh();
+ break;
+ case 'export-csv':
+ this.exportService.exportToFile({
+ delimiter: DelimiterType.comma,
+ filename: 'export',
+ format: FileType.csv,
+ useUtf8WithBom: true
});
- this.sharedService.grid.setSortColumns(newSortColumns); // add sort icon in UI
+ break;
+ case 'export-text-delimited':
+ this.exportService.exportToFile({
+ delimiter: DelimiterType.tab,
+ filename: 'export',
+ format: FileType.txt,
+ useUtf8WithBom: true
+ });
+ break;
+ case 'toggle-filter':
+ const showHeaderRow = this.sharedService && this.sharedService.gridOptions && this.sharedService.gridOptions.showHeaderRow || false;
+ this.sharedService.grid.setHeaderRowVisibility(!showHeaderRow);
+ break;
+ case 'toggle-toppanel':
+ const showTopPanel = this.sharedService && this.sharedService.gridOptions && this.sharedService.gridOptions.showTopPanel || false;
+ this.sharedService.grid.setTopPanelVisibility(!showTopPanel);
+ break;
+ case 'toggle-preheader':
+ const showPreHeaderPanel = this.sharedService && this.sharedService.gridOptions && this.sharedService.gridOptions.showPreHeaderPanel || false;
+ this.sharedService.grid.setPreHeaderPanelVisibility(!showPreHeaderPanel);
+ break;
+ case 'refresh-dataset':
+ this.refreshBackendDataset();
break;
default:
break;
@@ -379,44 +381,6 @@ export class GridMenuExtension implements Extension {
}
}
- /** Hide a column from the grid */
- hideColumn(column: Column) {
- if (this.sharedService.grid && this.sharedService.grid.getColumns && this.sharedService.grid.setColumns) {
- const columnIndex = this.sharedService.grid.getColumnIndex(column.id);
- this.sharedService.visibleColumns = this.extensionUtility.arrayRemoveItemByIndex(this.sharedService.grid.getColumns(), columnIndex);
- this.sharedService.grid.setColumns(this.sharedService.visibleColumns);
- }
- }
-
- /** Translate the Grid Menu titles and column picker */
- translateGridMenu() {
- // update the properties by pointers, that is the only way to get Grid Menu Control to see the new values
- // we also need to call the control init so that it takes the new Grid object with latest values
- if (this.sharedService.gridOptions && this.sharedService.gridOptions.gridMenu) {
- this.sharedService.gridOptions.gridMenu.customItems = [];
- this.emptyGridMenuTitles();
-
- // merge original user grid menu items with internal items
- // then sort all Grid Menu Custom Items (sorted by pointer, no need to use the return)
- this.sharedService.gridOptions.gridMenu.customItems = [...this.userOriginalGridMenu.customItems || [], ...this.addGridMenuCustomCommands()];
- this.extensionUtility.translateItems(this.sharedService.gridOptions.gridMenu.customItems, 'titleKey', 'title');
- this.extensionUtility.sortItems(this.sharedService.gridOptions.gridMenu.customItems, 'positionOrder');
-
- this.sharedService.gridOptions.gridMenu.columnTitle = this.extensionUtility.getPickerTitleOutputString('columnTitle', 'gridMenu');
- this.sharedService.gridOptions.gridMenu.forceFitTitle = this.extensionUtility.getPickerTitleOutputString('forceFitTitle', 'gridMenu');
- this.sharedService.gridOptions.gridMenu.syncResizeTitle = this.extensionUtility.getPickerTitleOutputString('syncResizeTitle', 'gridMenu');
-
- // translate all columns (including non-visible)
- this.extensionUtility.translateItems(this.sharedService.allColumns, 'headerKey', 'name');
-
- // re-initialize the Grid Menu, that will recreate all the menus & list
- // doing an "init()" won't drop any existing command attached
- if (this._extension.init) {
- this._extension.init(this.sharedService.grid);
- }
- }
- }
-
private emptyGridMenuTitles() {
if (this.sharedService && this.sharedService.gridOptions && this.sharedService.gridOptions.gridMenu) {
this.sharedService.gridOptions.gridMenu.customTitle = '';
@@ -426,9 +390,7 @@ export class GridMenuExtension implements Extension {
}
}
- /**
- * @return default Grid Menu options
- */
+ /** @return default Grid Menu options */
private getDefaultGridMenuOptions(): GridMenu {
return {
customTitle: undefined,
diff --git a/src/aurelia-slickgrid/extensions/headerMenuExtension.ts b/src/aurelia-slickgrid/extensions/headerMenuExtension.ts
index 298740a04..fa48a1250 100644
--- a/src/aurelia-slickgrid/extensions/headerMenuExtension.ts
+++ b/src/aurelia-slickgrid/extensions/headerMenuExtension.ts
@@ -11,6 +11,7 @@ import {
HeaderMenuItem,
HeaderMenuOnCommandArgs,
HeaderMenuOnBeforeMenuShowArgs,
+ SlickEventHandler,
} from '../models/index';
import { FilterService } from '../services/filter.service';
import { SortService } from '../services/sort.service';
@@ -29,8 +30,8 @@ declare var Slick: any;
SortService,
)
export class HeaderMenuExtension implements Extension {
- private _eventHandler: any = new Slick.EventHandler();
- private _extension: any;
+ private _addon: any;
+ private _eventHandler: SlickEventHandler;
constructor(
private extensionUtility: ExtensionUtility,
@@ -38,14 +39,20 @@ export class HeaderMenuExtension implements Extension {
private i18n: I18N,
private sharedService: SharedService,
private sortService: SortService,
- ) { }
+ ) {
+ this._eventHandler = new Slick.EventHandler();
+ }
+
+ get eventHandler(): SlickEventHandler {
+ return this._eventHandler;
+ }
dispose() {
// unsubscribe all SlickGrid events
this._eventHandler.unsubscribeAll();
- if (this._extension && this._extension.destroy) {
- this._extension.destroy();
+ if (this._addon && this._addon.destroy) {
+ this._addon.destroy();
}
}
@@ -64,27 +71,27 @@ export class HeaderMenuExtension implements Extension {
if (this.sharedService.gridOptions.enableHeaderMenu) {
this.sharedService.gridOptions.headerMenu = this.addHeaderMenuCustomCommands(this.sharedService.gridOptions, this.sharedService.columnDefinitions);
}
- this._extension = new Slick.Plugins.HeaderMenu(this.sharedService.gridOptions.headerMenu);
- this.sharedService.grid.registerPlugin(this._extension);
+ this._addon = new Slick.Plugins.HeaderMenu(this.sharedService.gridOptions.headerMenu);
+ this.sharedService.grid.registerPlugin(this._addon);
// hook all events
if (this.sharedService.grid && this.sharedService.gridOptions.headerMenu) {
if (this.sharedService.gridOptions.headerMenu.onExtensionRegistered) {
- this.sharedService.gridOptions.headerMenu.onExtensionRegistered(this._extension);
+ this.sharedService.gridOptions.headerMenu.onExtensionRegistered(this._addon);
}
- this._eventHandler.subscribe(this._extension.onCommand, (event: Event, args: HeaderMenuOnCommandArgs) => {
+ this._eventHandler.subscribe(this._addon.onCommand, (event: Event, args: HeaderMenuOnCommandArgs) => {
this.executeHeaderMenuInternalCommands(event, args);
if (this.sharedService.gridOptions.headerMenu && typeof this.sharedService.gridOptions.headerMenu.onCommand === 'function') {
this.sharedService.gridOptions.headerMenu.onCommand(event, args);
}
});
- this._eventHandler.subscribe(this._extension.onBeforeMenuShow, (event: Event, args: HeaderMenuOnBeforeMenuShowArgs) => {
+ this._eventHandler.subscribe(this._addon.onBeforeMenuShow, (event: Event, args: HeaderMenuOnBeforeMenuShowArgs) => {
if (this.sharedService.gridOptions.headerMenu && typeof this.sharedService.gridOptions.headerMenu.onBeforeMenuShow === 'function') {
this.sharedService.gridOptions.headerMenu.onBeforeMenuShow(event, args);
}
});
}
- return this._extension;
+ return this._addon;
}
return null;
}
@@ -130,7 +137,7 @@ export class HeaderMenuExtension implements Extension {
}
// add a divider (separator) between the top sort commands and the other clear commands
- if (!headerMenuOptions.hideSortCommandsDivider) {
+ if (columnHeaderMenuItems.filter((item: HeaderMenuItem) => item.positionOrder === 52).length === 0) {
columnHeaderMenuItems.push({ divider: true, command: '', positionOrder: 52 });
}
@@ -139,7 +146,7 @@ export class HeaderMenuExtension implements Extension {
iconCssClass: headerMenuOptions.iconClearSortCommand || 'fa fa-unsorted',
title: options.enableTranslate ? this.i18n.tr('REMOVE_SORT') : Constants.TEXT_REMOVE_SORT,
command: 'clear-sort',
- positionOrder: 53
+ positionOrder: 54
});
}
}
@@ -151,7 +158,7 @@ export class HeaderMenuExtension implements Extension {
iconCssClass: headerMenuOptions.iconClearFilterCommand || 'fa fa-filter',
title: options.enableTranslate ? this.i18n.tr('REMOVE_FILTER') : Constants.TEXT_REMOVE_FILTER,
command: 'clear-filter',
- positionOrder: 52
+ positionOrder: 53
});
}
}
@@ -162,19 +169,12 @@ export class HeaderMenuExtension implements Extension {
iconCssClass: headerMenuOptions.iconColumnHideCommand || 'fa fa-times',
title: options.enableTranslate ? this.i18n.tr('HIDE_COLUMN') : Constants.TEXT_HIDE_COLUMN,
command: 'hide',
- positionOrder: 54
+ positionOrder: 55
});
}
this.extensionUtility.translateItems(columnHeaderMenuItems, 'titleKey', 'title');
-
- // sort the custom items by their position in the list
- columnHeaderMenuItems.sort((itemA: any, itemB: any) => {
- if (itemA && itemB && itemA.hasOwnProperty('positionOrder') && itemB.hasOwnProperty('positionOrder')) {
- return itemA.positionOrder - itemB.positionOrder;
- }
- return 0;
- });
+ this.extensionUtility.sortItems(columnHeaderMenuItems, 'positionOrder');
}
});
@@ -183,47 +183,42 @@ export class HeaderMenuExtension implements Extension {
return headerMenuOptions;
}
- /** Execute the Header Menu Commands that was triggered by the onCommand subscribe */
- executeHeaderMenuInternalCommands(event: Event, args: HeaderMenuOnCommandArgs) {
- if (args && args.command) {
- switch (args.command) {
- case 'hide':
- this.hideColumn(args.column);
- if (this.sharedService.gridOptions && this.sharedService.gridOptions.enableAutoSizeColumns) {
- this.sharedService.grid.autosizeColumns();
- }
- break;
- case 'clear-filter':
- this.clearColumnFilter(event, args);
- break;
- case 'clear-sort':
- this.clearColumnSort(event, args);
- break;
- case 'sort-asc':
- case 'sort-desc':
- const isSortingAsc = (args.command === 'sort-asc');
- this.sortColumn(event, args, isSortingAsc);
- break;
- default:
- break;
- }
- }
- }
-
/** Hide a column from the grid */
hideColumn(column: Column) {
- if (this.sharedService.grid && this.sharedService.grid.getColumns && this.sharedService.grid.setColumns) {
+ if (this.sharedService.grid && this.sharedService.grid.getColumns && this.sharedService.grid.setColumns && this.sharedService.grid.getColumnIndex) {
const columnIndex = this.sharedService.grid.getColumnIndex(column.id);
- this.sharedService.visibleColumns = this.extensionUtility.arrayRemoveItemByIndex(this.sharedService.grid.getColumns(), columnIndex);
- this.sharedService.grid.setColumns(this.sharedService.visibleColumns);
+ const currentColumns = this.sharedService.grid.getColumns();
+ const visibleColumns = this.extensionUtility.arrayRemoveItemByIndex(currentColumns, columnIndex);
+ this.sharedService.visibleColumns = visibleColumns;
+ this.sharedService.grid.setColumns(visibleColumns);
+ }
+ }
+
+ /** Translate the Header Menu titles, we need to loop through all column definition to re-translate them */
+ translateHeaderMenu() {
+ if (this.sharedService.gridOptions && this.sharedService.gridOptions.headerMenu) {
+ this.resetHeaderMenuTranslations(this.sharedService.visibleColumns);
}
}
+ /**
+ * @return default Header Menu options
+ */
+ private getDefaultHeaderMenuOptions(): HeaderMenu {
+ return {
+ autoAlignOffset: 12,
+ minWidth: 140,
+ hideColumnHideCommand: false,
+ hideSortCommands: false,
+ title: ''
+ };
+ }
+
/**
* Reset all the Grid Menu options which have text to translate
* @param grid menu object
*/
- resetHeaderMenuTranslations(columnDefinitions: Column[]) {
+ private resetHeaderMenuTranslations(columnDefinitions: Column[]) {
columnDefinitions.forEach((columnDef: Column) => {
if (columnDef && columnDef.header && columnDef.header && columnDef.header.menu && columnDef.header.menu.items) {
if (!columnDef.excludeFromHeaderMenu) {
@@ -258,62 +253,14 @@ export class HeaderMenuExtension implements Extension {
});
}
- /**
- * Translate the Header Menu titles, we need to loop through all column definition to re-translate them
- */
- translateHeaderMenu() {
- if (this.sharedService.gridOptions && this.sharedService.gridOptions.headerMenu) {
- this.resetHeaderMenuTranslations(this.sharedService.visibleColumns);
- }
- }
-
- /**
- * @return default Header Menu options
- */
- private getDefaultHeaderMenuOptions(): HeaderMenu {
- return {
- autoAlignOffset: 12,
- minWidth: 140,
- hideColumnHideCommand: false,
- hideSortCommands: false,
- title: ''
- };
- }
-
- /** Sort the current column */
- private sortColumn(event: Event, args: HeaderMenuOnCommandArgs, isSortingAsc = true) {
- if (args && args.column) {
- // get previously sorted columns
- const sortedColsWithoutCurrent: ColumnSort[] = this.sortService.getPreviousColumnSorts(args.column.id + '');
-
- // add to the column array, the column sorted by the header menu
- sortedColsWithoutCurrent.push({ sortCol: args.column, sortAsc: isSortingAsc });
- if (this.sharedService.gridOptions.backendServiceApi) {
- this.sortService.onBackendSortChanged(event, { multiColumnSort: true, sortCols: sortedColsWithoutCurrent, grid: this.sharedService.grid });
- } else if (this.sharedService.dataView) {
- this.sortService.onLocalSortChanged(this.sharedService.grid, this.sharedService.dataView, sortedColsWithoutCurrent);
- } else {
- // when using customDataView, we will simply send it as a onSort event with notify
- const isMultiSort = this.sharedService && this.sharedService.gridOptions && this.sharedService.gridOptions.multiColumnSort || false;
- const sortOutput = isMultiSort ? sortedColsWithoutCurrent : sortedColsWithoutCurrent[0];
- args.grid.onSort.notify(sortOutput);
- }
-
- // update the this.sharedService.gridObj sortColumns array which will at the same add the visual sort icon(s) on the UI
- const newSortColumns: ColumnSort[] = sortedColsWithoutCurrent.map((col) => {
- return {
- columnId: col && col.sortCol && col.sortCol.id,
- sortAsc: col && col.sortAsc
- };
- });
- this.sharedService.grid.setSortColumns(newSortColumns); // add sort icon in UI
- }
- }
+ // --
+ // private functions
+ // ------------------
/** Clear the Filter on the current column (if it's actually filtered) */
- private clearColumnFilter(e: Event, args: HeaderMenuOnCommandArgs) {
+ private clearColumnFilter(event: Event, args: HeaderMenuOnCommandArgs) {
if (args && args.column) {
- this.filterService.clearFilterByColumnId(args.column.id);
+ this.filterService.clearFilterByColumnId(event, args.column.id);
}
}
@@ -324,7 +271,7 @@ export class HeaderMenuExtension implements Extension {
const allSortedCols: ColumnSort[] = this.sortService.getPreviousColumnSorts();
const sortedColsWithoutCurrent: ColumnSort[] = this.sortService.getPreviousColumnSorts(args.column.id + '');
- if (allSortedCols.length !== sortedColsWithoutCurrent.length) {
+ if (Array.isArray(allSortedCols) && Array.isArray(sortedColsWithoutCurrent) && allSortedCols.length !== sortedColsWithoutCurrent.length) {
if (this.sharedService.gridOptions.backendServiceApi) {
this.sortService.onBackendSortChanged(e, { multiColumnSort: true, sortCols: sortedColsWithoutCurrent, grid: this.sharedService.grid });
} else if (this.sharedService.dataView) {
@@ -347,4 +294,61 @@ export class HeaderMenuExtension implements Extension {
}
}
}
+
+ /** Execute the Header Menu Commands that was triggered by the onCommand subscribe */
+ private executeHeaderMenuInternalCommands(event: Event, args: HeaderMenuOnCommandArgs) {
+ if (args && args.command) {
+ switch (args.command) {
+ case 'hide':
+ this.hideColumn(args.column);
+ if (this.sharedService.gridOptions && this.sharedService.gridOptions.enableAutoSizeColumns) {
+ this.sharedService.grid.autosizeColumns();
+ }
+ break;
+ case 'clear-filter':
+ this.clearColumnFilter(event, args);
+ break;
+ case 'clear-sort':
+ this.clearColumnSort(event, args);
+ break;
+ case 'sort-asc':
+ case 'sort-desc':
+ const isSortingAsc = (args.command === 'sort-asc');
+ this.sortColumn(event, args, isSortingAsc);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /** Sort the current column */
+ private sortColumn(event: Event, args: HeaderMenuOnCommandArgs, isSortingAsc = true) {
+ if (args && args.column) {
+ // get previously sorted columns
+ const sortedColsWithoutCurrent: ColumnSort[] = this.sortService.getPreviousColumnSorts(args.column.id + '');
+
+ // add to the column array, the column sorted by the header menu
+ sortedColsWithoutCurrent.push({ sortCol: args.column, sortAsc: isSortingAsc });
+ if (this.sharedService.gridOptions.backendServiceApi) {
+ this.sortService.onBackendSortChanged(event, { multiColumnSort: true, sortCols: sortedColsWithoutCurrent, grid: this.sharedService.grid });
+ } else if (this.sharedService.dataView) {
+ this.sortService.onLocalSortChanged(this.sharedService.grid, this.sharedService.dataView, sortedColsWithoutCurrent);
+ } else {
+ // when using customDataView, we will simply send it as a onSort event with notify
+ const isMultiSort = this.sharedService && this.sharedService.gridOptions && this.sharedService.gridOptions.multiColumnSort || false;
+ const sortOutput = isMultiSort ? sortedColsWithoutCurrent : sortedColsWithoutCurrent[0];
+ args.grid.onSort.notify(sortOutput);
+ }
+
+ // update the this.sharedService.gridObj sortColumns array which will at the same add the visual sort icon(s) on the UI
+ const newSortColumns: ColumnSort[] = sortedColsWithoutCurrent.map((col) => {
+ return {
+ columnId: col && col.sortCol && col.sortCol.id,
+ sortAsc: col && col.sortAsc
+ };
+ });
+ this.sharedService.grid.setSortColumns(newSortColumns); // add sort icon in UI
+ }
+ }
}
diff --git a/src/aurelia-slickgrid/extensions/rowMoveManagerExtension.ts b/src/aurelia-slickgrid/extensions/rowMoveManagerExtension.ts
index ad37cfd2c..0b682e8e7 100644
--- a/src/aurelia-slickgrid/extensions/rowMoveManagerExtension.ts
+++ b/src/aurelia-slickgrid/extensions/rowMoveManagerExtension.ts
@@ -1,5 +1,5 @@
import { singleton, inject } from 'aurelia-framework';
-import { CellArgs, Extension, ExtensionName } from '../models/index';
+import { CellArgs, Extension, ExtensionName, SlickEventHandler } from '../models/index';
import { ExtensionUtility } from './extensionUtility';
import { SharedService } from '../services/shared.service';
@@ -9,17 +9,23 @@ declare var Slick: any;
@singleton(true)
@inject(ExtensionUtility, SharedService)
export class RowMoveManagerExtension implements Extension {
- private _eventHandler: any = new Slick.EventHandler();
- private _extension: any;
+ private _addon: any;
+ private _eventHandler: SlickEventHandler;
- constructor(private extensionUtility: ExtensionUtility, private sharedService: SharedService) { }
+ constructor(private extensionUtility: ExtensionUtility, private sharedService: SharedService) {
+ this._eventHandler = new Slick.EventHandler();
+ }
+
+ get eventHandler(): SlickEventHandler {
+ return this._eventHandler;
+ }
dispose() {
// unsubscribe all SlickGrid events
this._eventHandler.unsubscribeAll();
- if (this._extension && this._extension.destroy) {
- this._extension.destroy();
+ if (this._addon && this._addon.destroy) {
+ this._addon.destroy();
}
}
@@ -35,26 +41,26 @@ export class RowMoveManagerExtension implements Extension {
this.sharedService.grid.setSelectionModel(rowSelectionPlugin);
}
- this._extension = new Slick.RowMoveManager(this.sharedService.gridOptions.rowMoveManager || { cancelEditOnDrag: true });
- this.sharedService.grid.registerPlugin(this._extension);
+ this._addon = new Slick.RowMoveManager(this.sharedService.gridOptions.rowMoveManager || { cancelEditOnDrag: true });
+ this.sharedService.grid.registerPlugin(this._addon);
// hook all events
if (this.sharedService.grid && this.sharedService.gridOptions.rowMoveManager) {
if (this.sharedService.gridOptions.rowMoveManager.onExtensionRegistered) {
- this.sharedService.gridOptions.rowMoveManager.onExtensionRegistered(this._extension);
+ this.sharedService.gridOptions.rowMoveManager.onExtensionRegistered(this._addon);
}
- this._eventHandler.subscribe(this._extension.onBeforeMoveRows, (e: any, args: CellArgs) => {
+ this._eventHandler.subscribe(this._addon.onBeforeMoveRows, (e: any, args: CellArgs) => {
if (this.sharedService.gridOptions.rowMoveManager && typeof this.sharedService.gridOptions.rowMoveManager.onBeforeMoveRows === 'function') {
this.sharedService.gridOptions.rowMoveManager.onBeforeMoveRows(e, args);
}
});
- this._eventHandler.subscribe(this._extension.onMoveRows, (e: any, args: CellArgs) => {
+ this._eventHandler.subscribe(this._addon.onMoveRows, (e: any, args: CellArgs) => {
if (this.sharedService.gridOptions.rowMoveManager && typeof this.sharedService.gridOptions.rowMoveManager.onMoveRows === 'function') {
this.sharedService.gridOptions.rowMoveManager.onMoveRows(e, args);
}
});
}
- return this._extension;
+ return this._addon;
}
return null;
}
diff --git a/src/aurelia-slickgrid/models/columnPicker.interface.ts b/src/aurelia-slickgrid/models/columnPicker.interface.ts
index 4d28ea002..92032a6a1 100644
--- a/src/aurelia-slickgrid/models/columnPicker.interface.ts
+++ b/src/aurelia-slickgrid/models/columnPicker.interface.ts
@@ -14,6 +14,12 @@ export interface ColumnPicker {
/** Defaults to "Synchronous resize" which is 1 of the last 2 checkbox title shown at the end of the picker list */
syncResizeTitle?: string;
+ // --
+ // Events
+
+ /** Fired after extension (control) is registered by SlickGrid */
+ onExtensionRegistered?: (addon: any) => void;
+
/** SlickGrid Event fired when any of the columns checkbox selection changes. */
onColumnsChanged?: (e: Event, args: any) => void;
}
diff --git a/src/aurelia-slickgrid/services/filter.service.ts b/src/aurelia-slickgrid/services/filter.service.ts
index 5acddd871..8f475d95b 100644
--- a/src/aurelia-slickgrid/services/filter.service.ts
+++ b/src/aurelia-slickgrid/services/filter.service.ts
@@ -178,7 +178,7 @@ export class FilterService {
});
}
- clearFilterByColumnId(columnId: number | string) {
+ clearFilterByColumnId(event: Event, columnId: number | string) {
const colFilter: Filter = this._filters.find((filter: Filter) => filter.columnDef.id === columnId);
if (colFilter && colFilter.clear) {
colFilter.clear(true);
diff --git a/src/aurelia-slickgrid/services/grid.service.ts b/src/aurelia-slickgrid/services/grid.service.ts
index 0b3eecb87..06e429c09 100644
--- a/src/aurelia-slickgrid/services/grid.service.ts
+++ b/src/aurelia-slickgrid/services/grid.service.ts
@@ -6,15 +6,13 @@ import { FilterService } from './filter.service';
import { GridStateService } from './gridState.service';
import { SortService } from './sort.service';
-const GridServiceDeleteOptionDefaults: GridServiceDeleteOption = { triggerEvent: true };
-const GridServiceInsertOptionDefaults: GridServiceInsertOption = { highlightRow: true, resortGrid: false, selectRow: false, triggerEvent: true };
-const GridServiceUpdateOptionDefaults: GridServiceUpdateOption = { highlightRow: true, selectRow: false, triggerEvent: true };
-
// using external non-typed js libraries
declare var Slick: any;
let highlightTimerEnd: any;
-
const DEFAULT_AURELIA_EVENT_PREFIX = 'asg';
+const GridServiceDeleteOptionDefaults: GridServiceDeleteOption = { triggerEvent: true };
+const GridServiceInsertOptionDefaults: GridServiceInsertOption = { highlightRow: true, resortGrid: false, selectRow: false, triggerEvent: true };
+const GridServiceUpdateOptionDefaults: GridServiceUpdateOption = { highlightRow: true, selectRow: false, triggerEvent: true };
@singleton(true)
@inject(EventAggregator, ExtensionService, FilterService, GridStateService, SortService)