Skip to content

Commit a941029

Browse files
committed
feat: 🎸 added getNodeFromPath selector
added a selector that based on an id path lookup's a node
1 parent ea17dae commit a941029

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed

src/selectors/__tests__/nodes.test.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,52 @@ describe('selectors -> nodes ->', () => {
6262
expect(nodeSelectors.getNodeRenderOptions({children: [{}]})).toMatchSnapshot();
6363
});
6464
});
65+
66+
describe('getNodeFromPath', () => {
67+
test('should get a node from a path in the root of the tree', () => {
68+
expect(nodeSelectors.getNodeFromPath([Nodes[0].id], Nodes)).toEqual(Nodes[0]);
69+
expect(nodeSelectors.getNodeFromPath([Nodes[1].id], Nodes)).toEqual(Nodes[1]);
70+
expect(nodeSelectors.getNodeFromPath([Nodes[2].id], Nodes)).toEqual(Nodes[2]);
71+
});
72+
73+
test('should get a node from a path in the first set of children of the tree', () => {
74+
expect(nodeSelectors.getNodeFromPath([Nodes[0].id, Nodes[0].children[1].id], Nodes)).toEqual(
75+
Nodes[0].children[1],
76+
);
77+
});
78+
79+
test('should get a node from a path deep in the tree', () => {
80+
expect(
81+
nodeSelectors.getNodeFromPath(
82+
[Nodes[0].id, Nodes[0].children[0].id, Nodes[0].children[0].children[1].id],
83+
Nodes,
84+
),
85+
).toEqual(Nodes[0].children[0].children[1]);
86+
});
87+
88+
test('should throw custom error when the path is invalid', () => {
89+
expect(() => nodeSelectors.getNodeFromPath('', Nodes)).toThrowError('path is not an array');
90+
expect(() => nodeSelectors.getNodeFromPath({}, Nodes)).toThrowError('path is not an array');
91+
expect(() => nodeSelectors.getNodeFromPath(1245, Nodes)).toThrowError('path is not an array');
92+
expect(() => nodeSelectors.getNodeFromPath(true, Nodes)).toThrowError('path is not an array');
93+
});
94+
95+
test('should throw custom error when path does not exist in a middle node', () => {
96+
const {id: existingId1} = Nodes[0];
97+
const {id: existingId2} = Nodes[0].children[0].children[1];
98+
99+
expect(() => nodeSelectors.getNodeFromPath([existingId1, 25, existingId2], Nodes)).toThrowError(
100+
`Could not find node at ${existingId1},25,${existingId2}`,
101+
);
102+
});
103+
104+
test('should throw custom error when path does not exist in the final node', () => {
105+
const {id: existingId1} = Nodes[0];
106+
const {id: existingId2} = Nodes[0].children[0];
107+
108+
expect(() => nodeSelectors.getNodeFromPath([existingId1, existingId2, 25], Nodes)).toThrowError(
109+
`Could not find node at ${existingId1},${existingId2},25`,
110+
);
111+
});
112+
});
65113
});

src/selectors/nodes.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,40 @@ export const addNode = node => ({
9696
});
9797

9898
export const getRowIndexFromId = (flattenedTree, id) => findIndex(flattenedTree, node => node.id === id);
99+
100+
/**
101+
* Gets a node in the original tree from a provided path.
102+
*
103+
* @param {number|string[]} path - The id path to the node
104+
* @param {Object[]} tree - The Original tree
105+
*/
106+
export const getNodeFromPath = (path, tree) => {
107+
let node;
108+
let nextLevel = tree;
109+
110+
if (!Array.isArray(path)) {
111+
throw new Error('path is not an array');
112+
}
113+
114+
for (let i = 0; i < path.length; i++) {
115+
const id = path[i];
116+
117+
let nextNode = nextLevel.find(n => n.id === id);
118+
119+
if (!nextNode) {
120+
throw new Error(`Could not find node at ${path.join(',')}`);
121+
}
122+
123+
if (i === path.length - 1 && nextNode.id === id) {
124+
node = nextNode;
125+
} else {
126+
nextLevel = nextNode.children;
127+
}
128+
}
129+
130+
if (!node) {
131+
throw new Error(`Could not find node at ${path.join(',')}`);
132+
}
133+
134+
return node;
135+
};

0 commit comments

Comments
 (0)