diff --git a/packages/react/src/components/TextArea/TextArea.stories.js b/packages/react/src/components/TextArea/TextArea.stories.js index 14e595505a09..530e6fd7255a 100644 --- a/packages/react/src/components/TextArea/TextArea.stories.js +++ b/packages/react/src/components/TextArea/TextArea.stories.js @@ -139,6 +139,11 @@ Playground.argTypes = { }, defaultValue: 'This is a warning message.', }, + value: { + control: { + type: 'text', + }, + }, }; Playground.args = { diff --git a/packages/react/src/components/TextArea/TextArea.tsx b/packages/react/src/components/TextArea/TextArea.tsx index 334ac7f65e2f..9cdbdaf46a76 100644 --- a/packages/react/src/components/TextArea/TextArea.tsx +++ b/packages/react/src/components/TextArea/TextArea.tsx @@ -6,7 +6,7 @@ */ import PropTypes, { ReactNodeLike } from 'prop-types'; -import React, { useState, useContext, useRef } from 'react'; +import React, { useState, useContext, useRef, useEffect } from 'react'; import classNames from 'classnames'; import deprecate from '../../prop-types/deprecate'; import { WarningFilled, WarningAltFilled } from '@carbon/icons-react'; @@ -156,10 +156,16 @@ const TextArea = React.forwardRef((props: TextAreaProps, forwardRef) => { const { isFluid } = useContext(FormContext); const { defaultValue, value, disabled } = other; const [textCount, setTextCount] = useState( - defaultValue?.toString().length || value?.toString().length || 0 + defaultValue?.toString()?.length || value?.toString()?.length || 0 ); const { current: textAreaInstanceId } = useRef(getInstanceId()); + useEffect(() => { + setTextCount( + defaultValue?.toString()?.length || value?.toString()?.length || 0 + ); + }, [value, defaultValue]); + const textareaProps: { id: TextAreaProps['id']; onChange: TextAreaProps['onChange']; @@ -169,7 +175,10 @@ const TextArea = React.forwardRef((props: TextAreaProps, forwardRef) => { id, onChange: (evt) => { if (!other.disabled && onChange) { - setTextCount(evt.target.value?.length); + // delay textCount assignation to give the textarea element value time to catch up if is a controlled input + setTimeout(() => { + setTextCount(evt.target.value?.length); + }, 0); onChange(evt); } }, diff --git a/packages/react/src/components/TextArea/__tests__/TextArea-test.js b/packages/react/src/components/TextArea/__tests__/TextArea-test.js index 0ab1d2f38148..2060df91aee9 100644 --- a/packages/react/src/components/TextArea/__tests__/TextArea-test.js +++ b/packages/react/src/components/TextArea/__tests__/TextArea-test.js @@ -10,30 +10,301 @@ import TextArea from '../TextArea'; import userEvent from '@testing-library/user-event'; import { render, screen } from '@testing-library/react'; +const prefix = 'cds'; + describe('TextArea', () => { - describe('behaves as expected - Component API', () => { - it('should respect readOnly prop', async () => { - const onChange = jest.fn(); - const onClick = jest.fn(); + describe('renders as expected - Component API', () => { + it('should spread extra props onto the text area element', () => { + render( +