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 ec974ca commit 380f2f9
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 13 deletions.
42 changes: 41 additions & 1 deletion packages/common/src/services/__tests__/treeData.service.spec.ts
Expand Up @@ -399,20 +399,60 @@ describe('TreeData Service', () => {
});

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

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(pubSubSpy).not.toHaveBeenCalledWith(`onTreeItemToggled`);
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 pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');

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(pubSubSpy).toHaveBeenCalledWith(`onTreeItemToggled`, { fromItemId: 4, previousFullToggleType: 'full-collapse', toggledItems: [], 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 pubSubSpy = jest.spyOn(pubSubServiceStub, 'publish');

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(pubSubSpy).toHaveBeenCalledWith(`onTreeItemToggled`, { fromItemId: 4, previousFullToggleType: 'full-collapse', toggledItems: [], type: 'toggle-collapse' });
expect(endUpdateSpy).toHaveBeenCalled();
});
});
Expand Down
45 changes: 33 additions & 12 deletions packages/common/src/services/treeData.service.ts
Expand Up @@ -25,11 +25,7 @@ declare const Slick: SlickNamespace;

export class TreeDataService {
private _isLastFullToggleCollapsed = false;
private _lastToggleStateChange: Omit<TreeToggleStateChange, 'fromItemId'> = {
type: this.gridOptions?.treeDataOptions?.initiallyCollapsed ? 'full-collapse' : 'full-expand',
previousFullToggleType: this.gridOptions?.treeDataOptions?.initiallyCollapsed ? 'full-collapse' : 'full-expand',
toggledItems: null
};
private _lastToggleStateChange!: Omit<TreeToggleStateChange, 'fromItemId'>;
private _currentToggledItems: TreeToggledItem[] = [];
private _grid!: SlickGrid;
private _eventHandler: SlickEventHandler;
Expand Down Expand Up @@ -78,6 +74,11 @@ export class TreeDataService {
this._grid = grid;
this._isLastFullToggleCollapsed = this.gridOptions?.treeDataOptions?.initiallyCollapsed ?? false;
this._currentToggledItems = this.gridOptions.presets?.treeData?.toggledItems ?? [];
this._lastToggleStateChange = {
type: this._isLastFullToggleCollapsed ? 'full-collapse' : 'full-expand',
previousFullToggleType: this._isLastFullToggleCollapsed ? 'full-collapse' : 'full-expand',
toggledItems: this._currentToggledItems
};

// there's a few limitations with Tree Data, we'll just throw error when that happens
if (this.gridOptions?.enableTreeData) {
Expand Down Expand Up @@ -109,8 +110,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 @@ -122,17 +126,34 @@ 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) {
this.pubSubService.publish('onTreeItemToggled', {
...this._lastToggleStateChange,
fromItemId: collapsedItem.itemId,
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
Binary file not shown.

0 comments on commit 380f2f9

Please sign in to comment.