From 5ada274062d16c1acfe53f87c364ae2e01de3c99 Mon Sep 17 00:00:00 2001 From: Omar H Date: Tue, 7 Jan 2025 14:46:29 -0500 Subject: [PATCH 1/3] Refresh grid on iTwin/iModel action (#135) * Add forceRefresh function to ITwinTile and IModelTile components * Add option to refresh grid after iTwin/iModel action * Update onClick parameter to make value optional in ContextMenuBuilderItem --- ...ender-grid-on-action_2024-12-18-16-51.json | 10 +++++++ .../src/imodel-browser/IModelGrid.stories.tsx | 4 +-- .../containers/ITwinGrid/ITwinGrid.test.tsx | 6 ++-- .../src/containers/ITwinGrid/ITwinGrid.tsx | 3 ++ .../src/containers/ITwinGrid/ITwinTile.tsx | 13 ++++++-- .../src/containers/ITwinGrid/useITwinData.ts | 30 ++++++++++++------- .../ITwinGrid/useITwinTableConfig.tsx | 6 +++- .../src/containers/iModelGrid/IModelGrid.tsx | 3 ++ .../containers/iModelGrid/useIModelData.ts | 23 ++++++++------ .../iModelGrid/useIModelTableConfig.tsx | 6 +++- .../src/containers/iModelTiles/IModelTile.tsx | 13 ++++++-- .../src/utils/_buildMenuOptions.tsx | 9 +++--- 12 files changed, 93 insertions(+), 33 deletions(-) create mode 100644 common/changes/@itwin/imodel-browser-react/rerender-grid-on-action_2024-12-18-16-51.json diff --git a/common/changes/@itwin/imodel-browser-react/rerender-grid-on-action_2024-12-18-16-51.json b/common/changes/@itwin/imodel-browser-react/rerender-grid-on-action_2024-12-18-16-51.json new file mode 100644 index 00000000..5ff65c67 --- /dev/null +++ b/common/changes/@itwin/imodel-browser-react/rerender-grid-on-action_2024-12-18-16-51.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/imodel-browser-react", + "comment": "provide option to refresh the grid after an itwin/imodel action", + "type": "minor" + } + ], + "packageName": "@itwin/imodel-browser-react" +} \ No newline at end of file diff --git a/packages/apps/storybook/src/imodel-browser/IModelGrid.stories.tsx b/packages/apps/storybook/src/imodel-browser/IModelGrid.stories.tsx index 8dd681d1..01e665e1 100644 --- a/packages/apps/storybook/src/imodel-browser/IModelGrid.stories.tsx +++ b/packages/apps/storybook/src/imodel-browser/IModelGrid.stories.tsx @@ -216,7 +216,7 @@ const useIndividualState = (iModel: IModelFull, props: IModelTileProps) => { }), [fetchVersionsList, selection?.displayName, versions] ); - // Override the thumbnailClick so it recieves the selected version too. + // Override the thumbnailClick so it receives the selected version too. // Not great typewise, but it is an example of what someone could do if it was really needed. const onThumbnailClick = React.useCallback( (iModel: IModelFull) => { @@ -241,7 +241,7 @@ export const WithPostProcessCallback: Story = withAccessTokenOverride((args) => { const [filter, setFilter] = React.useState(""); const filterOrAddStartTile = React.useCallback( - (iModels: IModelFull[], status: DataStatus) => { + (iModels: IModelFull[], status?: DataStatus) => { if (status !== DataStatus.Complete) { return iModels; } diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.test.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.test.tsx index f6ae2a05..d7c44cc3 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.test.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.test.tsx @@ -24,6 +24,7 @@ describe("ITwinGrid", () => { ], status: DataStatus.Complete, fetchMore: undefined, + refetchITwins: jest.fn(), }); }); @@ -37,6 +38,7 @@ describe("ITwinGrid", () => { iTwins: [], status: DataStatus.Complete, fetchMore: undefined, + refetchITwins: jest.fn(), }); // Act @@ -58,11 +60,11 @@ describe("ITwinGrid", () => { it("should not refetch iTwins favorites when component rerenders", async () => { // Arrange - const fetchMore = jest.fn(); jest.spyOn(useITwinData, "useITwinData").mockReturnValue({ iTwins: [], status: DataStatus.Complete, - fetchMore, + fetchMore: jest.fn(), + refetchITwins: jest.fn(), }); // Act const signal = new AbortController().signal; diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx index 5aa93c3a..1f562c6d 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx @@ -145,6 +145,7 @@ export const ITwinGrid = ({ iTwins: fetchedItwins, status: fetchStatus, fetchMore, + refetchITwins, } = useITwinData({ requestType, iTwinSubClass, @@ -168,6 +169,7 @@ export const ITwinGrid = ({ iTwinFavorites, addITwinToFavorites, removeITwinFromFavorites, + refetchITwins, }); const noResultsText = { @@ -211,6 +213,7 @@ export const ITwinGrid = ({ isFavorite={iTwinFavorites.has(iTwin.id)} addToFavorites={addITwinToFavorites} removeFromFavorites={removeITwinFromFavorites} + refetchITwins={refetchITwins} {...tileOverrides} /> ))} diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx index 2241c716..eceb8c7c 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinTile.tsx @@ -44,6 +44,8 @@ export interface ITwinTileProps { addToFavorites?(iTwinId: string): Promise; /** Function to remove the iTwin from favorites */ removeFromFavorites?(iTwinId: string): Promise; + /** Function to refetch iTwins */ + refetchITwins?: () => void; } /** @@ -58,6 +60,7 @@ export const ITwinTile = ({ isFavorite, addToFavorites, removeFromFavorites, + refetchITwins, }: ITwinTileProps) => { const strings = _mergeStrings( { @@ -71,8 +74,14 @@ export const ITwinTile = ({ ); const moreOptions = React.useMemo( - () => _buildManagedContextMenuOptions(iTwinOptions, iTwin), - [iTwinOptions, iTwin] + () => + _buildManagedContextMenuOptions( + iTwinOptions, + iTwin, + undefined, + refetchITwins + ), + [iTwinOptions, iTwin, refetchITwins] ); return ( diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts index cac36ba4..a0c02298 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinData.ts @@ -42,6 +42,14 @@ export const useITwinData = ({ const filteredProjects = useITwinFilter(projects, filterOptions); const [page, setPage] = React.useState(0); const [morePages, setMorePages] = React.useState(true); + + const refetchData = React.useCallback(() => { + setStatus(DataStatus.Fetching); + setProjects([]); + setPage(0); + setMorePages(true); + }, []); + const fetchMore = React.useCallback(() => { setPage((page) => page + 1); }, []); @@ -57,20 +65,21 @@ export const useITwinData = ({ morePagesRef.current || !["favorites", "recents"].includes(requestType) ) { - setStatus(DataStatus.Fetching); - setProjects([]); - setPage(0); - setMorePages(true); + refetchData(); } - }, [filterOptions, requestType]); + }, [filterOptions, requestType, refetchData]); React.useEffect(() => { // If any of the dependencies change, always restart the fetch from scratch. - setStatus(DataStatus.Fetching); - setProjects([]); - setPage(0); - setMorePages(true); - }, [accessToken, requestType, iTwinSubClass, data, serverEnvironmentPrefix]); + refetchData(); + }, [ + accessToken, + requestType, + iTwinSubClass, + data, + serverEnvironmentPrefix, + refetchData, + ]); React.useEffect(() => { if (!morePages) { @@ -165,5 +174,6 @@ export const useITwinData = ({ iTwins: filteredProjects, status, fetchMore: morePages ? fetchMore : undefined, + refetchITwins: refetchData, }; }; diff --git a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx index 2994dbc9..2a158c3c 100644 --- a/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx +++ b/packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx @@ -22,6 +22,7 @@ export interface useITwinTableConfigProps { iTwinFavorites: Set; addITwinToFavorites: (iTwinId: string) => Promise; removeITwinFromFavorites: (iTwinId: string) => Promise; + refetchITwins: () => void; } export const useITwinTableConfig = ({ @@ -31,6 +32,7 @@ export const useITwinTableConfig = ({ iTwinFavorites, addITwinToFavorites, removeITwinFromFavorites, + refetchITwins, }: useITwinTableConfigProps) => { const onRowClick = (_: React.MouseEvent, row: any) => { const iTwin = row.original as ITwinFull; @@ -110,7 +112,8 @@ export const useITwinTableConfig = ({ const options = _buildManagedContextMenuOptions( iTwinActions, props.row.original, - close + close, + refetchITwins ); return options !== undefined ? options : []; }; @@ -145,6 +148,7 @@ export const useITwinTableConfig = ({ strings.tableColumnFavorites, strings.tableColumnLastModified, strings.tableColumnName, + refetchITwins, ] ); diff --git a/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.tsx b/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.tsx index abb91c30..c7e8f7bd 100644 --- a/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.tsx +++ b/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.tsx @@ -149,6 +149,7 @@ export const IModelGrid = ({ iModels: fetchediModels, status: fetchStatus, fetchMore, + refetchIModels, } = useIModelData({ accessToken, apiOverrides, @@ -170,6 +171,7 @@ export const IModelGrid = ({ iModelActions, onThumbnailClick, strings, + refetchIModels, }); const noResultsText = { @@ -206,6 +208,7 @@ export const IModelGrid = ({ onThumbnailClick={onThumbnailClick} apiOverrides={tileApiOverrides} useTileState={useIndividualState} + refetchIModels={refetchIModels} {...tileOverrides} /> ))} diff --git a/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelData.ts b/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelData.ts index fdc05b0a..af1da697 100644 --- a/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelData.ts +++ b/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelData.ts @@ -44,27 +44,30 @@ export const useIModelData = ({ const [status, setStatus] = React.useState(); const [page, setPage] = React.useState(0); const [morePages, setMorePages] = React.useState(true); + const fetchMore = React.useCallback(() => { viewMode === "cells" && setStatus(DataStatus.Fetching); status !== DataStatus.Fetching && setPage((page) => page + 1); }, [status, viewMode]); + const refetchData = React.useCallback(() => { + setStatus(DataStatus.Fetching); + setIModels([]); + setPage(0); + setMorePages(true); + }, []); + React.useEffect(() => { // If sort changes but we already have all the data, // let client side sorting do its job, otherwise, refetch from scratch. if (morePages) { - setStatus(DataStatus.Fetching); - setIModels([]); - setPage(0); - setMorePages(true); + refetchData(); } - }, [sortType, sortDescending, morePages]); + }, [sortType, sortDescending, morePages, refetchData]); + React.useEffect(() => { // If any of the dependencies change, always restart the fetch from scratch. - setStatus(DataStatus.Fetching); - setIModels([]); - setPage(0); - setMorePages(true); + refetchData(); }, [ accessToken, iTwinId, @@ -72,6 +75,7 @@ export const useIModelData = ({ apiOverrides?.serverEnvironmentPrefix, searchText, maxCount, + refetchData, ]); React.useEffect(() => { @@ -175,5 +179,6 @@ export const useIModelData = ({ iModels: sortedIModels, status, fetchMore: morePages ? fetchMore : undefined, + refetchIModels: refetchData, }; }; diff --git a/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelTableConfig.tsx b/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelTableConfig.tsx index 5fe39cba..ff561cc4 100644 --- a/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelTableConfig.tsx +++ b/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelTableConfig.tsx @@ -27,12 +27,14 @@ export interface useIModelTableConfigProps { noAuthentication: string; error: string; }; + refetchIModels: () => void; } export const useIModelTableConfig = ({ iModelActions, onThumbnailClick, strings, + refetchIModels, }: useIModelTableConfigProps) => { const onRowClick = (_: React.MouseEvent, row: any) => { const iModel = row.original as IModelFull; @@ -86,7 +88,8 @@ export const useIModelTableConfig = ({ const options = _buildManagedContextMenuOptions( iModelActions, props.row.original, - close + close, + refetchIModels ); return options !== undefined ? options : []; }; @@ -115,6 +118,7 @@ export const useIModelTableConfig = ({ strings.tableColumnDescription, strings.tableColumnLastModified, strings.tableColumnName, + refetchIModels, ] ); diff --git a/packages/modules/imodel-browser/src/containers/iModelTiles/IModelTile.tsx b/packages/modules/imodel-browser/src/containers/iModelTiles/IModelTile.tsx index 88703c39..e165d2be 100644 --- a/packages/modules/imodel-browser/src/containers/iModelTiles/IModelTile.tsx +++ b/packages/modules/imodel-browser/src/containers/iModelTiles/IModelTile.tsx @@ -27,6 +27,8 @@ export interface IModelTileProps { tileProps?: Partial; /** Object that configures different overrides for the API */ apiOverrides?: ApiOverrides; + /** Function to refetch iModels */ + refetchIModels?: () => void; } /** @@ -39,10 +41,17 @@ export const IModelTile = ({ onThumbnailClick, apiOverrides, tileProps, + refetchIModels, }: IModelTileProps) => { const moreOptions = React.useMemo( - () => _buildManagedContextMenuOptions(iModelOptions, iModel), - [iModelOptions, iModel] + () => + _buildManagedContextMenuOptions( + iModelOptions, + iModel, + undefined, + refetchIModels + ), + [iModelOptions, iModel, refetchIModels] ); const thumbnailApiOverride = apiOverrides || iModel?.thumbnail diff --git a/packages/modules/imodel-browser/src/utils/_buildMenuOptions.tsx b/packages/modules/imodel-browser/src/utils/_buildMenuOptions.tsx index c8928f7a..d6c29639 100644 --- a/packages/modules/imodel-browser/src/utils/_buildMenuOptions.tsx +++ b/packages/modules/imodel-browser/src/utils/_buildMenuOptions.tsx @@ -11,7 +11,7 @@ export interface ContextMenuBuilderItem extends Omit { key: string; visible?: boolean | ((value: T) => boolean); - onClick?: ((value?: unknown) => void) | undefined; + onClick?: ((value?: T, refetchData?: () => void) => void) | undefined; } /** Build MenuItem array for the value for each provided options @@ -20,8 +20,9 @@ export interface ContextMenuBuilderItem export const _buildManagedContextMenuOptions: ( options: ContextMenuBuilderItem[] | undefined, value: T, - closeMenu?: () => void -) => JSX.Element[] | undefined = (options, value, closeMenu) => { + closeMenu?: () => void, + refetchData?: () => void +) => JSX.Element[] | undefined = (options, value, closeMenu, refetchData) => { return options ?.filter?.(({ visible }) => { return typeof visible === "function" ? visible(value) : visible ?? true; @@ -32,7 +33,7 @@ export const _buildManagedContextMenuOptions: ( {...contextMenuProps} onClick={() => { closeMenu?.(); - onClick?.(value); + onClick?.(value, refetchData); }} key={key} value={value} From 66a75fcac63e129eeaabeeb6341bae565a310ea6 Mon Sep 17 00:00:00 2001 From: AdminComponentsWorkflow Date: Tue, 7 Jan 2025 19:47:55 +0000 Subject: [PATCH 2/3] Update changelogs [skip ci] --- .../itwin-grid-doc_2024-12-11-20-54.json | 10 ---------- ...erender-grid-on-action_2024-12-18-16-51.json | 10 ---------- packages/modules/imodel-browser/CHANGELOG.json | 17 +++++++++++++++++ packages/modules/imodel-browser/CHANGELOG.md | 9 ++++++++- 4 files changed, 25 insertions(+), 21 deletions(-) delete mode 100644 common/changes/@itwin/imodel-browser-react/itwin-grid-doc_2024-12-11-20-54.json delete mode 100644 common/changes/@itwin/imodel-browser-react/rerender-grid-on-action_2024-12-18-16-51.json diff --git a/common/changes/@itwin/imodel-browser-react/itwin-grid-doc_2024-12-11-20-54.json b/common/changes/@itwin/imodel-browser-react/itwin-grid-doc_2024-12-11-20-54.json deleted file mode 100644 index e3bfd07b..00000000 --- a/common/changes/@itwin/imodel-browser-react/itwin-grid-doc_2024-12-11-20-54.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@itwin/imodel-browser-react", - "comment": "Update access token docs for iTwinGrid and iModelGrid", - "type": "none" - } - ], - "packageName": "@itwin/imodel-browser-react" -} \ No newline at end of file diff --git a/common/changes/@itwin/imodel-browser-react/rerender-grid-on-action_2024-12-18-16-51.json b/common/changes/@itwin/imodel-browser-react/rerender-grid-on-action_2024-12-18-16-51.json deleted file mode 100644 index 5ff65c67..00000000 --- a/common/changes/@itwin/imodel-browser-react/rerender-grid-on-action_2024-12-18-16-51.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "changes": [ - { - "packageName": "@itwin/imodel-browser-react", - "comment": "provide option to refresh the grid after an itwin/imodel action", - "type": "minor" - } - ], - "packageName": "@itwin/imodel-browser-react" -} \ No newline at end of file diff --git a/packages/modules/imodel-browser/CHANGELOG.json b/packages/modules/imodel-browser/CHANGELOG.json index 79c06bf3..bcb90cf0 100644 --- a/packages/modules/imodel-browser/CHANGELOG.json +++ b/packages/modules/imodel-browser/CHANGELOG.json @@ -1,6 +1,23 @@ { "name": "@itwin/imodel-browser-react", "entries": [ + { + "version": "2.2.0", + "tag": "@itwin/imodel-browser-react_v2.2.0", + "date": "Tue, 07 Jan 2025 19:47:55 GMT", + "comments": { + "none": [ + { + "comment": "Update access token docs for iTwinGrid and iModelGrid" + } + ], + "minor": [ + { + "comment": "provide option to refresh the grid after an itwin/imodel action" + } + ] + } + }, { "version": "2.1.2", "tag": "@itwin/imodel-browser-react_v2.1.2", diff --git a/packages/modules/imodel-browser/CHANGELOG.md b/packages/modules/imodel-browser/CHANGELOG.md index a8deead4..41f513f6 100644 --- a/packages/modules/imodel-browser/CHANGELOG.md +++ b/packages/modules/imodel-browser/CHANGELOG.md @@ -1,6 +1,13 @@ # Change Log - @itwin/imodel-browser-react -This log was last generated on Mon, 09 Dec 2024 19:40:38 GMT and should not be manually modified. +This log was last generated on Tue, 07 Jan 2025 19:47:55 GMT and should not be manually modified. + +## 2.2.0 +Tue, 07 Jan 2025 19:47:55 GMT + +### Minor changes + +- provide option to refresh the grid after an itwin/imodel action ## 2.1.2 Mon, 09 Dec 2024 19:40:38 GMT From ac051b807be63aed9064a180fa166dcc329ed459 Mon Sep 17 00:00:00 2001 From: AdminComponentsWorkflow Date: Tue, 7 Jan 2025 19:47:56 +0000 Subject: [PATCH 3/3] Bump versions [skip ci] --- packages/apps/storybook/package.json | 2 +- packages/modules/imodel-browser/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/apps/storybook/package.json b/packages/apps/storybook/package.json index 1a9d2c58..44ed51ea 100644 --- a/packages/apps/storybook/package.json +++ b/packages/apps/storybook/package.json @@ -12,7 +12,7 @@ "@itwin/create-imodel-react": "^2.0.0", "@itwin/delete-imodel-react": "^2.0.0", "@itwin/delete-itwin-react": "^2.0.0", - "@itwin/imodel-browser-react": "~2.1.2", + "@itwin/imodel-browser-react": "~2.2.0", "@itwin/itwinui-react": "^2.12.18", "@itwin/manage-versions-react": "~2.0.0", "@itwin/storybook-auth-addon": "^0.1.0", diff --git a/packages/modules/imodel-browser/package.json b/packages/modules/imodel-browser/package.json index 844664b4..8b9fa675 100644 --- a/packages/modules/imodel-browser/package.json +++ b/packages/modules/imodel-browser/package.json @@ -2,7 +2,7 @@ "name": "@itwin/imodel-browser-react", "description": "Components that let the user browse the iModels of a context and select one.", "repository": "https://github.com/iTwin/admin-components-react/tree/main/packages/modules/imodel-browser", - "version": "2.1.2", + "version": "2.2.0", "main": "cjs/index.js", "module": "esm/index.js", "types": "cjs/index.d.ts",