diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index 9be473ee2e1a..dae5ea1476a3 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -9176,6 +9176,85 @@ Map { }, }, }, + "unstable__FluidTextArea" => Object { + "propTypes": Object { + "className": Object { + "type": "string", + }, + "cols": Object { + "type": "number", + }, + "defaultValue": Object { + "args": Array [ + Array [ + Object { + "type": "string", + }, + Object { + "type": "number", + }, + ], + ], + "type": "oneOfType", + }, + "disabled": Object { + "type": "bool", + }, + "enableCounter": Object { + "type": "bool", + }, + "helperText": Object { + "type": "node", + }, + "hideLabel": Object { + "type": "bool", + }, + "id": Object { + "type": "string", + }, + "invalid": Object { + "type": "bool", + }, + "invalidText": Object { + "type": "node", + }, + "labelText": Object { + "isRequired": true, + "type": "node", + }, + "light": Object { + "type": "bool", + }, + "maxCount": Object { + "type": "number", + }, + "onChange": Object { + "type": "func", + }, + "onClick": Object { + "type": "func", + }, + "placeholder": Object { + "type": "string", + }, + "rows": Object { + "type": "number", + }, + "value": Object { + "args": Array [ + Array [ + Object { + "type": "string", + }, + Object { + "type": "number", + }, + ], + ], + "type": "oneOfType", + }, + }, + }, "unstable__FluidTextInput" => Object { "propTypes": Object { "className": Object { diff --git a/packages/react/src/__tests__/index-test.js b/packages/react/src/__tests__/index-test.js index 62077eab9c9a..e6c1a66316c5 100644 --- a/packages/react/src/__tests__/index-test.js +++ b/packages/react/src/__tests__/index-test.js @@ -229,6 +229,7 @@ describe('Carbon Components React', () => { "unstable_Pagination", "unstable_Text", "unstable_TextDirection", + "unstable__FluidTextArea", "unstable__FluidTextInput", "unstable_useContextMenu", "unstable_useFeatureFlag", diff --git a/packages/react/src/components/FluidForm/FluidForm-story.js b/packages/react/src/components/FluidForm/FluidForm-story.js deleted file mode 100644 index 1edd27bc2cb3..000000000000 --- a/packages/react/src/components/FluidForm/FluidForm-story.js +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright IBM Corp. 2016, 2018 - * - * This source code is licensed under the Apache-2.0 license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import { action } from '@storybook/addon-actions'; - -import { withKnobs } from '@storybook/addon-knobs'; -import FluidForm from '../FluidForm'; -import TextInput from '../TextInput'; - -const additionalProps = { - className: 'some-class', - onSubmit: (e) => { - e.preventDefault(); - action('FormSubmitted')(e); - }, -}; - -const TextInputProps = { - className: 'some-class', - id: 'test2', - labelText: 'Text Input label', - placeholder: 'Placeholder text', -}; - -const InvalidPasswordProps = { - className: 'some-class', - id: 'test4', - labelText: 'Password', - invalid: true, - invalidText: - 'Your password must be at least 6 characters as well as contain at least one uppercase, one lowercase, and one number.', -}; - -export default { - title: 'Experimental/FluidForm', - component: FluidForm, - decorators: [withKnobs], -}; - -export const Default = () => ( - - - - - -); diff --git a/packages/react/src/components/FluidForm/FluidForm.stories.js b/packages/react/src/components/FluidForm/FluidForm.stories.js new file mode 100644 index 000000000000..14531e560186 --- /dev/null +++ b/packages/react/src/components/FluidForm/FluidForm.stories.js @@ -0,0 +1,84 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { action } from '@storybook/addon-actions'; + +import { withKnobs } from '@storybook/addon-knobs'; +import FluidForm from '.'; +import FluidTextInput from '../FluidTextInput'; +import FluidTextArea from '../FluidTextArea'; +import ModalWrapper from '../ModalWrapper'; + +const additionalProps = { + className: 'some-class', + onSubmit: (e) => { + e.preventDefault(); + action('FormSubmitted')(e); + }, +}; + +const TextInputProps = { + className: 'some-class', + id: 'test2', + labelText: 'Text Input label', + placeholder: 'Placeholder text', +}; + +const TextAreaProps = { + className: 'some-class', + id: 'test3', + labelText: 'Text Area label', + placeholder: 'Placeholder text', +}; + +const InvalidPasswordProps = { + className: 'some-class', + id: 'test4', + labelText: 'Password', + value: '0000', +}; + +export default { + title: 'Experimental/FluidForm', + component: FluidForm, + decorators: [withKnobs], +}; + +export const Default = () => ( + <> + + + + + + + + + {}} + size="md"> + + + + + + + > +); diff --git a/packages/react/src/components/FluidTextArea/FluidTextArea.Skeleton.js b/packages/react/src/components/FluidTextArea/FluidTextArea.Skeleton.js new file mode 100644 index 000000000000..fbe7186ae1fd --- /dev/null +++ b/packages/react/src/components/FluidTextArea/FluidTextArea.Skeleton.js @@ -0,0 +1,32 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import classnames from 'classnames'; +import { usePrefix } from '../../internal/usePrefix'; +import { FormContext } from '../FluidForm/FormContext'; + +function FluidTextAreaSkeleton({ className, ...other }) { + const prefix = usePrefix(); + + return ( + + + + + + + ); +} + +FluidTextAreaSkeleton.propTypes = { + /** + * Specify an optional className to be applied to the outer FluidForm wrapper + */ + className: PropTypes.string, +}; + +export default FluidTextAreaSkeleton; diff --git a/packages/react/src/components/FluidTextArea/FluidTextArea.js b/packages/react/src/components/FluidTextArea/FluidTextArea.js new file mode 100644 index 000000000000..88c066109360 --- /dev/null +++ b/packages/react/src/components/FluidTextArea/FluidTextArea.js @@ -0,0 +1,123 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import PropTypes from 'prop-types'; +import React from 'react'; +import classnames from 'classnames'; +import TextArea from '../TextArea'; +import { usePrefix } from '../../internal/usePrefix'; +import { FormContext } from '../FluidForm/FormContext'; + +function FluidTextArea({ className, ...other }) { + const prefix = usePrefix(); + const classNames = classnames(`${prefix}--text-area--fluid`, className); + + return ( + + + + ); +} + +FluidTextArea.propTypes = { + /** + * Provide a custom className that is applied directly to the underlying + * `` node + */ + className: PropTypes.string, + + /** + * Specify the `cols` attribute for the underlying `` node + */ + cols: PropTypes.number, + + /** + * Optionally provide the default value of the `` + */ + defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + + /** + * Specify whether the control is disabled + */ + disabled: PropTypes.bool, + + /** + * Specify whether to display the character counter + */ + enableCounter: PropTypes.bool, + + /** + * Provide text that is used alongside the control label for additional help + */ + helperText: PropTypes.node, + + /** + * Specify whether you want the underlying label to be visually hidden + */ + hideLabel: PropTypes.bool, + + /** + * Provide a unique identifier for the control + */ + id: PropTypes.string, + + /** + * Specify whether the control is currently invalid + */ + invalid: PropTypes.bool, + + /** + * Provide the text that is displayed when the control is in an invalid state + */ + invalidText: PropTypes.node, + + /** + * Provide the text that will be read by a screen reader when visiting this + * control + */ + labelText: PropTypes.node.isRequired, + + /** + * `true` to use the light version. For use on $ui-01 backgrounds only. + * Don't use this to make tile background color same as container background color. + */ + light: PropTypes.bool, + + /** + * Max character count allowed for the textarea. This is needed in order for enableCounter to display + */ + maxCount: PropTypes.number, + + /** + * Optionally provide an `onChange` handler that is called whenever `` + * is updated + */ + onChange: PropTypes.func, + + /** + * Optionally provide an `onClick` handler that is called whenever the + * `` is clicked + */ + onClick: PropTypes.func, + + /** + * Specify the placeholder attribute for the `` + */ + placeholder: PropTypes.string, + + /** + * Specify the rows attribute for the `` + */ + rows: PropTypes.number, + + /** + * Provide the current value of the `` + */ + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), +}; + +export default FluidTextArea; diff --git a/packages/react/src/components/FluidTextArea/FluidTextArea.stories.js b/packages/react/src/components/FluidTextArea/FluidTextArea.stories.js new file mode 100644 index 000000000000..89da14d91fbd --- /dev/null +++ b/packages/react/src/components/FluidTextArea/FluidTextArea.stories.js @@ -0,0 +1,183 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import FluidTextArea from '../FluidTextArea'; +import FluidTextAreaSkeleton from './FluidTextArea.Skeleton'; +import { + ToggletipLabel, + Toggletip, + ToggletipButton, + ToggletipContent, +} from '../Toggletip'; +import { Layer } from '../Layer'; +import { Information } from '@carbon/icons-react'; + +export default { + title: 'Experimental/unstable__FluidTextArea', + component: FluidTextArea, + argTypes: { + hideLabel: { + table: { + disable: true, + }, + }, + helperText: { + table: { + disable: true, + }, + }, + id: { + table: { + disable: true, + }, + }, + light: { + table: { + disable: true, + }, + }, + value: { + table: { + disable: true, + }, + }, + defaultValue: { + table: { + disable: true, + }, + }, + }, +}; + +export const Default = () => ( + +); + +export const DefaultWithLayers = () => ( + <> + + + + + + + + + + > +); + +const ToggleTip = ( + <> + Text Area label + + + + + + Additional field information here. + + + > +); + +export const DefaultWithTooltip = () => ( + +); + +export const Skeleton = () => ( + + + +); + +export const Playground = (args) => ( + + + +); + +Playground.argTypes = { + playgroundWidth: { + control: { type: 'range', min: 300, max: 800, step: 50 }, + defaultValue: 300, + }, + className: { + control: { + type: 'text', + }, + defaultValue: 'test-class', + }, + placeholder: { + control: { + type: 'text', + }, + defaultValue: 'Placeholder text', + }, + invalid: { + control: { + type: 'boolean', + }, + defaultValue: false, + }, + invalidText: { + control: { + type: 'text', + }, + defaultValue: + 'Error message that is really long can wrap to more lines but should not be excessively long.', + }, + disabled: { + control: { + type: 'boolean', + }, + defaultValue: false, + }, + enableCounter: { + control: { + type: 'boolean', + }, + defaultValue: false, + }, + labelText: { + control: { + type: 'text', + }, + defaultValue: 'Text Area label', + }, + maxCount: { + control: { + type: 'text', + }, + defaultValue: '500', + }, +}; diff --git a/packages/react/src/components/FluidTextArea/__tests__/FluidTextArea-test.js b/packages/react/src/components/FluidTextArea/__tests__/FluidTextArea-test.js new file mode 100644 index 000000000000..63e49d1330a0 --- /dev/null +++ b/packages/react/src/components/FluidTextArea/__tests__/FluidTextArea-test.js @@ -0,0 +1,199 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import FluidTextArea from '../FluidTextArea'; +import userEvent from '@testing-library/user-event'; +import { render, screen } from '@testing-library/react'; +import { FeatureFlags } from '../../FeatureFlags'; + +const prefix = 'cds'; + +describe('FluidTextArea', () => { + describe('renders as expected - Component API', () => { + it('should spread extra props onto the input element', () => { + render( + + ); + + expect(screen.getByText('Value')).toHaveAttribute( + 'data-testid', + 'test-id' + ); + }); + + it('should support a custom `className` prop on the outermost element', () => { + const { container } = render( + + + + ); + + expect(container.firstChild).toHaveClass('custom-class'); + }); + + it('should respect defaultValue prop', () => { + render( + + ); + + expect(screen.getByText('This is default text')).toBeInTheDocument(); + }); + + it('should respect disabled prop', () => { + render( + + ); + + expect(screen.getByTestId('test-id-2')).toBeDisabled(); + }); + + it('should respect id prop', () => { + render( + + ); + + expect(screen.getByTestId('test-id-3')).toHaveAttribute('id', 'input-1'); + }); + + it('should respect invalid prop', () => { + const { container } = render( + + ); + + const invalidIcon = container.querySelector( + `svg.${prefix}--text-area__invalid-icon` + ); + + expect(screen.getByTestId('test-id-4')).toHaveAttribute('aria-invalid'); + expect(screen.getByTestId('test-id-4')).toHaveClass( + `${prefix}--text-area--invalid` + ); + expect(invalidIcon).toBeInTheDocument(); + }); + + it('should respect invalidText prop', () => { + render( + + ); + + expect(screen.getByText('This is invalid text')).toBeInTheDocument(); + expect(screen.getByText('This is invalid text')).toHaveClass( + `${prefix}--form-requirement` + ); + }); + + it('should respect labelText prop', () => { + render(); + + expect(screen.getByText('FluidTextArea label')).toBeInTheDocument(); + expect(screen.getByText('FluidTextArea label')).toHaveClass( + `${prefix}--label` + ); + }); + + it('should respect placeholder prop', () => { + render( + + ); + + expect( + screen.getByPlaceholderText('Placeholder text') + ).toBeInTheDocument(); + }); + }); + + describe('behaves as expected - Component API', () => { + it('should respect onChange prop', () => { + const onChange = jest.fn(); + render( + + ); + + userEvent.type(screen.getByTestId('test-id-6'), 'x'); + expect(screen.getByTestId('test-id-6')).toHaveValue('x'); + expect(onChange).toHaveBeenCalledTimes(1); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + target: expect.any(Object), + }) + ); + }); + + it('should respect onClick prop', () => { + const onClick = jest.fn(); + render( + + ); + + userEvent.click(screen.getByTestId('test-id-7')); + expect(onClick).toHaveBeenCalled(); + }); + + it('should not call `onClick` when the `` is clicked but disabled', () => { + const onClick = jest.fn(); + render( + + ); + + userEvent.click(screen.getByTestId('test-id-8')); + expect(onClick).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/react/src/components/FluidTextArea/index.js b/packages/react/src/components/FluidTextArea/index.js new file mode 100644 index 000000000000..08b713378247 --- /dev/null +++ b/packages/react/src/components/FluidTextArea/index.js @@ -0,0 +1,9 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +export default from './FluidTextArea'; +export FluidTextArea from './FluidTextArea'; diff --git a/packages/react/src/components/TextArea/TextArea.js b/packages/react/src/components/TextArea/TextArea.js index 23e9a346deb8..89bda7ca2bd4 100644 --- a/packages/react/src/components/TextArea/TextArea.js +++ b/packages/react/src/components/TextArea/TextArea.js @@ -6,11 +6,12 @@ */ import PropTypes from 'prop-types'; -import React, { useState } from 'react'; +import React, { useState, useContext } from 'react'; import classNames from 'classnames'; import { WarningFilled } from '@carbon/icons-react'; import { useFeatureFlag } from '../FeatureFlags'; import { usePrefix } from '../../internal/usePrefix'; +import { FormContext } from '../FluidForm'; const TextArea = React.forwardRef(function TextArea( { @@ -32,6 +33,7 @@ const TextArea = React.forwardRef(function TextArea( ref ) { const prefix = usePrefix(); + const { isFluid } = useContext(FormContext); const enabled = useFeatureFlag('enable-v11-release'); const { defaultValue, value, disabled } = other; const [textCount, setTextCount] = useState( @@ -59,7 +61,7 @@ const TextArea = React.forwardRef(function TextArea( } const labelClasses = classNames(`${prefix}--label`, { - [`${prefix}--visually-hidden`]: hideLabel, + [`${prefix}--visually-hidden`]: hideLabel && !isFluid, [`${prefix}--label--disabled`]: disabled, }); @@ -91,6 +93,9 @@ const TextArea = React.forwardRef(function TextArea( const error = invalid ? ( {invalidText} + {isFluid && ( + + )} ) : null; @@ -129,12 +134,14 @@ const TextArea = React.forwardRef(function TextArea( - {invalid && ( + {invalid && !isFluid && ( )} {input} + {isFluid && } + {isFluid && invalid ? error : null} - {invalid ? error : helper} + {invalid && !isFluid ? error : helper} ); }); diff --git a/packages/react/src/index.js b/packages/react/src/index.js index 9c0006811ab5..ef89a6d5eb13 100644 --- a/packages/react/src/index.js +++ b/packages/react/src/index.js @@ -214,6 +214,7 @@ export { useFeatureFlags as unstable_useFeatureFlags, } from './components/FeatureFlags'; export { FluidTextInput as unstable__FluidTextInput } from './components/FluidTextInput'; +export { FluidTextArea as unstable__FluidTextArea } from './components/FluidTextArea'; export { Heading, Section } from './components/Heading'; export { IconButton } from './components/IconButton'; export { Layer, useLayer } from './components/Layer'; diff --git a/packages/styles/scss/components/_index.scss b/packages/styles/scss/components/_index.scss index 715314a80f19..de787864d5a6 100644 --- a/packages/styles/scss/components/_index.scss +++ b/packages/styles/scss/components/_index.scss @@ -23,6 +23,7 @@ @use 'dropdown'; @use 'file-uploader'; @use 'fluid-text-input'; +@use 'fluid-text-area'; @use 'form'; @use 'inline-loading'; @use 'link'; diff --git a/packages/styles/scss/components/fluid-text-area/_fluid-text-area.scss b/packages/styles/scss/components/fluid-text-area/_fluid-text-area.scss new file mode 100644 index 000000000000..8f8942c094c6 --- /dev/null +++ b/packages/styles/scss/components/fluid-text-area/_fluid-text-area.scss @@ -0,0 +1,167 @@ +// +// Copyright IBM Corp. 2018, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +//----------------------------- +// Fluid Text Area +//----------------------------- +@use '../../config' as *; +@use '../../spacing' as *; +@use '../../theme' as *; +@use '../../utilities/convert' as *; +@use '../../utilities/focus-outline' as *; + +@mixin fluid-text-area { + .#{$prefix}--text-area--fluid .#{$prefix}--text-area__wrapper { + position: relative; + height: 100%; + flex-direction: column; + background: $field; + } + + .#{$prefix}--modal + .#{$prefix}--text-area--fluid + .#{$prefix}--text-area__wrapper { + background: $field-02; + } + + .#{$prefix}--text-area--fluid .#{$prefix}--text-area__label-wrapper { + position: relative; + height: 100%; + } + + .#{$prefix}--text-area--fluid .#{$prefix}--label { + position: absolute; + z-index: 1; + top: rem(13px); + left: $spacing-05; + display: flex; + height: rem(16px); + align-items: center; + margin: 0; + } + + //counter + .#{$prefix}--text-area--fluid div.#{$prefix}--label { + right: 1rem; + left: initial; + } + + .#{$prefix}--text-area--fluid .#{$prefix}--text-area { + min-height: rem(64px); + padding: 0 $spacing-05 rem(13px); + margin-top: 2rem; + outline: none; + } + + .#{$prefix}--text-area--fluid .#{$prefix}--form__helper-text { + display: none; + } + + // invalid + .#{$prefix}--text-area--fluid .#{$prefix}--text-area--invalid, + .#{$prefix}--text-area--fluid .#{$prefix}--text-area:focus { + border-bottom: none; + } + + .#{$prefix}--text-area__divider, + .#{$prefix}--text-area--fluid .#{$prefix}--text-area__divider { + display: none; + } + + .#{$prefix}--text-area--fluid + .#{$prefix}--text-area--invalid + + .#{$prefix}--text-area__divider { + display: block; + border-style: solid; + border-color: $border-subtle; + border-bottom: none; + margin: 0 1rem; + } + + // invalid error message container + .#{$prefix}--text-area--fluid + .#{$prefix}--text-area--invalid + + .#{$prefix}--text-area__divider + + .#{$prefix}--form-requirement { + position: relative; + display: block; + overflow: visible; + max-height: 12.5rem; + padding: 0.5rem 2.5rem 0.5rem 1rem; + margin: 0; + background: $field; + color: $text-error; + } + + .#{$prefix}--modal + .#{$prefix}--text-area--fluid + .#{$prefix}--text-area--invalid + + .#{$prefix}--text-area__divider + + .#{$prefix}--form-requirement { + background: $field-02; + } + + .#{$prefix}--text-area--fluid .#{$prefix}--text-area__invalid-icon { + top: 0.5rem; + } + + //invalid outline + .#{$prefix}--text-area--fluid + .#{$prefix}--text-area__wrapper[data-invalid]:not(:focus) { + @include focus-outline('invalid'); + + outline-offset: 0; + } + + // focus + .#{$prefix}--text-area--fluid + .#{$prefix}--text-area__wrapper[data-invalid]:focus-within, + .#{$prefix}--text-area--fluid .#{$prefix}--text-area__wrapper:focus-within { + @include focus-outline('outline'); + + outline-offset: 0; + } + + .#{$prefix}--text-area--fluid + .#{$prefix}--text-area__wrapper[data-invalid] + > .#{$prefix}--text-area--invalid:focus, + .#{$prefix}--text-area--fluid + .#{$prefix}--text-area__wrapper + > .#{$prefix}--text-area:focus, + .#{$prefix}--text-area--fluid + .#{$prefix}--text-area__wrapper + > .#{$prefix}--text-area:active, + .#{$prefix}--text-area--fluid + .#{$prefix}--text-area__wrapper[data-invalid] + > .#{$prefix}--text-area--invalid { + outline: none; + transition: none; + } + + // Skeleton + .#{$prefix}--text-area--fluid__skeleton { + padding: $spacing-05; + border-bottom: 1px solid $skeleton-element; + background: $skeleton-background; + } + + .#{$prefix}--text-area--fluid__skeleton .#{$prefix}--skeleton, + .#{$prefix}--text-area--fluid__skeleton + .#{$prefix}--text-area.#{$prefix}--skeleton::before { + height: 0.5rem; + } + + .#{$prefix}--text-area--fluid__skeleton .#{$prefix}--label { + margin-bottom: $spacing-04; + } + + .#{$prefix}--text-area--fluid__skeleton + .#{$prefix}--text-area.#{$prefix}--skeleton { + width: 80%; + height: 4rem; + } +} diff --git a/packages/styles/scss/components/fluid-text-area/_index.scss b/packages/styles/scss/components/fluid-text-area/_index.scss new file mode 100644 index 000000000000..0b2c473691d9 --- /dev/null +++ b/packages/styles/scss/components/fluid-text-area/_index.scss @@ -0,0 +1,11 @@ +// +// Copyright IBM Corp. 2018, 2018 +// +// This source code is licensed under the Apache-2.0 license found in the +// LICENSE file in the root directory of this source tree. +// + +@forward 'fluid-text-area'; +@use 'fluid-text-area'; + +@include fluid-text-area.fluid-text-area; diff --git a/packages/styles/scss/components/text-area/_text-area.scss b/packages/styles/scss/components/text-area/_text-area.scss index cdaf07e091dd..8e9c6c3d0351 100644 --- a/packages/styles/scss/components/text-area/_text-area.scss +++ b/packages/styles/scss/components/text-area/_text-area.scss @@ -81,6 +81,7 @@ color: $text-disabled; cursor: not-allowed; outline: none; + resize: none; } .#{$prefix}--text-area:disabled::placeholder {
Additional field information here.