@khanacademy/perseus 1.2.0 → 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 +11 -0
- package/dist/es/index.css +3 -3
- package/dist/es/index.js +138 -167
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +3 -3
- package/dist/index.js +136 -165
- package/dist/index.js.map +1 -1
- package/package.json +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__/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/widgets/passage/get-line-height-for-node.js +0 -9
package/package.json
CHANGED
|
@@ -45,12 +45,16 @@
|
|
|
45
45
|
|
|
46
46
|
margin: 0 0 10px;
|
|
47
47
|
}
|
|
48
|
+
|
|
49
|
+
// note(matthewc): If you need to change any of this,
|
|
50
|
+
// check that line numbers line up correctly.
|
|
51
|
+
// The margin should probably be 0 or the line height
|
|
48
52
|
.passage-text div.paragraph {
|
|
49
|
-
font-family: "Noto Serif", serif;
|
|
50
53
|
font-weight: 400;
|
|
51
|
-
font-size:
|
|
52
|
-
line-height:
|
|
54
|
+
font-size: @font-size;
|
|
55
|
+
line-height: @line-height;
|
|
53
56
|
text-indent: 20px;
|
|
57
|
+
margin: 0;
|
|
54
58
|
|
|
55
59
|
span {
|
|
56
60
|
text-indent: 0;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
|
|
4
4
|
import {RendererWithDebugUI} from "../../../../../testing/renderer-with-debug-ui.jsx";
|
|
5
|
-
import {question1} from "../__testdata__/passage-ref_testdata.js";
|
|
5
|
+
import {question1, question2} from "../__testdata__/passage-ref_testdata.js";
|
|
6
6
|
|
|
7
7
|
type StoryArgs = {||};
|
|
8
8
|
|
|
@@ -14,6 +14,10 @@ export default ({
|
|
|
14
14
|
title: "Perseus/Widgets/PassageRef",
|
|
15
15
|
}: Story);
|
|
16
16
|
|
|
17
|
-
export const
|
|
17
|
+
export const ShortPassage = (args: StoryArgs): React.Node => {
|
|
18
18
|
return <RendererWithDebugUI question={question1} />;
|
|
19
19
|
};
|
|
20
|
+
|
|
21
|
+
export const LongPassage = (args: StoryArgs): React.Node => {
|
|
22
|
+
return <RendererWithDebugUI question={question2} />;
|
|
23
|
+
};
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
|
|
4
4
|
import {RendererWithDebugUI} from "../../../../../testing/renderer-with-debug-ui.jsx";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
question1,
|
|
7
|
+
question2,
|
|
8
|
+
question3,
|
|
9
|
+
} from "../__testdata__/passage_testdata.js";
|
|
6
10
|
|
|
7
11
|
export default {
|
|
8
12
|
title: "Perseus/Widgets/Passage",
|
|
@@ -17,3 +21,7 @@ export const SimpleQuestion = (args: StoryArgs): React.Node => {
|
|
|
17
21
|
export const MultiPassageQuestion = (args: StoryArgs): React.Node => {
|
|
18
22
|
return <RendererWithDebugUI question={question2} />;
|
|
19
23
|
};
|
|
24
|
+
|
|
25
|
+
export const SingleNumberedPassage = (args: StoryArgs): React.Node => {
|
|
26
|
+
return <RendererWithDebugUI question={question3} />;
|
|
27
|
+
};
|
|
@@ -6,6 +6,7 @@ import {RendererWithDebugUI} from "../../../../../testing/renderer-with-debug-ui
|
|
|
6
6
|
import {
|
|
7
7
|
questionWithPassage,
|
|
8
8
|
multiChoiceQuestion,
|
|
9
|
+
multiChoiceQuestionSimple,
|
|
9
10
|
} from "../__testdata__/radio_testdata.js";
|
|
10
11
|
|
|
11
12
|
import type {PerseusRenderer} from "../../perseus-types.js";
|
|
@@ -71,6 +72,16 @@ export const SingleSelect = (args: StoryArgs): React.Node => {
|
|
|
71
72
|
);
|
|
72
73
|
};
|
|
73
74
|
|
|
75
|
+
export const MultiSelectSimple = (args: StoryArgs): React.Node => {
|
|
76
|
+
return (
|
|
77
|
+
<RendererWithDebugUI
|
|
78
|
+
question={applyStoryArgs(multiChoiceQuestionSimple, args)}
|
|
79
|
+
apiOptions={buildApiOptions(args)}
|
|
80
|
+
reviewMode={args.reviewMode}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
74
85
|
export const MultiSelect = (args: StoryArgs): React.Node => {
|
|
75
86
|
return (
|
|
76
87
|
<RendererWithDebugUI
|
|
@@ -39,3 +39,65 @@ export const question1: PerseusRenderer = {
|
|
|
39
39
|
},
|
|
40
40
|
},
|
|
41
41
|
};
|
|
42
|
+
|
|
43
|
+
export const question2: PerseusRenderer = {
|
|
44
|
+
content:
|
|
45
|
+
'[[☃ passage 1]]\n\n"It is!" is used in [[☃ passage-ref 1]]\n\n"unsettle" is used in [[☃ passage-ref 2]]\n\n"flagon" is used in [[☃ passage-ref 3]]\n\n',
|
|
46
|
+
images: {},
|
|
47
|
+
widgets: {
|
|
48
|
+
"passage 1": {
|
|
49
|
+
alignment: "default",
|
|
50
|
+
graded: true,
|
|
51
|
+
options: {
|
|
52
|
+
footnotes: "",
|
|
53
|
+
passageText:
|
|
54
|
+
'\n\nOne afternoon, Mrs. Bretton, coaxing [Paulina] from her usual station in a corner, had lifted her into the window-seat, and, by way of occupying her attention, told her to watch the passengers and count how many ladies should go down the street in a given time. She had sat listlessly, hardly looking, and not counting, when—my eye being fixed on hers—I witnessed in its iris and pupil a startling transfiguration. These sudden, dangerous natures—*sensitive* as they are called—offer many a curious spectacle to those whom a cooler temperament has secured from participation in their angular vagaries. The fixed and heavy gaze swum, trembled, then glittered in fire; the small, overcast brow cleared; the trivial and dejected features lit up; the sad countenance vanished, and in its place appeared a sudden eagerness, an intense expectancy. {{"It *is!*"}} were her words.\n\nLike a bird or a shaft, or any other swift thing, she was gone from the room. How she got the house-door open I cannot tell; probably it might be ajar; perhaps Warren was in the way and obeyed her behest, which would be impetuous enough. I—watching calmly from the window—saw her, in her black frock and tiny braided apron (to pinafores she had an antipathy), dart half the length of the street; and, as I was on the point of turning, and quietly announcing to Mrs. Bretton that the child was run out mad, and ought instantly to be pursued, I saw her caught up, and rapt at once from my cool observation, and from the wondering stare of the passengers. A gentleman had done this good turn, and now, covering her with his cloak, advanced to restore her to the house whence he had seen her issue.\n\nI concluded he would leave her in a servant\'s charge and withdraw; but he entered: having tarried a little while below, he came up-stairs.\n\nHis reception immediately explained that he was known to Mrs. Bretton. She recognised him; she greeted him, and yet she was fluttered, surprised, taken unawares. Her look and manner were even expostulatory; and in reply to these, rather than her words, he said,—"I could not help it, madam: I found it impossible to leave the country without seeing with my own eyes how she settled."\n\n"But you will {{unsettle}} her."\n\n"I hope not. And how is papa\'s little Polly?"\n\nThis question he addressed to Paulina, as he sat down and placed her gently on the ground before him.\n\n"How is Polly\'s papa?" was the reply, as she leaned on his knee, and gazed up into his face.\n\nIt was not a noisy, not a wordy scene: for that I was thankful; but it was a scene of feeling too brimful, and which, because the cup did not foam up high or furiously overflow, only oppressed one the more. On all occasions of vehement, unrestrained expansion, a sense of disdain or ridicule comes to the weary spectator\'s relief; whereas I have ever felt most burdensome that sort of sensibility which bends of its own will, a giant slave under the sway of good sense.\n\nMr. Home was a stern-featured—perhaps I should rather say, a hard-featured man: his forehead was knotty, and his cheekbones were marked and prominent. The character of his face was quite Scotch; but there was feeling in his eye, and emotion in his now agitated countenance. His northern accent in speaking harmonised with his physiognomy. He was at once proud-looking and homely-looking. He laid his hand on the child\'s uplifted head. She said—"Kiss Polly."\n\nHe kissed her. I wished she would utter some hysterical cry, so that I might get relief and be at ease. She made wonderfully little noise: she seemed to have got what she wanted—all she wanted, and to be in a trance of content. Neither in mien nor in features was this creature like her sire, and yet she was of his strain: her mind had been filled from his, as the cup from the {{flagon}}.\n',
|
|
55
|
+
passageTitle: "",
|
|
56
|
+
showLineNumbers: true,
|
|
57
|
+
static: false,
|
|
58
|
+
},
|
|
59
|
+
static: false,
|
|
60
|
+
type: "passage",
|
|
61
|
+
version: {
|
|
62
|
+
major: 0,
|
|
63
|
+
minor: 0,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
"passage-ref 1": {
|
|
67
|
+
options: {
|
|
68
|
+
passageNumber: 1,
|
|
69
|
+
referenceNumber: 1,
|
|
70
|
+
summaryText: "",
|
|
71
|
+
},
|
|
72
|
+
type: "passage-ref",
|
|
73
|
+
version: {
|
|
74
|
+
major: 0,
|
|
75
|
+
minor: 1,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
"passage-ref 2": {
|
|
79
|
+
options: {
|
|
80
|
+
passageNumber: 1,
|
|
81
|
+
referenceNumber: 2,
|
|
82
|
+
summaryText: "",
|
|
83
|
+
},
|
|
84
|
+
type: "passage-ref",
|
|
85
|
+
version: {
|
|
86
|
+
major: 0,
|
|
87
|
+
minor: 1,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
"passage-ref 3": {
|
|
91
|
+
options: {
|
|
92
|
+
passageNumber: 1,
|
|
93
|
+
referenceNumber: 3,
|
|
94
|
+
summaryText: "",
|
|
95
|
+
},
|
|
96
|
+
type: "passage-ref",
|
|
97
|
+
version: {
|
|
98
|
+
major: 0,
|
|
99
|
+
minor: 1,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
@@ -69,3 +69,28 @@ export const question2: PerseusRenderer = {
|
|
|
69
69
|
},
|
|
70
70
|
},
|
|
71
71
|
};
|
|
72
|
+
|
|
73
|
+
export const question3: PerseusRenderer = {
|
|
74
|
+
content: "[[☃ passage 1]]",
|
|
75
|
+
images: {},
|
|
76
|
+
widgets: {
|
|
77
|
+
"passage 1": {
|
|
78
|
+
alignment: "default",
|
|
79
|
+
graded: true,
|
|
80
|
+
options: {
|
|
81
|
+
footnotes: "An example footnote",
|
|
82
|
+
passageText:
|
|
83
|
+
"{{For reasons that are not entirely clear, when animals make their way to isolated islands [[1]], they tend to evolve relatively quickly toward an outsized or pint-sized version of their mainland counterpart.}} \n\n{{Perhaps the most famous example of an island giant—and, sadly, of species extinction—is the dodo, once found on the Indian Ocean island of Mauritius.}} {{When the dodo's ancestor (thought to be a migratory pigeon) settled on this island with abundant food, no competition from terrestrial mammals, and no predators, it could survive without flying, and thus was freed from the energetic and size constraints of flight.}} {{New Zealand also had avian giants, now extinct, including the flightless moa, an ostrich-like bird, and Haast's eagle (_Harpagornis moorei_), which had a wingspan up to 3 meters.}} {{Though Haast's eagle could fly—and presumably used its wings to {{launch}} brutal attacks on the hapless moa—its body mass (10–14 kilograms) pushed the limits for self-propelled flight.}}\n\n{{As extreme evolutionary examples, these island birds can offer insights into the forces and events shaping evolutionary change.}} In a new study, Michael Bunce et al. compared ancient mitochondrial DNA extracted from Haast's eagle bones with DNA sequences of 16 living eagle species to better characterize the evolutionary history of the extinct giant raptor. {{Their results suggest the extinct raptor underwent a rapid evolutionary transformation that belies its kinship to some of the world's smallest eagle species.}}\n\nThe authors characterized the rates of sequence evolution within mitochondrial DNA to establish the evolutionary relationships between the different eagle species. Their analysis places Haast's eagle in the same evolutionary lineage as a group of small eagle species in the genus _Hieraaetus_. Surprisingly, the genetic distance separating the giant eagle and its more diminutive _Hieraaetus_ cousins from their last {{common}} ancestor is relatively small.\n",
|
|
84
|
+
passageTitle: "Passage 1",
|
|
85
|
+
showLineNumbers: true,
|
|
86
|
+
static: false,
|
|
87
|
+
},
|
|
88
|
+
static: false,
|
|
89
|
+
type: "passage",
|
|
90
|
+
version: {
|
|
91
|
+
major: 0,
|
|
92
|
+
minor: 0,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
};
|
|
@@ -194,6 +194,58 @@ export const multiChoiceQuestion: PerseusRenderer = {
|
|
|
194
194
|
},
|
|
195
195
|
};
|
|
196
196
|
|
|
197
|
+
export const multiChoiceQuestionSimple: PerseusRenderer = {
|
|
198
|
+
content: "What are some ways to say hello?\n\n[[\u2603 radio 1]]",
|
|
199
|
+
images: {},
|
|
200
|
+
widgets: {
|
|
201
|
+
"radio 1": ({
|
|
202
|
+
graded: true,
|
|
203
|
+
version: {
|
|
204
|
+
major: 1,
|
|
205
|
+
minor: 0,
|
|
206
|
+
},
|
|
207
|
+
static: false,
|
|
208
|
+
type: "radio",
|
|
209
|
+
options: {
|
|
210
|
+
onePerLine: true,
|
|
211
|
+
displayCount: null,
|
|
212
|
+
choices: [
|
|
213
|
+
{
|
|
214
|
+
content: "Hola",
|
|
215
|
+
isNoneOfTheAbove: false,
|
|
216
|
+
correct: true,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
content: "Hey",
|
|
220
|
+
isNoneOfTheAbove: false,
|
|
221
|
+
correct: true,
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
content: "Hi",
|
|
225
|
+
isNoneOfTheAbove: false,
|
|
226
|
+
correct: true,
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
content: "Goodbye",
|
|
230
|
+
isNoneOfTheAbove: false,
|
|
231
|
+
correct: false,
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
content: "None of these",
|
|
235
|
+
isNoneOfTheAbove: true,
|
|
236
|
+
correct: false,
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
hasNoneOfTheAbove: true,
|
|
240
|
+
multipleSelect: true,
|
|
241
|
+
randomize: false,
|
|
242
|
+
deselectEnabled: false,
|
|
243
|
+
},
|
|
244
|
+
alignment: "default",
|
|
245
|
+
}: RadioWidget),
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
|
|
197
249
|
export const multiChoiceQuestionAndAnswer: [
|
|
198
250
|
PerseusRenderer,
|
|
199
251
|
$ReadOnlyArray<number>,
|
|
@@ -55,13 +55,13 @@ exports[`passage-ref widget should render with invalid reference 1`] = `
|
|
|
55
55
|
<div>
|
|
56
56
|
<div>
|
|
57
57
|
<div
|
|
58
|
-
|
|
58
|
+
aria-hidden="true"
|
|
59
|
+
class="measurer_225hir"
|
|
59
60
|
>
|
|
60
|
-
<div
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
<div />
|
|
61
|
+
<div
|
|
62
|
+
class="paragraph"
|
|
63
|
+
>
|
|
64
|
+
Line Height Measurement
|
|
65
65
|
</div>
|
|
66
66
|
</div>
|
|
67
67
|
<div
|
|
@@ -200,13 +200,13 @@ exports[`passage-ref widget should render with multi-line reference 1`] = `
|
|
|
200
200
|
<div>
|
|
201
201
|
<div>
|
|
202
202
|
<div
|
|
203
|
-
|
|
203
|
+
aria-hidden="true"
|
|
204
|
+
class="measurer_225hir"
|
|
204
205
|
>
|
|
205
|
-
<div
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
<div />
|
|
206
|
+
<div
|
|
207
|
+
class="paragraph"
|
|
208
|
+
>
|
|
209
|
+
Line Height Measurement
|
|
210
210
|
</div>
|
|
211
211
|
</div>
|
|
212
212
|
<div
|
|
@@ -350,13 +350,13 @@ exports[`passage-ref widget should render with single-line reference 1`] = `
|
|
|
350
350
|
<div>
|
|
351
351
|
<div>
|
|
352
352
|
<div
|
|
353
|
-
|
|
353
|
+
aria-hidden="true"
|
|
354
|
+
class="measurer_225hir"
|
|
354
355
|
>
|
|
355
|
-
<div
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
<div />
|
|
356
|
+
<div
|
|
357
|
+
class="paragraph"
|
|
358
|
+
>
|
|
359
|
+
Line Height Measurement
|
|
360
360
|
</div>
|
|
361
361
|
</div>
|
|
362
362
|
<div
|
|
@@ -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);
|