From cb2a1919252c9dd409fa52a6609c0755f31952d9 Mon Sep 17 00:00:00 2001 From: Diego Andai Date: Mon, 20 Jan 2025 18:27:46 -0300 Subject: [PATCH 1/7] [material-ui][TextField] Fix filled state to be synced with autofill (#44135) --- .../src/FormControl/FormControl.js | 18 +++++++++----- .../src/TextField/TextField.test.js | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/mui-material/src/FormControl/FormControl.js b/packages/mui-material/src/FormControl/FormControl.js index dab1856439aa8a..f05e75e14872ce 100644 --- a/packages/mui-material/src/FormControl/FormControl.js +++ b/packages/mui-material/src/FormControl/FormControl.js @@ -193,6 +193,14 @@ const FormControl = React.forwardRef(function FormControl(inProps, ref) { }; } + const onFilled = React.useCallback(() => { + setFilled(true); + }, []); + + const onEmpty = React.useCallback(() => { + setFilled(false); + }, []); + const childContext = React.useMemo(() => { return { adornedStart, @@ -208,15 +216,11 @@ const FormControl = React.forwardRef(function FormControl(inProps, ref) { onBlur: () => { setFocused(false); }, - onEmpty: () => { - setFilled(false); - }, - onFilled: () => { - setFilled(true); - }, onFocus: () => { setFocused(true); }, + onEmpty, + onFilled, registerEffect, required, variant, @@ -231,6 +235,8 @@ const FormControl = React.forwardRef(function FormControl(inProps, ref) { fullWidth, hiddenLabel, registerEffect, + onEmpty, + onFilled, required, size, variant, diff --git a/packages/mui-material/src/TextField/TextField.test.js b/packages/mui-material/src/TextField/TextField.test.js index e1392563a0d5d7..adc29954d2ce2b 100644 --- a/packages/mui-material/src/TextField/TextField.test.js +++ b/packages/mui-material/src/TextField/TextField.test.js @@ -303,4 +303,28 @@ describe('', () => { expect(getByRole('textbox')).to.have.attribute('data-testid', 'input-element'); }); }); + + describe('autofill', () => { + it('should be filled after auto fill event', () => { + function AutoFillComponentTest() { + const [value, setValue] = React.useState(''); + return ( + setValue(event.target.value)} + label="test" + variant="standard" + slotProps={{ + htmlInput: { 'data-testid': 'htmlInput' }, + inputLabel: { 'data-testid': 'label' }, + }} + /> + ); + } + + const { getByTestId } = render(); + fireEvent.animationStart(getByTestId('htmlInput'), { animationName: 'mui-auto-fill' }); + expect(getByTestId('label').getAttribute('data-shrink')).to.equal('true'); + }); + }); }); From bea77fb5afae71981163207a109475154dd62ce5 Mon Sep 17 00:00:00 2001 From: Fullchee Zhang Date: Tue, 21 Jan 2025 04:12:43 -0500 Subject: [PATCH 2/7] [docs] Fix typo (#45070) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fullchee Zhang Co-authored-by: Aarón García Hervás --- docs/data/base/components/number-input/number-input.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data/base/components/number-input/number-input.md b/docs/data/base/components/number-input/number-input.md index 2e9baa262ebbb9..275d38cb1dc612 100644 --- a/docs/data/base/components/number-input/number-input.md +++ b/docs/data/base/components/number-input/number-input.md @@ -139,7 +139,7 @@ If you only define one or the other, the opposite end of the range will be open- ``` -The demo below shows a Number Input with a an accepted range of 1 to 99: +The demo below shows a Number Input with an accepted range of 1 to 99: {{"demo": "QuantityInput.js", "defaultCodeOpen": false}} From 57639f923cb464b7f2e365f1cec21dbc01a29f33 Mon Sep 17 00:00:00 2001 From: Siriwat K Date: Tue, 21 Jan 2025 17:00:24 +0700 Subject: [PATCH 3/7] [utils] Do not deep merge React component (#45058) --- packages/mui-utils/src/deepmerge/deepmerge.test.ts | 11 +++++++++++ packages/mui-utils/src/deepmerge/deepmerge.ts | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/mui-utils/src/deepmerge/deepmerge.test.ts b/packages/mui-utils/src/deepmerge/deepmerge.test.ts index 5bcc9d0e9a3d69..5d5db639ee3d72 100644 --- a/packages/mui-utils/src/deepmerge/deepmerge.test.ts +++ b/packages/mui-utils/src/deepmerge/deepmerge.test.ts @@ -132,6 +132,17 @@ describe('deepmerge', () => { expect(result.element).to.equal(element2); }); + it('should not deep clone React component', () => { + // most 3rd-party components use `forwardRef` + const Link = React.forwardRef((props, ref) => React.createElement('a', { ref, ...props })); + const result = deepmerge( + { defaultProps: { component: 'a' } }, + { defaultProps: { component: Link } }, + ); + + expect(result.defaultProps.component).to.equal(Link); + }); + it('should deep clone example correctly', () => { const result = deepmerge({ a: { b: 1 }, d: 2 }, { a: { c: 2 }, d: 4 }); diff --git a/packages/mui-utils/src/deepmerge/deepmerge.ts b/packages/mui-utils/src/deepmerge/deepmerge.ts index 6f202cb2cfc77e..d23ad92c807aa7 100644 --- a/packages/mui-utils/src/deepmerge/deepmerge.ts +++ b/packages/mui-utils/src/deepmerge/deepmerge.ts @@ -1,4 +1,5 @@ import * as React from 'react'; +import { isValidElementType } from 'react-is'; // https://github.com/sindresorhus/is-plain-obj/blob/main/index.js export function isPlainObject(item: unknown): item is Record { @@ -21,7 +22,7 @@ export interface DeepmergeOptions { } function deepClone(source: T): T | Record { - if (React.isValidElement(source) || !isPlainObject(source)) { + if (React.isValidElement(source) || isValidElementType(source) || !isPlainObject(source)) { return source; } @@ -61,7 +62,7 @@ export default function deepmerge( if (isPlainObject(target) && isPlainObject(source)) { Object.keys(source).forEach((key) => { - if (React.isValidElement(source[key])) { + if (React.isValidElement(source[key]) || isValidElementType(source[key])) { (output as Record)[key] = source[key]; } else if ( isPlainObject(source[key]) && From 4f872d616e589f10955b84e13122d05e3e89dde1 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Tue, 21 Jan 2025 11:15:13 +0100 Subject: [PATCH 4/7] [code-infra] Add "use client" directive to files with React APIs (#45036) --- .eslintrc.js | 15 ++++++ .../eslint-plugin-material-ui/src/index.js | 1 + ...disallow-react-api-in-server-components.js | 51 +++++++++++++++++++ .../mui-lab/src/Timeline/TimelineContext.ts | 1 + .../ButtonGroup/ButtonGroupButtonContext.ts | 1 + .../src/ButtonGroup/ButtonGroupContext.ts | 1 + .../mui-material/src/Dialog/DialogContext.ts | 1 + .../src/FormControl/FormControlContext.ts | 1 + packages/mui-material/src/Hidden/withWidth.js | 1 + .../src/RadioGroup/RadioGroupContext.ts | 1 + packages/mui-material/src/Step/StepContext.ts | 1 + .../src/Stepper/StepperContext.ts | 1 + .../src/Table/Tablelvl2Context.js | 1 + .../ToggleButtonGroupButtonContext.ts | 1 + .../ToggleButtonGroupContext.ts | 1 + .../src/useTheme/ThemeContext.js | 1 + .../src/StylesProvider/StylesProvider.js | 1 + .../mui-styles/src/makeStyles/makeStyles.js | 1 + packages/mui-system/src/RtlProvider/index.js | 1 + .../src/cssVars/createCssVarsProvider.js | 1 + 20 files changed, 84 insertions(+) create mode 100644 packages/eslint-plugin-material-ui/src/rules/disallow-react-api-in-server-components.js diff --git a/.eslintrc.js b/.eslintrc.js index 9560ab5032ba54..24f8b126a74ca9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -408,6 +408,21 @@ module.exports = /** @type {Config} */ ({ 'react/prop-types': 'off', }, }, + { + files: ['packages/*/src/*/*.?(c|m)[jt]s?(x)'], + excludedFiles: [ + '*.spec.*', + '*.test.*', + // deprecated library + '**/mui-base/**/*', + '**/mui-joy/**/*', + // used internally, not used on app router yet + '**/mui-docs/**/*', + ], + rules: { + 'material-ui/disallow-react-api-in-server-components': 'error', + }, + }, { files: ['packages/*/src/**/*.?(c|m)[jt]s?(x)'], excludedFiles: '*.spec.*', diff --git a/packages/eslint-plugin-material-ui/src/index.js b/packages/eslint-plugin-material-ui/src/index.js index ae6e389b24cc00..dc2aec3970eacf 100644 --- a/packages/eslint-plugin-material-ui/src/index.js +++ b/packages/eslint-plugin-material-ui/src/index.js @@ -8,4 +8,5 @@ module.exports.rules = { 'no-empty-box': require('./rules/no-empty-box'), 'no-styled-box': require('./rules/no-styled-box'), 'straight-quotes': require('./rules/straight-quotes'), + 'disallow-react-api-in-server-components': require('./rules/disallow-react-api-in-server-components'), }; diff --git a/packages/eslint-plugin-material-ui/src/rules/disallow-react-api-in-server-components.js b/packages/eslint-plugin-material-ui/src/rules/disallow-react-api-in-server-components.js new file mode 100644 index 00000000000000..02d894e764c133 --- /dev/null +++ b/packages/eslint-plugin-material-ui/src/rules/disallow-react-api-in-server-components.js @@ -0,0 +1,51 @@ +module.exports = { + create(context) { + let hasUseClientDirective = false; + const apis = new Set([ + 'useState', + 'useEffect', + 'useLayoutEffect', + 'useReducer', + 'useTransition', + 'createContext', + ]); + return { + Program(node) { + hasUseClientDirective = node.body.some( + (statement) => + statement.type === 'ExpressionStatement' && + statement.expression.type === 'Literal' && + statement.expression.value === 'use client', + ); + }, + CallExpression(node) { + if ( + !hasUseClientDirective && + node.callee.type === 'MemberExpression' && + node.callee.object.name === 'React' && + apis.has(node.callee.property.name) + ) { + context.report({ + node, + message: `Using 'React.${node.callee.property.name}' is forbidden if the file doesn't have a 'use client' directive.`, + fix(fixer) { + const sourceCode = context.getSourceCode(); + if ( + sourceCode.text.includes('"use server"') || + sourceCode.text.includes("'use server'") + ) { + return null; + } + + const firstToken = sourceCode.ast.body[0]; + return fixer.insertTextBefore(firstToken, "'use client';\n"); + }, + }); + } + }, + }; + }, + meta: { + fixable: 'code', + }, +}; diff --git a/packages/mui-lab/src/Timeline/TimelineContext.ts b/packages/mui-lab/src/Timeline/TimelineContext.ts index c7e7e2796ba7cd..760bc1218083b8 100644 --- a/packages/mui-lab/src/Timeline/TimelineContext.ts +++ b/packages/mui-lab/src/Timeline/TimelineContext.ts @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; /** diff --git a/packages/mui-material/src/ButtonGroup/ButtonGroupButtonContext.ts b/packages/mui-material/src/ButtonGroup/ButtonGroupButtonContext.ts index 8a93fe171954ef..a44e8dfb01cfdc 100644 --- a/packages/mui-material/src/ButtonGroup/ButtonGroupButtonContext.ts +++ b/packages/mui-material/src/ButtonGroup/ButtonGroupButtonContext.ts @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; type ButtonPositionClassName = string; diff --git a/packages/mui-material/src/ButtonGroup/ButtonGroupContext.ts b/packages/mui-material/src/ButtonGroup/ButtonGroupContext.ts index d336154db99963..66e9d9b3e41b09 100644 --- a/packages/mui-material/src/ButtonGroup/ButtonGroupContext.ts +++ b/packages/mui-material/src/ButtonGroup/ButtonGroupContext.ts @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; import type { ButtonGroupProps } from './ButtonGroup'; diff --git a/packages/mui-material/src/Dialog/DialogContext.ts b/packages/mui-material/src/Dialog/DialogContext.ts index d73b6aa7028425..fffd2cffd78ad3 100644 --- a/packages/mui-material/src/Dialog/DialogContext.ts +++ b/packages/mui-material/src/Dialog/DialogContext.ts @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; interface DialogContextValue { diff --git a/packages/mui-material/src/FormControl/FormControlContext.ts b/packages/mui-material/src/FormControl/FormControlContext.ts index 606057104b1457..15daa1d1b6dee9 100644 --- a/packages/mui-material/src/FormControl/FormControlContext.ts +++ b/packages/mui-material/src/FormControl/FormControlContext.ts @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; import type { FormControlProps } from './FormControl'; diff --git a/packages/mui-material/src/Hidden/withWidth.js b/packages/mui-material/src/Hidden/withWidth.js index d0a534c476c905..56ea1b458a8dce 100644 --- a/packages/mui-material/src/Hidden/withWidth.js +++ b/packages/mui-material/src/Hidden/withWidth.js @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; import getDisplayName from '@mui/utils/getDisplayName'; diff --git a/packages/mui-material/src/RadioGroup/RadioGroupContext.ts b/packages/mui-material/src/RadioGroup/RadioGroupContext.ts index 0679181062d1d4..230ae5a4b5be72 100644 --- a/packages/mui-material/src/RadioGroup/RadioGroupContext.ts +++ b/packages/mui-material/src/RadioGroup/RadioGroupContext.ts @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; export interface RadioGroupContextValue { diff --git a/packages/mui-material/src/Step/StepContext.ts b/packages/mui-material/src/Step/StepContext.ts index 25acdf43afc430..a48e883b22fd62 100644 --- a/packages/mui-material/src/Step/StepContext.ts +++ b/packages/mui-material/src/Step/StepContext.ts @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; export interface StepContextType { diff --git a/packages/mui-material/src/Stepper/StepperContext.ts b/packages/mui-material/src/Stepper/StepperContext.ts index 41fadd10bd6b4a..1bdbb3608a9783 100644 --- a/packages/mui-material/src/Stepper/StepperContext.ts +++ b/packages/mui-material/src/Stepper/StepperContext.ts @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; export interface StepperContextType { diff --git a/packages/mui-material/src/Table/Tablelvl2Context.js b/packages/mui-material/src/Table/Tablelvl2Context.js index f864806b6e5c39..6b1471c8f156ee 100644 --- a/packages/mui-material/src/Table/Tablelvl2Context.js +++ b/packages/mui-material/src/Table/Tablelvl2Context.js @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; /** diff --git a/packages/mui-material/src/ToggleButtonGroup/ToggleButtonGroupButtonContext.ts b/packages/mui-material/src/ToggleButtonGroup/ToggleButtonGroupButtonContext.ts index df044c2b87ca7b..36d0f2c40abe1d 100644 --- a/packages/mui-material/src/ToggleButtonGroup/ToggleButtonGroupButtonContext.ts +++ b/packages/mui-material/src/ToggleButtonGroup/ToggleButtonGroupButtonContext.ts @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; type ToggleButtonPositionClassName = string; diff --git a/packages/mui-material/src/ToggleButtonGroup/ToggleButtonGroupContext.ts b/packages/mui-material/src/ToggleButtonGroup/ToggleButtonGroupContext.ts index 88c3153db2be8c..6140564c895f22 100644 --- a/packages/mui-material/src/ToggleButtonGroup/ToggleButtonGroupContext.ts +++ b/packages/mui-material/src/ToggleButtonGroup/ToggleButtonGroupContext.ts @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; import type { ToggleButtonGroupProps } from './ToggleButtonGroup'; diff --git a/packages/mui-private-theming/src/useTheme/ThemeContext.js b/packages/mui-private-theming/src/useTheme/ThemeContext.js index cbd9f0ed799a25..59f9db29f9ea6f 100644 --- a/packages/mui-private-theming/src/useTheme/ThemeContext.js +++ b/packages/mui-private-theming/src/useTheme/ThemeContext.js @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; const ThemeContext = React.createContext(null); diff --git a/packages/mui-styles/src/StylesProvider/StylesProvider.js b/packages/mui-styles/src/StylesProvider/StylesProvider.js index 32306d2e2b6841..98ddcfa8953f95 100644 --- a/packages/mui-styles/src/StylesProvider/StylesProvider.js +++ b/packages/mui-styles/src/StylesProvider/StylesProvider.js @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; import { exactProp } from '@mui/utils'; diff --git a/packages/mui-styles/src/makeStyles/makeStyles.js b/packages/mui-styles/src/makeStyles/makeStyles.js index d18086cb7e6f9a..ebd03f16e6ba17 100644 --- a/packages/mui-styles/src/makeStyles/makeStyles.js +++ b/packages/mui-styles/src/makeStyles/makeStyles.js @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; import { getDynamicStyles } from 'jss'; import mergeClasses from '../mergeClasses'; diff --git a/packages/mui-system/src/RtlProvider/index.js b/packages/mui-system/src/RtlProvider/index.js index 17c01ee6d6b564..34f9bc5c9d0cba 100644 --- a/packages/mui-system/src/RtlProvider/index.js +++ b/packages/mui-system/src/RtlProvider/index.js @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; diff --git a/packages/mui-system/src/cssVars/createCssVarsProvider.js b/packages/mui-system/src/cssVars/createCssVarsProvider.js index 2ed616f210866f..3946722bd4574d 100644 --- a/packages/mui-system/src/cssVars/createCssVarsProvider.js +++ b/packages/mui-system/src/cssVars/createCssVarsProvider.js @@ -1,3 +1,4 @@ +'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; import { GlobalStyles } from '@mui/styled-engine'; From 819e662a2ce210f8805c45057389217cb1c39a83 Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Tue, 21 Jan 2025 14:19:02 +0100 Subject: [PATCH 5/7] [release] v6.4.1 (#45077) --- CHANGELOG.md | 42 +++++++++++++++++++ package.json | 2 +- packages/mui-base/package.json | 2 +- packages/mui-codemod/package.json | 2 +- .../mui-core-downloads-tracker/package.json | 2 +- packages/mui-docs/package.json | 2 +- packages/mui-icons-material/package.json | 2 +- packages/mui-lab/package.json | 2 +- .../mui-material-pigment-css/package.json | 2 +- packages/mui-material/package.json | 2 +- packages/mui-private-theming/package.json | 2 +- packages/mui-styles/package.json | 2 +- packages/mui-system/package.json | 2 +- packages/mui-utils/package.json | 2 +- pnpm-lock.yaml | 27 ++---------- 15 files changed, 59 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44e6ec48f8ee05..89a32d54584f00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,47 @@ # [Versions](https://mui.com/versions/) +## 6.4.1 + + + +_Jan 21, 2025_ + +A big thanks to the 9 contributors who made this release possible. + +### `@mui/material@6.4.1` + +- [ButtonBase] Export types used in ButtonBase props (#43530) @Janpot +- [Dialog] Add slots and slotProps (#44792) @sai6855 +- [Drawer] Deprecate composed classes (#44870) @yash49 +- [IconButton] Set default loading to `null` (#45057) @siriwatknp +- [ListItem] Add codemod for deprecated props (#45022) @sai6855 +- [Modal] Add migration guide and codemod for deprecated props (#45021) @sai6855 +- [TextField] Fix filled state to be synced with autofill (#44135) @DiegoAndai + +### `@mui/system@6.4.1` + +- Fix dark mode flicker using `useEnhancedEffect` (#44812) @siriwatknp + +### `@mui/utils@6.4.1` + +- Do not deep merge React component (#45058) @siriwatknp + +### Docs + +- Fix typo (#45070) @Fullchee +- Improve Toolpad templates section (#44914) @bharatkashyap +- Fix expand / collapse icons orientation (#44989) @zanivan +- Rename "Base UI" to "MUI Base" in all text (#45060) @mj12albert +- Add @mui/base deprecation callout (#45030) @mj12albert +- Update @mui/base deprecation message (#45064) @mj12albert + +### Core + +- [code-infra] Add "use client" directive to files with React APIs (#45036) @Janpot +- [docs] 301 redirect `/base-ui` to `base-ui.com` (#45061) @mj12albert + +All contributors of this release in alphabetical order: @bharatkashyap, @DiegoAndai, @Fullchee, @Janpot, @mj12albert, @sai6855, @siriwatknp, @yash49, @zanivan + ## 6.4.0 diff --git a/package.json b/package.json index 0819eb296f24f8..2404cb680c7eb4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mui/monorepo", - "version": "6.4.0", + "version": "6.4.1", "private": true, "scripts": { "preinstall": "npx only-allow pnpm", diff --git a/packages/mui-base/package.json b/packages/mui-base/package.json index da1bf81f2e5c63..e7aba7a2234037 100644 --- a/packages/mui-base/package.json +++ b/packages/mui-base/package.json @@ -1,6 +1,6 @@ { "name": "@mui/base", - "version": "5.0.0-beta.68", + "version": "5.0.0-beta.69", "private": false, "author": "MUI Team", "description": "MUI Base is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.", diff --git a/packages/mui-codemod/package.json b/packages/mui-codemod/package.json index e591ee65733706..cd86b6d57c6946 100644 --- a/packages/mui-codemod/package.json +++ b/packages/mui-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@mui/codemod", - "version": "6.4.0", + "version": "6.4.1", "bin": "./codemod.js", "private": false, "author": "MUI Team", diff --git a/packages/mui-core-downloads-tracker/package.json b/packages/mui-core-downloads-tracker/package.json index 9fbf875b712788..0f6dfc13b447f5 100644 --- a/packages/mui-core-downloads-tracker/package.json +++ b/packages/mui-core-downloads-tracker/package.json @@ -1,6 +1,6 @@ { "name": "@mui/core-downloads-tracker", - "version": "6.4.0", + "version": "6.4.1", "private": false, "author": "MUI Team", "description": "Internal package to track number of downloads of our design system libraries", diff --git a/packages/mui-docs/package.json b/packages/mui-docs/package.json index 25cd52a34b3536..0e7d90cc939fca 100644 --- a/packages/mui-docs/package.json +++ b/packages/mui-docs/package.json @@ -1,6 +1,6 @@ { "name": "@mui/docs", - "version": "6.4.0", + "version": "6.4.1", "private": false, "author": "MUI Team", "description": "MUI Docs - Documentation building blocks.", diff --git a/packages/mui-icons-material/package.json b/packages/mui-icons-material/package.json index a8d5c08ed6c217..b1be77921a246c 100644 --- a/packages/mui-icons-material/package.json +++ b/packages/mui-icons-material/package.json @@ -1,6 +1,6 @@ { "name": "@mui/icons-material", - "version": "6.4.0", + "version": "6.4.1", "private": false, "author": "MUI Team", "description": "Material Design icons distributed as SVG React components.", diff --git a/packages/mui-lab/package.json b/packages/mui-lab/package.json index 102ef8ec11c12e..d01b58e9922800 100644 --- a/packages/mui-lab/package.json +++ b/packages/mui-lab/package.json @@ -1,6 +1,6 @@ { "name": "@mui/lab", - "version": "6.0.0-beta.23", + "version": "6.0.0-beta.24", "private": false, "author": "MUI Team", "description": "Laboratory for new MUI modules.", diff --git a/packages/mui-material-pigment-css/package.json b/packages/mui-material-pigment-css/package.json index e339b6cdde038c..26ee59cc2587fe 100644 --- a/packages/mui-material-pigment-css/package.json +++ b/packages/mui-material-pigment-css/package.json @@ -1,6 +1,6 @@ { "name": "@mui/material-pigment-css", - "version": "6.4.0", + "version": "6.4.1", "author": "MUI Team", "description": "A wrapper over Pigment CSS that provides the same styled and theming APIs as Material UI.", "main": "./src/index.ts", diff --git a/packages/mui-material/package.json b/packages/mui-material/package.json index d6c54b0caebe74..2ff9a7bdbfda13 100644 --- a/packages/mui-material/package.json +++ b/packages/mui-material/package.json @@ -1,6 +1,6 @@ { "name": "@mui/material", - "version": "6.4.0", + "version": "6.4.1", "private": false, "author": "MUI Team", "description": "Material UI is an open-source React component library that implements Google's Material Design. It's comprehensive and can be used in production out of the box.", diff --git a/packages/mui-private-theming/package.json b/packages/mui-private-theming/package.json index 478b9ec0465369..3224f9a59906fb 100644 --- a/packages/mui-private-theming/package.json +++ b/packages/mui-private-theming/package.json @@ -1,6 +1,6 @@ { "name": "@mui/private-theming", - "version": "6.4.0", + "version": "6.4.1", "private": false, "author": "MUI Team", "description": "Private - The React theme context to be shared between `@mui/styles` and `@mui/material`.", diff --git a/packages/mui-styles/package.json b/packages/mui-styles/package.json index b76889cba5dea4..9a85c0b9eb8a27 100644 --- a/packages/mui-styles/package.json +++ b/packages/mui-styles/package.json @@ -1,6 +1,6 @@ { "name": "@mui/styles", - "version": "6.4.0", + "version": "6.4.1", "private": false, "author": "MUI Team", "description": "MUI Styles - The legacy JSS-based styling solution of Material UI.", diff --git a/packages/mui-system/package.json b/packages/mui-system/package.json index 9317b6490106ac..c6f02e6864386c 100644 --- a/packages/mui-system/package.json +++ b/packages/mui-system/package.json @@ -1,6 +1,6 @@ { "name": "@mui/system", - "version": "6.4.0", + "version": "6.4.1", "private": false, "author": "MUI Team", "description": "MUI System is a set of CSS utilities to help you build custom designs more efficiently. It makes it possible to rapidly lay out custom designs.", diff --git a/packages/mui-utils/package.json b/packages/mui-utils/package.json index f891518cbf847d..39805684f49793 100644 --- a/packages/mui-utils/package.json +++ b/packages/mui-utils/package.json @@ -1,6 +1,6 @@ { "name": "@mui/utils", - "version": "6.4.0", + "version": "6.4.1", "private": false, "author": "MUI Team", "description": "Utility functions for React components.", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea2fa53ba5813c..a79418d219d0e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -661,7 +661,7 @@ importers: version: 9.7.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@toolpad/core': specifier: ^0.12.0 - version: 0.12.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/icons-material@packages+mui-icons-material+build)(@mui/material-pigment-css@6.4.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@pigment-css/react@0.0.29(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/material@packages+mui-material+build)(@types/react@19.0.6)(next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.8.0)(@playwright/test@1.48.2)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react-router@7.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(vite@5.4.11(@types/node@20.17.12)(terser@5.37.0)) + version: 0.12.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/icons-material@packages+mui-icons-material+build)(@mui/material@packages+mui-material+build)(@types/react@19.0.6)(next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.8.0)(@playwright/test@1.48.2)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react-router@7.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(vite@5.4.11(@types/node@20.17.12)(terser@5.37.0)) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.49) @@ -4010,12 +4010,6 @@ packages: '@types/react': optional: true - '@mui/material-pigment-css@6.4.0': - resolution: {integrity: sha512-N+lXgmvuNCW3YrxdiyQwjyfkmwhJ4SxNcdZZ6dII3bzoFL7T1PngsxncExYp7y6+KJ+yTqHO9JvxFM8JPZe1uQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@pigment-css/react': 0.0.29 - '@mui/material@5.15.4': resolution: {integrity: sha512-T/LGRAC+M0c+D3+y67eHwIN5bSje0TxbcJCWR0esNvU11T0QwrX3jedXItPNBwMupF2F5VWCDHBVLlFnN3+ABA==} engines: {node: '>=12.0.0'} @@ -15084,7 +15078,7 @@ snapshots: '@emotion/styled': 11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0) '@types/react': 19.0.6 - '@mui/lab@6.0.0-beta.22(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/material-pigment-css@6.4.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@pigment-css/react@0.0.29(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/material@packages+mui-material+build)(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@mui/lab@6.0.0-beta.22(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/material@packages+mui-material+build)(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@babel/runtime': 7.26.0 '@mui/base': 5.0.0-beta.68(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -15099,21 +15093,8 @@ snapshots: optionalDependencies: '@emotion/react': 11.13.5(@types/react@19.0.6)(react@19.0.0) '@emotion/styled': 11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0) - '@mui/material-pigment-css': 6.4.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@pigment-css/react@0.0.29(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0) '@types/react': 19.0.6 - '@mui/material-pigment-css@6.4.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@pigment-css/react@0.0.29(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0)': - dependencies: - '@babel/runtime': 7.26.0 - '@mui/system': 6.4.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0) - '@pigment-css/react': 0.0.29(@types/react@19.0.6)(react@19.0.0) - transitivePeerDependencies: - - '@emotion/react' - - '@emotion/styled' - - '@types/react' - - react - optional: true - '@mui/material@5.15.4(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@babel/runtime': 7.26.0 @@ -16766,11 +16747,11 @@ snapshots: '@theme-ui/css': 0.17.1(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0)) react: 19.0.0 - '@toolpad/core@0.12.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/icons-material@packages+mui-icons-material+build)(@mui/material-pigment-css@6.4.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@pigment-css/react@0.0.29(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/material@packages+mui-material+build)(@types/react@19.0.6)(next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.8.0)(@playwright/test@1.48.2)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react-router@7.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(vite@5.4.11(@types/node@20.17.12)(terser@5.37.0))': + '@toolpad/core@0.12.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/icons-material@packages+mui-icons-material+build)(@mui/material@packages+mui-material+build)(@types/react@19.0.6)(next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.8.0)(@playwright/test@1.48.2)(babel-plugin-macros@3.1.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react-router@7.1.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(vite@5.4.11(@types/node@20.17.12)(terser@5.37.0))': dependencies: '@babel/runtime': 7.26.0 '@mui/icons-material': link:packages/mui-icons-material/build - '@mui/lab': 6.0.0-beta.22(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/material-pigment-css@6.4.0(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@pigment-css/react@0.0.29(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/material@packages+mui-material+build)(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@mui/lab': 6.0.0-beta.22(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@emotion/styled@11.13.5(@emotion/react@11.13.5(@types/react@19.0.6)(react@19.0.0))(@types/react@19.0.6)(react@19.0.0))(@mui/material@packages+mui-material+build)(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@mui/material': link:packages/mui-material/build '@mui/utils': 6.3.1(@types/react@19.0.6)(react@19.0.0) '@toolpad/utils': 0.12.0(react@19.0.0) From 8a4eaf6944e4960865ae6ab3b36ce917afe8c7e2 Mon Sep 17 00:00:00 2001 From: Zeeshan Tamboli Date: Tue, 21 Jan 2025 18:56:56 +0530 Subject: [PATCH 6/7] [Autocomplete] Prevent shrink animation in uncontrolled Autocomplete when default value is set (#44873) --- .../src/useAutocomplete/useAutocomplete.js | 26 ++++++++++++------- .../useAutocomplete/useAutocomplete.test.js | 20 ++++++++++++++ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/mui-material/src/useAutocomplete/useAutocomplete.js b/packages/mui-material/src/useAutocomplete/useAutocomplete.js index 35789e85e2917c..6f5b5ca0af62cc 100644 --- a/packages/mui-material/src/useAutocomplete/useAutocomplete.js +++ b/packages/mui-material/src/useAutocomplete/useAutocomplete.js @@ -61,6 +61,14 @@ const defaultIsActiveElementInListbox = (listboxRef) => const MULTIPLE_DEFAULT_VALUE = []; +function getInputValue(value, multiple, getOptionLabel) { + if (multiple || value == null) { + return ''; + } + const optionLabel = getOptionLabel(value); + return typeof optionLabel === 'string' ? optionLabel : ''; +} + function useAutocomplete(props) { const { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -137,6 +145,12 @@ function useAutocomplete(props) { const defaultHighlighted = autoHighlight ? 0 : -1; const highlightedIndexRef = React.useRef(defaultHighlighted); + // Calculate the initial inputValue on mount only. + // Using useRef since defaultValue doesn't need to update inputValue dynamically. + const initialInputValue = React.useRef( + getInputValue(defaultValue, multiple, getOptionLabel), + ).current; + const [value, setValueState] = useControlled({ controlled: valueProp, default: defaultValue, @@ -144,7 +158,7 @@ function useAutocomplete(props) { }); const [inputValue, setInputValueState] = useControlled({ controlled: inputValueProp, - default: '', + default: initialInputValue, name: componentName, state: 'inputValue', }); @@ -159,15 +173,7 @@ function useAutocomplete(props) { if (!isOptionSelected && !clearOnBlur) { return; } - let newInputValue; - if (multiple) { - newInputValue = ''; - } else if (newValue == null) { - newInputValue = ''; - } else { - const optionLabel = getOptionLabel(newValue); - newInputValue = typeof optionLabel === 'string' ? optionLabel : ''; - } + const newInputValue = getInputValue(newValue, multiple, getOptionLabel); if (inputValue === newInputValue) { return; diff --git a/packages/mui-material/src/useAutocomplete/useAutocomplete.test.js b/packages/mui-material/src/useAutocomplete/useAutocomplete.test.js index 01dd8dd4b7880a..0c39bfe0724317 100644 --- a/packages/mui-material/src/useAutocomplete/useAutocomplete.test.js +++ b/packages/mui-material/src/useAutocomplete/useAutocomplete.test.js @@ -395,4 +395,24 @@ describe('useAutocomplete', () => { fireEvent.click(button); }).not.to.throw(); }); + + describe('prop: defaultValue', () => { + it('should not trigger onInputChange when defaultValue is provided', () => { + const onInputChange = spy(); + const defaultValue = 'foo'; + + function Test() { + const { getInputProps } = useAutocomplete({ + defaultValue, + onInputChange, + options: ['foo', 'bar'], + }); + + return ; + } + + render(); + expect(onInputChange.callCount).to.equal(0); + }); + }); }); From cce1222039315b1119cb73d83dec999d25d5b04f Mon Sep 17 00:00:00 2001 From: Albert Yu Date: Tue, 21 Jan 2025 23:43:14 +0800 Subject: [PATCH 7/7] [material-ui][TextareaAutosize] Temporarily disconnect ResizeObserver to avoid loop error (#44540) --- .../src/TextareaAutosize/TextareaAutosize.tsx | 103 ++++++++++-------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/packages/mui-material/src/TextareaAutosize/TextareaAutosize.tsx b/packages/mui-material/src/TextareaAutosize/TextareaAutosize.tsx index 6b420f887cf19c..75a4144678024d 100644 --- a/packages/mui-material/src/TextareaAutosize/TextareaAutosize.tsx +++ b/packages/mui-material/src/TextareaAutosize/TextareaAutosize.tsx @@ -36,13 +36,16 @@ type TextareaStyles = { overflowing: boolean; }; +function isObjectEmpty(object: TextareaStyles) { + // eslint-disable-next-line + for (const _ in object) { + return false; + } + return true; +} + function isEmpty(obj: TextareaStyles) { - return ( - obj === undefined || - obj === null || - Object.keys(obj).length === 0 || - (obj.outerHeightStyle === 0 && !obj.overflowing) - ); + return isObjectEmpty(obj) || (obj.outerHeightStyle === 0 && !obj.overflowing); } /** @@ -62,16 +65,21 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize( const { onChange, maxRows, minRows = 1, style, value, ...other } = props; const { current: isControlled } = React.useRef(value != null); - const inputRef = React.useRef(null); - const handleRef = useForkRef(forwardedRef, inputRef); + const textareaRef = React.useRef(null); + const handleRef = useForkRef(forwardedRef, textareaRef); const heightRef = React.useRef(null); - const shadowRef = React.useRef(null); + const hiddenTextareaRef = React.useRef(null); const calculateTextareaStyles = React.useCallback(() => { - const input = inputRef.current!; + const textarea = textareaRef.current; + const hiddenTextarea = hiddenTextareaRef.current; - const containerWindow = ownerWindow(input); - const computedStyle = containerWindow.getComputedStyle(input); + if (!textarea || !hiddenTextarea) { + return undefined; + } + + const containerWindow = ownerWindow(textarea); + const computedStyle = containerWindow.getComputedStyle(textarea); // If input's width is shrunk and it's not visible, don't sync height. if (computedStyle.width === '0px') { @@ -81,15 +89,13 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize( }; } - const inputShallow = shadowRef.current!; - - inputShallow.style.width = computedStyle.width; - inputShallow.value = input.value || props.placeholder || 'x'; - if (inputShallow.value.slice(-1) === '\n') { + hiddenTextarea.style.width = computedStyle.width; + hiddenTextarea.value = textarea.value || props.placeholder || 'x'; + if (hiddenTextarea.value.slice(-1) === '\n') { // Certain fonts which overflow the line height will cause the textarea // to report a different scrollHeight depending on whether the last line // is empty. Make it non-empty to avoid this issue. - inputShallow.value += ' '; + hiddenTextarea.value += ' '; } const boxSizing = computedStyle.boxSizing; @@ -99,11 +105,11 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize( getStyleValue(computedStyle.borderBottomWidth) + getStyleValue(computedStyle.borderTopWidth); // The height of the inner content - const innerHeight = inputShallow.scrollHeight; + const innerHeight = hiddenTextarea.scrollHeight; // Measure height of a textarea with a single row - inputShallow.value = 'x'; - const singleRowHeight = inputShallow.scrollHeight; + hiddenTextarea.value = 'x'; + const singleRowHeight = hiddenTextarea.scrollHeight; // The height of the outer content let outerHeight = innerHeight; @@ -124,54 +130,55 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize( }, [maxRows, minRows, props.placeholder]); const syncHeight = React.useCallback(() => { + const textarea = textareaRef.current; const textareaStyles = calculateTextareaStyles(); - if (isEmpty(textareaStyles)) { + if (!textarea || !textareaStyles || isEmpty(textareaStyles)) { return; } const outerHeightStyle = textareaStyles.outerHeightStyle; - const input = inputRef.current!; if (heightRef.current !== outerHeightStyle) { heightRef.current = outerHeightStyle; - input.style.height = `${outerHeightStyle}px`; + textarea.style.height = `${outerHeightStyle}px`; } - input.style.overflow = textareaStyles.overflowing ? 'hidden' : ''; + textarea.style.overflow = textareaStyles.overflowing ? 'hidden' : ''; }, [calculateTextareaStyles]); + const frameRef = React.useRef(-1); + useEnhancedEffect(() => { - const handleResize = () => { - syncHeight(); - }; - // Workaround a "ResizeObserver loop completed with undelivered notifications" error - // in test. - // Note that we might need to use this logic in production per https://github.com/WICG/resize-observer/issues/38 - // Also see https://github.com/mui/mui-x/issues/8733 - let rAF: any; - const rAFHandleResize = () => { - cancelAnimationFrame(rAF); - rAF = requestAnimationFrame(() => { - handleResize(); - }); - }; - const debounceHandleResize = debounce(handleResize); - const input = inputRef.current!; - const containerWindow = ownerWindow(input); + const debounceHandleResize = debounce(() => syncHeight()); + const textarea = textareaRef?.current; + + if (!textarea) { + return undefined; + } + + const containerWindow = ownerWindow(textarea); containerWindow.addEventListener('resize', debounceHandleResize); let resizeObserver: ResizeObserver; if (typeof ResizeObserver !== 'undefined') { - resizeObserver = new ResizeObserver( - process.env.NODE_ENV === 'test' ? rAFHandleResize : handleResize, - ); - resizeObserver.observe(input); + resizeObserver = new ResizeObserver(() => { + // avoid "ResizeObserver loop completed with undelivered notifications" error + // by temporarily unobserving the textarea element while manipulating the height + // and reobserving one frame later + resizeObserver.unobserve(textarea); + cancelAnimationFrame(frameRef.current); + syncHeight(); + frameRef.current = requestAnimationFrame(() => { + resizeObserver.observe(textarea); + }); + }); + resizeObserver.observe(textarea); } return () => { debounceHandleResize.clear(); - cancelAnimationFrame(rAF); + cancelAnimationFrame(frameRef.current); containerWindow.removeEventListener('resize', debounceHandleResize); if (resizeObserver) { resizeObserver.disconnect(); @@ -208,7 +215,7 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize( aria-hidden className={props.className} readOnly - ref={shadowRef} + ref={hiddenTextareaRef} tabIndex={-1} style={{ ...styles.shadow,