@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.
- package/lib/index.css +616 -184
- package/lib/index.d.ts +17 -21
- package/lib/index.esm.css +616 -184
- package/lib/index.esm.js +36 -462
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +37 -463
- package/lib/index.js.map +1 -1
- package/lib/src/components/button/Button.stories.d.ts +1 -0
- package/lib/src/components/forms/input/Input.d.ts +5 -7
- package/lib/src/components/forms/passwordInput/PasswordInput.d.ts +5 -7
- package/lib/src/components/forms/subcomponents/Label.d.ts +6 -3
- package/lib/src/components/forms/textarea/Textarea.d.ts +5 -7
- package/lib/src/legacy/components/modals/ModalBase/ModalBase.d.ts +0 -1
- package/lib/src/storybook/labelArgTypes.d.ts +3 -0
- package/package.json +1 -1
- package/src/components/button/Button.mdx +6 -3
- package/src/components/button/Button.stories.tsx +8 -0
- package/src/components/button/Button.tsx +14 -6
- package/src/components/button/__tests__/Button.test.tsx +38 -0
- package/src/components/button/styles/Button.scss +14 -22
- package/src/components/button/styles/_variables.scss +77 -4
- package/src/components/forms/checkbox/styles/Checkbox.scss +3 -3
- package/src/components/forms/input/Input.mdx +15 -2
- package/src/components/forms/input/Input.stories.tsx +10 -45
- package/src/components/forms/input/Input.tsx +22 -15
- package/src/components/forms/input/styles/Input.scss +2 -15
- package/src/components/forms/passwordInput/PasswordInput.mdx +10 -8
- package/src/components/forms/passwordInput/PasswordInput.stories.tsx +3 -44
- package/src/components/forms/passwordInput/PasswordInput.tsx +20 -15
- package/src/components/forms/passwordInput/styles/PasswordInput.scss +2 -15
- package/src/components/forms/radio/styles/Radio.scss +3 -3
- package/src/components/forms/select/styles/Select.scss +21 -4
- package/src/components/forms/subcomponents/Label.tsx +29 -6
- package/src/components/forms/subcomponents/__tests__/Label.test.tsx +63 -15
- package/src/components/forms/textarea/Textarea.mdx +12 -2
- package/src/components/forms/textarea/Textarea.stories.tsx +4 -46
- package/src/components/forms/textarea/Textarea.tsx +15 -13
- package/src/components/forms/textarea/styles/Textarea.scss +2 -14
- package/src/components/forms/toggle/styles/Toggle.scss +2 -2
- package/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.scss +441 -0
- package/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.tsx +3 -4
- package/src/legacy/components/modals/ModalBase/ModalBase.tsx +5 -1
- package/src/storybook/labelArgTypes.ts +50 -0
- package/src/styles/globals.scss +11 -0
- package/src/styles/index.scss +2 -2
- package/src/styles/variables/themes/dark.scss +8 -7
- package/src/styles/variables/themes/light.scss +1 -0
- package/lib/src/legacy/components/inputs/NoInputDatePicker/NoInputDatePicker.styles.d.ts +0 -1
- 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 {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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={
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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. (
|
|
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
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
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:
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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={
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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:
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
|
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`
|
|
6
|
-
render(<Label label=
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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=
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
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. (
|
|
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
|
-
|
|
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
|
/>
|