@mui/x-virtualizer 1.0.0-beta.0 → 1.0.0-beta.1

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
@@ -1,5 +1,111 @@
1
1
  # Changelog
2
2
 
3
+ ## 9.0.0-beta.0
4
+
5
+ <!-- generated comparing v9.0.0-alpha.4..master -->
6
+
7
+ _Mar 27, 2026_
8
+
9
+ We'd like to extend a big thank you to the 10 contributors who made this release possible. Here are some highlights ✨:
10
+
11
+ - 🔊 New Charts voiceover component for improved screen reader support
12
+ - ⌨️ Charts keyboard navigation improvements: axis tooltip now shows when navigating with the keyboard
13
+ - 📊 Charts axes now can be set to automatically resize to fit their content
14
+ - 📝 New `rowCheckbox` slot in Data Grid for easier checkbox column customization
15
+ - ⚡️ `fetchRows()` API in Data Grid Pro now defaults `start` and `end` based on scroll position with lazy loading
16
+ - 🐞 Bugfixes and internal improvements
17
+
18
+ The following team members contributed to this release:
19
+ @aemartos, @alexfauquette, @arminmeh, @cherniavskii, @Janpot, @JCQuintas, @mapache-salvaje, @michelengelen, @noraleonte, @rita-codes
20
+
21
+ ### Data Grid
22
+
23
+ #### `@mui/x-data-grid@9.0.0-beta.0`
24
+
25
+ - [DataGrid] Add `rowCheckbox` slot for easier customization (#21797) @michelengelen
26
+ - [DataGrid] Prevent repeated `hasScrollbar` state updates (#21820) @arminmeh
27
+
28
+ #### `@mui/x-data-grid-pro@9.0.0-beta.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
29
+
30
+ Same changes as in `@mui/x-data-grid@9.0.0-beta.0`, plus:
31
+
32
+ - [DataGridPro] `fetchRows()` API's default `start` and `end` params based on scroll position with lazy loading (#21742) @arminmeh
33
+
34
+ #### `@mui/x-data-grid-premium@9.0.0-beta.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
35
+
36
+ Same changes as in `@mui/x-data-grid-pro@9.0.0-beta.0`.
37
+
38
+ ### Date and Time Pickers
39
+
40
+ #### `@mui/x-date-pickers@9.0.0-beta.0`
41
+
42
+ Internal changes.
43
+
44
+ #### `@mui/x-date-pickers-pro@9.0.0-beta.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
45
+
46
+ Same changes as in `@mui/x-date-pickers@9.0.0-beta.0`.
47
+
48
+ ### Charts
49
+
50
+ #### `@mui/x-charts@9.0.0-beta.0`
51
+
52
+ - [charts] Add `className` prop to Pro chart plot components (#21793) @JCQuintas
53
+ - [charts] Add experimental position-based pointer interaction for line series (#21809) @JCQuintas
54
+ - [charts] Add l10n to the bar accessibility (#21815) @alexfauquette
55
+ - [charts] Add localization for the basic charts (#21822) @alexfauquette
56
+ - [charts] Add voiceover component (#21344) @alexfauquette
57
+ - [charts] Allow axes to automatically resize to content (#21087) @JCQuintas
58
+ - [charts] Document multiple use-cases for references (#21768) @alexfauquette
59
+ - [charts] Remove compatibility layer for React vs native events (#21780) @JCQuintas
60
+ - [charts] Remove deprecated `barLabel` props (#21783) @alexfauquette
61
+ - [charts] Show axis tooltip when navigating with keyboard (#21689) @Copilot
62
+
63
+ #### `@mui/x-charts-pro@9.0.0-beta.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
64
+
65
+ Same changes as in `@mui/x-charts@9.0.0-beta.0`.
66
+
67
+ #### `@mui/x-charts-premium@9.0.0-beta.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan')
68
+
69
+ Same changes as in `@mui/x-charts-pro@9.0.0-beta.0`.
70
+
71
+ ### Tree View
72
+
73
+ #### `@mui/x-tree-view@9.0.0-alpha.4`
74
+
75
+ Internal changes.
76
+
77
+ #### `@mui/x-tree-view-pro@9.0.0-alpha.4` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan')
78
+
79
+ Same changes as in `@mui/x-tree-view@9.0.0-alpha.4`.
80
+
81
+ ### Codemod
82
+
83
+ #### `@mui/x-codemod@9.0.0-alpha.4`
84
+
85
+ Internal changes.
86
+
87
+ ### Docs
88
+
89
+ - [docs] Document how to customize voiceover announcement (#21833) @alexfauquette
90
+ - [docs] Remove Discord mention from docs (#21855) @mapache-salvaje
91
+ - [docs] Remove stabilized experimental feature from demo (#21869) @JCQuintas
92
+ - [docs] Update telemetry guide to reflect pseudonymous data collection and license compliance (#21812) @aemartos
93
+ - [docs] Revise the Sparkline doc (#21614) @mapache-salvaje
94
+ - [docs] Revise the Gauge doc (#21673) @mapache-salvaje
95
+ - [docs] Revise the Heatmap doc (#21676) @mapache-salvaje
96
+
97
+ ### Core
98
+
99
+ - [code-infra] Remove unused deps and unify es-toolkit via catalog (#21840) @Janpot
100
+ - [code-infra] Update @mui/internal-bundle-size-checker to canary.68 (#21836) @Janpot
101
+ - [code-infra] Update next (#21837) @Janpot
102
+ - [internal] Remove headless data grid packages (#21843) @cherniavskii
103
+
104
+ ### Miscellaneous
105
+
106
+ - Add @romgrk to CODEOWNERS for `x-virtualizer` and `x-internals` (#21819) @Copilot
107
+ - [x-license] add 2022 plan version (#21814) @aemartos
108
+
3
109
  ## 9.0.0-alpha.4
4
110
 
5
111
  _Mar 19, 2026_
@@ -59,7 +165,7 @@ Same changes as in `@mui/x-date-pickers@9.0.0-alpha.4`.
59
165
  - [charts] Remove deprecated `useMouseTracker()` (#21787) @alexfauquette
60
166
  - [charts] Remove deprecated classes (#21775) @alexfauquette
61
167
  - [charts] Remove deprecated props from PieArcLabel animation (#21789) @alexfauquette
62
- - [charts] Remove get*UtilityClass from public exports (#21769) @JCQuintas
168
+ - [charts] Remove get\*UtilityClass from public exports (#21769) @JCQuintas
63
169
  - [charts] Remove the deprecated `disableHover` property (#21785) @alexfauquette
64
170
  - [charts] Remove the deprecated `message` prop (#21784) @alexfauquette
65
171
  - [charts] Remove deprecated props about voronoi (#21796) @alexfauquette
@@ -97,6 +97,21 @@ function initializeState(params) {
97
97
  }
98
98
  function useDimensions(store, params, _api) {
99
99
  const isFirstSizing = React.useRef(true);
100
+
101
+ // Vertical scrollbar oscillation detector.
102
+ // Counts consecutive hasScrollY flips that happen with no row-height change.
103
+ // After 2 flips it is certainly a layout feedback loop, so every further flip
104
+ // is forced to false (no scrollbar). The counter resets when row heights change.
105
+ // Only vertical scrollbar can oscillate because column widths are never 'auto'.
106
+ // https://github.com/mui/mui-x/issues/20539
107
+ const scrollYOscillation = React.useRef({
108
+ counter: 0,
109
+ heights: {
110
+ content: 0,
111
+ pinnedTop: 0,
112
+ pinnedBottom: 0
113
+ }
114
+ });
100
115
  const {
101
116
  layout,
102
117
  dimensions: {
@@ -130,6 +145,7 @@ function useDimensions(store, params, _api) {
130
145
  width: columnsTotalWidth,
131
146
  height: (0, _math.roundToDecimalPlaces)(rowsMeta.currentPageTotalHeight, 1)
132
147
  };
148
+ const prevDimensions = store.state.dimensions;
133
149
  let viewportOuterSize;
134
150
  let viewportInnerSize;
135
151
  let hasScrollX = false;
@@ -167,6 +183,36 @@ function useDimensions(store, params, _api) {
167
183
  hasScrollY = content.height + scrollbarSize > container.height;
168
184
  }
169
185
  }
186
+
187
+ // Detect vertical scrollbar oscillation.
188
+ // Track consecutive hasScrollY flips with no row-height change.
189
+ // Once confirmed (≥ 2 flips), force hasScrollY off — the scrollbar is
190
+ // not genuinely needed, it is a layout feedback loop caused by stale
191
+ // rootSize or the horizontal scrollbar's height cascading.
192
+ {
193
+ const osc = scrollYOscillation.current;
194
+ const heightsChanged = rowsMeta.currentPageTotalHeight !== osc.heights.content || rowsMeta.pinnedTopRowsTotalHeight !== osc.heights.pinnedTop || rowsMeta.pinnedBottomRowsTotalHeight !== osc.heights.pinnedBottom;
195
+ if (heightsChanged) {
196
+ osc.counter = 0;
197
+ osc.heights = {
198
+ content: rowsMeta.currentPageTotalHeight,
199
+ pinnedTop: rowsMeta.pinnedTopRowsTotalHeight,
200
+ pinnedBottom: rowsMeta.pinnedBottomRowsTotalHeight
201
+ };
202
+ }
203
+ if (prevDimensions.isReady && hasScrollY !== prevDimensions.hasScrollY) {
204
+ if (!heightsChanged) {
205
+ osc.counter += 1;
206
+ }
207
+ if (osc.counter >= 2) {
208
+ hasScrollY = false;
209
+ // Recompute hasScrollX without the vertical scrollbar's width impact,
210
+ // otherwise the cascade (hasScrollY → narrower viewport → hasScrollX)
211
+ // keeps the horizontal scrollbar/filler alive and the root keeps resizing.
212
+ hasScrollX = hasScrollXIfNoYScrollBar;
213
+ }
214
+ }
215
+ }
170
216
  if (hasScrollY) {
171
217
  viewportInnerSize.width -= scrollbarSize;
172
218
  }
@@ -205,7 +251,6 @@ function useDimensions(store, params, _api) {
205
251
  autoHeight: params.dimensions.autoHeight,
206
252
  minimalContentHeight: params.dimensions.minimalContentHeight
207
253
  };
208
- const prevDimensions = store.state.dimensions;
209
254
  if ((0, _isDeepEqual.isDeepEqual)(prevDimensions, newDimensions)) {
210
255
  return;
211
256
  }
@@ -90,6 +90,21 @@ function initializeState(params) {
90
90
  }
91
91
  function useDimensions(store, params, _api) {
92
92
  const isFirstSizing = React.useRef(true);
93
+
94
+ // Vertical scrollbar oscillation detector.
95
+ // Counts consecutive hasScrollY flips that happen with no row-height change.
96
+ // After 2 flips it is certainly a layout feedback loop, so every further flip
97
+ // is forced to false (no scrollbar). The counter resets when row heights change.
98
+ // Only vertical scrollbar can oscillate because column widths are never 'auto'.
99
+ // https://github.com/mui/mui-x/issues/20539
100
+ const scrollYOscillation = React.useRef({
101
+ counter: 0,
102
+ heights: {
103
+ content: 0,
104
+ pinnedTop: 0,
105
+ pinnedBottom: 0
106
+ }
107
+ });
93
108
  const {
94
109
  layout,
95
110
  dimensions: {
@@ -123,6 +138,7 @@ function useDimensions(store, params, _api) {
123
138
  width: columnsTotalWidth,
124
139
  height: roundToDecimalPlaces(rowsMeta.currentPageTotalHeight, 1)
125
140
  };
141
+ const prevDimensions = store.state.dimensions;
126
142
  let viewportOuterSize;
127
143
  let viewportInnerSize;
128
144
  let hasScrollX = false;
@@ -160,6 +176,36 @@ function useDimensions(store, params, _api) {
160
176
  hasScrollY = content.height + scrollbarSize > container.height;
161
177
  }
162
178
  }
179
+
180
+ // Detect vertical scrollbar oscillation.
181
+ // Track consecutive hasScrollY flips with no row-height change.
182
+ // Once confirmed (≥ 2 flips), force hasScrollY off — the scrollbar is
183
+ // not genuinely needed, it is a layout feedback loop caused by stale
184
+ // rootSize or the horizontal scrollbar's height cascading.
185
+ {
186
+ const osc = scrollYOscillation.current;
187
+ const heightsChanged = rowsMeta.currentPageTotalHeight !== osc.heights.content || rowsMeta.pinnedTopRowsTotalHeight !== osc.heights.pinnedTop || rowsMeta.pinnedBottomRowsTotalHeight !== osc.heights.pinnedBottom;
188
+ if (heightsChanged) {
189
+ osc.counter = 0;
190
+ osc.heights = {
191
+ content: rowsMeta.currentPageTotalHeight,
192
+ pinnedTop: rowsMeta.pinnedTopRowsTotalHeight,
193
+ pinnedBottom: rowsMeta.pinnedBottomRowsTotalHeight
194
+ };
195
+ }
196
+ if (prevDimensions.isReady && hasScrollY !== prevDimensions.hasScrollY) {
197
+ if (!heightsChanged) {
198
+ osc.counter += 1;
199
+ }
200
+ if (osc.counter >= 2) {
201
+ hasScrollY = false;
202
+ // Recompute hasScrollX without the vertical scrollbar's width impact,
203
+ // otherwise the cascade (hasScrollY → narrower viewport → hasScrollX)
204
+ // keeps the horizontal scrollbar/filler alive and the root keeps resizing.
205
+ hasScrollX = hasScrollXIfNoYScrollBar;
206
+ }
207
+ }
208
+ }
163
209
  if (hasScrollY) {
164
210
  viewportInnerSize.width -= scrollbarSize;
165
211
  }
@@ -198,7 +244,6 @@ function useDimensions(store, params, _api) {
198
244
  autoHeight: params.dimensions.autoHeight,
199
245
  minimalContentHeight: params.dimensions.minimalContentHeight
200
246
  };
201
- const prevDimensions = store.state.dimensions;
202
247
  if (isDeepEqual(prevDimensions, newDimensions)) {
203
248
  return;
204
249
  }
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-virtualizer v1.0.0-beta.0
2
+ * @mui/x-virtualizer v1.0.0-beta.1
3
3
  *
4
4
  * @license MIT
5
5
  * This source code is licensed under the MIT license found in the
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/x-virtualizer v1.0.0-beta.0
2
+ * @mui/x-virtualizer v1.0.0-beta.1
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-virtualizer",
3
- "version": "1.0.0-beta.0",
3
+ "version": "1.0.0-beta.1",
4
4
  "author": "MUI Team",
5
5
  "description": "MUI virtualization library",
6
6
  "license": "MIT",