Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add definitionContainers to categories tree #1172

Open
wants to merge 44 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7cf395c
Add definitionContainers to categories tree
JonasDov Jan 31, 2025
20f6c13
Update tests
JonasDov Jan 31, 2025
27ff768
Fix tests
JonasDov Jan 31, 2025
ba1904a
Merge branch 'master' into JonasD/categories-tree-with-definition-con…
JonasDov Feb 3, 2025
2f5ffa3
Fix typo: childCound -> childCount
JonasDov Feb 3, 2025
0c35d45
Rename cte constants to Upper case for readability
JonasDov Feb 3, 2025
9ab2b2d
change getAllContainedCategories to accept Id64Array
JonasDov Feb 3, 2025
7542a02
Update packages/itwin/tree-widget/src/tree-widget-react/components/tr…
JonasDov Feb 3, 2025
3a396eb
Update packages/itwin/tree-widget/src/tree-widget-react/components/tr…
JonasDov Feb 3, 2025
8b08331
Update packages/itwin/tree-widget/src/tree-widget-react/components/tr…
JonasDov Feb 3, 2025
502a66e
Update packages/itwin/tree-widget/src/tree-widget-react/components/tr…
JonasDov Feb 3, 2025
58504d7
Update packages/itwin/tree-widget/src/tree-widget-react/components/tr…
JonasDov Feb 3, 2025
4195e85
Update packages/itwin/tree-widget/src/test/trees/categories-tree/inte…
JonasDov Feb 3, 2025
6a40ff4
Merge branch 'JonasD/categories-tree-with-definition-containers' of h…
JonasDov Feb 3, 2025
c40294e
Update packages/itwin/tree-widget/src/tree-widget-react/components/tr…
JonasDov Feb 3, 2025
2a34afe
Use Promise.all in getRootDefinitionContainersAndCategories
JonasDov Feb 3, 2025
fb73449
Merge branch 'JonasD/categories-tree-with-definition-containers' of h…
JonasDov Feb 3, 2025
7e04bc6
Make as upper case
JonasDov Feb 3, 2025
9033116
Move await in propper position
JonasDov Feb 3, 2025
3c30e77
change collect -> mergeMap + toArray
JonasDov Feb 3, 2025
c5a6120
Remove validateViewsCalls
JonasDov Feb 3, 2025
b5a53ca
Adjust catgory visibility
JonasDov Feb 3, 2025
1679c5c
Create a separate file for class name definitions
JonasDov Feb 3, 2025
0865c2c
Remove activeView dependency
JonasDov Feb 3, 2025
589760d
Use HierarchyFilteringPath.normalize(path)
JonasDov Feb 3, 2025
18d904f
Adjust getCategoriesFromPaths
JonasDov Feb 3, 2025
82b585a
Rename definitionContainer to definition container in test names
JonasDov Feb 3, 2025
704539a
Rename tests
JonasDov Feb 4, 2025
47418fa
Adjust expect calls
JonasDov Feb 4, 2025
05988d0
Dispose provider
JonasDov Feb 4, 2025
c226c0e
Adjust viewport mock creation
JonasDov Feb 4, 2025
afc99b1
Adjust visibilityExpectations API
JonasDov Feb 4, 2025
1913e12
Merge branch 'master' into JonasD/categories-tree-with-definition-con…
JonasDov Feb 4, 2025
c21bd26
Update queryCategories implementation
JonasDov Feb 4, 2025
05cdb52
Fix e2e tests
JonasDov Feb 4, 2025
e3001ce
Add changeset
JonasDov Feb 4, 2025
971d6c7
Use function for DefinitionContainers in parentInstancesNodePredicate
JonasDov Feb 4, 2025
ef53d44
Adjust categoriesTreeDefinition
JonasDov Feb 4, 2025
80788d9
Update screenshots
JonasDov Feb 4, 2025
ffdc0de
Merge branch 'master' into JonasD/categories-tree-with-definition-con…
JonasDov Feb 4, 2025
2070956
Create new CategoriesTreeIdsCache when imodel or viewport change
JonasDov Feb 5, 2025
96a150f
Use useMemo for creating idsCache
JonasDov Feb 5, 2025
199b4dd
Merge branch 'master' into JonasD/categories-tree-with-definition-con…
JonasDov Feb 7, 2025
e9ee67f
Adress comments
JonasDov Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "`CategoriesTree` component rendered `Categories` as a flat list, where each `Category` had zero or more child `SubCategories`. Some iTwin.js applications started to group `Categories` under `DefinitionContainers` and wanted to see them displayed in `CategoriesTree` component. Added `DefinitionContainers` to `CategoriesTree` component. This change doesn't affect applications that don't have `DefinitionContainers`.",
"packageName": "@itwin/tree-widget-react",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions packages/itwin/tree-widget/src/test/IModelUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,24 @@ export function insertSpatialCategory(
return { className, id };
}

export function insertDefinitionContainer(
props: BaseInstanceInsertProps & { codeValue: string; modelId?: Id64String; isPrivate?: boolean } & Partial<
Omit<ElementProps, "id" | "model" | "parent" | "code">
>,
) {
const { builder, classFullName, modelId, codeValue, ...elementProps } = props;
const defaultClassName = `BisCore${props.fullClassNameSeparator ?? "."}DefinitionContainer`;
const className = classFullName ?? defaultClassName;
const model = modelId ?? IModel.dictionaryId;
const id = builder.insertElement({
classFullName: className,
model,
code: builder.createCode(model, BisCodeSpec.nullCodeSpec, codeValue),
...elementProps,
});
return { className, id };
}

export function insertDrawingCategory(
props: BaseInstanceInsertProps & { codeValue: string; modelId?: Id64String } & Partial<Omit<CategoryProps, "id" | "model" | "parent" | "code">>,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ import { ECSchemaRpcInterface } from "@itwin/ecschema-rpcinterface-common";
import { ECSchemaRpcImpl } from "@itwin/ecschema-rpcinterface-impl";
import { PresentationRpcInterface } from "@itwin/presentation-common";
import { createIModelHierarchyProvider } from "@itwin/presentation-hierarchies";
import {
HierarchyCacheMode, initialize as initializePresentationTesting, terminate as terminatePresentationTesting,
} from "@itwin/presentation-testing";
import { HierarchyCacheMode, initialize as initializePresentationTesting, terminate as terminatePresentationTesting } from "@itwin/presentation-testing";
import { CategoriesTreeDefinition } from "../../../tree-widget-react/components/trees/categories-tree/CategoriesTreeDefinition.js";
import { CategoriesTreeIdsCache } from "../../../tree-widget-react/components/trees/categories-tree/internal/CategoriesTreeIdsCache.js";
import {
buildIModel, insertDrawingCategory, insertDrawingGraphic, insertDrawingModelWithPartition, insertPhysicalElement, insertPhysicalModelWithPartition,
insertSpatialCategory, insertSubCategory,
buildIModel,
insertDefinitionContainer,
insertDrawingCategory,
insertDrawingGraphic,
insertDrawingModelWithPartition,
insertPhysicalElement,
insertPhysicalModelWithPartition,
insertSpatialCategory,
insertSubCategory,
insertSubModel,
} from "../../IModelUtils.js";
import { createIModelAccess } from "../Common.js";
import { NodeValidators, validateHierarchy } from "../HierarchyValidation.js";
Expand Down Expand Up @@ -66,6 +73,204 @@ describe("Categories tree", () => {
});
});

it("does not show definition container when it doesn't contain category", async function () {
const { imodel, ...keys } = await buildIModel(this, async (builder) => {
const physicalModel = insertPhysicalModelWithPartition({ builder, codeValue: "TestPhysicalModel" });
const definitionContainer = insertDefinitionContainer({ builder, codeValue: "DefinitionContainer" });
insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainer.id });

const category = insertSpatialCategory({ builder, codeValue: "SpatialCategory" });

insertPhysicalElement({ builder, modelId: physicalModel.id, categoryId: category.id });

return { category };
});
await validateHierarchy({
provider: createCategoryTreeProvider(imodel, "3d"),
expect: [
NodeValidators.createForInstanceNode({
instanceKeys: [keys.category],
supportsFiltering: true,
children: false,
}),
],
});
});

it("does not show definition container when it contains definition container without categories", async function () {
const { imodel } = await buildIModel(this, async (builder) => {
insertPhysicalModelWithPartition({ builder, codeValue: "TestPhysicalModel" });
const definitionContainer = insertDefinitionContainer({ builder, codeValue: "DefinitionContainer" });
const definitionModel = insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainer.id });
const definitionContainerChild = insertDefinitionContainer({ builder, codeValue: "DefinitionContainerChild", modelId: definitionModel.id });
insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainerChild.id });
});
await validateHierarchy({
provider: createCategoryTreeProvider(imodel, "3d"),
expect: [],
});
});

it("does not show definition container or category when category is private", async function () {
const { imodel } = await buildIModel(this, async (builder) => {
const physicalModel = insertPhysicalModelWithPartition({ builder, codeValue: "TestPhysicalModel" });
const definitionContainer = insertDefinitionContainer({ builder, codeValue: "DefinitionContainer" });
const definitionModel = insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainer.id });

const category = insertSpatialCategory({ builder, codeValue: "SpatialCategory", modelId: definitionModel.id, isPrivate: true });

insertPhysicalElement({ builder, modelId: physicalModel.id, categoryId: category.id });
});
await validateHierarchy({
provider: createCategoryTreeProvider(imodel, "3d"),
expect: [],
});
});

it("does not show definition container or category when definition container is private", async function () {
const { imodel } = await buildIModel(this, async (builder) => {
const physicalModel = insertPhysicalModelWithPartition({ builder, codeValue: "TestPhysicalModel" });
const definitionContainer = insertDefinitionContainer({ builder, codeValue: "DefinitionContainer", isPrivate: true });
const definitionModel = insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainer.id });

const category = insertSpatialCategory({ builder, codeValue: "SpatialCategory", modelId: definitionModel.id });

insertPhysicalElement({ builder, modelId: physicalModel.id, categoryId: category.id });
});
await validateHierarchy({
provider: createCategoryTreeProvider(imodel, "3d"),
expect: [],
});
});

it("does not show definition containers or categories when definition container contains another definition container that is private", async function () {
const { imodel } = await buildIModel(this, async (builder) => {
const physicalModel = insertPhysicalModelWithPartition({ builder, codeValue: "TestPhysicalModel" });
const definitionContainer = insertDefinitionContainer({ builder, codeValue: "DefinitionContainer" });
const definitionModel = insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainer.id });
const definitionContainerChild = insertDefinitionContainer({
builder,
codeValue: "DefinitionContainerChild",
isPrivate: true,
modelId: definitionModel.id,
});
const defintionModelChild = insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainerChild.id });

const category = insertSpatialCategory({ builder, codeValue: "SpatialCategory", modelId: defintionModelChild.id });

insertPhysicalElement({ builder, modelId: physicalModel.id, categoryId: category.id });
});
await validateHierarchy({
provider: createCategoryTreeProvider(imodel, "3d"),
expect: [],
});
});

it("shows definition container when it contains category", async function () {
const { imodel, ...keys } = await buildIModel(this, async (builder) => {
const physicalModel = insertPhysicalModelWithPartition({ builder, codeValue: "TestPhysicalModel" });
const definitionContainer = insertDefinitionContainer({ builder, codeValue: "DefinitionContainer" });
const definitionModel = insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainer.id });

const category = insertSpatialCategory({ builder, codeValue: "SpatialCategory", modelId: definitionModel.id });

insertPhysicalElement({ builder, modelId: physicalModel.id, categoryId: category.id });

return { definitionContainer, category };
});
await validateHierarchy({
provider: createCategoryTreeProvider(imodel, "3d"),
expect: [
NodeValidators.createForInstanceNode({
instanceKeys: [keys.definitionContainer],
supportsFiltering: true,
children: [
NodeValidators.createForInstanceNode({
instanceKeys: [keys.category],
label: "SpatialCategory",
children: false,
}),
],
}),
],
});
});

it("shows all definition containers when they contain category directly or indirectly", async function () {
const { imodel, ...keys } = await buildIModel(this, async (builder) => {
const physicalModel = insertPhysicalModelWithPartition({ builder, codeValue: "TestPhysicalModel" });
const definitionContainer = insertDefinitionContainer({ builder, codeValue: "DefinitionContainer" });
const definitionModel = insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainer.id });
const definitionContainerChild = insertDefinitionContainer({ builder, codeValue: "DefinitionContainerChild", modelId: definitionModel.id });
const definitionModelChild = insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainerChild.id });

const category = insertSpatialCategory({ builder, codeValue: "SpatialCategory", modelId: definitionModelChild.id });

insertPhysicalElement({ builder, modelId: physicalModel.id, categoryId: category.id });

return { definitionContainer, definitionContainerChild, category };
});
await validateHierarchy({
provider: createCategoryTreeProvider(imodel, "3d"),
expect: [
NodeValidators.createForInstanceNode({
instanceKeys: [keys.definitionContainer],
supportsFiltering: true,
children: [
NodeValidators.createForInstanceNode({
instanceKeys: [keys.definitionContainerChild],
label: "DefinitionContainerChild",
children: [
NodeValidators.createForInstanceNode({
instanceKeys: [keys.category],
label: "SpatialCategory",
children: false,
}),
],
}),
],
}),
],
});
});

it("shows root categories and definition container", async function () {
const { imodel, ...keys } = await buildIModel(this, async (builder) => {
const physicalModel = insertPhysicalModelWithPartition({ builder, codeValue: "TestPhysicalModel" });
const definitionContainer = insertDefinitionContainer({ builder, codeValue: "DefinitionContainer" });
const definitionModel = insertSubModel({ builder, classFullName: "BisCore.DefinitionModel", modeledElementId: definitionContainer.id });

const category = insertSpatialCategory({ builder, codeValue: "SpatialCategory" });
const childCategory = insertSpatialCategory({ builder, codeValue: "ScChild", modelId: definitionModel.id });

insertPhysicalElement({ builder, modelId: physicalModel.id, categoryId: category.id });
insertPhysicalElement({ builder, modelId: physicalModel.id, categoryId: childCategory.id });

return { category, definitionContainer, childCategory };
});
await validateHierarchy({
provider: createCategoryTreeProvider(imodel, "3d"),
expect: [
NodeValidators.createForInstanceNode({
instanceKeys: [keys.definitionContainer],
supportsFiltering: true,
children: [
NodeValidators.createForInstanceNode({
instanceKeys: [keys.childCategory],
label: "ScChild",
children: false,
}),
],
}),
NodeValidators.createForInstanceNode({
instanceKeys: [keys.category],
supportsFiltering: true,
children: false,
}),
],
});
});

it("does not show private 3d subCategories", async function () {
const { imodel, ...keys } = await buildIModel(this, async (builder) => {
const physicalModel = insertPhysicalModelWithPartition({ builder, codeValue: "TestPhysicalModel" });
Expand Down Expand Up @@ -162,6 +367,6 @@ function createCategoryTreeProvider(imodel: IModelConnection, viewType: "2d" | "
const imodelAccess = createIModelAccess(imodel);
return createIModelHierarchyProvider({
imodelAccess,
hierarchyDefinition: new CategoriesTreeDefinition({ imodelAccess, viewType }),
hierarchyDefinition: new CategoriesTreeDefinition({ imodelAccess, viewType, idsCache: new CategoriesTreeIdsCache(imodelAccess, viewType) }),
});
}
Loading
Loading