@khanacademy/perseus 12.2.0 → 12.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 (36) hide show
  1. package/CHANGELOG.md +23 -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 +1 -66
  5. package/dist/es/index.js +86 -66
  6. package/dist/es/index.js.map +1 -1
  7. package/dist/index.css +1 -66
  8. package/dist/index.js +77 -54
  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/article-renderer.tsx +15 -13
  17. package/src/error-boundary.tsx +3 -0
  18. package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +5 -5
  19. package/src/renderer.tsx +29 -27
  20. package/src/server-item-renderer.tsx +1 -0
  21. package/src/styles/constants.ts +4 -4
  22. package/src/styles/perseus-renderer.less +0 -10
  23. package/src/styles/variables.less +2 -2
  24. package/src/styles/variables.shared.less +2 -2
  25. package/src/styles/widgets/expression.less +0 -77
  26. package/src/styles/widgets/passage.less +1 -19
  27. package/src/widget-container.tsx +32 -10
  28. package/src/widgets/__stories__/label-image.stories.tsx +30 -0
  29. package/src/widgets/__testdata__/label-image.testdata.ts +63 -0
  30. package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +5 -5
  31. package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +28 -28
  32. package/src/widgets/__tests__/expression.test.tsx +12 -9
  33. package/src/widgets/__tests__/renderQuestion.tsx +30 -13
  34. package/src/widgets/expression.tsx +19 -25
  35. package/src/widgets/transformer.tsx +1 -2
  36. package/tsconfig-build.tsbuildinfo +1 -1
@@ -1,6 +1,8 @@
1
1
  import * as React from "react";
2
2
  import InteractionTracker from "./interaction-tracker";
3
3
  import TranslationLinter from "./translation-linter";
4
+ import WidgetContainer from "./widget-container";
5
+ import type { DependenciesContext } from "./dependencies";
4
6
  import type { PerseusRenderer, PerseusWidgetOptions } from "./perseus-types";
5
7
  import type { APIOptions, APIOptionsWithDefaults, FilterCriterion, FocusPath, PerseusScore, WidgetInfo, WidgetProps } from "./types";
6
8
  import type { LinterContextProps } from "@khanacademy/perseus-linter";
@@ -37,7 +39,7 @@ export type Widget = {
37
39
  showRationalesForCurrentlySelectedChoices?: (options?: any) => void;
38
40
  examples?: () => ReadonlyArray<string>;
39
41
  };
40
- type Props = {
42
+ type Props = Partial<React.ContextType<typeof DependenciesContext>> & {
41
43
  apiOptions?: APIOptions;
42
44
  alwaysUpdate?: boolean;
43
45
  findExternalWidgets: any;
@@ -93,6 +95,7 @@ declare class Renderer extends React.Component<Props, State> {
93
95
  _isMounted: boolean;
94
96
  _isTwoColumn: boolean;
95
97
  _translationLinter: TranslationLinter;
98
+ _widgetContainers: Map<string, WidgetContainer>;
96
99
  lastRenderedMarkdown: React.ReactNode;
97
100
  reuseMarkdown: boolean;
98
101
  translationIndex: number;
@@ -1,5 +1,5 @@
1
- export declare const baseFontFamily = "'Proxima Nova',sans-serif";
2
- export declare const boldFontFamily = "'Proxima Nova Semibold',sans-serif";
1
+ export declare const baseFontFamily = "'Lato', sans-serif";
2
+ export declare const boldFontFamily = "'Lato-Bold', 'Lato', sans-serif";
3
3
  export declare const green = "#76A005";
4
4
  export declare const kaGreen = "#71B307";
5
5
  export declare const blue = "#1C758A";
@@ -5,6 +5,7 @@ import type { LinterContextProps } from "@khanacademy/perseus-linter";
5
5
  type Props = {
6
6
  shouldHighlight: boolean;
7
7
  type: string;
8
+ id: string;
8
9
  initialProps: WidgetProps<any, PerseusWidgetOptions>;
9
10
  linterContext: LinterContextProps;
10
11
  };
@@ -16,6 +17,7 @@ type State = {
16
17
  widgetProps: WidgetProps<any, PerseusWidgetOptions>;
17
18
  };
18
19
  declare class WidgetContainer extends React.Component<Props, State> {
20
+ widgetRef: React.RefObject<React.ComponentType<any>>;
19
21
  static defaultProps: DefaultProps;
20
22
  state: any;
21
23
  componentDidMount(): void;
@@ -51,7 +51,9 @@ export declare class Expression extends React.Component<Props, ExpressionState>
51
51
  componentDidUpdate: (prevProps: Props) => void;
52
52
  componentWillUnmount: () => void;
53
53
  _handleMouseDown: () => void;
54
- simpleValidate: (rubric: Rubric, onInputError: OnInputErrorFunctionType) => PerseusScore;
54
+ simpleValidate: (rubric: Rubric & {
55
+ scoring?: boolean;
56
+ }, onInputError: OnInputErrorFunctionType) => PerseusScore;
55
57
  getUserInput: () => string;
56
58
  change: (...args: any) => any | undefined;
57
59
  parse: (value: string, props: Props) => any;
@@ -65,7 +67,6 @@ export declare class Expression extends React.Component<Props, ExpressionState>
65
67
  getInputPaths: () => ReadonlyArray<ReadonlyArray<any>>;
66
68
  getGrammarTypeForPath: (inputPath: string) => string;
67
69
  setInputValue: (path: string, newValue: string, cb: () => void) => void;
68
- sendExpressionEvaluatedEvent(result: "correct" | "incorrect" | "invalid"): void;
69
70
  render(): React.ReactNode | React.ReactElement<React.ComponentProps<"div">>;
70
71
  }
71
72
  /**
@@ -213,7 +214,7 @@ declare const _default: Readonly<{
213
214
  onFocus: (blurPath: import("../types").FocusPath) => void;
214
215
  times: boolean;
215
216
  value: string;
216
- }, "static" | "trackInteraction" | "onChange" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "analytics" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
217
+ }, "static" | "trackInteraction" | "onChange" | "analytics" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
217
218
  value?: string | undefined;
218
219
  onFocus?: ((blurPath: import("../types").FocusPath) => void) | undefined;
219
220
  onBlur?: ((blurPath: import("../types").FocusPath) => void) | undefined;
@@ -419,7 +420,7 @@ declare const _default: Readonly<{
419
420
  onFocus: (blurPath: import("../types").FocusPath) => void;
420
421
  times: boolean;
421
422
  value: string;
422
- }, "static" | "trackInteraction" | "onChange" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "analytics" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
423
+ }, "static" | "trackInteraction" | "onChange" | "analytics" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
423
424
  value?: string | undefined;
424
425
  onFocus?: ((blurPath: import("../types").FocusPath) => void) | undefined;
425
426
  onBlur?: ((blurPath: import("../types").FocusPath) => void) | undefined;
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": "12.2.0",
6
+ "version": "12.3.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -22,13 +22,13 @@
22
22
  "test": "bash -c 'yarn --silent --cwd \"../..\" test ${@:0} $($([[ ${@: -1} = -* ]] || [[ ${@: -1} = bash ]]) && echo $PWD)'"
23
23
  },
24
24
  "dependencies": {
25
- "@khanacademy/kas": "^0.3.3",
26
- "@khanacademy/kmath": "^0.1.4",
27
- "@khanacademy/math-input": "^14.2.0",
28
- "@khanacademy/perseus-core": "1.1.2",
29
- "@khanacademy/perseus-linter": "^0.3.6",
30
- "@khanacademy/pure-markdown": "^0.2.8",
31
- "@khanacademy/simple-markdown": "^0.10.2"
25
+ "@khanacademy/kas": "^0.3.4",
26
+ "@khanacademy/kmath": "^0.1.5",
27
+ "@khanacademy/math-input": "^14.2.1",
28
+ "@khanacademy/perseus-core": "1.2.0",
29
+ "@khanacademy/perseus-linter": "^0.3.7",
30
+ "@khanacademy/pure-markdown": "^0.2.9",
31
+ "@khanacademy/simple-markdown": "^0.10.3"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@khanacademy/wonder-blocks-button": "^4.1.3",
@@ -8,9 +8,10 @@ import TestUtils from "react-dom/test-utils";
8
8
  import _ from "underscore";
9
9
  import "jest-enzyme";
10
10
 
11
- import {ClassNames, Dependencies, Renderer} from "@khanacademy/perseus";
12
-
13
11
  import {testDependencies} from "../../../../testing/test-dependencies";
12
+ import * as Dependencies from "../dependencies";
13
+ import {ClassNames} from "../perseus-api";
14
+ import Renderer from "../renderer";
14
15
  import {registerAllWidgetsForTesting} from "../util/register-all-widgets-for-testing";
15
16
 
16
17
  import imageItem from "./test-items/image-item";
@@ -18,6 +19,8 @@ import inputNumber1Item from "./test-items/input-number-1-item";
18
19
  import inputNumber2Item from "./test-items/input-number-2-item";
19
20
  import tableItem from "./test-items/table-item";
20
21
 
22
+ import type {APIOptions} from "../types";
23
+
21
24
  const itemWidget = inputNumber1Item;
22
25
 
23
26
  // Returns a promise that will resolve shortly after the end of this
@@ -30,16 +33,20 @@ const delayedPromise = (value: undefined) => {
30
33
  return deferred.promise();
31
34
  };
32
35
 
33
- const renderQuestionArea = function (item, apiOptions): any {
36
+ const renderQuestionArea = function (item, apiOptions?: APIOptions): any {
34
37
  const wrapper = mount(
35
38
  <RenderStateRoot>
36
- <Renderer
37
- content={item.question.content}
38
- images={item.question.images}
39
- widgets={item.question.widgets}
40
- problemNum={0}
41
- apiOptions={apiOptions}
42
- />
39
+ <Dependencies.DependenciesContext.Provider
40
+ value={{analytics: {onAnalyticsEvent: async () => undefined}}}
41
+ >
42
+ <Renderer
43
+ content={item.question.content}
44
+ images={item.question.images}
45
+ widgets={item.question.widgets}
46
+ problemNum={0}
47
+ apiOptions={apiOptions}
48
+ />
49
+ </Dependencies.DependenciesContext.Provider>
43
50
  </RenderStateRoot>,
44
51
  {includeDefaultTestHarness: false},
45
52
  );
@@ -56,7 +63,6 @@ describe("Perseus API", function () {
56
63
 
57
64
  describe("setInputValue", function () {
58
65
  it("should be able to produce a correctly graded value", function () {
59
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
60
66
  const renderer = renderQuestionArea(inputNumber1Item);
61
67
  renderer.setInputValue(["input-number 1"], "5");
62
68
  const score = renderer.guessAndScore()[1];
@@ -65,7 +71,6 @@ describe("Perseus API", function () {
65
71
  });
66
72
 
67
73
  it("should be able to produce a wrong value", function () {
68
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
69
74
  const renderer = renderQuestionArea(inputNumber1Item);
70
75
  renderer.setInputValue(["input-number 1"], "3", () => {});
71
76
  const score = renderer.guessAndScore()[1];
@@ -74,7 +79,6 @@ describe("Perseus API", function () {
74
79
  });
75
80
 
76
81
  it("should be able to produce an empty score", function () {
77
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
78
82
  const renderer = renderQuestionArea(inputNumber1Item);
79
83
  renderer.setInputValue(["input-number 1"], "3");
80
84
  let score = renderer.guessAndScore()[1];
@@ -86,7 +90,6 @@ describe("Perseus API", function () {
86
90
  });
87
91
 
88
92
  it("should be able to accept a callback", function (done) {
89
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
90
93
  const renderer = renderQuestionArea(inputNumber1Item);
91
94
  renderer.setInputValue(["input-number 1"], "3", function () {
92
95
  const guess = renderer.getUserInput()[0];
@@ -99,14 +102,12 @@ describe("Perseus API", function () {
99
102
 
100
103
  describe("getInputPaths", function () {
101
104
  it("should be able to find all the input widgets", function () {
102
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
103
105
  const renderer = renderQuestionArea(inputNumber2Item);
104
106
  const numPaths = renderer.getInputPaths().length;
105
107
  expect(numPaths).toBe(2);
106
108
  });
107
109
 
108
110
  it("should be able to find all inputs within widgets", function () {
109
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
110
111
  const renderer = renderQuestionArea(tableItem);
111
112
  const numPaths = renderer.getInputPaths().length;
112
113
  expect(numPaths).toBe(8);
@@ -115,7 +116,6 @@ describe("Perseus API", function () {
115
116
 
116
117
  describe("getDOMNodeForPath", function () {
117
118
  it("should find one DOM node per <input>", function () {
118
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
119
119
  const renderer = renderQuestionArea(inputNumber2Item);
120
120
  const inputPaths = renderer.getInputPaths();
121
121
  const allInputs = TestUtils.scryRenderedDOMComponentsWithTag(
@@ -126,7 +126,6 @@ describe("Perseus API", function () {
126
126
  });
127
127
 
128
128
  it("should find the right DOM nodes for the <input>s", function () {
129
- // @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
130
129
  const renderer = renderQuestionArea(inputNumber2Item);
131
130
  const inputPaths = renderer.getInputPaths();
132
131
  const allInputs = TestUtils.scryRenderedDOMComponentsWithTag(
@@ -165,11 +164,7 @@ describe("Perseus API", function () {
165
164
  // version
166
165
  expect(ClassNames.FOCUSED).toBe("perseus-focused");
167
166
 
168
- const renderer = renderQuestionArea(inputNumber1Item, {
169
- interceptInputFocus: function (widgetId) {
170
- renderer.setInputValue(widgetId, "5");
171
- },
172
- });
167
+ const renderer = renderQuestionArea(inputNumber1Item);
173
168
 
174
169
  const input =
175
170
  // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'querySelector' does not exist on type 'Element | Text'.
@@ -19,19 +19,20 @@ import type {KeypadAPI} from "@khanacademy/math-input";
19
19
  import type {KeypadContextRendererInterface} from "@khanacademy/perseus-core";
20
20
  import type {LinterContextProps} from "@khanacademy/perseus-linter";
21
21
 
22
- type Props = {
23
- apiOptions: APIOptions;
24
- json: PerseusRenderer | ReadonlyArray<PerseusRenderer>;
25
- // Whether to use the new Bibliotron styles for articles
26
- /**
27
- * @deprecated Does nothing
28
- */
29
- useNewStyles: boolean;
30
- linterContext: LinterContextProps;
31
- legacyPerseusLint?: ReadonlyArray<string>;
32
- keypadElement?: KeypadAPI | null | undefined;
33
- dependencies: PerseusDependenciesV2;
34
- } & KeypadProps;
22
+ type Props = Partial<React.ContextType<typeof DependenciesContext>> &
23
+ KeypadProps & {
24
+ apiOptions: APIOptions;
25
+ json: PerseusRenderer | ReadonlyArray<PerseusRenderer>;
26
+ // Whether to use the new Bibliotron styles for articles
27
+ /**
28
+ * @deprecated Does nothing
29
+ */
30
+ useNewStyles: boolean;
31
+ linterContext: LinterContextProps;
32
+ legacyPerseusLint?: ReadonlyArray<string>;
33
+ keypadElement?: KeypadAPI | null | undefined;
34
+ dependencies: PerseusDependenciesV2;
35
+ };
35
36
 
36
37
  type DefaultProps = {
37
38
  apiOptions: Props["apiOptions"];
@@ -208,6 +209,7 @@ class ArticleRenderer
208
209
  "article",
209
210
  )}
210
211
  legacyPerseusLint={this.props.legacyPerseusLint}
212
+ {...this.props.dependencies}
211
213
  />
212
214
  </div>
213
215
  );
@@ -5,6 +5,9 @@ import {Errors, Log} from "./logging/log";
5
5
  type Props = {
6
6
  children: React.ReactNode;
7
7
  metadata?: Record<string, string>;
8
+
9
+ // A callback that is called when the error boundary traps an error.
10
+ onError?: (error: Error, info: any) => void;
8
11
  };
9
12
  type State = {
10
13
  error: string;
@@ -218,7 +218,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
218
218
  style="border-color: transparent; border-radius: 50%;"
219
219
  >
220
220
  <div
221
- class="circle_1tys2th"
221
+ class="circle_1w6i3h1"
222
222
  data-is-radio-icon="true"
223
223
  data-test-id="choice-icon__library-choice-icon"
224
224
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
@@ -385,7 +385,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
385
385
  style="border-color: transparent; border-radius: 50%;"
386
386
  >
387
387
  <div
388
- class="circle_1tys2th"
388
+ class="circle_1w6i3h1"
389
389
  data-is-radio-icon="true"
390
390
  data-test-id="choice-icon__library-choice-icon"
391
391
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
@@ -552,7 +552,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
552
552
  style="border-color: transparent; border-radius: 50%;"
553
553
  >
554
554
  <div
555
- class="circle_1tys2th"
555
+ class="circle_1w6i3h1"
556
556
  data-is-radio-icon="true"
557
557
  data-test-id="choice-icon__library-choice-icon"
558
558
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
@@ -719,7 +719,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
719
719
  style="border-color: transparent; border-radius: 50%;"
720
720
  >
721
721
  <div
722
- class="circle_1tys2th"
722
+ class="circle_1w6i3h1"
723
723
  data-is-radio-icon="true"
724
724
  data-test-id="choice-icon__library-choice-icon"
725
725
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
@@ -886,7 +886,7 @@ exports[`multi-item renderer should snapshot: initial render 1`] = `
886
886
  style="border-color: transparent; border-radius: 50%;"
887
887
  >
888
888
  <div
889
- class="circle_1tys2th"
889
+ class="circle_1w6i3h1"
890
890
  data-is-radio-icon="true"
891
891
  data-test-id="choice-icon__library-choice-icon"
892
892
  style="border-color: rgba(33,36,44,0.64); color: rgba(33, 36, 44, 0.64); border-radius: 24px;"
package/src/renderer.tsx CHANGED
@@ -29,6 +29,7 @@ import preprocessTex from "./util/katex-preprocess";
29
29
  import WidgetContainer from "./widget-container";
30
30
  import * as Widgets from "./widgets";
31
31
 
32
+ import type {DependenciesContext} from "./dependencies";
32
33
  import type {PerseusRenderer, PerseusWidgetOptions} from "./perseus-types";
33
34
  import type {
34
35
  APIOptions,
@@ -52,6 +53,8 @@ const noopOnRender = () => {};
52
53
 
53
54
  const SHOULD_CLEAR_WIDGETS_PROP_LIST = ["content", "problemNum", "widgets"];
54
55
 
56
+ const makeContainerId = (id: string) => "container:" + id;
57
+
55
58
  // Check if one focus path / id path is a prefix of another
56
59
  // The focus path null will never be a prefix of any non-null
57
60
  // path, since it represents no focus.
@@ -144,7 +147,7 @@ export type Widget = {
144
147
  examples?: () => ReadonlyArray<string>;
145
148
  };
146
149
 
147
- type Props = {
150
+ type Props = Partial<React.ContextType<typeof DependenciesContext>> & {
148
151
  apiOptions?: APIOptions;
149
152
  alwaysUpdate?: boolean;
150
153
  findExternalWidgets: any;
@@ -223,6 +226,8 @@ class Renderer extends React.Component<Props, State> {
223
226
  // The i18n linter.
224
227
  _translationLinter: TranslationLinter;
225
228
 
229
+ _widgetContainers: Map<string, WidgetContainer> = new Map();
230
+
226
231
  lastRenderedMarkdown: React.ReactNode;
227
232
  // @ts-expect-error - TS2564 - Property 'reuseMarkdown' has no initializer and is not definitely assigned in the constructor.
228
233
  reuseMarkdown: boolean;
@@ -390,9 +395,7 @@ class Renderer extends React.Component<Props, State> {
390
395
  // they are re-rendered, so even if they've been
391
396
  // re-rendered we need to call these methods on them.
392
397
  _.each(this.widgetIds, (id) => {
393
- // eslint-disable-next-line react/no-string-refs
394
- const container = this.refs["container:" + id];
395
- // @ts-expect-error - TS2339 - Property 'replaceWidgetProps' does not exist on type 'ReactInstance'.
398
+ const container = this._widgetContainers.get(makeContainerId(id));
396
399
  container && container.replaceWidgetProps(this.getWidgetProps(id));
397
400
  });
398
401
 
@@ -550,21 +553,25 @@ class Renderer extends React.Component<Props, State> {
550
553
  // filtered out in this.render(), so we shouldn't have to
551
554
  // worry about using this widget key and ref:
552
555
  return (
553
- <ErrorBoundary
554
- key={"container:" + id}
555
- metadata={{widget_type: type, widget_id: id}}
556
- >
557
- <WidgetContainer
558
- ref={"container:" + id}
559
- type={type}
560
- initialProps={this.getWidgetProps(id)}
561
- shouldHighlight={shouldHighlight}
562
- linterContext={PerseusLinter.pushContextStack(
563
- this.props.linterContext,
564
- "widget",
565
- )}
566
- />
567
- </ErrorBoundary>
556
+ <WidgetContainer
557
+ key={makeContainerId(id)}
558
+ id={id}
559
+ ref={(node) => {
560
+ const containerId = makeContainerId(id);
561
+ if (node != null) {
562
+ this._widgetContainers.set(containerId, node);
563
+ } else {
564
+ this._widgetContainers.delete(containerId);
565
+ }
566
+ }}
567
+ type={type}
568
+ initialProps={this.getWidgetProps(id)}
569
+ shouldHighlight={shouldHighlight}
570
+ linterContext={PerseusLinter.pushContextStack(
571
+ this.props.linterContext,
572
+ "widget",
573
+ )}
574
+ />
568
575
  );
569
576
  }
570
577
  return null;
@@ -603,7 +610,6 @@ class Renderer extends React.Component<Props, State> {
603
610
 
604
611
  return {
605
612
  ...widgetProps,
606
- ref: id,
607
613
  widgetId: id,
608
614
  alignment: widgetInfo && widgetInfo.alignment,
609
615
  // When determining if a widget is static, we verify that the widget is not an
@@ -853,15 +859,11 @@ class Renderer extends React.Component<Props, State> {
853
859
  ];
854
860
  };
855
861
 
856
- getWidgetInstance: (id: string) => Widget | null | undefined = (
857
- id: string,
858
- ): Widget | null | undefined => {
859
- // eslint-disable-next-line react/no-string-refs
860
- const ref = this.refs["container:" + id];
862
+ getWidgetInstance: (id: string) => Widget | null | undefined = (id) => {
863
+ const ref = this._widgetContainers.get(makeContainerId(id));
861
864
  if (!ref) {
862
865
  return null;
863
866
  }
864
- // @ts-expect-error - TS2339 - Property 'getWidget' does not exist on type 'ReactInstance'.
865
867
  return ref.getWidget();
866
868
  };
867
869
 
@@ -1781,7 +1783,7 @@ class Renderer extends React.Component<Props, State> {
1781
1783
  // widget can be undefined if it hasn't yet been rendered
1782
1784
  if (widget && widget.simpleValidate) {
1783
1785
  widgetScores[id] = widget.simpleValidate(
1784
- props?.options,
1786
+ {...props?.options, scoring: true},
1785
1787
  onInputError,
1786
1788
  );
1787
1789
  }
@@ -416,6 +416,7 @@ export class ServerItemRenderer
416
416
  content={this.props.item.question.content}
417
417
  widgets={this.props.item.question.widgets}
418
418
  images={this.props.item.question.images}
419
+ {...this.props.dependencies}
419
420
  />
420
421
  </AssetContext.Provider>
421
422
  );
@@ -1,7 +1,7 @@
1
- // @baseFontFamily: "Proxima Nova", sans-serif;
2
- export const baseFontFamily = "'Proxima Nova',sans-serif";
3
- // @boldFontFamily: "Proxima Nova Semibold", sans-serif;
4
- export const boldFontFamily = "'Proxima Nova Semibold',sans-serif";
1
+ // @baseFontFamily: "Lato", sans-serif;
2
+ export const baseFontFamily = "'Lato', sans-serif";
3
+ // @boldFontFamily: "Lato-Bold", "Lato", sans-serif;
4
+ export const boldFontFamily = "'Lato-Bold', 'Lato', sans-serif";
5
5
  // @green: #76a005;
6
6
  export const green = "#76A005";
7
7
  // @kaGreen: #71b307;
@@ -113,16 +113,6 @@
113
113
 
114
114
  // HACK(yejia): Override the font size and line height for blurbs
115
115
  //
116
- // NOTE(yejia): Minion Pro is only loaded on GTP pages, because those
117
- // are the main call sites for blurbs and the passage widget. We
118
- // considered inserting the relevant stylesheet whenever the passage
119
- // widget loads, but this approach produced a "flash of unstyled
120
- // text", so we decided to embed the stylesheet into the relevant
121
- // page templates instead.
122
- //
123
- // NOTE(yejia): Minion Pro will not be available on ZNDs, due to
124
- // restrictions in our Adobe TypeKit configuration.
125
- //
126
116
  // TODO(TP-3315): Remove once Perseus uses Wonder Blocks BodySerif
127
117
  .test-prep-blurb div.paragraph {
128
118
  font-size: 16px;
@@ -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 {