Browse Source

fix(admin-ui): Preserve expanded state on moving collections

Fixes #515
Michael Bromley 5 years ago
parent
commit
8d028cf011

+ 52 - 3
packages/admin-ui/src/lib/catalog/src/components/collection-tree/array-to-tree.spec.ts

@@ -1,11 +1,17 @@
-import { arrayToTree, HasParent } from './array-to-tree';
+import { arrayToTree, HasParent, RootNode, TreeNode } from './array-to-tree';
 
 describe('arrayToTree()', () => {
     it('preserves ordering', () => {
-        const result1 = arrayToTree([{ id: '13', parent: { id: '1' } }, { id: '12', parent: { id: '1' } }]);
+        const result1 = arrayToTree([
+            { id: '13', parent: { id: '1' } },
+            { id: '12', parent: { id: '1' } },
+        ]);
         expect(result1.children.map(i => i.id)).toEqual(['13', '12']);
 
-        const result2 = arrayToTree([{ id: '12', parent: { id: '1' } }, { id: '13', parent: { id: '1' } }]);
+        const result2 = arrayToTree([
+            { id: '12', parent: { id: '1' } },
+            { id: '13', parent: { id: '1' } },
+        ]);
         expect(result2.children.map(i => i.id)).toEqual(['12', '13']);
     });
 
@@ -48,4 +54,47 @@ describe('arrayToTree()', () => {
             ],
         });
     });
+
+    it('preserves expanded state from existing RootNode', () => {
+        const existing: RootNode<TreeNode<any>> = {
+            id: '1',
+            children: [
+                {
+                    id: '12',
+                    parent: { id: '1' },
+                    expanded: false,
+                    children: [
+                        {
+                            id: '121',
+                            parent: { id: '12' },
+                            expanded: false,
+                            children: [{ id: '1211', expanded: false, parent: { id: '121' }, children: [] }],
+                        },
+                    ],
+                },
+                {
+                    id: '13',
+                    parent: { id: '1' },
+                    expanded: true,
+                    children: [
+                        { id: '132', expanded: true, parent: { id: '13' }, children: [] },
+                        { id: '131', expanded: false, parent: { id: '13' }, children: [] },
+                    ],
+                },
+            ],
+        };
+
+        const input: HasParent[] = [
+            { id: '12', parent: { id: '1' } },
+            { id: '13', parent: { id: '1' } },
+            { id: '132', parent: { id: '13' } },
+            { id: '131', parent: { id: '13' } },
+            { id: '1211', parent: { id: '121' } },
+            { id: '121', parent: { id: '12' } },
+        ];
+
+        const result = arrayToTree(input, existing);
+
+        expect(result).toEqual(existing);
+    });
 });

+ 20 - 2
packages/admin-ui/src/lib/catalog/src/components/collection-tree/array-to-tree.ts

@@ -6,9 +6,10 @@ export type RootNode<T extends HasParent> = { id?: string; children: Array<TreeN
  * Builds a tree from an array of nodes which have a parent.
  * Based on https://stackoverflow.com/a/31247960/772859, modified to preserve ordering.
  */
-export function arrayToTree<T extends HasParent>(nodes: T[]): RootNode<T> {
+export function arrayToTree<T extends HasParent>(nodes: T[], currentState?: RootNode<T>): RootNode<T> {
     const topLevelNodes: Array<TreeNode<T>> = [];
     const mappedArr: { [id: string]: TreeNode<T> } = {};
+    const currentStateMap = treeToMap(currentState);
 
     // First map the nodes of the array to an object -> create a hash table.
     for (const node of nodes) {
@@ -18,7 +19,7 @@ export function arrayToTree<T extends HasParent>(nodes: T[]): RootNode<T> {
     for (const id of nodes.map(n => n.id)) {
         if (mappedArr.hasOwnProperty(id)) {
             const mappedElem = mappedArr[id];
-            mappedElem.expanded = false;
+            mappedElem.expanded = currentStateMap.get(id)?.expanded ?? false;
             const parent = mappedElem.parent;
             if (!parent) {
                 continue;
@@ -40,3 +41,20 @@ export function arrayToTree<T extends HasParent>(nodes: T[]): RootNode<T> {
     const rootId = topLevelNodes.length ? topLevelNodes[0].parent!.id : undefined;
     return { id: rootId, children: topLevelNodes };
 }
+
+/**
+ * Converts an existing tree (as generated by the arrayToTree function) into a flat
+ * Map. This is used to persist certain states (e.g. `expanded`) when re-building the
+ * tree.
+ */
+function treeToMap<T extends HasParent>(tree?: RootNode<T>): Map<string, TreeNode<T>> {
+    const nodeMap = new Map<string, TreeNode<T>>();
+    function visit(node: TreeNode<T>) {
+        nodeMap.set(node.id, node);
+        node.children.forEach(visit);
+    }
+    if (tree) {
+        visit(tree as TreeNode<T>);
+    }
+    return nodeMap;
+}

+ 1 - 2
packages/admin-ui/src/lib/catalog/src/components/collection-tree/collection-tree.component.ts

@@ -8,7 +8,6 @@ import {
     Output,
     SimpleChanges,
 } from '@angular/core';
-
 import { Collection } from '@vendure/admin-ui/core';
 
 import { arrayToTree, HasParent, RootNode } from './array-to-tree';
@@ -32,7 +31,7 @@ export class CollectionTreeComponent implements OnChanges {
 
     ngOnChanges(changes: SimpleChanges) {
         if ('collections' in changes && this.collections) {
-            this.collectionTree = arrayToTree(this.collections);
+            this.collectionTree = arrayToTree(this.collections, this.collectionTree);
         }
     }