@khanacademy/wonder-blocks-form 2.4.3 → 2.4.4
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
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-form
|
|
2
2
|
|
|
3
|
+
## 2.4.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [5f4a4297]
|
|
8
|
+
- Updated dependencies [2b96fd59]
|
|
9
|
+
- @khanacademy/wonder-blocks-core@4.3.2
|
|
10
|
+
- @khanacademy/wonder-blocks-clickable@2.2.7
|
|
11
|
+
- @khanacademy/wonder-blocks-icon@1.2.28
|
|
12
|
+
- @khanacademy/wonder-blocks-layout@1.4.10
|
|
13
|
+
- @khanacademy/wonder-blocks-typography@1.1.32
|
|
14
|
+
|
|
3
15
|
## 2.4.3
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/index.js
CHANGED
|
@@ -182,8 +182,22 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
|
|
|
182
182
|
* ☑️ A nicely styled checkbox for all your checking needs. Can optionally take
|
|
183
183
|
* label and description props.
|
|
184
184
|
*
|
|
185
|
+
* If used by itself, a checkbox provides two options - checked and unchecked.
|
|
186
|
+
* A group of checkboxes can be used to allow a user to select multiple values
|
|
187
|
+
* from a list of options.
|
|
188
|
+
*
|
|
185
189
|
* If you want a whole group of Checkbox[es] that are related, see the Choice
|
|
186
190
|
* and CheckboxGroup components.
|
|
191
|
+
*
|
|
192
|
+
* ### Usage
|
|
193
|
+
*
|
|
194
|
+
* ```jsx
|
|
195
|
+
* import {Checkbox} from "@khanacademy/wonder-blocks-form";
|
|
196
|
+
*
|
|
197
|
+
* const [checked, setChecked] = React.useState(false);
|
|
198
|
+
*
|
|
199
|
+
* <Checkbox checked={checked} onChange={setChecked} />
|
|
200
|
+
* ```
|
|
187
201
|
*/
|
|
188
202
|
class Checkbox extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
189
203
|
render() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-form",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.4",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"description": "Form components for Wonder Blocks.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@babel/runtime": "^7.16.3",
|
|
19
|
-
"@khanacademy/wonder-blocks-clickable": "^2.2.
|
|
19
|
+
"@khanacademy/wonder-blocks-clickable": "^2.2.7",
|
|
20
20
|
"@khanacademy/wonder-blocks-color": "^1.1.20",
|
|
21
|
-
"@khanacademy/wonder-blocks-core": "^4.3.
|
|
22
|
-
"@khanacademy/wonder-blocks-icon": "^1.2.
|
|
23
|
-
"@khanacademy/wonder-blocks-layout": "^1.4.
|
|
21
|
+
"@khanacademy/wonder-blocks-core": "^4.3.2",
|
|
22
|
+
"@khanacademy/wonder-blocks-icon": "^1.2.28",
|
|
23
|
+
"@khanacademy/wonder-blocks-layout": "^1.4.10",
|
|
24
24
|
"@khanacademy/wonder-blocks-spacing": "^3.0.5",
|
|
25
|
-
"@khanacademy/wonder-blocks-typography": "^1.1.
|
|
25
|
+
"@khanacademy/wonder-blocks-typography": "^1.1.32"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"aphrodite": "^1.2.5",
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import {Meta, Story, Canvas} from "@storybook/addon-docs";
|
|
2
|
+
import {StyleSheet} from "aphrodite";
|
|
3
|
+
|
|
4
|
+
import Color from "@khanacademy/wonder-blocks-color";
|
|
5
|
+
import {Checkbox} from "@khanacademy/wonder-blocks-form";
|
|
6
|
+
import {LabelSmall} from "@khanacademy/wonder-blocks-typography";
|
|
7
|
+
|
|
8
|
+
<Meta
|
|
9
|
+
title="Form/Checkbox/Accessibility"
|
|
10
|
+
component={Checkbox}
|
|
11
|
+
parameters={{
|
|
12
|
+
previewTabs: {
|
|
13
|
+
canvas: {hidden: true},
|
|
14
|
+
},
|
|
15
|
+
viewMode: "docs",
|
|
16
|
+
chromatic: {
|
|
17
|
+
// Disables chromatic testing for these stories.
|
|
18
|
+
disableSnapshot: true,
|
|
19
|
+
},
|
|
20
|
+
}}
|
|
21
|
+
/>
|
|
22
|
+
|
|
23
|
+
export const ErrorTemplate = (args) => {
|
|
24
|
+
const [checked, setChecked] = React.useState(false);
|
|
25
|
+
const errorState = !checked;
|
|
26
|
+
return (
|
|
27
|
+
<View>
|
|
28
|
+
<Checkbox
|
|
29
|
+
checked={checked}
|
|
30
|
+
onChange={setChecked}
|
|
31
|
+
error={errorState}
|
|
32
|
+
aria-describedby={errorState && "error-message"}
|
|
33
|
+
aria-required={true}
|
|
34
|
+
{...args}
|
|
35
|
+
/>
|
|
36
|
+
{errorState && (
|
|
37
|
+
<LabelSmall style={styles.error} id="error-message">
|
|
38
|
+
You must agree to the terms to continue
|
|
39
|
+
</LabelSmall>
|
|
40
|
+
)}
|
|
41
|
+
</View>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const DisabledTemplate = (args) => {
|
|
46
|
+
const [checked, setChecked] = React.useState(false);
|
|
47
|
+
const errorState = !checked;
|
|
48
|
+
return (
|
|
49
|
+
<Checkbox
|
|
50
|
+
checked={checked}
|
|
51
|
+
onChange={setChecked}
|
|
52
|
+
label="Some setting"
|
|
53
|
+
description="You do not have permission to change this setting"
|
|
54
|
+
disabled={true}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
## Accessibility
|
|
60
|
+
|
|
61
|
+
### ARIA
|
|
62
|
+
|
|
63
|
+
`Checkbox` can take in all ARIA props defined in Wonder Blocks Core types.
|
|
64
|
+
|
|
65
|
+
Elements with role `"checkbox"` can have an `aria-checked` property that
|
|
66
|
+
exposes the checked state to assistive technology. The dev does not have
|
|
67
|
+
to worry about this because the Wonder Blocks Checkbox component is an
|
|
68
|
+
`input` element with type `"checkbox"`, as this has built-in semantics and
|
|
69
|
+
does not require ARIA.
|
|
70
|
+
|
|
71
|
+
The current implementation of `Checkbox` uses `aria-describedby` with the
|
|
72
|
+
label and description that may be passed in as props.
|
|
73
|
+
|
|
74
|
+
See the Error section for information about `aria-invalid` and
|
|
75
|
+
`aria-required`.
|
|
76
|
+
|
|
77
|
+
### Error state
|
|
78
|
+
|
|
79
|
+
The Wonder Blocks `Checkbox` component takes an `error` boolean prop. Setting
|
|
80
|
+
this prop to true will set `aria-invalid` to true, and the color of the
|
|
81
|
+
checkbox to red.
|
|
82
|
+
|
|
83
|
+
When a form input is invalid, the user should provide a reason for why
|
|
84
|
+
this is.
|
|
85
|
+
|
|
86
|
+
Generally, it is also suggested this is the validation error message is
|
|
87
|
+
passed to the checkbox's `aria-describedby` prop so assistive tech can
|
|
88
|
+
read it. However, this is not possible with the current implementation of
|
|
89
|
+
the Wonder Blocks Form Checkbox component.
|
|
90
|
+
|
|
91
|
+
The error state can be used to signal that a required checkbox has not been
|
|
92
|
+
checked. In cases where a checkbox is required, the checkbox component should
|
|
93
|
+
set the `aria-required` prop to true for assistive tech.
|
|
94
|
+
There should also be some sort of visual indication that checking
|
|
95
|
+
the box is required, such as a "Required" label or an asterisk.
|
|
96
|
+
|
|
97
|
+
<Canvas>
|
|
98
|
+
<Story
|
|
99
|
+
name="Error state"
|
|
100
|
+
args={{
|
|
101
|
+
label: "I accept the terms and conditions",
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
{ErrorTemplate.bind({})}
|
|
105
|
+
</Story>
|
|
106
|
+
</Canvas>
|
|
107
|
+
|
|
108
|
+
### Disabled state
|
|
109
|
+
|
|
110
|
+
The Wonder Blocks `Checkbox` compoenent takes a `disabled` boolean prop.
|
|
111
|
+
This sets the underlying `input` element's `disabled` prop to `true`.
|
|
112
|
+
This makes is so that the checkbox is not interactable. Also, assistive
|
|
113
|
+
tech will indicated that the checkbox is dimmed.
|
|
114
|
+
|
|
115
|
+
A user will not be able to navigate to the checkbox with a keyboard.
|
|
116
|
+
Screen reader users will be able to navigate to the checkbox with
|
|
117
|
+
screen reader controls.
|
|
118
|
+
|
|
119
|
+
It is suggested that if an element is disabled, an explanation as to why
|
|
120
|
+
should to provided somewhere.
|
|
121
|
+
|
|
122
|
+
<Canvas>
|
|
123
|
+
<Story name="Disabled state">{DisabledTemplate.bind({})}</Story>
|
|
124
|
+
</Canvas>
|
|
125
|
+
|
|
126
|
+
### Keyboard Interaction
|
|
127
|
+
|
|
128
|
+
If a checkbox is not disabled, a user can tab to it using standard
|
|
129
|
+
keyboard navigation. The Space key toggles the checked state of the checkbox.
|
|
130
|
+
|
|
131
|
+
Note the the Space key triggers the `onChange` function of the
|
|
132
|
+
Wonder Blocks Checkbox component. If the user does not specify an `onChange`
|
|
133
|
+
funciton prop that in turn updates the value of `checked`, neither clicking
|
|
134
|
+
nor the Space key will toggle the Checkbox.
|
|
135
|
+
|
|
136
|
+
### References
|
|
137
|
+
|
|
138
|
+
- [Accessible validation of checkbox and radiobutton groups](https://blog.tenon.io/accessible-validation-of-checkbox-and-radiobutton-groups/)
|
|
139
|
+
- [HTML: Validating a checkbox with HTML5](https://www.the-art-of-web.com/html/html5-checkbox-required/#example1)
|
|
140
|
+
- [aria-checked MDN Docs](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-checked)
|
|
141
|
+
- [ARIA: checkbox role MDN Docs](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/checkbox_role)
|
|
142
|
+
|
|
143
|
+
export const styles = StyleSheet.create({
|
|
144
|
+
error: {
|
|
145
|
+
color: Color.red,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {StyleSheet} from "aphrodite";
|
|
4
|
+
|
|
5
|
+
import {View} from "@khanacademy/wonder-blocks-core";
|
|
6
|
+
import {LabelMedium, LabelSmall} from "@khanacademy/wonder-blocks-typography";
|
|
7
|
+
import type {StoryComponentType} from "@storybook/react";
|
|
8
|
+
|
|
9
|
+
import Checkbox from "../checkbox.js";
|
|
10
|
+
|
|
11
|
+
import ComponentInfo from "../../../../../.storybook/components/component-info.js";
|
|
12
|
+
import {name, version} from "../../../package.json";
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
title: "Form / Checkbox",
|
|
16
|
+
component: Checkbox,
|
|
17
|
+
parameters: {
|
|
18
|
+
componentSubtitle: ((
|
|
19
|
+
<ComponentInfo name={name} version={version} />
|
|
20
|
+
): any),
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const Default: StoryComponentType = (args) => <Checkbox {...args} />;
|
|
25
|
+
|
|
26
|
+
Default.args = {
|
|
27
|
+
checked: false,
|
|
28
|
+
onChange: () => {},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
Default.parameters = {
|
|
32
|
+
chromatic: {
|
|
33
|
+
// We already have screenshots of another story that covers
|
|
34
|
+
// this and more cases.
|
|
35
|
+
disableSnapshot: true,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const Controlled: StoryComponentType = () => {
|
|
40
|
+
const [checked, setChecked] = React.useState(false);
|
|
41
|
+
return <Checkbox checked={checked} onChange={setChecked} />;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
Controlled.parameters = {
|
|
45
|
+
chromatic: {
|
|
46
|
+
// Disabling because this doesn't test visuals, its for testing
|
|
47
|
+
// that `state` works as expected.
|
|
48
|
+
disableSnapshot: true,
|
|
49
|
+
},
|
|
50
|
+
docs: {
|
|
51
|
+
storyDescription:
|
|
52
|
+
"Use state to keep track of whether the checkbox is checked or not",
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const Variants: StoryComponentType = () => (
|
|
57
|
+
<View style={styles.row}>
|
|
58
|
+
<Checkbox
|
|
59
|
+
error={false}
|
|
60
|
+
checked={false}
|
|
61
|
+
style={styles.marginRight}
|
|
62
|
+
onChange={() => {}}
|
|
63
|
+
/>
|
|
64
|
+
<Checkbox
|
|
65
|
+
error={false}
|
|
66
|
+
checked={true}
|
|
67
|
+
style={styles.marginRight}
|
|
68
|
+
onChange={() => {}}
|
|
69
|
+
/>
|
|
70
|
+
<Checkbox
|
|
71
|
+
error={true}
|
|
72
|
+
checked={false}
|
|
73
|
+
style={styles.marginRight}
|
|
74
|
+
onChange={() => {}}
|
|
75
|
+
/>
|
|
76
|
+
<Checkbox
|
|
77
|
+
error={true}
|
|
78
|
+
checked={true}
|
|
79
|
+
style={styles.marginRight}
|
|
80
|
+
onChange={() => {}}
|
|
81
|
+
/>
|
|
82
|
+
<Checkbox
|
|
83
|
+
disabled={true}
|
|
84
|
+
checked={false}
|
|
85
|
+
style={styles.marginRight}
|
|
86
|
+
onChange={() => {}}
|
|
87
|
+
/>
|
|
88
|
+
<Checkbox
|
|
89
|
+
disabled={true}
|
|
90
|
+
checked={true}
|
|
91
|
+
style={styles.marginRight}
|
|
92
|
+
onChange={() => {}}
|
|
93
|
+
/>
|
|
94
|
+
</View>
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
Variants.parameters = {
|
|
98
|
+
docs: {
|
|
99
|
+
storyDescription:
|
|
100
|
+
"The checkbox has various styles for clickable states. Here are sets of default checkboxes, checkboxes in an error state, and disabled checkboxes.",
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const WithLabel: StoryComponentType = () => {
|
|
105
|
+
const [checked, setChecked] = React.useState(false);
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Checkbox
|
|
109
|
+
label="Receive assignment reminders for Algebra"
|
|
110
|
+
description="You will receive a reminder 24 hours before each deadline"
|
|
111
|
+
checked={checked}
|
|
112
|
+
onChange={setChecked}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
WithLabel.parameters = {
|
|
118
|
+
docs: {
|
|
119
|
+
storyDescription:
|
|
120
|
+
"The checkbox can have a optional label and description. This allows it to be used as a settings-like item. The user of this component is responsible for keeping track of checked state and providing an onChange callback.",
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export const AdditionalClickTarget: StoryComponentType = () => {
|
|
125
|
+
const [checked, setChecked] = React.useState(false);
|
|
126
|
+
const headingText = "Functions";
|
|
127
|
+
const descriptionText = `A great cook knows how to take basic
|
|
128
|
+
ingredients and prepare a delicious meal. In this topic, you will
|
|
129
|
+
become function-chefs! You will learn how to combine functions
|
|
130
|
+
with arithmetic operations and how to compose functions.`;
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<View style={styles.wrapper}>
|
|
134
|
+
<View style={styles.topic}>
|
|
135
|
+
<label htmlFor="topic-123">
|
|
136
|
+
<LabelMedium>{headingText}</LabelMedium>
|
|
137
|
+
</label>
|
|
138
|
+
<LabelSmall>{descriptionText}</LabelSmall>
|
|
139
|
+
</View>
|
|
140
|
+
<Checkbox checked={checked} id="topic-123" onChange={setChecked} />
|
|
141
|
+
</View>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
AdditionalClickTarget.parameters = {
|
|
146
|
+
docs: {
|
|
147
|
+
storyDescription:
|
|
148
|
+
"Sometimes one may wish to use a checkbox in a different context (label may not be right next to the checkbox), like in this example content item. Use a `<label htmlFor={id}>` element where the id matches the `id` prop of the Checkbox. This is for accessibility purposes, and doing this also automatically makes the label a click target for the checkbox.",
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const styles = StyleSheet.create({
|
|
153
|
+
row: {
|
|
154
|
+
flexDirection: "row",
|
|
155
|
+
},
|
|
156
|
+
marginRight: {
|
|
157
|
+
marginRight: 16,
|
|
158
|
+
},
|
|
159
|
+
wrapper: {
|
|
160
|
+
flexDirection: "row",
|
|
161
|
+
alignItems: "center",
|
|
162
|
+
justifyContent: "space-evenly",
|
|
163
|
+
},
|
|
164
|
+
topic: {
|
|
165
|
+
maxWidth: 600,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
@@ -79,8 +79,22 @@ type DefaultProps = {|
|
|
|
79
79
|
* ☑️ A nicely styled checkbox for all your checking needs. Can optionally take
|
|
80
80
|
* label and description props.
|
|
81
81
|
*
|
|
82
|
+
* If used by itself, a checkbox provides two options - checked and unchecked.
|
|
83
|
+
* A group of checkboxes can be used to allow a user to select multiple values
|
|
84
|
+
* from a list of options.
|
|
85
|
+
*
|
|
82
86
|
* If you want a whole group of Checkbox[es] that are related, see the Choice
|
|
83
87
|
* and CheckboxGroup components.
|
|
88
|
+
*
|
|
89
|
+
* ### Usage
|
|
90
|
+
*
|
|
91
|
+
* ```jsx
|
|
92
|
+
* import {Checkbox} from "@khanacademy/wonder-blocks-form";
|
|
93
|
+
*
|
|
94
|
+
* const [checked, setChecked] = React.useState(false);
|
|
95
|
+
*
|
|
96
|
+
* <Checkbox checked={checked} onChange={setChecked} />
|
|
97
|
+
* ```
|
|
84
98
|
*/
|
|
85
99
|
export default class Checkbox extends React.Component<ChoiceComponentProps> {
|
|
86
100
|
static defaultProps: DefaultProps = {
|