Skip to content

Commit cc00f21

Browse files
committed
Add references to parentNode object in walk and map utils
1 parent 30326a4 commit cc00f21

File tree

2 files changed

+135
-18
lines changed

2 files changed

+135
-18
lines changed

src/utils/tree-data-utils.js

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,48 @@ export function getDescendantCount({ node, ignoreCollapsed = true }) {
6666
}
6767

6868
/**
69-
* Walk all descendants of the given node
69+
* Walk all descendants of the given node, depth-first
70+
*
71+
* @param {Object} args - Function parameters
72+
* @param {function} args.callback - Function to call on each node
73+
* @param {function} args.getNodeKey - Function to get the key from the nodeData and tree index
74+
* @param {boolean} args.ignoreCollapsed - Ignore children of nodes without `expanded` set to `true`
75+
* @param {boolean=} args.isPseudoRoot - If true, this node has no real data, and only serves
76+
* as the parent of all the nodes in the tree
77+
* @param {Object} args.node - A tree node
78+
* @param {Object=} args.parentNode - The parent node of `node`
79+
* @param {number} args.currentIndex - The treeIndex of `node`
80+
* @param {number[]|string[]} args.path - Array of keys leading up to node to be changed
81+
* @param {number[]} args.lowerSiblingCounts - An array containing the count of siblings beneath the
82+
* previous nodes in this path
83+
*
84+
* @return {number|false} nextIndex - Index of the next sibling of `node`,
85+
* or false if the walk should be terminated
7086
*/
7187
function walkDescendants({
7288
callback,
7389
getNodeKey,
7490
ignoreCollapsed,
7591
isPseudoRoot = false,
7692
node,
93+
parentNode = null,
7794
currentIndex,
7895
path = [],
7996
lowerSiblingCounts = [],
8097
}) {
8198
// The pseudo-root is not considered in the path
82-
const selfPath = !isPseudoRoot ? [ ...path, getNodeKey({ node, treeIndex: currentIndex }) ] : [];
83-
const selfInfo = !isPseudoRoot ? { node, path: selfPath, lowerSiblingCounts, treeIndex: currentIndex } : null;
99+
const selfPath = isPseudoRoot ? [] : [
100+
...path,
101+
getNodeKey({ node, treeIndex: currentIndex }),
102+
];
103+
const selfInfo = isPseudoRoot ? null : {
104+
node,
105+
parentNode,
106+
path: selfPath,
107+
lowerSiblingCounts,
108+
treeIndex: currentIndex,
109+
};
110+
84111
if (!isPseudoRoot) {
85112
const callbackResult = callback(selfInfo);
86113

@@ -105,6 +132,7 @@ function walkDescendants({
105132
getNodeKey,
106133
ignoreCollapsed,
107134
node: node.children[i],
135+
parentNode: isPseudoRoot ? null : node,
108136
currentIndex: childIndex + 1,
109137
lowerSiblingCounts: [ ...lowerSiblingCounts, childCount - i - 1 ],
110138
path: selfPath,
@@ -121,24 +149,52 @@ function walkDescendants({
121149
}
122150

123151
/**
124-
* Perform a change on the given node and all its descendants
152+
* Perform a change on the given node and all its descendants, traversing the tree depth-first
153+
*
154+
* @param {Object} args - Function parameters
155+
* @param {function} args.callback - Function to call on each node
156+
* @param {function} args.getNodeKey - Function to get the key from the nodeData and tree index
157+
* @param {boolean} args.ignoreCollapsed - Ignore children of nodes without `expanded` set to `true`
158+
* @param {boolean=} args.isPseudoRoot - If true, this node has no real data, and only serves
159+
* as the parent of all the nodes in the tree
160+
* @param {Object} args.node - A tree node
161+
* @param {Object=} args.parentNode - The parent node of `node`
162+
* @param {number} args.currentIndex - The treeIndex of `node`
163+
* @param {number[]|string[]} args.path - Array of keys leading up to node to be changed
164+
* @param {number[]} args.lowerSiblingCounts - An array containing the count of siblings beneath the
165+
* previous nodes in this path
166+
*
167+
* @return {number|false} nextIndex - Index of the next sibling of `node`,
168+
* or false if the walk should be terminated
125169
*/
126170
function mapDescendants({
127171
callback,
128172
getNodeKey,
129173
ignoreCollapsed,
130174
isPseudoRoot = false,
131175
node,
176+
parentNode = null,
132177
currentIndex,
133178
path = [],
134179
lowerSiblingCounts = [],
135180
}) {
181+
const nextNode = { ...node };
182+
136183
// The pseudo-root is not considered in the path
137-
const selfPath = !isPseudoRoot ? [ ...path, getNodeKey({ node, treeIndex: currentIndex }) ] : [];
138-
const selfInfo = !isPseudoRoot ? { node, path: selfPath, lowerSiblingCounts, treeIndex: currentIndex } : null;
184+
const selfPath = isPseudoRoot ? [] : [
185+
...path,
186+
getNodeKey({ node: nextNode, treeIndex: currentIndex }),
187+
];
188+
const selfInfo = {
189+
node: nextNode,
190+
parentNode,
191+
path: selfPath,
192+
lowerSiblingCounts,
193+
treeIndex: currentIndex,
194+
};
139195

140196
// Return self on nodes with no children or hidden children
141-
if (!node.children || (node.expanded !== true && ignoreCollapsed && !isPseudoRoot)) {
197+
if (!nextNode.children || (nextNode.expanded !== true && ignoreCollapsed && !isPseudoRoot)) {
142198
return {
143199
treeIndex: currentIndex,
144200
node: callback(selfInfo),
@@ -147,15 +203,15 @@ function mapDescendants({
147203

148204
// Get all descendants
149205
let childIndex = currentIndex;
150-
const childCount = node.children.length;
151-
let newChildren = node.children;
152-
if (typeof newChildren !== 'function') {
153-
newChildren = newChildren.map((child, i) => {
206+
const childCount = nextNode.children.length;
207+
if (typeof nextNode.children !== 'function') {
208+
nextNode.children = nextNode.children.map((child, i) => {
154209
const mapResult = mapDescendants({
155210
callback,
156211
getNodeKey,
157212
ignoreCollapsed,
158213
node: child,
214+
parentNode: isPseudoRoot ? null : nextNode,
159215
currentIndex: childIndex + 1,
160216
lowerSiblingCounts: [ ...lowerSiblingCounts, childCount - i - 1 ],
161217
path: selfPath,
@@ -167,13 +223,7 @@ function mapDescendants({
167223
}
168224

169225
return {
170-
node: callback({
171-
...selfInfo,
172-
node: {
173-
...node,
174-
children: newChildren,
175-
},
176-
}),
226+
node: callback(selfInfo),
177227
treeIndex: childIndex,
178228
};
179229
}

src/utils/tree-data-utils.test.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,21 @@ describe('walk', () => {
14421442
},
14431443
})).not.toThrow();
14441444
});
1445+
1446+
it('can get parents while walking', () => {
1447+
const treeData = [{ key: 1, children: [{ key: 12, children: [{ key: 3 }] }, { key: 4 }]}, { key: 5 }];
1448+
const results = [];
1449+
walk({
1450+
treeData,
1451+
getNodeKey: keyFromTreeIndex,
1452+
ignoreCollapsed: false,
1453+
callback: ({ parentNode }) => {
1454+
results.push(parentNode ? parentNode.key : null);
1455+
},
1456+
});
1457+
1458+
expect(results).toEqual([null, 1, 12, 1, null]);
1459+
});
14451460
});
14461461

14471462
describe('getTreeFromFlatData', () => {
@@ -1636,6 +1651,58 @@ describe('map', () => {
16361651
].forEach(checkFunction);
16371652
});
16381653

1654+
it('can get parents', () => {
1655+
checkFunction({
1656+
getNodeKey: keyFromKey,
1657+
callback: ({ node, parentNode }) => ({
1658+
...node,
1659+
parentKey: parentNode ? parentNode.key : null,
1660+
}),
1661+
ignoreCollapsed: false,
1662+
treeData: [
1663+
{
1664+
key: 1,
1665+
children: [
1666+
{
1667+
key: 12,
1668+
children: [
1669+
{ key: 3 },
1670+
],
1671+
},
1672+
{ key: 4 },
1673+
],
1674+
},
1675+
{ key: 5 }
1676+
],
1677+
expected: [
1678+
{
1679+
key: 1,
1680+
parentKey: null,
1681+
children: [
1682+
{
1683+
key: 12,
1684+
parentKey: 1,
1685+
children: [
1686+
{
1687+
key: 3,
1688+
parentKey: 12,
1689+
},
1690+
],
1691+
},
1692+
{
1693+
key: 4,
1694+
parentKey: 1,
1695+
},
1696+
],
1697+
},
1698+
{
1699+
key: 5,
1700+
parentKey: null,
1701+
},
1702+
],
1703+
});
1704+
});
1705+
16391706
it('can sort part of the tree', () => {
16401707
[
16411708
{

0 commit comments

Comments
 (0)