@khanacademy/perseus 1.3.0 → 1.3.2
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 +14 -0
- package/dist/es/index.js +18 -14
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +18 -14
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/index.js +1 -0
- package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +26 -16
- package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +26 -16
- package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +273 -169
- package/src/widgets/radio/__stories__/sat-styles.stories.jsx +191 -0
- package/src/widgets/radio/base-radio.jsx +1 -2
- package/src/widgets/radio/choice.jsx +13 -10
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import {action} from "@storybook/addon-actions";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import {generateChoice} from "../../__testdata__/base-radio_testdata.js";
|
|
6
|
+
import BaseRadio from "../base-radio.jsx";
|
|
7
|
+
|
|
8
|
+
type StoryArgs = {|
|
|
9
|
+
multipleSelect: boolean,
|
|
10
|
+
editMode: boolean,
|
|
11
|
+
countChoices: boolean,
|
|
12
|
+
|};
|
|
13
|
+
|
|
14
|
+
type Story = {|
|
|
15
|
+
title: string,
|
|
16
|
+
args: StoryArgs,
|
|
17
|
+
|};
|
|
18
|
+
|
|
19
|
+
export default ({
|
|
20
|
+
title: "Perseus/Widgets/Radio/SAT Base Radio",
|
|
21
|
+
args: {
|
|
22
|
+
multipleSelect: false,
|
|
23
|
+
editMode: false,
|
|
24
|
+
countChoices: false,
|
|
25
|
+
},
|
|
26
|
+
}: Story);
|
|
27
|
+
|
|
28
|
+
const defaultProps = {
|
|
29
|
+
apiOptions: {
|
|
30
|
+
satStyling: true,
|
|
31
|
+
},
|
|
32
|
+
choices: [
|
|
33
|
+
generateChoice({
|
|
34
|
+
content: "Content 1",
|
|
35
|
+
}),
|
|
36
|
+
generateChoice({
|
|
37
|
+
content: "Content 2",
|
|
38
|
+
}),
|
|
39
|
+
generateChoice({
|
|
40
|
+
content: "Content 3",
|
|
41
|
+
correct: true,
|
|
42
|
+
}),
|
|
43
|
+
generateChoice({
|
|
44
|
+
isNoneOfTheAbove: true,
|
|
45
|
+
}),
|
|
46
|
+
],
|
|
47
|
+
deselectEnabled: false,
|
|
48
|
+
editMode: false,
|
|
49
|
+
labelWrap: false,
|
|
50
|
+
countChoices: false,
|
|
51
|
+
numCorrect: 1,
|
|
52
|
+
multipleSelect: false,
|
|
53
|
+
|
|
54
|
+
// A callback indicating that this choice has changed. Its argument is
|
|
55
|
+
// an object with two keys: `checked` and `crossedOut`. Each contains
|
|
56
|
+
// an array of boolean values, specifying the new checked and
|
|
57
|
+
// crossed-out value of each choice.
|
|
58
|
+
onChange: action("changed"),
|
|
59
|
+
|
|
60
|
+
// Whether this widget was the most recently used widget in this
|
|
61
|
+
// Renderer. Determines whether we'll auto-scroll the page upon
|
|
62
|
+
// entering review mode.
|
|
63
|
+
isLastUsedWidget: false,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const Interactive = (args: StoryArgs): React.Node => {
|
|
67
|
+
const overwrittenProps = {...defaultProps, ...args};
|
|
68
|
+
return <BaseRadio {...overwrittenProps} />;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const SingleSelectWithNothingSelected = (
|
|
72
|
+
args: StoryArgs,
|
|
73
|
+
): React.Node => {
|
|
74
|
+
const overwrittenProps = {...defaultProps, multipleSelect: false};
|
|
75
|
+
return <BaseRadio {...overwrittenProps} />;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const MultipleSelectWithNothingSelected = (
|
|
79
|
+
args: StoryArgs,
|
|
80
|
+
): React.Node => {
|
|
81
|
+
const overwrittenProps = {...defaultProps, multipleSelect: true};
|
|
82
|
+
return <BaseRadio {...overwrittenProps} />;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const MultipleSelectWithCountChoicesLabel = (
|
|
86
|
+
args: StoryArgs,
|
|
87
|
+
): React.Node => {
|
|
88
|
+
const overwrittenProps = {
|
|
89
|
+
...defaultProps,
|
|
90
|
+
multipleSelect: true,
|
|
91
|
+
numCorrect: 2,
|
|
92
|
+
countChoices: true,
|
|
93
|
+
};
|
|
94
|
+
return <BaseRadio {...overwrittenProps} />;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const SingleSelected = (args: StoryArgs): React.Node => {
|
|
98
|
+
const choices = Array(4)
|
|
99
|
+
.fill(null)
|
|
100
|
+
.map((_, i) => generateChoice({content: `Choice ${i + 1}`}));
|
|
101
|
+
choices[1].checked = true;
|
|
102
|
+
|
|
103
|
+
const overwrittenProps = {...defaultProps, multipleSelect: false, choices};
|
|
104
|
+
return <BaseRadio {...overwrittenProps} />;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const MultipleSelected = (args: StoryArgs): React.Node => {
|
|
108
|
+
const choices = Array(4)
|
|
109
|
+
.fill(null)
|
|
110
|
+
.map((_, i) => generateChoice({content: `Choice ${i + 1}`}));
|
|
111
|
+
choices[1].checked = true;
|
|
112
|
+
choices[2].checked = true;
|
|
113
|
+
|
|
114
|
+
const overwrittenProps = {...defaultProps, multipleSelect: true, choices};
|
|
115
|
+
return <BaseRadio {...overwrittenProps} />;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const SingleKitchenSink = (args: StoryArgs): React.Node => {
|
|
119
|
+
const choices = Array(4)
|
|
120
|
+
.fill(null)
|
|
121
|
+
.map((_, i) => {
|
|
122
|
+
const choice = generateChoice({
|
|
123
|
+
content: `Choice ${i + 1}`,
|
|
124
|
+
rationale: "This is a neat rationale",
|
|
125
|
+
hasRationale: true,
|
|
126
|
+
showRationale: true,
|
|
127
|
+
correct: false,
|
|
128
|
+
showCorrectness: true,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
return choice;
|
|
132
|
+
});
|
|
133
|
+
choices[1].checked = true;
|
|
134
|
+
choices[2].correct = true;
|
|
135
|
+
|
|
136
|
+
const rubricChoices = choices.map(({correct}) => ({
|
|
137
|
+
// note(matthew): reviewModeRubric.choices requires content,
|
|
138
|
+
// but I don't see how it's getting used and Flow gets mad
|
|
139
|
+
// when I use choice.content because it's not a string.
|
|
140
|
+
// reviewModeRubric could probably use a look over.
|
|
141
|
+
content: "",
|
|
142
|
+
correct,
|
|
143
|
+
}));
|
|
144
|
+
|
|
145
|
+
const overwrittenProps = {
|
|
146
|
+
...defaultProps,
|
|
147
|
+
multipleSelect: false,
|
|
148
|
+
reviewModeRubric: {choices: rubricChoices},
|
|
149
|
+
choices,
|
|
150
|
+
};
|
|
151
|
+
return <BaseRadio {...overwrittenProps} />;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const MultipleKitchenSink = (args: StoryArgs): React.Node => {
|
|
155
|
+
const choices = Array(4)
|
|
156
|
+
.fill(null)
|
|
157
|
+
.map((_, i) => {
|
|
158
|
+
const choice = generateChoice({
|
|
159
|
+
content: `Choice ${i + 1}`,
|
|
160
|
+
rationale: "This is a neat rationale",
|
|
161
|
+
hasRationale: true,
|
|
162
|
+
showRationale: true,
|
|
163
|
+
correct: false,
|
|
164
|
+
showCorrectness: true,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return choice;
|
|
168
|
+
});
|
|
169
|
+
choices[1].checked = true;
|
|
170
|
+
choices[2].checked = true;
|
|
171
|
+
choices[2].correct = true;
|
|
172
|
+
choices[3].correct = true;
|
|
173
|
+
|
|
174
|
+
const rubricChoices = choices.map((c) => ({
|
|
175
|
+
// note(matthew): reviewModeRubric.choices requires content,
|
|
176
|
+
// but I don't see how it's getting used and Flow gets mad
|
|
177
|
+
// when I use choice.content because it's not a string.
|
|
178
|
+
// reviewModeRubric could probably use a look over.
|
|
179
|
+
content: "",
|
|
180
|
+
correct: c.correct,
|
|
181
|
+
}));
|
|
182
|
+
|
|
183
|
+
const overwrittenProps = {
|
|
184
|
+
...defaultProps,
|
|
185
|
+
multipleSelect: true,
|
|
186
|
+
numCorrect: 2,
|
|
187
|
+
reviewModeRubric: {choices: rubricChoices},
|
|
188
|
+
choices,
|
|
189
|
+
};
|
|
190
|
+
return <BaseRadio {...overwrittenProps} />;
|
|
191
|
+
};
|
|
@@ -14,7 +14,7 @@ import {useState, useEffect} from "react";
|
|
|
14
14
|
import _ from "underscore";
|
|
15
15
|
|
|
16
16
|
import Icon from "../../components/icon.jsx";
|
|
17
|
-
import {ApiOptions} from "../../perseus-api.jsx";
|
|
17
|
+
import {ApiOptions, ClassNames} from "../../perseus-api.jsx";
|
|
18
18
|
import * as styleConstants from "../../styles/constants.js";
|
|
19
19
|
import mediaQueries from "../../styles/media-queries.js";
|
|
20
20
|
|
|
@@ -140,7 +140,6 @@ function Choice(props: ChoicePropsWithForwardRef): React.Node {
|
|
|
140
140
|
css(
|
|
141
141
|
!apiOptions.satStyling && styles.description,
|
|
142
142
|
apiOptions.satStyling && styles.satDescription,
|
|
143
|
-
satCorrectChoice && styles.satDescriptionCorrect,
|
|
144
143
|
satCorrectChoice && checked && styles.satDescriptionCorrectChecked,
|
|
145
144
|
satIncorrectChecked && styles.satDescriptionIncorrectChecked,
|
|
146
145
|
),
|
|
@@ -178,13 +177,14 @@ function Choice(props: ChoicePropsWithForwardRef): React.Node {
|
|
|
178
177
|
style={{
|
|
179
178
|
dispay: "flex",
|
|
180
179
|
flexDirection: "column",
|
|
180
|
+
color: Color.offBlack,
|
|
181
181
|
}}
|
|
182
|
+
className={descriptionClassName}
|
|
182
183
|
>
|
|
183
184
|
<div
|
|
184
185
|
style={{
|
|
185
186
|
display: "flex",
|
|
186
187
|
flexDirection: "row",
|
|
187
|
-
width: "100%",
|
|
188
188
|
opacity: showDimmed ? 0.5 : 1.0,
|
|
189
189
|
}}
|
|
190
190
|
>
|
|
@@ -208,6 +208,7 @@ function Choice(props: ChoicePropsWithForwardRef): React.Node {
|
|
|
208
208
|
apiOptions.readOnly
|
|
209
209
|
}
|
|
210
210
|
tabIndex={-1}
|
|
211
|
+
className={ClassNames.RADIO.OPTION_CONTENT}
|
|
211
212
|
/>
|
|
212
213
|
<label htmlFor={choiceId}>
|
|
213
214
|
{a11yText} {content}
|
|
@@ -222,7 +223,6 @@ function Choice(props: ChoicePropsWithForwardRef): React.Node {
|
|
|
222
223
|
crossedOut: false,
|
|
223
224
|
});
|
|
224
225
|
}}
|
|
225
|
-
className={descriptionClassName}
|
|
226
226
|
disabled={
|
|
227
227
|
disabled ||
|
|
228
228
|
apiOptions.staticRender ||
|
|
@@ -341,6 +341,14 @@ function Choice(props: ChoicePropsWithForwardRef): React.Node {
|
|
|
341
341
|
pos,
|
|
342
342
|
)}`}
|
|
343
343
|
disabled={apiOptions.staticRender}
|
|
344
|
+
style={{
|
|
345
|
+
alignSelf: "center",
|
|
346
|
+
padding: "5px",
|
|
347
|
+
display: "flex",
|
|
348
|
+
justifyContent: "center",
|
|
349
|
+
alignItems: "center",
|
|
350
|
+
marginLeft: "10px",
|
|
351
|
+
}}
|
|
344
352
|
>
|
|
345
353
|
{({hovered, focused, pressed}) => (
|
|
346
354
|
<Icon
|
|
@@ -390,11 +398,7 @@ const styles = StyleSheet.create({
|
|
|
390
398
|
boxSizing: "border-box",
|
|
391
399
|
cursor: "pointer",
|
|
392
400
|
marginLeft: 0,
|
|
393
|
-
padding: "17px 14px",
|
|
394
|
-
},
|
|
395
|
-
|
|
396
|
-
satDescriptionCorrect: {
|
|
397
|
-
color: styleConstants.satCorrectColor,
|
|
401
|
+
padding: "17px 14px 17px 0",
|
|
398
402
|
},
|
|
399
403
|
|
|
400
404
|
satDescriptionCorrectChecked: {
|
|
@@ -402,7 +406,6 @@ const styles = StyleSheet.create({
|
|
|
402
406
|
},
|
|
403
407
|
|
|
404
408
|
satDescriptionIncorrectChecked: {
|
|
405
|
-
color: styleConstants.satIncorrectColor,
|
|
406
409
|
backgroundColor: styleConstants.satIncorrectBackgroundColor,
|
|
407
410
|
},
|
|
408
411
|
|