Skip to content

Commit

Permalink
fix(tree): using initiallyCollapsed change internal toggled state
Browse files Browse the repository at this point in the history
- if we defined `initiallyCollapsed` as true, we should expect the internal toggle state change to be updated accordingly
- also add extra arguments to the `applyToggledItemStateChanges()` method to skip a full toggle and also another argument to optionally trigger an item toggled event
  • Loading branch information
ghiscoding committed Jun 14, 2021
1 parent 60ae015 commit 91c48a2
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,14 @@ describe('SortService', () => {
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 rxStartSpy = jest.spyOn(sharedService.onTreeFullToggleStart, 'next');
const rxEndSpy = jest.spyOn(sharedService.onTreeFullToggleEnd, 'next');

service.init(gridStub);
service.toggleTreeDataCollapse(true);

// expect(pubSubSpy).toHaveBeenCalledWith(`onTreeFullToggleStart`, { collapsing: true });
// expect(pubSubSpy).toHaveBeenCalledWith(`onTreeFullToggleEnd`, { type: 'full-collapse', previousFullToggleType: 'full-collapse', toggledItems: null, });
expect(rxStartSpy).toHaveBeenCalledWith({ collapsing: true });
expect(rxEndSpy).toHaveBeenCalledWith({ type: 'full-collapse', previousFullToggleType: 'full-collapse', toggledItems: null, });
expect(dataGetItemsSpy).toHaveBeenCalled();
expect(beginUpdateSpy).toHaveBeenCalled();
expect(updateItemSpy).toHaveBeenNthCalledWith(1, 0, { __collapsed: true, __hasChildren: true, id: 0, file: 'TXT', size: 5.8 });
Expand All @@ -356,13 +357,14 @@ describe('SortService', () => {
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 rxStartSpy = jest.spyOn(sharedService.onTreeFullToggleStart, 'next');
const rxEndSpy = jest.spyOn(sharedService.onTreeFullToggleEnd, 'next');

service.init(gridStub);
service.toggleTreeDataCollapse(true);

// expect(pubSubSpy).toHaveBeenCalledWith(`onTreeFullToggleStart`, { collapsing: true });
// expect(pubSubSpy).toHaveBeenCalledWith(`onTreeFullToggleEnd`, { type: 'full-collapse', previousFullToggleType: 'full-collapse', toggledItems: null, });
expect(rxStartSpy).toHaveBeenCalledWith({ collapsing: true });
expect(rxEndSpy).toHaveBeenCalledWith({ type: 'full-collapse', previousFullToggleType: 'full-collapse', toggledItems: null, });
expect(dataGetItemsSpy).toHaveBeenCalled();
expect(dataGetItemsSpy).toHaveBeenCalled();
expect(beginUpdateSpy).toHaveBeenCalled();
Expand All @@ -376,13 +378,14 @@ describe('SortService', () => {
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 rxStartSpy = jest.spyOn(sharedService.onTreeFullToggleStart, 'next');
const rxEndSpy = jest.spyOn(sharedService.onTreeFullToggleEnd, 'next');

service.init(gridStub);
service.toggleTreeDataCollapse(false);

// expect(pubSubSpy).toHaveBeenCalledWith(`onTreeFullToggleStart`, { collapsing: false });
// expect(pubSubSpy).toHaveBeenCalledWith(`onTreeFullToggleEnd`, { type: 'full-expand', previousFullToggleType: 'full-expand', toggledItems: null, });
expect(rxStartSpy).toHaveBeenCalledWith({ collapsing: false });
expect(rxEndSpy).toHaveBeenCalledWith({ type: 'full-expand', previousFullToggleType: 'full-expand', toggledItems: null, });
expect(dataGetItemsSpy).toHaveBeenCalled();
expect(beginUpdateSpy).toHaveBeenCalled();
expect(updateItemSpy).toHaveBeenNthCalledWith(1, 0, { __collapsed: false, __hasChildren: true, id: 0, file: 'TXT', size: 5.8 });
Expand All @@ -391,20 +394,60 @@ describe('SortService', () => {
});

describe('applyToggledItemStateChanges method', () => {
it('should execute the method', () => {
it('should execute the method and expect a full toggle or items', () => {
const dataGetItemsSpy = jest.spyOn(dataViewStub, 'getItems').mockReturnValue(mockFlatDataset);
jest.spyOn(dataViewStub, 'getItemById').mockReturnValue(mockFlatDataset[3]);
jest.spyOn(SharedService.prototype, 'hierarchicalDataset', 'get').mockReturnValue(mockHierarchical);
const beginUpdateSpy = jest.spyOn(dataViewStub, 'beginUpdate');
const endUpdateSpy = jest.spyOn(dataViewStub, 'endUpdate');
const updateItemSpy = jest.spyOn(dataViewStub, 'updateItem');
const rxToggleSpy = jest.spyOn(sharedService.onTreeItemToggled, 'next');

service.init(gridStub);
service.applyToggledItemStateChanges([{ itemId: 4, isCollapsed: true }]);

expect(dataGetItemsSpy).toHaveBeenCalled();
expect(beginUpdateSpy).toHaveBeenCalled();
expect(updateItemSpy).toHaveBeenNthCalledWith(1, 4, { __collapsed: true, __hasChildren: true, id: 4, file: 'MP3', size: 3.4 });
expect(rxToggleSpy).not.toHaveBeenCalled();
expect(endUpdateSpy).toHaveBeenCalled();
});

it('should execute the method but without calling "getItems" to skip doing a full toggle of items', () => {
const dataGetItemsSpy = jest.spyOn(dataViewStub, 'getItems').mockReturnValue(mockFlatDataset);
jest.spyOn(dataViewStub, 'getItemById').mockReturnValue(mockFlatDataset[3]);
jest.spyOn(SharedService.prototype, 'hierarchicalDataset', 'get').mockReturnValue(mockHierarchical);
const beginUpdateSpy = jest.spyOn(dataViewStub, 'beginUpdate');
const endUpdateSpy = jest.spyOn(dataViewStub, 'endUpdate');
const updateItemSpy = jest.spyOn(dataViewStub, 'updateItem');
const rxToggleSpy = jest.spyOn(sharedService.onTreeItemToggled, 'next');

service.init(gridStub);
service.applyToggledItemStateChanges([{ itemId: 4, isCollapsed: true }], 'full-collapse', false, true);

expect(dataGetItemsSpy).not.toHaveBeenCalled();
expect(beginUpdateSpy).toHaveBeenCalled();
expect(updateItemSpy).toHaveBeenNthCalledWith(1, 4, { __collapsed: true, __hasChildren: true, id: 4, file: 'MP3', size: 3.4 });
expect(rxToggleSpy).toHaveBeenCalledWith({ fromItemId: 4, previousFullToggleType: 'full-collapse', toggledItems: [{ itemId: 4, isCollapsed: true }], type: 'toggle-collapse' });
expect(endUpdateSpy).toHaveBeenCalled();
});

it('should execute the method and also trigger an event when specified', () => {
const dataGetItemsSpy = jest.spyOn(dataViewStub, 'getItems').mockReturnValue(mockFlatDataset);
jest.spyOn(dataViewStub, 'getItemById').mockReturnValue(mockFlatDataset[3]);
jest.spyOn(SharedService.prototype, 'hierarchicalDataset', 'get').mockReturnValue(mockHierarchical);
const beginUpdateSpy = jest.spyOn(dataViewStub, 'beginUpdate');
const endUpdateSpy = jest.spyOn(dataViewStub, 'endUpdate');
const updateItemSpy = jest.spyOn(dataViewStub, 'updateItem');
const rxToggleSpy = jest.spyOn(sharedService.onTreeItemToggled, 'next');

service.init(gridStub);
service.applyToggledItemStateChanges([{ itemId: 4, isCollapsed: true }], 'full-collapse', true, true);

expect(dataGetItemsSpy).toHaveBeenCalled();
expect(beginUpdateSpy).toHaveBeenCalled();
expect(updateItemSpy).toHaveBeenNthCalledWith(1, 4, { __collapsed: true, __hasChildren: true, id: 4, file: 'MP3', size: 3.4 });
expect(rxToggleSpy).toHaveBeenCalledWith({ fromItemId: 4, previousFullToggleType: 'full-collapse', toggledItems: [{ itemId: 4, isCollapsed: true }], type: 'toggle-collapse' });
expect(endUpdateSpy).toHaveBeenCalled();
});
});
Expand Down
42 changes: 35 additions & 7 deletions src/app/modules/angular-slickgrid/services/treeData.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,11 @@ export class TreeDataService {
* Apply different tree toggle state changes by providing an array of parentIds that are designated as collapsed (or not).
* User will have to provide an array of `parentId` and `isCollapsed` boolean and the code will only apply the ones that are tagged as collapsed, everything else will be expanded
* @param {Array<TreeToggledItem>} treeToggledItems - array of parentId which are tagged as changed
* @param {ToggleStateChangeType} previousFullToggleType - optionally provide the previous full toggle type ('full-expand' or 'full-collapse')
* @param {Boolean} shouldPreProcessFullToggle - should we pre-process a full toggle on all items? defaults to True
* @param {Boolean} shouldTriggerEvent - should we trigger a toggled item event? defaults to False
*/
applyToggledItemStateChanges(treeToggledItems: TreeToggledItem[], previousFullToggleType?: Exclude<ToggleStateChangeType, 'toggle'> | Exclude<ToggleStateChangeTypeString, 'toggle'>) {
applyToggledItemStateChanges(treeToggledItems: TreeToggledItem[], previousFullToggleType?: Exclude<ToggleStateChangeType, 'toggle'> | Exclude<ToggleStateChangeTypeString, 'toggle'>, shouldPreProcessFullToggle = true, shouldTriggerEvent = false) {
if (Array.isArray(treeToggledItems)) {
const collapsedPropName = this.getTreeDataOptionPropName('collapsedPropName');
const hasChildrenPropName = this.getTreeDataOptionPropName('hasChildrenPropName');
Expand All @@ -107,17 +110,42 @@ export class TreeDataService {
// we first need to put back the previous full toggle state (whether it was a full collapse or expand) by collapsing/expanding everything depending on the last toggled that was called `isLastFullToggleCollapsed`
const previousFullToggle = previousFullToggleType ?? this._lastToggleStateChange.previousFullToggleType;
const shouldCollapseAll = previousFullToggle === 'full-collapse';
(this.dataView.getItems() || []).forEach((item: any) => {
// collapse/expand the item but only when it's a parent item with children
if (item[hasChildrenPropName]) {
item[collapsedPropName] = shouldCollapseAll;
}
});

// when full toggle type is provided, we also need to update our internal reference of our current toggle state
if (previousFullToggleType) {
this._lastToggleStateChange.previousFullToggleType = previousFullToggleType;
}

// typically (optionally and defaults to true) if we want to reapply some toggled items we probably want to be in the full toggled state as it was at the start
// collapse/expand from the last full toggle state, all the items which are parent items with children
if (shouldPreProcessFullToggle) {
(this.dataView.getItems() || []).forEach((item: any) => {
if (item[hasChildrenPropName]) {
item[collapsedPropName] = shouldCollapseAll;
}
});
}

// then we reapply only the ones that changed (provided as argument to the function)
for (const collapsedItem of treeToggledItems) {
const item = this.dataView.getItemById(collapsedItem.itemId);
this.updateToggledItem(item, collapsedItem.isCollapsed);

if (shouldTriggerEvent) {
const parentFoundIdx = this._currentToggledItems.findIndex(treeChange => treeChange.itemId === collapsedItem.itemId);
if (parentFoundIdx >= 0) {
this._currentToggledItems[parentFoundIdx].isCollapsed = collapsedItem.isCollapsed;
} else {
this._currentToggledItems.push({ itemId: collapsedItem.itemId, isCollapsed: collapsedItem.isCollapsed });
}

this.sharedService.onTreeItemToggled.next({
...this._lastToggleStateChange,
fromItemId: collapsedItem.itemId,
toggledItems: this._currentToggledItems,
type: collapsedItem.isCollapsed ? ToggleStateChangeType.toggleCollapse : ToggleStateChangeType.toggleExpand
} as TreeToggleStateChange);
}
}

// close the update transaction & call a refresh which will trigger a re-render with filters applied (including expand/collapse)
Expand Down

0 comments on commit 91c48a2

Please sign in to comment.