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

External sources tree: Add missing attributes id and getLabel #478

Merged
merged 1 commit into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions common/api/summary/tree-widget-react.exports.csv
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ internal;CreateRulesetProps = Omit
internal;createSearchRuleset(props: CreateSearchRulesetProps): Ruleset
internal;CreateSearchRulesetProps = Omit
public;createVisibilityTreeNodeRenderer: (iconsEnabled: boolean, descriptionEnabled: boolean) => (props: TreeNodeRendererProps) => JSX.Element
alpha;ExternalSourcesTree(props: ExternalSourcesTreeProps): JSX.Element
alpha;ExternalSourcesTreeComponent:
alpha;ExternalSourcesTreeProps
public;hideAllCategories(categories: string[], viewport: Viewport): Promise
public;hideAllModels(models: string[], viewport: Viewport): Promise
public;IModelContentTree: (props: IModelContentTreeProps) => JSX.Element
Expand All @@ -37,6 +40,7 @@ public;ModelsVisibilityHandler
public;ModelsVisibilityHandlerProps
public;ModelTreeComponentProps
internal;RULESET_CATEGORIES: Ruleset
internal;RULESET_EXTERNAL_SOURCES: Ruleset
internal;RULESET_IMODEL_CONTENT: Ruleset
public;showAllCategories(categories: string[], viewport: Viewport): Promise
public;showAllModels(models: string[], viewport: Viewport): Promise
Expand Down
74 changes: 46 additions & 28 deletions common/api/tree-widget-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,37 @@

/// <reference types="react" />

import { AbstractTreeNodeLoaderWithProvider } from '@itwin/components-react';
import type { AbstractTreeNodeLoaderWithProvider } from '@itwin/components-react';
import { BeEvent } from '@itwin/core-bentley';
import { ECClassGroupingNodeKey } from '@itwin/presentation-common';
import type { ECClassGroupingNodeKey } from '@itwin/presentation-common';
import { HighlightableTreeProps } from '@itwin/components-react';
import { Id64String } from '@itwin/core-bentley';
import { IDisposable } from '@itwin/core-bentley';
import { IFilteredPresentationTreeDataProvider } from '@itwin/presentation-components';
import { IModelConnection } from '@itwin/core-frontend';
import { IPresentationTreeDataProvider } from '@itwin/presentation-components';
import type { Id64String } from '@itwin/core-bentley';
import type { IDisposable } from '@itwin/core-bentley';
import type { IFilteredPresentationTreeDataProvider } from '@itwin/presentation-components';
import type { IModelConnection } from '@itwin/core-frontend';
import type { IPresentationTreeDataProvider } from '@itwin/presentation-components';
import type { Localization } from '@itwin/core-common';
import type { LocalizationOptions } from '@itwin/core-i18n';
import { NodeCheckboxRenderProps } from '@itwin/core-react';
import type { NodeCheckboxRenderProps } from '@itwin/core-react';
import { NodeKey } from '@itwin/presentation-common';
import * as React_2 from 'react';
import { default as React_3 } from 'react';
import { Ruleset } from '@itwin/presentation-common';
import type { Ruleset } from '@itwin/presentation-common';
import { SelectionMode as SelectionMode_2 } from '@itwin/components-react';
import { SingleSchemaClassSpecification } from '@itwin/presentation-common';
import type { SingleSchemaClassSpecification } from '@itwin/presentation-common';
import { StagePanelLocation } from '@itwin/appui-react';
import { StagePanelSection } from '@itwin/appui-react';
import { Subscription } from '@itwin/components-react';
import { TreeCheckboxStateChangeEventArgs } from '@itwin/components-react';
import { TreeNodeItem } from '@itwin/components-react';
import { TreeNodeRendererProps } from '@itwin/components-react';
import { TreeRendererProps } from '@itwin/components-react';
import { TreeSelectionModificationEventArgs } from '@itwin/components-react';
import { TreeSelectionReplacementEventArgs } from '@itwin/components-react';
import { UiItemsProvider } from '@itwin/appui-react';
import type { TreeCheckboxStateChangeEventArgs } from '@itwin/components-react';
import type { TreeNodeItem } from '@itwin/components-react';
import type { TreeNodeRendererProps } from '@itwin/components-react';
import type { TreeRendererProps } from '@itwin/components-react';
import type { TreeSelectionModificationEventArgs } from '@itwin/components-react';
import type { TreeSelectionReplacementEventArgs } from '@itwin/components-react';
import type { UiItemsProvider } from '@itwin/appui-react';
import { UnifiedSelectionTreeEventHandler } from '@itwin/presentation-components';
import { UnifiedSelectionTreeEventHandlerParams } from '@itwin/presentation-components';
import { ViewManager } from '@itwin/core-frontend';
import { Viewport } from '@itwin/core-frontend';
import { Widget } from '@itwin/appui-react';
import type { UnifiedSelectionTreeEventHandlerParams } from '@itwin/presentation-components';
import type { ViewManager } from '@itwin/core-frontend';
import type { Viewport } from '@itwin/core-frontend';
import type { Widget } from '@itwin/appui-react';

// @public
export function areAllModelsVisible(models: string[], viewport: Viewport): boolean;
Expand All @@ -62,7 +60,7 @@ export const CategoriesTreeComponent: {

// @public
export interface CategoriesTreeComponentProps extends Omit<CategoryTreeProps, "iModel" | "activeView" | "width" | "height" | "filterInfo" | "onFilterApplied" | "categories" | "categoryVisibilityHandler" | "viewManager"> {
headerButtons?: Array<(props: CategoriesTreeHeaderButtonProps) => React_3.ReactNode>;
headerButtons?: Array<(props: CategoriesTreeHeaderButtonProps) => React.ReactNode>;
}

// @public
Expand Down Expand Up @@ -162,6 +160,23 @@ export type CreateSearchRulesetProps = Omit<ModelsTreeHierarchyConfiguration, "e
// @public
export const createVisibilityTreeNodeRenderer: (iconsEnabled: boolean, descriptionEnabled: boolean) => (props: TreeNodeRendererProps) => JSX.Element;

// @alpha
export function ExternalSourcesTree(props: ExternalSourcesTreeProps): JSX.Element;

// @alpha
export const ExternalSourcesTreeComponent: {
(props: {}): JSX.Element | null;
id: string;
getLabel(): string;
};

// @alpha
export interface ExternalSourcesTreeProps {
height: number;
iModel: IModelConnection;
width: number;
}

// @public
export function hideAllCategories(categories: string[], viewport: Viewport): Promise<void>;

Expand All @@ -182,7 +197,7 @@ export const IModelContentTreeComponent: {
export type IModelContentTreeComponentProps = Omit<IModelContentTreeProps, "iModel" | "width" | "height">;

// @public
export interface IModelContentTreeProps extends Omit<React_3.HTMLProps<HTMLDivElement>, "children"> {
export interface IModelContentTreeProps extends Omit<React.HTMLProps<HTMLDivElement>, "children"> {
height: number;
iModel: IModelConnection;
selectionMode?: SelectionMode_2;
Expand Down Expand Up @@ -266,7 +281,7 @@ export interface ModelsTreeProps {
iModel: IModelConnection;
modelsVisibilityHandler?: ModelsVisibilityHandler;
onFilterApplied?: (filteredDataProvider: IPresentationTreeDataProvider, matchesCount: number) => void;
rootElementRef?: React_2.Ref<HTMLDivElement>;
rootElementRef?: React.Ref<HTMLDivElement>;
selectionMode?: SelectionMode_2;
selectionPredicate?: ModelsTreeSelectionPredicate;
width: number;
Expand Down Expand Up @@ -333,12 +348,15 @@ export interface ModelsVisibilityHandlerProps {

// @public
export interface ModelTreeComponentProps extends Omit<ModelsTreeProps, "iModel" | "activeView" | "width" | "height" | "filterInfo" | "onFilterApplied"> {
headerButtons?: Array<(props: ModelsTreeHeaderButtonProps) => React_3.ReactNode>;
headerButtons?: Array<(props: ModelsTreeHeaderButtonProps) => React.ReactNode>;
}

// @internal
export const RULESET_CATEGORIES: Ruleset;

// @internal
export const RULESET_EXTERNAL_SOURCES: Ruleset;

// @internal
export const RULESET_IMODEL_CONTENT: Ruleset;

Expand All @@ -362,7 +380,7 @@ export function toggleModels(models: string[], enable: boolean, viewport: Viewpo
export interface TreeDefinition {
getLabel: () => string;
id: string;
render: () => React_2.ReactNode;
render: () => React.ReactNode;
shouldShow?: (imodel: IModelConnection) => Promise<boolean>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"models": "Models",
"categories": "Categories",
"imodelContent": "iModel Content",
"externalSources": "External Sources",
"treeview": "Tree View",
"noTrees": "There are no trees available for this iModel.",
"categoriesTree": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import "../VisibilityTreeBase.scss";
import { useActiveIModelConnection } from "@itwin/appui-react";
import { TreeWidget } from "../../../TreeWidget";
import { AutoSizer } from "../../utils/AutoSizer";
import { ExternalSourcesTree } from "./ExternalSourcesTree";

Expand All @@ -14,15 +15,15 @@ import type { IModelConnection } from "@itwin/core-frontend";
* A component that displays an External Sources tree and any necessary "chrome".
* @alpha
*/
export function ExternalSourcesTreeComponent(props: {}) {
export const ExternalSourcesTreeComponent = (props: {}) => {
const iModel = useActiveIModelConnection();
if (!iModel) {
return null;
}
return (
<ExternalSourcesTreeComponentImpl {...props} iModel={iModel} />
);
}
};

function ExternalSourcesTreeComponentImpl(props: { iModel: IModelConnection }) {
return (
Expand All @@ -37,3 +38,15 @@ function ExternalSourcesTreeComponentImpl(props: { iModel: IModelConnection }) {
</AutoSizer>
);
}

/**
* Id of the component. May be used when a creating a [[TreeDefinition]] for [[ExternalSourcesTreeComponent]].
* @alpha
*/
ExternalSourcesTreeComponent.id = "external-sources-tree";

/**
* Label of the component. May be used when a creating a [[TreeDefinition]] for [[ExternalSourcesTreeComponent]].
* @alpha
*/
ExternalSourcesTreeComponent.getLabel = () => TreeWidget.translate("externalSources");
Original file line number Diff line number Diff line change
Expand Up @@ -10,57 +10,63 @@ import { IModelApp, NoRenderApp } from "@itwin/core-frontend";
import { render } from "@testing-library/react";
import * as externalSourcesTreeModule from "../../../components/trees/external-sources-tree/ExternalSourcesTree";
import * as autoSizerModule from "../../../components/utils/AutoSizer";
import { ExternalSourcesTreeComponent } from "../../../tree-widget-react";
import { ExternalSourcesTreeComponent, TreeWidget } from "../../../tree-widget-react";
import { TestUtils } from "../../TestUtils";

describe("<ExternalSourcesTreeComponent />", () => {
before(async () => {
// TODO: remove this eslint rule when tree-widget uses itwinjs-core 4.0.0 version
await NoRenderApp.startup(); // eslint-disable-line @itwin/no-internal
await TestUtils.initialize();
});

describe("#unit", () => {
before(async () => {
// TODO: remove this eslint rule when tree-widget uses itwinjs-core 4.0.0 version
await NoRenderApp.startup(); // eslint-disable-line @itwin/no-internal
await TestUtils.initialize();
});
after(async () => {
TestUtils.terminate();
await IModelApp.shutdown();
});

after(async () => {
TestUtils.terminate();
await IModelApp.shutdown();
});
afterEach(() => {
sinon.restore();
});

afterEach(() => {
sinon.restore();
describe("getLabel", () => {
it("returns translated label of the component", () => {
const translateSpy = sinon.stub(TreeWidget, "translate").returns("test label");
const result = ExternalSourcesTreeComponent.getLabel();
expect(translateSpy).to.be.calledWith("externalSources");
expect(result).to.eq("test label");
});
});

it("renders `ExternalSourcesTree` with size and iModel props", async () => {
const imodel = {} as any;
sinon.stub(UiFramework, "getIModelConnection").returns(imodel);
sinon.stub(autoSizerModule, "AutoSizer").callsFake((props) => <>{props.children({ width: 123, height: 456 })}</>);
const treeStub = sinon.stub(externalSourcesTreeModule, "ExternalSourcesTree").returns(<>test result</>);
it("renders `ExternalSourcesTree` with size and iModel props", async () => {
const imodel = {} as any;
sinon.stub(UiFramework, "getIModelConnection").returns(imodel);
sinon.stub(autoSizerModule, "AutoSizer").callsFake((props) => <>{props.children({ width: 123, height: 456 })}</>);
const treeStub = sinon.stub(externalSourcesTreeModule, "ExternalSourcesTree").returns(<>test result</>);

const { getByText } = render(
<ExternalSourcesTreeComponent />,
);
const { getByText } = render(
<ExternalSourcesTreeComponent />,
);

expect(treeStub).to.be.calledOnceWith({
width: 123,
height: 456,
iModel: imodel,
});
getByText("test result");
expect(treeStub).to.be.calledOnceWith({
width: 123,
height: 456,
iModel: imodel,
});
getByText("test result");
});

it("returns `null` if there's no active iModel", async () => {
sinon.stub(UiFramework, "getIModelConnection").returns(undefined);
const autosizerStub = sinon.stub(autoSizerModule, "AutoSizer").callsFake((props) => <>{props.children({ width: 123, height: 456 })}</>);
const treeStub = sinon.stub(externalSourcesTreeModule, "ExternalSourcesTree").returns(<></>);
it("returns `null` if there's no active iModel", async () => {
sinon.stub(UiFramework, "getIModelConnection").returns(undefined);
const autosizerStub = sinon.stub(autoSizerModule, "AutoSizer").callsFake((props) => <>{props.children({ width: 123, height: 456 })}</>);
const treeStub = sinon.stub(externalSourcesTreeModule, "ExternalSourcesTree").returns(<></>);

const { container } = render(
<ExternalSourcesTreeComponent />,
);
const { container } = render(
<ExternalSourcesTreeComponent />,
);

expect(autosizerStub).to.not.be.called;
expect(treeStub).to.not.be.called;
expect(container.innerHTML).to.be.empty;
});
expect(autosizerStub).to.not.be.called;
expect(treeStub).to.not.be.called;
expect(container.innerHTML).to.be.empty;
});
});