@indico-data/design-system 2.8.0 → 2.10.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 +216 -4
- package/lib/index.d.ts +41 -2
- package/lib/index.esm.css +216 -4
- package/lib/index.esm.js +19 -3
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +20 -2
- package/lib/index.js.map +1 -1
- package/lib/src/components/forms/input/Input.d.ts +2 -1
- package/lib/src/components/forms/passwordInput/PasswordInput.d.ts +16 -0
- package/lib/src/components/forms/passwordInput/PasswordInput.stories.d.ts +11 -0
- package/lib/src/components/forms/passwordInput/__tests__/PasswordInput.test.d.ts +1 -0
- package/lib/src/components/forms/passwordInput/index.d.ts +1 -0
- package/lib/src/components/forms/textarea/Textarea.d.ts +22 -0
- package/lib/src/components/forms/textarea/Textarea.stories.d.ts +11 -0
- package/lib/src/components/forms/textarea/__tests__/Textarea.test.d.ts +1 -0
- package/lib/src/components/forms/textarea/index.d.ts +1 -0
- package/lib/src/components/index.d.ts +2 -0
- package/lib/src/index.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/forms/checkbox/styles/Checkbox.scss +1 -1
- package/src/components/forms/input/Input.stories.tsx +70 -69
- package/src/components/forms/input/Input.tsx +3 -1
- package/src/components/forms/input/styles/Input.scss +11 -3
- package/src/components/forms/passwordInput/PasswordInput.mdx +28 -0
- package/src/components/forms/passwordInput/PasswordInput.stories.tsx +269 -0
- package/src/components/forms/passwordInput/PasswordInput.tsx +82 -0
- package/src/components/forms/passwordInput/__tests__/PasswordInput.test.tsx +129 -0
- package/src/components/forms/passwordInput/index.ts +1 -0
- package/src/components/forms/passwordInput/styles/PasswordInput.scss +120 -0
- package/src/components/forms/textarea/Textarea.mdx +19 -0
- package/src/components/forms/textarea/Textarea.stories.tsx +363 -0
- package/src/components/forms/textarea/Textarea.tsx +85 -0
- package/src/components/forms/textarea/__tests__/Textarea.test.tsx +103 -0
- package/src/components/forms/textarea/index.ts +1 -0
- package/src/components/forms/textarea/styles/Textarea.scss +102 -0
- package/src/components/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/styles/index.scss +2 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { PasswordInput, PasswordInputProps } from './PasswordInput';
|
|
3
|
+
import { SetStateAction, useEffect, useState } from 'react';
|
|
4
|
+
import { iconNames } from 'build/generated/iconTypes';
|
|
5
|
+
|
|
6
|
+
const meta: Meta = {
|
|
7
|
+
title: 'Forms/PasswordInput',
|
|
8
|
+
component: PasswordInput,
|
|
9
|
+
argTypes: {
|
|
10
|
+
hasShowPassword: {
|
|
11
|
+
control: 'boolean',
|
|
12
|
+
description: 'Toggles the visibility of the password',
|
|
13
|
+
table: {
|
|
14
|
+
category: 'Props',
|
|
15
|
+
type: {
|
|
16
|
+
summary: 'boolean',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultValue: { summary: true },
|
|
20
|
+
},
|
|
21
|
+
onChange: {
|
|
22
|
+
control: false,
|
|
23
|
+
description: 'onChange event handler',
|
|
24
|
+
table: {
|
|
25
|
+
category: 'Callbacks',
|
|
26
|
+
type: {
|
|
27
|
+
summary: '(e: React.ChangeEvent<HTMLInputElement>) => void',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
action: 'onChange',
|
|
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
|
+
placeholder: {
|
|
53
|
+
control: 'text',
|
|
54
|
+
description: 'The placeholder for the password field',
|
|
55
|
+
table: {
|
|
56
|
+
category: 'Props',
|
|
57
|
+
type: {
|
|
58
|
+
summary: 'string',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
value: {
|
|
63
|
+
control: 'text',
|
|
64
|
+
description: 'The value for the password field',
|
|
65
|
+
table: {
|
|
66
|
+
category: 'Props',
|
|
67
|
+
type: {
|
|
68
|
+
summary: 'string',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
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
|
+
isDisabled: {
|
|
84
|
+
control: 'boolean',
|
|
85
|
+
description: 'Toggles the disabled state of the input',
|
|
86
|
+
table: {
|
|
87
|
+
category: 'Props',
|
|
88
|
+
type: {
|
|
89
|
+
summary: 'boolean',
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
defaultValue: { summary: 'false' },
|
|
93
|
+
},
|
|
94
|
+
errorList: {
|
|
95
|
+
control: false,
|
|
96
|
+
description: 'An array of error messages',
|
|
97
|
+
table: {
|
|
98
|
+
category: 'Props',
|
|
99
|
+
type: {
|
|
100
|
+
summary: 'string[]',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
defaultValue: { summary: '[]' },
|
|
104
|
+
},
|
|
105
|
+
helpText: {
|
|
106
|
+
control: 'text',
|
|
107
|
+
description: 'The help text for the password field',
|
|
108
|
+
table: {
|
|
109
|
+
category: 'Props',
|
|
110
|
+
type: {
|
|
111
|
+
summary: 'string',
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
},
|
|
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
|
+
ref: {
|
|
127
|
+
table: {
|
|
128
|
+
disable: true,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export default meta;
|
|
135
|
+
|
|
136
|
+
type Story = StoryObj<typeof PasswordInput>;
|
|
137
|
+
|
|
138
|
+
const defaultArgs = {
|
|
139
|
+
label: 'Enter your name',
|
|
140
|
+
name: 'first_name',
|
|
141
|
+
placeholder: 'Please enter a value',
|
|
142
|
+
} as PasswordInputProps;
|
|
143
|
+
|
|
144
|
+
export const Default: Story = {
|
|
145
|
+
args: {
|
|
146
|
+
isRequired: false,
|
|
147
|
+
helpText: 'This Is Help Text',
|
|
148
|
+
label: 'Label Name',
|
|
149
|
+
name: 'first_name',
|
|
150
|
+
placeholder: 'Please enter a value',
|
|
151
|
+
hasHiddenLabel: false,
|
|
152
|
+
hasShowPassword: true,
|
|
153
|
+
isDisabled: false,
|
|
154
|
+
errorList: [],
|
|
155
|
+
value: '',
|
|
156
|
+
},
|
|
157
|
+
render: (args) => {
|
|
158
|
+
const [value, setValue] = useState('');
|
|
159
|
+
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
setValue(args.value);
|
|
162
|
+
}, [args.value]);
|
|
163
|
+
|
|
164
|
+
const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
|
|
165
|
+
setValue(e.target.value);
|
|
166
|
+
};
|
|
167
|
+
return <PasswordInput {...args} value={value} onChange={handleChange} />;
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export const Errors: Story = {
|
|
172
|
+
args: {
|
|
173
|
+
...defaultArgs,
|
|
174
|
+
errorList: ['You require a password value.'],
|
|
175
|
+
},
|
|
176
|
+
render: (args) => {
|
|
177
|
+
const [value, setValue] = useState('');
|
|
178
|
+
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
setValue(args.value);
|
|
181
|
+
}, [args.value]);
|
|
182
|
+
|
|
183
|
+
const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
|
|
184
|
+
setValue(e.target.value);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
return <PasswordInput {...args} value={value} onChange={handleChange} />;
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
export const HiddenLabel: Story = {
|
|
192
|
+
args: {
|
|
193
|
+
...defaultArgs,
|
|
194
|
+
hasHiddenLabel: true,
|
|
195
|
+
},
|
|
196
|
+
render: (args) => {
|
|
197
|
+
const [value, setValue] = useState('');
|
|
198
|
+
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
setValue(args.value);
|
|
201
|
+
}, [args.value]);
|
|
202
|
+
|
|
203
|
+
const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
|
|
204
|
+
setValue(e.target.value);
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
return <PasswordInput {...args} value={value} onChange={handleChange} />;
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const HelpText: Story = {
|
|
212
|
+
args: {
|
|
213
|
+
...defaultArgs,
|
|
214
|
+
helpText: 'In order to submit the form, this field is required.',
|
|
215
|
+
},
|
|
216
|
+
render: (args) => {
|
|
217
|
+
const [value, setValue] = useState('');
|
|
218
|
+
|
|
219
|
+
useEffect(() => {
|
|
220
|
+
setValue(args.value);
|
|
221
|
+
}, [args.value]);
|
|
222
|
+
|
|
223
|
+
const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
|
|
224
|
+
setValue(e.target.value);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return <PasswordInput {...args} value={value} onChange={handleChange} />;
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export const Required: Story = {
|
|
232
|
+
args: {
|
|
233
|
+
...defaultArgs,
|
|
234
|
+
isRequired: true,
|
|
235
|
+
},
|
|
236
|
+
render: (args) => {
|
|
237
|
+
const [value, setValue] = useState('');
|
|
238
|
+
|
|
239
|
+
useEffect(() => {
|
|
240
|
+
setValue(args.value);
|
|
241
|
+
}, [args.value]);
|
|
242
|
+
|
|
243
|
+
const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
|
|
244
|
+
setValue(e.target.value);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return <PasswordInput {...args} value={value} onChange={handleChange} />;
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export const NoTogglePasswordVisibility: Story = {
|
|
252
|
+
args: {
|
|
253
|
+
...defaultArgs,
|
|
254
|
+
hasShowPassword: false,
|
|
255
|
+
},
|
|
256
|
+
render: (args) => {
|
|
257
|
+
const [value, setValue] = useState('');
|
|
258
|
+
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
setValue(args.value);
|
|
261
|
+
}, [args.value]);
|
|
262
|
+
|
|
263
|
+
const handleChange = (e: { target: { value: SetStateAction<string> } }) => {
|
|
264
|
+
setValue(e.target.value);
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
return <PasswordInput {...args} value={value} onChange={handleChange} />;
|
|
268
|
+
},
|
|
269
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Icon } from '@/components/icons';
|
|
3
|
+
import { Label } from '../subcomponents/Label';
|
|
4
|
+
import { ErrorList } from '../subcomponents/ErrorList';
|
|
5
|
+
|
|
6
|
+
export interface PasswordInputProps {
|
|
7
|
+
ref?: React.LegacyRef<HTMLInputElement>;
|
|
8
|
+
label: string;
|
|
9
|
+
name: string;
|
|
10
|
+
placeholder: string;
|
|
11
|
+
value: string;
|
|
12
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
13
|
+
isRequired?: boolean;
|
|
14
|
+
isDisabled?: boolean;
|
|
15
|
+
errorList?: string[];
|
|
16
|
+
helpText?: string;
|
|
17
|
+
hasHiddenLabel?: boolean;
|
|
18
|
+
hasShowPassword?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
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;
|
|
36
|
+
|
|
37
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
38
|
+
|
|
39
|
+
const handleShowPassword = () => {
|
|
40
|
+
setShowPassword((prevShowPassword) => !prevShowPassword);
|
|
41
|
+
};
|
|
42
|
+
|
|
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"
|
|
71
|
+
/>
|
|
72
|
+
)}
|
|
73
|
+
</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
|
+
};
|
|
@@ -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
|
+
errorList={['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
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Canvas, Meta, Controls } from '@storybook/blocks';
|
|
2
|
+
import * as Textarea from './Textarea.stories';
|
|
3
|
+
|
|
4
|
+
<Meta title="Forms/Textarea" name="Textarea" />
|
|
5
|
+
|
|
6
|
+
# Textarea
|
|
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***)
|
|
9
|
+
|
|
10
|
+
<Canvas
|
|
11
|
+
of={Textarea.Default}
|
|
12
|
+
source={{
|
|
13
|
+
code: `
|
|
14
|
+
<Textarea name="first_name" isRequired helpText="This Is Help Text" placeholder="This is a placeholder" value="''" onChange={handleChange} label="Label Name" />
|
|
15
|
+
`,
|
|
16
|
+
}}
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<Controls of={Textarea.Default} />
|