@mui/x-data-grid 8.27.1 → 8.27.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,111 @@
5
5
  All notable changes to this project will be documented in this file.
6
6
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
7
7
 
8
+ ## 8.27.3
9
+
10
+ _Feb 25, 2026_
11
+
12
+ We'd like to extend a big thank you to the 4 contributors who made this release possible. Here are some highlights ✨:
13
+
14
+ - 🐞 Bugfixes
15
+ - ⚡️ Improved dynamic data support and cache invalidation in lazy loading for Data Grid Pro
16
+
17
+ The following team members contributed to this release:
18
+ @cherniavskii, @michelengelen, @MBilalShafi, @arminmeh
19
+
20
+ ### Data Grid
21
+
22
+ #### `@mui/x-data-grid@8.27.3`
23
+
24
+ - [DataGrid] Preserve key input during row edit when using `rowModesModel` (#21457) @michelengelen
25
+
26
+ #### `@mui/x-data-grid-pro@8.27.3` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
27
+
28
+ Same changes as in `@mui/x-data-grid@8.27.3`, plus:
29
+
30
+ - [DataGridPro] Improve dynamic data support and cache invalidation in lazy loading (#21465) @MBilalShafi
31
+
32
+ #### `@mui/x-data-grid-premium@8.27.3` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
33
+
34
+ Same changes as in `@mui/x-data-grid-pro@8.27.3`.
35
+
36
+ ### Core
37
+
38
+ - [code-infra] Do not append `x` to the last version for the compare API (#21422) @arminmeh
39
+ - [docs-infra] Fix current version detection logic (#21415) @cherniavskii
40
+
41
+ ## 8.27.2
42
+
43
+ _Feb 20, 2026_
44
+
45
+ We'd like to extend a big thank you to the 3 contributors who made this release possible. Here are some highlights ✨:
46
+
47
+ - 🐞 Bugfixes
48
+
49
+ ### Data Grid
50
+
51
+ #### `@mui/x-data-grid@8.27.2`
52
+
53
+ Internal changes.
54
+
55
+ #### `@mui/x-data-grid-pro@8.27.2` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
56
+
57
+ Same changes as in `@mui/x-data-grid@8.27.2`, plus:
58
+
59
+ - [DataGridPro] Fix number input visibility in header filters (#21345) @michelengelen
60
+
61
+ #### `@mui/x-data-grid-premium@8.27.2` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
62
+
63
+ Same changes as in `@mui/x-data-grid-pro@8.27.2`.
64
+
65
+ ### Date and Time Pickers
66
+
67
+ #### `@mui/x-date-pickers@8.27.2`
68
+
69
+ - [DatePicker] Add keyboard support for selecting day, month, and year (#21399) @michelengelen
70
+
71
+ #### `@mui/x-date-pickers-pro@8.27.2` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
72
+
73
+ Same changes as in `@mui/x-date-pickers@8.27.2`, plus:
74
+
75
+ - [DateRangePicker] Fix timezone update issue leading to `invalidRange` error (#21382) @michelengelen
76
+
77
+ ### Charts
78
+
79
+ #### `@mui/x-charts@8.27.2`
80
+
81
+ Internal changes.
82
+
83
+ #### `@mui/x-charts-pro@8.27.2` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
84
+
85
+ Same changes as in `@mui/x-charts@8.27.2`, plus:
86
+
87
+ - [charts-pro] Handle edge case in export image (#21206) @bernardobelchior
88
+
89
+ #### `@mui/x-charts-premium@8.27.2` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
90
+
91
+ Same changes as in `@mui/x-charts-pro@8.27.2`.
92
+
93
+ ### Tree View
94
+
95
+ #### `@mui/x-tree-view@8.27.2`
96
+
97
+ - [tree view] Focus item sibling on unmount instead of the 1st item (#21386) @flaviendelangle
98
+
99
+ #### `@mui/x-tree-view-pro@8.27.2` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
100
+
101
+ Same changes as in `@mui/x-tree-view@8.27.2`.
102
+
103
+ ### Codemod
104
+
105
+ #### `@mui/x-codemod@8.27.2`
106
+
107
+ Internal changes.
108
+
109
+ ### Core
110
+
111
+ - [code-infra] Only ignore `renovate[bot]` in changelog generation script (#21188) @bernardobelchior
112
+
8
113
  ## v8.27.1
9
114
 
10
115
  <!-- generated comparing v8.27.0..v8.x -->
@@ -180,6 +180,13 @@ DataGridRaw.propTypes = {
180
180
  get: _propTypes.default.func.isRequired,
181
181
  set: _propTypes.default.func.isRequired
182
182
  }),
183
+ /**
184
+ * If positive, the Data Grid will periodically revalidate data source rows by
185
+ * re-fetching them from the server when the cache entry has expired.
186
+ * Set to `0` to disable polling.
187
+ * @default 0
188
+ */
189
+ dataSourceRevalidateMs: _propTypes.default.number,
183
190
  /**
184
191
  * Set the density of the Data Grid.
185
192
  * @default "standard"
@@ -30,6 +30,7 @@ const DATA_GRID_PROPS_DEFAULT_VALUES = exports.DATA_GRID_PROPS_DEFAULT_VALUES =
30
30
  disableMultipleColumnsFiltering: false,
31
31
  disableMultipleColumnsSorting: false,
32
32
  disableMultipleRowSelection: false,
33
+ dataSourceRevalidateMs: 0,
33
34
  disableRowSelectionOnClick: false,
34
35
  disableRowSelectionExcludeModel: false,
35
36
  disableVirtualization: false,
@@ -173,6 +173,13 @@ DataGridRaw.propTypes = {
173
173
  get: PropTypes.func.isRequired,
174
174
  set: PropTypes.func.isRequired
175
175
  }),
176
+ /**
177
+ * If positive, the Data Grid will periodically revalidate data source rows by
178
+ * re-fetching them from the server when the cache entry has expired.
179
+ * Set to `0` to disable polling.
180
+ * @default 0
181
+ */
182
+ dataSourceRevalidateMs: PropTypes.number,
176
183
  /**
177
184
  * Set the density of the Data Grid.
178
185
  * @default "standard"
@@ -24,6 +24,7 @@ export const DATA_GRID_PROPS_DEFAULT_VALUES = {
24
24
  disableMultipleColumnsFiltering: false,
25
25
  disableMultipleColumnsSorting: false,
26
26
  disableMultipleRowSelection: false,
27
+ dataSourceRevalidateMs: 0,
27
28
  disableRowSelectionOnClick: false,
28
29
  disableRowSelectionExcludeModel: false,
29
30
  disableVirtualization: false,
@@ -5,6 +5,9 @@ import type { GridDataSourceCacheDefaultConfig } from "./cache.js";
5
5
  * The parameters for the `fetchRows` method.
6
6
  */
7
7
  export type GridDataSourceFetchRowsParams<T> = Partial<T> & GridGetRowsOptions;
8
+ export interface GridDataSourceFetchRowChildrenOptions {
9
+ showChildrenLoading?: boolean;
10
+ }
8
11
  export interface GridDataSourceApi {
9
12
  /**
10
13
  * The data source API.
@@ -38,7 +41,7 @@ export interface GridDataSourceApiBase {
38
41
  }
39
42
  export interface GridDataSourceBaseOptions {
40
43
  cacheOptions?: GridDataSourceCacheDefaultConfig;
41
- fetchRowChildren?: (parents: GridRowId[]) => void;
44
+ fetchRowChildren?: (parents: GridRowId[], options?: GridDataSourceFetchRowChildrenOptions) => void;
42
45
  clearDataSourceState?: () => void;
43
46
  handleEditRow?: (params: GridUpdateRowParams, updatedRow: GridRowModel) => void;
44
47
  }
@@ -6,17 +6,20 @@ import type { GridPrivateApiCommunity } from "../../../models/api/gridApiCommuni
6
6
  import type { DataGridProcessedProps } from "../../../models/props/DataGridProps.js";
7
7
  import type { GridStrategyProcessor } from "../../core/strategyProcessing/index.js";
8
8
  import type { GridEventListener } from "../../../models/events/index.js";
9
- export declare const useGridDataSourceBase: <Api extends GridPrivateApiCommunity>(apiRef: RefObject<Api>, props: Pick<DataGridProcessedProps, "dataSource" | "dataSourceCache" | "onDataSourceError" | "pageSizeOptions" | "pagination" | "signature">, options?: GridDataSourceBaseOptions) => {
9
+ import type { GridRowId } from "../../../models/gridRows.js";
10
+ export declare const useGridDataSourceBase: <Api extends GridPrivateApiCommunity>(apiRef: RefObject<Api>, props: Pick<DataGridProcessedProps, "dataSource" | "dataSourceCache" | "onDataSourceError" | "pageSizeOptions" | "pagination" | "signature" | "dataSourceRevalidateMs">, options?: GridDataSourceBaseOptions) => {
10
11
  api: {
11
12
  public: GridDataSourceApi;
12
13
  };
13
- debouncedFetchRows: ((parentId?: import("@mui/x-data-grid").GridRowId, params?: import("./models.js").GridDataSourceFetchRowsParams<import("@mui/x-data-grid").GridGetRowsParams>) => Promise<void>) & import("@mui/utils/debounce").Cancelable;
14
+ debouncedFetchRows: ((parentId?: GridRowId, params?: import("./models.js").GridDataSourceFetchRowsParams<import("@mui/x-data-grid").GridGetRowsParams>) => Promise<void>) & import("@mui/utils/debounce").Cancelable;
14
15
  strategyProcessor: {
15
16
  strategyName: DataSourceRowsUpdateStrategy;
16
17
  group: "dataSourceRowsUpdate";
17
18
  processor: GridStrategyProcessor<"dataSourceRowsUpdate">;
18
19
  };
19
20
  setStrategyAvailability: () => void;
21
+ startPolling: () => void;
22
+ stopPolling: () => void;
20
23
  cacheChunkManager: CacheChunkManager;
21
24
  cache: GridDataSourceCache;
22
25
  events: {
@@ -5,6 +5,7 @@ import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWith
5
5
  const _excluded = ["skipCache", "keepChildrenExpanded"];
6
6
  import * as React from 'react';
7
7
  import useLazyRef from '@mui/utils/useLazyRef';
8
+ import useEventCallback from '@mui/utils/useEventCallback';
8
9
  import debounce from '@mui/utils/debounce';
9
10
  import { warnOnce } from '@mui/x-internals/warning';
10
11
  import { isDeepEqual } from '@mui/x-internals/isDeepEqual';
@@ -12,7 +13,8 @@ import { GRID_ROOT_GROUP_ID } from "../rows/gridRowsUtils.js";
12
13
  import { runIf } from "../../../utils/utils.js";
13
14
  import { GridStrategyGroup } from "../../core/strategyProcessing/index.js";
14
15
  import { useGridSelector } from "../../utils/useGridSelector.js";
15
- import { gridPaginationModelSelector } from "../pagination/gridPaginationSelector.js";
16
+ import { gridPaginationModelSelector, gridVisibleRowsSelector } from "../pagination/gridPaginationSelector.js";
17
+ import { gridRowTreeSelector } from "../rows/gridRowsSelector.js";
16
18
  import { gridGetRowsParamsSelector } from "./gridDataSourceSelector.js";
17
19
  import { CacheChunkManager, DataSourceRowsUpdateStrategy } from "./utils.js";
18
20
  import { GridDataSourceCacheDefault } from "./cache.js";
@@ -38,7 +40,9 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
38
40
  }, [currentStrategy]);
39
41
  const paginationModel = useGridSelector(apiRef, gridPaginationModelSelector);
40
42
  const lastRequestId = React.useRef(0);
43
+ const pollingIntervalRef = React.useRef(null);
41
44
  const onDataSourceErrorProp = props.onDataSourceError;
45
+ const revalidateMs = props.dataSourceRevalidateMs;
42
46
  const cacheChunkManager = useLazyRef(() => {
43
47
  if (!props.pagination) {
44
48
  return new CacheChunkManager(paginationModel.pageSize);
@@ -128,6 +132,69 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
128
132
  const handleStrategyActivityChange = React.useCallback(() => {
129
133
  setCurrentStrategy(apiRef.current.getActiveStrategy(GridStrategyGroup.DataSource));
130
134
  }, [apiRef]);
135
+ const fetchRowChildrenOption = options.fetchRowChildren;
136
+ const revalidate = useEventCallback(async () => {
137
+ const getRows = props.dataSource?.getRows;
138
+ if (!getRows || !standardRowsUpdateStrategyActive) {
139
+ return;
140
+ }
141
+ const revalidateExpandedGroups = () => {
142
+ if (currentStrategy !== DataSourceRowsUpdateStrategy.GroupedData || !fetchRowChildrenOption) {
143
+ return;
144
+ }
145
+ const rowTree = gridRowTreeSelector(apiRef);
146
+ const visibleRows = gridVisibleRowsSelector(apiRef).rows;
147
+ const expandedGroupIds = visibleRows.reduce((acc, row) => {
148
+ const node = rowTree[row.id];
149
+ if (node.type === 'group' && node.id !== GRID_ROOT_GROUP_ID && node.childrenExpanded === true) {
150
+ acc.push(row.id);
151
+ }
152
+ return acc;
153
+ }, []);
154
+ if (expandedGroupIds.length > 0) {
155
+ fetchRowChildrenOption(expandedGroupIds, {
156
+ showChildrenLoading: false
157
+ });
158
+ }
159
+ };
160
+ const fetchParams = _extends({}, gridGetRowsParamsSelector(apiRef), apiRef.current.unstable_applyPipeProcessors('getRowsParams', {}));
161
+ const cacheKeys = cacheChunkManager.getCacheKeys(fetchParams);
162
+ const responses = cacheKeys.map(cacheKey => cache.get(cacheKey));
163
+ if (responses.every(response => response !== undefined)) {
164
+ revalidateExpandedGroups();
165
+ return;
166
+ }
167
+ try {
168
+ const response = await getRows(fetchParams);
169
+ const currentParams = _extends({}, gridGetRowsParamsSelector(apiRef), apiRef.current.unstable_applyPipeProcessors('getRowsParams', {}));
170
+ if (!isDeepEqual(fetchParams, currentParams)) {
171
+ return;
172
+ }
173
+ const cacheResponses = cacheChunkManager.splitResponse(fetchParams, response);
174
+ cacheResponses.forEach((cacheResponse, key) => cache.set(key, cacheResponse));
175
+ apiRef.current.applyStrategyProcessor('dataSourceRowsUpdate', {
176
+ response,
177
+ fetchParams,
178
+ options: {}
179
+ });
180
+ revalidateExpandedGroups();
181
+ } catch {
182
+ // Ignore background revalidation errors.
183
+ }
184
+ });
185
+ const stopPolling = React.useCallback(() => {
186
+ if (pollingIntervalRef.current !== null) {
187
+ clearInterval(pollingIntervalRef.current);
188
+ pollingIntervalRef.current = null;
189
+ }
190
+ }, []);
191
+ const startPolling = useEventCallback(() => {
192
+ stopPolling();
193
+ if (revalidateMs <= 0 || !standardRowsUpdateStrategyActive) {
194
+ return;
195
+ }
196
+ pollingIntervalRef.current = setInterval(revalidate, revalidateMs);
197
+ });
131
198
  const handleDataUpdate = React.useCallback(params => {
132
199
  if ('error' in params) {
133
200
  apiRef.current.setRows([]);
@@ -144,7 +211,8 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
144
211
  params: params.fetchParams,
145
212
  response
146
213
  }, true);
147
- }, [apiRef]);
214
+ startPolling();
215
+ }, [apiRef, startPolling]);
148
216
  const dataSourceUpdateRow = props.dataSource?.updateRow;
149
217
  const handleEditRowOption = options.handleEditRow;
150
218
  const editRow = React.useCallback(async params => {
@@ -184,6 +252,10 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
184
252
  }
185
253
  };
186
254
  const debouncedFetchRows = React.useMemo(() => debounce(fetchRows, 0), [fetchRows]);
255
+ const handleFetchRowsOnParamsChange = React.useCallback(() => {
256
+ stopPolling();
257
+ debouncedFetchRows();
258
+ }, [stopPolling, debouncedFetchRows]);
187
259
  const isFirstRender = React.useRef(true);
188
260
  React.useEffect(() => {
189
261
  if (isFirstRender.current) {
@@ -196,6 +268,17 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
196
268
  const newCache = getCache(props.dataSourceCache, options.cacheOptions);
197
269
  setCache(prevCache => prevCache !== newCache ? newCache : prevCache);
198
270
  }, [props.dataSourceCache, options.cacheOptions]);
271
+ React.useEffect(() => {
272
+ if (!standardRowsUpdateStrategyActive) {
273
+ stopPolling();
274
+ }
275
+ }, [standardRowsUpdateStrategyActive, stopPolling]);
276
+ React.useEffect(() => {
277
+ if (revalidateMs <= 0) {
278
+ stopPolling();
279
+ }
280
+ }, [revalidateMs, stopPolling]);
281
+ React.useEffect(() => stopPolling, [stopPolling]);
199
282
  React.useEffect(() => {
200
283
  // Return early if the proper strategy isn't set yet
201
284
  // Context: https://github.com/mui/mui-x/issues/19650
@@ -203,6 +286,8 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
203
286
  return undefined;
204
287
  }
205
288
  if (props.dataSource) {
289
+ stopPolling();
290
+ apiRef.current.setRows([]);
206
291
  apiRef.current.dataSource.cache.clear();
207
292
  apiRef.current.dataSource.fetchRows();
208
293
  }
@@ -210,7 +295,7 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
210
295
  // ignore the current request on unmount
211
296
  lastRequestId.current += 1;
212
297
  };
213
- }, [apiRef, props.dataSource, currentStrategy]);
298
+ }, [apiRef, props.dataSource, currentStrategy, stopPolling]);
214
299
  return {
215
300
  api: {
216
301
  public: dataSourceApi
@@ -222,13 +307,15 @@ export const useGridDataSourceBase = (apiRef, props, options = {}) => {
222
307
  processor: handleDataUpdate
223
308
  },
224
309
  setStrategyAvailability,
310
+ startPolling,
311
+ stopPolling,
225
312
  cacheChunkManager,
226
313
  cache,
227
314
  events: {
228
315
  strategyAvailabilityChange: handleStrategyActivityChange,
229
- sortModelChange: runIf(standardRowsUpdateStrategyActive, () => debouncedFetchRows()),
230
- filterModelChange: runIf(standardRowsUpdateStrategyActive, () => debouncedFetchRows()),
231
- paginationModelChange: runIf(standardRowsUpdateStrategyActive, () => debouncedFetchRows())
316
+ sortModelChange: runIf(standardRowsUpdateStrategyActive, handleFetchRowsOnParamsChange),
317
+ filterModelChange: runIf(standardRowsUpdateStrategyActive, handleFetchRowsOnParamsChange),
318
+ paginationModelChange: runIf(standardRowsUpdateStrategyActive, handleFetchRowsOnParamsChange)
232
319
  }
233
320
  };
234
321
  };
@@ -184,12 +184,15 @@ export const useGridRowEditing = (apiRef, props) => {
184
184
  const rowParams = apiRef.current.getRowParams(params.id);
185
185
  const newParams = _extends({}, rowParams, {
186
186
  field: params.field,
187
- reason
187
+ reason,
188
+ // Only pass the pressed key when the row editing is controlled via `rowModesModel`.
189
+ // In uncontrolled mode, the default editor already inserts the character and passing it here would duplicate it.
190
+ key: rowModesModelProp && isPrintableKey(event) ? event.key : undefined
188
191
  });
189
192
  apiRef.current.publishEvent('rowEditStart', newParams, event);
190
193
  }
191
194
  }
192
- }, [apiRef, hasFieldsWithErrors]);
195
+ }, [apiRef, hasFieldsWithErrors, rowModesModelProp]);
193
196
  const handleRowEditStart = React.useCallback(params => {
194
197
  const {
195
198
  id,
@@ -201,10 +204,17 @@ export const useGridRowEditing = (apiRef, props) => {
201
204
  fieldToFocus: field
202
205
  };
203
206
  if (reason === GridRowEditStartReasons.printableKeyDown || reason === GridRowEditStartReasons.deleteKeyDown) {
204
- startRowEditModeParams.deleteValue = !!field;
207
+ // If the user typed a printable key, initialize the value with that key
208
+ // to avoid losing the first character when the component is controlled.
209
+ if (rowModesModelProp && reason === GridRowEditStartReasons.printableKeyDown && params.key && field) {
210
+ startRowEditModeParams.initialValue = params.key;
211
+ } else {
212
+ // For Delete / Backspace or for uncontrolled row editing we clear the value
213
+ startRowEditModeParams.deleteValue = !!field;
214
+ }
205
215
  }
206
216
  apiRef.current.startRowEditMode(startRowEditModeParams);
207
- }, [apiRef]);
217
+ }, [apiRef, rowModesModelProp]);
208
218
  const handleRowEditStop = React.useCallback(params => {
209
219
  const {
210
220
  id,
package/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v8.27.1
2
+ * @mui/x-data-grid v8.27.3
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -123,7 +123,6 @@ export interface GridStartRowEditModeParams {
123
123
  /**
124
124
  * The initial value for the given `fieldToFocus`.
125
125
  * If `deleteValue` is also true, this value is not used.
126
- * @deprecated No longer needed.
127
126
  */
128
127
  initialValue?: string;
129
128
  }
@@ -73,7 +73,6 @@ export interface GridRowEditStartParams<R extends GridValidRowModel = any> exten
73
73
  reason?: GridRowEditStartReasons;
74
74
  /**
75
75
  * If the reason is related to a keyboard event, it contains which key was pressed.
76
- * @deprecated No longer needed.
77
76
  */
78
77
  key?: string;
79
78
  }
@@ -325,6 +325,13 @@ export interface DataGridPropsWithDefaultValues<R extends GridValidRowModel = an
325
325
  * @default 0
326
326
  */
327
327
  throttleRowsMs: number;
328
+ /**
329
+ * If positive, the Data Grid will periodically revalidate data source rows by re-fetching them from the server when the cache entry has expired.
330
+ * If the refetched rows are different from the current rows, the grid will update the rows.
331
+ * Set to `0` to disable polling.
332
+ * @default 0
333
+ */
334
+ dataSourceRevalidateMs: number;
328
335
  /**
329
336
  * If `true`, reordering columns is disabled.
330
337
  * @default false
@@ -5,6 +5,9 @@ import type { GridDataSourceCacheDefaultConfig } from "./cache.js";
5
5
  * The parameters for the `fetchRows` method.
6
6
  */
7
7
  export type GridDataSourceFetchRowsParams<T> = Partial<T> & GridGetRowsOptions;
8
+ export interface GridDataSourceFetchRowChildrenOptions {
9
+ showChildrenLoading?: boolean;
10
+ }
8
11
  export interface GridDataSourceApi {
9
12
  /**
10
13
  * The data source API.
@@ -38,7 +41,7 @@ export interface GridDataSourceApiBase {
38
41
  }
39
42
  export interface GridDataSourceBaseOptions {
40
43
  cacheOptions?: GridDataSourceCacheDefaultConfig;
41
- fetchRowChildren?: (parents: GridRowId[]) => void;
44
+ fetchRowChildren?: (parents: GridRowId[], options?: GridDataSourceFetchRowChildrenOptions) => void;
42
45
  clearDataSourceState?: () => void;
43
46
  handleEditRow?: (params: GridUpdateRowParams, updatedRow: GridRowModel) => void;
44
47
  }
@@ -6,17 +6,20 @@ import type { GridPrivateApiCommunity } from "../../../models/api/gridApiCommuni
6
6
  import type { DataGridProcessedProps } from "../../../models/props/DataGridProps.js";
7
7
  import type { GridStrategyProcessor } from "../../core/strategyProcessing/index.js";
8
8
  import type { GridEventListener } from "../../../models/events/index.js";
9
- export declare const useGridDataSourceBase: <Api extends GridPrivateApiCommunity>(apiRef: RefObject<Api>, props: Pick<DataGridProcessedProps, "dataSource" | "dataSourceCache" | "onDataSourceError" | "pageSizeOptions" | "pagination" | "signature">, options?: GridDataSourceBaseOptions) => {
9
+ import type { GridRowId } from "../../../models/gridRows.js";
10
+ export declare const useGridDataSourceBase: <Api extends GridPrivateApiCommunity>(apiRef: RefObject<Api>, props: Pick<DataGridProcessedProps, "dataSource" | "dataSourceCache" | "onDataSourceError" | "pageSizeOptions" | "pagination" | "signature" | "dataSourceRevalidateMs">, options?: GridDataSourceBaseOptions) => {
10
11
  api: {
11
12
  public: GridDataSourceApi;
12
13
  };
13
- debouncedFetchRows: ((parentId?: import("@mui/x-data-grid").GridRowId, params?: import("./models.js").GridDataSourceFetchRowsParams<import("@mui/x-data-grid").GridGetRowsParams>) => Promise<void>) & import("@mui/utils/debounce").Cancelable;
14
+ debouncedFetchRows: ((parentId?: GridRowId, params?: import("./models.js").GridDataSourceFetchRowsParams<import("@mui/x-data-grid").GridGetRowsParams>) => Promise<void>) & import("@mui/utils/debounce").Cancelable;
14
15
  strategyProcessor: {
15
16
  strategyName: DataSourceRowsUpdateStrategy;
16
17
  group: "dataSourceRowsUpdate";
17
18
  processor: GridStrategyProcessor<"dataSourceRowsUpdate">;
18
19
  };
19
20
  setStrategyAvailability: () => void;
21
+ startPolling: () => void;
22
+ stopPolling: () => void;
20
23
  cacheChunkManager: CacheChunkManager;
21
24
  cache: GridDataSourceCache;
22
25
  events: {
@@ -11,6 +11,7 @@ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")
11
11
  var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
12
12
  var React = _interopRequireWildcard(require("react"));
13
13
  var _useLazyRef = _interopRequireDefault(require("@mui/utils/useLazyRef"));
14
+ var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
14
15
  var _debounce = _interopRequireDefault(require("@mui/utils/debounce"));
15
16
  var _warning = require("@mui/x-internals/warning");
16
17
  var _isDeepEqual = require("@mui/x-internals/isDeepEqual");
@@ -19,6 +20,7 @@ var _utils = require("../../../utils/utils");
19
20
  var _strategyProcessing = require("../../core/strategyProcessing");
20
21
  var _useGridSelector = require("../../utils/useGridSelector");
21
22
  var _gridPaginationSelector = require("../pagination/gridPaginationSelector");
23
+ var _gridRowsSelector = require("../rows/gridRowsSelector");
22
24
  var _gridDataSourceSelector = require("./gridDataSourceSelector");
23
25
  var _utils2 = require("./utils");
24
26
  var _cache = require("./cache");
@@ -45,7 +47,9 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
45
47
  }, [currentStrategy]);
46
48
  const paginationModel = (0, _useGridSelector.useGridSelector)(apiRef, _gridPaginationSelector.gridPaginationModelSelector);
47
49
  const lastRequestId = React.useRef(0);
50
+ const pollingIntervalRef = React.useRef(null);
48
51
  const onDataSourceErrorProp = props.onDataSourceError;
52
+ const revalidateMs = props.dataSourceRevalidateMs;
49
53
  const cacheChunkManager = (0, _useLazyRef.default)(() => {
50
54
  if (!props.pagination) {
51
55
  return new _utils2.CacheChunkManager(paginationModel.pageSize);
@@ -135,6 +139,69 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
135
139
  const handleStrategyActivityChange = React.useCallback(() => {
136
140
  setCurrentStrategy(apiRef.current.getActiveStrategy(_strategyProcessing.GridStrategyGroup.DataSource));
137
141
  }, [apiRef]);
142
+ const fetchRowChildrenOption = options.fetchRowChildren;
143
+ const revalidate = (0, _useEventCallback.default)(async () => {
144
+ const getRows = props.dataSource?.getRows;
145
+ if (!getRows || !standardRowsUpdateStrategyActive) {
146
+ return;
147
+ }
148
+ const revalidateExpandedGroups = () => {
149
+ if (currentStrategy !== _utils2.DataSourceRowsUpdateStrategy.GroupedData || !fetchRowChildrenOption) {
150
+ return;
151
+ }
152
+ const rowTree = (0, _gridRowsSelector.gridRowTreeSelector)(apiRef);
153
+ const visibleRows = (0, _gridPaginationSelector.gridVisibleRowsSelector)(apiRef).rows;
154
+ const expandedGroupIds = visibleRows.reduce((acc, row) => {
155
+ const node = rowTree[row.id];
156
+ if (node.type === 'group' && node.id !== _gridRowsUtils.GRID_ROOT_GROUP_ID && node.childrenExpanded === true) {
157
+ acc.push(row.id);
158
+ }
159
+ return acc;
160
+ }, []);
161
+ if (expandedGroupIds.length > 0) {
162
+ fetchRowChildrenOption(expandedGroupIds, {
163
+ showChildrenLoading: false
164
+ });
165
+ }
166
+ };
167
+ const fetchParams = (0, _extends2.default)({}, (0, _gridDataSourceSelector.gridGetRowsParamsSelector)(apiRef), apiRef.current.unstable_applyPipeProcessors('getRowsParams', {}));
168
+ const cacheKeys = cacheChunkManager.getCacheKeys(fetchParams);
169
+ const responses = cacheKeys.map(cacheKey => cache.get(cacheKey));
170
+ if (responses.every(response => response !== undefined)) {
171
+ revalidateExpandedGroups();
172
+ return;
173
+ }
174
+ try {
175
+ const response = await getRows(fetchParams);
176
+ const currentParams = (0, _extends2.default)({}, (0, _gridDataSourceSelector.gridGetRowsParamsSelector)(apiRef), apiRef.current.unstable_applyPipeProcessors('getRowsParams', {}));
177
+ if (!(0, _isDeepEqual.isDeepEqual)(fetchParams, currentParams)) {
178
+ return;
179
+ }
180
+ const cacheResponses = cacheChunkManager.splitResponse(fetchParams, response);
181
+ cacheResponses.forEach((cacheResponse, key) => cache.set(key, cacheResponse));
182
+ apiRef.current.applyStrategyProcessor('dataSourceRowsUpdate', {
183
+ response,
184
+ fetchParams,
185
+ options: {}
186
+ });
187
+ revalidateExpandedGroups();
188
+ } catch {
189
+ // Ignore background revalidation errors.
190
+ }
191
+ });
192
+ const stopPolling = React.useCallback(() => {
193
+ if (pollingIntervalRef.current !== null) {
194
+ clearInterval(pollingIntervalRef.current);
195
+ pollingIntervalRef.current = null;
196
+ }
197
+ }, []);
198
+ const startPolling = (0, _useEventCallback.default)(() => {
199
+ stopPolling();
200
+ if (revalidateMs <= 0 || !standardRowsUpdateStrategyActive) {
201
+ return;
202
+ }
203
+ pollingIntervalRef.current = setInterval(revalidate, revalidateMs);
204
+ });
138
205
  const handleDataUpdate = React.useCallback(params => {
139
206
  if ('error' in params) {
140
207
  apiRef.current.setRows([]);
@@ -151,7 +218,8 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
151
218
  params: params.fetchParams,
152
219
  response
153
220
  }, true);
154
- }, [apiRef]);
221
+ startPolling();
222
+ }, [apiRef, startPolling]);
155
223
  const dataSourceUpdateRow = props.dataSource?.updateRow;
156
224
  const handleEditRowOption = options.handleEditRow;
157
225
  const editRow = React.useCallback(async params => {
@@ -191,6 +259,10 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
191
259
  }
192
260
  };
193
261
  const debouncedFetchRows = React.useMemo(() => (0, _debounce.default)(fetchRows, 0), [fetchRows]);
262
+ const handleFetchRowsOnParamsChange = React.useCallback(() => {
263
+ stopPolling();
264
+ debouncedFetchRows();
265
+ }, [stopPolling, debouncedFetchRows]);
194
266
  const isFirstRender = React.useRef(true);
195
267
  React.useEffect(() => {
196
268
  if (isFirstRender.current) {
@@ -203,6 +275,17 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
203
275
  const newCache = getCache(props.dataSourceCache, options.cacheOptions);
204
276
  setCache(prevCache => prevCache !== newCache ? newCache : prevCache);
205
277
  }, [props.dataSourceCache, options.cacheOptions]);
278
+ React.useEffect(() => {
279
+ if (!standardRowsUpdateStrategyActive) {
280
+ stopPolling();
281
+ }
282
+ }, [standardRowsUpdateStrategyActive, stopPolling]);
283
+ React.useEffect(() => {
284
+ if (revalidateMs <= 0) {
285
+ stopPolling();
286
+ }
287
+ }, [revalidateMs, stopPolling]);
288
+ React.useEffect(() => stopPolling, [stopPolling]);
206
289
  React.useEffect(() => {
207
290
  // Return early if the proper strategy isn't set yet
208
291
  // Context: https://github.com/mui/mui-x/issues/19650
@@ -210,6 +293,8 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
210
293
  return undefined;
211
294
  }
212
295
  if (props.dataSource) {
296
+ stopPolling();
297
+ apiRef.current.setRows([]);
213
298
  apiRef.current.dataSource.cache.clear();
214
299
  apiRef.current.dataSource.fetchRows();
215
300
  }
@@ -217,7 +302,7 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
217
302
  // ignore the current request on unmount
218
303
  lastRequestId.current += 1;
219
304
  };
220
- }, [apiRef, props.dataSource, currentStrategy]);
305
+ }, [apiRef, props.dataSource, currentStrategy, stopPolling]);
221
306
  return {
222
307
  api: {
223
308
  public: dataSourceApi
@@ -229,13 +314,15 @@ const useGridDataSourceBase = (apiRef, props, options = {}) => {
229
314
  processor: handleDataUpdate
230
315
  },
231
316
  setStrategyAvailability,
317
+ startPolling,
318
+ stopPolling,
232
319
  cacheChunkManager,
233
320
  cache,
234
321
  events: {
235
322
  strategyAvailabilityChange: handleStrategyActivityChange,
236
- sortModelChange: (0, _utils.runIf)(standardRowsUpdateStrategyActive, () => debouncedFetchRows()),
237
- filterModelChange: (0, _utils.runIf)(standardRowsUpdateStrategyActive, () => debouncedFetchRows()),
238
- paginationModelChange: (0, _utils.runIf)(standardRowsUpdateStrategyActive, () => debouncedFetchRows())
323
+ sortModelChange: (0, _utils.runIf)(standardRowsUpdateStrategyActive, handleFetchRowsOnParamsChange),
324
+ filterModelChange: (0, _utils.runIf)(standardRowsUpdateStrategyActive, handleFetchRowsOnParamsChange),
325
+ paginationModelChange: (0, _utils.runIf)(standardRowsUpdateStrategyActive, handleFetchRowsOnParamsChange)
239
326
  }
240
327
  };
241
328
  };
@@ -191,12 +191,15 @@ const useGridRowEditing = (apiRef, props) => {
191
191
  const rowParams = apiRef.current.getRowParams(params.id);
192
192
  const newParams = (0, _extends2.default)({}, rowParams, {
193
193
  field: params.field,
194
- reason
194
+ reason,
195
+ // Only pass the pressed key when the row editing is controlled via `rowModesModel`.
196
+ // In uncontrolled mode, the default editor already inserts the character and passing it here would duplicate it.
197
+ key: rowModesModelProp && (0, _keyboardUtils.isPrintableKey)(event) ? event.key : undefined
195
198
  });
196
199
  apiRef.current.publishEvent('rowEditStart', newParams, event);
197
200
  }
198
201
  }
199
- }, [apiRef, hasFieldsWithErrors]);
202
+ }, [apiRef, hasFieldsWithErrors, rowModesModelProp]);
200
203
  const handleRowEditStart = React.useCallback(params => {
201
204
  const {
202
205
  id,
@@ -208,10 +211,17 @@ const useGridRowEditing = (apiRef, props) => {
208
211
  fieldToFocus: field
209
212
  };
210
213
  if (reason === _gridRowParams.GridRowEditStartReasons.printableKeyDown || reason === _gridRowParams.GridRowEditStartReasons.deleteKeyDown) {
211
- startRowEditModeParams.deleteValue = !!field;
214
+ // If the user typed a printable key, initialize the value with that key
215
+ // to avoid losing the first character when the component is controlled.
216
+ if (rowModesModelProp && reason === _gridRowParams.GridRowEditStartReasons.printableKeyDown && params.key && field) {
217
+ startRowEditModeParams.initialValue = params.key;
218
+ } else {
219
+ // For Delete / Backspace or for uncontrolled row editing we clear the value
220
+ startRowEditModeParams.deleteValue = !!field;
221
+ }
212
222
  }
213
223
  apiRef.current.startRowEditMode(startRowEditModeParams);
214
- }, [apiRef]);
224
+ }, [apiRef, rowModesModelProp]);
215
225
  const handleRowEditStop = React.useCallback(params => {
216
226
  const {
217
227
  id,
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v8.27.1
2
+ * @mui/x-data-grid v8.27.3
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -123,7 +123,6 @@ export interface GridStartRowEditModeParams {
123
123
  /**
124
124
  * The initial value for the given `fieldToFocus`.
125
125
  * If `deleteValue` is also true, this value is not used.
126
- * @deprecated No longer needed.
127
126
  */
128
127
  initialValue?: string;
129
128
  }
@@ -73,7 +73,6 @@ export interface GridRowEditStartParams<R extends GridValidRowModel = any> exten
73
73
  reason?: GridRowEditStartReasons;
74
74
  /**
75
75
  * If the reason is related to a keyboard event, it contains which key was pressed.
76
- * @deprecated No longer needed.
77
76
  */
78
77
  key?: string;
79
78
  }
@@ -325,6 +325,13 @@ export interface DataGridPropsWithDefaultValues<R extends GridValidRowModel = an
325
325
  * @default 0
326
326
  */
327
327
  throttleRowsMs: number;
328
+ /**
329
+ * If positive, the Data Grid will periodically revalidate data source rows by re-fetching them from the server when the cache entry has expired.
330
+ * If the refetched rows are different from the current rows, the grid will update the rows.
331
+ * Set to `0` to disable polling.
332
+ * @default 0
333
+ */
334
+ dataSourceRevalidateMs: number;
328
335
  /**
329
336
  * If `true`, reordering columns is disabled.
330
337
  * @default false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/x-data-grid",
3
- "version": "8.27.1",
3
+ "version": "8.27.3",
4
4
  "author": "MUI Team",
5
5
  "description": "The Community plan edition of the MUI X Data Grid components.",
6
6
  "license": "MIT",