@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.
Files changed (34) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/es/index.css +4 -5
  3. package/dist/es/index.js +1282 -1429
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.css +4 -5
  6. package/dist/index.js +1283 -1429
  7. package/dist/index.js.map +1 -1
  8. package/package.json +7 -7
  9. package/src/__tests__/__snapshots__/renderer_test.jsx.snap +21 -3
  10. package/src/components/highlighting/ui/highlight-tooltip.jsx +29 -23
  11. package/src/components/highlighting/ui/selection-tracker.jsx +17 -28
  12. package/src/styles/articles.less +0 -2
  13. package/src/styles/perseus-renderer.less +1 -1
  14. package/src/styles/widgets/passage.less +7 -3
  15. package/src/widgets/__stories__/passage-ref.stories.jsx +6 -2
  16. package/src/widgets/__stories__/passage.stories.jsx +9 -1
  17. package/src/widgets/__stories__/radio.stories.jsx +11 -0
  18. package/src/widgets/__testdata__/passage-ref_testdata.js +62 -0
  19. package/src/widgets/__testdata__/passage_testdata.js +25 -0
  20. package/src/widgets/__testdata__/radio_testdata.js +52 -0
  21. package/src/widgets/__tests__/__snapshots__/dropdown_test.jsx.snap +16 -2
  22. package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +18 -18
  23. package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +36 -36
  24. package/src/widgets/__tests__/passage_test.jsx +6 -8
  25. package/src/widgets/__tests__/radio_test.jsx +3 -1
  26. package/src/widgets/passage.jsx +12 -41
  27. package/src/widgets/radio/widget.jsx +128 -129
  28. package/src/components/new-tooltip/README.md +0 -3
  29. package/src/components/new-tooltip/__stories__/new-tooltip.stories.jsx +0 -665
  30. package/src/components/new-tooltip/__tests__/style-constants_test.js +0 -8
  31. package/src/components/new-tooltip/new-tooltip.jsx +0 -155
  32. package/src/components/new-tooltip/style-constants.js +0 -13
  33. package/src/components/new-tooltip/types.js +0 -183
  34. package/src/widgets/passage/get-line-height-for-node.js +0 -9
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Core Perseus API (includes renderers and widgets)",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "1.1.2",
6
+ "version": "1.3.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -35,16 +35,16 @@
35
35
  "@khanacademy/wonder-blocks-color": "^1.1.20",
36
36
  "@khanacademy/wonder-blocks-core": "^4.3.0",
37
37
  "@khanacademy/wonder-blocks-data": "^7.0.0",
38
- "@khanacademy/wonder-blocks-dropdown": "^2.6.7",
38
+ "@khanacademy/wonder-blocks-dropdown": "^2.10.1",
39
39
  "@khanacademy/wonder-blocks-form": "^2.4.5",
40
40
  "@khanacademy/wonder-blocks-i18n": "^1.2.3",
41
41
  "@khanacademy/wonder-blocks-layout": "^1.4.9",
42
42
  "@khanacademy/wonder-blocks-link": "^3.8.5",
43
43
  "@khanacademy/wonder-blocks-modal": "^2.3.1",
44
- "@khanacademy/wonder-blocks-popover": "^1.1.10",
44
+ "@khanacademy/wonder-blocks-popover": "^1.1.24",
45
45
  "@khanacademy/wonder-blocks-progress-spinner": "^1.1.31",
46
46
  "@khanacademy/wonder-blocks-spacing": "^3.0.5",
47
- "@khanacademy/wonder-blocks-tooltip": "^1.3.10",
47
+ "@khanacademy/wonder-blocks-tooltip": "^1.4.0",
48
48
  "@khanacademy/wonder-blocks-typography": "^1.1.31",
49
49
  "@khanacademy/wonder-stuff-core": "^0.1.2",
50
50
  "aphrodite": "^1.2.5",
@@ -68,16 +68,16 @@
68
68
  "@khanacademy/wonder-blocks-color": "^1.1.20",
69
69
  "@khanacademy/wonder-blocks-core": "^4.3.0",
70
70
  "@khanacademy/wonder-blocks-data": "^7.0.0",
71
- "@khanacademy/wonder-blocks-dropdown": "^2.6.7",
71
+ "@khanacademy/wonder-blocks-dropdown": "^2.10.1",
72
72
  "@khanacademy/wonder-blocks-form": "^2.4.5",
73
73
  "@khanacademy/wonder-blocks-i18n": "^1.2.3",
74
74
  "@khanacademy/wonder-blocks-layout": "^1.4.9",
75
75
  "@khanacademy/wonder-blocks-link": "^3.8.5",
76
76
  "@khanacademy/wonder-blocks-modal": "^2.3.1",
77
- "@khanacademy/wonder-blocks-popover": "^1.1.10",
77
+ "@khanacademy/wonder-blocks-popover": "^1.1.24",
78
78
  "@khanacademy/wonder-blocks-progress-spinner": "^1.1.31",
79
79
  "@khanacademy/wonder-blocks-spacing": "^3.0.5",
80
- "@khanacademy/wonder-blocks-tooltip": "^1.3.10",
80
+ "@khanacademy/wonder-blocks-tooltip": "^1.4.0",
81
81
  "@khanacademy/wonder-blocks-typography": "^1.1.31",
82
82
  "@khanacademy/wonder-stuff-core": "^0.1.2",
83
83
  "aphrodite": "^1.2.5",
@@ -385,11 +385,17 @@ exports[`renderer snapshots correct answer: correct answer 1`] = `
385
385
  <div
386
386
  class="default_xu2jcg-o_O-menuWrapper_wvrnr4"
387
387
  >
388
+ <span
389
+ aria-atomic="true"
390
+ aria-live="polite"
391
+ aria-relevant="additions text"
392
+ class="srOnly_19bpjuy"
393
+ data-test-id="dropdown-live-region"
394
+ />
388
395
  <button
389
396
  aria-expanded="false"
390
397
  aria-haspopup="listbox"
391
398
  class="button_vr44p2-o_O-shared_u51dsh-o_O-default_kjzwx1-o_O-focus_1qpi544"
392
- tabindex="0"
393
399
  type="button"
394
400
  >
395
401
  <span
@@ -456,11 +462,17 @@ exports[`renderer snapshots incorrect answer: incorrect answer 1`] = `
456
462
  <div
457
463
  class="default_xu2jcg-o_O-menuWrapper_wvrnr4"
458
464
  >
465
+ <span
466
+ aria-atomic="true"
467
+ aria-live="polite"
468
+ aria-relevant="additions text"
469
+ class="srOnly_19bpjuy"
470
+ data-test-id="dropdown-live-region"
471
+ />
459
472
  <button
460
473
  aria-expanded="false"
461
474
  aria-haspopup="listbox"
462
475
  class="button_vr44p2-o_O-shared_u51dsh-o_O-default_kjzwx1-o_O-focus_1qpi544"
463
- tabindex="0"
464
476
  type="button"
465
477
  >
466
478
  <span
@@ -527,11 +539,17 @@ exports[`renderer snapshots initial render: initial render 1`] = `
527
539
  <div
528
540
  class="default_xu2jcg-o_O-menuWrapper_wvrnr4"
529
541
  >
542
+ <span
543
+ aria-atomic="true"
544
+ aria-live="polite"
545
+ aria-relevant="additions text"
546
+ class="srOnly_19bpjuy"
547
+ data-test-id="dropdown-live-region"
548
+ />
530
549
  <button
531
550
  aria-expanded="false"
532
551
  aria-haspopup="listbox"
533
552
  class="button_vr44p2-o_O-shared_u51dsh-o_O-default_kjzwx1"
534
- tabindex="0"
535
553
  type="button"
536
554
  >
537
555
  <span
@@ -13,12 +13,12 @@
13
13
  * still have to notify the tooltip to remeasure itself when the highlight
14
14
  * focus _changes_.
15
15
  */
16
+ import Spacing from "@khanacademy/wonder-blocks-spacing";
17
+ import Tooltip from "@khanacademy/wonder-blocks-tooltip";
16
18
  import {StyleSheet, css} from "aphrodite";
17
19
  import * as React from "react";
18
20
 
19
21
  import {Log} from "../../../logging/log.js";
20
- import {colors} from "../../../styles/global-styles.js";
21
- import NewTooltip from "../../new-tooltip/new-tooltip.jsx";
22
22
 
23
23
  import {getRelativeRect} from "./util.js";
24
24
 
@@ -36,8 +36,6 @@ type HighlightTooltipProps = {|
36
36
  |};
37
37
 
38
38
  class HighlightTooltip extends React.PureComponent<HighlightTooltipProps> {
39
- _tooltip: ?React.ElementRef<typeof NewTooltip>;
40
-
41
39
  _getFocusRect(): ?Rect {
42
40
  const {focusNode, focusOffset, offsetParent} = this.props;
43
41
 
@@ -94,31 +92,36 @@ class HighlightTooltip extends React.PureComponent<HighlightTooltipProps> {
94
92
  return null;
95
93
  }
96
94
 
97
- const content = (
98
- <div className={css(styles.tooltipLabel)}>{this.props.label}</div>
95
+ // using div instead of TooltipContent because
96
+ // TooltipContent wouldn't let me overwrite
97
+ // user-select and onClick
98
+ const content: $FlowFixMe = (
99
+ <div
100
+ className={css(styles.tooltipLabel)}
101
+ onClick={this.props.onClick}
102
+ >
103
+ {this.props.label}
104
+ </div>
99
105
  );
100
106
 
107
+ const style = {
108
+ position: "absolute",
109
+ left: focusRect.left,
110
+ // offset the spacing WB provides around tooltips
111
+ top: `calc(${Math.round(focusRect.top)}px + 0.95em)`,
112
+ height: 0,
113
+ };
114
+
101
115
  return (
102
- <NewTooltip
103
- content={content}
104
- color={colors.kaBlue}
105
- inverted={true}
106
- onClick={this.props.onClick}
116
+ <div
117
+ style={style}
107
118
  onMouseEnter={this.props.onMouseEnter}
108
119
  onMouseLeave={this.props.onMouseLeave}
109
- toggleOnHover={false}
110
- showOnMount={true}
111
- ref={(e) => (this._tooltip = e)}
112
120
  >
113
- <div
114
- style={{
115
- position: "absolute",
116
- left: focusRect.left,
117
- top: focusRect.top,
118
- height: focusRect.height,
119
- }}
120
- />
121
- </NewTooltip>
121
+ <Tooltip content={content} opened={true}>
122
+ <div />
123
+ </Tooltip>
124
+ </div>
122
125
  );
123
126
  }
124
127
  }
@@ -131,6 +134,9 @@ const styles = StyleSheet.create({
131
134
  // we lose our reference to the _actual_ text they want to highlight,
132
135
  // and the "Add highlight" action fails.
133
136
  userSelect: "none",
137
+ fontFamily: `"Lato", sans-serif`,
138
+ padding: `10px ${Spacing.medium_16}px`,
139
+ cursor: "pointer",
134
140
  },
135
141
  });
136
142
 
@@ -24,7 +24,7 @@ export type TrackedSelection = {|
24
24
  proposedHighlight: DOMHighlight,
25
25
  |};
26
26
 
27
- type SelectionTrackerProps = {
27
+ type SelectionTrackerProps = {|
28
28
  // A function that builds a DOMHighlight from the given DOMRange, if
29
29
  // possible. If it would not currently be valid to add a highlight over the
30
30
  // given DOMRange, returns null.
@@ -38,17 +38,15 @@ type SelectionTrackerProps = {
38
38
  ) => React.Element<any>,
39
39
  // If false, will not track selections.
40
40
  enabled: boolean,
41
- ...
42
- };
41
+ |};
43
42
 
44
- type SelectionTrackerState = {
43
+ type SelectionTrackerState = {|
45
44
  // The current state of the mouse button. We distinguish between down,
46
45
  // down and the selection has changed since going down, and up.
47
46
  mouseState: "down" | "down-and-selecting" | "up",
48
47
  // The current TrackedSelection, if any.
49
48
  trackedSelection: ?TrackedSelection,
50
- ...
51
- };
49
+ |};
52
50
 
53
51
  class SelectionTracker extends React.PureComponent<
54
52
  SelectionTrackerProps,
@@ -63,15 +61,15 @@ class SelectionTracker extends React.PureComponent<
63
61
  this._updateListeners(false, this.props.enabled);
64
62
  }
65
63
 
66
- UNSAFE_componentWillReceiveProps(nextProps: SelectionTrackerProps) {
67
- if (this.props.buildHighlight !== nextProps.buildHighlight) {
64
+ componentDidUpdate(prevProps: SelectionTrackerProps) {
65
+ if (this.props.buildHighlight !== prevProps.buildHighlight) {
68
66
  // The highlight-building function changed, so the
69
67
  // proposedHighlight we built with it might be different, or no
70
68
  // longer be valid. Update accordingly.
71
- this._updateTrackedSelection(nextProps.buildHighlight);
69
+ this._updateTrackedSelection();
72
70
  }
73
71
 
74
- this._updateListeners(this.props.enabled, nextProps.enabled);
72
+ this._updateListeners(prevProps.enabled, this.props.enabled);
75
73
  }
76
74
 
77
75
  componentWillUnmount() {
@@ -133,37 +131,28 @@ class SelectionTracker extends React.PureComponent<
133
131
  }
134
132
 
135
133
  /**
136
- * Compute the current TrackedSelection from the document state.
134
+ * Compute and update the TrackedSelection to reflect the document state.
137
135
  */
138
- _computeTrackedSelection(
139
- buildHighlight: (domRange: DOMRange) => ?DOMHighlight,
140
- ): ?TrackedSelection {
136
+ _updateTrackedSelection(): void {
141
137
  const focusAndRange = this._computeFocusAndRange();
142
138
  if (!focusAndRange) {
143
- return null;
139
+ this.setState({trackedSelection: null});
140
+ return;
144
141
  }
145
142
 
146
143
  const {focusNode, focusOffset, range} = focusAndRange;
147
- const proposedHighlight = buildHighlight(range);
144
+ const proposedHighlight = this.props.buildHighlight(range);
148
145
  if (!proposedHighlight) {
149
- return null;
146
+ this.setState({trackedSelection: null});
147
+ return;
150
148
  }
151
149
 
152
- return {focusNode, focusOffset, proposedHighlight};
153
- }
154
-
155
- /**
156
- * Update the TrackedSelection to reflect the document state.
157
- */
158
- _updateTrackedSelection(
159
- buildHighlight: (domRange: DOMRange) => ?DOMHighlight,
160
- ) {
161
- const trackedSelection = this._computeTrackedSelection(buildHighlight);
150
+ const trackedSelection = {focusNode, focusOffset, proposedHighlight};
162
151
  this.setState({trackedSelection});
163
152
  }
164
153
 
165
154
  _handleSelectionChange: () => void = () => {
166
- this._updateTrackedSelection(this.props.buildHighlight);
155
+ this._updateTrackedSelection();
167
156
 
168
157
  if (this.state.mouseState === "down") {
169
158
  this.setState({
@@ -178,8 +178,6 @@
178
178
  position: relative;
179
179
 
180
180
  &:before {
181
- background: linear-gradient(to right,
182
- rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%);
183
181
  bottom: 0;
184
182
  content: "";
185
183
  position: absolute;
@@ -107,7 +107,7 @@
107
107
  }
108
108
 
109
109
  div.paragraph {
110
- font-family: "Noto Serif", serif;
110
+ font-family: "Lato", sans-serif;
111
111
  font-weight: 400;
112
112
  font-size: 18px;
113
113
  line-height: 22px;
@@ -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: 20px;
52
- line-height: 24px;
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 Question1 = (args: StoryArgs): React.Node => {
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 {question1, question2} from "../__testdata__/passage_testdata.js";
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>,
@@ -23,11 +23,19 @@ exports[`Dropdown widget should snapshot when opened: dropdown open 1`] = `
23
23
  <div
24
24
  class="default_xu2jcg-o_O-menuWrapper_wvrnr4"
25
25
  >
26
+ <span
27
+ aria-atomic="true"
28
+ aria-live="polite"
29
+ aria-relevant="additions text"
30
+ class="srOnly_19bpjuy"
31
+ data-test-id="dropdown-live-region"
32
+ >
33
+ 3 items
34
+ </span>
26
35
  <button
27
36
  aria-expanded="true"
28
37
  aria-haspopup="listbox"
29
38
  class="button_vr44p2-o_O-shared_u51dsh-o_O-default_kjzwx1-o_O-focus_1qpi544"
30
- tabindex="0"
31
39
  type="button"
32
40
  >
33
41
  <span
@@ -94,11 +102,17 @@ exports[`Dropdown widget should snapshot: initial render 1`] = `
94
102
  <div
95
103
  class="default_xu2jcg-o_O-menuWrapper_wvrnr4"
96
104
  >
105
+ <span
106
+ aria-atomic="true"
107
+ aria-live="polite"
108
+ aria-relevant="additions text"
109
+ class="srOnly_19bpjuy"
110
+ data-test-id="dropdown-live-region"
111
+ />
97
112
  <button
98
113
  aria-expanded="false"
99
114
  aria-haspopup="listbox"
100
115
  class="button_vr44p2-o_O-shared_u51dsh-o_O-default_kjzwx1"
101
- tabindex="0"
102
116
  type="button"
103
117
  >
104
118
  <span
@@ -55,13 +55,13 @@ exports[`passage-ref widget should render with invalid reference 1`] = `
55
55
  <div>
56
56
  <div>
57
57
  <div
58
- class="measurer_vtwrnx"
58
+ aria-hidden="true"
59
+ class="measurer_225hir"
59
60
  >
60
- <div>
61
- <div
62
- class="paragraph"
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
- class="measurer_vtwrnx"
203
+ aria-hidden="true"
204
+ class="measurer_225hir"
204
205
  >
205
- <div>
206
- <div
207
- class="paragraph"
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
- class="measurer_vtwrnx"
353
+ aria-hidden="true"
354
+ class="measurer_225hir"
354
355
  >
355
- <div>
356
- <div
357
- class="paragraph"
358
- />
359
- <div />
356
+ <div
357
+ class="paragraph"
358
+ >
359
+ Line Height Measurement
360
360
  </div>
361
361
  </div>
362
362
  <div