@mui/x-data-grid 7.29.12 → 7.29.13

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
@@ -3,6 +3,41 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 7.29.13
7
+
8
+ _Apr 27, 2026_
9
+
10
+ We'd like to extend a big thank you to the 4 contributors who made this release possible. Here are some highlights ✨:
11
+
12
+ - 🐞 Bugfixes
13
+
14
+ The following are all team members who have contributed to this release:
15
+ @arminmeh, @dav-is, @LukasTy, @michelengelen
16
+
17
+ ### Data Grid
18
+
19
+ #### `@mui/x-data-grid@7.29.13`
20
+
21
+ - [DataGrid] Prevent repeated `hasScrollbar` state updates (#21849) @arminmeh
22
+
23
+ #### `@mui/x-data-grid-pro@7.29.13` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link "Pro plan")
24
+
25
+ Same changes as in `@mui/x-data-grid@7.29.13`.
26
+
27
+ #### `@mui/x-data-grid-premium@7.29.13` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link "Premium plan")
28
+
29
+ Same changes as in `@mui/x-data-grid-pro@7.29.13`.
30
+
31
+ ### Docs
32
+
33
+ - [docs] Add `v9` as root path link and move `v8` under subpath (#22037) @LukasTy
34
+ - [docs] Add check for auto-generated group rows in `renderCountry` (#22143) @michelengelen
35
+ - [docs] Remove obsolete v7 deprecation warning for `dayOfWeekFormatter` (@LukasTy) (#22121)
36
+
37
+ ### Miscellaneous
38
+
39
+ - [docs-infra] Set `SEARCH_INDEX` Env for v7 (#21876) @dav-is
40
+
6
41
  ## 7.29.12
7
42
 
8
43
  _Nov 26, 2025_
@@ -67,6 +67,21 @@ export function useGridDimensions(apiRef, props) {
67
67
  const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
68
68
  const columnsTotalWidth = useGridSelector(apiRef, columnsTotalWidthSelector);
69
69
  const isFirstSizing = React.useRef(true);
70
+
71
+ // Vertical scrollbar oscillation detector.
72
+ // Counts consecutive hasScrollY flips that happen with no row-height change.
73
+ // After 2 flips it is certainly a layout feedback loop, so every further flip
74
+ // is forced to false (no scrollbar). The counter resets when row heights change.
75
+ // Only vertical scrollbar can oscillate because column widths are never 'auto'.
76
+ // https://github.com/mui/mui-x/issues/20539
77
+ const scrollYOscillation = React.useRef({
78
+ counter: 0,
79
+ heights: {
80
+ content: 0,
81
+ pinnedTop: 0,
82
+ pinnedBottom: 0
83
+ }
84
+ });
70
85
  const {
71
86
  rowHeight,
72
87
  headerHeight,
@@ -134,6 +149,7 @@ export function useGridDimensions(apiRef, props) {
134
149
  width: nonPinnedColumnsTotalWidth,
135
150
  height: roundToDecimalPlaces(rowsMeta.currentPageTotalHeight, 1)
136
151
  };
152
+ const prevDimensions = apiRef.current.state.dimensions;
137
153
  let viewportOuterSize;
138
154
  let viewportInnerSize;
139
155
  let hasScrollX = false;
@@ -171,6 +187,36 @@ export function useGridDimensions(apiRef, props) {
171
187
  hasScrollY = content.height + scrollbarSize > container.height;
172
188
  }
173
189
  }
190
+
191
+ // Detect vertical scrollbar oscillation.
192
+ // Track consecutive hasScrollY flips with no row-height change.
193
+ // Once confirmed (≥ 2 flips), force hasScrollY off — the scrollbar is
194
+ // not genuinely needed, it is a layout feedback loop caused by stale
195
+ // rootSize or the horizontal scrollbar's height cascading.
196
+ {
197
+ const osc = scrollYOscillation.current;
198
+ const heightsChanged = rowsMeta.currentPageTotalHeight !== osc.heights.content || rowsMeta.pinnedTopRowsTotalHeight !== osc.heights.pinnedTop || rowsMeta.pinnedBottomRowsTotalHeight !== osc.heights.pinnedBottom;
199
+ if (heightsChanged) {
200
+ osc.counter = 0;
201
+ osc.heights = {
202
+ content: rowsMeta.currentPageTotalHeight,
203
+ pinnedTop: rowsMeta.pinnedTopRowsTotalHeight,
204
+ pinnedBottom: rowsMeta.pinnedBottomRowsTotalHeight
205
+ };
206
+ }
207
+ if (prevDimensions.isReady && hasScrollY !== prevDimensions.hasScrollY) {
208
+ if (!heightsChanged) {
209
+ osc.counter += 1;
210
+ }
211
+ if (osc.counter >= 2) {
212
+ hasScrollY = false;
213
+ // Recompute hasScrollX without the vertical scrollbar's width impact,
214
+ // otherwise the cascade (hasScrollY → narrower viewport → hasScrollX)
215
+ // keeps the horizontal scrollbar/filler alive and the root keeps resizing.
216
+ hasScrollX = hasScrollXIfNoYScrollBar;
217
+ }
218
+ }
219
+ }
174
220
  if (hasScrollY) {
175
221
  viewportInnerSize.width -= scrollbarSize;
176
222
  }
@@ -205,7 +251,6 @@ export function useGridDimensions(apiRef, props) {
205
251
  topContainerHeight,
206
252
  bottomContainerHeight
207
253
  };
208
- const prevDimensions = apiRef.current.state.dimensions;
209
254
  if (isDeepEqual(prevDimensions, newDimensions)) {
210
255
  return;
211
256
  }
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v7.29.12
2
+ * @mui/x-data-grid v7.29.13
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -67,6 +67,21 @@ export function useGridDimensions(apiRef, props) {
67
67
  const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector);
68
68
  const columnsTotalWidth = useGridSelector(apiRef, columnsTotalWidthSelector);
69
69
  const isFirstSizing = React.useRef(true);
70
+
71
+ // Vertical scrollbar oscillation detector.
72
+ // Counts consecutive hasScrollY flips that happen with no row-height change.
73
+ // After 2 flips it is certainly a layout feedback loop, so every further flip
74
+ // is forced to false (no scrollbar). The counter resets when row heights change.
75
+ // Only vertical scrollbar can oscillate because column widths are never 'auto'.
76
+ // https://github.com/mui/mui-x/issues/20539
77
+ const scrollYOscillation = React.useRef({
78
+ counter: 0,
79
+ heights: {
80
+ content: 0,
81
+ pinnedTop: 0,
82
+ pinnedBottom: 0
83
+ }
84
+ });
70
85
  const {
71
86
  rowHeight,
72
87
  headerHeight,
@@ -134,6 +149,7 @@ export function useGridDimensions(apiRef, props) {
134
149
  width: nonPinnedColumnsTotalWidth,
135
150
  height: roundToDecimalPlaces(rowsMeta.currentPageTotalHeight, 1)
136
151
  };
152
+ const prevDimensions = apiRef.current.state.dimensions;
137
153
  let viewportOuterSize;
138
154
  let viewportInnerSize;
139
155
  let hasScrollX = false;
@@ -171,6 +187,36 @@ export function useGridDimensions(apiRef, props) {
171
187
  hasScrollY = content.height + scrollbarSize > container.height;
172
188
  }
173
189
  }
190
+
191
+ // Detect vertical scrollbar oscillation.
192
+ // Track consecutive hasScrollY flips with no row-height change.
193
+ // Once confirmed (≥ 2 flips), force hasScrollY off — the scrollbar is
194
+ // not genuinely needed, it is a layout feedback loop caused by stale
195
+ // rootSize or the horizontal scrollbar's height cascading.
196
+ {
197
+ const osc = scrollYOscillation.current;
198
+ const heightsChanged = rowsMeta.currentPageTotalHeight !== osc.heights.content || rowsMeta.pinnedTopRowsTotalHeight !== osc.heights.pinnedTop || rowsMeta.pinnedBottomRowsTotalHeight !== osc.heights.pinnedBottom;
199
+ if (heightsChanged) {
200
+ osc.counter = 0;
201
+ osc.heights = {
202
+ content: rowsMeta.currentPageTotalHeight,
203
+ pinnedTop: rowsMeta.pinnedTopRowsTotalHeight,
204
+ pinnedBottom: rowsMeta.pinnedBottomRowsTotalHeight
205
+ };
206
+ }
207
+ if (prevDimensions.isReady && hasScrollY !== prevDimensions.hasScrollY) {
208
+ if (!heightsChanged) {
209
+ osc.counter += 1;
210
+ }
211
+ if (osc.counter >= 2) {
212
+ hasScrollY = false;
213
+ // Recompute hasScrollX without the vertical scrollbar's width impact,
214
+ // otherwise the cascade (hasScrollY → narrower viewport → hasScrollX)
215
+ // keeps the horizontal scrollbar/filler alive and the root keeps resizing.
216
+ hasScrollX = hasScrollXIfNoYScrollBar;
217
+ }
218
+ }
219
+ }
174
220
  if (hasScrollY) {
175
221
  viewportInnerSize.width -= scrollbarSize;
176
222
  }
@@ -205,7 +251,6 @@ export function useGridDimensions(apiRef, props) {
205
251
  topContainerHeight,
206
252
  bottomContainerHeight
207
253
  };
208
- const prevDimensions = apiRef.current.state.dimensions;
209
254
  if (isDeepEqual(prevDimensions, newDimensions)) {
210
255
  return;
211
256
  }
package/modern/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v7.29.12
2
+ * @mui/x-data-grid v7.29.13
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
@@ -77,6 +77,21 @@ function useGridDimensions(apiRef, props) {
77
77
  const densityFactor = (0, _utils2.useGridSelector)(apiRef, _density.gridDensityFactorSelector);
78
78
  const columnsTotalWidth = (0, _utils2.useGridSelector)(apiRef, columnsTotalWidthSelector);
79
79
  const isFirstSizing = React.useRef(true);
80
+
81
+ // Vertical scrollbar oscillation detector.
82
+ // Counts consecutive hasScrollY flips that happen with no row-height change.
83
+ // After 2 flips it is certainly a layout feedback loop, so every further flip
84
+ // is forced to false (no scrollbar). The counter resets when row heights change.
85
+ // Only vertical scrollbar can oscillate because column widths are never 'auto'.
86
+ // https://github.com/mui/mui-x/issues/20539
87
+ const scrollYOscillation = React.useRef({
88
+ counter: 0,
89
+ heights: {
90
+ content: 0,
91
+ pinnedTop: 0,
92
+ pinnedBottom: 0
93
+ }
94
+ });
80
95
  const {
81
96
  rowHeight,
82
97
  headerHeight,
@@ -144,6 +159,7 @@ function useGridDimensions(apiRef, props) {
144
159
  width: nonPinnedColumnsTotalWidth,
145
160
  height: (0, _roundToDecimalPlaces.roundToDecimalPlaces)(rowsMeta.currentPageTotalHeight, 1)
146
161
  };
162
+ const prevDimensions = apiRef.current.state.dimensions;
147
163
  let viewportOuterSize;
148
164
  let viewportInnerSize;
149
165
  let hasScrollX = false;
@@ -181,6 +197,36 @@ function useGridDimensions(apiRef, props) {
181
197
  hasScrollY = content.height + scrollbarSize > container.height;
182
198
  }
183
199
  }
200
+
201
+ // Detect vertical scrollbar oscillation.
202
+ // Track consecutive hasScrollY flips with no row-height change.
203
+ // Once confirmed (≥ 2 flips), force hasScrollY off — the scrollbar is
204
+ // not genuinely needed, it is a layout feedback loop caused by stale
205
+ // rootSize or the horizontal scrollbar's height cascading.
206
+ {
207
+ const osc = scrollYOscillation.current;
208
+ const heightsChanged = rowsMeta.currentPageTotalHeight !== osc.heights.content || rowsMeta.pinnedTopRowsTotalHeight !== osc.heights.pinnedTop || rowsMeta.pinnedBottomRowsTotalHeight !== osc.heights.pinnedBottom;
209
+ if (heightsChanged) {
210
+ osc.counter = 0;
211
+ osc.heights = {
212
+ content: rowsMeta.currentPageTotalHeight,
213
+ pinnedTop: rowsMeta.pinnedTopRowsTotalHeight,
214
+ pinnedBottom: rowsMeta.pinnedBottomRowsTotalHeight
215
+ };
216
+ }
217
+ if (prevDimensions.isReady && hasScrollY !== prevDimensions.hasScrollY) {
218
+ if (!heightsChanged) {
219
+ osc.counter += 1;
220
+ }
221
+ if (osc.counter >= 2) {
222
+ hasScrollY = false;
223
+ // Recompute hasScrollX without the vertical scrollbar's width impact,
224
+ // otherwise the cascade (hasScrollY → narrower viewport → hasScrollX)
225
+ // keeps the horizontal scrollbar/filler alive and the root keeps resizing.
226
+ hasScrollX = hasScrollXIfNoYScrollBar;
227
+ }
228
+ }
229
+ }
184
230
  if (hasScrollY) {
185
231
  viewportInnerSize.width -= scrollbarSize;
186
232
  }
@@ -215,7 +261,6 @@ function useGridDimensions(apiRef, props) {
215
261
  topContainerHeight,
216
262
  bottomContainerHeight
217
263
  };
218
- const prevDimensions = apiRef.current.state.dimensions;
219
264
  if ((0, _utils3.isDeepEqual)(prevDimensions, newDimensions)) {
220
265
  return;
221
266
  }
package/node/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-data-grid v7.29.12
2
+ * @mui/x-data-grid v7.29.13
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/x-data-grid",
3
- "version": "7.29.12",
3
+ "version": "7.29.13",
4
4
  "description": "The Community plan edition of the Data Grid components (MUI X).",
5
5
  "author": "MUI Team",
6
6
  "main": "./node/index.js",