@mui/utils 9.2.0 → 9.4.0

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,21 +1,175 @@
1
1
  # [Versions](https://mui.com/material-ui/getting-started/versions/)
2
2
 
3
+ ## 9.4.0
4
+
5
+ <!-- generated comparing v9.3.1..master -->
6
+
7
+ _Aug 27, 2026_
8
+
9
+ A big thanks to the 19 contributors who made this release possible. Here are some highlights ✨:
10
+
11
+ - ♿️ Opt in to a consistent [keyboard focus ring](https://mui.com/material-ui/customization/focus-visible/) across components with the new `theme.focusVisible`.
12
+ - ♿️ The [Tooltip](https://mui.com/material-ui/react-tooltip/) now supports disabled button triggers without an extra wrapper element.
13
+
14
+ ### `@mui/material@9.4.0`
15
+
16
+ - [theme] Add `theme.focusVisible` opt-in keyboard focus ring (#48743) @siriwatknp
17
+ - [buttongroup] Prevent sticky hover border on touch devices (#48993) @petr-kratochvil
18
+ - [l10n] Complete Traditional Chinese localization (#48946) @nrps9909
19
+ - [menulist] Preserve custom list padding when adjusting for the scrollbar (#48707) @creazyfrog
20
+ - [pagination] Manage focus whenever first / last / next / previous buttons become disabled (#48877) @silviuaavram
21
+ - [tooltip] Improve support for disabled button triggers (#48622) @mayank99
22
+
23
+ ### `@mui/utils@9.4.0`
24
+
25
+ - Fix resolveProps to correctly merge slotProps defined as functions (#48955) @mnajdova
26
+ - Replace deprecated `clip` CSS property with `clip-path` (#48966) @ZeeshanTamboli
27
+
28
+ ### `@mui/styled-engine-sc@9.4.0`
29
+
30
+ - Fix types for components extended with styled (#45988) @megos
31
+
32
+ ### Docs
33
+
34
+ - Bump @docsearch/react to ^4.7.0 (#48886) @LukasTy
35
+ - Fix broken links reported by Ahrefs (#48949) @brijeshb42
36
+ - Manage focus on Open in Chat button click (#48942) @silviuaavram
37
+ - Move focus to the main content with the skip link (#48944) @lazerg
38
+ - Use relative link for LICENSE in README (#49040) @loulanyue
39
+ - [autocomplete] Remove the "aria-owns" reference (#48958) @silviuaavram
40
+ - [table] Fix wrong number of rows reported to screen readers (#47653) @seomsoo
41
+ - [table] Reference DataGrid link in the row header section (#48988) @silviuaavram
42
+ - [table] Use and document the row header cells (#48965) @silviuaavram
43
+ - [tabs] Make tab panel focusable and show keep mounted examples (#48939) @silviuaavram
44
+ - [templates] Remove focusable links from hidden carousel slides (#49003) @gonzoblasco
45
+
46
+ ### Core
47
+
48
+ - [code-infra] Fix team sync review requests (#48914) @brijeshb42
49
+ - [code-infra] Import the test globals from vitest (#49033) @JCQuintas
50
+ - [code-infra] Point the support validator link at frontend-public (#49041) @Janpot
51
+ - [code-infra] Update CircleCI orb (#49030) @Janpot
52
+ - [code-infra] Use ESM current directory syntax for Vitest config (#49032) @ZeeshanTamboli
53
+ - [core] Use @typescript/typescript6 for the docs tooling (#49025) @Janpot
54
+ - [core-docs] Remove the `globalSelector` stylis middleware (#49029) @Janpot
55
+ - [test] Add an `assertions` mode to the axe regression harness (#48915) @michelengelen
56
+ - [test] Automate the CSS-dependent WCAG criteria (#48936) @michelengelen
57
+ - [test] Fail the regression run when a webfont does not load (#48948) @LukasTy
58
+ - [test] Fix typo 'overriden' -> 'overridden' in Alert test comment (#48987) @FirmaSpring
59
+ - [test] Replace webfontloader with the CSS Font Loading API (#48995) @LukasTy
60
+ - [test] Skip the CrudDashboard VRT on React 18 (#49027) @Janpot
61
+ - [test] Use the shared loadFonts from @mui/internal-test-utils (#49007) @LukasTy
62
+ - [test] Use the Vitest global expect instead of importing chai (#48996) @JCQuintas
63
+ - [test][tooltip] Keep the disabled trigger away from the real pointer (#48947) @LukasTy
64
+
65
+ All contributors of this release in alphabetical order: @brijeshb42, @creazyfrog, @FirmaSpring, @gonzoblasco, @Janpot, @JCQuintas, @lazerg, @loulanyue, @LukasTy, @mayank99, @megos, @michelengelen, @mnajdova, @nrps9909, @petr-kratochvil, @seomsoo, @silviuaavram, @siriwatknp, @ZeeshanTamboli
66
+
67
+ ## 9.3.1
68
+
69
+ <!-- generated comparing v9.3.0..master -->
70
+
71
+ _Aug 6, 2026_
72
+
73
+ A big thanks to the 4 contributors who made this release possible.
74
+
75
+ ### `@mui/material@9.3.1`
76
+
77
+ - [transitions] Prevent exit transitions from getting stuck (#48881) @ZeeshanTamboli
78
+
79
+ ### `@mui/codemod@9.3.1`
80
+
81
+ - Include transforms in published package (#48934) @brijeshb42
82
+
83
+ ### Core
84
+
85
+ - [blog] Clarify early bird renewal discount scope (#48906) @DanailH
86
+ - [test][pagination] Add more unit tests (#48927) @silviuaavram
87
+
88
+ All contributors of this release in alphabetical order: @brijeshb42, @DanailH, @silviuaavram, @ZeeshanTamboli
89
+
90
+ ## 9.3.0
91
+
92
+ <!-- generated comparing v9.2.0..master -->
93
+
94
+ _Aug 4, 2026_
95
+
96
+ A big thanks to the 18 contributors who made this release possible. Here are some highlights ✨:
97
+
98
+ - ♿️ Keyboard navigation in the [Toggle Button Group](https://mui.com/material-ui/react-toggle-button/) now follows the roving tabindex pattern.
99
+ - ♿️ The [Autocomplete](https://mui.com/material-ui/react-autocomplete/) announces its loading and no options messages through a new `status` slot.
100
+
101
+ ### `@mui/material@9.3.0`
102
+
103
+ - [autocomplete] Wrap the no results and loading messages in an aria live region (#48690) @silviuaavram
104
+ - [buttongroup] Respect global disableRipple / disableFocusRipple in grouped buttons (#48762) @siriwatknp
105
+ - [checkbox][radio] Respect global disableRipple from MuiButtonBase defaultProps (#48795) @siriwatknp
106
+ - [formcontrollabel] Add missing `labelPlacementEnd` class (#48843) @siriwatknp
107
+ - [listitembutton] Fix typos in component code (#48868) @ZeeshanTamboli
108
+ - [menuitem] Add `aria-checked` for checkbox and radio menu items (#48651) @siriwatknp
109
+ - [modal] Replace custom findIndexOf with findIndex (#48827) @ZeeshanTamboli
110
+ - [modal][dialog] Fix scrollbar compensation in Shadow DOM (#48826) @ZeeshanTamboli
111
+ - [select] Fix endAdornment overlapping the open indicator (#48723) @siriwatknp
112
+ - [tablepagination] Add focus style to default InputBase used in Select (#48871) @silviuaavram
113
+ - [togglebuttongroup] Add roving tabindex keyboard navigation (#48849) @silviuaavram
114
+
115
+ ### `@mui/system@9.3.0`
116
+
117
+ - Prevent prototype pollution in cssVarsParser (#48822) @Janpot
118
+
119
+ ### `@mui/codemod@9.3.0`
120
+
121
+ - Don't leak state between files in v5.0.0/path-imports (#48797) @manbearwiz
122
+ - Remove use of eval() (#48701) @oliviertassinari
123
+ - Transform all style exports in `v5.0.0/path-imports` codemod (#48800) @manbearwiz
124
+
125
+ ### Docs
126
+
127
+ - Clarify `container` prop inheritance for Shadow DOM (#48670) @Juan-C-Ceballos
128
+ - Clarify third-party script risk in CSP unsafe-inline note (#48787) @Janpot
129
+ - Document `box-sx-prop` codemod (#48888) @SR0725
130
+ - Document Grid direction column removal in v9 upgrade guide (#48880) @kalayciburak
131
+ - Fix color contrast in GitHub labels example (#48844) @silviuaavram
132
+ - Fix per-page canonical URL and stale SEO link (#48789) @brijeshb42
133
+ - Fix redirect loop on the Design Kits page (#48869) @Janpot
134
+ - Fix typo 'overriden' -> 'overridden' in CHANGELOG (#48785) @ZhouYinLong-lab
135
+ - Improve TransferList examples (#48845) @silviuaavram
136
+ - Link a new theme generator tool (#48847) @mansourcodes
137
+ - Remove broken learning resource links (#48598) @morning-verlu
138
+ - Update the 9.2.0 changelog (#48763) @silviuaavram
139
+ - [autocomplete] Clarify how to render custom start and end adornments (#48883) @DebadityaHait
140
+ - [drawer] Clarify clipped AppBar spacing (#48887) @rushijagani-dev
141
+ - [menu] Fix stale `selected` prop reference in Selected menu section (#48879) @LukasTy
142
+ - [skeleton] Document preserving typography sizing with custom border radius (#48596) @meaqua9420
143
+
144
+ ### Core
145
+
146
+ - [blog] Fix caret annotation broken by Prettier wrapping (#48884) @bernardobelchior
147
+ - [code-infra] Automate team sync on about page (#48794) @brijeshb42
148
+ - [code-infra] Bump @mui/internal-code-infra to 0.0.4-canary.101 (#48878) @Janpot
149
+ - [code-infra] Fix Dependabot alerts (#48756) @Janpot
150
+ - [code-infra] Fix team sync PR creation (#48911) @brijeshb42
151
+ - [code-infra] Pin node version in publish CI (#48799) @brijeshb42
152
+ - [core-docs] Tighten service worker host check (#48581) @Janpot
153
+ - [docs-infra] Change branch name to the allowed one (#48910) @brijeshb42
154
+
155
+ All contributors of this release in alphabetical order: @bernardobelchior, @brijeshb42, @DebadityaHait, @Janpot, @Juan-C-Ceballos, @kalayciburak, @LukasTy, @manbearwiz, @mansourcodes, @meaqua9420, @morning-verlu, @oliviertassinari, @rushijagani-dev, @silviuaavram, @siriwatknp, @SR0725, @ZeeshanTamboli, @ZhouYinLong-lab
156
+
3
157
  ## 9.2.0
4
158
 
5
159
  <!-- generated comparing v9.1.2..master -->
6
160
 
7
- _Jul 2, 2026_
161
+ _Jul 3, 2026_
8
162
 
9
163
  A big thanks to the 9 contributors who made this release possible.
10
164
 
11
165
  - ⚙️ Add support for [`data-*` attributes on `slotProps`](https://mui.com/material-ui/guides/typescript/#allowing-data-attributes-on-slotprops).
12
166
 
13
- ### `@mui/material@9.1.3`
167
+ ### `@mui/material@9.2.0`
14
168
 
15
169
  - [l10n] Add missing MuiPagination localization to zh-CN locale (#48741) @greymoth-jp
16
170
  - [select] Guard display ref during mouse down (#48744) @michelengelen
17
171
 
18
- ### `@mui/utils@9.1.3`
172
+ ### `@mui/utils@9.2.0`
19
173
 
20
174
  - [utils] Add opt-in `DataAttributesOverrides` augmentation for slot props (#48554) @LukasTy
21
175
 
@@ -1,5 +1,6 @@
1
+ declare const defaultGenerator: (componentName: string) => string;
1
2
  declare const ClassNameGenerator: {
2
- configure(generator: (componentName: string) => string): void;
3
+ configure(generator: typeof defaultGenerator): void;
3
4
  generate(componentName: string): string;
4
5
  reset(): void;
5
6
  };
@@ -1,5 +1,6 @@
1
+ declare const defaultGenerator: (componentName: string) => string;
1
2
  declare const ClassNameGenerator: {
2
- configure(generator: (componentName: string) => string): void;
3
+ configure(generator: typeof defaultGenerator): void;
3
4
  generate(componentName: string): string;
4
5
  reset(): void;
5
6
  };
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @mui/utils v9.2.0
2
+ * @mui/utils v9.4.0
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/utils v9.2.0
2
+ * @mui/utils v9.4.0
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/utils",
3
- "version": "9.2.0",
3
+ "version": "9.4.0",
4
4
  "author": "MUI Team",
5
5
  "description": "Utility functions for React components.",
6
6
  "license": "MIT",
@@ -24,12 +24,12 @@
24
24
  "url": "https://opencollective.com/mui-org"
25
25
  },
26
26
  "dependencies": {
27
- "@babel/runtime": "^7.29.2",
27
+ "@babel/runtime": "^7.29.7",
28
28
  "@types/prop-types": "^15.7.15",
29
29
  "clsx": "^2.1.1",
30
30
  "prop-types": "^15.8.1",
31
- "react-is": "^19.2.6",
32
- "@mui/types": "^9.1.1"
31
+ "react-is": "^19.2.8",
32
+ "@mui/types": "^9.4.0"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
@@ -41,7 +41,15 @@ function resolveProps(defaultProps, props, mergeClassNameAndStyle = false) {
41
41
  for (const slotKey in defaultSlotProps) {
42
42
  if (Object.prototype.hasOwnProperty.call(defaultSlotProps, slotKey)) {
43
43
  const slotPropName = slotKey;
44
- output[propName][slotPropName] = resolveProps(defaultSlotProps[slotPropName], slotProps[slotPropName], mergeClassNameAndStyle);
44
+ const defaultSlotPropValue = defaultSlotProps[slotPropName];
45
+ const slotPropValue = slotProps[slotPropName];
46
+ if (typeof defaultSlotPropValue === 'function' || typeof slotPropValue === 'function') {
47
+ // Function slot props are resolved later (with `ownerState`), so defer
48
+ // the merge until then to avoid dropping either side.
49
+ output[propName][slotPropName] = (...args) => resolveProps((typeof defaultSlotPropValue === 'function' ? defaultSlotPropValue(...args) : defaultSlotPropValue) ?? {}, (typeof slotPropValue === 'function' ? slotPropValue(...args) : slotPropValue) ?? {}, mergeClassNameAndStyle);
50
+ } else {
51
+ output[propName][slotPropName] = resolveProps(defaultSlotPropValue ?? {}, slotPropValue ?? {}, mergeClassNameAndStyle);
52
+ }
45
53
  }
46
54
  }
47
55
  }
@@ -35,7 +35,15 @@ export default function resolveProps(defaultProps, props, mergeClassNameAndStyle
35
35
  for (const slotKey in defaultSlotProps) {
36
36
  if (Object.prototype.hasOwnProperty.call(defaultSlotProps, slotKey)) {
37
37
  const slotPropName = slotKey;
38
- output[propName][slotPropName] = resolveProps(defaultSlotProps[slotPropName], slotProps[slotPropName], mergeClassNameAndStyle);
38
+ const defaultSlotPropValue = defaultSlotProps[slotPropName];
39
+ const slotPropValue = slotProps[slotPropName];
40
+ if (typeof defaultSlotPropValue === 'function' || typeof slotPropValue === 'function') {
41
+ // Function slot props are resolved later (with `ownerState`), so defer
42
+ // the merge until then to avoid dropping either side.
43
+ output[propName][slotPropName] = (...args) => resolveProps((typeof defaultSlotPropValue === 'function' ? defaultSlotPropValue(...args) : defaultSlotPropValue) ?? {}, (typeof slotPropValue === 'function' ? slotPropValue(...args) : slotPropValue) ?? {}, mergeClassNameAndStyle);
44
+ } else {
45
+ output[propName][slotPropName] = resolveProps(defaultSlotPropValue ?? {}, slotPropValue ?? {}, mergeClassNameAndStyle);
46
+ }
39
47
  }
40
48
  }
41
49
  }
@@ -91,7 +91,7 @@ export interface UseRovingTabIndexReturnValue<Key = unknown> {
91
91
  * Spread these props onto the list or composite root element that should listen for focus
92
92
  * and keyboard events.
93
93
  */
94
- getContainerProps: (ref?: React.Ref<HTMLElement>) => {
94
+ getContainerProps: (ref?: React.Ref<HTMLElement> | undefined, onFocus?: ((event: React.FocusEvent<HTMLElement>) => void) | undefined, onKeyDown?: ((event: React.KeyboardEvent<HTMLElement>) => void) | undefined) => {
95
95
  /**
96
96
  * Keeps the active item in sync when focus moves onto one of the registered items.
97
97
  */
@@ -91,7 +91,7 @@ export interface UseRovingTabIndexReturnValue<Key = unknown> {
91
91
  * Spread these props onto the list or composite root element that should listen for focus
92
92
  * and keyboard events.
93
93
  */
94
- getContainerProps: (ref?: React.Ref<HTMLElement>) => {
94
+ getContainerProps: (ref?: React.Ref<HTMLElement> | undefined, onFocus?: ((event: React.FocusEvent<HTMLElement>) => void) | undefined, onKeyDown?: ((event: React.KeyboardEvent<HTMLElement>) => void) | undefined) => {
95
95
  /**
96
96
  * Keeps the active item in sync when focus moves onto one of the registered items.
97
97
  */
@@ -97,8 +97,9 @@ function useRovingTabIndexRoot(params) {
97
97
  setActiveItemIdState(nextItem.id);
98
98
  return nextItem;
99
99
  }, [itemFilter]);
100
- const getContainerProps = React.useCallback(ref => {
100
+ const getContainerProps = React.useCallback((ref, onFocusProp, onKeyDownProp) => {
101
101
  const onFocus = event => {
102
+ onFocusProp?.(event);
102
103
  const snapshot = getNavigableItemsSnapshot(itemMapRef.current);
103
104
  const focusedIndex = findItemIndexByElement(snapshot, event.target);
104
105
  if (focusedIndex !== -1) {
@@ -106,7 +107,8 @@ function useRovingTabIndexRoot(params) {
106
107
  }
107
108
  };
108
109
  const onKeyDown = event => {
109
- if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) {
110
+ onKeyDownProp?.(event);
111
+ if (event.defaultPrevented || event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) {
110
112
  return;
111
113
  }
112
114
  if (!SUPPORTED_KEYS.includes(event.key)) {
@@ -88,8 +88,9 @@ export function useRovingTabIndexRoot(params) {
88
88
  setActiveItemIdState(nextItem.id);
89
89
  return nextItem;
90
90
  }, [itemFilter]);
91
- const getContainerProps = React.useCallback(ref => {
91
+ const getContainerProps = React.useCallback((ref, onFocusProp, onKeyDownProp) => {
92
92
  const onFocus = event => {
93
+ onFocusProp?.(event);
93
94
  const snapshot = getNavigableItemsSnapshot(itemMapRef.current);
94
95
  const focusedIndex = findItemIndexByElement(snapshot, event.target);
95
96
  if (focusedIndex !== -1) {
@@ -97,7 +98,8 @@ export function useRovingTabIndexRoot(params) {
97
98
  }
98
99
  };
99
100
  const onKeyDown = event => {
100
- if (event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) {
101
+ onKeyDownProp?.(event);
102
+ if (event.defaultPrevented || event.altKey || event.shiftKey || event.ctrlKey || event.metaKey) {
101
103
  return;
102
104
  }
103
105
  if (!SUPPORTED_KEYS.includes(event.key)) {
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  const visuallyHidden = {
8
8
  border: 0,
9
- clip: 'rect(0 0 0 0)',
9
+ clipPath: 'inset(50%)',
10
10
  height: '1px',
11
11
  margin: '-1px',
12
12
  overflow: 'hidden',
@@ -1,6 +1,6 @@
1
1
  const visuallyHidden = {
2
2
  border: 0,
3
- clip: 'rect(0 0 0 0)',
3
+ clipPath: 'inset(50%)',
4
4
  height: '1px',
5
5
  margin: '-1px',
6
6
  overflow: 'hidden',