@webitel/ui-sdk 26.8.117 → 26.8.118
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui-sdk.umd.cjs +96 -96
- package/package.json +1 -1
- package/src/utils/__tests__/findTreePath.spec.ts +83 -0
- package/src/utils/findTreePath.ts +33 -0
- package/src/utils/index.ts +1 -0
- package/types/.tsbuildinfo +1 -1
- package/types/utils/findTreePath.d.ts +10 -0
- package/types/utils/index.d.ts +1 -0
package/package.json
CHANGED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { findTreePath } from '../findTreePath';
|
|
4
|
+
|
|
5
|
+
interface Service {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
service?: Service[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const services: Service[] = [
|
|
12
|
+
{
|
|
13
|
+
id: 'billing',
|
|
14
|
+
name: 'Billing',
|
|
15
|
+
service: [
|
|
16
|
+
{
|
|
17
|
+
id: 'refunds',
|
|
18
|
+
name: 'Refunds',
|
|
19
|
+
service: [
|
|
20
|
+
{
|
|
21
|
+
id: 'card',
|
|
22
|
+
name: 'Card refund',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'support',
|
|
30
|
+
name: 'Support',
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const byId = (id: string) => (service: Service) => service.id === id;
|
|
35
|
+
|
|
36
|
+
describe('findTreePath', () => {
|
|
37
|
+
it('returns the nodes from the root down to the target', () => {
|
|
38
|
+
expect(
|
|
39
|
+
findTreePath(services, byId('card'), 'service')?.map(({ name }) => name),
|
|
40
|
+
).toEqual([
|
|
41
|
+
'Billing',
|
|
42
|
+
'Refunds',
|
|
43
|
+
'Card refund',
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('returns a one-node path for a root', () => {
|
|
48
|
+
expect(findTreePath(services, byId('support'), 'service')).toEqual([
|
|
49
|
+
services[1],
|
|
50
|
+
]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('returns null when nothing matches, or there is no tree', () => {
|
|
54
|
+
expect(findTreePath(services, byId('missing'), 'service')).toBeNull();
|
|
55
|
+
expect(findTreePath(undefined, byId('card'), 'service')).toBeNull();
|
|
56
|
+
expect(findTreePath([], byId('card'), 'service')).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('stops at the first match, depth first', () => {
|
|
60
|
+
const tree = [
|
|
61
|
+
{
|
|
62
|
+
id: 'a',
|
|
63
|
+
children: [
|
|
64
|
+
{
|
|
65
|
+
id: 'x',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'x',
|
|
71
|
+
},
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
expect(
|
|
75
|
+
findTreePath(tree, (node) => node.id === 'x', 'children')?.map(
|
|
76
|
+
({ id }) => id,
|
|
77
|
+
),
|
|
78
|
+
).toEqual([
|
|
79
|
+
'a',
|
|
80
|
+
'x',
|
|
81
|
+
]);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walks a tree depth-first and returns the nodes from a root down to the
|
|
3
|
+
* first node matching `isTarget`, both ends included, or `null` when nothing
|
|
4
|
+
* matches. Children are read from `childrenKey` (e.g. `service` for case
|
|
5
|
+
* service catalogs, whose services nest under `service`).
|
|
6
|
+
*
|
|
7
|
+
* The path is what a breadcrumb, an "A / B / C" label or an ancestor check
|
|
8
|
+
* needs: `path.map(({ name }) => name)`, `path.slice(0, -1)`, and so on.
|
|
9
|
+
*/
|
|
10
|
+
export function findTreePath<TNode extends object>(
|
|
11
|
+
nodes: readonly TNode[] | null | undefined,
|
|
12
|
+
isTarget: (node: TNode) => boolean,
|
|
13
|
+
// NoInfer: the node type comes from `nodes` only; inferred from the key
|
|
14
|
+
// literal instead, loosely typed trees narrowed to `{ service: any }`
|
|
15
|
+
childrenKey: NoInfer<keyof TNode>,
|
|
16
|
+
): TNode[] | null {
|
|
17
|
+
for (const node of nodes ?? []) {
|
|
18
|
+
if (isTarget(node))
|
|
19
|
+
return [
|
|
20
|
+
node,
|
|
21
|
+
];
|
|
22
|
+
const children = node[childrenKey];
|
|
23
|
+
if (Array.isArray(children)) {
|
|
24
|
+
const path = findTreePath(children as TNode[], isTarget, childrenKey);
|
|
25
|
+
if (path)
|
|
26
|
+
return [
|
|
27
|
+
node,
|
|
28
|
+
...path,
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
package/src/utils/index.ts
CHANGED