@khanacademy/wonder-blocks-form 4.3.0 → 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.
@@ -55,108 +55,109 @@ type Props = AriaProps & {
55
55
  * and RadioGroup. This design allows for more explicit prop typing. For
56
56
  * example, we can make onChange a required prop on Checkbox but not on Choice
57
57
  * (because for Choice, that prop would be auto-populated by CheckboxGroup).
58
- */ const ChoiceInternal = React.forwardRef(
59
- (props: Props, ref: React.ForwardedRef<HTMLInputElement>) => {
60
- const {
61
- checked,
62
- description,
63
- disabled = false,
64
- error = false,
65
- id,
66
- label,
67
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
68
- onChange,
69
- style,
70
- className,
71
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
72
- variant,
73
- // ...coreProps
74
- } = props;
58
+ */ const ChoiceInternal = React.forwardRef(function ChoiceInternal(
59
+ props: Props,
60
+ ref: React.ForwardedRef<HTMLInputElement>,
61
+ ) {
62
+ const {
63
+ checked,
64
+ description,
65
+ disabled = false,
66
+ error = false,
67
+ id,
68
+ label,
69
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
70
+ onChange,
71
+ style,
72
+ className,
73
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
74
+ variant,
75
+ // ...coreProps
76
+ } = props;
75
77
 
76
- const handleClick: () => void = () => {
77
- // Radio buttons cannot be unchecked
78
- if (variant === "radio" && checked) {
79
- return;
80
- }
81
- onChange(!checked);
82
- };
78
+ const handleClick: () => void = () => {
79
+ // Radio buttons cannot be unchecked
80
+ if (variant === "radio" && checked) {
81
+ return;
82
+ }
83
+ onChange(!checked);
84
+ };
83
85
 
84
- const getChoiceCoreComponent = ():
85
- | typeof RadioCore
86
- | typeof CheckboxCore => {
87
- if (variant === "radio") {
88
- return RadioCore;
89
- } else {
90
- return CheckboxCore;
91
- }
92
- };
86
+ const getChoiceCoreComponent = ():
87
+ | typeof RadioCore
88
+ | typeof CheckboxCore => {
89
+ if (variant === "radio") {
90
+ return RadioCore;
91
+ } else {
92
+ return CheckboxCore;
93
+ }
94
+ };
93
95
 
94
- const getLabel = (id: string): React.ReactNode => {
95
- return (
96
- <LabelMedium
97
- style={[styles.label, disabled && styles.disabledLabel]}
98
- >
99
- <label htmlFor={id}>{label}</label>
100
- </LabelMedium>
101
- );
102
- };
96
+ const getLabel = (id: string): React.ReactNode => {
97
+ return (
98
+ <LabelMedium
99
+ style={[styles.label, disabled && styles.disabledLabel]}
100
+ >
101
+ <label htmlFor={id}>{label}</label>
102
+ </LabelMedium>
103
+ );
104
+ };
103
105
 
104
- const getDescription = (id?: string): React.ReactNode => {
105
- return (
106
- <LabelSmall style={styles.description} id={id}>
107
- {description}
108
- </LabelSmall>
109
- );
110
- };
106
+ const getDescription = (id?: string): React.ReactNode => {
107
+ return (
108
+ <LabelSmall style={styles.description} id={id}>
109
+ {description}
110
+ </LabelSmall>
111
+ );
112
+ };
111
113
 
112
- const ChoiceCore = getChoiceCoreComponent();
114
+ const ChoiceCore = getChoiceCoreComponent();
113
115
 
114
- return (
115
- <UniqueIDProvider mockOnFirstRender={true} scope="choice">
116
- {(ids) => {
117
- // A choice element should always have a unique ID set
118
- // so that the label can always refer to this element.
119
- // This guarantees that clicking on the label will
120
- // always click on the choice as well. If an ID is
121
- // passed in as a prop, use that one. Otherwise,
122
- // create a unique ID using the provider.
123
- const uniqueId = id || ids.get("main");
116
+ return (
117
+ <UniqueIDProvider mockOnFirstRender={true} scope="choice">
118
+ {(ids) => {
119
+ // A choice element should always have a unique ID set
120
+ // so that the label can always refer to this element.
121
+ // This guarantees that clicking on the label will
122
+ // always click on the choice as well. If an ID is
123
+ // passed in as a prop, use that one. Otherwise,
124
+ // create a unique ID using the provider.
125
+ const uniqueId = id || ids.get("main");
124
126
 
125
- // Create a unique ID for the description section to be
126
- // used by this element's `aria-describedby`.
127
- const descriptionId = description
128
- ? ids.get("description")
129
- : undefined;
127
+ // Create a unique ID for the description section to be
128
+ // used by this element's `aria-describedby`.
129
+ const descriptionId = description
130
+ ? ids.get("description")
131
+ : undefined;
130
132
 
131
- return (
132
- <View style={style} className={className}>
133
- <View
134
- style={styles.wrapper}
135
- // We are resetting the tabIndex=0 from handlers
136
- // because the ChoiceCore component will receive
137
- // focus on basis of it being an input element.
138
- tabIndex={-1}
139
- >
140
- <ChoiceCore
141
- {...props}
142
- id={uniqueId}
143
- aria-describedby={descriptionId}
144
- onClick={handleClick}
145
- disabled={disabled}
146
- error={error}
147
- ref={ref}
148
- />
149
- <Strut size={Spacing.xSmall_8} />
150
- {label && getLabel(uniqueId)}
151
- </View>
152
- {description && getDescription(descriptionId)}
133
+ return (
134
+ <View style={style} className={className}>
135
+ <View
136
+ style={styles.wrapper}
137
+ // We are resetting the tabIndex=0 from handlers
138
+ // because the ChoiceCore component will receive
139
+ // focus on basis of it being an input element.
140
+ tabIndex={-1}
141
+ >
142
+ <ChoiceCore
143
+ {...props}
144
+ id={uniqueId}
145
+ aria-describedby={descriptionId}
146
+ onClick={handleClick}
147
+ disabled={disabled}
148
+ error={error}
149
+ ref={ref}
150
+ />
151
+ <Strut size={Spacing.xSmall_8} />
152
+ {label && getLabel(uniqueId)}
153
153
  </View>
154
- );
155
- }}
156
- </UniqueIDProvider>
157
- );
158
- },
159
- );
154
+ {description && getDescription(descriptionId)}
155
+ </View>
156
+ );
157
+ }}
158
+ </UniqueIDProvider>
159
+ );
160
+ });
160
161
 
161
162
  const styles = StyleSheet.create({
162
163
  wrapper: {
@@ -117,40 +117,41 @@ type Props = AriaProps & {
117
117
  * </RadioGroup>
118
118
  * ```
119
119
  */
120
- const Choice = React.forwardRef(
121
- (props: Props, ref: React.ForwardedRef<HTMLInputElement>) => {
122
- const {
123
- checked = false,
124
- disabled = false,
125
- onChange = () => {},
126
- // we don't need this going into the ChoiceComponent
127
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
128
- value,
129
- variant,
130
- ...remainingProps
131
- } = props;
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
- const getChoiceComponent = (
134
- variant?: string | null,
135
- ): typeof Radio | typeof Checkbox => {
136
- if (variant === "checkbox") {
137
- return Checkbox;
138
- } else {
139
- return Radio;
140
- }
141
- };
135
+ const getChoiceComponent = (
136
+ variant?: string | null,
137
+ ): typeof Radio | typeof Checkbox => {
138
+ if (variant === "checkbox") {
139
+ return Checkbox;
140
+ } else {
141
+ return Radio;
142
+ }
143
+ };
142
144
 
143
- const ChoiceComponent = getChoiceComponent(variant);
144
- return (
145
- <ChoiceComponent
146
- {...remainingProps}
147
- checked={checked}
148
- disabled={disabled}
149
- onChange={onChange}
150
- ref={ref}
151
- />
152
- );
153
- },
154
- );
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
+ });
155
156
 
156
157
  export default Choice;
@@ -12,53 +12,47 @@ const StyledInput = addStyle("input");
12
12
 
13
13
  /**
14
14
  * The internal stateless 🔘 Radio button
15
- */ const RadioCore = React.forwardRef(
16
- (props: ChoiceCoreProps, ref: React.ForwardedRef<HTMLInputElement>) => {
17
- const handleChange = () => {
18
- // Empty because change is handled by ClickableBehavior
19
- return;
20
- };
21
-
22
- const {
23
- checked,
24
- disabled,
25
- error,
26
- groupName,
27
- id,
28
- testId,
29
- ...sharedProps
30
- } = props;
31
-
32
- const stateStyles = _generateStyles(checked, error);
33
- const defaultStyle = [
34
- sharedStyles.inputReset,
35
- sharedStyles.default,
36
- !disabled && stateStyles.default,
37
- disabled && sharedStyles.disabled,
38
- ];
39
-
40
- return (
41
- <React.Fragment>
42
- <StyledInput
43
- {...sharedProps}
44
- type="radio"
45
- aria-invalid={error}
46
- checked={checked ?? undefined}
47
- disabled={disabled}
48
- id={id}
49
- name={groupName}
50
- // Need to specify because this is a controlled React form
51
- // component, but we handle the click via ClickableBehavior
52
- onChange={handleChange}
53
- style={defaultStyle}
54
- data-test-id={testId}
55
- ref={ref}
56
- />
57
- {disabled && checked && <span style={disabledChecked} />}
58
- </React.Fragment>
59
- );
60
- },
61
- );
15
+ */ const RadioCore = React.forwardRef(function RadioCore(
16
+ props: ChoiceCoreProps,
17
+ ref: React.ForwardedRef<HTMLInputElement>,
18
+ ) {
19
+ const handleChange = () => {
20
+ // Empty because change is handled by ClickableBehavior
21
+ return;
22
+ };
23
+
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
+ });
62
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 = {
@@ -96,69 +96,64 @@ const StyledLegend = addStyle("legend");
96
96
  * </RadioGroup>
97
97
  * ```
98
98
  */
99
- const RadioGroup = React.forwardRef(
100
- (props: RadioGroupProps, ref: React.ForwardedRef<HTMLFieldSetElement>) => {
101
- const {
102
- children,
103
- label,
104
- description,
105
- errorMessage,
106
- groupName,
107
- onChange,
108
- selectedValue,
109
- style,
110
- testId,
111
- } = props;
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;
112
114
 
113
- const allChildren = React.Children.toArray(children).filter(Boolean);
115
+ const allChildren = React.Children.toArray(children).filter(Boolean);
114
116
 
115
- return (
116
- <StyledFieldset
117
- data-test-id={testId}
118
- style={styles.fieldset}
119
- ref={ref}
120
- >
121
- {/* We have a View here because fieldset cannot be used with flexbox*/}
122
- <View style={style}>
123
- {label && (
124
- <StyledLegend style={styles.legend}>
125
- <LabelMedium>{label}</LabelMedium>
126
- </StyledLegend>
127
- )}
128
- {description && (
129
- <LabelSmall style={styles.description}>
130
- {description}
131
- </LabelSmall>
132
- )}
133
- {errorMessage && (
134
- <LabelSmall style={styles.error}>
135
- {errorMessage}
136
- </LabelSmall>
137
- )}
138
- {(label || description || errorMessage) && (
139
- <Strut size={Spacing.small_12} />
140
- )}
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
+ )}
141
137
 
142
- {allChildren.map((child, index) => {
143
- // @ts-expect-error [FEI-5019] - TS2339 - Property 'props' does not exist on type 'ReactChild | ReactFragment | ReactPortal'.
144
- const {style, value} = child.props;
145
- const checked = selectedValue === value;
146
- // @ts-expect-error [FEI-5019] - TS2769 - No overload matches this call.
147
- return React.cloneElement(child, {
148
- checked: checked,
149
- error: !!errorMessage,
150
- groupName: groupName,
151
- id: `${groupName}-${value}`,
152
- key: value,
153
- onChange: () => onChange(value),
154
- style: [index > 0 && styles.defaultLineGap, style],
155
- variant: "radio",
156
- });
157
- })}
158
- </View>
159
- </StyledFieldset>
160
- );
161
- },
162
- );
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
+ });
163
158
 
164
159
  export default RadioGroup;
@@ -62,23 +62,21 @@ type ChoiceComponentProps = AriaProps & {
62
62
  *
63
63
  * This component should not really be used by itself because radio buttons are
64
64
  * often grouped together. See RadioGroup.
65
- */ const Radio = React.forwardRef(
66
- (
67
- props: ChoiceComponentProps,
68
- ref: React.ForwardedRef<HTMLInputElement>,
69
- ) => {
70
- const {disabled = false, error = false, ...otherProps} = props;
65
+ */ const Radio = React.forwardRef(function Radio(
66
+ props: ChoiceComponentProps,
67
+ ref: React.ForwardedRef<HTMLInputElement>,
68
+ ) {
69
+ const {disabled = false, error = false, ...otherProps} = props;
71
70
 
72
- return (
73
- <ChoiceInternal
74
- {...otherProps}
75
- variant="radio"
76
- disabled={disabled}
77
- error={error}
78
- ref={ref}
79
- />
80
- );
81
- },
82
- );
71
+ return (
72
+ <ChoiceInternal
73
+ {...otherProps}
74
+ variant="radio"
75
+ disabled={disabled}
76
+ error={error}
77
+ ref={ref}
78
+ />
79
+ );
80
+ });
83
81
 
84
82
  export default Radio;