@khanacademy/perseus 1.1.2 → 1.3.0
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 +23 -0
- package/dist/es/index.css +4 -5
- package/dist/es/index.js +1282 -1429
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +4 -5
- package/dist/index.js +1283 -1429
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/__tests__/__snapshots__/renderer_test.jsx.snap +21 -3
- package/src/components/highlighting/ui/highlight-tooltip.jsx +29 -23
- package/src/components/highlighting/ui/selection-tracker.jsx +17 -28
- package/src/styles/articles.less +0 -2
- package/src/styles/perseus-renderer.less +1 -1
- package/src/styles/widgets/passage.less +7 -3
- package/src/widgets/__stories__/passage-ref.stories.jsx +6 -2
- package/src/widgets/__stories__/passage.stories.jsx +9 -1
- package/src/widgets/__stories__/radio.stories.jsx +11 -0
- package/src/widgets/__testdata__/passage-ref_testdata.js +62 -0
- package/src/widgets/__testdata__/passage_testdata.js +25 -0
- package/src/widgets/__testdata__/radio_testdata.js +52 -0
- package/src/widgets/__tests__/__snapshots__/dropdown_test.jsx.snap +16 -2
- package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +18 -18
- package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +36 -36
- package/src/widgets/__tests__/passage_test.jsx +6 -8
- package/src/widgets/__tests__/radio_test.jsx +3 -1
- package/src/widgets/passage.jsx +12 -41
- package/src/widgets/radio/widget.jsx +128 -129
- package/src/components/new-tooltip/README.md +0 -3
- package/src/components/new-tooltip/__stories__/new-tooltip.stories.jsx +0 -665
- package/src/components/new-tooltip/__tests__/style-constants_test.js +0 -8
- package/src/components/new-tooltip/new-tooltip.jsx +0 -155
- package/src/components/new-tooltip/style-constants.js +0 -13
- package/src/components/new-tooltip/types.js +0 -183
- package/src/widgets/passage/get-line-height-for-node.js +0 -9
|
@@ -219,13 +219,13 @@ exports[`passage widget should snapshot multiple passages (mobile: false) 1`] =
|
|
|
219
219
|
<div>
|
|
220
220
|
<div>
|
|
221
221
|
<div
|
|
222
|
-
|
|
222
|
+
aria-hidden="true"
|
|
223
|
+
class="measurer_225hir"
|
|
223
224
|
>
|
|
224
|
-
<div
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
<div />
|
|
225
|
+
<div
|
|
226
|
+
class="paragraph"
|
|
227
|
+
>
|
|
228
|
+
Line Height Measurement
|
|
229
229
|
</div>
|
|
230
230
|
</div>
|
|
231
231
|
<div
|
|
@@ -711,13 +711,13 @@ exports[`passage widget should snapshot multiple passages (mobile: false) 1`] =
|
|
|
711
711
|
<div>
|
|
712
712
|
<div>
|
|
713
713
|
<div
|
|
714
|
-
|
|
714
|
+
aria-hidden="true"
|
|
715
|
+
class="measurer_225hir"
|
|
715
716
|
>
|
|
716
|
-
<div
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
<div />
|
|
717
|
+
<div
|
|
718
|
+
class="paragraph"
|
|
719
|
+
>
|
|
720
|
+
Line Height Measurement
|
|
721
721
|
</div>
|
|
722
722
|
</div>
|
|
723
723
|
<div
|
|
@@ -1090,13 +1090,13 @@ exports[`passage widget should snapshot multiple passages (mobile: true) 1`] = `
|
|
|
1090
1090
|
<div>
|
|
1091
1091
|
<div>
|
|
1092
1092
|
<div
|
|
1093
|
-
|
|
1093
|
+
aria-hidden="true"
|
|
1094
|
+
class="measurer_225hir"
|
|
1094
1095
|
>
|
|
1095
|
-
<div
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
<div />
|
|
1096
|
+
<div
|
|
1097
|
+
class="paragraph"
|
|
1098
|
+
>
|
|
1099
|
+
Line Height Measurement
|
|
1100
1100
|
</div>
|
|
1101
1101
|
</div>
|
|
1102
1102
|
<div
|
|
@@ -1582,13 +1582,13 @@ exports[`passage widget should snapshot multiple passages (mobile: true) 1`] = `
|
|
|
1582
1582
|
<div>
|
|
1583
1583
|
<div>
|
|
1584
1584
|
<div
|
|
1585
|
-
|
|
1585
|
+
aria-hidden="true"
|
|
1586
|
+
class="measurer_225hir"
|
|
1586
1587
|
>
|
|
1587
|
-
<div
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
<div />
|
|
1588
|
+
<div
|
|
1589
|
+
class="paragraph"
|
|
1590
|
+
>
|
|
1591
|
+
Line Height Measurement
|
|
1592
1592
|
</div>
|
|
1593
1593
|
</div>
|
|
1594
1594
|
<div
|
|
@@ -1840,13 +1840,13 @@ exports[`passage widget should snapshot simple passage (mobile: false) 1`] = `
|
|
|
1840
1840
|
<div>
|
|
1841
1841
|
<div>
|
|
1842
1842
|
<div
|
|
1843
|
-
|
|
1843
|
+
aria-hidden="true"
|
|
1844
|
+
class="measurer_225hir"
|
|
1844
1845
|
>
|
|
1845
|
-
<div
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
<div />
|
|
1846
|
+
<div
|
|
1847
|
+
class="paragraph"
|
|
1848
|
+
>
|
|
1849
|
+
Line Height Measurement
|
|
1850
1850
|
</div>
|
|
1851
1851
|
</div>
|
|
1852
1852
|
<div
|
|
@@ -1941,13 +1941,13 @@ exports[`passage widget should snapshot simple passage (mobile: true) 1`] = `
|
|
|
1941
1941
|
<div>
|
|
1942
1942
|
<div>
|
|
1943
1943
|
<div
|
|
1944
|
-
|
|
1944
|
+
aria-hidden="true"
|
|
1945
|
+
class="measurer_225hir"
|
|
1945
1946
|
>
|
|
1946
|
-
<div
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
<div />
|
|
1947
|
+
<div
|
|
1948
|
+
class="paragraph"
|
|
1949
|
+
>
|
|
1950
|
+
Line Height Measurement
|
|
1951
1951
|
</div>
|
|
1952
1952
|
</div>
|
|
1953
1953
|
<div
|
|
@@ -8,15 +8,12 @@ import {testDependencies} from "../../../../../testing/test-dependencies.js";
|
|
|
8
8
|
import * as Dependencies from "../../dependencies.js";
|
|
9
9
|
import {ApiOptions} from "../../perseus-api.jsx";
|
|
10
10
|
import {question1, question2} from "../__testdata__/passage_testdata.js";
|
|
11
|
-
import PassageWidgetExport from "../passage.jsx";
|
|
12
|
-
import * as getLineHeightModule from "../passage/get-line-height-for-node.js";
|
|
11
|
+
import PassageWidgetExport, {LineHeightMeasurer} from "../passage.jsx";
|
|
13
12
|
|
|
14
13
|
import {renderQuestion} from "./renderQuestion.jsx";
|
|
15
14
|
|
|
16
15
|
import type {APIOptions} from "../../types.js";
|
|
17
16
|
|
|
18
|
-
jest.mock("../passage/get-line-height-for-node.js");
|
|
19
|
-
|
|
20
17
|
function renderPassage(overwrite) {
|
|
21
18
|
const widgetPropsBase = {
|
|
22
19
|
footnotes: "",
|
|
@@ -53,10 +50,6 @@ function renderPassage(overwrite) {
|
|
|
53
50
|
|
|
54
51
|
describe("passage widget", () => {
|
|
55
52
|
beforeEach(() => {
|
|
56
|
-
jest.spyOn(getLineHeightModule, "getLineHeightForNode").mockReturnValue(
|
|
57
|
-
21,
|
|
58
|
-
);
|
|
59
|
-
|
|
60
53
|
// passage widget uses the height of the entire rendered passage along
|
|
61
54
|
// with the above line height calculations to determine the number of
|
|
62
55
|
// lines rendered (as a way to provide line numbering).
|
|
@@ -66,6 +59,11 @@ describe("passage widget", () => {
|
|
|
66
59
|
"get",
|
|
67
60
|
).mockReturnValue(200.0);
|
|
68
61
|
|
|
62
|
+
jest.spyOn(
|
|
63
|
+
LineHeightMeasurer.prototype,
|
|
64
|
+
"measureLineHeight",
|
|
65
|
+
).mockReturnValue(20);
|
|
66
|
+
|
|
69
67
|
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
70
68
|
testDependencies,
|
|
71
69
|
);
|
|
@@ -800,7 +800,9 @@ describe("multi-choice question", () => {
|
|
|
800
800
|
|
|
801
801
|
// Act
|
|
802
802
|
const option = screen.getAllByRole("checkbox");
|
|
803
|
-
userEvent.click(option[
|
|
803
|
+
userEvent.click(option[0]); // correct
|
|
804
|
+
userEvent.click(option[1]); // correct
|
|
805
|
+
userEvent.click(option[2]); // incorrect
|
|
804
806
|
|
|
805
807
|
// Assert
|
|
806
808
|
expect(renderer).toHaveInvalidInput(
|
package/src/widgets/passage.jsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
|
|
3
2
|
import {linterContextDefault} from "@khanacademy/perseus-linter";
|
|
4
3
|
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
5
4
|
import {StyleSheet, css} from "aphrodite";
|
|
@@ -12,7 +11,6 @@ import HighlightableContent from "../components/highlighting/highlightable-conte
|
|
|
12
11
|
import {getDependencies} from "../dependencies.js";
|
|
13
12
|
import Renderer from "../renderer.jsx";
|
|
14
13
|
|
|
15
|
-
import {getLineHeightForNode} from "./passage/get-line-height-for-node.js";
|
|
16
14
|
import PassageMarkdown from "./passage/passage-markdown.jsx";
|
|
17
15
|
|
|
18
16
|
import type {SerializedHighlightSet} from "../components/highlighting/types.js";
|
|
@@ -27,51 +25,24 @@ import type {
|
|
|
27
25
|
import type {ParseState} from "./passage/passage-markdown.jsx";
|
|
28
26
|
import type {SingleASTNode} from "@khanacademy/simple-markdown";
|
|
29
27
|
|
|
30
|
-
// A fake paragraph to measure the line height of the passage
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
class LineHeightMeasurer extends React.Component<{...}> {
|
|
35
|
-
_cachedLineHeight: number;
|
|
36
|
-
body: ?HTMLDivElement;
|
|
37
|
-
end: ?HTMLDivElement;
|
|
28
|
+
// A fake paragraph to measure the line height of the passage,
|
|
29
|
+
// so we can adapt to browser zoom
|
|
30
|
+
export class LineHeightMeasurer extends React.Component<{...}> {
|
|
31
|
+
_line: ?HTMLDivElement;
|
|
38
32
|
|
|
39
33
|
measureLineHeight(): number {
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return this._cachedLineHeight;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
forceMeasureLineHeight() {
|
|
48
|
-
const body = this.body;
|
|
49
|
-
const end = this.end;
|
|
50
|
-
|
|
51
|
-
if (!body || !end) {
|
|
52
|
-
return;
|
|
34
|
+
if (!this._line) {
|
|
35
|
+
return 0;
|
|
53
36
|
}
|
|
54
37
|
|
|
55
|
-
|
|
56
|
-
body.textContent = " \u0080";
|
|
57
|
-
|
|
58
|
-
// Now, the line height is the difference between the top of the
|
|
59
|
-
// second line and the top of the first line.
|
|
60
|
-
this._cachedLineHeight = getLineHeightForNode(body, end);
|
|
61
|
-
|
|
62
|
-
// Clear out the first line so it doesn't overlap the passage.
|
|
63
|
-
body.textContent = "";
|
|
38
|
+
return this._line.clientHeight;
|
|
64
39
|
}
|
|
65
40
|
|
|
66
41
|
render(): React.Node {
|
|
67
42
|
return (
|
|
68
|
-
<div className={css(styles.measurer)}>
|
|
69
|
-
<div>
|
|
70
|
-
|
|
71
|
-
ref={(ref) => (this.body = ref)}
|
|
72
|
-
className="paragraph"
|
|
73
|
-
/>
|
|
74
|
-
<div ref={(ref) => (this.end = ref)} />
|
|
43
|
+
<div className={css(styles.measurer)} aria-hidden="true">
|
|
44
|
+
<div ref={(ref) => (this._line = ref)} className="paragraph">
|
|
45
|
+
Line Height Measurement
|
|
75
46
|
</div>
|
|
76
47
|
</div>
|
|
77
48
|
);
|
|
@@ -81,9 +52,9 @@ class LineHeightMeasurer extends React.Component<{...}> {
|
|
|
81
52
|
const styles = StyleSheet.create({
|
|
82
53
|
measurer: {
|
|
83
54
|
position: "absolute",
|
|
84
|
-
width: "100%",
|
|
85
55
|
top: 0,
|
|
86
56
|
left: 0,
|
|
57
|
+
visibility: "hidden",
|
|
87
58
|
// keep from blocking text selection
|
|
88
59
|
zIndex: -1,
|
|
89
60
|
},
|
|
@@ -168,7 +139,7 @@ class Passage extends React.Component<PassageProps, PassageState> {
|
|
|
168
139
|
// height changes we expect are subpixel changes when the user
|
|
169
140
|
// zooms in/out, and the only way to listen for zoom events is to
|
|
170
141
|
// listen for resize events.
|
|
171
|
-
this._lineHeightMeasurerRef?.
|
|
142
|
+
this._lineHeightMeasurerRef?.measureLineHeight();
|
|
172
143
|
this._updateState();
|
|
173
144
|
}, 500);
|
|
174
145
|
window.addEventListener("resize", this._onResize);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable react/sort-comp */
|
|
2
1
|
// @flow
|
|
3
2
|
import {linterContextDefault} from "@khanacademy/perseus-linter";
|
|
4
3
|
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
@@ -66,6 +65,134 @@ class Radio extends React.Component<Props> {
|
|
|
66
65
|
linterContext: linterContextDefault,
|
|
67
66
|
};
|
|
68
67
|
|
|
68
|
+
static validate(userInput: UserInput, rubric: Rubric): PerseusScore {
|
|
69
|
+
const numSelected = userInput.choicesSelected.reduce(
|
|
70
|
+
(sum, selected) => {
|
|
71
|
+
return sum + (selected ? 1 : 0);
|
|
72
|
+
},
|
|
73
|
+
0,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
if (numSelected === 0) {
|
|
77
|
+
return {
|
|
78
|
+
type: "invalid",
|
|
79
|
+
message: null,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
userInput.numCorrect &&
|
|
85
|
+
userInput.numCorrect > 1 &&
|
|
86
|
+
numSelected !== userInput.numCorrect
|
|
87
|
+
) {
|
|
88
|
+
return {
|
|
89
|
+
type: "invalid",
|
|
90
|
+
message: i18n._("Please choose the correct number of answers."),
|
|
91
|
+
};
|
|
92
|
+
// If NOTA and some other answer are checked, ...
|
|
93
|
+
}
|
|
94
|
+
if (userInput.noneOfTheAboveSelected && numSelected > 1) {
|
|
95
|
+
return {
|
|
96
|
+
type: "invalid",
|
|
97
|
+
message: i18n._(
|
|
98
|
+
"'None of the above' may not be selected " +
|
|
99
|
+
"when other answers are selected.",
|
|
100
|
+
),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const correct = userInput.choicesSelected.every((selected, i) => {
|
|
105
|
+
let isCorrect;
|
|
106
|
+
if (userInput.noneOfTheAboveIndex === i) {
|
|
107
|
+
isCorrect = rubric.choices.every((choice, j) => {
|
|
108
|
+
return i === j || !choice.correct;
|
|
109
|
+
});
|
|
110
|
+
} else {
|
|
111
|
+
isCorrect = !!rubric.choices[i].correct;
|
|
112
|
+
}
|
|
113
|
+
return isCorrect === selected;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
type: "points",
|
|
118
|
+
earned: correct ? 1 : 0,
|
|
119
|
+
total: 1,
|
|
120
|
+
message: null,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
static getUserInputFromProps(props: Props): UserInput {
|
|
125
|
+
// Return checked inputs in the form {choicesSelected: [bool]}. (Dear
|
|
126
|
+
// future timeline implementers: this used to be {value: i} before
|
|
127
|
+
// multiple select was added)
|
|
128
|
+
if (props.choiceStates) {
|
|
129
|
+
let noneOfTheAboveIndex = null;
|
|
130
|
+
let noneOfTheAboveSelected = false;
|
|
131
|
+
|
|
132
|
+
const choiceStates = props.choiceStates;
|
|
133
|
+
const choicesSelected = choiceStates.map(() => false);
|
|
134
|
+
const countChoices = props.countChoices;
|
|
135
|
+
const numCorrect = props.numCorrect;
|
|
136
|
+
|
|
137
|
+
for (let i = 0; i < choicesSelected.length; i++) {
|
|
138
|
+
const index = props.choices[i].originalIndex;
|
|
139
|
+
|
|
140
|
+
choicesSelected[index] = choiceStates[i].selected;
|
|
141
|
+
|
|
142
|
+
if (props.choices[i].isNoneOfTheAbove) {
|
|
143
|
+
noneOfTheAboveIndex = index;
|
|
144
|
+
|
|
145
|
+
if (choicesSelected[i]) {
|
|
146
|
+
noneOfTheAboveSelected = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
countChoices,
|
|
153
|
+
choicesSelected,
|
|
154
|
+
numCorrect,
|
|
155
|
+
noneOfTheAboveIndex,
|
|
156
|
+
noneOfTheAboveSelected,
|
|
157
|
+
};
|
|
158
|
+
// Support legacy choiceState implementation
|
|
159
|
+
}
|
|
160
|
+
/* istanbul ignore if - props.values is deprecated */
|
|
161
|
+
const {values} = props;
|
|
162
|
+
if (values) {
|
|
163
|
+
let noneOfTheAboveIndex = null;
|
|
164
|
+
let noneOfTheAboveSelected = false;
|
|
165
|
+
|
|
166
|
+
const choicesSelected = [...values];
|
|
167
|
+
const countChoices = props.countChoices;
|
|
168
|
+
const numCorrect = props.numCorrect;
|
|
169
|
+
const valuesLength = values.length;
|
|
170
|
+
|
|
171
|
+
for (let i = 0; i < valuesLength; i++) {
|
|
172
|
+
const index = props.choices[i].originalIndex;
|
|
173
|
+
choicesSelected[index] = values[i];
|
|
174
|
+
|
|
175
|
+
if (props.choices[i].isNoneOfTheAbove) {
|
|
176
|
+
noneOfTheAboveIndex = index;
|
|
177
|
+
if (choicesSelected[i]) {
|
|
178
|
+
noneOfTheAboveSelected = true;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
choicesSelected,
|
|
184
|
+
noneOfTheAboveIndex,
|
|
185
|
+
noneOfTheAboveSelected,
|
|
186
|
+
countChoices,
|
|
187
|
+
numCorrect,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// Nothing checked
|
|
191
|
+
return {
|
|
192
|
+
choicesSelected: props.choices.map(() => false),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
69
196
|
_renderRenderer: (content?: string) => React.Node = (
|
|
70
197
|
content?: string = "",
|
|
71
198
|
) => {
|
|
@@ -382,134 +509,6 @@ class Radio extends React.Component<Props> {
|
|
|
382
509
|
/>
|
|
383
510
|
);
|
|
384
511
|
}
|
|
385
|
-
|
|
386
|
-
static validate(userInput: UserInput, rubric: Rubric): PerseusScore {
|
|
387
|
-
const numSelected = userInput.choicesSelected.reduce(
|
|
388
|
-
(sum, selected) => {
|
|
389
|
-
return sum + (selected ? 1 : 0);
|
|
390
|
-
},
|
|
391
|
-
0,
|
|
392
|
-
);
|
|
393
|
-
|
|
394
|
-
if (numSelected === 0) {
|
|
395
|
-
return {
|
|
396
|
-
type: "invalid",
|
|
397
|
-
message: null,
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
if (
|
|
402
|
-
userInput.numCorrect &&
|
|
403
|
-
userInput.numCorrect > 1 &&
|
|
404
|
-
numSelected !== userInput.numCorrect
|
|
405
|
-
) {
|
|
406
|
-
return {
|
|
407
|
-
type: "invalid",
|
|
408
|
-
message: i18n._("Please choose the correct number of answers."),
|
|
409
|
-
};
|
|
410
|
-
// If NOTA and some other answer are checked, ...
|
|
411
|
-
}
|
|
412
|
-
if (userInput.noneOfTheAboveSelected && numSelected > 1) {
|
|
413
|
-
return {
|
|
414
|
-
type: "invalid",
|
|
415
|
-
message: i18n._(
|
|
416
|
-
"'None of the above' may not be selected " +
|
|
417
|
-
"when other answers are selected.",
|
|
418
|
-
),
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const correct = userInput.choicesSelected.every((selected, i) => {
|
|
423
|
-
let isCorrect;
|
|
424
|
-
if (userInput.noneOfTheAboveIndex === i) {
|
|
425
|
-
isCorrect = rubric.choices.every((choice, j) => {
|
|
426
|
-
return i === j || !choice.correct;
|
|
427
|
-
});
|
|
428
|
-
} else {
|
|
429
|
-
isCorrect = !!rubric.choices[i].correct;
|
|
430
|
-
}
|
|
431
|
-
return isCorrect === selected;
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
return {
|
|
435
|
-
type: "points",
|
|
436
|
-
earned: correct ? 1 : 0,
|
|
437
|
-
total: 1,
|
|
438
|
-
message: null,
|
|
439
|
-
};
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
static getUserInputFromProps(props: Props): UserInput {
|
|
443
|
-
// Return checked inputs in the form {choicesSelected: [bool]}. (Dear
|
|
444
|
-
// future timeline implementers: this used to be {value: i} before
|
|
445
|
-
// multiple select was added)
|
|
446
|
-
if (props.choiceStates) {
|
|
447
|
-
let noneOfTheAboveIndex = null;
|
|
448
|
-
let noneOfTheAboveSelected = false;
|
|
449
|
-
|
|
450
|
-
const choiceStates = props.choiceStates;
|
|
451
|
-
const choicesSelected = choiceStates.map(() => false);
|
|
452
|
-
const countChoices = props.countChoices;
|
|
453
|
-
const numCorrect = props.numCorrect;
|
|
454
|
-
|
|
455
|
-
for (let i = 0; i < choicesSelected.length; i++) {
|
|
456
|
-
const index = props.choices[i].originalIndex;
|
|
457
|
-
|
|
458
|
-
choicesSelected[index] = choiceStates[i].selected;
|
|
459
|
-
|
|
460
|
-
if (props.choices[i].isNoneOfTheAbove) {
|
|
461
|
-
noneOfTheAboveIndex = index;
|
|
462
|
-
|
|
463
|
-
if (choicesSelected[i]) {
|
|
464
|
-
noneOfTheAboveSelected = true;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
return {
|
|
470
|
-
countChoices,
|
|
471
|
-
choicesSelected,
|
|
472
|
-
numCorrect,
|
|
473
|
-
noneOfTheAboveIndex,
|
|
474
|
-
noneOfTheAboveSelected,
|
|
475
|
-
};
|
|
476
|
-
// Support legacy choiceState implementation
|
|
477
|
-
}
|
|
478
|
-
/* istanbul ignore if - props.values is deprecated */
|
|
479
|
-
const {values} = props;
|
|
480
|
-
if (values) {
|
|
481
|
-
let noneOfTheAboveIndex = null;
|
|
482
|
-
let noneOfTheAboveSelected = false;
|
|
483
|
-
|
|
484
|
-
const choicesSelected = [...values];
|
|
485
|
-
const countChoices = props.countChoices;
|
|
486
|
-
const numCorrect = props.numCorrect;
|
|
487
|
-
const valuesLength = values.length;
|
|
488
|
-
|
|
489
|
-
for (let i = 0; i < valuesLength; i++) {
|
|
490
|
-
const index = props.choices[i].originalIndex;
|
|
491
|
-
choicesSelected[index] = values[i];
|
|
492
|
-
|
|
493
|
-
if (props.choices[i].isNoneOfTheAbove) {
|
|
494
|
-
noneOfTheAboveIndex = index;
|
|
495
|
-
if (choicesSelected[i]) {
|
|
496
|
-
noneOfTheAboveSelected = true;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
return {
|
|
501
|
-
choicesSelected,
|
|
502
|
-
noneOfTheAboveIndex,
|
|
503
|
-
noneOfTheAboveSelected,
|
|
504
|
-
countChoices,
|
|
505
|
-
numCorrect,
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
// Nothing checked
|
|
509
|
-
return {
|
|
510
|
-
choicesSelected: props.choices.map(() => false),
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
512
|
}
|
|
514
513
|
|
|
515
514
|
export default Radio;
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
# `NewTooltip`: KA tooltip component
|
|
2
|
-
|
|
3
|
-
This is a wrapper around [Wonderblock's popover component](https://github.com/Khan/wonder-blocks). It is API compatiable with Perseus and remains for compatiability and legacy reasons. If you are anyone but the Perseus group at Khan Academy please use the wonderblocks component directly.
|