@khanacademy/wonder-blocks-form 2.4.4 → 2.4.7
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 +21 -0
- package/dist/index.js +150 -0
- package/package.json +5 -5
- package/src/components/__docs__/checkbox-group.stories.js +266 -0
- package/src/components/__docs__/checkbox.stories.js +1 -1
- package/src/components/__docs__/choice.stories.js +86 -0
- package/src/components/__docs__/labeled-text-field.argtypes.js +248 -0
- package/src/components/{labeled-text-field.stories.js → __docs__/labeled-text-field.stories.js} +274 -35
- package/src/components/__docs__/radio-group.stories.js +182 -0
- package/src/components/__docs__/radio.stories.js +160 -0
- package/src/components/__docs__/text-field.argtypes.js +206 -0
- package/src/components/{text-field.stories.js → __docs__/text-field.stories.js} +268 -28
- package/src/components/checkbox-group.js +27 -0
- package/src/components/choice.js +59 -1
- package/src/components/labeled-text-field.js +20 -0
- package/src/components/radio-group.js +27 -0
- package/src/components/text-field.js +17 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {StyleSheet} from "aphrodite";
|
|
4
|
+
|
|
5
|
+
import {View} from "@khanacademy/wonder-blocks-core";
|
|
6
|
+
import {Radio} from "@khanacademy/wonder-blocks-form";
|
|
7
|
+
import {LabelMedium, LabelSmall} from "@khanacademy/wonder-blocks-typography";
|
|
8
|
+
import type {StoryComponentType} from "@storybook/react";
|
|
9
|
+
|
|
10
|
+
import ComponentInfo from "../../../../../.storybook/components/component-info.js";
|
|
11
|
+
import {name, version} from "../../../package.json";
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
title: "Form / Radio",
|
|
15
|
+
component: Radio,
|
|
16
|
+
parameters: {
|
|
17
|
+
componentSubtitle: ((
|
|
18
|
+
<ComponentInfo name={name} version={version} />
|
|
19
|
+
): any),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Default: StoryComponentType = (args) => <Radio {...args} />;
|
|
24
|
+
|
|
25
|
+
Default.args = {
|
|
26
|
+
checked: false,
|
|
27
|
+
onChange: () => {},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
Default.parameters = {
|
|
31
|
+
chromatic: {
|
|
32
|
+
// We already have screenshots of another story that covers
|
|
33
|
+
// this and more cases.
|
|
34
|
+
disableSnapshot: true,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const Controlled: StoryComponentType = () => {
|
|
39
|
+
const [checked, setChecked] = React.useState(false);
|
|
40
|
+
return <Radio checked={checked} onChange={setChecked} />;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
Controlled.parameters = {
|
|
44
|
+
chromatic: {
|
|
45
|
+
// Disabling because this doesn't test visuals, it tests
|
|
46
|
+
// that the `checked` state works as expected.
|
|
47
|
+
disableSnapshot: true,
|
|
48
|
+
},
|
|
49
|
+
docs: {
|
|
50
|
+
storyDescription: `Use state to keep track of whether
|
|
51
|
+
the radio button has been checked. A radio button cannot be unchecked
|
|
52
|
+
by the user once it has been checked. It would become unchecked if a
|
|
53
|
+
different radio button is selected as part of a radio group.`,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const Variants: StoryComponentType = () => (
|
|
58
|
+
<View style={styles.row}>
|
|
59
|
+
<Radio checked={false} style={styles.marginRight} onChange={() => {}} />
|
|
60
|
+
<Radio checked={true} style={styles.marginRight} onChange={() => {}} />
|
|
61
|
+
<Radio
|
|
62
|
+
error={true}
|
|
63
|
+
checked={false}
|
|
64
|
+
style={styles.marginRight}
|
|
65
|
+
onChange={() => {}}
|
|
66
|
+
/>
|
|
67
|
+
<Radio
|
|
68
|
+
error={true}
|
|
69
|
+
checked={true}
|
|
70
|
+
style={styles.marginRight}
|
|
71
|
+
onChange={() => {}}
|
|
72
|
+
/>
|
|
73
|
+
<Radio
|
|
74
|
+
disabled={true}
|
|
75
|
+
checked={false}
|
|
76
|
+
style={styles.marginRight}
|
|
77
|
+
onChange={() => {}}
|
|
78
|
+
/>
|
|
79
|
+
<Radio disabled={true} checked={true} onChange={() => {}} />
|
|
80
|
+
</View>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
Variants.parameters = {
|
|
84
|
+
docs: {
|
|
85
|
+
storyDescription: `The radio button has various styles for
|
|
86
|
+
clickable states. Here are sets of default radio buttons,
|
|
87
|
+
radio buttons in an error state, and disabled radio buttons.`,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const WithLabel: StoryComponentType = () => {
|
|
92
|
+
const [checked, setChecked] = React.useState(false);
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<Radio
|
|
96
|
+
label="Easy"
|
|
97
|
+
description="Opt for a less difficult exercise set."
|
|
98
|
+
checked={checked}
|
|
99
|
+
onChange={setChecked}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
WithLabel.parameters = {
|
|
105
|
+
docs: {
|
|
106
|
+
storyDescription: `The radio button can have an optional label
|
|
107
|
+
and description. This allows it to be used as a settings-like item,
|
|
108
|
+
as opposed to its usage in a radio grid.`,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const AdditionalClickTarget: StoryComponentType = () => {
|
|
113
|
+
const [checked, setChecked] = React.useState(false);
|
|
114
|
+
const headingText = "Functions";
|
|
115
|
+
const descriptionText = `A great cook knows how to take basic
|
|
116
|
+
ingredients and prepare a delicious meal. In this topic, you will
|
|
117
|
+
become function-chefs! You will learn how to combine functions
|
|
118
|
+
with arithmetic operations and how to compose functions.`;
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<View style={styles.wrapper}>
|
|
122
|
+
<View style={styles.topic}>
|
|
123
|
+
<label htmlFor="topic-123">
|
|
124
|
+
<LabelMedium>{headingText}</LabelMedium>
|
|
125
|
+
</label>
|
|
126
|
+
<LabelSmall>{descriptionText}</LabelSmall>
|
|
127
|
+
</View>
|
|
128
|
+
<Radio checked={checked} id="topic-123" onChange={setChecked} />
|
|
129
|
+
</View>
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
AdditionalClickTarget.parameters = {
|
|
134
|
+
docs: {
|
|
135
|
+
storyDescription: `Sometimes one may wish to use a radio button
|
|
136
|
+
in a different context (label may not be right next to the
|
|
137
|
+
radio button), like in this example content item. Use a
|
|
138
|
+
\`<label htmlFor={id}>\` element where the id matches the \`id\`
|
|
139
|
+
prop of the Radio. This is for accessibility purposes,
|
|
140
|
+
and doing this also automatically makes the label a click target
|
|
141
|
+
for the radio button.`,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const styles = StyleSheet.create({
|
|
146
|
+
row: {
|
|
147
|
+
flexDirection: "row",
|
|
148
|
+
},
|
|
149
|
+
marginRight: {
|
|
150
|
+
marginRight: 16,
|
|
151
|
+
},
|
|
152
|
+
wrapper: {
|
|
153
|
+
flexDirection: "row",
|
|
154
|
+
alignItems: "center",
|
|
155
|
+
justifyContent: "space-evenly",
|
|
156
|
+
},
|
|
157
|
+
topic: {
|
|
158
|
+
maxWidth: 600,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
id: {
|
|
5
|
+
description: "The unique identifier for the input.",
|
|
6
|
+
type: {required: true},
|
|
7
|
+
table: {
|
|
8
|
+
type: {
|
|
9
|
+
summary: "string",
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
control: {
|
|
13
|
+
type: "text",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
type: {
|
|
17
|
+
description:
|
|
18
|
+
"Determines the type of input. Defaults to text. This may change the appearance or type of characters allowed.",
|
|
19
|
+
table: {
|
|
20
|
+
type: {
|
|
21
|
+
summary: `"text" | "password" | "email" | "number" | "tel"`,
|
|
22
|
+
},
|
|
23
|
+
defaultValue: {
|
|
24
|
+
summary: "text",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
options: ["text", "password", "email", "number", "tel"],
|
|
28
|
+
control: {
|
|
29
|
+
type: "select",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
value: {
|
|
33
|
+
description: "The input value.",
|
|
34
|
+
type: {required: true},
|
|
35
|
+
table: {
|
|
36
|
+
type: {
|
|
37
|
+
summary: "string",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
control: {type: "text"},
|
|
41
|
+
},
|
|
42
|
+
autoComplete: {
|
|
43
|
+
description: "Specifies if the input field allows autocomplete.",
|
|
44
|
+
table: {
|
|
45
|
+
type: {
|
|
46
|
+
summary: "string",
|
|
47
|
+
detail: `There is a large number of options, including "on", "off", "username", "current-password", and many others.`,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
control: {
|
|
51
|
+
type: "text",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
disabled: {
|
|
55
|
+
description: "Makes a read-only input field that cannot be focused.",
|
|
56
|
+
table: {
|
|
57
|
+
type: {
|
|
58
|
+
summary: "boolean",
|
|
59
|
+
},
|
|
60
|
+
defaultValue: {
|
|
61
|
+
summary: "false",
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
control: {
|
|
65
|
+
type: "boolean",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
light: {
|
|
69
|
+
description:
|
|
70
|
+
"Change the default focus ring color to fit a dark background.",
|
|
71
|
+
table: {
|
|
72
|
+
type: {
|
|
73
|
+
summary: "boolean",
|
|
74
|
+
},
|
|
75
|
+
defaultValue: {
|
|
76
|
+
summary: "false",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
control: {
|
|
80
|
+
type: "boolean",
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
required: {
|
|
84
|
+
description:
|
|
85
|
+
"Whether this field is required to to continue, or the error message to render if this field is left blank. Pass in a message instead of `true` if possible.",
|
|
86
|
+
table: {
|
|
87
|
+
type: {
|
|
88
|
+
summary: "boolean | string",
|
|
89
|
+
detail: "The string will not be used if a `validate` prop is passed in.",
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
control: {
|
|
93
|
+
type: "null",
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
placeholder: {
|
|
97
|
+
description: "Provide hints or examples of what to enter.",
|
|
98
|
+
table: {
|
|
99
|
+
type: {
|
|
100
|
+
summary: "string",
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
control: {
|
|
104
|
+
type: "text",
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
readOnly: {
|
|
108
|
+
description: "Specifies if the input field is read-only.",
|
|
109
|
+
table: {
|
|
110
|
+
type: {
|
|
111
|
+
summary: "boolean",
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
control: {
|
|
115
|
+
type: "boolean",
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
style: {
|
|
119
|
+
description: "Custom styles for the input.",
|
|
120
|
+
table: {
|
|
121
|
+
type: {
|
|
122
|
+
summary: "StyleType",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
testId: {
|
|
127
|
+
description: "Optional test ID for e2e testing.",
|
|
128
|
+
table: {
|
|
129
|
+
type: {
|
|
130
|
+
summary: "string",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
control: {
|
|
134
|
+
type: "text",
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
validate: {
|
|
138
|
+
description:
|
|
139
|
+
"Provide a validation for the input value. Return a string error message or null | void for a valid input.",
|
|
140
|
+
table: {
|
|
141
|
+
type: {
|
|
142
|
+
summary: "(value: string) => ?string",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
control: {
|
|
146
|
+
type: "null",
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Events
|
|
152
|
+
*/
|
|
153
|
+
onValidate: {
|
|
154
|
+
description: "Called right after the TextField input is validated.",
|
|
155
|
+
table: {
|
|
156
|
+
category: "Events",
|
|
157
|
+
type: {
|
|
158
|
+
summary: "(errorMessage: ?string) => mixed",
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
onChange: {
|
|
163
|
+
description:
|
|
164
|
+
"Called when the value has changed. Use this in conjunction with the `value` prop to update the string rendered in the input field.",
|
|
165
|
+
type: {required: true},
|
|
166
|
+
table: {
|
|
167
|
+
category: "Events",
|
|
168
|
+
type: {
|
|
169
|
+
summary: "(newValue: string) => mixed",
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
onKeyDown: {
|
|
174
|
+
action: "keyDown",
|
|
175
|
+
description: "Called when a key is pressed.",
|
|
176
|
+
table: {
|
|
177
|
+
category: "Events",
|
|
178
|
+
type: {
|
|
179
|
+
summary:
|
|
180
|
+
"(event: SyntheticKeyboardEvent<HTMLInputElement>) => mixed",
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
onFocus: {
|
|
185
|
+
action: "focus",
|
|
186
|
+
description: "Called when the element has been focused.",
|
|
187
|
+
table: {
|
|
188
|
+
category: "Events",
|
|
189
|
+
type: {
|
|
190
|
+
summary:
|
|
191
|
+
"(event: SyntheticFocusEvent<HTMLInputElement>) => mixed",
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
onBlur: {
|
|
196
|
+
action: "blur",
|
|
197
|
+
description: "Called when the element has been blurred.",
|
|
198
|
+
table: {
|
|
199
|
+
category: "Events",
|
|
200
|
+
type: {
|
|
201
|
+
summary:
|
|
202
|
+
"(event: SyntheticFocusEvent<HTMLInputElement>) => mixed",
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
};
|