@khanacademy/wonder-blocks-form 4.2.3 → 4.3.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/CHANGELOG.md +36 -0
- package/dist/components/checkbox-core.d.ts +13 -8
- package/dist/components/checkbox-core.js.flow +19 -10
- package/dist/components/checkbox-group.d.ts +2 -5
- package/dist/components/checkbox-group.js.flow +5 -6
- package/dist/components/checkbox.d.ts +33 -39
- package/dist/components/checkbox.js.flow +38 -41
- package/dist/components/choice-internal.d.ts +19 -31
- package/dist/components/choice-internal.js.flow +25 -32
- package/dist/components/choice.d.ts +50 -60
- package/dist/components/choice.js.flow +79 -84
- package/dist/components/radio-core.d.ts +13 -5
- package/dist/components/radio-core.js.flow +19 -7
- package/dist/components/radio-group.d.ts +2 -5
- package/dist/components/radio-group.js.flow +5 -6
- package/dist/components/radio.d.ts +18 -24
- package/dist/components/radio.js.flow +24 -27
- package/dist/es/index.js +262 -294
- package/dist/index.js +262 -294
- package/dist/util/types.d.ts +1 -1
- package/dist/util/types.js.flow +1 -1
- package/package.json +6 -6
- package/src/components/__tests__/{checkbox.test.js → checkbox.test.tsx} +55 -1
- package/src/components/checkbox-core.tsx +66 -71
- package/src/components/checkbox-group.tsx +62 -59
- package/src/components/checkbox.tsx +19 -16
- package/src/components/choice-internal.tsx +80 -77
- package/src/components/choice.tsx +34 -26
- package/src/components/radio-core.tsx +40 -44
- package/src/components/radio-group.tsx +57 -59
- package/src/components/radio.tsx +19 -16
- package/src/util/types.ts +1 -1
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -12,7 +12,7 @@ type Props = AriaProps & {
|
|
|
12
12
|
/** User-defined. Should be distinct for each item in the group. */
|
|
13
13
|
value: string;
|
|
14
14
|
/** User-defined. Whether this choice option is disabled. Default false. */
|
|
15
|
-
disabled
|
|
15
|
+
disabled?: boolean;
|
|
16
16
|
/** User-defined. Optional id for testing purposes. */
|
|
17
17
|
testId?: string;
|
|
18
18
|
/** User-defined. Optional additional styling. */
|
|
@@ -21,7 +21,7 @@ type Props = AriaProps & {
|
|
|
21
21
|
* Auto-populated by parent. Whether this choice is checked.
|
|
22
22
|
* @ignore
|
|
23
23
|
*/
|
|
24
|
-
checked
|
|
24
|
+
checked?: boolean;
|
|
25
25
|
/**
|
|
26
26
|
* Auto-populated by parent. Whether this choice is in error mode (everything
|
|
27
27
|
* in a choice group would be in error mode at the same time).
|
|
@@ -43,7 +43,7 @@ type Props = AriaProps & {
|
|
|
43
43
|
* Auto-populated by parent. Returns the new checked state of the component.
|
|
44
44
|
* @ignore
|
|
45
45
|
*/
|
|
46
|
-
onChange
|
|
46
|
+
onChange?: (newCheckedState: boolean) => unknown;
|
|
47
47
|
/**
|
|
48
48
|
* Auto-populated by parent.
|
|
49
49
|
* @ignore
|
|
@@ -51,12 +51,6 @@ type Props = AriaProps & {
|
|
|
51
51
|
variant?: "radio" | "checkbox";
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
type DefaultProps = {
|
|
55
|
-
checked: Props["checked"];
|
|
56
|
-
disabled: Props["disabled"];
|
|
57
|
-
onChange: Props["onChange"];
|
|
58
|
-
};
|
|
59
|
-
|
|
60
54
|
/**
|
|
61
55
|
* This is a labeled 🔘 or ☑️ item. Choice is meant to be used as children of
|
|
62
56
|
* CheckboxGroup and RadioGroup because many of its props are auto-populated
|
|
@@ -123,27 +117,41 @@ type DefaultProps = {
|
|
|
123
117
|
* </RadioGroup>
|
|
124
118
|
* ```
|
|
125
119
|
*/
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
120
|
+
const Choice = React.forwardRef(function Choice(
|
|
121
|
+
props: Props,
|
|
122
|
+
ref: React.ForwardedRef<HTMLInputElement>,
|
|
123
|
+
) {
|
|
124
|
+
const {
|
|
125
|
+
checked = false,
|
|
126
|
+
disabled = false,
|
|
127
|
+
onChange = () => {},
|
|
128
|
+
// we don't need this going into the ChoiceComponent
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
130
|
+
value,
|
|
131
|
+
variant,
|
|
132
|
+
...remainingProps
|
|
133
|
+
} = props;
|
|
132
134
|
|
|
133
|
-
getChoiceComponent(
|
|
135
|
+
const getChoiceComponent = (
|
|
134
136
|
variant?: string | null,
|
|
135
|
-
): typeof Radio | typeof Checkbox {
|
|
137
|
+
): typeof Radio | typeof Checkbox => {
|
|
136
138
|
if (variant === "checkbox") {
|
|
137
139
|
return Checkbox;
|
|
138
140
|
} else {
|
|
139
141
|
return Radio;
|
|
140
142
|
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const ChoiceComponent = getChoiceComponent(variant);
|
|
146
|
+
return (
|
|
147
|
+
<ChoiceComponent
|
|
148
|
+
{...remainingProps}
|
|
149
|
+
checked={checked}
|
|
150
|
+
disabled={disabled}
|
|
151
|
+
onChange={onChange}
|
|
152
|
+
ref={ref}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
export default Choice;
|
|
@@ -12,54 +12,48 @@ const StyledInput = addStyle("input");
|
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* The internal stateless 🔘 Radio button
|
|
15
|
-
*/
|
|
16
|
-
|
|
15
|
+
*/ const RadioCore = React.forwardRef(function RadioCore(
|
|
16
|
+
props: ChoiceCoreProps,
|
|
17
|
+
ref: React.ForwardedRef<HTMLInputElement>,
|
|
18
|
+
) {
|
|
19
|
+
const handleChange = () => {
|
|
17
20
|
// Empty because change is handled by ClickableBehavior
|
|
18
21
|
return;
|
|
19
22
|
};
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
onChange={this.handleChange}
|
|
55
|
-
style={defaultStyle}
|
|
56
|
-
{...props}
|
|
57
|
-
/>
|
|
58
|
-
{disabled && checked && <span style={disabledChecked} />}
|
|
59
|
-
</React.Fragment>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
24
|
+
const {checked, disabled, error, groupName, id, testId, ...sharedProps} =
|
|
25
|
+
props;
|
|
26
|
+
|
|
27
|
+
const stateStyles = _generateStyles(checked, error);
|
|
28
|
+
const defaultStyle = [
|
|
29
|
+
sharedStyles.inputReset,
|
|
30
|
+
sharedStyles.default,
|
|
31
|
+
!disabled && stateStyles.default,
|
|
32
|
+
disabled && sharedStyles.disabled,
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<React.Fragment>
|
|
37
|
+
<StyledInput
|
|
38
|
+
{...sharedProps}
|
|
39
|
+
type="radio"
|
|
40
|
+
aria-invalid={error}
|
|
41
|
+
checked={checked ?? undefined}
|
|
42
|
+
disabled={disabled}
|
|
43
|
+
id={id}
|
|
44
|
+
name={groupName}
|
|
45
|
+
// Need to specify because this is a controlled React form
|
|
46
|
+
// component, but we handle the click via ClickableBehavior
|
|
47
|
+
onChange={handleChange}
|
|
48
|
+
style={defaultStyle}
|
|
49
|
+
data-test-id={testId}
|
|
50
|
+
ref={ref}
|
|
51
|
+
/>
|
|
52
|
+
{disabled && checked && <span style={disabledChecked} />}
|
|
53
|
+
</React.Fragment>
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
63
57
|
const size = 16; // circle with a different color. Here, we add that center circle. // If the checkbox is disabled and selected, it has a border but also an inner
|
|
64
58
|
const disabledChecked = {
|
|
65
59
|
position: "absolute",
|
|
@@ -175,3 +169,5 @@ const _generateStyles = (checked: Checked, error: boolean) => {
|
|
|
175
169
|
styles[styleKey] = StyleSheet.create(newStyles);
|
|
176
170
|
return styles[styleKey];
|
|
177
171
|
};
|
|
172
|
+
|
|
173
|
+
export default RadioCore;
|
|
@@ -96,66 +96,64 @@ const StyledLegend = addStyle("legend");
|
|
|
96
96
|
* </RadioGroup>
|
|
97
97
|
* ```
|
|
98
98
|
*/
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
const RadioGroup = React.forwardRef(function RadioGroup(
|
|
100
|
+
props: RadioGroupProps,
|
|
101
|
+
ref: React.ForwardedRef<HTMLFieldSetElement>,
|
|
102
|
+
) {
|
|
103
|
+
const {
|
|
104
|
+
children,
|
|
105
|
+
label,
|
|
106
|
+
description,
|
|
107
|
+
errorMessage,
|
|
108
|
+
groupName,
|
|
109
|
+
onChange,
|
|
110
|
+
selectedValue,
|
|
111
|
+
style,
|
|
112
|
+
testId,
|
|
113
|
+
} = props;
|
|
103
114
|
|
|
104
|
-
|
|
105
|
-
const {
|
|
106
|
-
children,
|
|
107
|
-
label,
|
|
108
|
-
description,
|
|
109
|
-
errorMessage,
|
|
110
|
-
groupName,
|
|
111
|
-
selectedValue,
|
|
112
|
-
style,
|
|
113
|
-
testId,
|
|
114
|
-
} = this.props;
|
|
115
|
+
const allChildren = React.Children.toArray(children).filter(Boolean);
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
return (
|
|
118
|
+
<StyledFieldset data-test-id={testId} style={styles.fieldset} ref={ref}>
|
|
119
|
+
{/* We have a View here because fieldset cannot be used with flexbox*/}
|
|
120
|
+
<View style={style}>
|
|
121
|
+
{label && (
|
|
122
|
+
<StyledLegend style={styles.legend}>
|
|
123
|
+
<LabelMedium>{label}</LabelMedium>
|
|
124
|
+
</StyledLegend>
|
|
125
|
+
)}
|
|
126
|
+
{description && (
|
|
127
|
+
<LabelSmall style={styles.description}>
|
|
128
|
+
{description}
|
|
129
|
+
</LabelSmall>
|
|
130
|
+
)}
|
|
131
|
+
{errorMessage && (
|
|
132
|
+
<LabelSmall style={styles.error}>{errorMessage}</LabelSmall>
|
|
133
|
+
)}
|
|
134
|
+
{(label || description || errorMessage) && (
|
|
135
|
+
<Strut size={Spacing.small_12} />
|
|
136
|
+
)}
|
|
117
137
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
<Strut size={Spacing.small_12} />
|
|
139
|
-
)}
|
|
138
|
+
{allChildren.map((child, index) => {
|
|
139
|
+
// @ts-expect-error [FEI-5019] - TS2339 - Property 'props' does not exist on type 'ReactChild | ReactFragment | ReactPortal'.
|
|
140
|
+
const {style, value} = child.props;
|
|
141
|
+
const checked = selectedValue === value;
|
|
142
|
+
// @ts-expect-error [FEI-5019] - TS2769 - No overload matches this call.
|
|
143
|
+
return React.cloneElement(child, {
|
|
144
|
+
checked: checked,
|
|
145
|
+
error: !!errorMessage,
|
|
146
|
+
groupName: groupName,
|
|
147
|
+
id: `${groupName}-${value}`,
|
|
148
|
+
key: value,
|
|
149
|
+
onChange: () => onChange(value),
|
|
150
|
+
style: [index > 0 && styles.defaultLineGap, style],
|
|
151
|
+
variant: "radio",
|
|
152
|
+
});
|
|
153
|
+
})}
|
|
154
|
+
</View>
|
|
155
|
+
</StyledFieldset>
|
|
156
|
+
);
|
|
157
|
+
});
|
|
140
158
|
|
|
141
|
-
|
|
142
|
-
// @ts-expect-error [FEI-5019] - TS2339 - Property 'props' does not exist on type 'ReactChild | ReactFragment | ReactPortal'.
|
|
143
|
-
const {style, value} = child.props;
|
|
144
|
-
const checked = selectedValue === value;
|
|
145
|
-
// @ts-expect-error [FEI-5019] - TS2769 - No overload matches this call.
|
|
146
|
-
return React.cloneElement(child, {
|
|
147
|
-
checked: checked,
|
|
148
|
-
error: !!errorMessage,
|
|
149
|
-
groupName: groupName,
|
|
150
|
-
id: `${groupName}-${value}`,
|
|
151
|
-
key: value,
|
|
152
|
-
onChange: () => this.handleChange(value),
|
|
153
|
-
style: [index > 0 && styles.defaultLineGap, style],
|
|
154
|
-
variant: "radio",
|
|
155
|
-
});
|
|
156
|
-
})}
|
|
157
|
-
</View>
|
|
158
|
-
</StyledFieldset>
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
159
|
+
export default RadioGroup;
|
package/src/components/radio.tsx
CHANGED
|
@@ -12,11 +12,11 @@ type ChoiceComponentProps = AriaProps & {
|
|
|
12
12
|
/**
|
|
13
13
|
* Whether this component is disabled
|
|
14
14
|
*/
|
|
15
|
-
disabled
|
|
15
|
+
disabled?: boolean;
|
|
16
16
|
/**
|
|
17
17
|
* Whether this component should show an error state
|
|
18
18
|
*/
|
|
19
|
-
error
|
|
19
|
+
error?: boolean;
|
|
20
20
|
/**
|
|
21
21
|
* Callback when this component is selected. The newCheckedState is the
|
|
22
22
|
* new checked state of the component.
|
|
@@ -56,24 +56,27 @@ type ChoiceComponentProps = AriaProps & {
|
|
|
56
56
|
groupName?: string;
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
type DefaultProps = {
|
|
60
|
-
disabled: ChoiceComponentProps["disabled"];
|
|
61
|
-
error: ChoiceComponentProps["error"];
|
|
62
|
-
};
|
|
63
|
-
|
|
64
59
|
/**
|
|
65
60
|
* 🔘 A nicely styled radio button for all your non-AMFM radio button needs. Can
|
|
66
61
|
* optionally take label and description props.
|
|
67
62
|
*
|
|
68
63
|
* This component should not really be used by itself because radio buttons are
|
|
69
64
|
* often grouped together. See RadioGroup.
|
|
70
|
-
*/
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
};
|
|
65
|
+
*/ const Radio = React.forwardRef(function Radio(
|
|
66
|
+
props: ChoiceComponentProps,
|
|
67
|
+
ref: React.ForwardedRef<HTMLInputElement>,
|
|
68
|
+
) {
|
|
69
|
+
const {disabled = false, error = false, ...otherProps} = props;
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<ChoiceInternal
|
|
73
|
+
{...otherProps}
|
|
74
|
+
variant="radio"
|
|
75
|
+
disabled={disabled}
|
|
76
|
+
error={error}
|
|
77
|
+
ref={ref}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
});
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
return <ChoiceInternal variant="radio" {...this.props} />;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
82
|
+
export default Radio;
|
package/src/util/types.ts
CHANGED
|
@@ -46,7 +46,7 @@ export type ChoiceComponentProps = ChoiceCoreProps & {
|
|
|
46
46
|
|
|
47
47
|
export type SharedGroupProps = {
|
|
48
48
|
/** Children should be Choice components. */
|
|
49
|
-
children: Choice;
|
|
49
|
+
children: typeof Choice;
|
|
50
50
|
/** Group name for this checkbox or radio group. Should be unique for all
|
|
51
51
|
* such groups displayed on a page. */
|
|
52
52
|
groupName: string;
|