@indico-data/design-system 2.9.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 +112 -0
- package/lib/index.d.ts +41 -2
- package/lib/index.esm.css +112 -0
- 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/index.d.ts +2 -0
- package/lib/src/index.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/forms/input/Input.stories.tsx +0 -5
- package/src/components/forms/input/Input.tsx +3 -1
- package/src/components/forms/input/styles/Input.scss +1 -0
- 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/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/styles/index.scss +1 -0
|
@@ -14,5 +14,6 @@ export interface InputProps {
|
|
|
14
14
|
hasHiddenLabel?: boolean;
|
|
15
15
|
iconName?: IconName;
|
|
16
16
|
isClearable?: boolean;
|
|
17
|
+
className?: string;
|
|
17
18
|
}
|
|
18
|
-
export declare const Input: ({ ref, label, name, placeholder, value, onChange, isRequired, isDisabled, errorList, helpText, iconName, hasHiddenLabel, isClearable, ...rest }: InputProps) => import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare const Input: ({ ref, label, name, placeholder, value, onChange, isRequired, isDisabled, errorList, helpText, iconName, hasHiddenLabel, isClearable, className, ...rest }: InputProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface PasswordInputProps {
|
|
3
|
+
ref?: React.LegacyRef<HTMLInputElement>;
|
|
4
|
+
label: string;
|
|
5
|
+
name: string;
|
|
6
|
+
placeholder: string;
|
|
7
|
+
value: string;
|
|
8
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
9
|
+
isRequired?: boolean;
|
|
10
|
+
isDisabled?: boolean;
|
|
11
|
+
errorList?: string[];
|
|
12
|
+
helpText?: string;
|
|
13
|
+
hasHiddenLabel?: boolean;
|
|
14
|
+
hasShowPassword?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare const PasswordInput: ({ label, name, placeholder, value, onChange, isRequired, isDisabled, errorList, helpText, hasHiddenLabel, hasShowPassword, ...rest }: PasswordInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { PasswordInput } from './PasswordInput';
|
|
3
|
+
declare const meta: Meta;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof PasswordInput>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const Errors: Story;
|
|
8
|
+
export declare const HiddenLabel: Story;
|
|
9
|
+
export declare const HelpText: Story;
|
|
10
|
+
export declare const Required: Story;
|
|
11
|
+
export declare const NoTogglePasswordVisibility: Story;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PasswordInput } from './PasswordInput';
|
|
@@ -6,3 +6,5 @@ export { Input } from './forms/input';
|
|
|
6
6
|
export { Radio } from './forms/radio';
|
|
7
7
|
export { Checkbox } from './forms/checkbox';
|
|
8
8
|
export { Toggle } from './forms/toggle';
|
|
9
|
+
export { Textarea } from './forms/textarea';
|
|
10
|
+
export { PasswordInput } from './forms/passwordInput';
|
package/lib/src/index.d.ts
CHANGED
|
@@ -11,3 +11,5 @@ export { Input } from './components/forms/input';
|
|
|
11
11
|
export { Radio as RadioInput } from './components/forms/radio';
|
|
12
12
|
export { Checkbox } from './components/forms/checkbox';
|
|
13
13
|
export { Toggle as ToggleInput } from './components/forms/toggle';
|
|
14
|
+
export { Textarea } from './components/forms/textarea';
|
|
15
|
+
export { PasswordInput } from './components/forms/passwordInput';
|
package/package.json
CHANGED
|
@@ -27,7 +27,6 @@ const meta: Meta = {
|
|
|
27
27
|
summary: 'string',
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
|
-
defaultValue: { summary: '' },
|
|
31
30
|
},
|
|
32
31
|
name: {
|
|
33
32
|
control: 'text',
|
|
@@ -38,7 +37,6 @@ const meta: Meta = {
|
|
|
38
37
|
summary: 'string',
|
|
39
38
|
},
|
|
40
39
|
},
|
|
41
|
-
defaultValue: { summary: '' },
|
|
42
40
|
},
|
|
43
41
|
placeholder: {
|
|
44
42
|
control: 'text',
|
|
@@ -49,7 +47,6 @@ const meta: Meta = {
|
|
|
49
47
|
summary: 'string',
|
|
50
48
|
},
|
|
51
49
|
},
|
|
52
|
-
defaultValue: { summary: '' },
|
|
53
50
|
},
|
|
54
51
|
value: {
|
|
55
52
|
control: 'text',
|
|
@@ -60,7 +57,6 @@ const meta: Meta = {
|
|
|
60
57
|
summary: 'string',
|
|
61
58
|
},
|
|
62
59
|
},
|
|
63
|
-
defaultValue: { summary: '' },
|
|
64
60
|
},
|
|
65
61
|
isRequired: {
|
|
66
62
|
control: 'boolean',
|
|
@@ -104,7 +100,6 @@ const meta: Meta = {
|
|
|
104
100
|
summary: 'string',
|
|
105
101
|
},
|
|
106
102
|
},
|
|
107
|
-
defaultValue: { summary: '' },
|
|
108
103
|
},
|
|
109
104
|
hasHiddenLabel: {
|
|
110
105
|
control: 'boolean',
|
|
@@ -18,6 +18,7 @@ export interface InputProps {
|
|
|
18
18
|
hasHiddenLabel?: boolean;
|
|
19
19
|
iconName?: IconName;
|
|
20
20
|
isClearable?: boolean;
|
|
21
|
+
className?: string;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export const Input = ({
|
|
@@ -34,6 +35,7 @@ export const Input = ({
|
|
|
34
35
|
iconName,
|
|
35
36
|
hasHiddenLabel,
|
|
36
37
|
isClearable,
|
|
38
|
+
className = '',
|
|
37
39
|
...rest
|
|
38
40
|
}: InputProps) => {
|
|
39
41
|
const hasErrors = errorList && errorList.length > 0;
|
|
@@ -58,7 +60,7 @@ export const Input = ({
|
|
|
58
60
|
placeholder={placeholder}
|
|
59
61
|
value={value}
|
|
60
62
|
onChange={onChange}
|
|
61
|
-
className={`input ${hasErrors ? 'error' : ''} ${iconName ? 'input--has-icon' : ''}`}
|
|
63
|
+
className={`input ${hasErrors ? 'error' : ''} ${iconName ? 'input--has-icon' : ''} ${className}`}
|
|
62
64
|
aria-invalid={hasErrors}
|
|
63
65
|
aria-describedby={hasErrors || helpText ? `${name}-helper` : undefined}
|
|
64
66
|
aria-required={isRequired}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Canvas, Meta, Controls } from '@storybook/blocks';
|
|
2
|
+
import * as PasswordInput from './PasswordInput.stories';
|
|
3
|
+
|
|
4
|
+
<Meta title="Forms/PasswordInput" name="PasswordInput" />
|
|
5
|
+
|
|
6
|
+
# Password Input
|
|
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***)
|
|
9
|
+
|
|
10
|
+
<Canvas
|
|
11
|
+
of={PasswordInput.Default}
|
|
12
|
+
source={{
|
|
13
|
+
code: `
|
|
14
|
+
<PasswordInput
|
|
15
|
+
name="first_name"
|
|
16
|
+
isRequired
|
|
17
|
+
hasShowPassword
|
|
18
|
+
placeholder="Placeholder Text"
|
|
19
|
+
value=''
|
|
20
|
+
onChange={() => {}}
|
|
21
|
+
helpText="This Is Help Text"
|
|
22
|
+
label="Label Name"
|
|
23
|
+
/>
|
|
24
|
+
`,
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
27
|
+
|
|
28
|
+
<Controls of={PasswordInput.Default} />
|
|
@@ -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';
|