diff --git a/packages/x-data-grid-premium/src/tests/dataSourceAggregation.DataGridPremium.test.tsx b/packages/x-data-grid-premium/src/tests/dataSourceAggregation.DataGridPremium.test.tsx
index 8a67f0acf0957..8defca0540cd1 100644
--- a/packages/x-data-grid-premium/src/tests/dataSourceAggregation.DataGridPremium.test.tsx
+++ b/packages/x-data-grid-premium/src/tests/dataSourceAggregation.DataGridPremium.test.tsx
@@ -99,18 +99,12 @@ describe(' - Data source aggregation', () => {
it('should show aggregation option in the column menu', async () => {
const { user } = render();
- await waitFor(() => {
- expect(getRowsSpy.callCount).to.be.greaterThan(0);
- });
await user.click(within(getColumnHeaderCell(0)).getByLabelText('Menu'));
expect(screen.queryByLabelText('Aggregation')).not.to.equal(null);
});
it('should not show aggregation option in the column menu when no aggregation function is defined', async () => {
const { user } = render();
- await waitFor(() => {
- expect(getRowsSpy.callCount).to.be.greaterThan(0);
- });
await user.click(within(getColumnHeaderCell(0)).getByLabelText('Menu'));
expect(screen.queryByLabelText('Aggregation')).to.equal(null);
});
@@ -123,9 +117,6 @@ describe(' - Data source aggregation', () => {
}}
/>,
);
- await waitFor(() => {
- expect(getRowsSpy.callCount).to.be.greaterThan(0);
- });
expect(getRowsSpy.args[0][0].aggregationModel).to.deep.equal({ id: 'size' });
});
diff --git a/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourceBase.ts b/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourceBase.ts
index 9df4b1832b99e..ca83df7ca1238 100644
--- a/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourceBase.ts
+++ b/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourceBase.ts
@@ -17,6 +17,7 @@ import {
GridDataSourceCache,
runIf,
} from '@mui/x-data-grid/internals';
+import { unstable_debounce as debounce } from '@mui/utils';
import { GridPrivateApiPro } from '../../../models/gridApiPro';
import { DataGridProProcessedProps } from '../../../models/dataGridProProps';
import { gridGetRowsParamsSelector, gridDataSourceErrorsSelector } from './gridDataSourceSelector';
@@ -64,9 +65,6 @@ export const useGridDataSourceBase = (
| 'unstable_dataSource'
| 'unstable_dataSourceCache'
| 'unstable_onDataSourceError'
- | 'sortingMode'
- | 'filterMode'
- | 'paginationMode'
| 'pageSizeOptions'
| 'treeData'
| 'unstable_lazyLoading'
@@ -324,6 +322,8 @@ export const useGridDataSourceBase = (
[apiRef],
);
+ const debouncedFetchRows = React.useMemo(() => debounce(fetchRows, 0), [fetchRows]);
+
const handleStrategyActivityChange = React.useCallback<
GridEventListener<'strategyAvailabilityChange'>
>(() => {
@@ -422,9 +422,9 @@ export const useGridDataSourceBase = (
},
events: {
strategyAvailabilityChange: handleStrategyActivityChange,
- sortModelChange: runIf(defaultRowsUpdateStrategyActive, () => fetchRows()),
- filterModelChange: runIf(defaultRowsUpdateStrategyActive, () => fetchRows()),
- paginationModelChange: runIf(defaultRowsUpdateStrategyActive, () => fetchRows()),
+ sortModelChange: runIf(defaultRowsUpdateStrategyActive, () => debouncedFetchRows()),
+ filterModelChange: runIf(defaultRowsUpdateStrategyActive, () => debouncedFetchRows()),
+ paginationModelChange: runIf(defaultRowsUpdateStrategyActive, () => debouncedFetchRows()),
},
};
};
diff --git a/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts b/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts
index 5e14c182e0b33..9bb5eae0ce7fd 100644
--- a/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts
+++ b/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts
@@ -1,6 +1,7 @@
import * as React from 'react';
import { RefObject } from '@mui/x-internals/types';
import { throttle } from '@mui/x-internals/throttle';
+import { unstable_debounce as debounce } from '@mui/utils';
import {
useGridApiEventHandler,
useGridSelector,
@@ -79,6 +80,15 @@ export const useGridDataSourceLazyLoader = (
const loadingTrigger = React.useRef(null);
const rowsStale = React.useRef(false);
+ const fetchRows = React.useCallback(
+ (params: Partial) => {
+ privateApiRef.current.unstable_dataSource.fetchRows(GRID_ROOT_GROUP_ID, params);
+ },
+ [privateApiRef],
+ );
+
+ const debouncedFetchRows = React.useMemo(() => debounce(fetchRows, 0), [fetchRows]);
+
// Adjust the render context range to fit the pagination model's page size
// First row index should be decreased to the start of the page, end row index should be increased to the end of the page
const adjustRowParams = React.useCallback(
@@ -108,8 +118,8 @@ export const useGridDataSourceLazyLoader = (
filterModel,
};
- privateApiRef.current.unstable_dataSource.fetchRows(GRID_ROOT_GROUP_ID, getRowsParams);
- }, [privateApiRef, sortModel, filterModel, paginationModel.pageSize]);
+ fetchRows(getRowsParams);
+ }, [privateApiRef, sortModel, filterModel, paginationModel.pageSize, fetchRows]);
const ensureValidRowCount = React.useCallback(
(previousLoadingTrigger: LoadingTrigger, newLoadingTrigger: LoadingTrigger) => {
@@ -303,7 +313,7 @@ export const useGridDataSourceLazyLoader = (
);
const handleRowCountChange = React.useCallback(() => {
- if (loadingTrigger.current === null) {
+ if (rowsStale.current || loadingTrigger.current === null) {
return;
}
@@ -314,11 +324,12 @@ export const useGridDataSourceLazyLoader = (
const handleScrolling: GridEventListener<'scrollPositionChange'> = React.useCallback(
(newScrollPosition) => {
+ if (rowsStale.current || loadingTrigger.current !== LoadingTrigger.SCROLL_END) {
+ return;
+ }
+
const renderContext = gridRenderContextSelector(privateApiRef);
- if (
- loadingTrigger.current !== LoadingTrigger.SCROLL_END ||
- previousLastRowIndex.current >= renderContext.lastRowIndex
- ) {
+ if (previousLastRowIndex.current >= renderContext.lastRowIndex) {
return;
}
@@ -336,10 +347,7 @@ export const useGridDataSourceLazyLoader = (
};
privateApiRef.current.setLoading(true);
- privateApiRef.current.unstable_dataSource.fetchRows(
- GRID_ROOT_GROUP_ID,
- adjustRowParams(getRowsParams),
- );
+ fetchRows(adjustRowParams(getRowsParams));
}
},
[
@@ -350,6 +358,7 @@ export const useGridDataSourceLazyLoader = (
dimensions,
paginationModel.pageSize,
adjustRowParams,
+ fetchRows,
],
);
@@ -357,7 +366,7 @@ export const useGridDataSourceLazyLoader = (
GridEventListener<'renderedRowsIntervalChange'>
>(
(params) => {
- if (loadingTrigger.current !== LoadingTrigger.VIEWPORT) {
+ if (rowsStale.current || loadingTrigger.current !== LoadingTrigger.VIEWPORT) {
return;
}
@@ -401,10 +410,7 @@ export const useGridDataSourceLazyLoader = (
getRowsParams.start = skeletonRowsSection.firstRowIndex;
getRowsParams.end = skeletonRowsSection.lastRowIndex;
- privateApiRef.current.unstable_dataSource.fetchRows(
- GRID_ROOT_GROUP_ID,
- adjustRowParams(getRowsParams),
- );
+ fetchRows(adjustRowParams(getRowsParams));
},
[
privateApiRef,
@@ -413,6 +419,7 @@ export const useGridDataSourceLazyLoader = (
sortModel,
filterModel,
adjustRowParams,
+ fetchRows,
],
);
@@ -444,12 +451,9 @@ export const useGridDataSourceLazyLoader = (
};
privateApiRef.current.setLoading(true);
- privateApiRef.current.unstable_dataSource.fetchRows(
- GRID_ROOT_GROUP_ID,
- adjustRowParams(getRowsParams),
- );
+ debouncedFetchRows(adjustRowParams(getRowsParams));
},
- [privateApiRef, filterModel, paginationModel.pageSize, adjustRowParams],
+ [privateApiRef, filterModel, paginationModel.pageSize, adjustRowParams, debouncedFetchRows],
);
const handleGridFilterModelChange = React.useCallback>(
@@ -464,9 +468,9 @@ export const useGridDataSourceLazyLoader = (
};
privateApiRef.current.setLoading(true);
- privateApiRef.current.unstable_dataSource.fetchRows(GRID_ROOT_GROUP_ID, getRowsParams);
+ debouncedFetchRows(getRowsParams);
},
- [privateApiRef, sortModel, paginationModel.pageSize],
+ [privateApiRef, sortModel, paginationModel.pageSize, debouncedFetchRows],
);
const handleStrategyActivityChange = React.useCallback<
diff --git a/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx
index b3746723d6f09..c5ef6d7cb2fcc 100644
--- a/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx
+++ b/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
import { useMockServer } from '@mui/x-data-grid-generator';
-import { act, createRenderer, waitFor, screen, within } from '@mui/internal-test-utils';
+import { act, createRenderer, waitFor } from '@mui/internal-test-utils';
import { expect } from 'chai';
import { RefObject } from '@mui/x-internals/types';
import {
@@ -8,44 +8,62 @@ import {
DataGridProProps,
GridApi,
GridDataSource,
- GridDataSourceCache,
GridGetRowsParams,
GridGetRowsResponse,
useGridApiRef,
} from '@mui/x-data-grid-pro';
-import { SinonSpy, spy } from 'sinon';
+import { spy } from 'sinon';
import { describeSkipIf, isJSDOM } from 'test/utils/skipIf';
import { getKeyDefault } from '../hooks/features/dataSource/cache';
-const cache = new Map();
+class TestCache {
+ private cache: Map;
-const testCache: GridDataSourceCache = {
- set: (key, value) => cache.set(getKeyDefault(key), value),
- get: (key) => cache.get(getKeyDefault(key)),
- clear: () => cache.clear(),
-};
+ constructor() {
+ this.cache = new Map();
+ }
+
+ set(key: GridGetRowsParams, value: GridGetRowsResponse) {
+ this.cache.set(getKeyDefault(key), value);
+ }
+
+ get(key: GridGetRowsParams) {
+ return this.cache.get(getKeyDefault(key));
+ }
+
+ size() {
+ return this.cache.size;
+ }
+
+ clear() {
+ this.cache.clear();
+ }
+}
+
+const pageSizeOptions = [10, 20];
+const serverOptions = { useCursorPagination: false, minDelay: 0, maxDelay: 0, verbose: false };
// Needs layout
describeSkipIf(isJSDOM)(' - Data source', () => {
const { render } = createRenderer();
+ const fetchRowsSpy = spy();
let apiRef: RefObject;
- let fetchRowsSpy: SinonSpy;
let mockServer: ReturnType;
function TestDataSource(props: Partial & { shouldRequestsFail?: boolean }) {
apiRef = useGridApiRef();
- const { shouldRequestsFail = false, ...rest } = props;
mockServer = useMockServer(
{ rowLength: 100, maxColumns: 1 },
- { useCursorPagination: false, minDelay: 0, maxDelay: 0, verbose: false },
- shouldRequestsFail,
+ serverOptions,
+ props.shouldRequestsFail ?? false,
);
- fetchRowsSpy = spy(mockServer, 'fetchRows');
+
const { fetchRows } = mockServer;
- const dataSource: GridDataSource = React.useMemo(
- () => ({
+ const dataSource: GridDataSource = React.useMemo(() => {
+ fetchRowsSpy.resetHistory();
+ return {
getRows: async (params: GridGetRowsParams) => {
const urlParams = new URLSearchParams({
filterModel: JSON.stringify(params.filterModel),
@@ -54,40 +72,34 @@ describeSkipIf(isJSDOM)(' - Data source', () => {
end: `${params.end}`,
});
- const getRowsResponse = await fetchRows(
- `https://mui.com/x/api/data-grid?${urlParams.toString()}`,
- );
+ const url = `https://mui.com/x/api/data-grid?${urlParams.toString()}`;
+ fetchRowsSpy(url);
+ const getRowsResponse = await fetchRows(url);
return {
rows: getRowsResponse.rows,
rowCount: getRowsResponse.rowCount,
};
},
- }),
- [fetchRows],
- );
-
- const baselineProps = {
- unstable_dataSource: dataSource,
- columns: mockServer.columns,
- initialState: { pagination: { paginationModel: { page: 0, pageSize: 10 } } },
- disableVirtualization: true,
- pagination: true,
- pageSizeOptions: [10],
- };
+ };
+ }, [fetchRows]);
return (
-
+
);
}
- // eslint-disable-next-line mocha/no-top-level-hooks
- beforeEach(() => {
- cache.clear();
- });
-
it('should fetch the data on initial render', async () => {
render();
await waitFor(() => {
@@ -128,19 +140,32 @@ describeSkipIf(isJSDOM)(' - Data source', () => {
});
});
+ it('should re-fetch the data once if multiple models have changed', async () => {
+ const { setProps } = render();
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(1);
+ });
+
+ setProps({
+ paginationModel: { page: 1, pageSize: 10 },
+ sortModel: [{ field: 'name', sort: 'asc' }],
+ filterModel: { items: [{ field: 'name', value: 'John', operator: 'contains' }] },
+ });
+
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(2);
+ });
+ });
+
describe('Cache', () => {
it('should cache the data using the default cache', async () => {
- render();
+ const pageChangeSpy = spy();
+ render();
+
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(1);
});
-
- const dataRow1 = await screen.findByText(
- (_, el) => el?.getAttribute('data-rowindex') === '0',
- );
-
- const cell1 = within(dataRow1).getByRole('gridcell');
- const cell1Content = cell1.innerText;
+ expect(pageChangeSpy.callCount).to.equal(0);
act(() => {
apiRef.current?.setPage(1);
@@ -149,50 +174,55 @@ describeSkipIf(isJSDOM)(' - Data source', () => {
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(2);
});
-
- const dataRow2 = await screen.findByText(
- (_, el) => el?.getAttribute('data-rowindex') === '0' && el !== dataRow1,
- );
- const cell2 = within(dataRow2).getByRole('gridcell');
- const cell2Content = cell2.innerText;
- expect(cell2Content).not.to.equal(cell1Content);
+ expect(pageChangeSpy.callCount).to.equal(1);
act(() => {
apiRef.current?.setPage(0);
});
- expect(fetchRowsSpy.callCount).to.equal(2);
-
- const dataRow3 = await screen.findByText(
- (_, el) => el?.getAttribute('data-rowindex') === '0' && el !== dataRow1 && el !== dataRow2,
- );
- const cell3 = within(dataRow3).getByRole('gridcell');
- const cell3Content = cell3.innerText;
- expect(cell3Content).to.equal(cell1Content);
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(2);
+ });
+ expect(pageChangeSpy.callCount).to.equal(2);
});
it('should cache the data using the custom cache', async () => {
+ const testCache = new TestCache();
render();
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(1);
});
- expect(cache.size).to.equal(1);
+ expect(testCache.size()).to.equal(1);
});
- it('should use the cached data when the same query is made again', async () => {
- render();
+ it('should cache the data in the chunks defined by the minimum page size', async () => {
+ const testCache = new TestCache();
+ render(
+ ,
+ );
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(1);
});
- expect(cache.size).to.equal(1);
+ expect(testCache.size()).to.equal(2); // 2 chunks of 10 rows
+ });
- const dataRow1 = await screen.findByText(
- (_, el) => el?.getAttribute('data-rowindex') === '0',
+ it('should use the cached data when the same query is made again', async () => {
+ const testCache = new TestCache();
+ const pageChangeSpy = spy();
+ render(
+ ,
);
-
- const cell1 = within(dataRow1).getByRole('gridcell');
-
- const cell1Content = cell1.innerText;
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(1);
+ });
+ expect(testCache.size()).to.equal(1);
+ expect(pageChangeSpy.callCount).to.equal(0);
act(() => {
apiRef.current?.setPage(1);
@@ -201,48 +231,31 @@ describeSkipIf(isJSDOM)(' - Data source', () => {
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(2);
});
- expect(cache.size).to.equal(2);
-
- const dataRow2 = await screen.findByText(
- (_, el) => el?.getAttribute('data-rowindex') === '0' && el !== dataRow1,
- );
-
- const cell2 = within(dataRow2).getByRole('gridcell');
-
- const cell2Content = cell2.innerText;
- expect(cell2Content).not.to.equal(cell1Content);
+ await waitFor(() => {
+ expect(testCache.size()).to.equal(2);
+ });
+ expect(pageChangeSpy.callCount).to.equal(1);
act(() => {
apiRef.current?.setPage(0);
});
- const dataRow3 = await screen.findByText(
- (_, el) => el?.getAttribute('data-rowindex') === '0' && el !== dataRow1 && el !== dataRow2,
- );
-
- const cell3 = within(dataRow3).getByRole('gridcell');
-
- const cell3Content = cell3.innerText;
- expect(cell3Content).to.equal(cell1Content);
-
- expect(fetchRowsSpy.callCount).to.equal(2);
- expect(cache.size).to.equal(2);
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(2);
+ });
+ expect(testCache.size()).to.equal(2);
+ expect(pageChangeSpy.callCount).to.equal(2);
});
it('should allow to disable the default cache', async () => {
- // only
- render();
+ const pageChangeSpy = spy();
+ render(
+ ,
+ );
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(1);
});
-
- const dataRow1 = await screen.findByText(
- (_, el) => el?.getAttribute('data-rowindex') === '0',
- );
-
- const cell1 = within(dataRow1).getByRole('gridcell');
-
- const cell1Content = cell1.innerText;
+ expect(pageChangeSpy.callCount).to.equal(0);
act(() => {
apiRef.current?.setPage(1);
@@ -251,15 +264,7 @@ describeSkipIf(isJSDOM)(' - Data source', () => {
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(2);
});
-
- const dataRow2 = await screen.findByText(
- (_, el) => el?.getAttribute('data-rowindex') === '0' && el !== dataRow1,
- );
-
- const cell2 = within(dataRow2).getByRole('gridcell');
-
- const cell2Content = cell2.innerText;
- expect(cell2Content).not.to.equal(cell1Content);
+ expect(pageChangeSpy.callCount).to.equal(1);
act(() => {
apiRef.current?.setPage(0);
@@ -268,6 +273,7 @@ describeSkipIf(isJSDOM)(' - Data source', () => {
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(3);
});
+ expect(pageChangeSpy.callCount).to.equal(2);
});
});
diff --git a/packages/x-data-grid-pro/src/tests/dataSourceLazyLoader.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/dataSourceLazyLoader.DataGridPro.test.tsx
index 26fae9e21c748..4f0864c7fb28d 100644
--- a/packages/x-data-grid-pro/src/tests/dataSourceLazyLoader.DataGridPro.test.tsx
+++ b/packages/x-data-grid-pro/src/tests/dataSourceLazyLoader.DataGridPro.test.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
import { useMockServer } from '@mui/x-data-grid-generator';
-import { createRenderer, waitFor } from '@mui/internal-test-utils';
+import { act, createRenderer, waitFor } from '@mui/internal-test-utils';
import { getRow } from 'test/utils/helperFn';
import { expect } from 'chai';
import { RefObject } from '@mui/x-internals/types';
@@ -13,17 +13,17 @@ import {
GridGetRowsResponse,
useGridApiRef,
} from '@mui/x-data-grid-pro';
-import { SinonSpy, spy } from 'sinon';
+import { spy } from 'sinon';
import { describeSkipIf, isJSDOM } from 'test/utils/skipIf';
// Needs layout
describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
const { render } = createRenderer();
const defaultTransformGetRowsResponse = (response: GridGetRowsResponse) => response;
+ const fetchRowsSpy = spy();
let transformGetRowsResponse: (response: GridGetRowsResponse) => GridGetRowsResponse;
let apiRef: RefObject;
- let fetchRowsSpy: SinonSpy;
let mockServer: ReturnType;
function TestDataSourceLazyLoader(props: Partial) {
@@ -32,11 +32,12 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
{ rowLength: 100, maxColumns: 1 },
{ useCursorPagination: false, minDelay: 0, maxDelay: 0, verbose: false },
);
- fetchRowsSpy = spy(mockServer, 'fetchRows');
+
const { fetchRows } = mockServer;
- const dataSource: GridDataSource = React.useMemo(
- () => ({
+ const dataSource: GridDataSource = React.useMemo(() => {
+ fetchRowsSpy.resetHistory();
+ return {
getRows: async (params: GridGetRowsParams) => {
const urlParams = new URLSearchParams({
filterModel: JSON.stringify(params.filterModel),
@@ -45,9 +46,9 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
end: `${params.end}`,
});
- const getRowsResponse = await fetchRows(
- `https://mui.com/x/api/data-grid?${urlParams.toString()}`,
- );
+ const url = `https://mui.com/x/api/data-grid?${urlParams.toString()}`;
+ fetchRowsSpy(url);
+ const getRowsResponse = await fetchRows(url);
const response = transformGetRowsResponse(getRowsResponse);
return {
@@ -55,21 +56,20 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
rowCount: response.rowCount,
};
},
- }),
- [fetchRows],
- );
-
- const baselineProps = {
- unstable_dataSource: dataSource,
- columns: mockServer.columns,
- unstable_lazyLoading: true,
- paginationModel: { page: 0, pageSize: 10 },
- disableVirtualization: true,
- };
+ };
+ }, [fetchRows]);
return (
-
+
);
}
@@ -86,6 +86,22 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
});
});
+ it('should re-fetch the data once if multiple models have changed', async () => {
+ const { setProps } = render();
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(1);
+ });
+
+ setProps({
+ sortModel: [{ field: 'name', sort: 'asc' }],
+ filterModel: { items: [{ field: 'name', value: 'John', operator: 'contains' }] },
+ });
+
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(2);
+ });
+ });
+
describe('Viewport loading', () => {
it('should render skeleton rows if rowCount is bigger than the number of rows', async () => {
render();
@@ -104,7 +120,9 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
// reset the spy call count
fetchRowsSpy.resetHistory();
- apiRef.current?.scrollToIndexes({ rowIndex: 10 });
+ act(() => {
+ apiRef.current?.scrollToIndexes({ rowIndex: 50 });
+ });
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(1);
@@ -119,7 +137,9 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
const initialSearchParams = new URL(fetchRowsSpy.lastCall.args[0]).searchParams;
expect(initialSearchParams.get('end')).to.equal('9');
- apiRef.current?.scrollToIndexes({ rowIndex: 10 });
+ act(() => {
+ apiRef.current?.scrollToIndexes({ rowIndex: 10 });
+ });
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(2);
@@ -128,7 +148,9 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
const beforeSortSearchParams = new URL(fetchRowsSpy.lastCall.args[0]).searchParams;
expect(beforeSortSearchParams.get('end')).to.not.equal('9');
- apiRef.current?.sortColumn(mockServer.columns[0].field, 'asc');
+ act(() => {
+ apiRef.current?.sortColumn(mockServer.columns[0].field, 'asc');
+ });
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(3);
@@ -143,7 +165,9 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
// wait until the rows are rendered
await waitFor(() => expect(getRow(0)).not.to.be.undefined);
- apiRef.current?.scrollToIndexes({ rowIndex: 10 });
+ act(() => {
+ apiRef.current?.scrollToIndexes({ rowIndex: 10 });
+ });
await waitFor(() => {
expect(fetchRowsSpy.callCount).to.equal(2);
@@ -153,14 +177,16 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
// first row is not the first page anymore
expect(beforeFilteringSearchParams.get('start')).to.not.equal('0');
- apiRef.current?.setFilterModel({
- items: [
- {
- field: mockServer.columns[0].field,
- value: '0',
- operator: 'contains',
- },
- ],
+ act(() => {
+ apiRef.current?.setFilterModel({
+ items: [
+ {
+ field: mockServer.columns[0].field,
+ value: '0',
+ operator: 'contains',
+ },
+ ],
+ });
});
await waitFor(() => {
@@ -197,8 +223,12 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
fetchRowsSpy.resetHistory();
// make one small and one big scroll that makes sure that the bottom of the grid window is reached
- apiRef.current?.scrollToIndexes({ rowIndex: 1 });
- apiRef.current?.scrollToIndexes({ rowIndex: 9 });
+ act(() => {
+ apiRef.current?.scrollToIndexes({ rowIndex: 1 });
+ });
+ act(() => {
+ apiRef.current?.scrollToIndexes({ rowIndex: 9 });
+ });
// Only one additional fetch should have been made
await waitFor(() => {
@@ -211,7 +241,14 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
// wait until the rows are rendered
await waitFor(() => expect(getRow(0)).not.to.be.undefined);
- apiRef.current?.scrollToIndexes({ rowIndex: 9 });
+ act(() => {
+ apiRef.current?.scrollToIndexes({ rowIndex: 9 });
+ });
+
+ // wait until the debounced fetch
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(2);
+ });
// wait until the rows are rendered
await waitFor(() => expect(getRow(10)).not.to.be.undefined);
@@ -220,7 +257,13 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
// last row is not the first page anymore
expect(beforeSortingSearchParams.get('end')).to.not.equal('9');
- apiRef.current?.sortColumn(mockServer.columns[0].field, 'asc');
+ act(() => {
+ apiRef.current?.sortColumn(mockServer.columns[0].field, 'asc');
+ });
+
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(3);
+ });
const afterSortingSearchParams = new URL(fetchRowsSpy.lastCall.args[0]).searchParams;
// last row is the end of the first page
@@ -232,7 +275,14 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
// wait until the rows are rendered
await waitFor(() => expect(getRow(0)).not.to.be.undefined);
- apiRef.current?.scrollToIndexes({ rowIndex: 9 });
+ act(() => {
+ apiRef.current?.scrollToIndexes({ rowIndex: 9 });
+ });
+
+ // wait until the debounced fetch
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(2);
+ });
// wait until the rows are rendered
await waitFor(() => expect(getRow(10)).not.to.be.undefined);
@@ -241,14 +291,20 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
// last row is not the first page anymore
expect(beforeFilteringSearchParams.get('end')).to.not.equal('9');
- apiRef.current?.setFilterModel({
- items: [
- {
- field: mockServer.columns[0].field,
- value: '0',
- operator: 'contains',
- },
- ],
+ act(() => {
+ apiRef.current?.setFilterModel({
+ items: [
+ {
+ field: mockServer.columns[0].field,
+ value: '0',
+ operator: 'contains',
+ },
+ ],
+ });
+ });
+
+ await waitFor(() => {
+ expect(fetchRowsSpy.callCount).to.equal(3);
});
const afterFilteringSearchParams = new URL(fetchRowsSpy.lastCall.args[0]).searchParams;
@@ -272,7 +328,9 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
setProps({ rowCount: 100 });
// The 11th row should be a skeleton
- expect(getRow(10).dataset.id).to.equal('auto-generated-skeleton-row-root-10');
+ await waitFor(() =>
+ expect(getRow(10).dataset.id).to.equal('auto-generated-skeleton-row-root-10'),
+ );
});
it('should reset the grid if the rowCount becomes unknown', async () => {
@@ -305,11 +363,15 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
fetchRowsSpy.resetHistory();
// reduce the rowCount to be more than the number of rows
- apiRef.current?.setRowCount(80);
+ act(() => {
+ apiRef.current?.setRowCount(80);
+ });
expect(fetchRowsSpy.callCount).to.equal(0);
// reduce the rowCount once more, but now to be less than the number of rows
- apiRef.current?.setRowCount(20);
+ act(() => {
+ apiRef.current?.setRowCount(20);
+ });
await waitFor(() => expect(fetchRowsSpy.callCount).to.equal(1));
});
@@ -324,7 +386,9 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => {
expect(() => getRow(10)).to.throw();
// set the rowCount via API
- apiRef.current?.setRowCount(100);
+ act(() => {
+ apiRef.current?.setRowCount(100);
+ });
// wait until the rows are added
await waitFor(() => expect(getRow(10)).not.to.be.undefined);
diff --git a/packages/x-data-grid-pro/src/tests/dataSourceTreeData.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/dataSourceTreeData.DataGridPro.test.tsx
index 556567465f326..4bcb6c8de77a1 100644
--- a/packages/x-data-grid-pro/src/tests/dataSourceTreeData.DataGridPro.test.tsx
+++ b/packages/x-data-grid-pro/src/tests/dataSourceTreeData.DataGridPro.test.tsx
@@ -13,7 +13,7 @@ import {
GridGroupNode,
useGridApiRef,
} from '@mui/x-data-grid-pro';
-import { SinonSpy, spy } from 'sinon';
+import { spy } from 'sinon';
import { getCell } from 'test/utils/helperFn';
import { describeSkipIf, isJSDOM } from 'test/utils/skipIf';
@@ -30,19 +30,21 @@ const serverOptions = { minDelay: 0, maxDelay: 0, verbose: false };
// Needs layout
describeSkipIf(isJSDOM)(' - Data source tree data', () => {
const { render } = createRenderer();
+ const fetchRowsSpy = spy();
let apiRef: RefObject;
- let fetchRowsSpy: SinonSpy;
let mockServer: ReturnType;
function TestDataSource(props: Partial & { shouldRequestsFail?: boolean }) {
apiRef = useGridApiRef();
mockServer = useMockServer(dataSetOptions, serverOptions, props.shouldRequestsFail ?? false);
- fetchRowsSpy = spy(mockServer, 'fetchRows');
- const { fetchRows, columns } = mockServer;
+ const { columns } = mockServer;
- const dataSource: GridDataSource = React.useMemo(
- () => ({
+ const { fetchRows } = mockServer;
+
+ const dataSource: GridDataSource = React.useMemo(() => {
+ fetchRowsSpy.resetHistory();
+ return {
getRows: async (params: GridGetRowsParams) => {
const urlParams = new URLSearchParams({
paginationModel: JSON.stringify(params.paginationModel),
@@ -51,9 +53,9 @@ describeSkipIf(isJSDOM)(' - Data source tree data', () => {
groupKeys: JSON.stringify(params.groupKeys),
});
- const getRowsResponse = await fetchRows(
- `https://mui.com/x/api/data-grid?${urlParams.toString()}`,
- );
+ const url = `https://mui.com/x/api/data-grid?${urlParams.toString()}`;
+ fetchRowsSpy(url);
+ const getRowsResponse = await fetchRows(url);
return {
rows: getRowsResponse.rows,
@@ -62,9 +64,8 @@ describeSkipIf(isJSDOM)(' - Data source tree data', () => {
},
getGroupKey: (row) => row[dataSetOptions.treeData.groupingField],
getChildrenCount: (row) => row.descendantCount,
- }),
- [fetchRows],
- );
+ };
+ }, [fetchRows]);
return (