@indico-data/design-system 2.17.2 → 2.19.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 (49) hide show
  1. package/lib/index.css +616 -184
  2. package/lib/index.d.ts +17 -21
  3. package/lib/index.esm.css +616 -184
  4. package/lib/index.esm.js +36 -462
  5. package/lib/index.esm.js.map +1 -1
  6. package/lib/index.js +37 -463
  7. package/lib/index.js.map +1 -1
  8. package/lib/src/components/button/Button.stories.d.ts +1 -0
  9. package/lib/src/components/forms/input/Input.d.ts +5 -7
  10. package/lib/src/components/forms/passwordInput/PasswordInput.d.ts +5 -7
  11. package/lib/src/components/forms/subcomponents/Label.d.ts +6 -3
  12. package/lib/src/components/forms/textarea/Textarea.d.ts +5 -7
  13. package/lib/src/legacy/components/modals/ModalBase/ModalBase.d.ts +0 -1
  14. package/lib/src/storybook/labelArgTypes.d.ts +3 -0
  15. package/package.json +1 -1
  16. package/src/components/button/Button.mdx +6 -3
  17. package/src/components/button/Button.stories.tsx +8 -0
  18. package/src/components/button/Button.tsx +14 -6
  19. package/src/components/button/__tests__/Button.test.tsx +38 -0
  20. package/src/components/button/styles/Button.scss +14 -22
  21. package/src/components/button/styles/_variables.scss +77 -4
  22. package/src/components/forms/checkbox/styles/Checkbox.scss +3 -3
  23. package/src/components/forms/input/Input.mdx +15 -2
  24. package/src/components/forms/input/Input.stories.tsx +10 -45
  25. package/src/components/forms/input/Input.tsx +22 -15
  26. package/src/components/forms/input/styles/Input.scss +2 -15
  27. package/src/components/forms/passwordInput/PasswordInput.mdx +10 -8
  28. package/src/components/forms/passwordInput/PasswordInput.stories.tsx +3 -44
  29. package/src/components/forms/passwordInput/PasswordInput.tsx +20 -15
  30. package/src/components/forms/passwordInput/styles/PasswordInput.scss +2 -15
  31. package/src/components/forms/radio/styles/Radio.scss +3 -3
  32. package/src/components/forms/select/styles/Select.scss +21 -4
  33. package/src/components/forms/subcomponents/Label.tsx +29 -6
  34. package/src/components/forms/subcomponents/__tests__/Label.test.tsx +63 -15
  35. package/src/components/forms/textarea/Textarea.mdx +12 -2
  36. package/src/components/forms/textarea/Textarea.stories.tsx +4 -46
  37. package/src/components/forms/textarea/Textarea.tsx +15 -13
  38. package/src/components/forms/textarea/styles/Textarea.scss +2 -14
  39. package/src/components/forms/toggle/styles/Toggle.scss +2 -2
  40. package/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.scss +441 -0
  41. package/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.tsx +3 -4
  42. package/src/legacy/components/modals/ModalBase/ModalBase.tsx +5 -1
  43. package/src/storybook/labelArgTypes.ts +50 -0
  44. package/src/styles/globals.scss +11 -0
  45. package/src/styles/index.scss +2 -2
  46. package/src/styles/variables/themes/dark.scss +8 -7
  47. package/src/styles/variables/themes/light.scss +1 -0
  48. package/lib/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.styles.d.ts +0 -1
  49. package/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.styles.ts +0 -449
@@ -1,30 +1,27 @@
1
1
  import React from 'react';
2
+ import classNames from 'classnames';
3
+
2
4
  import { Icon } from '@/components/icons';
3
5
  import { IconName } from '@/types';
4
- import { Label } from '../subcomponents/Label';
6
+ import { withLabel, WithLabelProps } from '../subcomponents/Label';
5
7
  import { DisplayFormError } from '../subcomponents/DisplayFormError';
6
8
 
7
- export interface InputProps {
8
- label: string;
9
- name: string;
9
+ export interface InputProps extends WithLabelProps {
10
10
  value?: string | undefined;
11
- placeholder: string;
11
+ placeholder?: string;
12
12
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
13
- isRequired?: boolean;
14
13
  isDisabled?: boolean;
15
14
  errorMessage?: string | undefined;
16
15
  helpText?: string;
17
- hasHiddenLabel?: boolean;
18
16
  iconName?: IconName;
19
17
  isClearable?: boolean;
20
18
  className?: string;
21
19
  defaultValue?: string;
22
20
  }
23
21
 
24
- export const Input = React.forwardRef<HTMLInputElement, InputProps>(
22
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
25
23
  (
26
24
  {
27
- label,
28
25
  name,
29
26
  placeholder,
30
27
  onChange,
@@ -34,7 +31,6 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
34
31
  errorMessage,
35
32
  helpText,
36
33
  iconName,
37
- hasHiddenLabel,
38
34
  className,
39
35
  ...rest
40
36
  },
@@ -45,9 +41,17 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
45
41
  onChange({ target: { value: '' } } as React.ChangeEvent<HTMLInputElement>);
46
42
  };
47
43
 
44
+ const inputClasses = classNames(
45
+ 'input',
46
+ {
47
+ error: hasErrors,
48
+ 'input--has-icon': iconName,
49
+ },
50
+ className,
51
+ );
52
+
48
53
  return (
49
- <div className="form-control">
50
- <Label label={label} name={name} isRequired={isRequired} hasHiddenLabel={hasHiddenLabel} />
54
+ <>
51
55
  <div className="input-wrapper">
52
56
  {iconName && (
53
57
  <Icon name={iconName} data-testid={`${name}-embedded-icon`} className="embedded-icon" />
@@ -60,11 +64,10 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
60
64
  disabled={isDisabled}
61
65
  placeholder={placeholder}
62
66
  onChange={onChange}
63
- className={`input ${hasErrors ? 'error' : ''} ${iconName ? 'input--has-icon' : ''} ${className}`}
67
+ className={inputClasses}
64
68
  aria-invalid={hasErrors ? true : undefined}
65
69
  aria-describedby={hasErrors || helpText ? `${name}-helper` : undefined}
66
70
  aria-required={isRequired}
67
- aria-label={label}
68
71
  {...rest}
69
72
  />
70
73
  {isClearable && (
@@ -83,7 +86,11 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
83
86
  {helpText}
84
87
  </div>
85
88
  )}
86
- </div>
89
+ </>
87
90
  );
88
91
  },
89
92
  );
93
+
94
+ const LabeledInput = withLabel(Input);
95
+
96
+ export { LabeledInput as Input };
@@ -4,14 +4,12 @@
4
4
  :root [data-theme='dark'] {
5
5
  // Typography
6
6
  --pf-input-background-color: var(--pf-white-color);
7
- --pf-input-border-color: var(--pf-gray-color);
7
+ --pf-input-border-color: var(--pf-form-input-border-color);
8
8
  --pf-input-text-color: var(--pf-gray-color);
9
9
  --pf-input-placeholder-text-color: var(--pf-gray-color-300);
10
10
  --pf-input-help-text-color: var(--pf-gray-color-400);
11
11
  --pf-input-disabled-background-color: var(--pf-gray-color-100);
12
- --pf-input-border-color: var(--pf-gray-color);
13
12
  --pf-input-disabled-color: var(--pf-gray-color-400);
14
- --pf-input-border-color: var(--pf-gray-color);
15
13
 
16
14
  // input Radius
17
15
  --pf-input-rounded: var(--pf-rounded);
@@ -20,7 +18,7 @@
20
18
  // Dark Theme Specific Variables
21
19
  :root [data-theme='dark'] {
22
20
  --pf-input-background-color: var(--pf-primary-color);
23
- --pf-input-border-color: var(--pf-gray-color-100);
21
+ --pf-input-border-color: var(--pf-form-input-border-color);
24
22
  --pf-input-text-color: var(--pf-gray-color-100);
25
23
  --pf-input-placeholder-text-color: var(--pf-gray-color);
26
24
  --pf-input-help-text-color: var(--pf-gray-color-200);
@@ -96,17 +94,6 @@
96
94
  cursor: pointer;
97
95
  }
98
96
  }
99
- .is-visually-hidden {
100
- position: absolute;
101
- width: 1px;
102
- height: 1px;
103
- padding: 0;
104
- margin: -1px;
105
- overflow: hidden;
106
- clip: rect(0, 0, 0, 0);
107
- white-space: nowrap;
108
- border: 0;
109
- }
110
97
  .form-label {
111
98
  margin-bottom: var(--pf-margin-2);
112
99
  }
@@ -5,22 +5,24 @@ import * as PasswordInput from './PasswordInput.stories';
5
5
 
6
6
  # Password Input
7
7
 
8
- The password input component is the building block of any form. Below you will find the accepted properties for this component. It is encouraged to build forms utilizing [React Hook Form](https://react-hook-form.com/) library in your application. This will facilitate form state management and enforce best practices. (***Our components are compatible with but do not provide the plugin***)
8
+ The password input component is the building block of any form. Below you will find the accepted properties for this component. It is encouraged to build forms utilizing [React Hook Form](https://react-hook-form.com/) library in your application. This will facilitate form state management and enforce best practices. (**_Our components are compatible with but do not provide the plugin_**)
9
9
 
10
10
  <Canvas
11
11
  of={PasswordInput.Default}
12
12
  source={{
13
13
  code: `
14
14
  <PasswordInput
15
- name="first_name"
15
+ label="Label Name"
16
+ name="first_name"
17
+ placeholder="Please enter a value"
18
+ helpText="This Is Help Text"
16
19
  isRequired
20
+ hasHiddenLabel={false}
17
21
  hasShowPassword
18
- placeholder="Placeholder Text"
19
- value=''
20
- onChange={() => {}}
21
- helpText="This Is Help Text"
22
- label="Label Name"
23
- />
22
+ isDisabled={false}
23
+ errorMessage=""
24
+ value=""
25
+ onChange={() => {}}/>
24
26
  `,
25
27
  }}
26
28
  />
@@ -1,7 +1,7 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
2
  import { PasswordInput, PasswordInputProps } from './PasswordInput';
3
3
  import { SetStateAction, useEffect, useState } from 'react';
4
- import { iconNames } from 'build/generated/iconTypes';
4
+ import labelArgTypes from '@/storybook/labelArgTypes';
5
5
 
6
6
  const meta: Meta = {
7
7
  title: 'Forms/PasswordInput',
@@ -29,26 +29,6 @@ const meta: Meta = {
29
29
  },
30
30
  action: 'onChange',
31
31
  },
32
- label: {
33
- control: 'text',
34
- description: 'The label for the password field',
35
- table: {
36
- category: 'Props',
37
- type: {
38
- summary: 'string',
39
- },
40
- },
41
- },
42
- name: {
43
- control: 'text',
44
- description: 'The name for the password field',
45
- table: {
46
- category: 'Props',
47
- type: {
48
- summary: 'string',
49
- },
50
- },
51
- },
52
32
  placeholder: {
53
33
  control: 'text',
54
34
  description: 'The placeholder for the password field',
@@ -69,17 +49,6 @@ const meta: Meta = {
69
49
  },
70
50
  },
71
51
  },
72
- isRequired: {
73
- control: 'boolean',
74
- description: 'Toggles the required astherisk on the label',
75
- table: {
76
- category: 'Props',
77
- type: {
78
- summary: 'boolean',
79
- },
80
- },
81
- defaultValue: { summary: 'false' },
82
- },
83
52
  isDisabled: {
84
53
  control: 'boolean',
85
54
  description: 'Toggles the disabled state of the input',
@@ -112,22 +81,12 @@ const meta: Meta = {
112
81
  },
113
82
  },
114
83
  },
115
- hasHiddenLabel: {
116
- control: 'boolean',
117
- description: 'Hides the label visually (retains it for screen readers)',
118
- table: {
119
- category: 'Props',
120
- type: {
121
- summary: 'boolean',
122
- },
123
- },
124
- defaultValue: { summary: 'false' },
125
- },
126
84
  ref: {
127
85
  table: {
128
86
  disable: true,
129
87
  },
130
88
  },
89
+ ...labelArgTypes,
131
90
  },
132
91
  };
133
92
 
@@ -143,7 +102,7 @@ const defaultArgs = {
143
102
 
144
103
  export const Default: Story = {
145
104
  args: {
146
- isRequired: false,
105
+ isRequired: true,
147
106
  helpText: 'This Is Help Text',
148
107
  label: 'Label Name',
149
108
  name: 'first_name',
@@ -1,28 +1,24 @@
1
1
  import React, { useState } from 'react';
2
2
  import { Icon } from '@/components/icons';
3
- import { Label } from '../subcomponents/Label';
3
+ import { withLabel, WithLabelProps } from '../subcomponents/Label';
4
4
  import { DisplayFormError } from '../subcomponents/DisplayFormError';
5
+ import classNames from 'classnames';
5
6
 
6
- export interface PasswordInputProps {
7
+ export interface PasswordInputProps extends WithLabelProps {
7
8
  ref?: React.LegacyRef<HTMLInputElement>;
8
- label: string;
9
9
  value?: string | undefined;
10
- name: string;
11
- placeholder: string;
10
+ placeholder?: string;
12
11
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
13
- isRequired?: boolean;
14
12
  isDisabled?: boolean;
15
13
  errorMessage?: string | undefined;
16
14
  helpText?: string;
17
- hasHiddenLabel?: boolean;
18
15
  hasShowPassword?: boolean;
19
16
  defaultValue?: string;
20
17
  }
21
18
 
22
- export const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
19
+ const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
23
20
  (
24
21
  {
25
- label,
26
22
  name,
27
23
  placeholder,
28
24
  onChange,
@@ -30,7 +26,6 @@ export const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputPro
30
26
  isDisabled,
31
27
  errorMessage,
32
28
  helpText,
33
- hasHiddenLabel,
34
29
  hasShowPassword = true,
35
30
  ...rest
36
31
  },
@@ -44,9 +39,16 @@ export const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputPro
44
39
  setShowPassword((prevShowPassword) => !prevShowPassword);
45
40
  };
46
41
 
42
+ const inputClasses = classNames(
43
+ 'password-input',
44
+ {
45
+ error: hasErrors,
46
+ },
47
+ 'password-input--has-icon',
48
+ );
49
+
47
50
  return (
48
- <div className="form-control">
49
- <Label label={label} name={name} isRequired={isRequired} hasHiddenLabel={hasHiddenLabel} />
51
+ <>
50
52
  <div className="password-input-wrapper">
51
53
  <Icon name="lock" data-testid={`${name}-embedded-icon`} className="embedded-icon" />
52
54
  <input
@@ -57,11 +59,10 @@ export const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputPro
57
59
  disabled={isDisabled}
58
60
  placeholder={placeholder}
59
61
  onChange={onChange}
60
- className={`password-input ${hasErrors ? 'error' : ''} password-input--has-icon`}
62
+ className={inputClasses}
61
63
  aria-invalid={hasErrors ? 'true' : 'false'}
62
64
  aria-describedby={hasErrors || helpText ? `${name}-helper` : undefined}
63
65
  aria-required={isRequired}
64
- aria-label={label}
65
66
  {...rest}
66
67
  />
67
68
  {hasShowPassword && (
@@ -80,7 +81,11 @@ export const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputPro
80
81
  {helpText}
81
82
  </div>
82
83
  )}
83
- </div>
84
+ </>
84
85
  );
85
86
  },
86
87
  );
88
+
89
+ const LabeledPasswordInput = withLabel(PasswordInput);
90
+
91
+ export { LabeledPasswordInput as PasswordInput };
@@ -4,14 +4,12 @@
4
4
  :root [data-theme='dark'] {
5
5
  // Typography
6
6
  --pf-password-input-background-color: var(--pf-white-color);
7
- --pf-password-input-border-color: var(--pf-gray-color);
7
+ --pf-password-input-border-color: var(--pf-form-input-border-color);
8
8
  --pf-password-input-text-color: var(--pf-gray-color);
9
9
  --pf-password-input-placeholder-text-color: var(--pf-gray-color-300);
10
10
  --pf-password-input-help-text-color: var(--pf-gray-color-400);
11
11
  --pf-password-input-disabled-background-color: var(--pf-gray-color-100);
12
- --pf-password-input-border-color: var(--pf-gray-color);
13
12
  --pf-password-input-disabled-color: var(--pf-gray-color-400);
14
- --pf-password-input-border-color: var(--pf-gray-color);
15
13
 
16
14
  // input Radius
17
15
  --pf-password-input-rounded: var(--pf-rounded);
@@ -20,7 +18,7 @@
20
18
  // Dark Theme Specific Variables
21
19
  :root [data-theme='dark'] {
22
20
  --pf-password-input-background-color: var(--pf-primary-color);
23
- --pf-password-input-border-color: var(--pf-gray-color-100);
21
+ --pf-password-input-border-color: var(--pf-form-input-border-color);
24
22
  --pf-password-input-text-color: var(--pf-gray-color-100);
25
23
  --pf-password-input-placeholder-text-color: var(--pf-gray-color);
26
24
  --pf-password-input-help-text-color: var(--pf-gray-color-200);
@@ -103,17 +101,6 @@
103
101
  cursor: pointer;
104
102
  }
105
103
  }
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
104
  .form-label {
118
105
  margin-bottom: var(--pf-margin-2);
119
106
  }
@@ -4,15 +4,15 @@
4
4
  :root [data-theme='dark'] {
5
5
  --pf-radio-background-color: var(--pf-white-color);
6
6
  --pf-radio-check-color: var(--pf-primary-color);
7
- --pf-radio-border-color: var(--pf-gray-color);
7
+ --pf-radio-border-color: var(--pf-form-input-border-color);
8
8
  --pf-radio-disabled-color: var(--pf-gray-color-400);
9
9
  }
10
10
 
11
11
  // Dark Theme Specific Variables
12
12
  :root [data-theme='dark'] {
13
- --pf-radio-background-color: transparent;
13
+ --pf-radio-background-color: var(--pf-primary-color-600);
14
14
  --pf-radio-check-color: var(--pf-white-color);
15
- --pf-radio-border-color: var(--pf-white-color);
15
+ --pf-radio-border-color: var(--pf-form-input-border-color);
16
16
  --pf-radio-disabled-color: var(--pf-gray-color-300);
17
17
  }
18
18
 
@@ -2,12 +2,12 @@
2
2
  :root,
3
3
  :root [data-theme='light'] {
4
4
  --pf-select-background-color: var(--pf-white-color);
5
- --pf-select-border-color: var(--pf-gray-color);
5
+ --pf-select-border-color: var(--pf-form-input-border-color);
6
6
  --pf-select-text-color: var(--pf-gray-color);
7
7
  --pf-select-indicator-color: var(--pf-gray-color);
8
8
  --pf-select-placeholder-text-color: var(--pf-gray-color);
9
9
  --pf-select-hover-color: var(--pf-gray-color);
10
- --pf-select-option-selected-color: var(--pf-primary-color-200);
10
+ --pf-select-option-selected-color: var(--pf-primary-color-100);
11
11
  --pf-select-option-text-color: var(--pf-gray-color);
12
12
  --pf-select-option-hover-color: var(--pf-primary-color-100);
13
13
 
@@ -19,12 +19,12 @@
19
19
  // Dark Theme Specific Variables
20
20
  :root [data-theme='dark'] {
21
21
  --pf-select-background-color: var(--pf-primary-color);
22
- --pf-select-border-color: var(--pf-gray-color-100);
22
+ --pf-select-border-color: var(--pf-form-input-border-color);
23
23
  --pf-select-text-color: var(--pf-gray-color-100);
24
24
  --pf-select-indicator-color: var(--pf-gray-color-100);
25
25
  --pf-select-placeholder-text-color: var(--pf-gray-color-100);
26
26
  --pf-select-hover-color: var(--pf-gray-color-100);
27
- --pf-select-option-selected-color: var(--pf-primary-color-200);
27
+ --pf-select-option-selected-color: var(--pf-primary-color-900);
28
28
  --pf-select-option-text-color: var(--pf-gray-color-100);
29
29
  --pf-select-option-hover-color: var(--pf-primary-color-300);
30
30
 
@@ -65,6 +65,23 @@
65
65
  color: var(--pf-select-text-color);
66
66
  }
67
67
 
68
+ &multi-value {
69
+ background-color: var(--pf-select-option-selected-color);
70
+ border-radius: var(--pf-rounded);
71
+ &__label {
72
+ padding-top: 5px; // TODO -- clean up padding on this component overall
73
+ color: var(--pf-select-option-text-color);
74
+ }
75
+ &__remove {
76
+ color: var(--pf-select-option-text-color);
77
+ cursor: pointer;
78
+ &:hover {
79
+ background-color: var(--pf-select-option-hover-color);
80
+ color: var(--pf-select-hover-color);
81
+ }
82
+ }
83
+ }
84
+
68
85
  &menu {
69
86
  border: 1px solid var(--pf-select-border-color);
70
87
  box-shadow: 0px 1px 8px 0px rgba(0, 0, 0, 0.4);
@@ -1,16 +1,18 @@
1
- interface LabelProps {
1
+ import React, { forwardRef } from 'react';
2
+
3
+ export interface LabelProps {
2
4
  label: string;
3
5
  name: string;
4
6
  isRequired?: boolean;
7
+ }
8
+
9
+ export interface WithLabelProps extends LabelProps {
5
10
  hasHiddenLabel?: boolean;
6
11
  }
7
12
 
8
- export const Label = ({ label, name, isRequired, hasHiddenLabel }: LabelProps) => {
13
+ export const Label = ({ label, name, isRequired }: LabelProps) => {
9
14
  return (
10
- <div
11
- data-testid={`${name}-testId`}
12
- className={`form-label ${hasHiddenLabel ? 'is-visually-hidden' : ''}`}
13
- >
15
+ <div data-testid={`${name}-testId`} className={`form-label`}>
14
16
  <label htmlFor={`${name}`}>
15
17
  {label}
16
18
  {isRequired ? <span className="text-error"> *</span> : ''}
@@ -18,3 +20,24 @@ export const Label = ({ label, name, isRequired, hasHiddenLabel }: LabelProps) =
18
20
  </div>
19
21
  );
20
22
  };
23
+
24
+ // HOC to add common label functionality to components
25
+ export function withLabel<P extends object>(WrappedComponent: React.ComponentType<P>) {
26
+ const WithLabelComponent = (
27
+ { label, hasHiddenLabel = false, name, isRequired, ...rest }: P & WithLabelProps,
28
+ ref: React.Ref<any>,
29
+ ) => {
30
+ const ariaLabel = hasHiddenLabel
31
+ ? { 'aria-label': isRequired ? `${label} (required)` : label }
32
+ : {};
33
+
34
+ return (
35
+ <div className="form-control">
36
+ {!hasHiddenLabel && <Label label={label} name={name} isRequired={isRequired} />}
37
+ <WrappedComponent {...(rest as P)} id={name} name={name} {...ariaLabel} ref={ref} />
38
+ </div>
39
+ );
40
+ };
41
+
42
+ return forwardRef(WithLabelComponent);
43
+ }
@@ -1,33 +1,81 @@
1
1
  import { render, screen } from '@testing-library/react';
2
- import { Label } from '@/components/forms/subcomponents/Label';
2
+ import { Label, withLabel } from '@/components/forms/subcomponents/Label';
3
+
4
+ // Mock component to wrap with HOC
5
+ const MockComponent: React.FC = (props) => {
6
+ return <input {...props} />;
7
+ };
8
+
9
+ const LabeledMockComponent = withLabel(MockComponent);
3
10
 
4
11
  describe('Label', () => {
5
- it('renders the `required` astherisc when the label is required', () => {
6
- render(<Label label={'name'} name={'name'} isRequired={true} hasHiddenLabel={false} />);
12
+ it('renders the `required` asterisk when the label is required', () => {
13
+ render(<Label label="name" name="name" isRequired={true} />);
7
14
  expect(screen.getByText('*')).toBeInTheDocument();
8
15
  expect(screen.getByText('*')).toBeVisible();
9
16
  });
10
- it('does not render the `required` astherisc when the label is not required', () => {
11
- render(<Label label={'name'} name={'name'} isRequired={false} hasHiddenLabel={false} />);
17
+
18
+ it('does not render the `required` asterisk when the label is not required', () => {
19
+ render(<Label label="name" name="name" isRequired={false} />);
12
20
  expect(screen.queryByText('*')).not.toBeInTheDocument();
13
21
  expect(screen.queryByText('*')).toBeNull();
14
22
  });
15
- it('renders the label for screen readers while hidden visually', () => {
16
- render(<Label label={'name'} name={'name'} isRequired={false} hasHiddenLabel={true} />);
17
- expect(screen.getByText('name')).toBeInTheDocument();
18
- expect(screen.getByText('name')).toBeVisible();
19
- });
23
+
20
24
  it('renders the label text', () => {
21
- render(<Label label={'name'} name={'name'} isRequired={false} hasHiddenLabel={false} />);
25
+ render(<Label label="name" name="name" isRequired={false} />);
22
26
  const label = screen.getByTestId('name-testId');
23
27
  expect(label).toBeInTheDocument();
24
28
  expect(label).toBeVisible();
25
- expect(label).not.toHaveClass('is-visually-hidden');
26
29
  });
30
+ });
27
31
 
28
- it('renders the label text when the label is hidden', () => {
29
- render(<Label label={'name'} name={'name'} isRequired={false} hasHiddenLabel={true} />);
32
+ describe('withLabel HOC', () => {
33
+ it('renders the wrapped component with a visible label', () => {
34
+ render(
35
+ <LabeledMockComponent label="name" name="name" isRequired={false} hasHiddenLabel={false} />,
36
+ );
30
37
  const label = screen.getByTestId('name-testId');
31
- expect(label).toHaveClass('is-visually-hidden');
38
+ const input = screen.getByRole('textbox');
39
+
40
+ expect(label).toBeInTheDocument();
41
+ expect(label).toBeVisible();
42
+ expect(input).toBeInTheDocument();
43
+ });
44
+
45
+ it('renders the wrapped component with an aria-label when hasHiddenLabel is true', () => {
46
+ render(<LabeledMockComponent label="name" name="name" isRequired={false} hasHiddenLabel />);
47
+ const input = screen.getByRole('textbox');
48
+
49
+ expect(input).toBeInTheDocument();
50
+ expect(input).toHaveAttribute('aria-label', 'name');
51
+ });
52
+
53
+ it('renders the wrapped component with an aria-label including "required" when isRequired is true and hasHiddenLabel is true', () => {
54
+ render(<LabeledMockComponent label="name" name="name" isRequired={true} hasHiddenLabel />);
55
+ const input = screen.getByRole('textbox');
56
+
57
+ expect(input).toBeInTheDocument();
58
+ expect(input).toHaveAttribute('aria-label', 'name (required)');
59
+ });
60
+
61
+ it('does not render the visible label when hasHiddenLabel is true', () => {
62
+ render(<LabeledMockComponent label="name" name="name" isRequired={false} hasHiddenLabel />);
63
+ expect(screen.queryByTestId('name-testId')).not.toBeInTheDocument();
64
+ });
65
+
66
+ it('renders the `required` asterisk when isRequired is true and hasHiddenLabel is false', () => {
67
+ render(
68
+ <LabeledMockComponent label="name" name="name" isRequired={true} hasHiddenLabel={false} />,
69
+ );
70
+ expect(screen.getByText('*')).toBeInTheDocument();
71
+ expect(screen.getByText('*')).toBeVisible();
72
+ });
73
+
74
+ it('does not render the `required` asterisk when isRequired is false and hasHiddenLabel is false', () => {
75
+ render(
76
+ <LabeledMockComponent label="name" name="name" isRequired={false} hasHiddenLabel={false} />,
77
+ );
78
+ expect(screen.queryByText('*')).not.toBeInTheDocument();
79
+ expect(screen.queryByText('*')).toBeNull();
32
80
  });
33
81
  });
@@ -5,13 +5,23 @@ import * as Textarea from './Textarea.stories';
5
5
 
6
6
  # Textarea
7
7
 
8
- The Textarea component is the building block of any form. Below you will find the accepted properties for this component. It is encouraged to build forms utilizing [React Hook Form](https://react-hook-form.com/) library in your application. This will facilitate form state management and enforce best practices. (***Our components are compatible with but do not provide the plugin***)
8
+ The Textarea component is the building block of any form. Below you will find the accepted properties for this component. It is encouraged to build forms utilizing [React Hook Form](https://react-hook-form.com/) library in your application. This will facilitate form state management and enforce best practices. (**_Our components are compatible with but do not provide the plugin_**)
9
9
 
10
10
  <Canvas
11
11
  of={Textarea.Default}
12
12
  source={{
13
13
  code: `
14
- <Textarea name="first_name" isRequired helpText="This Is Help Text" placeholder="This is a placeholder" value="''" onChange={handleChange} label="Label Name" />
14
+ <Textarea
15
+ label="Label Name"
16
+ name="textarea"
17
+ placeholder="Please enter a value"
18
+ helpText="This Is Help Text"
19
+ isRequired
20
+ isDisabled={false}
21
+ errorMessage=""
22
+ value=""
23
+ onChange={() => {}}
24
+ />
15
25
  `,
16
26
  }}
17
27
  />