@khanacademy/wonder-blocks-form 2.2.1
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/LICENSE +21 -0
- package/dist/es/index.js +1100 -0
- package/dist/index.js +1419 -0
- package/dist/index.js.flow +2 -0
- package/docs.md +1 -0
- package/package.json +35 -0
- package/src/__tests__/__snapshots__/custom-snapshot.test.js.snap +1349 -0
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +6126 -0
- package/src/__tests__/custom-snapshot.test.js +66 -0
- package/src/__tests__/generated-snapshot.test.js +654 -0
- package/src/components/__tests__/checkbox-group.test.js +84 -0
- package/src/components/__tests__/field-heading.test.js +182 -0
- package/src/components/__tests__/labeled-text-field.test.js +442 -0
- package/src/components/__tests__/radio-group.test.js +84 -0
- package/src/components/__tests__/text-field.test.js +424 -0
- package/src/components/checkbox-core.js +201 -0
- package/src/components/checkbox-group.js +161 -0
- package/src/components/checkbox-group.md +200 -0
- package/src/components/checkbox.js +94 -0
- package/src/components/checkbox.md +134 -0
- package/src/components/choice-internal.js +206 -0
- package/src/components/choice.js +104 -0
- package/src/components/field-heading.js +157 -0
- package/src/components/field-heading.md +43 -0
- package/src/components/group-styles.js +35 -0
- package/src/components/labeled-text-field.js +265 -0
- package/src/components/labeled-text-field.md +535 -0
- package/src/components/labeled-text-field.stories.js +359 -0
- package/src/components/radio-core.js +176 -0
- package/src/components/radio-group.js +142 -0
- package/src/components/radio-group.md +129 -0
- package/src/components/radio.js +93 -0
- package/src/components/radio.md +26 -0
- package/src/components/text-field.js +326 -0
- package/src/components/text-field.md +770 -0
- package/src/components/text-field.stories.js +513 -0
- package/src/index.js +18 -0
- package/src/util/types.js +77 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {StyleSheet} from "aphrodite";
|
|
4
|
+
|
|
5
|
+
import {View, 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
|
+
export default {
|
|
15
|
+
title: "Form / TextField",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const text: StoryComponentType = () => {
|
|
19
|
+
const [value, setValue] = React.useState("");
|
|
20
|
+
|
|
21
|
+
const handleChange = (newValue: string) => {
|
|
22
|
+
setValue(newValue);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
26
|
+
if (event.key === "Enter") {
|
|
27
|
+
event.currentTarget.blur();
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<TextField
|
|
33
|
+
id="tf-1"
|
|
34
|
+
type="text"
|
|
35
|
+
value={value}
|
|
36
|
+
placeholder="Text"
|
|
37
|
+
onChange={handleChange}
|
|
38
|
+
onKeyDown={handleKeyDown}
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const number: StoryComponentType = () => {
|
|
44
|
+
const [value, setValue] = React.useState("12345");
|
|
45
|
+
|
|
46
|
+
const handleChange = (newValue: string) => {
|
|
47
|
+
setValue(newValue);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
51
|
+
if (event.key === "Enter") {
|
|
52
|
+
event.currentTarget.blur();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<TextField
|
|
58
|
+
id="tf-1"
|
|
59
|
+
type="number"
|
|
60
|
+
value={value}
|
|
61
|
+
placeholder="Number"
|
|
62
|
+
onChange={handleChange}
|
|
63
|
+
onKeyDown={handleKeyDown}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const password: StoryComponentType = () => {
|
|
69
|
+
const [value, setValue] = React.useState("Password123");
|
|
70
|
+
const [errorMessage, setErrorMessage] = React.useState();
|
|
71
|
+
const [focused, setFocused] = React.useState(false);
|
|
72
|
+
|
|
73
|
+
const handleChange = (newValue: string) => {
|
|
74
|
+
setValue(newValue);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const validate = (value: string) => {
|
|
78
|
+
if (value.length < 8) {
|
|
79
|
+
return "Password must be at least 8 characters long";
|
|
80
|
+
}
|
|
81
|
+
if (!/\d/.test(value)) {
|
|
82
|
+
return "Password must contain a numeric value";
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handleValidate = (errorMessage: ?string) => {
|
|
87
|
+
setErrorMessage(errorMessage);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
91
|
+
if (event.key === "Enter") {
|
|
92
|
+
event.currentTarget.blur();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const handleFocus = () => {
|
|
97
|
+
setFocused(true);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const handleBlur = () => {
|
|
101
|
+
setFocused(false);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<View>
|
|
106
|
+
<TextField
|
|
107
|
+
id="tf-1"
|
|
108
|
+
type="password"
|
|
109
|
+
value={value}
|
|
110
|
+
placeholder="Password"
|
|
111
|
+
validate={validate}
|
|
112
|
+
onValidate={handleValidate}
|
|
113
|
+
onChange={handleChange}
|
|
114
|
+
onKeyDown={handleKeyDown}
|
|
115
|
+
onFocus={handleFocus}
|
|
116
|
+
onBlur={handleBlur}
|
|
117
|
+
/>
|
|
118
|
+
{!focused && errorMessage && (
|
|
119
|
+
<View>
|
|
120
|
+
<Strut size={Spacing.xSmall_8} />
|
|
121
|
+
<Text style={styles.errorMessage}>{errorMessage}</Text>
|
|
122
|
+
</View>
|
|
123
|
+
)}
|
|
124
|
+
</View>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const email: StoryComponentType = () => {
|
|
129
|
+
const [value, setValue] = React.useState("khan@khanacademy.org");
|
|
130
|
+
const [errorMessage, setErrorMessage] = React.useState();
|
|
131
|
+
const [focused, setFocused] = React.useState(false);
|
|
132
|
+
|
|
133
|
+
const handleChange = (newValue: string) => {
|
|
134
|
+
setValue(newValue);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const validate = (value: string) => {
|
|
138
|
+
const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/;
|
|
139
|
+
if (!emailRegex.test(value)) {
|
|
140
|
+
return "Please enter a valid email";
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const handleValidate = (errorMessage: ?string) => {
|
|
145
|
+
setErrorMessage(errorMessage);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
149
|
+
if (event.key === "Enter") {
|
|
150
|
+
event.currentTarget.blur();
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const handleFocus = () => {
|
|
155
|
+
setFocused(true);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const handleBlur = () => {
|
|
159
|
+
setFocused(false);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<View>
|
|
164
|
+
<TextField
|
|
165
|
+
id="tf-1"
|
|
166
|
+
type="email"
|
|
167
|
+
value={value}
|
|
168
|
+
placeholder="Email"
|
|
169
|
+
validate={validate}
|
|
170
|
+
onValidate={handleValidate}
|
|
171
|
+
onChange={handleChange}
|
|
172
|
+
onKeyDown={handleKeyDown}
|
|
173
|
+
onFocus={handleFocus}
|
|
174
|
+
onBlur={handleBlur}
|
|
175
|
+
/>
|
|
176
|
+
{!focused && errorMessage && (
|
|
177
|
+
<View>
|
|
178
|
+
<Strut size={Spacing.xSmall_8} />
|
|
179
|
+
<Text style={styles.errorMessage}>{errorMessage}</Text>
|
|
180
|
+
</View>
|
|
181
|
+
)}
|
|
182
|
+
</View>
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export const telephone: StoryComponentType = () => {
|
|
187
|
+
const [value, setValue] = React.useState("123-456-7890");
|
|
188
|
+
const [errorMessage, setErrorMessage] = React.useState();
|
|
189
|
+
const [focused, setFocused] = React.useState(false);
|
|
190
|
+
|
|
191
|
+
const handleChange = (newValue: string) => {
|
|
192
|
+
setValue(newValue);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const validate = (value: string) => {
|
|
196
|
+
const telRegex = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/;
|
|
197
|
+
if (!telRegex.test(value)) {
|
|
198
|
+
return "Invalid US telephone number";
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const handleValidate = (errorMessage: ?string) => {
|
|
203
|
+
setErrorMessage(errorMessage);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
207
|
+
if (event.key === "Enter") {
|
|
208
|
+
event.currentTarget.blur();
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const handleFocus = () => {
|
|
213
|
+
setFocused(true);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const handleBlur = () => {
|
|
217
|
+
setFocused(false);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<View>
|
|
222
|
+
<TextField
|
|
223
|
+
id="tf-1"
|
|
224
|
+
type="tel"
|
|
225
|
+
value={value}
|
|
226
|
+
placeholder="Telephone"
|
|
227
|
+
validate={validate}
|
|
228
|
+
onValidate={handleValidate}
|
|
229
|
+
onChange={handleChange}
|
|
230
|
+
onKeyDown={handleKeyDown}
|
|
231
|
+
onFocus={handleFocus}
|
|
232
|
+
onBlur={handleBlur}
|
|
233
|
+
/>
|
|
234
|
+
{!focused && errorMessage && (
|
|
235
|
+
<View>
|
|
236
|
+
<Strut size={Spacing.xSmall_8} />
|
|
237
|
+
<Text style={styles.errorMessage}>{errorMessage}</Text>
|
|
238
|
+
</View>
|
|
239
|
+
)}
|
|
240
|
+
</View>
|
|
241
|
+
);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
export const error: StoryComponentType = () => {
|
|
245
|
+
const [value, setValue] = React.useState("khan");
|
|
246
|
+
const [errorMessage, setErrorMessage] = React.useState();
|
|
247
|
+
const [focused, setFocused] = React.useState(false);
|
|
248
|
+
|
|
249
|
+
const handleChange = (newValue: string) => {
|
|
250
|
+
setValue(newValue);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const validate = (value: string) => {
|
|
254
|
+
const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/;
|
|
255
|
+
if (!emailRegex.test(value)) {
|
|
256
|
+
return "Please enter a valid email";
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const handleValidate = (errorMessage: ?string) => {
|
|
261
|
+
setErrorMessage(errorMessage);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
265
|
+
if (event.key === "Enter") {
|
|
266
|
+
event.currentTarget.blur();
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const handleFocus = () => {
|
|
271
|
+
setFocused(true);
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const handleBlur = () => {
|
|
275
|
+
setFocused(false);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<View>
|
|
280
|
+
<TextField
|
|
281
|
+
id="tf-1"
|
|
282
|
+
type="email"
|
|
283
|
+
value={value}
|
|
284
|
+
placeholder="Email"
|
|
285
|
+
validate={validate}
|
|
286
|
+
onValidate={handleValidate}
|
|
287
|
+
onChange={handleChange}
|
|
288
|
+
onKeyDown={handleKeyDown}
|
|
289
|
+
onFocus={handleFocus}
|
|
290
|
+
onBlur={handleBlur}
|
|
291
|
+
/>
|
|
292
|
+
{!focused && errorMessage && (
|
|
293
|
+
<View>
|
|
294
|
+
<Strut size={Spacing.xSmall_8} />
|
|
295
|
+
<Text style={styles.errorMessage}>{errorMessage}</Text>
|
|
296
|
+
</View>
|
|
297
|
+
)}
|
|
298
|
+
</View>
|
|
299
|
+
);
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
export const disabled: StoryComponentType = () => (
|
|
303
|
+
<TextField
|
|
304
|
+
id="tf-1"
|
|
305
|
+
value=""
|
|
306
|
+
placeholder="This field is disabled."
|
|
307
|
+
onChange={() => {}}
|
|
308
|
+
disabled={true}
|
|
309
|
+
/>
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
export const light: StoryComponentType = () => {
|
|
313
|
+
const [value, setValue] = React.useState("khan@khanacademy.org");
|
|
314
|
+
const [errorMessage, setErrorMessage] = React.useState();
|
|
315
|
+
const [focused, setFocused] = React.useState(false);
|
|
316
|
+
|
|
317
|
+
const handleChange = (newValue: string) => {
|
|
318
|
+
setValue(newValue);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const validate = (value: string) => {
|
|
322
|
+
const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/;
|
|
323
|
+
if (!emailRegex.test(value)) {
|
|
324
|
+
return "Please enter a valid email";
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const handleValidate = (errorMessage: ?string) => {
|
|
329
|
+
setErrorMessage(errorMessage);
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
333
|
+
if (event.key === "Enter") {
|
|
334
|
+
event.currentTarget.blur();
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const handleFocus = () => {
|
|
339
|
+
setFocused(true);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const handleBlur = () => {
|
|
343
|
+
setFocused(false);
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
return (
|
|
347
|
+
<View style={styles.darkBackground}>
|
|
348
|
+
<TextField
|
|
349
|
+
id="tf-1"
|
|
350
|
+
type="email"
|
|
351
|
+
value={value}
|
|
352
|
+
placeholder="Email"
|
|
353
|
+
light={true}
|
|
354
|
+
validate={validate}
|
|
355
|
+
onValidate={handleValidate}
|
|
356
|
+
onChange={handleChange}
|
|
357
|
+
onKeyDown={handleKeyDown}
|
|
358
|
+
onFocus={handleFocus}
|
|
359
|
+
onBlur={handleBlur}
|
|
360
|
+
/>
|
|
361
|
+
{!focused && errorMessage && (
|
|
362
|
+
<View>
|
|
363
|
+
<Strut size={Spacing.xSmall_8} />
|
|
364
|
+
<Text style={styles.errorMessageLight}>{errorMessage}</Text>
|
|
365
|
+
</View>
|
|
366
|
+
)}
|
|
367
|
+
</View>
|
|
368
|
+
);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
export const customStyle: StoryComponentType = () => {
|
|
372
|
+
const [value, setValue] = React.useState("");
|
|
373
|
+
|
|
374
|
+
const handleChange = (newValue: string) => {
|
|
375
|
+
setValue(newValue);
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
379
|
+
if (event.key === "Enter") {
|
|
380
|
+
event.currentTarget.blur();
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
return (
|
|
385
|
+
<TextField
|
|
386
|
+
id="tf-1"
|
|
387
|
+
style={styles.customField}
|
|
388
|
+
type="text"
|
|
389
|
+
value={value}
|
|
390
|
+
placeholder="Text"
|
|
391
|
+
onChange={handleChange}
|
|
392
|
+
onKeyDown={handleKeyDown}
|
|
393
|
+
/>
|
|
394
|
+
);
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
export const ref: StoryComponentType = () => {
|
|
398
|
+
const [value, setValue] = React.useState("");
|
|
399
|
+
const inputRef: RefObject<typeof HTMLInputElement> = React.createRef();
|
|
400
|
+
|
|
401
|
+
const handleChange = (newValue: string) => {
|
|
402
|
+
setValue(newValue);
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
406
|
+
if (event.key === "Enter") {
|
|
407
|
+
event.currentTarget.blur();
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const handleSubmit = () => {
|
|
412
|
+
if (inputRef.current) {
|
|
413
|
+
inputRef.current.focus();
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
return (
|
|
418
|
+
<View>
|
|
419
|
+
<TextField
|
|
420
|
+
id="tf-1"
|
|
421
|
+
type="text"
|
|
422
|
+
value={value}
|
|
423
|
+
placeholder="Text"
|
|
424
|
+
onChange={handleChange}
|
|
425
|
+
onKeyDown={handleKeyDown}
|
|
426
|
+
ref={inputRef}
|
|
427
|
+
/>
|
|
428
|
+
<Strut size={Spacing.medium_16} />
|
|
429
|
+
<Button style={styles.button} onClick={handleSubmit}>
|
|
430
|
+
Focus Input
|
|
431
|
+
</Button>
|
|
432
|
+
</View>
|
|
433
|
+
);
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
export const readOnly: StoryComponentType = () => {
|
|
437
|
+
const [value, setValue] = React.useState("Khan");
|
|
438
|
+
|
|
439
|
+
const handleChange = (newValue: string) => {
|
|
440
|
+
setValue(newValue);
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
444
|
+
if (event.key === "Enter") {
|
|
445
|
+
event.currentTarget.blur();
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
return (
|
|
450
|
+
<TextField
|
|
451
|
+
id="tf-1"
|
|
452
|
+
type="text"
|
|
453
|
+
value={value}
|
|
454
|
+
placeholder="Text"
|
|
455
|
+
onChange={handleChange}
|
|
456
|
+
onKeyDown={handleKeyDown}
|
|
457
|
+
readOnly={true}
|
|
458
|
+
/>
|
|
459
|
+
);
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
export const autoComplete: StoryComponentType = () => {
|
|
463
|
+
const [value, setValue] = React.useState("");
|
|
464
|
+
|
|
465
|
+
const handleChange = (newValue: string) => {
|
|
466
|
+
setValue(newValue);
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
const handleKeyDown = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
|
470
|
+
if (event.key === "Enter") {
|
|
471
|
+
event.currentTarget.blur();
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
return (
|
|
476
|
+
<TextField
|
|
477
|
+
id="tf-1"
|
|
478
|
+
type="text"
|
|
479
|
+
value={value}
|
|
480
|
+
placeholder="Name"
|
|
481
|
+
onChange={handleChange}
|
|
482
|
+
onKeyDown={handleKeyDown}
|
|
483
|
+
autoComplete="name"
|
|
484
|
+
/>
|
|
485
|
+
);
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
const styles = StyleSheet.create({
|
|
489
|
+
errorMessage: {
|
|
490
|
+
color: Color.red,
|
|
491
|
+
paddingLeft: Spacing.xxxSmall_4,
|
|
492
|
+
},
|
|
493
|
+
errorMessageLight: {
|
|
494
|
+
color: Color.white,
|
|
495
|
+
paddingLeft: Spacing.xxxSmall_4,
|
|
496
|
+
},
|
|
497
|
+
darkBackground: {
|
|
498
|
+
backgroundColor: Color.darkBlue,
|
|
499
|
+
padding: Spacing.medium_16,
|
|
500
|
+
},
|
|
501
|
+
customField: {
|
|
502
|
+
backgroundColor: Color.darkBlue,
|
|
503
|
+
color: Color.white,
|
|
504
|
+
border: "none",
|
|
505
|
+
maxWidth: 250,
|
|
506
|
+
"::placeholder": {
|
|
507
|
+
color: Color.white64,
|
|
508
|
+
},
|
|
509
|
+
},
|
|
510
|
+
button: {
|
|
511
|
+
maxWidth: 150,
|
|
512
|
+
},
|
|
513
|
+
});
|
package/src/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import Checkbox from "./components/checkbox.js";
|
|
3
|
+
import Radio from "./components/radio.js";
|
|
4
|
+
import Choice from "./components/choice.js";
|
|
5
|
+
import CheckboxGroup from "./components/checkbox-group.js";
|
|
6
|
+
import RadioGroup from "./components/radio-group.js";
|
|
7
|
+
import TextField from "./components/text-field.js";
|
|
8
|
+
import LabeledTextField from "./components/labeled-text-field.js";
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
Checkbox,
|
|
12
|
+
Radio,
|
|
13
|
+
Choice,
|
|
14
|
+
CheckboxGroup,
|
|
15
|
+
RadioGroup,
|
|
16
|
+
TextField,
|
|
17
|
+
LabeledTextField,
|
|
18
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
// NOTE(sophie): Unfortunately, styleguidist does not pull prop definitions
|
|
3
|
+
// from imported types. We've duplicated the shared props for each component
|
|
4
|
+
// they apply to, so that the prop definitions will show up on the generated
|
|
5
|
+
// guide.
|
|
6
|
+
import type {AriaProps, StyleType} from "@khanacademy/wonder-blocks-core";
|
|
7
|
+
|
|
8
|
+
import typeof Choice from "../components/choice.js";
|
|
9
|
+
|
|
10
|
+
// Shared props for radio-core and checkbox-core
|
|
11
|
+
export type ChoiceCoreProps = {|
|
|
12
|
+
...AriaProps,
|
|
13
|
+
/** Whether this component is checked */
|
|
14
|
+
checked: boolean,
|
|
15
|
+
/** Whether this component is disabled */
|
|
16
|
+
disabled: boolean,
|
|
17
|
+
/** Whether this component should show an error state */
|
|
18
|
+
error: boolean,
|
|
19
|
+
/** Name for the checkbox or radio button group */
|
|
20
|
+
groupName?: string,
|
|
21
|
+
/** Unique identifier attached to the HTML input element. If used, need to
|
|
22
|
+
* guarantee that the ID is unique within everything rendered on a page.
|
|
23
|
+
* Used to match <label> with <input> elements for screenreaders. */
|
|
24
|
+
id?: string,
|
|
25
|
+
/** Optional test ID for e2e testing */
|
|
26
|
+
testId?: string,
|
|
27
|
+
|};
|
|
28
|
+
|
|
29
|
+
// Props for checkbox and radio button
|
|
30
|
+
export type ChoiceComponentProps = {|
|
|
31
|
+
...ChoiceCoreProps,
|
|
32
|
+
/** Callback when this component is selected. The newCheckedState is the
|
|
33
|
+
* new checked state of the component. */
|
|
34
|
+
onChange: (newCheckedState: boolean) => mixed,
|
|
35
|
+
/** Optional label for the field. */
|
|
36
|
+
label?: string,
|
|
37
|
+
/** Optional description for the field. */
|
|
38
|
+
description?: string,
|
|
39
|
+
/** Ignored because only applicable to Choice components in a group. */
|
|
40
|
+
value?: string,
|
|
41
|
+
/** Optional styling for the container. Does not style the component. */
|
|
42
|
+
style?: StyleType,
|
|
43
|
+
|};
|
|
44
|
+
|
|
45
|
+
export type SharedGroupProps = {|
|
|
46
|
+
/** Children should be Choice components. */
|
|
47
|
+
children: Choice,
|
|
48
|
+
/** Group name for this checkbox or radio group. Should be unique for all
|
|
49
|
+
* such groups displayed on a page. */
|
|
50
|
+
groupName: string,
|
|
51
|
+
/** Optional label for the group. This label is optional to allow for
|
|
52
|
+
* greater flexibility in implementing checkbox and radio groups. */
|
|
53
|
+
label?: string,
|
|
54
|
+
/** Optional description for the group. */
|
|
55
|
+
description?: string,
|
|
56
|
+
/** Optional error message. If supplied, the group will be displayed in an
|
|
57
|
+
* error state, along with this error message. If no error state is desired,
|
|
58
|
+
* simply do not supply this prop, or pass along null. */
|
|
59
|
+
errorMessage?: string,
|
|
60
|
+
/** Custom styling for this group of checkboxes. */
|
|
61
|
+
style?: StyleType,
|
|
62
|
+
|};
|
|
63
|
+
|
|
64
|
+
export type CheckboxGroupProps = {|
|
|
65
|
+
/** Callback for when selection of the group has changed. Passes the newly
|
|
66
|
+
* selected values. */
|
|
67
|
+
onChange: (selectedValues: Array<string>) => mixed,
|
|
68
|
+
/** An array of the values of the selected values in this checkbox group. */
|
|
69
|
+
selectedValues: Array<string>,
|
|
70
|
+
|};
|
|
71
|
+
|
|
72
|
+
export type RadioGroupProps = {|
|
|
73
|
+
/** Callback for when the selected value of the radio group has changed. */
|
|
74
|
+
onChange: (selectedValue: string) => mixed,
|
|
75
|
+
/** Value of the selected radio item. */
|
|
76
|
+
selectedValue: string,
|
|
77
|
+
|};
|