@indico-data/design-system 2.10.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 (43) hide show
  1. package/lib/index.css +7 -8
  2. package/lib/index.d.ts +18 -13
  3. package/lib/index.esm.css +7 -8
  4. package/lib/index.esm.js +28 -28
  5. package/lib/index.esm.js.map +1 -1
  6. package/lib/index.js +28 -28
  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 +4 -4
  12. package/lib/src/components/forms/passwordInput/PasswordInput.d.ts +4 -3
  13. package/lib/src/components/forms/radio/Radio.d.ts +2 -1
  14. package/lib/src/components/forms/subcomponents/DisplayFormError.d.ts +5 -0
  15. package/lib/src/components/forms/textarea/Textarea.d.ts +4 -3
  16. package/lib/src/components/forms/toggle/Toggle.d.ts +2 -1
  17. package/package.json +5 -2
  18. package/src/components/forms/checkbox/Checkbox.stories.tsx +2 -2
  19. package/src/components/forms/checkbox/Checkbox.tsx +32 -41
  20. package/src/components/forms/form/Form.mdx +134 -0
  21. package/src/components/forms/form/Form.stories.tsx +413 -0
  22. package/src/components/forms/form/Form.tsx +64 -0
  23. package/src/components/forms/form/__tests__/Form.test.tsx +35 -0
  24. package/src/components/forms/form/index.ts +0 -0
  25. package/src/components/forms/form/styles/Form.scss +3 -0
  26. package/src/components/forms/input/Input.tsx +66 -65
  27. package/src/components/forms/input/__tests__/Input.test.tsx +2 -13
  28. package/src/components/forms/input/styles/Input.scss +1 -8
  29. package/src/components/forms/passwordInput/PasswordInput.stories.tsx +11 -12
  30. package/src/components/forms/passwordInput/PasswordInput.tsx +63 -59
  31. package/src/components/forms/passwordInput/__tests__/PasswordInput.test.tsx +1 -1
  32. package/src/components/forms/radio/Radio.tsx +32 -35
  33. package/src/components/forms/subcomponents/DisplayFormError.tsx +7 -0
  34. package/src/components/forms/textarea/Textarea.stories.tsx +15 -21
  35. package/src/components/forms/textarea/Textarea.tsx +64 -62
  36. package/src/components/forms/textarea/__tests__/Textarea.test.tsx +1 -1
  37. package/src/components/forms/textarea/styles/Textarea.scss +1 -1
  38. package/src/components/forms/toggle/Toggle.tsx +30 -37
  39. package/src/styles/index.scss +1 -0
  40. package/lib/src/components/forms/subcomponents/ErrorList.d.ts +0 -6
  41. package/src/components/forms/subcomponents/ErrorList.tsx +0 -14
  42. package/src/components/forms/subcomponents/__tests__/ErrorList.test.tsx +0 -16
  43. /package/lib/src/components/forms/{subcomponents/__tests__/ErrorList.test.d.ts → form/__tests__/Form.test.d.ts} +0 -0
@@ -17,7 +17,6 @@ describe('Input', () => {
17
17
  iconName="user"
18
18
  isClearable={true}
19
19
  ref={undefined}
20
- value={''}
21
20
  onChange={handleOnChange}
22
21
  />,
23
22
  );
@@ -34,7 +33,6 @@ describe('Input', () => {
34
33
  iconName="user"
35
34
  isClearable={true}
36
35
  ref={undefined}
37
- value={''}
38
36
  onChange={handleOnChange}
39
37
  />,
40
38
  );
@@ -53,7 +51,6 @@ describe('Input', () => {
53
51
  iconName="user"
54
52
  isClearable={false}
55
53
  ref={undefined}
56
- value={''}
57
54
  onChange={handleOnChange}
58
55
  />,
59
56
  );
@@ -71,9 +68,8 @@ describe('Input', () => {
71
68
  placeholder="Please enter a value"
72
69
  iconName="user"
73
70
  isClearable={true}
74
- ref={undefined}
75
- value={'test'}
76
71
  onChange={handleOnChange}
72
+ value="test"
77
73
  />,
78
74
  );
79
75
  const input = screen.getByTestId('form-input-name');
@@ -95,8 +91,6 @@ describe('Input', () => {
95
91
  placeholder="Please enter a value"
96
92
  iconName="user"
97
93
  isClearable={true}
98
- ref={undefined}
99
- value={'test'}
100
94
  onChange={handleOnChange}
101
95
  />,
102
96
  );
@@ -113,8 +107,6 @@ describe('Input', () => {
113
107
  name="name"
114
108
  placeholder="Please enter a value"
115
109
  isClearable={true}
116
- ref={undefined}
117
- value={'test'}
118
110
  onChange={handleOnChange}
119
111
  />,
120
112
  );
@@ -126,13 +118,12 @@ describe('Input', () => {
126
118
  render(
127
119
  <Input
128
120
  isRequired={true}
129
- errorList={['You require a username value.']}
121
+ errorMessage="You require a username value."
130
122
  label="Enter your name"
131
123
  helpText="In order to submit the form, this field is required."
132
124
  name="name"
133
125
  placeholder="Please enter a value"
134
126
  isClearable={true}
135
- ref={undefined}
136
127
  value={'test'}
137
128
  onChange={handleOnChange}
138
129
  />,
@@ -201,8 +192,6 @@ describe('Input', () => {
201
192
  label="Enter your name"
202
193
  name="name"
203
194
  placeholder="Please enter a value"
204
- ref={undefined}
205
- value={''}
206
195
  onChange={handleOnChange}
207
196
  />,
208
197
  );
@@ -73,14 +73,7 @@
73
73
  }
74
74
 
75
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
- }
76
+ margin-bottom: var(--pf-margin-3);
84
77
  .help-text {
85
78
  margin-top: var(--pf-margin-2);
86
79
  margin-bottom: var(--pf-margin-2);
@@ -91,16 +91,16 @@ const meta: Meta = {
91
91
  },
92
92
  defaultValue: { summary: 'false' },
93
93
  },
94
- errorList: {
94
+ errorMessage: {
95
95
  control: false,
96
96
  description: 'An array of error messages',
97
97
  table: {
98
98
  category: 'Props',
99
99
  type: {
100
- summary: 'string[]',
100
+ summary: 'string',
101
101
  },
102
102
  },
103
- defaultValue: { summary: '[]' },
103
+ defaultValue: { summary: undefined },
104
104
  },
105
105
  helpText: {
106
106
  control: 'text',
@@ -151,14 +151,13 @@ export const Default: Story = {
151
151
  hasHiddenLabel: false,
152
152
  hasShowPassword: true,
153
153
  isDisabled: false,
154
- errorList: [],
155
- value: '',
154
+ errorMessage: '',
156
155
  },
157
156
  render: (args) => {
158
157
  const [value, setValue] = useState('');
159
158
 
160
159
  useEffect(() => {
161
- setValue(args.value);
160
+ setValue(args.value || '');
162
161
  }, [args.value]);
163
162
 
164
163
  const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
@@ -171,13 +170,13 @@ export const Default: Story = {
171
170
  export const Errors: Story = {
172
171
  args: {
173
172
  ...defaultArgs,
174
- errorList: ['You require a password value.'],
173
+ errorMessage: 'You require a password value.',
175
174
  },
176
175
  render: (args) => {
177
176
  const [value, setValue] = useState('');
178
177
 
179
178
  useEffect(() => {
180
- setValue(args.value);
179
+ setValue(args.value || '');
181
180
  }, [args.value]);
182
181
 
183
182
  const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
@@ -197,7 +196,7 @@ export const HiddenLabel: Story = {
197
196
  const [value, setValue] = useState('');
198
197
 
199
198
  useEffect(() => {
200
- setValue(args.value);
199
+ setValue(args.value || '');
201
200
  }, [args.value]);
202
201
 
203
202
  const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
@@ -217,7 +216,7 @@ export const HelpText: Story = {
217
216
  const [value, setValue] = useState('');
218
217
 
219
218
  useEffect(() => {
220
- setValue(args.value);
219
+ setValue(args.value || '');
221
220
  }, [args.value]);
222
221
 
223
222
  const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
@@ -237,7 +236,7 @@ export const Required: Story = {
237
236
  const [value, setValue] = useState('');
238
237
 
239
238
  useEffect(() => {
240
- setValue(args.value);
239
+ setValue(args.value || '');
241
240
  }, [args.value]);
242
241
 
243
242
  const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
@@ -257,7 +256,7 @@ export const NoTogglePasswordVisibility: Story = {
257
256
  const [value, setValue] = useState('');
258
257
 
259
258
  useEffect(() => {
260
- setValue(args.value);
259
+ setValue(args.value || '');
261
260
  }, [args.value]);
262
261
 
263
262
  const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
@@ -1,82 +1,86 @@
1
1
  import React, { useState } from 'react';
2
2
  import { Icon } from '@/components/icons';
3
3
  import { Label } from '../subcomponents/Label';
4
- import { ErrorList } from '../subcomponents/ErrorList';
4
+ import { DisplayFormError } from '../subcomponents/DisplayFormError';
5
5
 
6
6
  export interface PasswordInputProps {
7
7
  ref?: React.LegacyRef<HTMLInputElement>;
8
8
  label: string;
9
+ value?: string | undefined;
9
10
  name: string;
10
11
  placeholder: string;
11
- value: string;
12
12
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
13
13
  isRequired?: boolean;
14
14
  isDisabled?: boolean;
15
- errorList?: string[];
15
+ errorMessage?: string | undefined;
16
16
  helpText?: string;
17
17
  hasHiddenLabel?: boolean;
18
18
  hasShowPassword?: boolean;
19
+ defaultValue?: string;
19
20
  }
20
21
 
21
- export const PasswordInput = ({
22
- label,
23
- name,
24
- placeholder,
25
- value,
26
- onChange,
27
- isRequired,
28
- isDisabled,
29
- errorList,
30
- helpText,
31
- hasHiddenLabel,
32
- hasShowPassword = true,
33
- ...rest
34
- }: PasswordInputProps) => {
35
- const hasErrors = errorList && errorList.length > 0;
22
+ export const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
23
+ (
24
+ {
25
+ label,
26
+ name,
27
+ placeholder,
28
+ onChange,
29
+ isRequired,
30
+ isDisabled,
31
+ errorMessage,
32
+ helpText,
33
+ hasHiddenLabel,
34
+ hasShowPassword = true,
35
+ ...rest
36
+ },
37
+ ref,
38
+ ) => {
39
+ const hasErrors = errorMessage && errorMessage.length > 0;
36
40
 
37
- const [showPassword, setShowPassword] = useState(false);
41
+ const [showPassword, setShowPassword] = useState(false);
38
42
 
39
- const handleShowPassword = () => {
40
- setShowPassword((prevShowPassword) => !prevShowPassword);
41
- };
43
+ const handleShowPassword = () => {
44
+ setShowPassword((prevShowPassword) => !prevShowPassword);
45
+ };
42
46
 
43
- return (
44
- <div className="form-control">
45
- <Label label={label} name={name} isRequired={isRequired} hasHiddenLabel={hasHiddenLabel} />
46
- <div className="password-input-wrapper">
47
- <Icon name="lock" data-testid={`${name}-embedded-icon`} className="embedded-icon" />
48
- <input
49
- data-testid={`form-password-input-${name}`}
50
- name={name}
51
- type={showPassword ? 'text' : 'password'}
52
- disabled={isDisabled}
53
- required={isRequired}
54
- placeholder={placeholder}
55
- value={value}
56
- onChange={onChange}
57
- className={`password-input ${hasErrors ? 'error' : ''} password-input--has-icon`}
58
- aria-invalid={hasErrors}
59
- aria-describedby={hasErrors || helpText ? `${name}-helper` : undefined}
60
- aria-required={isRequired}
61
- aria-label={label}
62
- {...rest}
63
- />
64
- {hasShowPassword && (
65
- <Icon
66
- name={showPassword ? 'fa-eye-slash' : 'eye'}
67
- data-testid={`${name}-${showPassword ? 'hide' : 'show'}-password-icon`}
68
- size="md"
69
- onClick={handleShowPassword}
70
- className="toggle-show-password-icon"
47
+ return (
48
+ <div className="form-control">
49
+ <Label label={label} name={name} isRequired={isRequired} hasHiddenLabel={hasHiddenLabel} />
50
+ <div className="password-input-wrapper">
51
+ <Icon name="lock" data-testid={`${name}-embedded-icon`} className="embedded-icon" />
52
+ <input
53
+ ref={ref}
54
+ data-testid={`form-password-input-${name}`}
55
+ name={name}
56
+ type={showPassword ? 'text' : 'password'}
57
+ disabled={isDisabled}
58
+ placeholder={placeholder}
59
+ onChange={onChange}
60
+ className={`password-input ${hasErrors ? 'error' : ''} password-input--has-icon`}
61
+ aria-invalid={hasErrors ? 'true' : 'false'}
62
+ aria-describedby={hasErrors || helpText ? `${name}-helper` : undefined}
63
+ aria-required={isRequired}
64
+ aria-label={label}
65
+ {...rest}
71
66
  />
67
+ {hasShowPassword && (
68
+ <Icon
69
+ name={showPassword ? 'fa-eye-slash' : 'eye'}
70
+ data-testid={`${name}-${showPassword ? 'hide' : 'show'}-password-icon`}
71
+ size="md"
72
+ onClick={handleShowPassword}
73
+ className="toggle-show-password-icon"
74
+ />
75
+ )}
76
+ </div>
77
+ {hasErrors && <DisplayFormError message={errorMessage} />}
78
+ {helpText && (
79
+ <div data-testid={`${name}-help-text`} className="help-text" id={`${name}-helper`}>
80
+ {helpText}
81
+ </div>
72
82
  )}
73
83
  </div>
74
- {hasErrors && <ErrorList errorList={errorList} name={name} />}
75
- {helpText && (
76
- <div data-testid={`${name}-help-text`} className="help-text" id={`${name}-helper`}>
77
- {helpText}
78
- </div>
79
- )}
80
- </div>
81
- );
82
- };
84
+ );
85
+ },
86
+ );
@@ -48,7 +48,7 @@ describe('Input', () => {
48
48
  render(
49
49
  <PasswordInput
50
50
  isRequired
51
- errorList={['You require a username value.']}
51
+ errorMessage="You require a username value."
52
52
  label="Enter your name"
53
53
  helpText="In order to submit the form, this field is required."
54
54
  name="name"
@@ -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