Skip to content

Commit 3a173aa

Browse files
committed
Change subsetTreeConfig to getSubsetTreePaths
1 parent 4620661 commit 3a173aa

File tree

7 files changed

+138
-133
lines changed

7 files changed

+138
-133
lines changed

change/@itwin-tree-widget-react-8f8e08b3-854d-476d-a87b-7e0e2e51f245.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"type": "minor",
3-
"comment": "Added option to add additional filtering to models tree based on instance keys. `UseModelsTreeProps` now has `subsetTreeConfig` which enables this.",
3+
"comment": "Added option to add additional filtering to models tree based on instance keys. `UseModelsTreeProps` now has `getSubsetTreePaths` which enables this.",
44
"packageName": "@itwin/tree-widget-react",
55
"email": "100586436+JonasDov@users.noreply.github.com",
66
"dependentChangeType": "patch"

packages/itwin/tree-widget/api/tree-widget-react.api.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ export const ModelsTreeComponent: {
260260
};
261261

262262
// @public (undocumented)
263-
interface ModelsTreeComponentProps extends Pick<ModelsTreeProps, "getSchemaContext" | "selectionStorage" | "density" | "hierarchyLevelConfig" | "selectionMode" | "selectionPredicate" | "hierarchyConfig" | "visibilityHandlerOverrides" | "getFilteredPaths" | "subsetTreeConfig"> {
263+
interface ModelsTreeComponentProps extends Pick<ModelsTreeProps, "getSchemaContext" | "selectionStorage" | "density" | "hierarchyLevelConfig" | "selectionMode" | "selectionPredicate" | "hierarchyConfig" | "visibilityHandlerOverrides" | "getFilteredPaths" | "getSubsetTreePaths"> {
264264
headerButtons?: Array<(props: ModelsTreeHeaderButtonProps) => React.ReactNode>;
265265
// (undocumented)
266266
onFeatureUsed?: (feature: string) => void;
@@ -508,7 +508,7 @@ interface UseCategoriesTreeResult {
508508
export function useFocusedInstancesContext(): FocusedInstancesContext;
509509

510510
// @beta
511-
export function useModelsTree({ activeView, filter, hierarchyConfig, visibilityHandlerOverrides, getFilteredPaths, onModelsFiltered, selectionPredicate: nodeTypeSelectionPredicate, subsetTreeConfig, }: UseModelsTreeProps): UseModelsTreeResult;
511+
export function useModelsTree({ activeView, filter, hierarchyConfig, visibilityHandlerOverrides, getFilteredPaths, onModelsFiltered, selectionPredicate: nodeTypeSelectionPredicate, getSubsetTreePaths, }: UseModelsTreeProps): UseModelsTreeResult;
512512

513513
// @public
514514
export function useModelsTreeButtonProps({ imodel, viewport }: {
@@ -532,6 +532,11 @@ interface UseModelsTreeProps {
532532
}) => Promise<HierarchyFilteringPath[]>;
533533
filter?: string;
534534
}) => Promise<HierarchyFilteringPath[]>;
535+
getSubsetTreePaths?: (props: {
536+
createInstanceKeyPaths: (props: {
537+
targetItems: Array<InstanceKey | ElementsGroupInfo>;
538+
}) => Promise<HierarchyFilteringPath[]>;
539+
}) => Promise<HierarchyFilteringPath[]>;
535540
// (undocumented)
536541
hierarchyConfig?: Partial<ModelsTreeHierarchyConfiguration>;
537542
// (undocumented)
@@ -540,9 +545,6 @@ interface UseModelsTreeProps {
540545
node: PresentationHierarchyNode;
541546
type: "subject" | "model" | "category" | "element" | "elements-class-group";
542547
}) => boolean;
543-
subsetTreeConfig?: {
544-
targetItems: Array<InstanceKey>;
545-
};
546548
// (undocumented)
547549
visibilityHandlerOverrides?: ModelsTreeVisibilityHandlerOverrides;
548550
}

packages/itwin/tree-widget/src/test/trees/common/Utils.test.ts

Lines changed: 103 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -11,115 +11,113 @@ import type { HierarchyNodeIdentifiersPath } from "@itwin/presentation-hierarchi
1111

1212
describe("Utils", () => {
1313
describe("joinHierarchyFilteringPaths", () => {
14-
const subject = {id: "0x1", className: "s", imodelKey: "key"};
15-
const model = {id: "0x2", className: "m", imodelKey: "key"};
16-
const category = {id: "0x3", className: "c", imodelKey: "key"};
17-
const category2 = {id: "0x3", className: "c", imodelKey: "key"};
18-
const element = {id: "0x4", className: "c", imodelKey: "key"};
19-
const element2 = {id: "0x5", className: "c", imodelKey: "key"};
20-
const element3 = {id: "0x6", className: "c", imodelKey: "key"};
21-
const element4 = {id: "0x7", className: "c", imodelKey: "key"};
14+
const subject = { id: "0x1", className: "s", imodelKey: "key" };
15+
const model = { id: "0x2", className: "m", imodelKey: "key" };
16+
const category = { id: "0x3", className: "c", imodelKey: "key" };
17+
const category2 = { id: "0x3", className: "c", imodelKey: "key" };
18+
const element = { id: "0x4", className: "c", imodelKey: "key" };
19+
const element2 = { id: "0x5", className: "c", imodelKey: "key" };
20+
const element3 = { id: "0x6", className: "c", imodelKey: "key" };
21+
const element4 = { id: "0x7", className: "c", imodelKey: "key" };
2222

23-
it("returns empty when filter and subset paths dont overlap", () => {
24-
const subsetPaths: HierarchyNodeIdentifiersPath[] = [
25-
[subject, model],
26-
]
27-
const filterPaths: HierarchyFilteringPath[] = [
28-
[subject, {...model, imodelKey: "random"}],
29-
[subject, {...model, className: "random"}],
30-
[subject, {...model, id: "random"}],
31-
[subject, category],
32-
[category, model],
33-
[model],
34-
[category],
35-
[]
36-
]
37-
const joinedPaths = joinHierarchyFilteringPaths(subsetPaths, filterPaths);
38-
expect(joinedPaths).to.deep.eq([])
39-
});
23+
it("returns empty when filter and subset paths dont overlap", () => {
24+
const subsetPaths: HierarchyNodeIdentifiersPath[] = [[subject, model]];
25+
const filterPaths: HierarchyFilteringPath[] = [
26+
[subject, { ...model, imodelKey: "random" }],
27+
[subject, { ...model, className: "random" }],
28+
[subject, { ...model, id: "random" }],
29+
[subject, category],
30+
[category, model],
31+
[model],
32+
[category],
33+
[],
34+
];
35+
const joinedPaths = joinHierarchyFilteringPaths(subsetPaths, filterPaths);
36+
expect(joinedPaths).to.deep.eq([]);
37+
});
4038

41-
it("returns subset paths when filter paths are shorter than subset paths", () => {
42-
const subsetPaths: HierarchyNodeIdentifiersPath[] = [
43-
[subject, model],
44-
[model, category, element, element2],
45-
[model, category, element, element3],
46-
[model, category, element2, element3],
47-
[model, category2, element4],
48-
]
49-
const filterPaths: HierarchyFilteringPath[] = [
50-
[subject],
51-
{ path: [model, category, element], options: { autoExpand: true } },
52-
{ path: [model, category], options: { autoExpand: { depth: 1 } } },
53-
{ path: [model, category, element2], options: { autoExpand: { depth: 2 } } },
54-
{ path: [model, category2], options: { autoExpand: true } },
55-
]
56-
const joinedPaths = joinHierarchyFilteringPaths(subsetPaths, filterPaths);
57-
const expectedPaths = [
58-
{
59-
path: [subject, model],
60-
options: undefined
61-
},
62-
{
63-
path: [model, category, element, element2],
64-
options: { autoExpand: { depth: 2 } }
65-
},
66-
{
39+
it("returns subset paths when filter paths are shorter than subset paths", () => {
40+
const subsetPaths: HierarchyNodeIdentifiersPath[] = [
41+
[subject, model],
42+
[model, category, element, element2],
43+
[model, category, element, element3],
44+
[model, category, element2, element3],
45+
[model, category2, element4],
46+
];
47+
const filterPaths: HierarchyFilteringPath[] = [
48+
[subject],
49+
{ path: [model, category, element], options: { autoExpand: true } },
50+
{ path: [model, category], options: { autoExpand: { depth: 1 } } },
51+
{ path: [model, category, element2], options: { autoExpand: { depth: 2 } } },
52+
{ path: [model, category2], options: { autoExpand: true } },
53+
];
54+
const joinedPaths = joinHierarchyFilteringPaths(subsetPaths, filterPaths);
55+
const expectedPaths = [
56+
{
57+
path: [subject, model],
58+
options: undefined,
59+
},
60+
{
61+
path: [model, category, element, element2],
62+
options: { autoExpand: { depth: 2 } },
63+
},
64+
{
6765
path: [model, category, element, element3],
68-
options: { autoExpand: { depth: 2 } }
69-
},
70-
{
71-
path: [model, category, element2, element3],
72-
options: { autoExpand: { depth: 2 } }
73-
},
74-
{
75-
path: [model, category2, element4],
76-
options: { autoExpand: { depth: 1 } }
77-
}
78-
]
79-
expect(joinedPaths).to.deep.eq(expectedPaths)
80-
});
66+
options: { autoExpand: { depth: 2 } },
67+
},
68+
{
69+
path: [model, category, element2, element3],
70+
options: { autoExpand: { depth: 2 } },
71+
},
72+
{
73+
path: [model, category2, element4],
74+
options: { autoExpand: { depth: 1 } },
75+
},
76+
];
77+
expect(joinedPaths).to.deep.eq(expectedPaths);
78+
});
8179

82-
it("returns subset paths and filter paths when filter paths are longer than subset paths", () => {
83-
const subsetPaths: HierarchyNodeIdentifiersPath[] = [
84-
[subject, model],
85-
[model, category, element, element2],
86-
[model, category, element, element3],
87-
]
88-
const filterPaths: HierarchyFilteringPath[] = [
89-
[subject, model, category],
90-
{ path: [model, category, element, element2, element3], options: { autoExpand: true } },
91-
{ path: [model, category, element, element3, element], options: { autoExpand: { depth: 2 } } },
92-
]
93-
const sortFn = (lhs: HierarchyFilteringPath, rhs: HierarchyFilteringPath) => {
94-
const lhsStr = JSON.stringify(lhs);
95-
const rhsStr = JSON.stringify(rhs);
96-
if (rhsStr === lhsStr) {
97-
return 0;
98-
}
99-
if (lhsStr < rhsStr) {
100-
return -1;
101-
}
102-
return 1;
80+
it("returns subset paths and filter paths when filter paths are longer than subset paths", () => {
81+
const subsetPaths: HierarchyNodeIdentifiersPath[] = [
82+
[subject, model],
83+
[model, category, element, element2],
84+
[model, category, element, element3],
85+
];
86+
const filterPaths: HierarchyFilteringPath[] = [
87+
[subject, model, category],
88+
{ path: [model, category, element, element2, element3], options: { autoExpand: true } },
89+
{ path: [model, category, element, element3, element], options: { autoExpand: { depth: 2 } } },
90+
];
91+
const sortFn = (lhs: HierarchyFilteringPath, rhs: HierarchyFilteringPath) => {
92+
const lhsStr = JSON.stringify(lhs);
93+
const rhsStr = JSON.stringify(rhs);
94+
if (rhsStr === lhsStr) {
95+
return 0;
96+
}
97+
if (lhsStr < rhsStr) {
98+
return -1;
10399
}
104-
const joinedPaths = joinHierarchyFilteringPaths(subsetPaths, filterPaths).sort(sortFn);
105-
const expectedPaths = [
106-
{
107-
path: subsetPaths[0],
108-
options: undefined
109-
},
110-
{
111-
path: subsetPaths[1],
112-
options: undefined
113-
},
114-
{
115-
path: subsetPaths[2],
116-
options: undefined
117-
},
118-
{ path: HierarchyFilteringPath.normalize(filterPaths[0]).path },
119-
filterPaths[1],
120-
filterPaths[2]
121-
].sort(sortFn);
122-
expect(joinedPaths).to.deep.eq(expectedPaths);
123-
});
100+
return 1;
101+
};
102+
const joinedPaths = joinHierarchyFilteringPaths(subsetPaths, filterPaths).sort(sortFn);
103+
const expectedPaths = [
104+
{
105+
path: subsetPaths[0],
106+
options: undefined,
107+
},
108+
{
109+
path: subsetPaths[1],
110+
options: undefined,
111+
},
112+
{
113+
path: subsetPaths[2],
114+
options: undefined,
115+
},
116+
{ path: HierarchyFilteringPath.normalize(filterPaths[0]).path },
117+
filterPaths[1],
118+
filterPaths[2],
119+
].sort(sortFn);
120+
expect(joinedPaths).to.deep.eq(expectedPaths);
124121
});
122+
});
125123
});

packages/itwin/tree-widget/src/tree-widget-react/components/trees/common/Utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export function joinHierarchyFilteringPaths(subsetPaths: HierarchyNodeIdentifier
103103
if (addSubsetPathToResult) {
104104
result.push({
105105
path: subsetPath,
106-
options: depth === 0 ? undefined : ({ autoExpand: depth >= subsetPath.length - 1 ? true : { depth } }),
106+
options: depth === 0 ? undefined : { autoExpand: depth >= subsetPath.length - 1 ? true : { depth } },
107107
});
108108
}
109109
});

packages/itwin/tree-widget/src/tree-widget-react/components/trees/models-tree/ModelsTree.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function ModelsTree({
3333
visibilityHandlerOverrides,
3434
getFilteredPaths,
3535
onModelsFiltered,
36-
subsetTreeConfig,
36+
getSubsetTreePaths,
3737
}: ModelsTreeProps) {
3838
const { modelsTreeProps, rendererProps } = useModelsTree({
3939
activeView,
@@ -43,7 +43,7 @@ export function ModelsTree({
4343
getFilteredPaths,
4444
onModelsFiltered,
4545
selectionPredicate,
46-
subsetTreeConfig,
46+
getSubsetTreePaths,
4747
});
4848

4949
return (

packages/itwin/tree-widget/src/tree-widget-react/components/trees/models-tree/ModelsTreeComponent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ interface ModelsTreeComponentProps
4040
| "hierarchyConfig"
4141
| "visibilityHandlerOverrides"
4242
| "getFilteredPaths"
43-
| "subsetTreeConfig"
43+
| "getSubsetTreePaths"
4444
> {
4545
/**
4646
* Renderers of header buttons. Defaults to:

packages/itwin/tree-widget/src/tree-widget-react/components/trees/models-tree/UseModelsTree.tsx

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,15 @@ export interface UseModelsTreeProps {
7070
filter?: string;
7171
}) => Promise<HierarchyFilteringPath[]>;
7272
/**
73-
* Optional props for applying custom filtering on the hierarchy.
73+
* Optional function for applying custom filtering on the hierarchy.
7474
* Use it when you want to display only part of the hierarchy, but don't want to change how filtering works.
7575
*
76-
* When defined, only nodes that are children/parents to provided targetItems will be part of the hierarchy.
76+
* When defined, only nodes that are in provided paths or children of filter targets will be part of the hierarchy.
7777
*/
78-
subsetTreeConfig?: {
79-
targetItems: Array<InstanceKey>;
80-
};
78+
getSubsetTreePaths?: (props: {
79+
/** A function that creates filtering paths based on provided target instance keys. */
80+
createInstanceKeyPaths: (props: { targetItems: Array<InstanceKey | ElementsGroupInfo> }) => Promise<HierarchyFilteringPath[]>;
81+
}) => Promise<HierarchyFilteringPath[]>;
8182
onModelsFiltered?: (modelIds: Id64String[] | undefined) => void;
8283
/**
8384
* An optional predicate to allow or prohibit selection of a node.
@@ -107,7 +108,7 @@ export function useModelsTree({
107108
getFilteredPaths,
108109
onModelsFiltered,
109110
selectionPredicate: nodeTypeSelectionPredicate,
110-
subsetTreeConfig,
111+
getSubsetTreePaths,
111112
}: UseModelsTreeProps): UseModelsTreeResult {
112113
const [filteringError, setFilteringError] = useState<ModelsTreeFilteringError | undefined>(undefined);
113114
const hierarchyConfiguration = useMemo<ModelsTreeHierarchyConfiguration>(
@@ -147,25 +148,29 @@ export function useModelsTree({
147148
const getSubsetPaths = useMemo<
148149
((...props: Parameters<Required<VisibilityTreeProps>["getFilteredPaths"]>) => Promise<HierarchyNodeIdentifiersPath[]>) | undefined
149150
>(() => {
150-
if (!subsetTreeConfig) {
151+
if (!getSubsetTreePaths) {
151152
return undefined;
152153
}
154+
153155
return async ({ imodelAccess, abortSignal }) => {
154156
try {
155-
const paths = await ModelsTreeDefinition.createInstanceKeyPaths({
156-
imodelAccess,
157-
targetItems: subsetTreeConfig.targetItems,
158-
idsCache: getModelsTreeIdsCache(),
159-
hierarchyConfig: hierarchyConfiguration,
160-
limit: "unbounded",
161-
abortSignal
157+
const paths = await getSubsetTreePaths({
158+
createInstanceKeyPaths: async ({ targetItems }) =>
159+
ModelsTreeDefinition.createInstanceKeyPaths({
160+
imodelAccess,
161+
targetItems,
162+
idsCache: getModelsTreeIdsCache(),
163+
hierarchyConfig: hierarchyConfiguration,
164+
limit: "unbounded",
165+
abortSignal,
166+
}),
162167
});
163168
return paths.map((path) => HierarchyFilteringPath.normalize(path).path);
164169
} catch (e) {
165170
return [];
166171
}
167172
};
168-
}, [getModelsTreeIdsCache, hierarchyConfiguration, subsetTreeConfig]);
173+
}, [getModelsTreeIdsCache, hierarchyConfiguration, getSubsetTreePaths]);
169174

170175
const getPaths = useMemo<VisibilityTreeProps["getFilteredPaths"] | undefined>(() => {
171176
setFilteringError(undefined);
@@ -197,7 +202,7 @@ export function useModelsTree({
197202
idsCache: getModelsTreeIdsCache(),
198203
targetItems: focusedItems,
199204
hierarchyConfig: hierarchyConfiguration,
200-
abortSignal
205+
abortSignal,
201206
}).then((createdPaths) => createdPaths.map((path) => ("path" in path ? path : { path, options: { autoExpand: true } }))),
202207
]);
203208
const joinedPaths = !subSetPaths ? focusedPaths : joinHierarchyFilteringPaths(subSetPaths, focusedPaths);
@@ -228,7 +233,7 @@ export function useModelsTree({
228233
idsCache: getModelsTreeIdsCache(),
229234
hierarchyConfig: hierarchyConfiguration,
230235
limit: "unbounded",
231-
abortSignal
236+
abortSignal,
232237
}),
233238
filter,
234239
}),
@@ -259,7 +264,7 @@ export function useModelsTree({
259264
label: filter,
260265
idsCache: getModelsTreeIdsCache(),
261266
hierarchyConfig: hierarchyConfiguration,
262-
abortSignal
267+
abortSignal,
263268
}).then((createdPaths) => createdPaths.map((path) => ("path" in path ? path : { path, options: { autoExpand: true } }))),
264269
]);
265270

0 commit comments

Comments
 (0)