diff --git a/docs/pages/base/api/textarea-autosize.json b/docs/pages/base/api/textarea-autosize.json
index f775a92e61519e..afcb9836ac3a17 100644
--- a/docs/pages/base/api/textarea-autosize.json
+++ b/docs/pages/base/api/textarea-autosize.json
@@ -10,7 +10,7 @@
"styles": { "classes": [], "globalClasses": {}, "name": null },
"spread": false,
"forwardsRefTo": "HTMLTextAreaElement",
- "filename": "/packages/mui-base/src/TextareaAutosize/TextareaAutosize.js",
+ "filename": "/packages/mui-base/src/TextareaAutosize/TextareaAutosize.tsx",
"inheritance": null,
"demos": "
",
"cssComponent": false
diff --git a/packages/mui-base/src/TextareaAutosize/TextareaAutosize.test.js b/packages/mui-base/src/TextareaAutosize/TextareaAutosize.test.tsx
similarity index 77%
rename from packages/mui-base/src/TextareaAutosize/TextareaAutosize.test.js
rename to packages/mui-base/src/TextareaAutosize/TextareaAutosize.test.tsx
index cd0756671df658..53715300970af5 100644
--- a/packages/mui-base/src/TextareaAutosize/TextareaAutosize.test.js
+++ b/packages/mui-base/src/TextareaAutosize/TextareaAutosize.test.tsx
@@ -13,7 +13,7 @@ import TextareaAutosize from '@mui/base/TextareaAutosize';
describe('', () => {
const { clock, render } = createRenderer();
- const mount = createMount;
+ const mount = createMount();
describeConformanceUnstyled(, () => ({
render,
@@ -28,21 +28,28 @@ describe('', () => {
'ownerStatePropagation',
'propsSpread',
'refForwarding',
- 'rootClass',
'slotsProp',
],
}));
describe('layout', () => {
- const getComputedStyleStub = {};
+ const getComputedStyleStub = new Map>();
function setLayout(
- input,
- shadow,
- { getComputedStyle, scrollHeight, lineHeight: lineHeightArg },
+ input: HTMLTextAreaElement,
+ shadow: Element,
+ {
+ getComputedStyle,
+ scrollHeight,
+ lineHeight: lineHeightArg,
+ }: {
+ getComputedStyle: Partial;
+ scrollHeight?: number;
+ lineHeight?: number | (() => number);
+ },
) {
const lineHeight = typeof lineHeightArg === 'function' ? lineHeightArg : () => lineHeightArg;
- getComputedStyleStub[input] = getComputedStyle;
+ getComputedStyleStub.set(input, getComputedStyle);
let index = 0;
stub(shadow, 'scrollHeight').get(() => {
@@ -57,7 +64,9 @@ describe('', () => {
this.skip();
}
- stub(window, 'getComputedStyle').value((node) => getComputedStyleStub[node] || {});
+ stub(window, 'getComputedStyle').value(
+ (node: Element) => getComputedStyleStub.get(node) || {},
+ );
});
after(() => {
@@ -69,14 +78,15 @@ describe('', () => {
it('should handle the resize event', () => {
const { container } = render();
- const input = container.querySelector('textarea[aria-hidden=null]');
- const shadow = container.querySelector('textarea[aria-hidden=true]');
- expect(input.style).to.have.property('height', '');
- expect(input.style).to.have.property('overflow', '');
+ const input = container.querySelector('textarea[aria-hidden=null]')!;
+ const shadow = container.querySelector('textarea[aria-hidden=true]')!;
+
+ expect(input.style).to.have.property('height', '0px');
+ expect(input.style).to.have.property('overflow', 'hidden');
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'content-box',
+ boxSizing: 'content-box',
},
scrollHeight: 30,
lineHeight: 15,
@@ -93,13 +103,13 @@ describe('', () => {
it('should update when uncontrolled', () => {
const handleChange = spy();
const { container } = render();
- const input = container.querySelector('textarea[aria-hidden=null]');
- const shadow = container.querySelector('textarea[aria-hidden=true]');
+ const input = container.querySelector('textarea[aria-hidden=null]')!;
+ const shadow = container.querySelector('textarea[aria-hidden=true]')!;
expect(input.style).to.have.property('height', '0px');
expect(input.style).to.have.property('overflow', 'hidden');
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'content-box',
+ boxSizing: 'content-box',
},
scrollHeight: 30,
lineHeight: 15,
@@ -107,7 +117,8 @@ describe('', () => {
act(() => {
input.focus();
});
- fireEvent.change(document.activeElement, { target: { value: 'a' } });
+ const activeElement = document.activeElement!;
+ fireEvent.change(activeElement, { target: { value: 'a' } });
expect(input.style).to.have.property('height', '30px');
expect(input.style).to.have.property('overflow', 'hidden');
expect(handleChange.callCount).to.equal(1);
@@ -116,14 +127,14 @@ describe('', () => {
it('should take the border into account with border-box', () => {
const border = 5;
const { container, forceUpdate } = render();
- const input = container.querySelector('textarea[aria-hidden=null]');
- const shadow = container.querySelector('textarea[aria-hidden=true]');
+ const input = container.querySelector('textarea[aria-hidden=null]')!;
+ const shadow = container.querySelector('textarea[aria-hidden=true]')!;
expect(input.style).to.have.property('height', '0px');
expect(input.style).to.have.property('overflow', 'hidden');
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'border-box',
- 'border-bottom-width': `${border}px`,
+ boxSizing: 'border-box',
+ borderBottomWidth: `${border}px`,
},
scrollHeight: 30,
lineHeight: 15,
@@ -136,12 +147,12 @@ describe('', () => {
it('should take the padding into account with content-box', () => {
const padding = 5;
const { container, forceUpdate } = render();
- const input = container.querySelector('textarea[aria-hidden=null]');
- const shadow = container.querySelector('textarea[aria-hidden=true]');
+ const input = container.querySelector('textarea[aria-hidden=null]')!;
+ const shadow = container.querySelector('textarea[aria-hidden=true]')!;
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'border-box',
- 'padding-top': `${padding}px`,
+ boxSizing: 'border-box',
+ paddingTop: `${padding}px`,
},
scrollHeight: 30,
lineHeight: 15,
@@ -155,11 +166,11 @@ describe('', () => {
const minRows = 3;
const lineHeight = 15;
const { container, forceUpdate } = render();
- const input = container.querySelector('textarea[aria-hidden=null]');
- const shadow = container.querySelector('textarea[aria-hidden=true]');
+ const input = container.querySelector('textarea[aria-hidden=null]')!;
+ const shadow = container.querySelector('textarea[aria-hidden=true]')!;
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'content-box',
+ boxSizing: 'content-box',
},
scrollHeight: 30,
lineHeight,
@@ -173,11 +184,11 @@ describe('', () => {
const maxRows = 3;
const lineHeight = 15;
const { container, forceUpdate } = render();
- const input = container.querySelector('textarea[aria-hidden=null]');
- const shadow = container.querySelector('textarea[aria-hidden=true]');
+ const input = container.querySelector('textarea[aria-hidden=null]')!;
+ const shadow = container.querySelector('textarea[aria-hidden=true]')!;
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'content-box',
+ boxSizing: 'content-box',
},
scrollHeight: 100,
lineHeight,
@@ -191,11 +202,11 @@ describe('', () => {
const maxRows = 3;
const lineHeight = 15;
const { container, forceUpdate } = render();
- const input = container.querySelector('textarea[aria-hidden=null]');
- const shadow = container.querySelector('textarea[aria-hidden=true]');
+ const input = container.querySelector('textarea[aria-hidden=null]')!;
+ const shadow = container.querySelector('textarea[aria-hidden=true]')!;
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'border-box',
+ boxSizing: 'border-box',
},
scrollHeight: lineHeight * 2,
lineHeight,
@@ -205,7 +216,7 @@ describe('', () => {
expect(input.style).to.have.property('overflow', 'hidden');
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'border-box',
+ boxSizing: 'border-box',
},
scrollHeight: lineHeight * 3,
lineHeight,
@@ -215,7 +226,7 @@ describe('', () => {
expect(input.style).to.have.property('overflow', 'hidden');
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'border-box',
+ boxSizing: 'border-box',
},
scrollHeight: lineHeight * 4,
lineHeight,
@@ -228,11 +239,11 @@ describe('', () => {
it('should update its height when the "maxRows" prop changes', () => {
const lineHeight = 15;
const { container, forceUpdate, setProps } = render();
- const input = container.querySelector('textarea[aria-hidden=null]');
- const shadow = container.querySelector('textarea[aria-hidden=true]');
+ const input = container.querySelector('textarea[aria-hidden=null]')!;
+ const shadow = container.querySelector('textarea[aria-hidden=true]')!;
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'content-box',
+ boxSizing: 'content-box',
},
scrollHeight: 100,
lineHeight,
@@ -248,12 +259,12 @@ describe('', () => {
it('should not sync height if container width is 0px', () => {
const lineHeight = 15;
const { container, forceUpdate } = render();
- const input = container.querySelector('textarea[aria-hidden=null]');
- const shadow = container.querySelector('textarea[aria-hidden=true]');
+ const input = container.querySelector('textarea[aria-hidden=null]')!;
+ const shadow = container.querySelector('textarea[aria-hidden=true]')!;
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'content-box',
+ boxSizing: 'content-box',
},
scrollHeight: lineHeight * 2,
lineHeight,
@@ -265,7 +276,7 @@ describe('', () => {
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'content-box',
+ boxSizing: 'content-box',
width: '0px',
},
scrollHeight: lineHeight * 3,
@@ -280,12 +291,12 @@ describe('', () => {
describe('warnings', () => {
it('warns if layout is unstable but not crash', () => {
const { container, forceUpdate } = render();
- const input = container.querySelector('textarea[aria-hidden=null]');
- const shadow = container.querySelector('textarea[aria-hidden=true]');
+ const input = container.querySelector('textarea[aria-hidden=null]')!;
+ const shadow = container.querySelector('textarea[aria-hidden=true]')!;
let index = 0;
setLayout(input, shadow, {
getComputedStyle: {
- 'box-sizing': 'content-box',
+ boxSizing: 'content-box',
},
scrollHeight: 100,
lineHeight: () => {
diff --git a/packages/mui-base/src/TextareaAutosize/TextareaAutosize.js b/packages/mui-base/src/TextareaAutosize/TextareaAutosize.tsx
similarity index 79%
rename from packages/mui-base/src/TextareaAutosize/TextareaAutosize.js
rename to packages/mui-base/src/TextareaAutosize/TextareaAutosize.tsx
index e47acd62bcf0d5..7a1f0f63b3a07b 100644
--- a/packages/mui-base/src/TextareaAutosize/TextareaAutosize.js
+++ b/packages/mui-base/src/TextareaAutosize/TextareaAutosize.tsx
@@ -7,12 +7,20 @@ import {
unstable_useEnhancedEffect as useEnhancedEffect,
unstable_ownerWindow as ownerWindow,
} from '@mui/utils';
+import { TextareaAutosizeProps } from './TextareaAutosize.types';
-function getStyleValue(computedStyle, property) {
- return parseInt(computedStyle[property], 10) || 0;
+type State = {
+ outerHeightStyle: number;
+ overflow?: boolean | undefined;
+};
+
+function getStyleValue(value: string) {
+ return parseInt(value, 10) || 0;
}
-const styles = {
+const styles: {
+ shadow: React.CSSProperties;
+} = {
shadow: {
// Visibility needed to hide the extra text area on iPads
visibility: 'hidden',
@@ -28,31 +36,56 @@ const styles = {
},
};
-function isEmpty(obj) {
- return obj === undefined || obj === null || Object.keys(obj).length === 0;
+function isEmpty(obj: State) {
+ return (
+ obj === undefined ||
+ obj === null ||
+ Object.keys(obj).length === 0 ||
+ (obj.outerHeightStyle === 0 && !obj.overflow)
+ );
}
-const TextareaAutosize = React.forwardRef(function TextareaAutosize(props, ref) {
+/**
+ *
+ * Demos:
+ *
+ * - [Textarea Autosize](https://mui.com/base/react-textarea-autosize/)
+ * - [Textarea Autosize](https://mui.com/material-ui/react-textarea-autosize/)
+ *
+ * API:
+ *
+ * - [TextareaAutosize API](https://mui.com/base/api/textarea-autosize/)
+ */
+const TextareaAutosize = React.forwardRef(function TextareaAutosize(
+ props: TextareaAutosizeProps,
+ ref: React.ForwardedRef,
+) {
const { onChange, maxRows, minRows = 1, style, value, ...other } = props;
const { current: isControlled } = React.useRef(value != null);
- const inputRef = React.useRef(null);
+ const inputRef = React.useRef(null);
const handleRef = useForkRef(ref, inputRef);
- const shadowRef = React.useRef(null);
+ const shadowRef = React.useRef(null);
const renders = React.useRef(0);
- const [state, setState] = React.useState({});
+ const [state, setState] = React.useState({
+ outerHeightStyle: 0,
+ });
const getUpdatedState = React.useCallback(() => {
- const input = inputRef.current;
+ const input = inputRef.current!;
+
const containerWindow = ownerWindow(input);
const computedStyle = containerWindow.getComputedStyle(input);
// If input's width is shrunk and it's not visible, don't sync height.
if (computedStyle.width === '0px') {
- return {};
+ return {
+ outerHeightStyle: 0,
+ };
}
- const inputShallow = shadowRef.current;
+ const inputShallow = shadowRef.current!;
+
inputShallow.style.width = computedStyle.width;
inputShallow.value = input.value || props.placeholder || 'x';
if (inputShallow.value.slice(-1) === '\n') {
@@ -62,12 +95,11 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize(props, ref)
inputShallow.value += ' ';
}
- const boxSizing = computedStyle['box-sizing'];
+ const boxSizing = computedStyle.boxSizing;
const padding =
- getStyleValue(computedStyle, 'padding-bottom') + getStyleValue(computedStyle, 'padding-top');
+ getStyleValue(computedStyle.paddingBottom) + getStyleValue(computedStyle.paddingTop);
const border =
- getStyleValue(computedStyle, 'border-bottom-width') +
- getStyleValue(computedStyle, 'border-top-width');
+ getStyleValue(computedStyle.borderBottomWidth) + getStyleValue(computedStyle.borderTopWidth);
// The height of the inner content
const innerHeight = inputShallow.scrollHeight;
@@ -94,7 +126,7 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize(props, ref)
return { outerHeightStyle, overflow };
}, [maxRows, minRows, props.placeholder]);
- const updateState = (prevState, newState) => {
+ const updateState = (prevState: State, newState: State) => {
const { outerHeightStyle, overflow } = newState;
// Need a large enough difference to update the height.
// This prevents infinite rendering loop.
@@ -164,13 +196,16 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize(props, ref)
syncHeightWithFlushSycn();
}
});
- const containerWindow = ownerWindow(inputRef.current);
+ let resizeObserver: ResizeObserver;
+
+ const input = inputRef.current!;
+ const containerWindow = ownerWindow(input);
+
containerWindow.addEventListener('resize', handleResize);
- let resizeObserver;
if (typeof ResizeObserver !== 'undefined') {
resizeObserver = new ResizeObserver(handleResize);
- resizeObserver.observe(inputRef.current);
+ resizeObserver.observe(input);
}
return () => {
@@ -190,7 +225,7 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize(props, ref)
renders.current = 0;
}, [value]);
- const handleChange = (event) => {
+ const handleChange = (event: React.ChangeEvent) => {
renders.current = 0;
if (!isControlled) {
@@ -209,12 +244,12 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize(props, ref)
onChange={handleChange}
ref={handleRef}
// Apply the rows prop to get a "correct" first SSR paint
- rows={minRows}
+ rows={minRows as number}
style={{
height: state.outerHeightStyle,
// Need a large enough difference to allow scrolling.
// This prevents infinite rendering loop.
- overflow: state.overflow ? 'hidden' : null,
+ overflow: state.overflow ? 'hidden' : undefined,
...style,
}}
{...other}
@@ -238,7 +273,7 @@ const TextareaAutosize = React.forwardRef(function TextareaAutosize(props, ref)
TextareaAutosize.propTypes /* remove-proptypes */ = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
- // | To update them edit the d.ts file and run "yarn proptypes" |
+ // | To update them edit TypeScript types and run "yarn proptypes" |
// ----------------------------------------------------------------------
/**
* @ignore
@@ -273,6 +308,6 @@ TextareaAutosize.propTypes /* remove-proptypes */ = {
PropTypes.number,
PropTypes.string,
]),
-};
+} as any;
export default TextareaAutosize;
diff --git a/packages/mui-base/src/TextareaAutosize/TextareaAutosize.d.ts b/packages/mui-base/src/TextareaAutosize/TextareaAutosize.types.ts
similarity index 52%
rename from packages/mui-base/src/TextareaAutosize/TextareaAutosize.d.ts
rename to packages/mui-base/src/TextareaAutosize/TextareaAutosize.types.ts
index 70f2f048af38a2..db83a932802cb3 100644
--- a/packages/mui-base/src/TextareaAutosize/TextareaAutosize.d.ts
+++ b/packages/mui-base/src/TextareaAutosize/TextareaAutosize.types.ts
@@ -13,16 +13,3 @@ export interface TextareaAutosizeProps
*/
minRows?: string | number;
}
-
-/**
- *
- * Demos:
- *
- * - [Textarea Autosize](https://mui.com/base/react-textarea-autosize/)
- * - [Textarea Autosize](https://mui.com/material-ui/react-textarea-autosize/)
- *
- * API:
- *
- * - [TextareaAutosize API](https://mui.com/base/api/textarea-autosize/)
- */
-export default function TextareaAutosize(props: TextareaAutosizeProps): JSX.Element;
diff --git a/packages/mui-base/src/TextareaAutosize/index.d.ts b/packages/mui-base/src/TextareaAutosize/index.d.ts
deleted file mode 100644
index 8da10b5232403f..00000000000000
--- a/packages/mui-base/src/TextareaAutosize/index.d.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export { default } from './TextareaAutosize';
-export * from './TextareaAutosize';
diff --git a/packages/mui-base/src/TextareaAutosize/index.js b/packages/mui-base/src/TextareaAutosize/index.ts
similarity index 52%
rename from packages/mui-base/src/TextareaAutosize/index.js
rename to packages/mui-base/src/TextareaAutosize/index.ts
index 05c119fc457ddb..4561e403520716 100644
--- a/packages/mui-base/src/TextareaAutosize/index.js
+++ b/packages/mui-base/src/TextareaAutosize/index.ts
@@ -1 +1,2 @@
export { default } from './TextareaAutosize';
+export * from './TextareaAutosize.types';