Skip to content

Commit

Permalink
fix(tree): reset to initial tree sort when calling "Clear all Sorting"
Browse files Browse the repository at this point in the history
  • Loading branch information
ghiscoding committed Nov 1, 2021
1 parent d00b46c commit 984e3a7
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 68 deletions.
19 changes: 16 additions & 3 deletions packages/common/src/extensions/__tests__/gridMenuExtension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Column, SlickDataView, GridOption, SlickGrid, SlickNamespace, GridMenu,
import { GridMenuExtension } from '../gridMenuExtension';
import { ExtensionUtility } from '../extensionUtility';
import { SharedService } from '../../services/shared.service';
import { ExcelExportService, TextExportService, FilterService, SortService, BackendUtilityService } from '../../services';
import { BackendUtilityService, ExcelExportService, FilterService, PubSubService, SortService, TextExportService } from '../../services';
import { TranslateServiceStub } from '../../../../../test/translateServiceStub';

declare const Slick: SlickNamespace;
Expand Down Expand Up @@ -52,6 +52,13 @@ const gridStub = {
scrollColumnIntoView: jest.fn(),
} as unknown as SlickGrid;

const pubSubServiceStub = {
publish: jest.fn(),
subscribe: jest.fn(),
unsubscribe: jest.fn(),
unsubscribeAll: jest.fn(),
} as PubSubService;

const mockGridMenuAddon = {
init: jest.fn(),
destroy: jest.fn(),
Expand Down Expand Up @@ -141,7 +148,7 @@ describe('gridMenuExtension', () => {
sharedService = new SharedService();
translateService = new TranslateServiceStub();
extensionUtility = new ExtensionUtility(sharedService, translateService);
extension = new GridMenuExtension(extensionUtility, filterServiceStub, sharedService, sortServiceStub, backendUtilityService, translateService);
extension = new GridMenuExtension(extensionUtility, filterServiceStub, pubSubServiceStub, sharedService, sortServiceStub, backendUtilityService, translateService);
translateService.use('fr');
});

Expand Down Expand Up @@ -633,6 +640,7 @@ describe('gridMenuExtension', () => {
it('should call "clearFrozenColumns" when the command triggered is "clear-pinning"', () => {
const setOptionsSpy = jest.spyOn(gridStub, 'setOptions');
const setColumnsSpy = jest.spyOn(gridStub, 'setColumns');
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const onCommandSpy = jest.spyOn(SharedService.prototype.gridOptions.gridMenu as GridMenu, 'onCommand');
jest.spyOn(SharedService.prototype, 'allColumns', 'get').mockReturnValue(columnsMock);
jest.spyOn(SharedService.prototype, 'visibleColumns', 'get').mockReturnValue(columnsMock.slice(0, 1));
Expand All @@ -643,32 +651,37 @@ describe('gridMenuExtension', () => {
expect(onCommandSpy).toHaveBeenCalled();
expect(setColumnsSpy).toHaveBeenCalled();
expect(setOptionsSpy).toHaveBeenCalledWith({ frozenColumn: -1, frozenRow: -1, frozenBottom: false, enableMouseWheelScrollHandler: false });
expect(pubSubSpy).toHaveBeenCalledWith('onGridMenuClearAllPinning');
});

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 as GridMenu, 'onCommand');
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');

const instance = extension.register() as SlickGridMenu;
instance.onCommand!.notify({ item: { command: 'clear-filter' }, column: {} as Column, grid: gridStub, command: 'clear-filter' }, new Slick.EventData(), gridStub);

expect(onCommandSpy).toHaveBeenCalled();
expect(filterSpy).toHaveBeenCalled();
expect(refreshSpy).toHaveBeenCalled();
expect(pubSubSpy).toHaveBeenCalledWith('onGridMenuClearAllFilters');
});

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 as GridMenu, 'onCommand');
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');

const instance = extension.register() as SlickGridMenu;
instance.onCommand!.notify({ item: { command: 'clear-sorting' }, column: {} as Column, grid: gridStub, command: 'clear-sorting' }, new Slick.EventData(), gridStub);

expect(onCommandSpy).toHaveBeenCalled();
expect(sortSpy).toHaveBeenCalled();
expect(refreshSpy).toHaveBeenCalled();
expect(pubSubSpy).toHaveBeenCalledWith('onGridMenuClearAllSorting');
});

it('should call "exportToExcel" and expect an error thrown when ExcelExportService is not registered prior to calling the method', (done) => {
Expand Down Expand Up @@ -854,7 +867,7 @@ describe('gridMenuExtension', () => {
beforeEach(() => {
translateService = undefined as any;
backendUtilityService = new BackendUtilityService();
extension = new GridMenuExtension({} as ExtensionUtility, filterServiceStub, { gridOptions: { enableTranslate: true } } as SharedService, {} as SortService, backendUtilityService, translateService);
extension = new GridMenuExtension({} as ExtensionUtility, filterServiceStub, pubSubServiceStub, { gridOptions: { enableTranslate: true } } as SharedService, {} as SortService, backendUtilityService, translateService);
});

it('should throw an error if "enableTranslate" is set but the I18N Service is null', () => {
Expand Down
5 changes: 5 additions & 0 deletions packages/common/src/extensions/gridMenuExtension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ExcelExportService } from '../services/excelExport.service';
import { TextExportService } from '../services/textExport.service';
import { ExtensionUtility } from './extensionUtility';
import { FilterService } from '../services/filter.service';
import { PubSubService } from '../services/pubSub.service';
import { SortService } from '../services/sort.service';
import { SharedService } from '../services/shared.service';
import { TranslaterService } from '../services/translater.service';
Expand All @@ -35,6 +36,7 @@ export class GridMenuExtension implements Extension {
constructor(
private readonly extensionUtility: ExtensionUtility,
private readonly filterService: FilterService,
private readonly pubSubService: PubSubService,
private readonly sharedService: SharedService,
private readonly sortService: SortService,
private readonly backendUtilities?: BackendUtilityService,
Expand Down Expand Up @@ -430,14 +432,17 @@ export class GridMenuExtension implements Extension {
if (gridOptions.enableAutoSizeColumns) {
this.sharedService.slickGrid.autosizeColumns();
}
this.pubSubService.publish('onGridMenuClearAllPinning');
break;
case 'clear-filter':
this.filterService.clearFilters();
this.sharedService.dataView.refresh();
this.pubSubService.publish('onGridMenuClearAllFilters');
break;
case 'clear-sorting':
this.sortService.clearSorting();
this.sharedService.dataView.refresh();
this.pubSubService.publish('onGridMenuClearAllSorting');
break;
case 'export-csv':
const exportCsvService: TextExportService = registeredResources.find((service: any) => service.className === 'TextExportService');
Expand Down
4 changes: 2 additions & 2 deletions packages/common/src/services/__tests__/sort.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ describe('SortService', () => {
setTimeout(() => {
expect(previousSortSpy).toHaveBeenCalled();
expect(localSortSpy).toHaveBeenNthCalledWith(1, gridStub, [], true, true);
expect(localSortSpy).toHaveBeenNthCalledWith(2, gridStub, [{ columnId: 'id', clearSortTriggered: true, sortAsc: true, sortCol: { field: 'id', id: 'id' } }]);
expect(localSortSpy).toHaveBeenNthCalledWith(2, gridStub, [{ columnId: 'id', clearSortTriggered: true, sortAsc: true, sortCol: { field: 'id', id: 'id' } }], false, true);
expect(emitSortChangedSpy).toHaveBeenCalledWith('local', []);
expect(setSortSpy).toHaveBeenCalled();
expect(sortDefaultSpy).toHaveBeenCalled();
Expand All @@ -252,7 +252,7 @@ describe('SortService', () => {
expect(previousSortSpy).toHaveBeenCalled();
expect(localSortSpy).toHaveBeenNthCalledWith(1, gridStub, [], true, true);
expect(emitSortChangedSpy).toHaveBeenCalledWith('local', []);
expect(localSortSpy).toHaveBeenNthCalledWith(2, gridStub, [{ columnId: 'customId', clearSortTriggered: true, sortAsc: true, sortCol: { field: 'customId', id: 'customId' } }]);
expect(localSortSpy).toHaveBeenNthCalledWith(2, gridStub, [{ columnId: 'customId', clearSortTriggered: true, sortAsc: true, sortCol: { field: 'customId', id: 'customId' } }], false, true);
expect(setSortSpy).toHaveBeenCalled();
expect(sortDefaultSpy).toHaveBeenCalled();
done();
Expand Down
36 changes: 26 additions & 10 deletions packages/common/src/services/__tests__/treeData.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,20 @@ const gridStub = {
setSortColumns: jest.fn(),
} as unknown as SlickGrid;

const pubSubServiceStub = {
const fnCallbacks = {};
const mockPubSub = {
publish: jest.fn(),
subscribe: jest.fn(),
subscribe: (eventName, fn) => fnCallbacks[eventName as string] = fn,
unsubscribe: jest.fn(),
unsubscribeAll: jest.fn(),
} as PubSubService;
jest.mock('../pubSub.service', () => ({
PubSubService: () => mockPubSub
}));

const sortServiceStub = {
clearSorting: jest.fn(),
loadGridSorters: jest.fn(),
sortHierarchicalDataset: jest.fn(),
} as unknown as SortService;

Expand All @@ -82,7 +87,7 @@ describe('TreeData Service', () => {
gridOptionsMock.treeDataOptions = {
columnId: 'file'
};
service = new TreeDataService(pubSubServiceStub, sharedService, sortServiceStub);
service = new TreeDataService(mockPubSub, sharedService, sortServiceStub);
slickgridEventHandler = service.eventHandler;
jest.spyOn(gridStub, 'getData').mockReturnValue(dataViewStub);
});
Expand Down Expand Up @@ -233,6 +238,17 @@ describe('TreeData Service', () => {
expect(spyGetCols).not.toHaveBeenCalled();
});

it('should call "clearSorting" and set back initial sort when "onGridMenuClearAllSorting" event is triggered', () => {
const clearSortingSpy = jest.spyOn(service, 'clearSorting');
const loadGridSorterSpy = jest.spyOn(sortServiceStub, 'loadGridSorters');

service.init(gridStub);
fnCallbacks['onGridMenuClearAllSorting']();

expect(clearSortingSpy).toHaveBeenCalled();
expect(loadGridSorterSpy).toHaveBeenCalled();
});

it('should toggle the "__collapsed" to True when the "toggle" class name was found without a collapsed class', () => {
jest.spyOn(gridStub, 'getData').mockReturnValue(dataViewStub);
const spyGetItem = jest.spyOn(dataViewStub, 'getItem').mockReturnValue(mockRowData);
Expand Down Expand Up @@ -344,7 +360,7 @@ describe('TreeData Service', () => {
const beginUpdateSpy = jest.spyOn(dataViewStub, 'beginUpdate');
const endUpdateSpy = jest.spyOn(dataViewStub, 'endUpdate');
const updateItemSpy = jest.spyOn(dataViewStub, 'updateItem');
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const pubSubSpy = jest.spyOn(mockPubSub, 'publish');

service.init(gridStub);
await service.toggleTreeDataCollapse(true);
Expand All @@ -371,7 +387,7 @@ describe('TreeData Service', () => {
const beginUpdateSpy = jest.spyOn(dataViewStub, 'beginUpdate');
const endUpdateSpy = jest.spyOn(dataViewStub, 'endUpdate');
const updateItemSpy = jest.spyOn(dataViewStub, 'updateItem');
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const pubSubSpy = jest.spyOn(mockPubSub, 'publish');

service.init(gridStub);
await service.toggleTreeDataCollapse(true);
Expand All @@ -391,7 +407,7 @@ describe('TreeData Service', () => {
const beginUpdateSpy = jest.spyOn(dataViewStub, 'beginUpdate');
const endUpdateSpy = jest.spyOn(dataViewStub, 'endUpdate');
const updateItemSpy = jest.spyOn(dataViewStub, 'updateItem');
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const pubSubSpy = jest.spyOn(mockPubSub, 'publish');

service.init(gridStub);
await service.toggleTreeDataCollapse(false);
Expand All @@ -413,7 +429,7 @@ describe('TreeData Service', () => {
const beginUpdateSpy = jest.spyOn(dataViewStub, 'beginUpdate');
const endUpdateSpy = jest.spyOn(dataViewStub, 'endUpdate');
const updateItemSpy = jest.spyOn(dataViewStub, 'updateItem');
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const pubSubSpy = jest.spyOn(mockPubSub, 'publish');

service.init(gridStub);
service.applyToggledItemStateChanges([{ itemId: 4, isCollapsed: true }]);
Expand All @@ -432,7 +448,7 @@ describe('TreeData Service', () => {
const beginUpdateSpy = jest.spyOn(dataViewStub, 'beginUpdate');
const endUpdateSpy = jest.spyOn(dataViewStub, 'endUpdate');
const updateItemSpy = jest.spyOn(dataViewStub, 'updateItem');
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const pubSubSpy = jest.spyOn(mockPubSub, 'publish');

service.init(gridStub);
service.applyToggledItemStateChanges([{ itemId: 4, isCollapsed: true }], 'full-collapse', false, true);
Expand All @@ -451,7 +467,7 @@ describe('TreeData Service', () => {
const beginUpdateSpy = jest.spyOn(dataViewStub, 'beginUpdate');
const endUpdateSpy = jest.spyOn(dataViewStub, 'endUpdate');
const updateItemSpy = jest.spyOn(dataViewStub, 'updateItem');
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const pubSubSpy = jest.spyOn(mockPubSub, 'publish');

service.init(gridStub);
service.applyToggledItemStateChanges([{ itemId: 4, isCollapsed: true }], 'full-collapse', true, true);
Expand All @@ -472,7 +488,7 @@ describe('TreeData Service', () => {
const beginUpdateSpy = jest.spyOn(dataViewStub, 'beginUpdate');
const endUpdateSpy = jest.spyOn(dataViewStub, 'endUpdate');
const updateItemSpy = jest.spyOn(dataViewStub, 'updateItem');
const pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');
const pubSubSpy = jest.spyOn(mockPubSub, 'publish');

service.init(gridStub);
service.dynamicallyToggleItemState([{ itemId: 4, isCollapsed: true }], true);
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/services/resizer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ export class ResizerService {
// resize the slickgrid canvas on all browser except some IE versions
// exclude all IE below IE11
// IE11 wants to be a better standard (W3C) follower (finally) they even changed their appName output to also have 'Netscape'
if (new RegExp('MSIE [6-8]').exec(navigator.userAgent) === null && this._grid && this._grid.resizeCanvas) {
if (new RegExp('MSIE [6-8]').exec(navigator.userAgent) === null && this._grid?.resizeCanvas && $(this._gridContainerElm)) {
this._grid.resizeCanvas();
}

Expand Down
8 changes: 4 additions & 4 deletions packages/common/src/services/sort.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ export class SortService {
sortLocalGridByDefaultSortFieldId() {
const sortColFieldId = this._gridOptions && this._gridOptions.defaultColumnSortFieldId || this._gridOptions.datasetIdPropertyName || 'id';
const sortCol = { id: sortColFieldId, field: sortColFieldId } as Column;
this.onLocalSortChanged(this._grid, new Array({ columnId: sortCol.id, sortAsc: true, sortCol, clearSortTriggered: true }));
this.onLocalSortChanged(this._grid, new Array({ columnId: sortCol.id, sortAsc: true, sortCol, clearSortTriggered: true }), false, true);
}

sortComparers(sortColumns: ColumnSort[], dataRow1: any, dataRow2: any): number {
Expand Down Expand Up @@ -518,13 +518,13 @@ export class SortService {
sortTreeData(treeArray: any[], sortColumns: Array<ColumnSort>) {
if (Array.isArray(sortColumns)) {
for (const sortColumn of sortColumns) {
this.sortTreeChild(treeArray, sortColumn, 0);
this.sortTreeChildren(treeArray, sortColumn, 0);
}
}
}

/** Sort the Tree Children of a hierarchical dataset by recursion */
sortTreeChild(treeArray: any[], sortColumn: ColumnSort, treeLevel: number) {
sortTreeChildren(treeArray: any[], sortColumn: ColumnSort, treeLevel: number) {
const treeDataOptions = this._gridOptions?.treeDataOptions;
const childrenPropName = treeDataOptions?.childrenPropName ?? 'children';
treeArray.sort((a: any, b: any) => this.sortComparer(sortColumn, a, b) ?? SortDirectionNumber.neutral);
Expand All @@ -536,7 +536,7 @@ export class SortService {
// when item has a child, we'll sort recursively
if (hasChildren) {
treeLevel++;
this.sortTreeChild(item[childrenPropName], sortColumn, treeLevel);
this.sortTreeChildren(item[childrenPropName], sortColumn, treeLevel);
treeLevel--;
}
}
Expand Down
9 changes: 9 additions & 0 deletions packages/common/src/services/treeData.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ export class TreeDataService {
if (onClickHandler) {
(this._eventHandler as SlickEventHandler<GetSlickEventType<typeof onClickHandler>>).subscribe(onClickHandler, this.handleOnCellClick.bind(this));
}

// when "Clear all Sorting" is triggered by the Grid Menu, we'll resort with `initialSort` when defined (or else by 'id')
this.pubSubService.subscribe('onGridMenuClearAllSorting', this.clearSorting.bind(this));
}

/**
Expand Down Expand Up @@ -258,6 +261,12 @@ export class TreeDataService {
return propName;
}

/** Clear the sorting and set it back to initial sort */
clearSorting() {
const initialSort = this.getInitialSort(this.sharedService.columnDefinitions, this.sharedService.gridOptions);
this.sortService.loadGridSorters([{ columnId: initialSort.columnId, direction: initialSort.sortAsc ? 'ASC' : 'DESC' }]);
}

/**
* Takes a flat dataset, converts it into a hierarchical dataset, sort it by recursion and finally return back the final and sorted flat array
* @param {Array<Object>} flatDataset - parent/child flat dataset
Expand Down
Loading

0 comments on commit 984e3a7

Please sign in to comment.