diff --git a/components/textarea/__tests__/__snapshots__/index.test.tsx.snap b/components/textarea/__tests__/__snapshots__/index.test.tsx.snap
index 328bdc6ad..5c1716c7d 100644
--- a/components/textarea/__tests__/__snapshots__/index.test.tsx.snap
+++ b/components/textarea/__tests__/__snapshots__/index.test.tsx.snap
@@ -2,208 +2,208 @@
exports[`Textarea should render correctly 1`] = `
"
"
+ .wrapper {
+ display: inline-flex;
+ box-sizing: border-box;
+ user-select: none;
+ width: initial;
+ min-width: 12.5rem;
+ max-width: 95vw;
+ height: auto;
+ border-radius: 5px;
+ border: 1px solid #eaeaea;
+ color: #000;
+ transition: border 0.2s ease 0s, color 0.2s ease 0s;
+ }
+
+ .wrapper.hover {
+ border-color: #666;
+ }
+
+ .wrapper.disabled {
+ background-color: #fafafa;
+ border-color: #eaeaea;
+ cursor: not-allowed;
+ }
+
+ textarea {
+ background-color: transparent;
+ box-shadow: none;
+ display: block;
+ font-family: -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", sans-serif;
+ font-size: 0.875rem;
+ width: 100%;
+ height: 100%;
+ min-height: 6.25rem;
+ resize: none;
+ border: none;
+ outline: none;
+ padding: 8pt;
+ }
+
+ .disabled > textarea {
+ cursor: not-allowed;
+ }
+
+ textarea:-webkit-autofill,
+ textarea:-webkit-autofill:hover,
+ textarea:-webkit-autofill:active,
+ textarea:-webkit-autofill:focus {
+ -webkit-box-shadow: 0 0 0 30px #fff inset !important;
+ }
+ "
`;
exports[`Textarea should work with different styles 1`] = `
""
+ .wrapper {
+ display: inline-flex;
+ box-sizing: border-box;
+ user-select: none;
+ width: initial;
+ min-width: 12.5rem;
+ max-width: 95vw;
+ height: auto;
+ border-radius: 5px;
+ border: 1px solid #666;
+ color: #000;
+ transition: border 0.2s ease 0s, color 0.2s ease 0s;
+ }
+
+ .wrapper.hover {
+ border-color: #666;
+ }
+
+ .wrapper.disabled {
+ background-color: #fafafa;
+ border-color: #eaeaea;
+ cursor: not-allowed;
+ }
+
+ textarea {
+ background-color: transparent;
+ box-shadow: none;
+ display: block;
+ font-family: -apple-system, BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", sans-serif;
+ font-size: 0.875rem;
+ width: 100%;
+ height: 100%;
+ min-height: 6.25rem;
+ resize: none;
+ border: none;
+ outline: none;
+ padding: 8pt;
+ }
+
+ .disabled > textarea {
+ cursor: not-allowed;
+ }
+
+ textarea:-webkit-autofill,
+ textarea:-webkit-autofill:hover,
+ textarea:-webkit-autofill:active,
+ textarea:-webkit-autofill:focus {
+ -webkit-box-shadow: 0 0 0 30px #fff inset !important;
+ }
+ "
`;
diff --git a/components/textarea/textarea.tsx b/components/textarea/textarea.tsx
index 1cffa1c15..f5638303e 100644
--- a/components/textarea/textarea.tsx
+++ b/components/textarea/textarea.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo, useState } from 'react'
+import React, { useRef, useImperativeHandle, useEffect, useMemo, useState } from 'react'
import useTheme from '../styles/use-theme'
import withDefaults from '../utils/with-defaults'
import { NormalTypes } from '../utils/prop-types'
@@ -32,113 +32,130 @@ const defaultProps = {
type NativeAttrs = Omit, keyof Props>
export type TextareaProps = Props & typeof defaultProps & NativeAttrs
-const Textarea: React.FC> = ({
- width,
- status,
- minHeight,
- disabled,
- readOnly,
- onFocus,
- onBlur,
- className,
- initialValue,
- onChange,
- value,
- placeholder,
- ...props
-}) => {
- const theme = useTheme()
- const [selfValue, setSelfValue] = useState(initialValue)
- const [hover, setHover] = useState(false)
- const { color, borderColor, hoverBorder } = useMemo(() => getColors(theme.palette, status), [
- theme.palette,
- status,
- ])
+const Textarea = React.forwardRef>(
+ (
+ {
+ width,
+ status,
+ minHeight,
+ disabled,
+ readOnly,
+ onFocus,
+ onBlur,
+ className,
+ initialValue,
+ onChange,
+ value,
+ placeholder,
+ ...props
+ },
+ ref: React.Ref,
+ ) => {
+ const theme = useTheme()
+ const textareaRef = useRef(null)
+ useImperativeHandle(ref, () => textareaRef.current)
+ const isControlledComponent = useMemo(() => value !== undefined, [value])
+ const [selfValue, setSelfValue] = useState(initialValue)
+ const [hover, setHover] = useState(false)
+ const { color, borderColor, hoverBorder } = useMemo(() => getColors(theme.palette, status), [
+ theme.palette,
+ status,
+ ])
- const changeHandler = (event: React.ChangeEvent) => {
- if (disabled || readOnly) return
- setSelfValue(event.target.value)
- onChange && onChange(event)
- }
- const focusHandler = (e: React.FocusEvent) => {
- setHover(true)
- onFocus && onFocus(e)
- }
- const blurHandler = (e: React.FocusEvent) => {
- setHover(false)
- onBlur && onBlur(e)
- }
+ const changeHandler = (event: React.ChangeEvent) => {
+ if (disabled || readOnly) return
+ setSelfValue(event.target.value)
+ onChange && onChange(event)
+ }
+ const focusHandler = (e: React.FocusEvent) => {
+ setHover(true)
+ onFocus && onFocus(e)
+ }
+ const blurHandler = (e: React.FocusEvent) => {
+ setHover(false)
+ onBlur && onBlur(e)
+ }
- useEffect(() => {
- if (value === undefined) return
- setSelfValue(value)
- }, [value])
+ useEffect(() => {
+ if (isControlledComponent) {
+ setSelfValue(value as string)
+ }
+ })
- return (
-
-
-
-
- )
-}
+ .disabled > textarea {
+ cursor: not-allowed;
+ }
+
+ textarea:-webkit-autofill,
+ textarea:-webkit-autofill:hover,
+ textarea:-webkit-autofill:active,
+ textarea:-webkit-autofill:focus {
+ -webkit-box-shadow: 0 0 0 30px ${theme.palette.background} inset !important;
+ }
+ `}
+
+ )
+ },
+)
export default withDefaults(Textarea, defaultProps)
diff --git a/pages/en-us/components/textarea.mdx b/pages/en-us/components/textarea.mdx
index f9363bba6..981f4f73b 100644
--- a/pages/en-us/components/textarea.mdx
+++ b/pages/en-us/components/textarea.mdx
@@ -99,6 +99,28 @@ Retrieve multi-line user input.
`}
/>
+ {
+ const ref = React.useRef(null)
+ const setChange = () => {
+ ref && (ref.current.value = Math.random().toString(32))
+ }
+ return (
+ <>
+