@khanacademy/wonder-blocks-form 4.0.9 → 4.1.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/CHANGELOG.md +6 -0
- package/dist/components/checkbox-core.d.ts +3 -0
- package/dist/components/checkbox-core.js.flow +3 -0
- package/dist/components/checkbox.d.ts +3 -2
- package/dist/components/checkbox.js.flow +3 -2
- package/dist/components/choice-internal.d.ts +1 -2
- package/dist/components/choice-internal.js.flow +1 -2
- package/dist/es/index.js +39 -12
- package/dist/index.js +39 -12
- package/dist/util/types.d.ts +2 -1
- package/dist/util/types.js.flow +2 -1
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/custom-snapshot.test.tsx.snap +308 -0
- package/src/__tests__/custom-snapshot.test.tsx +1 -1
- package/src/components/checkbox-core.tsx +54 -15
- package/src/components/checkbox.tsx +4 -2
- package/src/components/choice-internal.tsx +1 -3
- package/src/components/radio-core.tsx +4 -3
- package/src/util/types.ts +4 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`CheckboxCore type:default checked:false 1`] = `
|
|
4
4
|
<input
|
|
5
|
+
aria-checked="false"
|
|
5
6
|
aria-invalid={false}
|
|
6
7
|
checked={false}
|
|
7
8
|
className=""
|
|
@@ -46,9 +47,73 @@ exports[`CheckboxCore type:default checked:false 1`] = `
|
|
|
46
47
|
/>
|
|
47
48
|
`;
|
|
48
49
|
|
|
50
|
+
exports[`CheckboxCore type:default checked:null 1`] = `
|
|
51
|
+
[
|
|
52
|
+
<input
|
|
53
|
+
aria-checked="mixed"
|
|
54
|
+
aria-invalid={false}
|
|
55
|
+
className=""
|
|
56
|
+
disabled={false}
|
|
57
|
+
onChange={[Function]}
|
|
58
|
+
onClick={[Function]}
|
|
59
|
+
style={
|
|
60
|
+
{
|
|
61
|
+
":active": {
|
|
62
|
+
"background": "#1b50b3",
|
|
63
|
+
"boxShadow": "0 0 0 1px #ffffff, 0 0 0 3px #1b50b3",
|
|
64
|
+
},
|
|
65
|
+
":focusVisible": {
|
|
66
|
+
"boxShadow": "0 0 0 1px #ffffff, 0 0 0 3px #1865f2",
|
|
67
|
+
},
|
|
68
|
+
":hover": {
|
|
69
|
+
"boxShadow": "0 0 0 1px #ffffff, 0 0 0 3px #1865f2",
|
|
70
|
+
},
|
|
71
|
+
"MozAppearance": "none",
|
|
72
|
+
"WebkitAppearance": "none",
|
|
73
|
+
"appearance": "none",
|
|
74
|
+
"backgroundColor": "#1865f2",
|
|
75
|
+
"borderRadius": 3,
|
|
76
|
+
"borderStyle": "solid",
|
|
77
|
+
"borderWidth": 0,
|
|
78
|
+
"boxSizing": "border-box",
|
|
79
|
+
"height": 16,
|
|
80
|
+
"margin": 0,
|
|
81
|
+
"minHeight": 16,
|
|
82
|
+
"minWidth": 16,
|
|
83
|
+
"outline": "none",
|
|
84
|
+
"width": 16,
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
type="checkbox"
|
|
88
|
+
/>,
|
|
89
|
+
<svg
|
|
90
|
+
className=""
|
|
91
|
+
height={16}
|
|
92
|
+
style={
|
|
93
|
+
{
|
|
94
|
+
"display": "inline-block",
|
|
95
|
+
"flexGrow": 0,
|
|
96
|
+
"flexShrink": 0,
|
|
97
|
+
"pointerEvents": "none",
|
|
98
|
+
"position": "absolute",
|
|
99
|
+
"verticalAlign": "text-bottom",
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
viewBox="0 0 16 16"
|
|
103
|
+
width={16}
|
|
104
|
+
>
|
|
105
|
+
<path
|
|
106
|
+
d="M3 8C3 7.44772 3.44772 7 4 7H12C12.5523 7 13 7.44772 13 8C13 8.55228 12.5523 9 12 9H4C3.44772 9 3 8.55228 3 8Z"
|
|
107
|
+
fill="#ffffff"
|
|
108
|
+
/>
|
|
109
|
+
</svg>,
|
|
110
|
+
]
|
|
111
|
+
`;
|
|
112
|
+
|
|
49
113
|
exports[`CheckboxCore type:default checked:true 1`] = `
|
|
50
114
|
[
|
|
51
115
|
<input
|
|
116
|
+
aria-checked="true"
|
|
52
117
|
aria-invalid={false}
|
|
53
118
|
checked={true}
|
|
54
119
|
className=""
|
|
@@ -111,6 +176,7 @@ exports[`CheckboxCore type:default checked:true 1`] = `
|
|
|
111
176
|
|
|
112
177
|
exports[`CheckboxCore type:disabled checked:false 1`] = `
|
|
113
178
|
<input
|
|
179
|
+
aria-checked="false"
|
|
114
180
|
aria-invalid={false}
|
|
115
181
|
checked={false}
|
|
116
182
|
className=""
|
|
@@ -141,9 +207,65 @@ exports[`CheckboxCore type:disabled checked:false 1`] = `
|
|
|
141
207
|
/>
|
|
142
208
|
`;
|
|
143
209
|
|
|
210
|
+
exports[`CheckboxCore type:disabled checked:null 1`] = `
|
|
211
|
+
[
|
|
212
|
+
<input
|
|
213
|
+
aria-checked="mixed"
|
|
214
|
+
aria-invalid={false}
|
|
215
|
+
className=""
|
|
216
|
+
disabled={true}
|
|
217
|
+
onChange={[Function]}
|
|
218
|
+
onClick={[Function]}
|
|
219
|
+
style={
|
|
220
|
+
{
|
|
221
|
+
"MozAppearance": "none",
|
|
222
|
+
"WebkitAppearance": "none",
|
|
223
|
+
"appearance": "none",
|
|
224
|
+
"backgroundColor": "#f7f8fa",
|
|
225
|
+
"borderColor": "rgba(33,36,44,0.16)",
|
|
226
|
+
"borderRadius": 3,
|
|
227
|
+
"borderStyle": "solid",
|
|
228
|
+
"borderWidth": 1,
|
|
229
|
+
"boxSizing": "border-box",
|
|
230
|
+
"cursor": "auto",
|
|
231
|
+
"height": 16,
|
|
232
|
+
"margin": 0,
|
|
233
|
+
"minHeight": 16,
|
|
234
|
+
"minWidth": 16,
|
|
235
|
+
"outline": "none",
|
|
236
|
+
"width": 16,
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
type="checkbox"
|
|
240
|
+
/>,
|
|
241
|
+
<svg
|
|
242
|
+
className=""
|
|
243
|
+
height={16}
|
|
244
|
+
style={
|
|
245
|
+
{
|
|
246
|
+
"display": "inline-block",
|
|
247
|
+
"flexGrow": 0,
|
|
248
|
+
"flexShrink": 0,
|
|
249
|
+
"pointerEvents": "none",
|
|
250
|
+
"position": "absolute",
|
|
251
|
+
"verticalAlign": "text-bottom",
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
viewBox="0 0 16 16"
|
|
255
|
+
width={16}
|
|
256
|
+
>
|
|
257
|
+
<path
|
|
258
|
+
d="M3 8C3 7.44772 3.44772 7 4 7H12C12.5523 7 13 7.44772 13 8C13 8.55228 12.5523 9 12 9H4C3.44772 9 3 8.55228 3 8Z"
|
|
259
|
+
fill="rgba(33,36,44,0.32)"
|
|
260
|
+
/>
|
|
261
|
+
</svg>,
|
|
262
|
+
]
|
|
263
|
+
`;
|
|
264
|
+
|
|
144
265
|
exports[`CheckboxCore type:disabled checked:true 1`] = `
|
|
145
266
|
[
|
|
146
267
|
<input
|
|
268
|
+
aria-checked="true"
|
|
147
269
|
aria-invalid={false}
|
|
148
270
|
checked={true}
|
|
149
271
|
className=""
|
|
@@ -198,6 +320,7 @@ exports[`CheckboxCore type:disabled checked:true 1`] = `
|
|
|
198
320
|
|
|
199
321
|
exports[`CheckboxCore type:error checked:false 1`] = `
|
|
200
322
|
<input
|
|
323
|
+
aria-checked="false"
|
|
201
324
|
aria-invalid={true}
|
|
202
325
|
checked={false}
|
|
203
326
|
className=""
|
|
@@ -242,9 +365,73 @@ exports[`CheckboxCore type:error checked:false 1`] = `
|
|
|
242
365
|
/>
|
|
243
366
|
`;
|
|
244
367
|
|
|
368
|
+
exports[`CheckboxCore type:error checked:null 1`] = `
|
|
369
|
+
[
|
|
370
|
+
<input
|
|
371
|
+
aria-checked="mixed"
|
|
372
|
+
aria-invalid={true}
|
|
373
|
+
className=""
|
|
374
|
+
disabled={false}
|
|
375
|
+
onChange={[Function]}
|
|
376
|
+
onClick={[Function]}
|
|
377
|
+
style={
|
|
378
|
+
{
|
|
379
|
+
":active": {
|
|
380
|
+
"background": "#9e271d",
|
|
381
|
+
"boxShadow": "0 0 0 1px #ffffff, 0 0 0 3px #9e271d",
|
|
382
|
+
},
|
|
383
|
+
":focusVisible": {
|
|
384
|
+
"boxShadow": "0 0 0 1px #ffffff, 0 0 0 3px #d92916",
|
|
385
|
+
},
|
|
386
|
+
":hover": {
|
|
387
|
+
"boxShadow": "0 0 0 1px #ffffff, 0 0 0 3px #d92916",
|
|
388
|
+
},
|
|
389
|
+
"MozAppearance": "none",
|
|
390
|
+
"WebkitAppearance": "none",
|
|
391
|
+
"appearance": "none",
|
|
392
|
+
"backgroundColor": "#d92916",
|
|
393
|
+
"borderRadius": 3,
|
|
394
|
+
"borderStyle": "solid",
|
|
395
|
+
"borderWidth": 0,
|
|
396
|
+
"boxSizing": "border-box",
|
|
397
|
+
"height": 16,
|
|
398
|
+
"margin": 0,
|
|
399
|
+
"minHeight": 16,
|
|
400
|
+
"minWidth": 16,
|
|
401
|
+
"outline": "none",
|
|
402
|
+
"width": 16,
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
type="checkbox"
|
|
406
|
+
/>,
|
|
407
|
+
<svg
|
|
408
|
+
className=""
|
|
409
|
+
height={16}
|
|
410
|
+
style={
|
|
411
|
+
{
|
|
412
|
+
"display": "inline-block",
|
|
413
|
+
"flexGrow": 0,
|
|
414
|
+
"flexShrink": 0,
|
|
415
|
+
"pointerEvents": "none",
|
|
416
|
+
"position": "absolute",
|
|
417
|
+
"verticalAlign": "text-bottom",
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
viewBox="0 0 16 16"
|
|
421
|
+
width={16}
|
|
422
|
+
>
|
|
423
|
+
<path
|
|
424
|
+
d="M3 8C3 7.44772 3.44772 7 4 7H12C12.5523 7 13 7.44772 13 8C13 8.55228 12.5523 9 12 9H4C3.44772 9 3 8.55228 3 8Z"
|
|
425
|
+
fill="#ffffff"
|
|
426
|
+
/>
|
|
427
|
+
</svg>,
|
|
428
|
+
]
|
|
429
|
+
`;
|
|
430
|
+
|
|
245
431
|
exports[`CheckboxCore type:error checked:true 1`] = `
|
|
246
432
|
[
|
|
247
433
|
<input
|
|
434
|
+
aria-checked="true"
|
|
248
435
|
aria-invalid={true}
|
|
249
436
|
checked={true}
|
|
250
437
|
className=""
|
|
@@ -351,6 +538,51 @@ exports[`RadioCore type:default checked:false 1`] = `
|
|
|
351
538
|
/>
|
|
352
539
|
`;
|
|
353
540
|
|
|
541
|
+
exports[`RadioCore type:default checked:null 1`] = `
|
|
542
|
+
<input
|
|
543
|
+
aria-invalid={false}
|
|
544
|
+
className=""
|
|
545
|
+
disabled={false}
|
|
546
|
+
onChange={[Function]}
|
|
547
|
+
onClick={[Function]}
|
|
548
|
+
style={
|
|
549
|
+
{
|
|
550
|
+
":active": {
|
|
551
|
+
"backgroundColor": "#dae6fd",
|
|
552
|
+
"borderColor": "#1865f2",
|
|
553
|
+
"borderWidth": 2,
|
|
554
|
+
},
|
|
555
|
+
":focusVisible": {
|
|
556
|
+
"backgroundColor": "#ffffff",
|
|
557
|
+
"borderColor": "#1865f2",
|
|
558
|
+
"borderWidth": 2,
|
|
559
|
+
},
|
|
560
|
+
":hover": {
|
|
561
|
+
"backgroundColor": "#ffffff",
|
|
562
|
+
"borderColor": "#1865f2",
|
|
563
|
+
"borderWidth": 2,
|
|
564
|
+
},
|
|
565
|
+
"MozAppearance": "none",
|
|
566
|
+
"WebkitAppearance": "none",
|
|
567
|
+
"appearance": "none",
|
|
568
|
+
"backgroundColor": "#ffffff",
|
|
569
|
+
"borderColor": "rgba(33,36,44,0.50)",
|
|
570
|
+
"borderRadius": "50%",
|
|
571
|
+
"borderStyle": "solid",
|
|
572
|
+
"borderWidth": 1,
|
|
573
|
+
"boxSizing": "border-box",
|
|
574
|
+
"height": 16,
|
|
575
|
+
"margin": 0,
|
|
576
|
+
"minHeight": 16,
|
|
577
|
+
"minWidth": 16,
|
|
578
|
+
"outline": "none",
|
|
579
|
+
"width": 16,
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
type="radio"
|
|
583
|
+
/>
|
|
584
|
+
`;
|
|
585
|
+
|
|
354
586
|
exports[`RadioCore type:default checked:true 1`] = `
|
|
355
587
|
<input
|
|
356
588
|
aria-invalid={false}
|
|
@@ -424,6 +656,37 @@ exports[`RadioCore type:disabled checked:false 1`] = `
|
|
|
424
656
|
/>
|
|
425
657
|
`;
|
|
426
658
|
|
|
659
|
+
exports[`RadioCore type:disabled checked:null 1`] = `
|
|
660
|
+
<input
|
|
661
|
+
aria-invalid={false}
|
|
662
|
+
className=""
|
|
663
|
+
disabled={true}
|
|
664
|
+
onChange={[Function]}
|
|
665
|
+
onClick={[Function]}
|
|
666
|
+
style={
|
|
667
|
+
{
|
|
668
|
+
"MozAppearance": "none",
|
|
669
|
+
"WebkitAppearance": "none",
|
|
670
|
+
"appearance": "none",
|
|
671
|
+
"backgroundColor": "#f7f8fa",
|
|
672
|
+
"borderColor": "rgba(33,36,44,0.16)",
|
|
673
|
+
"borderRadius": "50%",
|
|
674
|
+
"borderStyle": "solid",
|
|
675
|
+
"borderWidth": 1,
|
|
676
|
+
"boxSizing": "border-box",
|
|
677
|
+
"cursor": "auto",
|
|
678
|
+
"height": 16,
|
|
679
|
+
"margin": 0,
|
|
680
|
+
"minHeight": 16,
|
|
681
|
+
"minWidth": 16,
|
|
682
|
+
"outline": "none",
|
|
683
|
+
"width": 16,
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
type="radio"
|
|
687
|
+
/>
|
|
688
|
+
`;
|
|
689
|
+
|
|
427
690
|
exports[`RadioCore type:disabled checked:true 1`] = `
|
|
428
691
|
[
|
|
429
692
|
<input
|
|
@@ -517,6 +780,51 @@ exports[`RadioCore type:error checked:false 1`] = `
|
|
|
517
780
|
/>
|
|
518
781
|
`;
|
|
519
782
|
|
|
783
|
+
exports[`RadioCore type:error checked:null 1`] = `
|
|
784
|
+
<input
|
|
785
|
+
aria-invalid={true}
|
|
786
|
+
className=""
|
|
787
|
+
disabled={false}
|
|
788
|
+
onChange={[Function]}
|
|
789
|
+
onClick={[Function]}
|
|
790
|
+
style={
|
|
791
|
+
{
|
|
792
|
+
":active": {
|
|
793
|
+
"backgroundColor": "#fceeec",
|
|
794
|
+
"borderColor": "#9e271d",
|
|
795
|
+
"borderWidth": 2,
|
|
796
|
+
},
|
|
797
|
+
":focusVisible": {
|
|
798
|
+
"backgroundColor": "#fceeec",
|
|
799
|
+
"borderColor": "#d92916",
|
|
800
|
+
"borderWidth": 2,
|
|
801
|
+
},
|
|
802
|
+
":hover": {
|
|
803
|
+
"backgroundColor": "#fceeec",
|
|
804
|
+
"borderColor": "#d92916",
|
|
805
|
+
"borderWidth": 2,
|
|
806
|
+
},
|
|
807
|
+
"MozAppearance": "none",
|
|
808
|
+
"WebkitAppearance": "none",
|
|
809
|
+
"appearance": "none",
|
|
810
|
+
"backgroundColor": "#fceeec",
|
|
811
|
+
"borderColor": "#d92916",
|
|
812
|
+
"borderRadius": "50%",
|
|
813
|
+
"borderStyle": "solid",
|
|
814
|
+
"borderWidth": 1,
|
|
815
|
+
"boxSizing": "border-box",
|
|
816
|
+
"height": 16,
|
|
817
|
+
"margin": 0,
|
|
818
|
+
"minHeight": 16,
|
|
819
|
+
"minWidth": 16,
|
|
820
|
+
"outline": "none",
|
|
821
|
+
"width": 16,
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
type="radio"
|
|
825
|
+
/>
|
|
826
|
+
`;
|
|
827
|
+
|
|
520
828
|
exports[`RadioCore type:error checked:true 1`] = `
|
|
521
829
|
<input
|
|
522
830
|
aria-invalid={true}
|
|
@@ -5,7 +5,7 @@ import CheckboxCore from "../components/checkbox-core";
|
|
|
5
5
|
import RadioCore from "../components/radio-core";
|
|
6
6
|
|
|
7
7
|
const states = ["default", "error", "disabled"];
|
|
8
|
-
const checkedStates = [false, true];
|
|
8
|
+
const checkedStates = [false, true, null];
|
|
9
9
|
|
|
10
10
|
describe("CheckboxCore", () => {
|
|
11
11
|
states.forEach((state: any) => {
|
|
@@ -6,21 +6,54 @@ import {addStyle} from "@khanacademy/wonder-blocks-core";
|
|
|
6
6
|
import Icon from "@khanacademy/wonder-blocks-icon";
|
|
7
7
|
|
|
8
8
|
import type {IconAsset} from "@khanacademy/wonder-blocks-icon";
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
import type {ChoiceCoreProps, Checked} from "../util/types";
|
|
10
|
+
|
|
11
|
+
// `AriaChecked` and `mapCheckedToAriaChecked()` are used to convert the
|
|
12
|
+
// `checked` prop value to a value that a screen reader can understand via the
|
|
13
|
+
// `aria-checked` attribute
|
|
14
|
+
type AriaChecked = "true" | "false" | "mixed";
|
|
15
|
+
|
|
16
|
+
function mapCheckedToAriaChecked(value: Checked): AriaChecked {
|
|
17
|
+
switch (value) {
|
|
18
|
+
case true:
|
|
19
|
+
return "true";
|
|
20
|
+
case false:
|
|
21
|
+
return "false";
|
|
22
|
+
default:
|
|
23
|
+
return "mixed";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
11
26
|
|
|
12
27
|
const {blue, red, white, offWhite, offBlack16, offBlack32, offBlack50} = Color;
|
|
13
28
|
|
|
14
29
|
const StyledInput = addStyle("input");
|
|
15
30
|
|
|
16
|
-
const
|
|
31
|
+
const checkPath: IconAsset = {
|
|
17
32
|
small: "M11.263 4.324a1 1 0 1 1 1.474 1.352l-5.5 6a1 1 0 0 1-1.505-.036l-2.5-3a1 1 0 1 1 1.536-1.28L6.536 9.48l4.727-5.157z",
|
|
18
33
|
};
|
|
19
34
|
|
|
35
|
+
const indeterminatePath: IconAsset = {
|
|
36
|
+
small: "M3 8C3 7.44772 3.44772 7 4 7H12C12.5523 7 13 7.44772 13 8C13 8.55228 12.5523 9 12 9H4C3.44772 9 3 8.55228 3 8Z",
|
|
37
|
+
};
|
|
38
|
+
|
|
20
39
|
/**
|
|
21
40
|
* The internal stateless ☑️ Checkbox
|
|
22
41
|
*/
|
|
23
42
|
export default class CheckboxCore extends React.Component<ChoiceCoreProps> {
|
|
43
|
+
componentDidMount(): void {
|
|
44
|
+
if (this.props.checked == null && this.inputRef.current != null) {
|
|
45
|
+
this.inputRef.current.indeterminate = true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
componentDidUpdate(prevProps: Readonly<ChoiceCoreProps>): void {
|
|
50
|
+
if (this.inputRef.current != null) {
|
|
51
|
+
this.inputRef.current.indeterminate = this.props.checked == null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
inputRef: React.RefObject<HTMLInputElement> = React.createRef();
|
|
56
|
+
|
|
24
57
|
handleChange: () => void = () => {
|
|
25
58
|
// Empty because change is handled by ClickableBehavior
|
|
26
59
|
return;
|
|
@@ -50,13 +83,26 @@ export default class CheckboxCore extends React.Component<ChoiceCoreProps> {
|
|
|
50
83
|
"data-test-id": testId,
|
|
51
84
|
} as const;
|
|
52
85
|
|
|
86
|
+
const checkboxIcon = (
|
|
87
|
+
<Icon
|
|
88
|
+
color={disabled ? offBlack32 : white}
|
|
89
|
+
icon={checked ? checkPath : indeterminatePath}
|
|
90
|
+
size="small"
|
|
91
|
+
style={sharedStyles.checkboxIcon}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const ariaChecked = mapCheckedToAriaChecked(checked);
|
|
96
|
+
|
|
53
97
|
return (
|
|
54
98
|
<React.Fragment>
|
|
55
99
|
<StyledInput
|
|
56
100
|
{...sharedProps}
|
|
101
|
+
ref={this.inputRef}
|
|
57
102
|
type="checkbox"
|
|
103
|
+
aria-checked={ariaChecked}
|
|
58
104
|
aria-invalid={error}
|
|
59
|
-
checked={checked}
|
|
105
|
+
checked={checked ?? undefined}
|
|
60
106
|
disabled={disabled}
|
|
61
107
|
id={id}
|
|
62
108
|
name={groupName}
|
|
@@ -66,14 +112,7 @@ export default class CheckboxCore extends React.Component<ChoiceCoreProps> {
|
|
|
66
112
|
style={defaultStyle}
|
|
67
113
|
{...props}
|
|
68
114
|
/>
|
|
69
|
-
{checked
|
|
70
|
-
<Icon
|
|
71
|
-
color={disabled ? offBlack32 : white}
|
|
72
|
-
icon={checkboxCheck}
|
|
73
|
-
size="small"
|
|
74
|
-
style={sharedStyles.checkIcon}
|
|
75
|
-
/>
|
|
76
|
-
)}
|
|
115
|
+
{checked || checked == null ? checkboxIcon : <></>}
|
|
77
116
|
</React.Fragment>
|
|
78
117
|
);
|
|
79
118
|
}
|
|
@@ -109,7 +148,7 @@ const sharedStyles = StyleSheet.create({
|
|
|
109
148
|
borderWidth: 1,
|
|
110
149
|
},
|
|
111
150
|
|
|
112
|
-
|
|
151
|
+
checkboxIcon: {
|
|
113
152
|
position: "absolute",
|
|
114
153
|
pointerEvents: "none",
|
|
115
154
|
},
|
|
@@ -135,7 +174,7 @@ const colors = {
|
|
|
135
174
|
|
|
136
175
|
const styles: Record<string, any> = {};
|
|
137
176
|
|
|
138
|
-
const _generateStyles = (checked:
|
|
177
|
+
const _generateStyles = (checked: Checked, error: boolean) => {
|
|
139
178
|
// "hash" the parameters
|
|
140
179
|
const styleKey = `${String(checked)}-${String(error)}`;
|
|
141
180
|
if (styles[styleKey]) {
|
|
@@ -145,7 +184,7 @@ const _generateStyles = (checked: boolean, error: boolean) => {
|
|
|
145
184
|
const palette = error ? colors.error : colors.default;
|
|
146
185
|
|
|
147
186
|
let newStyles: Record<string, any> = {};
|
|
148
|
-
if (checked) {
|
|
187
|
+
if (checked || checked == null) {
|
|
149
188
|
newStyles = {
|
|
150
189
|
default: {
|
|
151
190
|
backgroundColor: palette.base,
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
3
|
import type {AriaProps, StyleType} from "@khanacademy/wonder-blocks-core";
|
|
4
|
+
import type {Checked} from "../util/types";
|
|
5
|
+
|
|
4
6
|
import ChoiceInternal from "./choice-internal";
|
|
5
7
|
|
|
6
8
|
// Keep synced with ChoiceComponentProps in ../util/types.js
|
|
7
9
|
type ChoiceComponentProps = AriaProps & {
|
|
8
10
|
/**
|
|
9
|
-
* Whether this component is checked
|
|
11
|
+
* Whether this component is checked or indeterminate
|
|
10
12
|
*/
|
|
11
|
-
checked:
|
|
13
|
+
checked: Checked;
|
|
12
14
|
/**
|
|
13
15
|
* Whether this component is disabled
|
|
14
16
|
*/
|
|
@@ -12,7 +12,7 @@ import RadioCore from "./radio-core";
|
|
|
12
12
|
|
|
13
13
|
type Props = AriaProps & {
|
|
14
14
|
/** Whether this choice is checked. */
|
|
15
|
-
checked: boolean;
|
|
15
|
+
checked: boolean | null | undefined;
|
|
16
16
|
/** Whether this choice option is disabled. */
|
|
17
17
|
disabled: boolean;
|
|
18
18
|
/** Whether this choice is in error mode. */
|
|
@@ -49,7 +49,6 @@ type Props = AriaProps & {
|
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
type DefaultProps = {
|
|
52
|
-
checked: Props["checked"];
|
|
53
52
|
disabled: Props["disabled"];
|
|
54
53
|
error: Props["error"];
|
|
55
54
|
};
|
|
@@ -63,7 +62,6 @@ type DefaultProps = {
|
|
|
63
62
|
* (because for Choice, that prop would be auto-populated by CheckboxGroup).
|
|
64
63
|
*/ export default class ChoiceInternal extends React.Component<Props> {
|
|
65
64
|
static defaultProps: DefaultProps = {
|
|
66
|
-
checked: false,
|
|
67
65
|
disabled: false,
|
|
68
66
|
error: false,
|
|
69
67
|
};
|
|
@@ -4,7 +4,7 @@ import {StyleSheet} from "aphrodite";
|
|
|
4
4
|
import Color, {mix, fade} from "@khanacademy/wonder-blocks-color";
|
|
5
5
|
import {addStyle} from "@khanacademy/wonder-blocks-core";
|
|
6
6
|
|
|
7
|
-
import type {ChoiceCoreProps} from "../util/types";
|
|
7
|
+
import type {ChoiceCoreProps, Checked} from "../util/types";
|
|
8
8
|
|
|
9
9
|
const {blue, red, white, offWhite, offBlack16, offBlack32, offBlack50} = Color;
|
|
10
10
|
|
|
@@ -28,6 +28,7 @@ const StyledInput = addStyle("input");
|
|
|
28
28
|
testId,
|
|
29
29
|
...sharedProps
|
|
30
30
|
} = this.props;
|
|
31
|
+
|
|
31
32
|
const stateStyles = _generateStyles(checked, error);
|
|
32
33
|
const defaultStyle = [
|
|
33
34
|
sharedStyles.inputReset,
|
|
@@ -44,7 +45,7 @@ const StyledInput = addStyle("input");
|
|
|
44
45
|
{...sharedProps}
|
|
45
46
|
type="radio"
|
|
46
47
|
aria-invalid={error}
|
|
47
|
-
checked={checked}
|
|
48
|
+
checked={checked ?? undefined}
|
|
48
49
|
disabled={disabled}
|
|
49
50
|
id={id}
|
|
50
51
|
name={groupName}
|
|
@@ -112,7 +113,7 @@ const colors = {
|
|
|
112
113
|
},
|
|
113
114
|
} as const;
|
|
114
115
|
const styles: Record<string, any> = {};
|
|
115
|
-
const _generateStyles = (checked:
|
|
116
|
+
const _generateStyles = (checked: Checked, error: boolean) => {
|
|
116
117
|
// "hash" the parameters
|
|
117
118
|
const styleKey = `${String(checked)}-${String(error)}`;
|
|
118
119
|
if (styles[styleKey]) {
|
package/src/util/types.ts
CHANGED
|
@@ -6,10 +6,13 @@ import type {AriaProps, StyleType} from "@khanacademy/wonder-blocks-core";
|
|
|
6
6
|
|
|
7
7
|
import Choice from "../components/choice";
|
|
8
8
|
|
|
9
|
+
// Checkbox is in indeterminate state when `checked` is `null` | `undefined`
|
|
10
|
+
export type Checked = boolean | null | undefined;
|
|
11
|
+
|
|
9
12
|
// Shared props for radio-core and checkbox-core
|
|
10
13
|
export type ChoiceCoreProps = AriaProps & {
|
|
11
14
|
/** Whether this component is checked */
|
|
12
|
-
checked:
|
|
15
|
+
checked: Checked;
|
|
13
16
|
/** Whether this component is disabled */
|
|
14
17
|
disabled: boolean;
|
|
15
18
|
/** Whether this component should show an error state */
|