@khanacademy/wonder-blocks-form 2.4.8 → 3.0.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.
Files changed (31) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/es/index.js +11 -11
  3. package/dist/index.js +71 -75
  4. package/docs.md +5 -1
  5. package/package.json +2 -2
  6. package/src/__docs__/_overview_.stories.mdx +15 -0
  7. package/src/components/__docs__/checkbox-group.stories.js +0 -1
  8. package/src/components/__docs__/labeled-text-field.argtypes.js +2 -2
  9. package/src/components/__docs__/labeled-text-field.stories.js +25 -0
  10. package/src/components/__docs__/radio.stories.js +3 -2
  11. package/src/components/__tests__/checkbox-group.test.js +118 -67
  12. package/src/components/__tests__/field-heading.test.js +40 -0
  13. package/src/components/__tests__/radio-group.test.js +131 -58
  14. package/src/components/checkbox-group.js +5 -13
  15. package/src/components/checkbox.js +2 -2
  16. package/src/components/choice-internal.js +5 -3
  17. package/src/components/choice.js +2 -2
  18. package/src/components/field-heading.js +27 -43
  19. package/src/components/labeled-text-field.js +2 -3
  20. package/src/components/radio-group.js +2 -2
  21. package/src/components/radio.js +2 -2
  22. package/src/index.js +0 -2
  23. package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +0 -6126
  24. package/src/__tests__/generated-snapshot.test.js +0 -654
  25. package/src/components/checkbox-group.md +0 -200
  26. package/src/components/checkbox.md +0 -134
  27. package/src/components/field-heading.md +0 -43
  28. package/src/components/labeled-text-field.md +0 -535
  29. package/src/components/radio-group.md +0 -129
  30. package/src/components/radio.md +0 -26
  31. package/src/components/text-field.md +0 -770
@@ -1,535 +0,0 @@
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
- ```
@@ -1,129 +0,0 @@
1
- This example has a disabled item. Selecting the last item results in an error
2
- state for the entire group.
3
-
4
- Try out the keyboard navigation! Use up/left and down/right to navigate among
5
- the choices, and use tab to navigate between this radio group and the rest of
6
- the page.
7
-
8
- ```js
9
- import {Choice, RadioGroup} from "@khanacademy/wonder-blocks-form";
10
- import {View} from "@khanacademy/wonder-blocks-core";
11
- import {StyleSheet} from "aphrodite";
12
-
13
- const styles = StyleSheet.create({
14
- wrapper: {
15
- width: 300,
16
- },
17
- });
18
-
19
- class RadioGroupPokemonExample extends React.Component {
20
- constructor() {
21
- super();
22
- this.state = {
23
- selectedValue: null,
24
- };
25
- this.handleChange = this.handleChange.bind(this);
26
- }
27
-
28
- handleChange(change) {
29
- console.log(`${change} was selected!`);
30
- const error = this.checkForError(change);
31
- this.setState({
32
- selectedValue: change,
33
- error: error,
34
- });
35
- }
36
-
37
- checkForError(input) {
38
- if (input === "infiltrator") {
39
- return "Superman isn't a Pokemon!";
40
- }
41
- }
42
-
43
- render() {
44
- return <RadioGroup
45
- label="Choose a starter!"
46
- description="Your first Pokemon"
47
- errorMessage={this.state.error}
48
- groupName="Pokemon"
49
- onChange={this.handleChange}
50
- selectedValue={this.state.selectedValue}
51
- >
52
- <Choice label="Bulbasaur" value="bulb" />
53
- <Choice label="Charmander" value="char" description="Oops, we ran out of Charmanders" disabled />
54
- <Choice label="Squirtle" value="squirt" />
55
- <Choice label="Pikachu" value="pika" />
56
- <Choice label="Superman" value="infiltrator" />
57
- </RadioGroup>
58
- }
59
- }
60
- <View style={styles.wrapper}>
61
- <RadioGroupPokemonExample />
62
- </View>
63
- ```
64
-
65
- This example shows how to use custom styling to change the appearance of the
66
- radio group to look more like a multiple choice question.
67
-
68
- ```js
69
- import {Choice, RadioGroup} from "@khanacademy/wonder-blocks-form";
70
- import {View} from "@khanacademy/wonder-blocks-core";
71
- import Color from "@khanacademy/wonder-blocks-color";
72
- import {LabelLarge} from "@khanacademy/wonder-blocks-typography";
73
- import {StyleSheet} from "aphrodite";
74
-
75
- const styles = StyleSheet.create({
76
- wrapper: {
77
- width: 650,
78
- },
79
- choice: {
80
- margin: 0,
81
- height: 48,
82
- borderTop: "solid 1px #CCC",
83
- justifyContent: "center",
84
- ":last-child": {
85
- borderBottom: "solid 1px #CCC",
86
- },
87
- },
88
- prompt: {
89
- marginBottom: 16,
90
- },
91
- });
92
-
93
- class ClassSelectorExample extends React.Component {
94
- constructor() {
95
- super();
96
- this.state = {
97
- selectedValue: null,
98
- };
99
- this.handleChange = this.handleChange.bind(this);
100
- }
101
-
102
- handleChange(change) {
103
- console.log(`${change} was selected!`);
104
- this.setState({
105
- selectedValue: change,
106
- });
107
- }
108
-
109
- render() {
110
- return <RadioGroup
111
- groupName="science-classes"
112
- onChange={this.handleChange}
113
- selectedValue={this.state.selectedValue}
114
- >
115
- <Choice label="A" value="1" style={styles.choice} />
116
- <Choice label="B" value="2" style={styles.choice} />
117
- <Choice label="AB" value="3" style={styles.choice} />
118
- <Choice label="O" value="4" style={styles.choice} />
119
- </RadioGroup>
120
- }
121
- }
122
-
123
- <View>
124
- <LabelLarge style={styles.prompt}>
125
- Select your blood type
126
- </LabelLarge>
127
- <ClassSelectorExample />
128
- </View>
129
- ```
@@ -1,26 +0,0 @@
1
- The radio button has various styles for clickable states. Here are sets of default radio buttons, radio buttons in an error state, and disabled radio buttons.
2
- ```js
3
- import {View} from "@khanacademy/wonder-blocks-core";
4
- import {Radio} from "@khanacademy/wonder-blocks-form";
5
- import {StyleSheet} from "aphrodite";
6
-
7
- const styles = StyleSheet.create({
8
- row: {
9
- flexDirection: "row",
10
- },
11
- marginRight: {
12
- marginRight: 16,
13
- }
14
- });
15
-
16
- const handleChange = (checked) => console.log(`clicked on radio, will be checked=${checked.toString()}`);
17
-
18
- <View style={styles.row}>
19
- <Radio error={false} checked={false} style={styles.marginRight} onChange={handleChange} />
20
- <Radio error={false} checked={true} style={styles.marginRight} onChange={handleChange} />
21
- <Radio error={true} checked={false} style={styles.marginRight} onChange={handleChange} />
22
- <Radio error={true} checked={true} style={styles.marginRight} onChange={handleChange} />
23
- <Radio disabled={true} checked={false} style={styles.marginRight} onChange={handleChange} />
24
- <Radio disabled={true} checked={true} style={styles.marginRight} onChange={handleChange} />
25
- </View>
26
- ```