@khanacademy/perseus 12.2.0 → 12.4.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 (40) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/article-renderer.d.ts +3 -2
  3. package/dist/error-boundary.d.ts +1 -0
  4. package/dist/es/index.css +4 -69
  5. package/dist/es/index.js +135 -78
  6. package/dist/es/index.js.map +1 -1
  7. package/dist/index.css +4 -69
  8. package/dist/index.js +130 -67
  9. package/dist/index.js.map +1 -1
  10. package/dist/renderer.d.ts +4 -1
  11. package/dist/styles/constants.d.ts +2 -2
  12. package/dist/widget-container.d.ts +2 -0
  13. package/dist/widgets/expression.d.ts +5 -4
  14. package/package.json +8 -8
  15. package/src/__tests__/renderer-api.test.tsx +18 -23
  16. package/src/__tests__/widget-container.test.tsx +126 -0
  17. package/src/article-renderer.tsx +15 -13
  18. package/src/error-boundary.tsx +4 -0
  19. package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +5 -5
  20. package/src/renderer.tsx +29 -27
  21. package/src/server-item-renderer.tsx +1 -0
  22. package/src/styles/articles.less +3 -1
  23. package/src/styles/constants.ts +4 -4
  24. package/src/styles/perseus-renderer.less +1 -11
  25. package/src/styles/variables.less +2 -2
  26. package/src/styles/variables.shared.less +2 -2
  27. package/src/styles/widgets/expression.less +0 -77
  28. package/src/styles/widgets/passage.less +1 -19
  29. package/src/widget-container.tsx +33 -10
  30. package/src/widgets/__stories__/label-image.stories.tsx +30 -0
  31. package/src/widgets/__testdata__/label-image.testdata.ts +63 -0
  32. package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +50 -19
  33. package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +5 -5
  34. package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +28 -28
  35. package/src/widgets/__tests__/expression.test.tsx +12 -9
  36. package/src/widgets/__tests__/renderQuestion.tsx +30 -13
  37. package/src/widgets/expression.tsx +19 -25
  38. package/src/widgets/graded-group-set.tsx +70 -25
  39. package/src/widgets/transformer.tsx +1 -2
  40. package/tsconfig-build.tsbuildinfo +1 -1
@@ -1,6 +1,6 @@
1
1
  // Steal from webapp/stylesheets/shared-package/variables.less
2
- @baseFontFamily: "Proxima Nova", sans-serif;
3
- @boldFontFamily: "Proxima Nova Semibold", sans-serif;
2
+ @baseFontFamily: "Lato", sans-serif;
3
+ @boldFontFamily: "Lato-Bold", "Lato", sans-serif;
4
4
  @green: #76a005;
5
5
  @kaGreen: #71b307;
6
6
  @blue: #1c758a;
@@ -86,8 +86,8 @@
86
86
 
87
87
  // Typography
88
88
  @baseFontSize: 14px;
89
- // The body of the page is setup to use 'Helvetica' by default and 'Proxima
90
- // Nova' (or 'Lato' if you're testing our new typography) if/once it downloads.
89
+ // The body of the page is setup to use 'Helvetica' by default and
90
+ // 'Lato' if/once it downloads.
91
91
  // For your elements to follow this pattern, all you need is to make the text
92
92
  // in your element inherit from the body using the "inherit" value.
93
93
  @baseFontFamily: inherit;
@@ -63,80 +63,3 @@
63
63
  }
64
64
  }
65
65
  }
66
-
67
- // Old expression widget
68
- .perseus-widget-expression-old {
69
- &,
70
- > .output,
71
- > .output > .tex,
72
- > .output > .placeholder {
73
- display: block;
74
- }
75
- }
76
-
77
- .perseus-widget-expression-old input,
78
- #answer_area .perseus-widget-expression-old input {
79
- direction: ltr;
80
- border: 1px solid #a4a4a4;
81
- border-radius: 5px;
82
- box-sizing: border-box;
83
- font-size: 14px;
84
- margin-bottom: 5px;
85
- max-width: 240px;
86
- padding: 6px;
87
- width: 100%;
88
- }
89
-
90
- .perseus-widget-expression-old > .output {
91
- background: #f2f2f2;
92
- border-radius: 5px;
93
- padding: 10px;
94
- margin: 10px 0;
95
- }
96
-
97
- #answer_area .perseus-widget-expression-old > .output {
98
- background: #f7f7f7;
99
- }
100
-
101
- .perseus-widget-expression-old > .output > .tex {
102
- overflow-x: auto;
103
- padding: 5px;
104
- }
105
-
106
- .perseus-widget-expression-old > .output > .placeholder {
107
- position: relative;
108
- height: 40px;
109
- overflow-y: hidden;
110
- }
111
-
112
- .perseus-widget-expression-old > .output > .placeholder > .error {
113
- border-radius: 10px;
114
- background: #f7f7f7;
115
- border: 1px solid #ddd;
116
- box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5);
117
- color: #000;
118
- display: none;
119
- font-weight: normal;
120
- min-height: 22px;
121
- width: 168px;
122
- position: absolute;
123
- top: 0px;
124
- left: 40px;
125
- margin: auto;
126
- }
127
-
128
- .perseus-widget-expression-old > .output > .placeholder > .error > .buddy {
129
- background-image: url(/images/perseus/error-buddy.png);
130
- background-size: 100%;
131
- height: 36px;
132
- width: 40px;
133
- position: absolute;
134
- top: -3px;
135
- left: -42px;
136
- }
137
-
138
- .perseus-widget-expression-old > .output > .placeholder > .error > .message {
139
- font: 12px @baseFontFamily;
140
- line-height: 1.4em;
141
- margin: 0px 4px;
142
- }
@@ -17,26 +17,8 @@
17
17
  margin: 22px;
18
18
  position: relative;
19
19
 
20
- // Use the "Minion Pro" font, if available. If not, fall back to
21
- // KaTeX_Main or another serif font.
22
- //
23
- // NOTE(mdr): Minion Pro is only loaded on SAT and GTP pages, because
24
- // those are the main call sites for the passage widget. We
25
- // considered inserting the relevant stylesheet whenever the
26
- // passage widget loads, but this approach produced a "flash of
27
- // unstyled text", so we decided to embed the stylesheet into the
28
- // relevant page templates instead.
29
- //
30
- // NOTE(mdr): Minion Pro will not be available on ZNDs, due to
31
- // restrictions in our Adobe TypeKit configuration.
32
- //
33
- // TODO(mdr): The BodySerif component will hopefully soon use Minion
34
- // Pro by default (WB-399). Once that happens, we should be able to
35
- // switch to the BodySerif component (TP-3112), and remove this
36
- // special font-family rule.
37
20
  div.paragraph {
38
- font-family: "minion-pro", KaTeX_Main, Times, "Times New Roman",
39
- serif;
21
+ font-family: KaTeX_Main, Times, "Times New Roman", serif;
40
22
  }
41
23
 
42
24
  .passage-title div.paragraph {
@@ -4,6 +4,8 @@ import classNames from "classnames";
4
4
  import * as React from "react";
5
5
  import ReactDOM from "react-dom";
6
6
 
7
+ import {DependenciesContext} from "./dependencies";
8
+ import ErrorBoundary from "./error-boundary";
7
9
  import {zIndexInteractiveComponent} from "./styles/constants";
8
10
  import {containerSizeClass, getClassFromWidth} from "./util/sizing-utils";
9
11
  import * as Widgets from "./widgets";
@@ -15,6 +17,7 @@ import type {LinterContextProps} from "@khanacademy/perseus-linter";
15
17
  type Props = {
16
18
  shouldHighlight: boolean;
17
19
  type: string; // widget type/name,
20
+ id: string; // widget id
18
21
  initialProps: WidgetProps<any, PerseusWidgetOptions>;
19
22
  linterContext: LinterContextProps;
20
23
  };
@@ -29,6 +32,8 @@ type State = {
29
32
  };
30
33
 
31
34
  class WidgetContainer extends React.Component<Props, State> {
35
+ widgetRef = React.createRef<React.ComponentType<any>>();
36
+
32
37
  static defaultProps: DefaultProps = {
33
38
  linterContext: linterContextDefault,
34
39
  };
@@ -132,14 +137,33 @@ class WidgetContainer extends React.Component<Props, State> {
132
137
  className={className}
133
138
  style={isStatic ? staticContainerStyles : {}}
134
139
  >
135
- <WidgetType
136
- {...this.state.widgetProps}
137
- linterContext={linterContext}
138
- containerSizeClass={this.state.sizeClass}
139
- // eslint-disable-next-line react/no-string-refs
140
- ref="widget"
141
- />
142
- {isStatic && <div style={staticOverlayStyles} />}
140
+ <DependenciesContext.Consumer>
141
+ {({analytics}) => (
142
+ <ErrorBoundary
143
+ metadata={{
144
+ widget_type: type,
145
+ widget_id: this.props.id,
146
+ }}
147
+ onError={() => {
148
+ analytics.onAnalyticsEvent({
149
+ type: "perseus:widget-rendering-error",
150
+ payload: {
151
+ widgetType: type,
152
+ widgetId: this.props.id,
153
+ },
154
+ });
155
+ }}
156
+ >
157
+ <WidgetType
158
+ {...this.state.widgetProps}
159
+ linterContext={linterContext}
160
+ containerSizeClass={this.state.sizeClass}
161
+ ref={this.widgetRef}
162
+ />
163
+ {isStatic && <div style={staticOverlayStyles} />}
164
+ </ErrorBoundary>
165
+ )}
166
+ </DependenciesContext.Consumer>
143
167
  </div>
144
168
  );
145
169
  }
@@ -163,8 +187,7 @@ class WidgetContainer extends React.Component<Props, State> {
163
187
  }
164
188
 
165
189
  getWidget: () => any = () => {
166
- // eslint-disable-next-line react/no-string-refs
167
- return this.refs.widget;
190
+ return this.widgetRef.current;
168
191
  };
169
192
 
170
193
  replaceWidgetProps: (arg1: WidgetProps<any, PerseusWidgetOptions>) => void =
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+
3
+ import {RendererWithDebugUI} from "../../../../../testing/renderer-with-debug-ui";
4
+ import {question} from "../__testdata__/label-image.testdata";
5
+
6
+ import type {APIOptions} from "../../types";
7
+
8
+ type StoryArgs = {
9
+ isMobile: boolean;
10
+ };
11
+
12
+ type ImageStory = {
13
+ title: string;
14
+ args: StoryArgs;
15
+ };
16
+
17
+ export const Question1 = (args: StoryArgs): React.ReactElement => {
18
+ const apiOptions: APIOptions = {
19
+ isMobile: args.isMobile,
20
+ };
21
+
22
+ return <RendererWithDebugUI question={question} apiOptions={apiOptions} />;
23
+ };
24
+
25
+ export default {
26
+ title: "Perseus/Widgets/Label Image",
27
+ args: {
28
+ isMobile: false,
29
+ },
30
+ } as ImageStory;
@@ -0,0 +1,63 @@
1
+ import type {PerseusRenderer} from "../../perseus-types";
2
+
3
+ export const question: PerseusRenderer = {
4
+ content:
5
+ "Carol created a chart and a bar graph to show how many of each type of vehicle were in her supermarket parking lot.\n\nVehicle Type | Number in the parking lot\n:- | :-: \nTrucks| $25$ \nVans | $5$ \nCars| $40$ \nSUVs | $10$ \n\n**Label each bar on the bar graph.**\n\n[[☃ label-image 1]]\n\n",
6
+ images: {
7
+ "web+graphie://ka-perseus-graphie.s3.amazonaws.com/1e28332fd2321975639ab50c9ce442e568c18421":
8
+ {
9
+ width: 341,
10
+ height: 310,
11
+ },
12
+ },
13
+ widgets: {
14
+ "label-image 1": {
15
+ type: "label-image",
16
+ alignment: "default",
17
+ static: false,
18
+ graded: true,
19
+ options: {
20
+ static: false,
21
+ choices: ["Trucks", "Vans", "Cars", "SUVs"],
22
+ imageAlt:
23
+ "A bar graph with four bar lines that shows the horizontal axis labeled Number in the parking lot and the vertical axis labeled Vehicle Type. The horizontal axis is labeled, from left to right: 0, 10, 20, 30, 40, and 50. The vertical axis has, from the bottom to the top, four unlabeled bar lines as follows: the first unlabeled bar line extends to the middle of 0 and 10, the second unlabeled bar line extends to 40, the third unlabeled bar line extends to the middle of 20 and 30, and fourth unlabeled bar line extends to 10.",
24
+ imageUrl:
25
+ "web+graphie://ka-perseus-graphie.s3.amazonaws.com/56c60c72e96cd353e4a8b5434506cd3a21e717af",
26
+ imageWidth: 415,
27
+ imageHeight: 314,
28
+ markers: [
29
+ {
30
+ answers: ["SUVs"],
31
+ label: "The fourth unlabeled bar line.",
32
+ x: 25,
33
+ y: 17.7,
34
+ },
35
+ {
36
+ answers: ["Trucks"],
37
+ label: "The third unlabeled bar line.",
38
+ x: 25,
39
+ y: 35.3,
40
+ },
41
+ {
42
+ answers: ["Cars"],
43
+ label: "The second unlabeled bar line.",
44
+ x: 25,
45
+ y: 53,
46
+ },
47
+ {
48
+ answers: ["Vans"],
49
+ label: "The first unlabeled bar line.",
50
+ x: 25,
51
+ y: 70.3,
52
+ },
53
+ ],
54
+ multipleAnswers: false,
55
+ hideChoicesFromInstructions: true,
56
+ },
57
+ version: {
58
+ major: 0,
59
+ minor: 0,
60
+ },
61
+ },
62
+ },
63
+ };
@@ -38,32 +38,63 @@ exports[`graded group widget should snapshot 1`] = `
38
38
  class="spacer_bc4egv"
39
39
  />
40
40
  <ul
41
- class="indicatorContainer_rjg7c2"
41
+ class="indicatorContainer_2pd7o4 indicatorContainer"
42
42
  >
43
43
  <li
44
- aria-label="Skip to Problem 1a"
45
- class="indicator_qk5jgn-o_O-selectedIndicator_14jsc6v"
46
- role="button"
47
- tabindex="0"
44
+ class="indicator_7k5kza"
48
45
  >
49
- <span
50
- class="srOnly_19bpjuy"
46
+ <button
47
+ aria-disabled="false"
48
+ aria-label="Skip to Problem 1a"
49
+ class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-indicatorButton_8uaklu"
50
+ role="button"
51
+ type="button"
51
52
  >
52
- Current
53
- </span>
53
+ <div
54
+ class="default_xu2jcg-o_O-indicatorDot_yy8ygm"
55
+ >
56
+ <div
57
+ class="default_xu2jcg-o_O-indicatorDotActive_1pky1a"
58
+ >
59
+ <span
60
+ class="srOnly_19bpjuy"
61
+ >
62
+ Current
63
+ </span>
64
+ </div>
65
+ </div>
66
+ </button>
54
67
  </li>
55
68
  <li
56
- aria-label="Skip to Problem 1b"
57
- class="indicator_qk5jgn"
58
- role="button"
59
- tabindex="0"
60
- />
69
+ class="indicator_7k5kza"
70
+ >
71
+ <button
72
+ aria-disabled="false"
73
+ aria-label="Skip to Problem 1b"
74
+ class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-indicatorButton_8uaklu"
75
+ role="button"
76
+ type="button"
77
+ >
78
+ <div
79
+ class="default_xu2jcg-o_O-indicatorDot_yy8ygm"
80
+ />
81
+ </button>
82
+ </li>
61
83
  <li
62
- aria-label="Skip to Problem 1c"
63
- class="indicator_qk5jgn"
64
- role="button"
65
- tabindex="0"
66
- />
84
+ class="indicator_7k5kza"
85
+ >
86
+ <button
87
+ aria-disabled="false"
88
+ aria-label="Skip to Problem 1c"
89
+ class="button_vr44p2-o_O-reset_152ygtm-o_O-link_13xlah4-o_O-indicatorButton_8uaklu"
90
+ role="button"
91
+ type="button"
92
+ >
93
+ <div
94
+ class="default_xu2jcg-o_O-indicatorDot_yy8ygm"
95
+ />
96
+ </button>
97
+ </li>
67
98
  </ul>
68
99
  </div>
69
100
  <div
@@ -192,7 +192,7 @@ exports[`group widget should snapshot: initial render 1`] = `
192
192
  style="border-color: transparent; border-radius: 50%;"
193
193
  >
194
194
  <div
195
- class="circle_1tys2th"
195
+ class="circle_1w6i3h1"
196
196
  data-is-radio-icon="true"
197
197
  data-test-id="choice-icon__library-choice-icon"
198
198
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
@@ -307,7 +307,7 @@ exports[`group widget should snapshot: initial render 1`] = `
307
307
  style="border-color: transparent; border-radius: 50%;"
308
308
  >
309
309
  <div
310
- class="circle_1tys2th"
310
+ class="circle_1w6i3h1"
311
311
  data-is-radio-icon="true"
312
312
  data-test-id="choice-icon__library-choice-icon"
313
313
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
@@ -422,7 +422,7 @@ exports[`group widget should snapshot: initial render 1`] = `
422
422
  style="border-color: transparent; border-radius: 50%;"
423
423
  >
424
424
  <div
425
- class="circle_1tys2th"
425
+ class="circle_1w6i3h1"
426
426
  data-is-radio-icon="true"
427
427
  data-test-id="choice-icon__library-choice-icon"
428
428
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
@@ -537,7 +537,7 @@ exports[`group widget should snapshot: initial render 1`] = `
537
537
  style="border-color: transparent; border-radius: 50%;"
538
538
  >
539
539
  <div
540
- class="circle_1tys2th"
540
+ class="circle_1w6i3h1"
541
541
  data-is-radio-icon="true"
542
542
  data-test-id="choice-icon__library-choice-icon"
543
543
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
@@ -652,7 +652,7 @@ exports[`group widget should snapshot: initial render 1`] = `
652
652
  style="border-color: transparent; border-radius: 50%;"
653
653
  >
654
654
  <div
655
- class="circle_1tys2th"
655
+ class="circle_1w6i3h1"
656
656
  data-is-radio-icon="true"
657
657
  data-test-id="choice-icon__library-choice-icon"
658
658
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"