Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"changes": [
{
"packageName": "@itwin/imodel-browser-react",
"comment": "Add onDataStateChange callback to ITwinGrid",
"type": "minor"
},
{
"packageName": "@itwin/imodel-browser-react",
"comment": "Report `fetching` rather than `undefined` for the iTwin grid's status on the first render, which `postProcessCallback` receives as its second argument",
"type": "minor"
}
],
"packageName": "@itwin/imodel-browser-react"
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from "@itwin/itwinui-react";
import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React, { PropsWithChildren } from "react";
import { fn } from "storybook/test";

import { accessTokenArgTypes } from "../utils/storyHelp";

Expand Down Expand Up @@ -58,6 +59,7 @@ export default {
args: {
apiOverrides: { serverEnvironmentPrefix: "qa" },
requestType: "all",
onDataStateChange: fn(),
},

excludeStories: ["ITwinGrid"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import {
type IndividualITwinStateHook,
type ITwinDataQuery,
type ITwinDataState,
type ITwinFull,
type ITwinGridProps,
DataStatus,
Expand All @@ -21,6 +23,7 @@ import Typography from "@mui/material/Typography";
import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React from "react";
import { action } from "storybook/actions";
import { fn } from "storybook/test";

import bridgeThumbnail from "../../utils/bridge.jpg";
import nightThumbnail from "../../utils/night.jpg";
Expand Down Expand Up @@ -266,6 +269,92 @@ export const WithPostProcessCallback: StoryObj<typeof ITwinGrid> = {
},
};

const describeQuery = (query: ITwinDataQuery) =>
`${query.requestType || "all"}${
query.filterText ? ` "${query.filterText}"` : ""
}`;

const WithOnDataStateChangeRender = ({
onDataStateChange: reportToActionsPanel,
...args
}: ITwinGridProps) => {
const [log, setLog] = React.useState<string[]>([]);
const fetchStartedAt = React.useRef<Record<string, number>>({});

const onDataStateChange = React.useCallback(
(state: ITwinDataState) => {
reportToActionsPanel?.(state);

const query = describeQuery(state.query);
const append = (line: string) =>
setLog((log) => [`${query}: ${line}`, ...log].slice(0, 12));

if (state.status === DataStatus.Fetching) {
fetchStartedAt.current[query] = performance.now();
append("fetching");
return;
}

const startedAt = fetchStartedAt.current[query];
const timing =
startedAt === undefined
? "from the iTwins already loaded"
: `after ${Math.round(performance.now() - startedAt)}ms`;
append(
[
`${state.status} ${timing}`,
`${state.iTwins.length} iTwins`,
state.hasMore ? "more pages remain" : undefined,
state.error ? String(state.error) : undefined,
]
.filter(Boolean)
.join(", ")
);
},
[reportToActionsPanel]
);

return (
<div>
<Typography variant="body1" sx={{ mb: 2 }}>
Property <Code>onDataStateChange</Code> reports the grid&apos;s data
state as it changes, newest first, and sends each report to the Actions
panel. Search to see a query reported before its result. The favorites
and recents tabs answer a search from the iTwins they already hold, so
they report a result with no fetch before it.
</Typography>
<Box
sx={{
mb: 2,
p: 1,
height: "12rem",
overflowY: "auto",
border: "1px solid",
borderColor: "divider",
}}
>
{log.length === 0 ? (
<Typography variant="body2">Nothing reported yet.</Typography>
) : (
log.map((line, index) => (
<Typography key={index} variant="body2">
<Code>{line}</Code>
</Typography>
))
)}
</Box>
<ITwinGrid {...args} onDataStateChange={onDataStateChange} />
</div>
);
};

export const WithOnDataStateChange: StoryObj<typeof ITwinGrid> = {
render: (args) => <WithOnDataStateChangeRender {...args} />,
args: {
apiOverrides: { serverEnvironmentPrefix: "qa" },
},
};

export const FetchAllSubclasses: StoryObj<typeof ITwinGrid> = {
args: {
apiOverrides: { serverEnvironmentPrefix: "qa" },
Expand Down Expand Up @@ -428,6 +517,7 @@ export default {
},
args: {
requestType: "all",
onDataStateChange: fn(),
},
excludeStories: ["ITwinGrid"],
} as Meta;
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ describe("ITwinGrid", () => {
expect(wrapper.getAllByRole("row").length).toEqual(3); // First row is header
});

it("should hand onDataStateChange to the data hook", () => {
const onDataStateChange = jest.fn();

render(<ITwinGrid onDataStateChange={onDataStateChange} />);

expect(useITwinData.useITwinData).toHaveBeenCalledWith(
expect.objectContaining({ onDataStateChange })
);
});

it("should not refetch iTwins favorites when component rerenders", async () => {
// Arrange
jest.spyOn(useITwinData, "useITwinData").mockReturnValue({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
ApiOverrides,
DataStatus,
ITwinCellOverrides,
ITwinDataState,
ITwinFilterOptions,
ITwinFull,
ITwinSubClass,
Expand Down Expand Up @@ -106,6 +107,11 @@ export interface ITwinGridProps {
fetchStatus: DataStatus | undefined,
totalCount: number | undefined
) => ITwinFull[];
/**
* Called with the grid's data state when it changes: once for each query, then for each result it
* returns. Does not need to be memoized.
*/
onDataStateChange?: (state: ITwinDataState) => void;
/**iTwin view mode */
viewMode?: ViewType;
/** Overrides for cell rendering in cells viewMode */
Expand Down Expand Up @@ -141,6 +147,7 @@ const ITwinGridInternal = ({
tileOverrides,
useIndividualState,
postProcessCallback,
onDataStateChange,
viewMode,
cellOverrides,
className,
Expand Down Expand Up @@ -191,6 +198,7 @@ const ITwinGridInternal = ({
orderbyOptions,
shouldRefetchFavorites,
resetShouldRefetchFavorites,
onDataStateChange,
});

const iTwins = React.useMemo(
Expand Down
Loading
Loading