@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.
@@ -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
+ };
@@ -454,8 +454,7 @@ const styles: StyleDeclaration = StyleSheet.create({
454
454
  },
455
455
 
456
456
  radio: {
457
- // Avoid centering
458
- width: "100%",
457
+ padding: 0,
459
458
  },
460
459
 
461
460
  responsiveRadioContainer: {
@@ -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} &nbsp; {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