@indico-data/design-system 2.4.2 → 2.6.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/.storybook/preview.ts +4 -14
- package/lib/index.css +208 -0
- package/lib/index.d.ts +31 -3
- package/lib/index.esm.css +208 -0
- package/lib/index.esm.js +23 -1
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +24 -0
- package/lib/index.js.map +1 -1
- package/lib/src/components/forms/input/Input.d.ts +18 -0
- package/lib/src/components/forms/input/Input.stories.d.ts +12 -0
- package/lib/src/components/forms/input/__tests__/Input.test.d.ts +1 -0
- package/lib/src/components/forms/input/index.d.ts +1 -0
- package/lib/src/components/forms/radio/Radio.d.ts +11 -0
- package/lib/src/components/forms/radio/Radio.stories.d.ts +6 -0
- package/lib/src/components/forms/radio/__tests__/Radio.test.d.ts +1 -0
- package/lib/src/components/forms/radio/index.d.ts +1 -0
- package/lib/src/components/forms/subcomponents/ErrorList.d.ts +6 -0
- package/lib/src/components/forms/subcomponents/Label.d.ts +8 -0
- package/lib/src/components/forms/subcomponents/__tests__/ErrorList.test.d.ts +1 -0
- package/lib/src/components/forms/subcomponents/__tests__/Label.test.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.mdx +19 -0
- package/src/components/forms/input/Input.stories.tsx +301 -0
- package/src/components/forms/input/Input.tsx +86 -0
- package/src/components/forms/input/__tests__/Input.test.tsx +213 -0
- package/src/components/forms/input/index.ts +1 -0
- package/src/components/forms/input/styles/Input.scss +112 -0
- package/src/components/forms/radio/Radio.mdx +83 -0
- package/src/components/forms/radio/Radio.stories.tsx +121 -0
- package/src/components/forms/radio/Radio.tsx +47 -0
- package/src/components/forms/radio/__tests__/Radio.test.tsx +35 -0
- package/src/components/forms/radio/index.ts +1 -0
- package/src/components/forms/radio/styles/Radio.scss +98 -0
- package/src/components/forms/subcomponents/ErrorList.tsx +14 -0
- package/src/components/forms/subcomponents/Label.tsx +20 -0
- package/src/components/forms/subcomponents/__tests__/ErrorList.test.tsx +16 -0
- package/src/components/forms/subcomponents/__tests__/Label.test.tsx +33 -0
- package/src/components/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/styles/_typography.scss +29 -11
- package/src/styles/index.scss +2 -0
- package/src/styles/storybook.scss +15 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { IconName } from '@/types';
|
|
3
|
+
export interface InputProps {
|
|
4
|
+
ref?: React.LegacyRef<HTMLInputElement>;
|
|
5
|
+
label: string;
|
|
6
|
+
name: string;
|
|
7
|
+
placeholder: string;
|
|
8
|
+
value: string;
|
|
9
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
10
|
+
isRequired?: boolean;
|
|
11
|
+
isDisabled?: boolean;
|
|
12
|
+
errorList?: string[];
|
|
13
|
+
helpText?: string;
|
|
14
|
+
hasHiddenLabel?: boolean;
|
|
15
|
+
iconName?: IconName;
|
|
16
|
+
isClearable?: boolean;
|
|
17
|
+
}
|
|
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;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Input } from './Input';
|
|
3
|
+
declare const meta: Meta;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof Input>;
|
|
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 Clearable: Story;
|
|
11
|
+
export declare const Icon: Story;
|
|
12
|
+
export declare const Required: Story;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Input } from './Input';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface RadioProps {
|
|
3
|
+
ref?: React.LegacyRef<HTMLInputElement>;
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
name: string;
|
|
7
|
+
value?: string;
|
|
8
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
9
|
+
isDisabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare const Radio: ({ ref, id, label, name, value, onChange, isDisabled, ...rest }: RadioProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Radio } from './Radio';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/src/index.d.ts
CHANGED
|
@@ -7,3 +7,5 @@ export { Container, Row, Col } from './components/grid';
|
|
|
7
7
|
export { Button } from './components/button';
|
|
8
8
|
export { Icon } from './components/icons';
|
|
9
9
|
export { Table } from './components/table';
|
|
10
|
+
export { Input } from './components/forms/input';
|
|
11
|
+
export { Radio as RadioInput } from './components/forms/radio';
|
package/package.json
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Canvas, Meta, Controls } from '@storybook/blocks';
|
|
2
|
+
import * as Input from './Input.stories';
|
|
3
|
+
|
|
4
|
+
<Meta title="Forms/Input" name="Input" />
|
|
5
|
+
|
|
6
|
+
# Input
|
|
7
|
+
|
|
8
|
+
The 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={Input.Default}
|
|
12
|
+
source={{
|
|
13
|
+
code: `
|
|
14
|
+
<Input name="first_name" required={true} isRequired helpText="This Is Help Text" isClearable label="Label Name" iconName="user" />
|
|
15
|
+
`,
|
|
16
|
+
}}
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<Controls of={Input.Default} />
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Input, InputProps } from './Input';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { iconNames } from 'build/generated/iconTypes';
|
|
5
|
+
|
|
6
|
+
const meta: Meta = {
|
|
7
|
+
title: 'Forms/Input',
|
|
8
|
+
component: Input,
|
|
9
|
+
argTypes: {
|
|
10
|
+
onChange: {
|
|
11
|
+
control: false,
|
|
12
|
+
description: 'onChange event handler',
|
|
13
|
+
table: {
|
|
14
|
+
category: 'Callbacks',
|
|
15
|
+
type: {
|
|
16
|
+
summary: '(e: React.ChangeEvent<HTMLInputElement>) => void',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
action: 'onChange',
|
|
20
|
+
},
|
|
21
|
+
label: {
|
|
22
|
+
control: 'text',
|
|
23
|
+
description: 'The label for the input field',
|
|
24
|
+
table: {
|
|
25
|
+
category: 'Props',
|
|
26
|
+
type: {
|
|
27
|
+
summary: 'string',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultValue: { summary: '' },
|
|
31
|
+
},
|
|
32
|
+
name: {
|
|
33
|
+
control: 'text',
|
|
34
|
+
description: 'The name for the input field',
|
|
35
|
+
table: {
|
|
36
|
+
category: 'Props',
|
|
37
|
+
type: {
|
|
38
|
+
summary: 'string',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
defaultValue: { summary: '' },
|
|
42
|
+
},
|
|
43
|
+
placeholder: {
|
|
44
|
+
control: 'text',
|
|
45
|
+
description: 'The placeholder for the input field',
|
|
46
|
+
table: {
|
|
47
|
+
category: 'Props',
|
|
48
|
+
type: {
|
|
49
|
+
summary: 'string',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
defaultValue: { summary: '' },
|
|
53
|
+
},
|
|
54
|
+
value: {
|
|
55
|
+
control: 'text',
|
|
56
|
+
description: 'The value for the input field',
|
|
57
|
+
table: {
|
|
58
|
+
category: 'Props',
|
|
59
|
+
type: {
|
|
60
|
+
summary: 'string',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
defaultValue: { summary: '' },
|
|
64
|
+
},
|
|
65
|
+
isRequired: {
|
|
66
|
+
control: 'boolean',
|
|
67
|
+
description: 'Toggles the required astherisc on the label',
|
|
68
|
+
table: {
|
|
69
|
+
category: 'Props',
|
|
70
|
+
type: {
|
|
71
|
+
summary: 'boolean',
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
defaultValue: { summary: 'false' },
|
|
75
|
+
},
|
|
76
|
+
isDisabled: {
|
|
77
|
+
control: 'boolean',
|
|
78
|
+
description: 'Toggles the disabled state of the input',
|
|
79
|
+
table: {
|
|
80
|
+
category: 'Props',
|
|
81
|
+
type: {
|
|
82
|
+
summary: 'boolean',
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
defaultValue: { summary: 'false' },
|
|
86
|
+
},
|
|
87
|
+
errorList: {
|
|
88
|
+
control: false,
|
|
89
|
+
description: 'An array of error messages',
|
|
90
|
+
table: {
|
|
91
|
+
category: 'Props',
|
|
92
|
+
type: {
|
|
93
|
+
summary: 'string[]',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
defaultValue: { summary: '[]' },
|
|
97
|
+
},
|
|
98
|
+
helpText: {
|
|
99
|
+
control: 'text',
|
|
100
|
+
description: 'The help text for the input field',
|
|
101
|
+
table: {
|
|
102
|
+
category: 'Props',
|
|
103
|
+
type: {
|
|
104
|
+
summary: 'string',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
defaultValue: { summary: '' },
|
|
108
|
+
},
|
|
109
|
+
hasHiddenLabel: {
|
|
110
|
+
control: 'boolean',
|
|
111
|
+
description: 'Hides the label visually (retains it for screen readers)',
|
|
112
|
+
table: {
|
|
113
|
+
category: 'Props',
|
|
114
|
+
type: {
|
|
115
|
+
summary: 'boolean',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
defaultValue: { summary: 'false' },
|
|
119
|
+
},
|
|
120
|
+
iconName: {
|
|
121
|
+
control: 'select',
|
|
122
|
+
options: iconNames,
|
|
123
|
+
description: 'Adds an icon to the left hand side of the input field',
|
|
124
|
+
table: {
|
|
125
|
+
category: 'Props',
|
|
126
|
+
type: {
|
|
127
|
+
summary: 'string',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
defaultValue: { summary: '' },
|
|
131
|
+
},
|
|
132
|
+
isClearable: {
|
|
133
|
+
control: 'boolean',
|
|
134
|
+
description: 'Adds a clear x icon to the right hand side of the input field',
|
|
135
|
+
table: {
|
|
136
|
+
category: 'Props',
|
|
137
|
+
type: {
|
|
138
|
+
summary: 'boolean',
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
defaultValue: { summary: 'false' },
|
|
142
|
+
},
|
|
143
|
+
ref: {
|
|
144
|
+
table: {
|
|
145
|
+
disable: true,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export default meta;
|
|
152
|
+
|
|
153
|
+
type Story = StoryObj<typeof Input>;
|
|
154
|
+
|
|
155
|
+
const defaultArgs = {
|
|
156
|
+
label: 'Enter your name',
|
|
157
|
+
name: 'first_name',
|
|
158
|
+
placeholder: 'Please enter a value',
|
|
159
|
+
} as InputProps;
|
|
160
|
+
|
|
161
|
+
export const Default: Story = {
|
|
162
|
+
args: {
|
|
163
|
+
isRequired: true,
|
|
164
|
+
iconName: 'user',
|
|
165
|
+
helpText: 'This Is Help Text',
|
|
166
|
+
isClearable: true,
|
|
167
|
+
label: 'Label Name',
|
|
168
|
+
name: 'firsT_name',
|
|
169
|
+
placeholder: 'Please enter a value',
|
|
170
|
+
hasHiddenLabel: false,
|
|
171
|
+
isDisabled: false,
|
|
172
|
+
errorList: [],
|
|
173
|
+
value: '',
|
|
174
|
+
},
|
|
175
|
+
render: (args) => {
|
|
176
|
+
const [value, setValue] = useState('');
|
|
177
|
+
return (
|
|
178
|
+
<Input
|
|
179
|
+
{...args}
|
|
180
|
+
value={value}
|
|
181
|
+
onChange={(e) => {
|
|
182
|
+
setValue(e.target.value);
|
|
183
|
+
}}
|
|
184
|
+
/>
|
|
185
|
+
);
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
export const Errors: Story = {
|
|
190
|
+
args: {
|
|
191
|
+
...defaultArgs,
|
|
192
|
+
errorList: ['You require a username value.'],
|
|
193
|
+
},
|
|
194
|
+
render: (args) => {
|
|
195
|
+
const [value, setValue] = useState('');
|
|
196
|
+
return (
|
|
197
|
+
<Input
|
|
198
|
+
{...args}
|
|
199
|
+
value={value}
|
|
200
|
+
onChange={(e) => {
|
|
201
|
+
setValue(e.target.value);
|
|
202
|
+
}}
|
|
203
|
+
/>
|
|
204
|
+
);
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export const HiddenLabel: Story = {
|
|
209
|
+
args: {
|
|
210
|
+
...defaultArgs,
|
|
211
|
+
hasHiddenLabel: true,
|
|
212
|
+
},
|
|
213
|
+
render: (args) => {
|
|
214
|
+
const [value, setValue] = useState('');
|
|
215
|
+
return (
|
|
216
|
+
<Input
|
|
217
|
+
{...args}
|
|
218
|
+
value={value}
|
|
219
|
+
onChange={(e) => {
|
|
220
|
+
setValue(e.target.value);
|
|
221
|
+
}}
|
|
222
|
+
/>
|
|
223
|
+
);
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
export const HelpText: Story = {
|
|
228
|
+
args: {
|
|
229
|
+
...defaultArgs,
|
|
230
|
+
helpText: 'In order to submit the form, this field is required.',
|
|
231
|
+
},
|
|
232
|
+
render: (args) => {
|
|
233
|
+
const [value, setValue] = useState('');
|
|
234
|
+
return (
|
|
235
|
+
<Input
|
|
236
|
+
{...args}
|
|
237
|
+
value={value}
|
|
238
|
+
onChange={(e) => {
|
|
239
|
+
setValue(e.target.value);
|
|
240
|
+
}}
|
|
241
|
+
/>
|
|
242
|
+
);
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
export const Clearable: Story = {
|
|
247
|
+
args: {
|
|
248
|
+
...defaultArgs,
|
|
249
|
+
isClearable: true,
|
|
250
|
+
},
|
|
251
|
+
render: (args) => {
|
|
252
|
+
const [value, setValue] = useState('');
|
|
253
|
+
return (
|
|
254
|
+
<Input
|
|
255
|
+
{...args}
|
|
256
|
+
value={value}
|
|
257
|
+
onChange={(e) => {
|
|
258
|
+
setValue(e.target.value);
|
|
259
|
+
}}
|
|
260
|
+
/>
|
|
261
|
+
);
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
export const Icon: Story = {
|
|
266
|
+
args: {
|
|
267
|
+
...defaultArgs,
|
|
268
|
+
iconName: 'user',
|
|
269
|
+
},
|
|
270
|
+
render: (args) => {
|
|
271
|
+
const [value, setValue] = useState('');
|
|
272
|
+
return (
|
|
273
|
+
<Input
|
|
274
|
+
{...args}
|
|
275
|
+
value={value}
|
|
276
|
+
onChange={(e) => {
|
|
277
|
+
setValue(e.target.value);
|
|
278
|
+
}}
|
|
279
|
+
/>
|
|
280
|
+
);
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
export const Required: Story = {
|
|
285
|
+
args: {
|
|
286
|
+
...defaultArgs,
|
|
287
|
+
isRequired: true,
|
|
288
|
+
},
|
|
289
|
+
render: (args) => {
|
|
290
|
+
const [value, setValue] = useState('');
|
|
291
|
+
return (
|
|
292
|
+
<Input
|
|
293
|
+
{...args}
|
|
294
|
+
value={value}
|
|
295
|
+
onChange={(e) => {
|
|
296
|
+
setValue(e.target.value);
|
|
297
|
+
}}
|
|
298
|
+
/>
|
|
299
|
+
);
|
|
300
|
+
},
|
|
301
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Icon } from '@/components/icons';
|
|
3
|
+
import { IconName } from '@/types';
|
|
4
|
+
import { Label } from '../subcomponents/Label';
|
|
5
|
+
import { ErrorList } from '../subcomponents/ErrorList';
|
|
6
|
+
|
|
7
|
+
export interface InputProps {
|
|
8
|
+
ref?: React.LegacyRef<HTMLInputElement>;
|
|
9
|
+
label: string;
|
|
10
|
+
name: string;
|
|
11
|
+
placeholder: string;
|
|
12
|
+
value: string;
|
|
13
|
+
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
14
|
+
isRequired?: boolean;
|
|
15
|
+
isDisabled?: boolean;
|
|
16
|
+
errorList?: string[];
|
|
17
|
+
helpText?: string;
|
|
18
|
+
hasHiddenLabel?: boolean;
|
|
19
|
+
iconName?: IconName;
|
|
20
|
+
isClearable?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const Input = ({
|
|
24
|
+
ref,
|
|
25
|
+
label,
|
|
26
|
+
name,
|
|
27
|
+
placeholder,
|
|
28
|
+
value,
|
|
29
|
+
onChange,
|
|
30
|
+
isRequired,
|
|
31
|
+
isDisabled,
|
|
32
|
+
errorList,
|
|
33
|
+
helpText,
|
|
34
|
+
iconName,
|
|
35
|
+
hasHiddenLabel,
|
|
36
|
+
isClearable,
|
|
37
|
+
...rest
|
|
38
|
+
}: InputProps) => {
|
|
39
|
+
const hasErrors = errorList && errorList.length > 0;
|
|
40
|
+
const handleClear = () => {
|
|
41
|
+
onChange({ target: { value: '' } } as React.ChangeEvent<HTMLInputElement>);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className="form-control">
|
|
46
|
+
<Label label={label} name={name} isRequired={isRequired} hasHiddenLabel={hasHiddenLabel} />
|
|
47
|
+
<div className="input-wrapper">
|
|
48
|
+
{iconName && (
|
|
49
|
+
<Icon name={iconName} data-testid={`${name}-embedded-icon`} className="embedded-icon" />
|
|
50
|
+
)}
|
|
51
|
+
<input
|
|
52
|
+
ref={ref}
|
|
53
|
+
data-testid={`form-input-${name}`}
|
|
54
|
+
name={name}
|
|
55
|
+
type="text"
|
|
56
|
+
disabled={isDisabled}
|
|
57
|
+
required={isRequired}
|
|
58
|
+
placeholder={placeholder}
|
|
59
|
+
value={value}
|
|
60
|
+
onChange={onChange}
|
|
61
|
+
className={`input ${hasErrors ? 'error' : ''} ${iconName ? 'input--has-icon' : ''}`}
|
|
62
|
+
aria-invalid={hasErrors}
|
|
63
|
+
aria-describedby={hasErrors || helpText ? `${name}-helper` : undefined}
|
|
64
|
+
aria-required={isRequired}
|
|
65
|
+
aria-label={label}
|
|
66
|
+
{...rest}
|
|
67
|
+
/>
|
|
68
|
+
{isClearable && (
|
|
69
|
+
<Icon
|
|
70
|
+
name="x-close"
|
|
71
|
+
data-testid={`${name}-clearable-icon`}
|
|
72
|
+
size="sm"
|
|
73
|
+
onClick={handleClear}
|
|
74
|
+
className="clearable-icon"
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
{hasErrors && <ErrorList errorList={errorList} name={name} />}
|
|
79
|
+
{helpText && (
|
|
80
|
+
<div data-testid={`${name}-help-text`} className="help-text" id={`${name}-helper`}>
|
|
81
|
+
{helpText}
|
|
82
|
+
</div>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
};
|