Skip to content

Commit

Permalink
Add Textarea.error prop
Browse files Browse the repository at this point in the history
This mirrors the changes to the `Input` component in
5923074.
  • Loading branch information
robertknight committed Apr 29, 2024
1 parent cec7a00 commit 41f27ed
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 8 deletions.
22 changes: 15 additions & 7 deletions src/components/input/Textarea.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import type { JSX } from 'preact';

import type { PresentationalProps } from '../../types';
import { useSyncedRef } from '../../hooks/use-synced-ref';
import { useValidationError } from '../../hooks/use-validation-error';
import type { FormControlProps, PresentationalProps } from '../../types';
import { downcastRef } from '../../util/typing';
import { inputStyles } from './Input';

type ComponentProps = {
feedback?: 'error' | 'warning';
};

export type TextareaProps = PresentationalProps &
ComponentProps &
FormControlProps &
JSX.HTMLAttributes<HTMLTextAreaElement>;

/**
* Render a textarea
*/
export default function Textarea({
elementRef,
error,
feedback,
classes,

Expand All @@ -28,11 +27,20 @@ export default function Textarea({
);
}

const textAreaRef = downcastRef<HTMLElement | undefined, HTMLTextAreaElement>(
elementRef,
);
const ref = useSyncedRef<HTMLTextAreaElement>(textAreaRef);
if (error) {
feedback = 'error';
}
useValidationError(ref, error);

return (
<textarea
data-component="Textarea"
{...htmlAttributes}
ref={downcastRef(elementRef)}
ref={ref}
className={inputStyles({ classes, feedback })}
aria-invalid={feedback === 'error'}
/>
Expand Down
20 changes: 20 additions & 0 deletions src/components/input/test/Textarea-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,26 @@ describe('Textarea', () => {
console.warn.restore();
});

[
{
error: undefined,
validationMessage: '',
invalid: false,
},
{
error: 'Not a valid URL',
validationMessage: 'Not a valid URL',
invalid: true,
},
].forEach(({ error, validationMessage, invalid }) => {
it('should set custom validation error if `error` prop is provided', () => {
const wrapper = mount(<Textarea aria-label="Test" error={error} />);
const textarea = wrapper.find('textarea');
assert.equal(textarea.getDOMNode().validationMessage, validationMessage);
assert.equal(textarea.prop('aria-invalid'), invalid);
});
});

[
{ feedback: undefined, expectedInvalid: false },
{ feedback: 'error', expectedInvalid: true },
Expand Down
39 changes: 38 additions & 1 deletion src/pattern-library/components/patterns/input/TextareaPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function TextareaPage() {
</Library.Pattern>

<Library.Pattern title="Working with Textareas">
<Library.Example title="Accessibility">
<Library.Example title="Labels">
<p>
Hypothesis does not currently have a design pattern for labeling
textareas. However, for accessibility, it is critical that a{' '}
Expand Down Expand Up @@ -64,6 +64,26 @@ export default function TextareaPage() {
</div>
</Library.Demo>
</Library.Example>
<Library.Example title="Validation errors">
<p>
Validation errors can be triggered as a result of standard HTML
attributes such as <code>required</code> or a custom error set
using the <code>error</code> prop. Errors set using{' '}
<code>error</code> are synced to the browser via{' '}
<code>HTMLTextAreaElement.setCustomValidity</code>. This allows
the browser to alert the user when they try to submit the
containing form.
</p>
<p>
If the text area has custom validation checks, they should be
performed in the element&apos;s <code>onChange</code> handler.
</p>
<p>
If the form is using custom UI to present validation errors,
inputs must link to the element displaying their validation error
using <code>aria-describedby</code>.
</p>
</Library.Example>
</Library.Pattern>

<Library.Pattern title="Component API">
Expand All @@ -72,6 +92,23 @@ export default function TextareaPage() {
presentational component props
</Library.Link>
.
<Library.Example title="error">
<Library.Info>
<Library.InfoItem label="description">
Set <code>error</code> to indicate a validation error. This
implicitly sets{' '}
<code>
feedback={'"'}error{'"'}
</code>
. In addition to visually and semantically indicating the error
state, this will set a custom validation error on the textarea
using <code>HTMLTextAreaElement.setCustomValidity</code>.
</Library.InfoItem>
<Library.InfoItem label="type">
<code>string</code>
</Library.InfoItem>
</Library.Info>
</Library.Example>
<Library.Example title="feedback">
<Library.Info>
<Library.InfoItem label="description">
Expand Down

0 comments on commit 41f27ed

Please sign in to comment.