diff --git a/common/changes/@itwin/imodel-browser-react/vp-fix-filtering-issue_2025-09-17-09-16.json b/common/changes/@itwin/imodel-browser-react/vp-fix-filtering-issue_2025-09-17-09-16.json
new file mode 100644
index 00000000..c5be9596
--- /dev/null
+++ b/common/changes/@itwin/imodel-browser-react/vp-fix-filtering-issue_2025-09-17-09-16.json
@@ -0,0 +1,10 @@
+{
+ "changes": [
+ {
+ "packageName": "@itwin/imodel-browser-react",
+ "comment": "Fix iModelGrid Filtering via postProcessCallback to Show Complete Results Across All Data",
+ "type": "patch"
+ }
+ ],
+ "packageName": "@itwin/imodel-browser-react"
+}
\ No newline at end of file
diff --git a/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.test.tsx b/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.test.tsx
index fb205f61..0576b836 100644
--- a/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.test.tsx
+++ b/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.test.tsx
@@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import "@testing-library/jest-dom";
-import { render } from "@testing-library/react";
+import { render, waitFor } from "@testing-library/react";
import React from "react";
-import { DataStatus, IModelCellOverrides, IModelGrid } from "../..";
+import { DataStatus, IModelCellOverrides, IModelFull, IModelGrid } from "../..";
import * as useIModelData from "./useIModelData";
describe("IModelGrid", () => {
@@ -185,4 +185,120 @@ describe("IModelGrid", () => {
menuItems = queryAllByRole("menuitem");
expect(menuItems.length).toBe(2); // Action 1 and Action 2
});
+
+ const generateMockIModels = (count: number, prefix = ""): IModelFull[] => {
+ return Array.from({ length: count }).map((_, index) => ({
+ id: `${prefix}${index}`,
+ displayName: `${prefix}${index}`,
+ name: `${prefix}${index}`,
+ description: `${prefix}-description`,
+ }));
+ };
+
+ it("should show all data with postProcessCallback filtering", async () => {
+ const pageSize = 3;
+ const firstPageIModels = generateMockIModels(3, "iModel");
+ firstPageIModels[1].description = "unmatched";
+ firstPageIModels[2].description = "unmatched";
+
+ const secondPageIModels = generateMockIModels(3, "iModel");
+ secondPageIModels[1].description = "unmatched";
+ secondPageIModels[2].description = "unmatched";
+
+ const thirdPageIModels = generateMockIModels(3, "iModel");
+
+ const mockFetchMore = jest.fn();
+
+ const useIModelDataMock = jest.spyOn(useIModelData, "useIModelData");
+
+ useIModelDataMock.mockReturnValue({
+ iModels: firstPageIModels,
+ status: DataStatus.Complete,
+ fetchMore: mockFetchMore,
+ refetchIModels: jest.fn(),
+ });
+
+ const postProcessCallback = jest
+ .fn()
+ .mockImplementation((iModels: IModelFull[]) => {
+ return iModels.filter(
+ (iModel) => iModel.description === "iModel-description"
+ );
+ });
+
+ const { rerender } = render(
+
+ );
+
+ await waitFor(() => {
+ expect(mockFetchMore).toHaveBeenCalled();
+ });
+
+ useIModelDataMock.mockReturnValue({
+ iModels: [...firstPageIModels, ...secondPageIModels],
+ status: DataStatus.Complete,
+ fetchMore: mockFetchMore,
+ refetchIModels: jest.fn(),
+ });
+
+ mockFetchMore.mockClear();
+ postProcessCallback.mockImplementation((iModels: IModelFull[]) => {
+ return iModels.filter(
+ (iModel) => iModel.description === "iModel-description"
+ );
+ });
+
+ rerender(
+
+ );
+
+ expect(postProcessCallback).toHaveBeenCalledWith(
+ [...firstPageIModels, ...secondPageIModels],
+ DataStatus.Complete,
+ undefined
+ );
+
+ await waitFor(() => {
+ expect(mockFetchMore).toHaveBeenCalled();
+ });
+
+ useIModelDataMock.mockReturnValue({
+ iModels: [...firstPageIModels, ...secondPageIModels, ...thirdPageIModels],
+ status: DataStatus.Complete,
+ fetchMore: mockFetchMore,
+ refetchIModels: jest.fn(),
+ });
+
+ mockFetchMore.mockClear();
+
+ postProcessCallback.mockImplementation((iModels: IModelFull[]) => {
+ return iModels.filter(
+ (iModel) => iModel.description === "iModel-description"
+ );
+ });
+
+ rerender(
+
+ );
+
+ expect(postProcessCallback).toHaveBeenCalledWith(
+ [...firstPageIModels, ...secondPageIModels, ...thirdPageIModels],
+ DataStatus.Complete,
+ undefined
+ );
+
+ expect(mockFetchMore).not.toHaveBeenCalled();
+ });
});
diff --git a/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.tsx b/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.tsx
index 657c6951..b7832ad4 100644
--- a/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.tsx
+++ b/packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.tsx
@@ -20,7 +20,7 @@ import { _mergeStrings } from "../../utils/_apiOverrides";
import { ContextMenuBuilderItem } from "../../utils/_buildMenuOptions";
import { IModelGhostTile } from "../iModelTiles/IModelGhostTile";
import { IModelTile, IModelTileProps } from "../iModelTiles/IModelTile";
-import { useIModelData } from "./useIModelData";
+import { DEFAULT_PAGE_SIZE, useIModelData } from "./useIModelData";
import { useIModelTableConfig } from "./useIModelTableConfig";
export interface IModelGridProps {
/**
@@ -173,6 +173,16 @@ export const IModelGrid = ({
[postProcessCallback, fetchediModels, fetchStatus, searchText]
);
+ React.useEffect(() => {
+ if (
+ iModels.length < (pageSize ?? DEFAULT_PAGE_SIZE) &&
+ fetchMore &&
+ fetchStatus !== DataStatus.Fetching
+ ) {
+ fetchMore();
+ }
+ }, [iModels.length, pageSize, fetchMore, fetchStatus]);
+
const { columns, onRowClick } = useIModelTableConfig({
iModelActions,
onThumbnailClick,
diff --git a/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelData.ts b/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelData.ts
index d2610255..82cbc23d 100644
--- a/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelData.ts
+++ b/packages/modules/imodel-browser/src/containers/iModelGrid/useIModelData.ts
@@ -25,7 +25,7 @@ export interface IModelDataHookOptions {
/** @deprecated in 2.1 It is no longer used as it has no effect on the data fetching. */
viewMode?: ViewType;
}
-const DEFAULT_PAGE_SIZE = 100;
+export const DEFAULT_PAGE_SIZE = 100;
export const useIModelData = ({
iTwinId,