Skip to content

Commit

Permalink
fix(header): re-create header grouping title after changing picker cols
Browse files Browse the repository at this point in the history
- after hiding/showing columns from ColumnPicker/GridMenu we need to re-create the header grouping titles
  • Loading branch information
ghiscoding-SE committed Jun 17, 2020
1 parent bc07790 commit 872c780
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 20 deletions.
9 changes: 4 additions & 5 deletions packages/common/src/interfaces/extensionModel.interface.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { Extension } from './extension.interface';
import { ExtensionName } from '../enums/extensionName.enum';
import { SlickControlList, SlickPluginList } from '../enums';
import { ExtensionName, SlickControlList, SlickPluginList } from '../enums/index';

export interface ExtensionModel {
export interface ExtensionModel<P extends (SlickControlList | SlickPluginList), E extends Extension> {
/** Name of the Slickgrid-Universal Extension */
name: ExtensionName;

/** Instance of the Addon (3rd party SlickGrid Control or Plugin) */
instance: SlickControlList | SlickPluginList;
instance: P;

/** Extension Service (in Slickgrid-Universal) */
class: Extension | null;
class: E;
}
1 change: 0 additions & 1 deletion packages/common/src/interfaces/slickNamespace.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
CellMenuOption,
CheckboxSelectorOption,
Column,
ColumnPickerOption,
ContextMenuOption,
DraggableGroupingOption,
ExcelCopyBufferOption,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
SlickDataView,
GridStateChange,
GridState,
SlickColumnPicker,
SlickGrid,
SlickNamespace,
SlickRowSelectionModel,
Expand Down Expand Up @@ -164,7 +165,7 @@ describe('GridStateService', () => {
const instanceMock = { onColumnsChanged: slickgridEvent };
const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[];
const associatedColumnsMock = [{ columnId: 'field1', cssClass: 'red', headerCssClass: '', width: 100 }] as CurrentColumn[];
const extensionMock = { name: ExtensionName.columnPicker, addon: instanceMock, instance: instanceMock, class: null };
const extensionMock = { name: ExtensionName.columnPicker, addon: instanceMock, instance: instanceMock as SlickColumnPicker, class: null };
const gridStateMock = { columns: associatedColumnsMock, filters: [], sorters: [] } as GridState;
const stateChangeMock = { change: { newValues: associatedColumnsMock, type: GridStateType.columns }, gridState: gridStateMock } as GridStateChange;

Expand Down Expand Up @@ -869,7 +870,7 @@ describe('GridStateService', () => {
});

it('should call the method and call the grid selection reset when the selection extension is used', () => {
const extensionMock = { name: ExtensionName.rowSelection, addon: {}, instance: {}, class: null };
const extensionMock = { name: ExtensionName.rowSelection, addon: {}, instance: {} as unknown as SlickColumnPicker, class: null };
const gridOptionsMock = { enableRowSelection: true } as GridOption;
const gridOptionSpy = jest.spyOn(gridStub, 'getOptions').mockReturnValue(gridOptionsMock);
const setSelectionSpy = jest.spyOn(gridStub, 'setSelectedRows');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { GroupingAndColspanService } from '../groupingAndColspan.service';
import { Column, SlickDataView, GridOption, SlickEventHandler, SlickGrid, SlickNamespace } from '../../interfaces/index';
import { Column, SlickDataView, GridOption, SlickEventHandler, SlickGrid, SlickNamespace, SlickColumnPicker, SlickGridMenu } from '../../interfaces/index';
import { ExtensionUtility } from '../../extensions/extensionUtility';
import { ExtensionService } from '../extension.service';
import { ExtensionName } from '../../enums/index';

declare const Slick: SlickNamespace;
const gridId = 'grid1';
Expand All @@ -19,6 +21,10 @@ const dataViewStub = {
reSort: jest.fn(),
} as unknown as SlickDataView;

const extensionServiceStub = {
getExtensionByName: jest.fn()
} as unknown as ExtensionService;

const resizerPluginStub = {
init: jest.fn(),
destroy: jest.fn(),
Expand Down Expand Up @@ -72,20 +78,23 @@ const template =
describe('GroupingAndColspanService', () => {
let service: GroupingAndColspanService;
let slickgridEventHandler: SlickEventHandler;
let slickgridEvent;

beforeEach(() => {
const div = document.createElement('div');
div.innerHTML = template;
document.body.appendChild(div);

service = new GroupingAndColspanService(mockExtensionUtility);
service = new GroupingAndColspanService(mockExtensionUtility, extensionServiceStub);
slickgridEventHandler = service.eventHandler;
slickgridEvent = new Slick.Event();
});

afterEach(() => {
jest.clearAllMocks();
service.dispose();
gridStub.getOptions = () => gridOptionMock;
slickgridEvent.unsubscribe();
});

it('should create the service', () => {
Expand Down Expand Up @@ -121,6 +130,10 @@ describe('GroupingAndColspanService', () => {
jest.spyOn(gridStub, 'getPreHeaderPanel').mockReturnValue(`<div style="width: 2815px; left: -1000px;" class="slick-header-columns"></div>` as unknown as HTMLElement);
});

afterEach(() => {
jest.clearAllMocks();
});

it('should call the "renderPreHeaderRowGroupingTitles" on initial load even when there are no column definitions', () => {
const spy = jest.spyOn(service, 'renderPreHeaderRowGroupingTitles');
gridStub.getColumns = undefined;
Expand Down Expand Up @@ -193,6 +206,38 @@ describe('GroupingAndColspanService', () => {
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 75);
});

it('should call the "renderPreHeaderRowGroupingTitles" after changing column visibility from column picker', () => {
const spy = jest.spyOn(service, 'renderPreHeaderRowGroupingTitles');
const instanceMock = { onColumnsChanged: slickgridEvent };
const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[];
const extensionMock = { name: ExtensionName.columnPicker, addon: instanceMock, instance: instanceMock as SlickColumnPicker, class: null };
jest.spyOn(extensionServiceStub, 'getExtensionByName').mockReturnValue(extensionMock);
service.init(gridStub);

slickgridEvent.notify({ columns: columnsMock }, new Slick.EventData(), gridStub);
jest.runAllTimers(); // fast-forward timer

expect(spy).toHaveBeenCalledTimes(3);
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 75);
});

it('should call the "renderPreHeaderRowGroupingTitles" after changing column visibility from grid menu', () => {
const spy = jest.spyOn(service, 'renderPreHeaderRowGroupingTitles');
const instanceMock = { onColumnsChanged: slickgridEvent };
const columnsMock = [{ id: 'field1', field: 'field1', width: 100, cssClass: 'red' }] as Column[];
const extensionMock = { name: ExtensionName.columnPicker, addon: instanceMock, instance: instanceMock as SlickGridMenu, class: null };
jest.spyOn(extensionServiceStub, 'getExtensionByName').mockReturnValue(extensionMock);
service.init(gridStub);

slickgridEvent.notify({ columns: columnsMock }, new Slick.EventData(), gridStub);
jest.runAllTimers(); // fast-forward timer

expect(spy).toHaveBeenCalledTimes(3);
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 75);
});

it('should call the "renderPreHeaderRowGroupingTitles" after calling the "translateGroupingAndColSpan" method', () => {
gridOptionMock.enableTranslate = true;
const renderSpy = jest.spyOn(service, 'renderPreHeaderRowGroupingTitles');
Expand Down
14 changes: 7 additions & 7 deletions packages/common/src/services/extension.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import 'slickgrid/plugins/slick.cellrangedecorator';
import 'slickgrid/plugins/slick.cellrangeselector';
import 'slickgrid/plugins/slick.cellselectionmodel';

import { Column, ExtensionModel, GridOption, SlickRowSelectionModel, SlickColumnPicker, SlickGridMenu, } from '../interfaces/index';
import { ExtensionName } from '../enums/extensionName.enum';
import { Column, Extension, ExtensionModel, GridOption, SlickRowSelectionModel, } from '../interfaces/index';
import { ExtensionName, SlickControlList, SlickPluginList } from '../enums/index';
import {
AutoTooltipExtension,
CellExternalCopyManagerExtension,
Expand All @@ -26,7 +26,7 @@ import { TranslaterService } from './translater.service';

export class ExtensionService {
private _extensionCreatedList: any[] = [];
private _extensionList: ExtensionModel[] = [];
private _extensionList: ExtensionModel<any, any>[] = [];

constructor(
private autoTooltipExtension: AutoTooltipExtension,
Expand Down Expand Up @@ -71,19 +71,19 @@ export class ExtensionService {
}

/** Get all Extensions */
getAllExtensions(): ExtensionModel[] {
getAllExtensions(): ExtensionModel<any, any>[] {
return this._extensionList;
}

/**
* Get an Extension by it's name
* @param name
*/
getExtensionByName(name: ExtensionName): ExtensionModel | undefined {
getExtensionByName<P extends (SlickControlList | SlickPluginList) = any, E extends Extension = Extension>(name: ExtensionName): ExtensionModel<P, E> | undefined {
if (!Array.isArray(this._extensionList) || this._extensionList.length === 0) {
return undefined;
}
return this._extensionList.find((p) => p.name === name);
return this._extensionList.find(ext => ext.name === name);
}

/**
Expand Down Expand Up @@ -425,7 +425,7 @@ export class ExtensionService {
* Get an Extension that was created by calling its "create" method (there are only 3 extensions which uses this method)
* @param name
*/
private getCreatedExtensionByName(name: ExtensionName): ExtensionModel | undefined {
private getCreatedExtensionByName<P extends (SlickControlList | SlickPluginList) = any, E extends Extension = any>(name: ExtensionName): ExtensionModel<P, E> | undefined {
return Array.isArray(this._extensionCreatedList) && this._extensionCreatedList.find((p) => p.name === name);
}

Expand Down
22 changes: 20 additions & 2 deletions packages/common/src/services/groupingAndColspan.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import {
SlickGrid,
SlickNamespace,
SlickResizer,
SlickColumnPicker,
SlickGridMenu,
} from './../interfaces/index';
import { ExtensionName } from '../enums/index';
import { ExtensionUtility } from '../extensions/extensionUtility';
import { ExtensionService } from '../services/extension.service';

// using external non-typed js libraries
declare let $: any;
Expand All @@ -18,7 +22,7 @@ export class GroupingAndColspanService {
private _eventHandler: SlickEventHandler;
private _grid: SlickGrid;

constructor(private extensionUtility: ExtensionUtility) {
constructor(private extensionUtility: ExtensionUtility, private extensionService: ExtensionService) {
this._eventHandler = new Slick.EventHandler();
}

Expand Down Expand Up @@ -49,7 +53,6 @@ export class GroupingAndColspanService {
*/
init(grid: SlickGrid) {
this._grid = grid;
const resizerPlugin = grid.getPluginByName<SlickResizer>('Resizer');

if (grid && this._gridOptions) {
// When dealing with Pre-Header Grouping colspan, we need to re-create the pre-header in multiple occasions
Expand All @@ -59,10 +62,25 @@ export class GroupingAndColspanService {
this._eventHandler.subscribe(grid.onColumnsResized, () => this.renderPreHeaderRowGroupingTitles());
this._eventHandler.subscribe(grid.onColumnsReordered, () => this.renderPreHeaderRowGroupingTitles());
this._eventHandler.subscribe(this._dataView.onRowCountChanged, () => this.renderPreHeaderRowGroupingTitles());

// for both picker (columnPicker/gridMenu) we also need to re-create after hiding/showing columns
const columnPickerExtension = this.extensionService.getExtensionByName<SlickColumnPicker>(ExtensionName.columnPicker);
if (columnPickerExtension?.instance?.onColumnsChanged) {
this._eventHandler.subscribe(columnPickerExtension.instance.onColumnsChanged, () => this.renderPreHeaderRowGroupingTitles());
}

const gridMenuExtension = this.extensionService.getExtensionByName<SlickGridMenu>(ExtensionName.gridMenu);
if (gridMenuExtension?.instance?.onColumnsChanged) {
this._eventHandler.subscribe(gridMenuExtension.instance.onColumnsChanged, () => this.renderPreHeaderRowGroupingTitles());
}

// we also need to re-create after a grid resize
const resizerPlugin = grid.getPluginByName<SlickResizer>('Resizer');
if (resizerPlugin?.onGridAfterResize) {
this._eventHandler.subscribe(resizerPlugin.onGridAfterResize, () => this.renderPreHeaderRowGroupingTitles());
}

// and finally we need to re-create after user calls the Grid "setOptions" when changing from regular to frozen grid (and vice versa)
const onSetOptionsHandler = grid.onSetOptions;
(this._eventHandler as SlickEventHandler<GetSlickEventType<typeof onSetOptionsHandler>>).subscribe(onSetOptionsHandler, (_e, args) => {
// when user changes frozen columns dynamically (e.g. from header menu), we need to re-render the pre-header of the grouping titles
Expand Down
2 changes: 1 addition & 1 deletion packages/vanilla-bundle/src/vanilla-grid-bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ export class VanillaGridBundle {
this.sortService = new SortService(this.sharedService, this._eventPubSubService);
this.treeDataService = new TreeDataService(this.sharedService);
this.extensionUtility = new ExtensionUtility(this.sharedService, this.translateService);
this.groupingAndColspanService = new GroupingAndColspanService(this.extensionUtility);
this.autoTooltipExtension = new AutoTooltipExtension(this.extensionUtility, this.sharedService);
this.cellExternalCopyManagerExtension = new CellExternalCopyManagerExtension(this.extensionUtility, this.sharedService);
this.cellMenuExtension = new CellMenuExtension(this.extensionUtility, this.sharedService, this.translateService);
Expand Down Expand Up @@ -239,6 +238,7 @@ export class VanillaGridBundle {
this.sharedService,
this.translateService,
);
this.groupingAndColspanService = new GroupingAndColspanService(this.extensionUtility, this.extensionService);

if (hierarchicalDataset) {
this.sharedService.hierarchicalDataset = (isDeepCopyDataOnPageLoadEnabled ? $.extend(true, [], hierarchicalDataset) : hierarchicalDataset) || [];
Expand Down

0 comments on commit 872c780

Please sign in to comment.