@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.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/dist/es/index.js +1100 -0
  3. package/dist/index.js +1419 -0
  4. package/dist/index.js.flow +2 -0
  5. package/docs.md +1 -0
  6. package/package.json +35 -0
  7. package/src/__tests__/__snapshots__/custom-snapshot.test.js.snap +1349 -0
  8. package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +6126 -0
  9. package/src/__tests__/custom-snapshot.test.js +66 -0
  10. package/src/__tests__/generated-snapshot.test.js +654 -0
  11. package/src/components/__tests__/checkbox-group.test.js +84 -0
  12. package/src/components/__tests__/field-heading.test.js +182 -0
  13. package/src/components/__tests__/labeled-text-field.test.js +442 -0
  14. package/src/components/__tests__/radio-group.test.js +84 -0
  15. package/src/components/__tests__/text-field.test.js +424 -0
  16. package/src/components/checkbox-core.js +201 -0
  17. package/src/components/checkbox-group.js +161 -0
  18. package/src/components/checkbox-group.md +200 -0
  19. package/src/components/checkbox.js +94 -0
  20. package/src/components/checkbox.md +134 -0
  21. package/src/components/choice-internal.js +206 -0
  22. package/src/components/choice.js +104 -0
  23. package/src/components/field-heading.js +157 -0
  24. package/src/components/field-heading.md +43 -0
  25. package/src/components/group-styles.js +35 -0
  26. package/src/components/labeled-text-field.js +265 -0
  27. package/src/components/labeled-text-field.md +535 -0
  28. package/src/components/labeled-text-field.stories.js +359 -0
  29. package/src/components/radio-core.js +176 -0
  30. package/src/components/radio-group.js +142 -0
  31. package/src/components/radio-group.md +129 -0
  32. package/src/components/radio.js +93 -0
  33. package/src/components/radio.md +26 -0
  34. package/src/components/text-field.js +326 -0
  35. package/src/components/text-field.md +770 -0
  36. package/src/components/text-field.stories.js +513 -0
  37. package/src/index.js +18 -0
  38. package/src/util/types.js +77 -0
@@ -0,0 +1,535 @@
1
+ LabeledTextField derives from TextField and allows the handling of single lines of text with convenient label, description, and error messages.
2
+
3
+ Text
4
+
5
+ ```js
6
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
7
+
8
+ class LabeledTextFieldExample extends React.Component {
9
+ constructor(props) {
10
+ super(props);
11
+ this.state = {
12
+ value: "Khan",
13
+ };
14
+ }
15
+
16
+ handleKeyDown(event) {
17
+ if (event.key === "Enter") {
18
+ event.currentTarget.blur();
19
+ }
20
+ }
21
+
22
+ render() {
23
+ return (
24
+ <LabeledTextField
25
+ label="Name"
26
+ description="Please enter your name"
27
+ value={this.state.value}
28
+ onChange={(newValue) => this.setState({value: newValue})}
29
+ placeholder="Name"
30
+ onKeyDown={this.handleKeyDown}
31
+ />
32
+ );
33
+ }
34
+ }
35
+
36
+ <LabeledTextFieldExample />
37
+ ```
38
+
39
+ Number
40
+
41
+ ```js
42
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
43
+
44
+ class LabeledTextFieldExample extends React.Component {
45
+ constructor(props) {
46
+ super(props);
47
+ this.state = {
48
+ value: "18",
49
+ };
50
+ }
51
+
52
+ handleKeyDown(event) {
53
+ if (event.key === "Enter") {
54
+ event.currentTarget.blur();
55
+ }
56
+ }
57
+
58
+ render() {
59
+ return (
60
+ <LabeledTextField
61
+ label="Age"
62
+ type="number"
63
+ description="Please enter your age"
64
+ value={this.state.value}
65
+ onChange={(newValue) => this.setState({value: newValue})}
66
+ placeholder="Age"
67
+ onKeyDown={this.handleKeyDown}
68
+ />
69
+ );
70
+ }
71
+ }
72
+
73
+ <LabeledTextFieldExample />
74
+ ```
75
+
76
+ Password
77
+
78
+ ```js
79
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
80
+
81
+ class LabeledTextFieldExample extends React.Component {
82
+ constructor(props) {
83
+ super(props);
84
+ this.state = {
85
+ value: "Password123",
86
+ };
87
+ }
88
+
89
+ validate(value) {
90
+ if (value.length < 8) {
91
+ return "Password must be at least 8 characters long";
92
+ }
93
+ if (!/\d/.test(value)) {
94
+ return "Password must contain a numeric value";
95
+ }
96
+ }
97
+
98
+ handleKeyDown(event) {
99
+ if (event.key === "Enter") {
100
+ event.currentTarget.blur();
101
+ }
102
+ }
103
+
104
+ render() {
105
+ return (
106
+ <LabeledTextField
107
+ label="Password"
108
+ type="password"
109
+ description="Please enter a secure password"
110
+ value={this.state.value}
111
+ onChange={(newValue) => this.setState({value: newValue})}
112
+ placeholder="Password"
113
+ validate={this.validate}
114
+ onKeyDown={this.handleKeyDown}
115
+ />
116
+ );
117
+ }
118
+ }
119
+
120
+ <LabeledTextFieldExample />
121
+ ```
122
+
123
+ Email
124
+
125
+ ```js
126
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
127
+
128
+ class LabeledTextFieldExample extends React.Component {
129
+ constructor(props) {
130
+ super(props);
131
+ this.state = {
132
+ value: "khan@khan.org",
133
+ };
134
+ }
135
+
136
+ validate(value) {
137
+ const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/;
138
+ if (!emailRegex.test(value)) {
139
+ return "Please enter a valid email";
140
+ }
141
+ }
142
+
143
+ handleKeyDown(event) {
144
+ if (event.key === "Enter") {
145
+ event.currentTarget.blur();
146
+ }
147
+ }
148
+
149
+ render() {
150
+ return (
151
+ <LabeledTextField
152
+ label="Email"
153
+ type="email"
154
+ description="Please provide your personal email"
155
+ value={this.state.value}
156
+ onChange={(newValue) => this.setState({value: newValue})}
157
+ placeholder="Email"
158
+ validate={this.validate}
159
+ onKeyDown={this.handleKeyDown}
160
+ />
161
+ );
162
+ }
163
+ }
164
+
165
+ <LabeledTextFieldExample />
166
+ ```
167
+
168
+ Telephone
169
+
170
+ ```js
171
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
172
+
173
+ class LabeledTextFieldExample extends React.Component {
174
+ constructor(props) {
175
+ super(props);
176
+ this.state = {
177
+ value: "123-456-7890",
178
+ };
179
+ }
180
+
181
+ validate(value) {
182
+ const telRegex = /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/;
183
+ if (!telRegex.test(value)) {
184
+ return "Invalid US telephone number";
185
+ }
186
+ }
187
+
188
+ handleKeyDown(event) {
189
+ if (event.key === "Enter") {
190
+ event.currentTarget.blur();
191
+ }
192
+ }
193
+
194
+ render() {
195
+ return (
196
+ <LabeledTextField
197
+ label="Telephone"
198
+ type="tel"
199
+ description="Please provide your personal phone number"
200
+ value={this.state.value}
201
+ onChange={(newValue) => this.setState({value: newValue})}
202
+ placeholder="Telephone"
203
+ validate={this.validate}
204
+ onKeyDown={this.handleKeyDown}
205
+ />
206
+ );
207
+ }
208
+ }
209
+
210
+ <LabeledTextFieldExample />
211
+ ```
212
+
213
+ The field can have an error
214
+
215
+ ```js
216
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
217
+
218
+ class LabeledTextFieldExample extends React.Component {
219
+ constructor(props) {
220
+ super(props);
221
+ this.state = {
222
+ value: "khan",
223
+ };
224
+ }
225
+
226
+ validate(value) {
227
+ const emailRegex = /^[^@\s]+@[^@\s.]+\.[^@.\s]+$/;
228
+ if (!emailRegex.test(value)) {
229
+ return "Please enter a valid email";
230
+ }
231
+ }
232
+
233
+ handleKeyDown(event) {
234
+ if (event.key === "Enter") {
235
+ event.currentTarget.blur();
236
+ }
237
+ }
238
+
239
+ render() {
240
+ return (
241
+ <LabeledTextField
242
+ label="Email"
243
+ type="email"
244
+ description="Please enter your personal email"
245
+ value={this.state.value}
246
+ onChange={(newValue) => this.setState({value: newValue})}
247
+ placeholder="Email"
248
+ validate={this.validate}
249
+ onKeyDown={this.handleKeyDown}
250
+ />
251
+ );
252
+ }
253
+ }
254
+
255
+ <LabeledTextFieldExample />
256
+ ```
257
+
258
+ The field can be disabled
259
+
260
+ ```js
261
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
262
+
263
+ <LabeledTextField
264
+ label="Name"
265
+ description="Please enter your name"
266
+ value=""
267
+ onChange={() => {}}
268
+ placeholder="Name"
269
+ disabled={true}
270
+ />
271
+ ```
272
+
273
+ The field can be in light mode to fit a dark background
274
+
275
+ ```js
276
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
277
+ import {View} from "@khanacademy/wonder-blocks-core";
278
+ import {LabelMedium, LabelSmall} from "@khanacademy/wonder-blocks-typography";
279
+ import Color from "@khanacademy/wonder-blocks-color";
280
+ import Spacing from "@khanacademy/wonder-blocks-spacing";
281
+ import {StyleSheet} from "aphrodite";
282
+
283
+ class LabeledTextFieldExample extends React.Component {
284
+ constructor(props) {
285
+ super(props);
286
+ this.state = {
287
+ value: "",
288
+ };
289
+ }
290
+
291
+ handleKeyDown(event) {
292
+ if (event.key === "Enter") {
293
+ event.currentTarget.blur();
294
+ }
295
+ }
296
+
297
+ render() {
298
+ return (
299
+ <View style={styles.darkBackground}>
300
+ <LabeledTextField
301
+ label={
302
+ <LabelMedium style={styles.whiteColor}>Name</LabelMedium>
303
+ }
304
+ description={
305
+ <LabelSmall style={styles.offWhiteColor}>
306
+ Please enter your name
307
+ </LabelSmall>
308
+ }
309
+ value={this.state.value}
310
+ onChange={(newValue) => this.setState({value: newValue})}
311
+ placeholder="Name"
312
+ light={true}
313
+ onKeyDown={this.handleKeyDown}
314
+ />
315
+ </View>
316
+ );
317
+ }
318
+ }
319
+
320
+ const styles = StyleSheet.create({
321
+ darkBackground: {
322
+ background: Color.darkBlue,
323
+ padding: `${Spacing.medium_16}px`,
324
+ },
325
+ whiteColor: {
326
+ color: Color.white,
327
+ },
328
+ offWhiteColor: {
329
+ color: Color.white64,
330
+ },
331
+ });
332
+
333
+ <LabeledTextFieldExample />
334
+ ```
335
+
336
+ The field container can have a style
337
+
338
+ ```js
339
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
340
+ import {View} from "@khanacademy/wonder-blocks-core";
341
+ import {Strut} from "@khanacademy/wonder-blocks-layout";
342
+ import Spacing from "@khanacademy/wonder-blocks-spacing";
343
+ import {StyleSheet} from "aphrodite";
344
+
345
+ class LabeledTextFieldExample extends React.Component {
346
+ constructor(props) {
347
+ super(props);
348
+ this.state = {
349
+ firstName: "",
350
+ lastName: "",
351
+ };
352
+ }
353
+
354
+ handleKeyDown(event) {
355
+ if (event.key === "Enter") {
356
+ event.currentTarget.blur();
357
+ }
358
+ }
359
+
360
+ render() {
361
+ return (
362
+ <View style={styles.row}>
363
+ <LabeledTextField
364
+ label="First name"
365
+ description="Please enter your first name"
366
+ placeholder="Khan"
367
+ value={this.state.firstName}
368
+ onChange={(newValue) => this.setState({firstName: newValue})}
369
+ placeholder="Khan"
370
+ style={styles.grow}
371
+ onKeyDown={this.handleKeyDown}
372
+ />
373
+ <Strut size={Spacing.xLarge_32} />
374
+ <LabeledTextField
375
+ label="Last name"
376
+ description="Please enter your last name"
377
+ placeholder="Academy"
378
+ value={this.state.lastName}
379
+ onChange={(newValue) => this.setState({lastName: newValue})}
380
+ placeholder="Academy"
381
+ style={styles.grow}
382
+ onKeyDown={this.handleKeyDown}
383
+ />
384
+ </View>
385
+ );
386
+ }
387
+ }
388
+
389
+ const styles = StyleSheet.create({
390
+ row: {
391
+ flexDirection: "row",
392
+ },
393
+ grow: {
394
+ flexGrow: 1,
395
+ },
396
+ });
397
+
398
+ <LabeledTextFieldExample />
399
+ ```
400
+
401
+ The field forwards its ref to the input
402
+
403
+ ```js
404
+ import {View} from "@khanacademy/wonder-blocks-core";
405
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
406
+ import Button from "@khanacademy/wonder-blocks-button";
407
+ import {Strut} from "@khanacademy/wonder-blocks-layout";
408
+ import Spacing from "@khanacademy/wonder-blocks-spacing";
409
+ import {StyleSheet} from "aphrodite";
410
+
411
+ class LabeledTextFieldExample extends React.Component {
412
+ constructor(props) {
413
+ super(props);
414
+ this.state = {
415
+ value: "Khan",
416
+ };
417
+ this.inputRef = React.createRef();
418
+ this.handleSubmit = this.handleSubmit.bind(this);
419
+ }
420
+
421
+ handleKeyDown(event) {
422
+ if (event.key === "Enter") {
423
+ event.currentTarget.blur();
424
+ }
425
+ }
426
+
427
+ handleSubmit() {
428
+ if (this.inputRef.current) {
429
+ this.inputRef.current.focus();
430
+ }
431
+ }
432
+
433
+ render() {
434
+ return (
435
+ <View>
436
+ <LabeledTextField
437
+ label="Name"
438
+ description="Please enter your name"
439
+ value={this.state.value}
440
+ onChange={(newValue) => this.setState({value: newValue})}
441
+ placeholder="Name"
442
+ onKeyDown={this.handleKeyDown}
443
+ ref={this.inputRef}
444
+ />
445
+ <Strut size={Spacing.medium_16} />
446
+ <Button style={styles.button} onClick={this.handleSubmit}>
447
+ Focus Input
448
+ </Button>
449
+ </View>
450
+ );
451
+ }
452
+ }
453
+
454
+ const styles = StyleSheet.create({
455
+ button: {
456
+ maxWidth: 150,
457
+ },
458
+ });
459
+
460
+ <LabeledTextFieldExample />
461
+ ```
462
+
463
+ The field can be read-only
464
+
465
+ ```js
466
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
467
+
468
+ class LabeledTextFieldExample extends React.Component {
469
+ constructor(props) {
470
+ super(props);
471
+ this.state = {
472
+ value: "Khan",
473
+ };
474
+ }
475
+
476
+ handleKeyDown(event) {
477
+ if (event.key === "Enter") {
478
+ event.currentTarget.blur();
479
+ }
480
+ }
481
+
482
+ render() {
483
+ return (
484
+ <LabeledTextField
485
+ label="Name"
486
+ description="Please enter your name"
487
+ value={this.state.value}
488
+ onChange={(newValue) => this.setState({value: newValue})}
489
+ placeholder="Name"
490
+ onKeyDown={this.handleKeyDown}
491
+ readOnly={true}
492
+ />
493
+ );
494
+ }
495
+ }
496
+
497
+ <LabeledTextFieldExample />
498
+ ```
499
+
500
+ The field can have specific autocomplete
501
+
502
+ ```js
503
+ import {LabeledTextField} from "@khanacademy/wonder-blocks-form";
504
+
505
+ class LabeledTextFieldExample extends React.Component {
506
+ constructor(props) {
507
+ super(props);
508
+ this.state = {
509
+ value: "",
510
+ };
511
+ }
512
+
513
+ handleKeyDown(event) {
514
+ if (event.key === "Enter") {
515
+ event.currentTarget.blur();
516
+ }
517
+ }
518
+
519
+ render() {
520
+ return (
521
+ <LabeledTextField
522
+ label="Name"
523
+ description="Please enter your name"
524
+ value={this.state.value}
525
+ onChange={(newValue) => this.setState({value: newValue})}
526
+ placeholder="Name"
527
+ onKeyDown={this.handleKeyDown}
528
+ autoComplete="name"
529
+ />
530
+ );
531
+ }
532
+ }
533
+
534
+ <LabeledTextFieldExample />
535
+ ```