Skip to content

Commit 5077a9c

Browse files
JonasDovgrigaspsaskliutas
authored
Add ability to filter multiple classes by shared property (#825)
* Add ability to filter multiple classes by shared property * Update changelog * Update .changeset/tall-shrimps-beg.md Co-authored-by: Grigas <[email protected]> * Adress comments * Update api * Update tests * Update tests * Update packages/components/src/presentation-components/instance-filter-builder/Utils.ts Co-authored-by: Saulius Skliutas <[email protected]> * Rename --------- Co-authored-by: Grigas <[email protected]> Co-authored-by: Saulius Skliutas <[email protected]>
1 parent 516ed54 commit 5077a9c

File tree

8 files changed

+73
-13
lines changed

8 files changed

+73
-13
lines changed

.changeset/tall-shrimps-beg.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@itwin/presentation-components": minor
3+
---
4+
5+
`PresentationInstanceFilterDialog` and `PresentationInstanceFilterBuilder`: Fix classes selector not being updated with all classes that contain selected property. `PresentationInstanceFilterPropertyInfo` now has `sourceClassIds` and `sourceClassId` is deprecated.

packages/components/api/presentation-components.api.md

+2
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,9 @@ export interface PresentationInstanceFilterPropertyInfo {
463463
className: string;
464464
field: PropertiesField;
465465
propertyDescription: PropertyDescription;
466+
// @deprecated
466467
sourceClassId: ClassId;
468+
sourceClassIds: ClassId[];
467469
}
468470

469471
// @public

packages/components/src/presentation-components/instance-filter-builder/InstanceFilterBuilder.tsx

+12-5
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ async function computePropertiesByClasses(
262262
const filteredProperties: PresentationInstanceFilterPropertyInfo[] = [];
263263
for (const prop of properties) {
264264
// property should be shown if at least one of selected classes is derived from property source class
265-
if (ecClassInfos.some((info) => info && info.isDerivedFrom(prop.sourceClassId))) {
265+
if (ecClassInfos.some((info) => info && prop.sourceClassIds.some((sourceClassId) => info.isDerivedFrom(sourceClassId)))) {
266266
filteredProperties.push(prop);
267267
}
268268
}
@@ -276,16 +276,23 @@ async function computeClassesByProperty(
276276
imodel: IModelConnection,
277277
): Promise<ClassInfo[]> {
278278
const metadataProvider = getIModelMetadataProvider(imodel);
279-
const propertyClass = await metadataProvider.getECClassInfo(property.sourceClassId);
279+
280+
const propertyClasses = (
281+
await Promise.all(
282+
property.sourceClassIds.map(async (sourceClassId) => {
283+
return metadataProvider.getECClassInfo(sourceClassId);
284+
}),
285+
)
286+
).filter((propertyClass) => propertyClass !== undefined);
287+
280288
/* c8 ignore next 3 */
281-
if (!propertyClass) {
289+
if (propertyClasses.length === 0) {
282290
return classes;
283291
}
284292

285293
const classesWithProperty: ClassInfo[] = [];
286294
for (const currentClass of classes) {
287-
// add classes that are derived from property source class
288-
if (propertyClass.isBaseOf(currentClass.id)) {
295+
if (propertyClasses.some((propertyClass) => propertyClass.isBaseOf(currentClass.id))) {
289296
classesWithProperty.push(currentClass);
290297
}
291298
}

packages/components/src/presentation-components/instance-filter-builder/PresentationFilterBuilder.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,12 @@ export interface PresentationInstanceFilterPropertyInfo {
6161
field: PropertiesField;
6262
/** Property description */
6363
propertyDescription: PropertyDescription;
64-
/** Id of the class where this property is defined. */
64+
/** Ids of the classes where this property is defined. */
65+
sourceClassIds: ClassId[];
66+
/**
67+
* Id of the class where this property is defined.
68+
* @deprecated in 5.8. Use [[sourceClassIds]] instead.
69+
*/
6570
sourceClassId: ClassId;
6671
/** Name of the class that was used to access this property. */
6772
className: string;

packages/components/src/presentation-components/instance-filter-builder/Utils.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ export function getInstanceFilterFieldName(property: PropertyDescription) {
4141
/** @internal */
4242
export const DEFAULT_ROOT_CATEGORY_NAME = "/selected-item/";
4343

44-
function getPropertySourceClassInfo(field: PropertiesField | NestedContentField): ClassInfo {
44+
function getPropertySourceClassInfos(field: PropertiesField | NestedContentField): ClassInfo[] {
4545
if (field.parent) {
46-
return getPropertySourceClassInfo(field.parent);
46+
return getPropertySourceClassInfos(field.parent);
4747
}
4848

4949
if (field.isPropertiesField()) {
50-
return field.properties[0].property.classInfo;
50+
return field.properties.map((fieldProperty) => fieldProperty.property.classInfo);
5151
}
52-
return field.pathToPrimaryClass[field.pathToPrimaryClass.length - 1].targetClassInfo;
52+
return [field.pathToPrimaryClass[field.pathToPrimaryClass.length - 1].targetClassInfo];
5353
}
5454

5555
function getPropertyClassInfo(field: PropertiesField): ClassInfo {
@@ -112,9 +112,11 @@ export function createPropertyInfoFromPropertiesField(field: PropertiesField): P
112112
koqName: field.properties[0].property.kindOfQuantity?.name,
113113
});
114114

115+
const sourceClassIds = getPropertySourceClassInfos(field).map((classInfo) => classInfo.id);
115116
return {
116117
field,
117-
sourceClassId: getPropertySourceClassInfo(field).id,
118+
sourceClassIds,
119+
sourceClassId: sourceClassIds[0],
118120
propertyDescription,
119121
categoryLabel: categoryInfo.label,
120122
className: getPropertyClassInfo(field).name,

packages/components/src/test/_helpers/Common.ts

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export function stubRaf() {
119119

120120
export const createTestPresentationInstanceFilterPropertyInfo = (props?: Partial<PresentationInstanceFilterPropertyInfo>) => ({
121121
sourceClassId: "0x1",
122+
sourceClassIds: ["0x1"],
122123
field: createTestPropertiesContentField({ properties: [{ property: createTestPropertyInfo() }], category: createTestCategoryDescription() }),
123124
propertyDescription: {
124125
name: "TestName",

packages/components/src/test/instance-filter-builder/IntanceFilterBuilder.test.tsx

+22-2
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,15 @@ describe("usePresentationInstanceFilteringProps", () => {
231231
label: "ConcreteField2",
232232
category,
233233
});
234+
const mergedPropertiesField = createTestPropertiesContentField({
235+
properties: [
236+
{ property: { classInfo: concreteClass1, name: "mergedProp", type: "string" } },
237+
{ property: { classInfo: concreteClass2, name: "mergedProp", type: "string" } },
238+
],
239+
name: "mergedField",
240+
label: "MergedField",
241+
category,
242+
});
234243
const derivedPropertiesField = createTestPropertiesContentField({
235244
properties: [{ property: { classInfo: derivedClass, name: "derivedProp", type: "string" } }],
236245
name: "derivedField",
@@ -243,7 +252,7 @@ describe("usePresentationInstanceFilteringProps", () => {
243252
{ selectClassInfo: concreteClass2, isSelectPolymorphic: false },
244253
],
245254
categories: [category],
246-
fields: [basePropertiesField, concretePropertiesField1, concretePropertiesField2, derivedPropertiesField],
255+
fields: [basePropertiesField, concretePropertiesField1, concretePropertiesField2, mergedPropertiesField, derivedPropertiesField],
247256
});
248257

249258
const onCloseEvent = new BeEvent<() => void>();
@@ -350,7 +359,7 @@ describe("usePresentationInstanceFilteringProps", () => {
350359
act(() => {
351360
result.current.onSelectedClassesChanged([concreteClass2.id]);
352361
});
353-
await waitFor(() => expect(result.current.properties).to.have.lengthOf(2));
362+
await waitFor(() => expect(result.current.properties).to.have.lengthOf(3));
354363
});
355364

356365
it("return all properties when selected class contains all available properties", async () => {
@@ -414,6 +423,17 @@ describe("usePresentationInstanceFilteringProps", () => {
414423
await waitFor(() => expect(result.current.selectedClasses).to.have.lengthOf(2).and.containSubset([concreteClass1, concreteClass2]));
415424
});
416425

426+
it("selects all classes that have selected property", async () => {
427+
const { result } = renderHook((props: HookProps) => usePresentationInstanceFilteringProps(props.descriptor, props.imodel), { initialProps });
428+
429+
const property = result.current.properties.find((prop) => prop.displayLabel === mergedPropertiesField.label) as PropertyDescription;
430+
431+
act(() => {
432+
result.current.onRulePropertySelected(property);
433+
});
434+
await waitFor(() => expect(result.current.selectedClasses).to.have.lengthOf(2).and.containSubset([concreteClass1, concreteClass2]));
435+
});
436+
417437
it("does not change selected classes when selected property class is already selected", async () => {
418438
const { result } = renderHook((props: HookProps) => usePresentationInstanceFilteringProps(props.descriptor, props.imodel), { initialProps });
419439

packages/components/src/test/instance-filter-builder/Utils.test.snap

+18
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ Array [
3838
"typename": "string",
3939
},
4040
"sourceClassId": "0x1",
41+
"sourceClassIds": Array [
42+
"0x1",
43+
],
4144
},
4245
Object {
4346
"categoryLabel": "Root Category | Nested Category 2 | Nested Category 2 1",
@@ -75,6 +78,9 @@ Array [
7578
"typename": "string",
7679
},
7780
"sourceClassId": "0x1",
81+
"sourceClassIds": Array [
82+
"0x1",
83+
],
7884
},
7985
]
8086
`;
@@ -117,6 +123,9 @@ Array [
117123
"typename": "string",
118124
},
119125
"sourceClassId": "0x1",
126+
"sourceClassIds": Array [
127+
"0x1",
128+
],
120129
},
121130
Object {
122131
"categoryLabel": "Root Category",
@@ -163,6 +172,9 @@ Array [
163172
"typename": "string",
164173
},
165174
"sourceClassId": "0x1",
175+
"sourceClassIds": Array [
176+
"0x1",
177+
],
166178
},
167179
]
168180
`;
@@ -205,6 +217,9 @@ Array [
205217
"typename": "string",
206218
},
207219
"sourceClassId": "0x2",
220+
"sourceClassIds": Array [
221+
"0x2",
222+
],
208223
},
209224
Object {
210225
"categoryLabel": "Root Category",
@@ -242,6 +257,9 @@ Array [
242257
"typename": "string",
243258
},
244259
"sourceClassId": "0x2",
260+
"sourceClassIds": Array [
261+
"0x2",
262+
],
245263
},
246264
]
247265
`;

0 commit comments

Comments
 (0)