@indico-data/design-system 2.9.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/lib/index.css +119 -8
  2. package/lib/index.d.ts +52 -8
  3. package/lib/index.esm.css +119 -8
  4. package/lib/index.esm.js +35 -19
  5. package/lib/index.esm.js.map +1 -1
  6. package/lib/index.js +36 -18
  7. package/lib/index.js.map +1 -1
  8. package/lib/src/components/forms/checkbox/Checkbox.d.ts +2 -1
  9. package/lib/src/components/forms/form/Form.d.ts +14 -0
  10. package/lib/src/components/forms/form/Form.stories.d.ts +8 -0
  11. package/lib/src/components/forms/input/Input.d.ts +5 -4
  12. package/lib/src/components/forms/passwordInput/PasswordInput.d.ts +17 -0
  13. package/lib/src/components/forms/passwordInput/PasswordInput.stories.d.ts +11 -0
  14. package/lib/src/components/forms/passwordInput/__tests__/PasswordInput.test.d.ts +1 -0
  15. package/lib/src/components/forms/passwordInput/index.d.ts +1 -0
  16. package/lib/src/components/forms/radio/Radio.d.ts +2 -1
  17. package/lib/src/components/forms/subcomponents/DisplayFormError.d.ts +5 -0
  18. package/lib/src/components/forms/textarea/Textarea.d.ts +4 -3
  19. package/lib/src/components/forms/toggle/Toggle.d.ts +2 -1
  20. package/lib/src/components/index.d.ts +2 -0
  21. package/lib/src/index.d.ts +2 -0
  22. package/package.json +5 -2
  23. package/src/components/forms/checkbox/Checkbox.stories.tsx +2 -2
  24. package/src/components/forms/checkbox/Checkbox.tsx +32 -41
  25. package/src/components/forms/form/Form.mdx +134 -0
  26. package/src/components/forms/form/Form.stories.tsx +413 -0
  27. package/src/components/forms/form/Form.tsx +64 -0
  28. package/src/components/forms/form/__tests__/Form.test.tsx +35 -0
  29. package/src/components/forms/form/index.ts +0 -0
  30. package/src/components/forms/form/styles/Form.scss +3 -0
  31. package/src/components/forms/input/Input.stories.tsx +0 -5
  32. package/src/components/forms/input/Input.tsx +67 -64
  33. package/src/components/forms/input/__tests__/Input.test.tsx +2 -13
  34. package/src/components/forms/input/styles/Input.scss +2 -8
  35. package/src/components/forms/passwordInput/PasswordInput.mdx +28 -0
  36. package/src/components/forms/passwordInput/PasswordInput.stories.tsx +268 -0
  37. package/src/components/forms/passwordInput/PasswordInput.tsx +86 -0
  38. package/src/components/forms/passwordInput/__tests__/PasswordInput.test.tsx +129 -0
  39. package/src/components/forms/passwordInput/index.ts +1 -0
  40. package/src/components/forms/passwordInput/styles/PasswordInput.scss +120 -0
  41. package/src/components/forms/radio/Radio.tsx +32 -35
  42. package/src/components/forms/subcomponents/DisplayFormError.tsx +7 -0
  43. package/src/components/forms/textarea/Textarea.stories.tsx +15 -21
  44. package/src/components/forms/textarea/Textarea.tsx +64 -62
  45. package/src/components/forms/textarea/__tests__/Textarea.test.tsx +1 -1
  46. package/src/components/forms/textarea/styles/Textarea.scss +1 -1
  47. package/src/components/forms/toggle/Toggle.tsx +30 -37
  48. package/src/components/index.ts +2 -0
  49. package/src/index.ts +2 -0
  50. package/src/styles/index.scss +2 -0
  51. package/lib/src/components/forms/subcomponents/ErrorList.d.ts +0 -6
  52. package/src/components/forms/subcomponents/ErrorList.tsx +0 -14
  53. package/src/components/forms/subcomponents/__tests__/ErrorList.test.tsx +0 -16
  54. /package/lib/src/components/forms/{subcomponents/__tests__/ErrorList.test.d.ts → form/__tests__/Form.test.d.ts} +0 -0
@@ -0,0 +1,129 @@
1
+ import { render, screen, act } from '@testing-library/react';
2
+ import { PasswordInput } from '@/components/forms/passwordInput/PasswordInput';
3
+ import { ChangeEvent } from 'react';
4
+ import userEvent from '@testing-library/user-event';
5
+
6
+ const handleOnChange = jest.fn();
7
+ const handleShowPassword = jest.fn();
8
+
9
+ describe('Input', () => {
10
+ it('renders the input field', () => {
11
+ render(
12
+ <PasswordInput
13
+ isRequired
14
+ label="Enter your name"
15
+ helpText="In order to submit the form, this field is required."
16
+ name="name"
17
+ placeholder="Please enter a value"
18
+ value={''}
19
+ onChange={handleOnChange}
20
+ />,
21
+ );
22
+ expect(screen.getByText('Enter your name')).toBeInTheDocument();
23
+ });
24
+
25
+ it('clicking on the eye changes the password input to a text input and back and changes the icon from a show icon to hide icon and back', async () => {
26
+ render(
27
+ <PasswordInput
28
+ isRequired
29
+ label="Enter your name"
30
+ helpText="In order to submit the form, this field is required."
31
+ name="name"
32
+ placeholder="Please enter a value"
33
+ value={'test'}
34
+ onChange={handleOnChange}
35
+ />,
36
+ );
37
+ const input = screen.getByTestId('form-password-input-name');
38
+ const showPasswordIcon = screen.getByTestId('name-show-password-icon');
39
+ expect(input).toHaveValue('test');
40
+ await userEvent.click(showPasswordIcon);
41
+ expect(input).toHaveAttribute('type', 'text');
42
+ const hidePasswordIcon = screen.getByTestId('name-hide-password-icon');
43
+ await userEvent.click(hidePasswordIcon);
44
+ expect(input).toHaveAttribute('type', 'password');
45
+ });
46
+
47
+ it('adds the error class when errors exist', () => {
48
+ render(
49
+ <PasswordInput
50
+ isRequired
51
+ errorMessage="You require a username value."
52
+ label="Enter your name"
53
+ helpText="In order to submit the form, this field is required."
54
+ name="name"
55
+ placeholder="Please enter a value"
56
+ value={'test'}
57
+ onChange={handleOnChange}
58
+ />,
59
+ );
60
+ const input = screen.getByTestId('form-password-input-name');
61
+ expect(input).toHaveClass('error');
62
+ });
63
+
64
+ it('does not highlight the input when no errors exist', () => {
65
+ render(
66
+ <PasswordInput
67
+ isRequired
68
+ label="Enter your name"
69
+ helpText="In order to submit the form, this field is required."
70
+ name="name"
71
+ placeholder="Please enter a value"
72
+ value={'test'}
73
+ onChange={handleOnChange}
74
+ />,
75
+ );
76
+ const input = screen.getByTestId('form-password-input-name');
77
+ expect(input).not.toHaveClass('error');
78
+ });
79
+
80
+ it('renders help text when help text exists', () => {
81
+ render(
82
+ <PasswordInput
83
+ isRequired
84
+ label="Enter your name"
85
+ helpText="In order to submit the form, this field is required."
86
+ name="name"
87
+ placeholder="Please enter a value"
88
+ value={'test'}
89
+ onChange={handleOnChange}
90
+ />,
91
+ );
92
+ const helpText = screen.getByText('In order to submit the form, this field is required.');
93
+ expect(helpText).toBeInTheDocument();
94
+ expect(helpText).toBeVisible();
95
+ });
96
+
97
+ it('does not render help text when help text does not exist', () => {
98
+ render(
99
+ <PasswordInput
100
+ isRequired
101
+ label="Enter your name"
102
+ name="name"
103
+ placeholder="Please enter a value"
104
+ value={'test'}
105
+ onChange={handleOnChange}
106
+ />,
107
+ );
108
+ const helpText = screen.queryByTestId('name-help-text');
109
+ expect(helpText).not.toBeInTheDocument();
110
+ expect(helpText).toBeNull();
111
+ });
112
+
113
+ it('emits the value when user types', async () => {
114
+ const handleOnChange = jest.fn();
115
+ render(
116
+ <PasswordInput
117
+ isRequired
118
+ label="Enter your name"
119
+ name="name"
120
+ placeholder="Please enter a value"
121
+ value={''}
122
+ onChange={handleOnChange}
123
+ />,
124
+ );
125
+ const input = screen.getByTestId('form-password-input-name');
126
+ await userEvent.type(input, 't');
127
+ expect(handleOnChange).toHaveBeenCalled();
128
+ });
129
+ });
@@ -0,0 +1 @@
1
+ export { PasswordInput } from './PasswordInput';
@@ -0,0 +1,120 @@
1
+ // Common Variables
2
+ :root,
3
+ :root [data-theme='light'],
4
+ :root [data-theme='dark'] {
5
+ // Typography
6
+ --pf-password-input-background-color: var(--pf-white-color);
7
+ --pf-password-input-border-color: var(--pf-gray-color);
8
+ --pf-password-input-text-color: var(--pf-gray-color);
9
+ --pf-password-input-placeholder-text-color: var(--pf-gray-color-300);
10
+ --pf-password-input-help-text-color: var(--pf-gray-color-400);
11
+ --pf-password-input-disabled-background-color: var(--pf-gray-color-100);
12
+ --pf-password-input-border-color: var(--pf-gray-color);
13
+ --pf-password-input-disabled-color: var(--pf-gray-color-400);
14
+ --pf-password-input-border-color: var(--pf-gray-color);
15
+
16
+ // input Radius
17
+ --pf-password-input-rounded: var(--pf-rounded);
18
+ }
19
+
20
+ // Dark Theme Specific Variables
21
+ :root [data-theme='dark'] {
22
+ --pf-password-input-background-color: var(--pf-primary-color);
23
+ --pf-password-input-border-color: var(--pf-gray-color-100);
24
+ --pf-password-input-text-color: var(--pf-gray-color-100);
25
+ --pf-password-input-placeholder-text-color: var(--pf-gray-color);
26
+ --pf-password-input-help-text-color: var(--pf-gray-color-200);
27
+ --pf-password-input-disabled-background-color: var(--pf-primary-color-200);
28
+ --pf-password-input-disabled-border-color: var(--pf-gray-color-300);
29
+ --pf-password-input-disabled-color: var(--pf-gray-color-400);
30
+ }
31
+
32
+ .password-input {
33
+ background-color: var(--pf-password-input-background-color);
34
+ border: 1px solid var(--pf-password-input-border-color);
35
+ border-radius: var(--pf-password-input-rounded);
36
+ color: var(--pf-password-input-text-color);
37
+ padding: 10px;
38
+ width: 100%;
39
+ box-sizing: border-box;
40
+ height: 36px;
41
+ &::placeholder {
42
+ color: var(--pf-password-input-placeholder-text-color);
43
+ }
44
+
45
+ &:focus {
46
+ border-color: var(--pf-primary-color);
47
+ }
48
+
49
+ &.error {
50
+ border-color: var(--pf-error-color);
51
+ }
52
+
53
+ &.success {
54
+ border-color: var(--pf-success-color);
55
+ }
56
+
57
+ &.warning {
58
+ border-color: var(--pf-warning-color);
59
+ }
60
+
61
+ &.info {
62
+ border-color: var(--pf-info-color);
63
+ }
64
+
65
+ &:disabled {
66
+ background-color: var(--pf-password-input-disabled-background-color);
67
+ border-color: var(--pf-password-input-disabled-border-color);
68
+ color: var(--pf-password-input-disabled-color);
69
+ }
70
+ &--has-icon {
71
+ padding-left: var(--pf-padding-7);
72
+ }
73
+ }
74
+
75
+ .form-control {
76
+ .error-list {
77
+ list-style: none;
78
+ padding: 0;
79
+ margin: 0;
80
+ margin-top: var(--pf-margin-2);
81
+ margin-bottom: var(--pf-margin-2);
82
+ color: var(--pf-error-color);
83
+ }
84
+ .help-text {
85
+ margin-top: var(--pf-margin-2);
86
+ margin-bottom: var(--pf-margin-2);
87
+ color: var(--pf-password-input-help-text-color);
88
+ font-size: var(--pf-font-size-subtitle2);
89
+ }
90
+ .password-input-wrapper {
91
+ position: relative;
92
+ .embedded-icon {
93
+ position: absolute;
94
+ top: 10px;
95
+ left: var(--pf-margin-2);
96
+ color: var(--pf-password-input-text-color);
97
+ }
98
+ .toggle-show-password-icon {
99
+ position: absolute;
100
+ top: var(--pf-margin-3);
101
+ right: var(--pf-margin-2);
102
+ color: var(--pf-password-input-text-color);
103
+ cursor: pointer;
104
+ }
105
+ }
106
+ .is-visually-hidden {
107
+ position: absolute;
108
+ width: 1px;
109
+ height: 1px;
110
+ padding: 0;
111
+ margin: -1px;
112
+ overflow: hidden;
113
+ clip: rect(0, 0, 0, 0);
114
+ white-space: nowrap;
115
+ border: 0;
116
+ }
117
+ .form-label {
118
+ margin-bottom: var(--pf-margin-2);
119
+ }
120
+ }
@@ -8,40 +8,37 @@ export interface RadioProps {
8
8
  value?: string;
9
9
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
10
10
  isDisabled?: boolean;
11
+ defaultChecked?: boolean;
11
12
  }
12
-
13
- export const Radio = ({
14
- ref,
15
- id,
16
- label,
17
- name,
18
- value,
19
- onChange,
20
- isDisabled,
21
- ...rest
22
- }: RadioProps) => {
23
- return (
24
- <div className="form-control">
25
- <div className="radio-wrapper">
26
- <input
27
- data-testid={`form-radio-input-${name}`}
28
- {...rest}
29
- className="radio-input"
30
- type="radio"
31
- id={id}
32
- name={name}
33
- value={value}
34
- disabled={isDisabled}
35
- ref={ref}
36
- onChange={onChange}
37
- tabIndex={0}
38
- aria-describedby={id}
39
- aria-label={label}
40
- />
41
- <label htmlFor={id} className="radio-input-label" data-testid={`label-radio-input-${name}`}>
42
- {label}
43
- </label>
13
+ export const Radio = React.forwardRef<HTMLInputElement, RadioProps>(
14
+ ({ id, label, name, value, onChange, isDisabled, ...rest }, ref) => {
15
+ return (
16
+ <div className="form-control">
17
+ <div className="radio-wrapper">
18
+ <input
19
+ data-testid={`form-radio-input-${name}`}
20
+ className="radio-input"
21
+ type="radio"
22
+ id={id}
23
+ name={name}
24
+ value={value}
25
+ disabled={isDisabled}
26
+ ref={ref}
27
+ onChange={onChange}
28
+ tabIndex={0}
29
+ aria-describedby={id}
30
+ aria-label={label}
31
+ {...rest}
32
+ />
33
+ <label
34
+ htmlFor={id}
35
+ className="radio-input-label"
36
+ data-testid={`label-radio-input-${name}`}
37
+ >
38
+ {label}
39
+ </label>
40
+ </div>
44
41
  </div>
45
- </div>
46
- );
47
- };
42
+ );
43
+ },
44
+ );
@@ -0,0 +1,7 @@
1
+ interface DisplayFormErrorProps {
2
+ message: string | undefined;
3
+ }
4
+
5
+ export const DisplayFormError = ({ message }: DisplayFormErrorProps) => {
6
+ return <p className="form-errors">{message}</p>;
7
+ };
@@ -83,7 +83,7 @@ const meta: Meta = {
83
83
  },
84
84
  defaultValue: { summary: 'false' },
85
85
  },
86
- errorList: {
86
+ errorMessage: {
87
87
  control: false,
88
88
  description: 'An array of error messages',
89
89
  table: {
@@ -214,8 +214,7 @@ export const Default: Story = {
214
214
  placeholder: 'Please enter a value',
215
215
  hasHiddenLabel: false,
216
216
  isDisabled: false,
217
- errorList: [],
218
- value: '',
217
+ errorMessage: '',
219
218
  readonly: false,
220
219
  cols: 0,
221
220
  rows: 0,
@@ -228,10 +227,10 @@ export const Default: Story = {
228
227
  const [value, setValue] = useState(args.value);
229
228
 
230
229
  useEffect(() => {
231
- setValue(args.value);
230
+ setValue(args.value || '');
232
231
  }, [args.value]);
233
232
 
234
- const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
233
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
235
234
  setValue(e.target.value);
236
235
  };
237
236
 
@@ -247,17 +246,16 @@ export const Errors: Story = {
247
246
  placeholder: 'Please enter a value',
248
247
  hasHiddenLabel: false,
249
248
  isDisabled: false,
250
- errorList: ['This Is An Error', 'This Is Another Error'],
251
- value: '',
249
+ errorMessage: 'This Is An Error',
252
250
  },
253
251
  render: (args) => {
254
252
  const [value, setValue] = useState(args.value);
255
253
 
256
254
  useEffect(() => {
257
- setValue(args.value);
255
+ setValue(args.value || '');
258
256
  }, [args.value]);
259
257
 
260
- const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
258
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
261
259
  setValue(e.target.value);
262
260
  };
263
261
 
@@ -273,16 +271,15 @@ export const HiddenLabel: Story = {
273
271
  placeholder: 'Please enter a value',
274
272
  hasHiddenLabel: true,
275
273
  isDisabled: false,
276
- value: '',
277
274
  },
278
275
  render: (args) => {
279
276
  const [value, setValue] = useState(args.value);
280
277
 
281
278
  useEffect(() => {
282
- setValue(args.value);
279
+ setValue(args.value || '');
283
280
  }, [args.value]);
284
281
 
285
- const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
282
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
286
283
  setValue(e.target.value);
287
284
  };
288
285
 
@@ -297,16 +294,15 @@ export const HelpText: Story = {
297
294
  name: 'textarea',
298
295
  placeholder: 'Please enter a value',
299
296
  isDisabled: false,
300
- value: '',
301
297
  },
302
298
  render: (args) => {
303
299
  const [value, setValue] = useState(args.value);
304
300
 
305
301
  useEffect(() => {
306
- setValue(args.value);
302
+ setValue(args.value || '');
307
303
  }, [args.value]);
308
304
 
309
- const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
305
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
310
306
  setValue(e.target.value);
311
307
  };
312
308
 
@@ -321,16 +317,15 @@ export const Disabled: Story = {
321
317
  name: 'textarea',
322
318
  placeholder: 'Please enter a value',
323
319
  isDisabled: true,
324
- value: '',
325
320
  },
326
321
  render: (args) => {
327
322
  const [value, setValue] = useState(args.value);
328
323
 
329
324
  useEffect(() => {
330
- setValue(args.value);
325
+ setValue(args.value || '');
331
326
  }, [args.value]);
332
327
 
333
- const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
328
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
334
329
  setValue(e.target.value);
335
330
  };
336
331
 
@@ -344,17 +339,16 @@ export const Readonly: Story = {
344
339
  label: 'Label Name',
345
340
  name: 'textarea',
346
341
  placeholder: 'Please enter a value',
347
- value: '',
348
342
  readonly: true,
349
343
  },
350
344
  render: (args) => {
351
345
  const [value, setValue] = useState(args.value);
352
346
 
353
347
  useEffect(() => {
354
- setValue(args.value);
348
+ setValue(args.value || '');
355
349
  }, [args.value]);
356
350
 
357
- const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
351
+ const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
358
352
  setValue(e.target.value);
359
353
  };
360
354
 
@@ -1,17 +1,17 @@
1
1
  import React from 'react';
2
2
  import { Label } from '../subcomponents/Label';
3
- import { ErrorList } from '../subcomponents/ErrorList';
3
+ import { DisplayFormError } from '../subcomponents/DisplayFormError';
4
4
 
5
5
  export interface TextareaProps {
6
6
  ref?: React.LegacyRef<HTMLTextAreaElement>;
7
7
  label: string;
8
8
  name: string;
9
9
  placeholder: string;
10
- value: string;
10
+ value?: string | undefined;
11
11
  onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
12
12
  isRequired?: boolean;
13
13
  isDisabled?: boolean;
14
- errorList?: string[];
14
+ errorMessage?: string | undefined;
15
15
  helpText?: string;
16
16
  hasHiddenLabel?: boolean;
17
17
  rows?: number;
@@ -21,65 +21,67 @@ export interface TextareaProps {
21
21
  form?: string;
22
22
  maxLength?: number;
23
23
  autofocus?: boolean;
24
+ defaultValue?: string;
24
25
  }
26
+ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
27
+ (
28
+ {
29
+ label,
30
+ name,
31
+ placeholder,
32
+ value,
33
+ onChange,
34
+ isRequired,
35
+ isDisabled,
36
+ errorMessage,
37
+ helpText,
38
+ hasHiddenLabel,
39
+ rows,
40
+ cols,
41
+ readonly,
42
+ wrap,
43
+ form,
44
+ maxLength,
45
+ autofocus,
46
+ ...rest
47
+ },
48
+ ref,
49
+ ) => {
50
+ const hasErrors = errorMessage && errorMessage.length > 0;
25
51
 
26
- export const Textarea = ({
27
- ref,
28
- label,
29
- name,
30
- placeholder,
31
- value,
32
- onChange,
33
- isRequired,
34
- isDisabled,
35
- errorList,
36
- helpText,
37
- hasHiddenLabel,
38
- rows,
39
- cols,
40
- readonly,
41
- wrap,
42
- form,
43
- maxLength,
44
- autofocus,
45
- ...rest
46
- }: TextareaProps) => {
47
- const hasErrors = errorList && errorList.length > 0;
48
-
49
- return (
50
- <div className="form-control">
51
- <Label label={label} name={name} isRequired={isRequired} hasHiddenLabel={hasHiddenLabel} />
52
- <div className="textarea-wrapper">
53
- <textarea
54
- ref={ref}
55
- rows={rows}
56
- cols={cols}
57
- autoFocus={autofocus}
58
- wrap={wrap}
59
- form={form}
60
- value={value}
61
- maxLength={maxLength}
62
- readOnly={readonly}
63
- data-testid={`form-textarea-${name}`}
64
- name={name}
65
- disabled={isDisabled}
66
- required={isRequired}
67
- placeholder={placeholder}
68
- onChange={onChange}
69
- className={`textarea ${hasErrors ? 'error' : ''}`}
70
- aria-invalid={hasErrors}
71
- aria-describedby={hasErrors || helpText ? `${name}-helper` : undefined}
72
- aria-required={isRequired}
73
- aria-label={label}
74
- {...rest}
75
- />
76
- </div>
77
- {hasErrors && <ErrorList errorList={errorList} name={name} />}
78
- {helpText && (
79
- <div data-testid={`${name}-help-text`} className="help-text" id={`${name}-helper`}>
80
- {helpText}
52
+ return (
53
+ <div className="form-control">
54
+ <Label label={label} name={name} isRequired={isRequired} hasHiddenLabel={hasHiddenLabel} />
55
+ <div className="textarea-wrapper">
56
+ <textarea
57
+ ref={ref}
58
+ rows={rows}
59
+ cols={cols}
60
+ autoFocus={autofocus}
61
+ wrap={wrap}
62
+ form={form}
63
+ maxLength={maxLength}
64
+ readOnly={readonly}
65
+ data-testid={`form-textarea-${name}`}
66
+ name={name}
67
+ disabled={isDisabled}
68
+ placeholder={placeholder}
69
+ onChange={onChange}
70
+ className={`textarea ${hasErrors ? 'error' : ''}`}
71
+ aria-invalid={hasErrors ? true : undefined}
72
+ aria-describedby={hasErrors || helpText ? `${name}-helper` : undefined}
73
+ aria-required={isRequired}
74
+ aria-label={label}
75
+ {...rest}
76
+ />
81
77
  </div>
82
- )}
83
- </div>
84
- );
85
- };
78
+ {hasErrors && <DisplayFormError message={errorMessage} />}
79
+ {helpText && (
80
+ <div data-testid={`${name}-help-text`} className="help-text" id={`${name}-helper`}>
81
+ {helpText}
82
+ </div>
83
+ )}
84
+ </div>
85
+ );
86
+ },
87
+ );
@@ -25,7 +25,7 @@ describe('Textarea', () => {
25
25
  render(
26
26
  <Textarea
27
27
  isRequired={true}
28
- errorList={['You require a username value.']}
28
+ errorMessage="You require a username value."
29
29
  label="Enter your name"
30
30
  helpText="In order to submit the form, this field is required."
31
31
  name="name"
@@ -71,7 +71,7 @@
71
71
  }
72
72
 
73
73
  .form-control {
74
- .error-list {
74
+ .form-errors {
75
75
  list-style: none;
76
76
  padding: 0;
77
77
  margin: 0;