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 ( -
-