diff --git a/packages/mui-material/src/TextareaAutosize/TextareaAutosize.test.tsx b/packages/mui-material/src/TextareaAutosize/TextareaAutosize.test.tsx
index 1647deca7afe1d..e7aae18afa703d 100644
--- a/packages/mui-material/src/TextareaAutosize/TextareaAutosize.test.tsx
+++ b/packages/mui-material/src/TextareaAutosize/TextareaAutosize.test.tsx
@@ -366,6 +366,89 @@ describe('', () => {
expect(input.style).to.have.property('overflow', '');
});
+ it('should show scrollbar when the content is taller than the CSS "max-height"', () => {
+ const lineHeight = 15;
+ const maxHeight = lineHeight * 3;
+ const { forceUpdate } = render();
+ const input = screen.getByRole('textbox', {
+ hidden: false,
+ });
+ const shadow = screen.getAllByRole('textbox', {
+ hidden: true,
+ })[1];
+ setLayout(input, shadow, {
+ getComputedStyle: {
+ boxSizing: 'content-box',
+ maxHeight: `${maxHeight}px`,
+ },
+ scrollHeight: lineHeight * 2,
+ lineHeight,
+ });
+ forceUpdate();
+ expect(input.style).to.have.property('height', `${lineHeight * 2}px`);
+ expect(input.style).to.have.property('overflow', 'hidden');
+
+ setLayout(input, shadow, {
+ getComputedStyle: {
+ boxSizing: 'content-box',
+ maxHeight: `${maxHeight}px`,
+ },
+ scrollHeight: lineHeight * 4,
+ lineHeight,
+ });
+ forceUpdate();
+ expect(input.style).to.have.property('overflow', '');
+ });
+
+ it('should take the box sizing into account when comparing with the CSS "max-height"', () => {
+ const border = 5;
+ const lineHeight = 15;
+ // The content on its own fits, the border pushes the border box past the limit.
+ const maxHeight = lineHeight * 2 + border - 1;
+ const { forceUpdate } = render();
+ const input = screen.getByRole('textbox', {
+ hidden: false,
+ });
+ const shadow = screen.getAllByRole('textbox', {
+ hidden: true,
+ })[1];
+ setLayout(input, shadow, {
+ getComputedStyle: {
+ boxSizing: 'border-box',
+ borderBottomWidth: `${border}px`,
+ maxHeight: `${maxHeight}px`,
+ },
+ scrollHeight: lineHeight * 2,
+ lineHeight,
+ });
+ forceUpdate();
+ expect(input.style).to.have.property('height', `${lineHeight * 2 + border}px`);
+ expect(input.style).to.have.property('overflow', '');
+ });
+
+ it('should keep hiding the overflow when the CSS "max-height" is not an absolute length', () => {
+ const lineHeight = 15;
+ const { forceUpdate } = render();
+ const input = screen.getByRole('textbox', {
+ hidden: false,
+ });
+ const shadow = screen.getAllByRole('textbox', {
+ hidden: true,
+ })[1];
+ setLayout(input, shadow, {
+ getComputedStyle: {
+ boxSizing: 'content-box',
+ // `getComputedStyle` does not resolve percentages for `max-height`.
+ maxHeight: '50%',
+ },
+ scrollHeight: lineHeight * 4,
+ lineHeight,
+ });
+ forceUpdate();
+ expect(input.style).to.have.property('height', `${lineHeight * 4}px`);
+ expect(input.style).to.have.property('overflow', 'hidden');
+ });
+
it('should update its height when the "maxRows" prop changes', () => {
const lineHeight = 15;
const { forceUpdate, setProps } = render();
diff --git a/packages/mui-material/src/TextareaAutosize/TextareaAutosize.tsx b/packages/mui-material/src/TextareaAutosize/TextareaAutosize.tsx
index b3d460a1d6eaef..59d809c79efbdb 100644
--- a/packages/mui-material/src/TextareaAutosize/TextareaAutosize.tsx
+++ b/packages/mui-material/src/TextareaAutosize/TextareaAutosize.tsx
@@ -12,6 +12,17 @@ function getStyleValue(value: string) {
return parseInt(value, 10) || 0;
}
+function getMaxHeight(value: string) {
+ // `getComputedStyle` resolves `max-height` to an absolute length, except for values that
+ // depend on the layout, for example percentages or `calc()` expressions mixing both.
+ // Those can't be compared with the computed height, so they are treated as unbounded.
+ if (!value || !value.endsWith('px')) {
+ return Infinity;
+ }
+
+ return getStyleValue(value);
+}
+
const styles: {
shadow: React.CSSProperties;
} = {
@@ -123,7 +134,10 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize(
// Take the box sizing into account for applying this value as a style.
const outerHeightStyle = outerHeight + (boxSizing === 'border-box' ? padding + border : 0);
- const overflowing = Math.abs(outerHeight - innerHeight) <= 1;
+ // A CSS `max-height` clips the textarea before it reaches `outerHeightStyle`, so the content
+ // has to stay scrollable, the same way it does once `maxRows` is reached.
+ const clippedByMaxHeight = outerHeightStyle > getMaxHeight(computedStyle.maxHeight);
+ const overflowing = Math.abs(outerHeight - innerHeight) <= 1 && !clippedByMaxHeight;
return { outerHeightStyle, overflowing };
}, [maxRows, minRows, props.placeholder]);