@khanacademy/wonder-blocks-form 3.1.11 → 3.1.13
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/CHANGELOG.md +41 -0
- package/dist/components/checkbox-core.d.ts +16 -0
- package/dist/components/checkbox-core.js.flow +26 -0
- package/dist/components/checkbox-group.d.ts +84 -0
- package/dist/components/checkbox-group.js.flow +103 -0
- package/dist/components/checkbox.d.ts +83 -0
- package/dist/components/checkbox.js.flow +106 -0
- package/dist/components/choice-internal.d.ts +63 -0
- package/dist/components/choice-internal.js.flow +100 -0
- package/dist/components/choice.d.ts +127 -0
- package/dist/components/choice.js.flow +161 -0
- package/dist/components/field-heading.d.ts +50 -0
- package/dist/components/field-heading.js.flow +64 -0
- package/dist/components/group-styles.d.ts +3 -0
- package/dist/components/group-styles.js.flow +10 -0
- package/dist/components/labeled-text-field.d.ts +169 -0
- package/dist/components/labeled-text-field.js.flow +211 -0
- package/dist/components/radio-core.d.ts +15 -0
- package/dist/components/radio-core.js.flow +26 -0
- package/dist/components/radio-group.d.ts +85 -0
- package/dist/components/radio-group.js.flow +104 -0
- package/dist/components/radio.d.ts +68 -0
- package/dist/components/radio.js.flow +92 -0
- package/dist/components/text-field.d.ts +146 -0
- package/dist/components/text-field.js.flow +186 -0
- package/dist/es/index.js +258 -224
- package/dist/index.d.ts +7 -0
- package/dist/index.js +281 -249
- package/dist/index.js.flow +21 -2
- package/dist/util/types.d.ts +62 -0
- package/dist/util/types.js.flow +138 -0
- package/package.json +10 -10
- package/src/__tests__/{custom-snapshot.test.js → custom-snapshot.test.tsx} +8 -9
- package/src/components/__tests__/{checkbox-group.test.js → checkbox-group.test.tsx} +5 -5
- package/src/components/__tests__/{field-heading.test.js → field-heading.test.tsx} +0 -1
- package/src/components/__tests__/{labeled-text-field.test.js → labeled-text-field.test.tsx} +4 -5
- package/src/components/__tests__/{radio-group.test.js → radio-group.test.tsx} +8 -8
- package/src/components/__tests__/{text-field.test.js → text-field.test.tsx} +22 -18
- package/src/components/{checkbox-core.js → checkbox-core.tsx} +12 -15
- package/src/components/{checkbox-group.js → checkbox-group.tsx} +20 -23
- package/src/components/{checkbox.js → checkbox.tsx} +18 -32
- package/src/components/{choice-internal.js → choice-internal.tsx} +25 -39
- package/src/components/{choice.js → choice.tsx} +24 -37
- package/src/components/{field-heading.js → field-heading.tsx} +16 -23
- package/src/components/{group-styles.js → group-styles.ts} +0 -1
- package/src/components/{labeled-text-field.js → labeled-text-field.tsx} +54 -69
- package/src/components/{radio-core.js → radio-core.tsx} +13 -16
- package/src/components/{radio-group.js → radio-group.tsx} +20 -23
- package/src/components/{radio.js → radio.tsx} +18 -32
- package/src/components/{text-field.js → text-field.tsx} +53 -64
- package/src/{index.js → index.ts} +0 -1
- package/src/util/{types.js → types.ts} +32 -35
- package/tsconfig.json +19 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/src/__docs__/_overview_.stories.mdx +0 -15
- package/src/components/__docs__/checkbox-accessibility.stories.mdx +0 -147
- package/src/components/__docs__/checkbox-group.stories.js +0 -300
- package/src/components/__docs__/checkbox.stories.js +0 -167
- package/src/components/__docs__/choice.stories.js +0 -86
- package/src/components/__docs__/labeled-text-field.argtypes.js +0 -248
- package/src/components/__docs__/labeled-text-field.stories.js +0 -709
- package/src/components/__docs__/radio-group.stories.js +0 -217
- package/src/components/__docs__/radio.stories.js +0 -161
- package/src/components/__docs__/text-field.argtypes.js +0 -206
- package/src/components/__docs__/text-field.stories.js +0 -780
- /package/src/__tests__/__snapshots__/{custom-snapshot.test.js.snap → custom-snapshot.test.tsx.snap} +0 -0
|
@@ -1,780 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
import {StyleSheet} from "aphrodite";
|
|
4
|
-
|
|
5
|
-
import {View, Text as _Text} from "@khanacademy/wonder-blocks-core";
|
|
6
|
-
import Color from "@khanacademy/wonder-blocks-color";
|
|
7
|
-
import {Strut} from "@khanacademy/wonder-blocks-layout";
|
|
8
|
-
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
9
|
-
import {TextField} from "@khanacademy/wonder-blocks-form";
|
|
10
|
-
import Button from "@khanacademy/wonder-blocks-button";
|
|
11
|
-
|
|
12
|
-
import type {StoryComponentType} from "@storybook/react";
|
|
13
|
-
|
|
14
|
-
import ComponentInfo from "../../../../../.storybook/components/component-info";
|
|
15
|
-
import {name, version} from "../../../package.json";
|
|
16
|
-
import TextFieldArgTypes from "./text-field.argtypes";
|
|
17
|
-
|
|
18
|
-
export default {
|
|
19
|
-
title: "Form / TextField",
|
|
20
|
-
component: TextField,
|
|
21
|
-
parameters: {
|
|
22
|
-
componentSubtitle: ((
|
|
23
|
-
<ComponentInfo name={name} version={version} />
|
|
24
|
-
): any),
|
|
25
|
-
},
|
|
26
|
-
argTypes: TextFieldArgTypes,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export const Default: StoryComponentType = (args) => {
|
|
30
|
-
return <TextField {...args} />;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
Default.args = {
|
|
34
|
-
id: "some-id",
|
|
35
|
-
type: "text",
|
|
36
|
-
value: "",
|
|
37
|
-
disabled: false,
|
|
38
|
-
placeholder: "",
|
|
39
|
-
required: false,
|
|
40
|
-
light: false,
|
|
41
|
-
testId: "",
|
|
42
|
-
readOnly: false,
|
|
43
|
-
autoComplete: "off",
|
|
44
|
-
validate: () => {},
|
|
45
|
-
onValidate: () => {},
|
|
46
|
-
onChange: () => {},
|
|
47
|
-
onKeyDown: () => {},
|
|
48
|
-
onFocus: () => {},
|
|
49
|
-
onBlur: () => {},
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export const Text: StoryComponentType = () => {
|
|
53
|
-
const [value, setValue] = React.useState("");
|
|
54
|
-
|
|
55
|
-
const handleChange = (newValue: string) => {
|
|
56
|
-
setValue(newValue);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
60
|
-
if (event.key === "Enter") {
|
|
61
|
-
event.currentTarget.blur();
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<TextField
|
|
67
|
-
id="tf-1"
|
|
68
|
-
type="text"
|
|
69
|
-
value={value}
|
|
70
|
-
placeholder="Text"
|
|
71
|
-
onChange={handleChange}
|
|
72
|
-
onKeyDown={handleKeyDown}
|
|
73
|
-
/>
|
|
74
|
-
);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
Text.parameters = {
|
|
78
|
-
docs: {
|
|
79
|
-
storyDescription:
|
|
80
|
-
"An input field with type `text` takes all kinds of characters.",
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export const Required: StoryComponentType = () => {
|
|
85
|
-
const [value, setValue] = React.useState("");
|
|
86
|
-
|
|
87
|
-
const handleChange = (newValue: string) => {
|
|
88
|
-
setValue(newValue);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
92
|
-
if (event.key === "Enter") {
|
|
93
|
-
event.currentTarget.blur();
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<TextField
|
|
99
|
-
id="tf-2"
|
|
100
|
-
type="text"
|
|
101
|
-
value={value}
|
|
102
|
-
onChange={handleChange}
|
|
103
|
-
onKeyDown={handleKeyDown}
|
|
104
|
-
required={true}
|
|
105
|
-
/>
|
|
106
|
-
);
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
Required.parameters = {
|
|
110
|
-
docs: {
|
|
111
|
-
storyDescription: `A required field will have error styling if the
|
|
112
|
-
field is left blank. To observe this, type something into the
|
|
113
|
-
field, backspace all the way, and then shift focus out of the field.`,
|
|
114
|
-
},
|
|
115
|
-
chromatic: {
|
|
116
|
-
// Disabling snapshot because it doesn't show the error style
|
|
117
|
-
// until after the user interacts with this field.
|
|
118
|
-
disableSnapshot: true,
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
export const Number: StoryComponentType = () => {
|
|
123
|
-
const [value, setValue] = React.useState("12345");
|
|
124
|
-
|
|
125
|
-
const handleChange = (newValue: string) => {
|
|
126
|
-
setValue(newValue);
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
130
|
-
if (event.key === "Enter") {
|
|
131
|
-
event.currentTarget.blur();
|
|
132
|
-
}
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
return (
|
|
136
|
-
<TextField
|
|
137
|
-
id="tf-3"
|
|
138
|
-
type="number"
|
|
139
|
-
value={value}
|
|
140
|
-
placeholder="Number"
|
|
141
|
-
onChange={handleChange}
|
|
142
|
-
onKeyDown={handleKeyDown}
|
|
143
|
-
/>
|
|
144
|
-
);
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
Number.parameters = {
|
|
148
|
-
docs: {
|
|
149
|
-
storyDescription:
|
|
150
|
-
"An input field with type `number` will only take numeric characters as input.",
|
|
151
|
-
},
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
export const Password: StoryComponentType = () => {
|
|
155
|
-
const [value, setValue] = React.useState("Password123");
|
|
156
|
-
const [errorMessage, setErrorMessage] = React.useState();
|
|
157
|
-
const [focused, setFocused] = React.useState(false);
|
|
158
|
-
|
|
159
|
-
const handleChange = (newValue: string) => {
|
|
160
|
-
setValue(newValue);
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const validate = (value: string) => {
|
|
164
|
-
if (value.length < 8) {
|
|
165
|
-
return "Password must be at least 8 characters long";
|
|
166
|
-
}
|
|
167
|
-
if (!/\d/.test(value)) {
|
|
168
|
-
return "Password must contain a numeric value";
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
const handleValidate = (errorMessage: ?string) => {
|
|
173
|
-
setErrorMessage(errorMessage);
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
177
|
-
if (event.key === "Enter") {
|
|
178
|
-
event.currentTarget.blur();
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const handleFocus = () => {
|
|
183
|
-
setFocused(true);
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
const handleBlur = () => {
|
|
187
|
-
setFocused(false);
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
return (
|
|
191
|
-
<View>
|
|
192
|
-
<TextField
|
|
193
|
-
id="tf-4"
|
|
194
|
-
type="password"
|
|
195
|
-
value={value}
|
|
196
|
-
placeholder="Password"
|
|
197
|
-
validate={validate}
|
|
198
|
-
onValidate={handleValidate}
|
|
199
|
-
onChange={handleChange}
|
|
200
|
-
onKeyDown={handleKeyDown}
|
|
201
|
-
onFocus={handleFocus}
|
|
202
|
-
onBlur={handleBlur}
|
|
203
|
-
/>
|
|
204
|
-
{!focused && errorMessage && (
|
|
205
|
-
<View>
|
|
206
|
-
<Strut size={Spacing.xSmall_8} />
|
|
207
|
-
<_Text style={styles.errorMessage}>{errorMessage}</_Text>
|
|
208
|
-
</View>
|
|
209
|
-
)}
|
|
210
|
-
</View>
|
|
211
|
-
);
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
Password.parameters = {
|
|
215
|
-
docs: {
|
|
216
|
-
storyDescription: `An input field with type \`password\` will
|
|
217
|
-
obscure the input value. It also often contains validation.
|
|
218
|
-
In this example, the password must be over 8 characters long and
|
|
219
|
-
must contain a numeric value.`,
|
|
220
|
-
},
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
export const Email: StoryComponentType = () => {
|
|
224
|
-
const [value, setValue] = React.useState("khan@khanacademy.org");
|
|
225
|
-
const [errorMessage, setErrorMessage] = React.useState();
|
|
226
|
-
const [focused, setFocused] = React.useState(false);
|
|
227
|
-
|
|
228
|
-
const handleChange = (newValue: string) => {
|
|
229
|
-
setValue(newValue);
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
const validate = (value: string) => {
|
|
233
|
-
const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/;
|
|
234
|
-
if (!emailRegex.test(value)) {
|
|
235
|
-
return "Please enter a valid email";
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const handleValidate = (errorMessage: ?string) => {
|
|
240
|
-
setErrorMessage(errorMessage);
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
244
|
-
if (event.key === "Enter") {
|
|
245
|
-
event.currentTarget.blur();
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
const handleFocus = () => {
|
|
250
|
-
setFocused(true);
|
|
251
|
-
};
|
|
252
|
-
|
|
253
|
-
const handleBlur = () => {
|
|
254
|
-
setFocused(false);
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
return (
|
|
258
|
-
<View>
|
|
259
|
-
<TextField
|
|
260
|
-
id="tf-5"
|
|
261
|
-
type="email"
|
|
262
|
-
value={value}
|
|
263
|
-
placeholder="Email"
|
|
264
|
-
validate={validate}
|
|
265
|
-
onValidate={handleValidate}
|
|
266
|
-
onChange={handleChange}
|
|
267
|
-
onKeyDown={handleKeyDown}
|
|
268
|
-
onFocus={handleFocus}
|
|
269
|
-
onBlur={handleBlur}
|
|
270
|
-
/>
|
|
271
|
-
{!focused && errorMessage && (
|
|
272
|
-
<View>
|
|
273
|
-
<Strut size={Spacing.xSmall_8} />
|
|
274
|
-
<_Text style={styles.errorMessage}>{errorMessage}</_Text>
|
|
275
|
-
</View>
|
|
276
|
-
)}
|
|
277
|
-
</View>
|
|
278
|
-
);
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
Email.parameters = {
|
|
282
|
-
docs: {
|
|
283
|
-
storyDescription: `An input field with type \`email\` will automatically
|
|
284
|
-
validate an input on submit to ensure it's either formatted properly
|
|
285
|
-
or blank. \`TextField\` will run validation on blur if the
|
|
286
|
-
\`validate\` prop is passed in, as in this example.`,
|
|
287
|
-
},
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
export const Telephone: StoryComponentType = () => {
|
|
291
|
-
const [value, setValue] = React.useState("123-456-7890");
|
|
292
|
-
const [errorMessage, setErrorMessage] = React.useState();
|
|
293
|
-
const [focused, setFocused] = React.useState(false);
|
|
294
|
-
|
|
295
|
-
const handleChange = (newValue: string) => {
|
|
296
|
-
setValue(newValue);
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
const validate = (value: string) => {
|
|
300
|
-
const telRegex = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/;
|
|
301
|
-
if (!telRegex.test(value)) {
|
|
302
|
-
return "Invalid US telephone number";
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
const handleValidate = (errorMessage: ?string) => {
|
|
307
|
-
setErrorMessage(errorMessage);
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
311
|
-
if (event.key === "Enter") {
|
|
312
|
-
event.currentTarget.blur();
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
const handleFocus = () => {
|
|
317
|
-
setFocused(true);
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
const handleBlur = () => {
|
|
321
|
-
setFocused(false);
|
|
322
|
-
};
|
|
323
|
-
|
|
324
|
-
return (
|
|
325
|
-
<View>
|
|
326
|
-
<TextField
|
|
327
|
-
id="tf-6"
|
|
328
|
-
type="tel"
|
|
329
|
-
value={value}
|
|
330
|
-
placeholder="Telephone"
|
|
331
|
-
validate={validate}
|
|
332
|
-
onValidate={handleValidate}
|
|
333
|
-
onChange={handleChange}
|
|
334
|
-
onKeyDown={handleKeyDown}
|
|
335
|
-
onFocus={handleFocus}
|
|
336
|
-
onBlur={handleBlur}
|
|
337
|
-
/>
|
|
338
|
-
{!focused && errorMessage && (
|
|
339
|
-
<View>
|
|
340
|
-
<Strut size={Spacing.xSmall_8} />
|
|
341
|
-
<_Text style={styles.errorMessage}>{errorMessage}</_Text>
|
|
342
|
-
</View>
|
|
343
|
-
)}
|
|
344
|
-
</View>
|
|
345
|
-
);
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
Telephone.parameters = {
|
|
349
|
-
docs: {
|
|
350
|
-
storyDescription: `An input field with type \`tel\` will NOT
|
|
351
|
-
validate an input on submit by default as telephone numbers
|
|
352
|
-
can vary considerably. \`TextField\` will run validation on blur
|
|
353
|
-
if the \`validate\` prop is passed in, as in this example.`,
|
|
354
|
-
},
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
export const Error: StoryComponentType = () => {
|
|
358
|
-
const [value, setValue] = React.useState("khan");
|
|
359
|
-
const [errorMessage, setErrorMessage] = React.useState();
|
|
360
|
-
const [focused, setFocused] = React.useState(false);
|
|
361
|
-
|
|
362
|
-
const handleChange = (newValue: string) => {
|
|
363
|
-
setValue(newValue);
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
const validate = (value: string) => {
|
|
367
|
-
const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/;
|
|
368
|
-
if (!emailRegex.test(value)) {
|
|
369
|
-
return "Please enter a valid email";
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
const handleValidate = (errorMessage: ?string) => {
|
|
374
|
-
setErrorMessage(errorMessage);
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
378
|
-
if (event.key === "Enter") {
|
|
379
|
-
event.currentTarget.blur();
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
const handleFocus = () => {
|
|
384
|
-
setFocused(true);
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
const handleBlur = () => {
|
|
388
|
-
setFocused(false);
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
return (
|
|
392
|
-
<View>
|
|
393
|
-
<TextField
|
|
394
|
-
id="tf-7"
|
|
395
|
-
type="email"
|
|
396
|
-
value={value}
|
|
397
|
-
placeholder="Email"
|
|
398
|
-
validate={validate}
|
|
399
|
-
onValidate={handleValidate}
|
|
400
|
-
onChange={handleChange}
|
|
401
|
-
onKeyDown={handleKeyDown}
|
|
402
|
-
onFocus={handleFocus}
|
|
403
|
-
onBlur={handleBlur}
|
|
404
|
-
/>
|
|
405
|
-
{!focused && errorMessage && (
|
|
406
|
-
<View>
|
|
407
|
-
<Strut size={Spacing.xSmall_8} />
|
|
408
|
-
<_Text style={styles.errorMessage}>{errorMessage}</_Text>
|
|
409
|
-
</View>
|
|
410
|
-
)}
|
|
411
|
-
</View>
|
|
412
|
-
);
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
Error.parameters = {
|
|
416
|
-
docs: {
|
|
417
|
-
storyDescription: `If an input value fails validation,
|
|
418
|
-
\`TextField\` will have error styling.`,
|
|
419
|
-
},
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
export const Light: StoryComponentType = () => {
|
|
423
|
-
const [value, setValue] = React.useState("khan@khanacademy.org");
|
|
424
|
-
const [errorMessage, setErrorMessage] = React.useState();
|
|
425
|
-
const [focused, setFocused] = React.useState(false);
|
|
426
|
-
|
|
427
|
-
const handleChange = (newValue: string) => {
|
|
428
|
-
setValue(newValue);
|
|
429
|
-
};
|
|
430
|
-
|
|
431
|
-
const validate = (value: string) => {
|
|
432
|
-
const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/;
|
|
433
|
-
if (!emailRegex.test(value)) {
|
|
434
|
-
return "Please enter a valid email";
|
|
435
|
-
}
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
const handleValidate = (errorMessage: ?string) => {
|
|
439
|
-
setErrorMessage(errorMessage);
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
443
|
-
if (event.key === "Enter") {
|
|
444
|
-
event.currentTarget.blur();
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
|
|
448
|
-
const handleFocus = () => {
|
|
449
|
-
setFocused(true);
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
const handleBlur = () => {
|
|
453
|
-
setFocused(false);
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
return (
|
|
457
|
-
<View style={styles.darkBackground}>
|
|
458
|
-
<TextField
|
|
459
|
-
id="tf-9"
|
|
460
|
-
type="email"
|
|
461
|
-
value={value}
|
|
462
|
-
placeholder="Email"
|
|
463
|
-
light={true}
|
|
464
|
-
validate={validate}
|
|
465
|
-
onValidate={handleValidate}
|
|
466
|
-
onChange={handleChange}
|
|
467
|
-
onKeyDown={handleKeyDown}
|
|
468
|
-
onFocus={handleFocus}
|
|
469
|
-
onBlur={handleBlur}
|
|
470
|
-
/>
|
|
471
|
-
{!focused && errorMessage && (
|
|
472
|
-
<View>
|
|
473
|
-
<Strut size={Spacing.xSmall_8} />
|
|
474
|
-
<_Text style={styles.errorMessageLight}>
|
|
475
|
-
{errorMessage}
|
|
476
|
-
</_Text>
|
|
477
|
-
</View>
|
|
478
|
-
)}
|
|
479
|
-
</View>
|
|
480
|
-
);
|
|
481
|
-
};
|
|
482
|
-
|
|
483
|
-
Light.parameters = {
|
|
484
|
-
docs: {
|
|
485
|
-
storyDescription: `If the \`light\` prop is set to true,
|
|
486
|
-
\`TextField\` will have light styling. This is intended to be used
|
|
487
|
-
on a dark background. There is also a specific light styling for the
|
|
488
|
-
error state, as seen in the \`ErrorLight\` story.`,
|
|
489
|
-
},
|
|
490
|
-
};
|
|
491
|
-
|
|
492
|
-
export const ErrorLight: StoryComponentType = () => {
|
|
493
|
-
const [value, setValue] = React.useState("khan");
|
|
494
|
-
const [errorMessage, setErrorMessage] = React.useState();
|
|
495
|
-
const [focused, setFocused] = React.useState(false);
|
|
496
|
-
|
|
497
|
-
const handleChange = (newValue: string) => {
|
|
498
|
-
setValue(newValue);
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
const validate = (value: string) => {
|
|
502
|
-
const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/;
|
|
503
|
-
if (!emailRegex.test(value)) {
|
|
504
|
-
return "Please enter a valid email";
|
|
505
|
-
}
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
const handleValidate = (errorMessage: ?string) => {
|
|
509
|
-
setErrorMessage(errorMessage);
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
513
|
-
if (event.key === "Enter") {
|
|
514
|
-
event.currentTarget.blur();
|
|
515
|
-
}
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
const handleFocus = () => {
|
|
519
|
-
setFocused(true);
|
|
520
|
-
};
|
|
521
|
-
|
|
522
|
-
const handleBlur = () => {
|
|
523
|
-
setFocused(false);
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
return (
|
|
527
|
-
<View style={styles.darkBackground}>
|
|
528
|
-
<TextField
|
|
529
|
-
id="tf-7"
|
|
530
|
-
type="email"
|
|
531
|
-
value={value}
|
|
532
|
-
placeholder="Email"
|
|
533
|
-
light={true}
|
|
534
|
-
validate={validate}
|
|
535
|
-
onValidate={handleValidate}
|
|
536
|
-
onChange={handleChange}
|
|
537
|
-
onKeyDown={handleKeyDown}
|
|
538
|
-
onFocus={handleFocus}
|
|
539
|
-
onBlur={handleBlur}
|
|
540
|
-
/>
|
|
541
|
-
{!focused && errorMessage && (
|
|
542
|
-
<View>
|
|
543
|
-
<Strut size={Spacing.xSmall_8} />
|
|
544
|
-
<_Text style={styles.errorMessage}>{errorMessage}</_Text>
|
|
545
|
-
</View>
|
|
546
|
-
)}
|
|
547
|
-
</View>
|
|
548
|
-
);
|
|
549
|
-
};
|
|
550
|
-
|
|
551
|
-
ErrorLight.parameters = {
|
|
552
|
-
docs: {
|
|
553
|
-
storyDescription: `If an input value fails validation and the
|
|
554
|
-
\`light\` prop is true, \`TextField\` will have light error styling.`,
|
|
555
|
-
},
|
|
556
|
-
};
|
|
557
|
-
|
|
558
|
-
export const Disabled: StoryComponentType = () => (
|
|
559
|
-
<TextField
|
|
560
|
-
id="tf-8"
|
|
561
|
-
value=""
|
|
562
|
-
placeholder="This field is disabled."
|
|
563
|
-
onChange={() => {}}
|
|
564
|
-
disabled={true}
|
|
565
|
-
/>
|
|
566
|
-
);
|
|
567
|
-
|
|
568
|
-
Disabled.parameters = {
|
|
569
|
-
docs: {
|
|
570
|
-
storyDescription: `If the \`disabled\` prop is set to true,
|
|
571
|
-
\`TextField\` will have disabled styling and will not be interactable.`,
|
|
572
|
-
},
|
|
573
|
-
};
|
|
574
|
-
|
|
575
|
-
export const CustomStyle: StoryComponentType = () => {
|
|
576
|
-
const [value, setValue] = React.useState("");
|
|
577
|
-
|
|
578
|
-
const handleChange = (newValue: string) => {
|
|
579
|
-
setValue(newValue);
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
583
|
-
if (event.key === "Enter") {
|
|
584
|
-
event.currentTarget.blur();
|
|
585
|
-
}
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
return (
|
|
589
|
-
<TextField
|
|
590
|
-
id="tf-10"
|
|
591
|
-
style={styles.customField}
|
|
592
|
-
type="text"
|
|
593
|
-
value={value}
|
|
594
|
-
placeholder="Text"
|
|
595
|
-
onChange={handleChange}
|
|
596
|
-
onKeyDown={handleKeyDown}
|
|
597
|
-
/>
|
|
598
|
-
);
|
|
599
|
-
};
|
|
600
|
-
|
|
601
|
-
CustomStyle.parameters = {
|
|
602
|
-
docs: {
|
|
603
|
-
storyDescription: `\`TextField\` can take in custom styles that
|
|
604
|
-
override the default styles. This example has custom styles for the
|
|
605
|
-
\`backgroundColor\`, \`color\`, \`border\`, \`maxWidth\`, and
|
|
606
|
-
placeholder \`color\` properties.`,
|
|
607
|
-
},
|
|
608
|
-
};
|
|
609
|
-
|
|
610
|
-
export const Ref: StoryComponentType = () => {
|
|
611
|
-
const [value, setValue] = React.useState("");
|
|
612
|
-
const inputRef: RefObject<typeof HTMLInputElement> = React.createRef();
|
|
613
|
-
|
|
614
|
-
const handleChange = (newValue: string) => {
|
|
615
|
-
setValue(newValue);
|
|
616
|
-
};
|
|
617
|
-
|
|
618
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
619
|
-
if (event.key === "Enter") {
|
|
620
|
-
event.currentTarget.blur();
|
|
621
|
-
}
|
|
622
|
-
};
|
|
623
|
-
|
|
624
|
-
const handleSubmit = () => {
|
|
625
|
-
if (inputRef.current) {
|
|
626
|
-
inputRef.current.focus();
|
|
627
|
-
}
|
|
628
|
-
};
|
|
629
|
-
|
|
630
|
-
return (
|
|
631
|
-
<View>
|
|
632
|
-
<TextField
|
|
633
|
-
id="tf-11"
|
|
634
|
-
type="text"
|
|
635
|
-
value={value}
|
|
636
|
-
placeholder="Text"
|
|
637
|
-
onChange={handleChange}
|
|
638
|
-
onKeyDown={handleKeyDown}
|
|
639
|
-
ref={inputRef}
|
|
640
|
-
/>
|
|
641
|
-
<Strut size={Spacing.medium_16} />
|
|
642
|
-
<Button style={styles.button} onClick={handleSubmit}>
|
|
643
|
-
Focus Input
|
|
644
|
-
</Button>
|
|
645
|
-
</View>
|
|
646
|
-
);
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
Ref.parameters = {
|
|
650
|
-
docs: {
|
|
651
|
-
storyDescription: `If you need to save a reference to the input
|
|
652
|
-
field, you can do so by using the \`ref\` prop. In this example,
|
|
653
|
-
we want the input field to receive focus when the button is
|
|
654
|
-
pressed. We can do this by creating a React ref of type
|
|
655
|
-
\`HTMLInputElement\` and passing it into \`TextField\`'s \`ref\` prop.
|
|
656
|
-
Now we can use the ref variable in the \`handleSubmit\` function to
|
|
657
|
-
shift focus to the field.`,
|
|
658
|
-
chromatic: {
|
|
659
|
-
// Disabling snapshot because this is testing interaction,
|
|
660
|
-
// not visuals.
|
|
661
|
-
disableSnapshot: true,
|
|
662
|
-
},
|
|
663
|
-
},
|
|
664
|
-
};
|
|
665
|
-
|
|
666
|
-
export const ReadOnly: StoryComponentType = () => {
|
|
667
|
-
const [value, setValue] = React.useState("Khan");
|
|
668
|
-
|
|
669
|
-
const handleChange = (newValue: string) => {
|
|
670
|
-
setValue(newValue);
|
|
671
|
-
};
|
|
672
|
-
|
|
673
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
674
|
-
if (event.key === "Enter") {
|
|
675
|
-
event.currentTarget.blur();
|
|
676
|
-
}
|
|
677
|
-
};
|
|
678
|
-
|
|
679
|
-
return (
|
|
680
|
-
<TextField
|
|
681
|
-
id="tf-12"
|
|
682
|
-
type="text"
|
|
683
|
-
value={value}
|
|
684
|
-
placeholder="Text"
|
|
685
|
-
onChange={handleChange}
|
|
686
|
-
onKeyDown={handleKeyDown}
|
|
687
|
-
readOnly={true}
|
|
688
|
-
/>
|
|
689
|
-
);
|
|
690
|
-
};
|
|
691
|
-
|
|
692
|
-
ReadOnly.parameters = {
|
|
693
|
-
docs: {
|
|
694
|
-
storyDescription: `An input field with the prop \`readOnly\` set
|
|
695
|
-
to true is not interactable. It looks the same as if it were not
|
|
696
|
-
read only, and it can still receive focus, but the interaction
|
|
697
|
-
point will not appear and the input will not change.`,
|
|
698
|
-
chromatic: {
|
|
699
|
-
// Disabling snapshot because this is testing interaction,
|
|
700
|
-
// not visuals.
|
|
701
|
-
disableSnapshot: true,
|
|
702
|
-
},
|
|
703
|
-
},
|
|
704
|
-
};
|
|
705
|
-
|
|
706
|
-
export const AutoComplete: StoryComponentType = () => {
|
|
707
|
-
const [value, setValue] = React.useState("");
|
|
708
|
-
|
|
709
|
-
const handleChange = (newValue: string) => {
|
|
710
|
-
setValue(newValue);
|
|
711
|
-
};
|
|
712
|
-
|
|
713
|
-
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
714
|
-
if (event.key === "Enter") {
|
|
715
|
-
event.currentTarget.blur();
|
|
716
|
-
}
|
|
717
|
-
};
|
|
718
|
-
|
|
719
|
-
return (
|
|
720
|
-
<form>
|
|
721
|
-
<TextField
|
|
722
|
-
id="tf-13"
|
|
723
|
-
type="text"
|
|
724
|
-
value={value}
|
|
725
|
-
placeholder="Name"
|
|
726
|
-
onChange={handleChange}
|
|
727
|
-
onKeyDown={handleKeyDown}
|
|
728
|
-
style={styles.fieldWithButton}
|
|
729
|
-
autoComplete="name"
|
|
730
|
-
/>
|
|
731
|
-
<Button type="submit">Submit</Button>
|
|
732
|
-
</form>
|
|
733
|
-
);
|
|
734
|
-
};
|
|
735
|
-
|
|
736
|
-
AutoComplete.parameters = {
|
|
737
|
-
docs: {
|
|
738
|
-
storyDescription: `If \`TextField\`'s \`autocomplete\` prop is set,
|
|
739
|
-
the browser can predict values for the input. When the user starts
|
|
740
|
-
to type in the field, a list of options will show up based on
|
|
741
|
-
values that may have been submitted at a previous time.
|
|
742
|
-
In this example, the text field provides options after you
|
|
743
|
-
input a value, press the submit button, and refresh the page.`,
|
|
744
|
-
chromatic: {
|
|
745
|
-
// Disabling snapshot because this is testing interaction,
|
|
746
|
-
// not visuals.
|
|
747
|
-
disableSnapshot: true,
|
|
748
|
-
},
|
|
749
|
-
},
|
|
750
|
-
};
|
|
751
|
-
|
|
752
|
-
const styles = StyleSheet.create({
|
|
753
|
-
errorMessage: {
|
|
754
|
-
color: Color.red,
|
|
755
|
-
paddingLeft: Spacing.xxxSmall_4,
|
|
756
|
-
},
|
|
757
|
-
errorMessageLight: {
|
|
758
|
-
color: Color.white,
|
|
759
|
-
paddingLeft: Spacing.xxxSmall_4,
|
|
760
|
-
},
|
|
761
|
-
darkBackground: {
|
|
762
|
-
backgroundColor: Color.darkBlue,
|
|
763
|
-
padding: Spacing.medium_16,
|
|
764
|
-
},
|
|
765
|
-
customField: {
|
|
766
|
-
backgroundColor: Color.darkBlue,
|
|
767
|
-
color: Color.white,
|
|
768
|
-
border: "none",
|
|
769
|
-
maxWidth: 250,
|
|
770
|
-
"::placeholder": {
|
|
771
|
-
color: Color.white64,
|
|
772
|
-
},
|
|
773
|
-
},
|
|
774
|
-
button: {
|
|
775
|
-
maxWidth: 150,
|
|
776
|
-
},
|
|
777
|
-
fieldWithButton: {
|
|
778
|
-
marginBottom: Spacing.medium_16,
|
|
779
|
-
},
|
|
780
|
-
});
|