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

Virtualized Property Grid: display loader on all loads #660

Merged
merged 21 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/components-react",
"comment": "Show loading spinner in subsequent loads if delay threshold is reached VirtualizedPropertyGrid",
"type": "patch"
}
],
"packageName": "@itwin/components-react"
}
11 changes: 11 additions & 0 deletions docs/changehistory/NextVersion.md
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
# NextVersion <!-- omit from toc -->

Table of contents:

- [@itwin/components-react](#itwincomponents-react)
- [Improvements](#improvements)

## @itwin/components-react

### Improvements

- Show loading spinner in subsequent loads if delay threshold is reached `VirtualizedPropertyGrid.`
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
* @module PropertyGrid
*/

import React from "react";
import React, { useEffect, useState } from "react";
import { DelayedSpinner } from "../../common/DelayedSpinner";
import {
usePropertyGridEventHandler,
usePropertyGridModel,
usePropertyGridModelSource,
useTrackedPropertyGridModelSource,
} from "../internal/PropertyGridHooks";
import type { PropertyCategoryRendererManager } from "../PropertyCategoryRendererManager";
import type { IPropertyDataProvider } from "../PropertyDataProvider";
Expand Down Expand Up @@ -47,25 +47,41 @@ export interface VirtualizedPropertyGridWithDataProviderProps
export function VirtualizedPropertyGridWithDataProvider(
props: VirtualizedPropertyGridWithDataProviderProps
) {
const modelSource = usePropertyGridModelSource({
const [showSpinner, setShowSpinner] = useState(false);

const { modelSource, inProgress } = useTrackedPropertyGridModelSource({
dataProvider: props.dataProvider,
});

const model = usePropertyGridModel({ modelSource });
const eventHandler = usePropertyGridEventHandler({ modelSource });
useEffect(() => {
if (!inProgress) {
setShowSpinner(inProgress);
return;
}

// only set a timeout when inProgress is set to `true`
const timeout = setTimeout(() => {
setShowSpinner(inProgress);
}, 250);

if (!model) {
return (
<div className="components-virtualized-property-grid-loader">
<DelayedSpinner size="large" />
</div>
);
}
return () => clearTimeout(timeout);
}, [inProgress, model]);

return (
<VirtualizedPropertyGrid
{...props}
model={model}
eventHandler={eventHandler}
/>
<>
{showSpinner || !model ? (
<div className="components-virtualized-property-grid-loader">
<DelayedSpinner size="large" />
</div>
) : (
<VirtualizedPropertyGrid
{...props}
model={model}
eventHandler={eventHandler}
/>
)}
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export function usePropertyData(props: {
* Custom hook that creates a [[PropertyGridModelSource]] and subscribes it to data updates from the data provider.
* @throws if/when `IPropertyDataProvider.getData()` promise is rejected. The error is thrown in the React's render loop, so it can be caught using an error boundary.
* @public
* @deprecated in 4.9.0. Use useTrackedPropertyGridModelSource instead.
*/
export function usePropertyGridModelSource(props: {
dataProvider: IPropertyDataProvider;
Expand All @@ -67,6 +68,31 @@ export function usePropertyGridModelSource(props: {
return modelSource;
}

/**
* Custom hook that creates a [[PropertyGridModelSource]] and subscribes it to data updates from the data provider while also providing information on data update progress.
* @throws if/when `IPropertyDataProvider.getData()` promise is rejected. The error is thrown in the React's render loop, so it can be caught using an error boundary.
* @public
*/
export function useTrackedPropertyGridModelSource(props: {
dataProvider: IPropertyDataProvider;
}) {
const { value: propertyData, inProgress } = usePropertyData(props);
const { dataProvider } = { ...props };

// Model source needs to be recreated if data provider changes
const modelSource = useMemo(
() => new PropertyGridModelSource(new MutableGridItemFactory()),
// eslint-disable-next-line react-hooks/exhaustive-deps
[dataProvider]
);

useEffect(() => {
if (propertyData) modelSource.setPropertyData(propertyData);
}, [modelSource, propertyData]);

return { modelSource, inProgress };
}

/**
* Custom hook that creates memoized version of [[PropertyGridEventHandler]] that modifies given modelSource
* @public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,37 @@ describe("VirtualizedPropertyGridWithDataProvider", () => {
.to.be.not.null;
});

it("renders loader on subsequent selections that take longer to load", async () => {
const { container, findByText } = render(
<VirtualizedPropertyGridWithDataProvider {...defaultProps} />
);

// assert that initial property grid is loaded
await waitFor(async () => findByText("Group 1"));
await waitFor(
async () =>
expect(container.querySelector(".virtualized-grid-node")).to.be.not
.null
);

// stub getData with a method that can be manually resolved
const getDataResult = new ResolvablePromise<PropertyData>();
dataProvider.getData = async () => getDataResult;

dataProvider.onDataChanged.raiseEvent();

// do not resolve the getData promise until the loader is displayed
await waitFor(
async () =>
expect(
container.querySelector(
".components-virtualized-property-grid-loader"
)
).to.be.not.null,
{ timeout: 500 }
);
});

it("renders PropertyCategoryBlocks correctly", async () => {
const { container } = render(
<VirtualizedPropertyGridWithDataProvider {...defaultProps} />
Expand Down