@khanacademy/perseus 16.0.0 → 17.0.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.
@@ -1,3 +1,4 @@
1
+ import * as PerseusLinter from "@khanacademy/perseus-linter";
1
2
  /**
2
3
  * A copy of the ItemRenderer which renders its question renderer and hints
3
4
  * renderer normally instead of ReactDOM.render()ing them into elements in the
@@ -8,18 +9,18 @@
8
9
  */
9
10
  import * as React from "react";
10
11
  import Renderer from "./renderer";
11
- import type { KeypadProps } from "./mixins/provide-keypad";
12
12
  import type { APIOptions, FocusPath, PerseusDependenciesV2 } from "./types";
13
13
  import type { KeypadAPI } from "@khanacademy/math-input";
14
14
  import type { KeypadContextRendererInterface, RendererInterface, KEScore } from "@khanacademy/perseus-core";
15
- type OwnProps = // These props are used by the ProvideKeypad mixin.
16
- KeypadProps & {
15
+ import type { LinterContextProps } from "@khanacademy/perseus-linter";
16
+ type OwnProps = {
17
17
  apiOptions: APIOptions;
18
18
  hintsVisible?: number;
19
19
  item: {
20
20
  hints: ReadonlyArray<any>;
21
21
  question: any;
22
22
  };
23
+ linterContext: LinterContextProps;
23
24
  problemNum?: number;
24
25
  reviewMode?: boolean;
25
26
  keypadElement?: KeypadAPI | null | undefined;
@@ -32,6 +33,7 @@ type Props = OwnProps & HOCProps;
32
33
  type DefaultProps = {
33
34
  apiOptions: Props["apiOptions"];
34
35
  onRendered: Props["onRendered"];
36
+ linterContext: Props["linterContext"];
35
37
  };
36
38
  type State = {
37
39
  questionCompleted: boolean;
@@ -102,16 +104,41 @@ export declare class ServerItemRenderer extends React.Component<Props, State> im
102
104
  setAssetStatus: (assetKey: string, status: boolean) => void;
103
105
  render(): React.ReactNode;
104
106
  }
105
- declare const ref: React.ForwardRefExoticComponent<Omit<KeypadProps & {
106
- apiOptions: APIOptions;
107
- hintsVisible?: number | undefined;
108
- item: {
109
- hints: ReadonlyArray<any>;
110
- question: any;
111
- };
112
- problemNum?: number | undefined;
113
- reviewMode?: boolean | undefined;
114
- keypadElement?: KeypadAPI | null | undefined;
115
- dependencies: PerseusDependenciesV2;
116
- } & HOCProps, "onRendered"> & React.RefAttributes<ServerItemRenderer>>;
107
+ declare const ref: React.ForwardRefExoticComponent<Omit<Pick<OwnProps & HOCProps, "item" | "problemNum" | "reviewMode" | "keypadElement" | "hintsVisible" | "dependencies"> & {
108
+ linterContext?: PerseusLinter.LinterContextProps | undefined;
109
+ apiOptions?: Readonly<{
110
+ isArticle?: boolean | undefined;
111
+ onInputError?: ((widgetId: any, value: string, message?: string | null | undefined) => unknown) | undefined;
112
+ onFocusChange?: ((newFocusPath: FocusPath, oldFocusPath: FocusPath, keypadElement?: Element | Text | null | undefined, focusedElement?: HTMLElement | undefined) => unknown) | undefined;
113
+ GroupMetadataEditor?: React.ComponentType<any> | undefined;
114
+ showAlignmentOptions?: boolean | undefined;
115
+ readOnly?: boolean | undefined;
116
+ answerableCallback?: ((arg1: boolean) => unknown) | undefined;
117
+ getAnotherHint?: (() => unknown) | undefined;
118
+ interactionCallback?: (() => unknown) | undefined;
119
+ groupAnnotator?: ((groupNumber: number, widgetId: string) => React.ReactNode) | undefined;
120
+ imagePlaceholder?: React.ReactNode;
121
+ widgetPlaceholder?: React.ReactNode;
122
+ baseElements?: {
123
+ Link: React.ComponentType<any>;
124
+ } | undefined;
125
+ imagePreloader?: ((dimensions: import("./types").Dimensions) => React.ReactNode) | undefined;
126
+ trackInteraction?: ((args: {
127
+ type: string;
128
+ id: string;
129
+ correct?: boolean | undefined;
130
+ } & Partial<import("./types").TrackingGradedGroupExtraArguments> & Partial<import("./types").TrackingSequenceExtraArguments>) => void) | undefined;
131
+ customKeypad?: boolean | undefined;
132
+ nativeKeypadProxy?: ((blur: () => void) => KeypadAPI) | undefined;
133
+ isMobile?: boolean | undefined;
134
+ setDrawingAreaAvailable?: ((arg1: boolean) => unknown) | undefined;
135
+ useDraftEditor?: boolean | undefined;
136
+ hintProgressColor?: string | undefined;
137
+ canScrollPage?: boolean | undefined;
138
+ inModal?: boolean | undefined;
139
+ crossOutEnabled?: boolean | undefined;
140
+ editorChangeDelay?: number | undefined;
141
+ }> | undefined;
142
+ onRendered?: ((isRendered: boolean) => void) | undefined;
143
+ } & {}, "onRendered"> & React.RefAttributes<ServerItemRenderer>>;
117
144
  export default ref;
package/dist/strings.js CHANGED
@@ -53,8 +53,6 @@ module.exports = [
53
53
  i18n._("Correct (but you crossed it out)", {}),
54
54
  i18n._("Correct (selected)", {}),
55
55
  i18n._("Correct!", {}),
56
- i18n._("Cosine", {}),
57
- i18n._("Cube root", {}),
58
56
  i18n._("Current", {}),
59
57
  // I18N: As in the command, "Dilate the polygon"
60
58
  i18n._("Dilate", {}),
@@ -62,19 +60,13 @@ module.exports = [
62
60
  i18n._("Dilation", {}),
63
61
  i18n.$_("Dilation about %(point)s by %(scale)s", {}),
64
62
  i18n.$_("Dilation of scale %(scale)s about %(point)s", {}),
65
- i18n._("Divide", {}),
66
63
  i18n._("Drag handles to make graph", {}),
67
64
  i18n._("End of reading passage.", {}),
68
65
  i18n._("entered in an invalid answer.", {}),
69
- i18n._("Equals sign", {}),
70
66
  i18n.$_("Error rendering: %(error)s", {}),
71
67
  i18n._("Explain", {}),
72
- i18n._("Exponent", {}),
73
68
  i18n._("False", {}),
74
- i18n._("Fraction, excluding the current expression", {}),
75
69
  i18n._("Get another hint", {}),
76
- i18n._("Greater than or equal to sign", {}),
77
- i18n._("Greater than sign", {}),
78
70
  i18n._("Hide answer choices", {}),
79
71
  i18n._("Hide explanation", {}),
80
72
  i18n._("Hint #%(pos)s", {}),
@@ -87,38 +79,27 @@ module.exports = [
87
79
  i18n._("It looks like you left something blank or ", {}),
88
80
  i18n._("Keep trying", {}),
89
81
  i18n._("Khan Academy video wrapper", {}),
90
- i18n._("Less than or equal to sign", {}),
91
- i18n._("Less than sign", {}),
92
82
  // I18N: a label next to a reading passage to denote the line number
93
83
  i18n._("Line ", {}),
94
84
  i18n.$_("line %(lineNumber)s", {}),
95
85
  i18n.$_("lines %(lineRange)s", {}),
96
86
  i18n._("Loading...", {}),
97
- i18n._("Logarithm with base 10", {}),
98
- i18n._("Logarithm with custom base", {}),
99
87
  i18n._("Make circle filled", {}),
100
88
  i18n._("Make circle open", {}),
101
89
  i18n._("Make sure you fill in all cells in the matrix.", {}),
102
90
  i18n._("Make sure you select something for every row.", {}),
103
- i18n._("Minus", {}),
104
91
  i18n.ngettext("Moves: %(num)s","Moves: %(num)s", {}),
105
- i18n._("Multiply", {}),
106
- i18n._("Natural logarithm", {}),
107
92
  i18n._("Next question", {}),
108
93
  i18n._("No", {}),
109
94
  i18n._("None of the above", {}),
110
- i18n._("Not-equals sign", {}),
111
95
  i18n._("Note: For this question, the shape will not move.", {}),
112
96
  i18n._("Number of divisions:", {}),
113
97
  i18n._("Number of trials:", {}),
114
98
  i18n._("Oops!", {}),
115
99
  i18n._("open math keypad", {}),
116
100
  i18n._("Parameter", {}),
117
- i18n._("Phi", {}),
118
- i18n._("Pi", {}),
119
101
  i18n._("Please choose the correct number of answers.", {}),
120
102
  i18n.$_("Please make sure the number of divisions is in the range %(divRangeString)s.", {}),
121
- i18n._("Plus", {}),
122
103
  i18n._("Reaction arrow pointing to the right.", {}),
123
104
  // I18N: As in the command, "Reflect the polygon"
124
105
  i18n._("Reflect", {}),
@@ -136,19 +117,15 @@ module.exports = [
136
117
  i18n._("Run simulation", {}),
137
118
  i18n._("Sample size:", {}),
138
119
  i18n._("See video transcript", {}),
139
- i18n._("Sine", {}),
140
120
  i18n._("Skip to %(title)s", {}),
141
121
  i18n._("Something went wrong.", {}),
142
122
  i18n._("Sorry, I don't understand that!", {}),
143
- i18n._("Square root", {}),
144
123
  i18n._("Switch direction", {}),
145
- i18n._("Tangent", {}),
146
124
  i18n._("Tap each dot on the image to select all answers that apply.", {}),
147
125
  i18n._("Tap each dot on the image to select an answer.", {}),
148
126
  i18n._("Tap to add points", {}),
149
127
  i18n._("That answer is numerically incorrect.", {}),
150
128
  i18n._("The symbol %(questionSymbol)s indicates that question %(questionNumber)s references this portion of the passage.", {}),
151
- i18n._("Theta", {}),
152
129
  i18n._("This controls the proportion or percentage that will be used in your simulation.", {}),
153
130
  i18n._("This controls the sample size that will be used in your simulation. For example, if you set this to 100, then for each trial, responses from 100 participants will be simulated.", {}),
154
131
  i18n._("This is the number of trials used in the simulation. For example, if set to 50, then the survey will be conducted 50 times.", {}),
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": "16.0.0",
6
+ "version": "17.0.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -24,7 +24,7 @@
24
24
  "dependencies": {
25
25
  "@khanacademy/kas": "^0.3.6",
26
26
  "@khanacademy/kmath": "^0.1.7",
27
- "@khanacademy/math-input": "^16.1.0",
27
+ "@khanacademy/math-input": "^16.1.1",
28
28
  "@khanacademy/perseus-core": "1.4.0",
29
29
  "@khanacademy/perseus-linter": "^0.3.9",
30
30
  "@khanacademy/pure-markdown": "^0.2.12",
@@ -1,10 +1,13 @@
1
1
  import * as React from "react";
2
2
 
3
3
  import {ServerItemRendererWithDebugUI} from "../../../../testing/server-item-renderer-with-debug-ui";
4
+ import {testDependenciesV2} from "../../../../testing/test-dependencies";
4
5
  import {
5
6
  itemWithInput,
7
+ itemWithLintingError,
6
8
  labelImageItem,
7
- } from "../__testdata__/item-renderer.testdata";
9
+ } from "../__testdata__/server-item-renderer.testdata";
10
+ import {ServerItemRenderer} from "../server-item-renderer";
8
11
 
9
12
  type StoryArgs = Record<any, any>;
10
13
 
@@ -23,3 +26,19 @@ export const InputNumberItem = (args: StoryArgs): React.ReactElement => {
23
26
  export const LabelImageItem = (args: StoryArgs): React.ReactElement => {
24
27
  return <ServerItemRendererWithDebugUI item={labelImageItem} />;
25
28
  };
29
+
30
+ export const WithLintingError = (args: StoryArgs): React.ReactElement => {
31
+ return (
32
+ <ServerItemRenderer
33
+ problemNum={0}
34
+ item={itemWithLintingError}
35
+ dependencies={testDependenciesV2}
36
+ linterContext={{
37
+ contentType: "",
38
+ highlightLint: true,
39
+ paths: [],
40
+ stack: [],
41
+ }}
42
+ />
43
+ );
44
+ };
@@ -0,0 +1,173 @@
1
+ import type {
2
+ InputNumberWidget,
3
+ LabelImageWidget,
4
+ PerseusItem,
5
+ PerseusRenderer,
6
+ } from "../perseus-types";
7
+
8
+ export const itemWithInput: PerseusItem = {
9
+ question: {
10
+ content:
11
+ "Enter the number $$-42$$ in the box: [[\u2603 input-number 1]]",
12
+ images: {},
13
+ widgets: {
14
+ "input-number 1": {
15
+ type: "input-number",
16
+ graded: true,
17
+ options: {
18
+ answerType: "number",
19
+ value: "-42",
20
+ simplify: "required",
21
+ size: "normal",
22
+ inexact: false,
23
+ maxError: 0.1,
24
+ },
25
+ } as InputNumberWidget,
26
+ },
27
+ },
28
+ hints: [
29
+ {content: "Hint #1", images: {}, widgets: {}},
30
+ {content: "Hint #2", images: {}, widgets: {}},
31
+ {content: "Hint #3", images: {}, widgets: {}},
32
+ ],
33
+ answerArea: null,
34
+ _multi: null,
35
+ itemDataVersion: {major: 0, minor: 0},
36
+ answer: null,
37
+ };
38
+
39
+ export const labelImageItem: PerseusItem = {
40
+ answerArea: {
41
+ calculator: false,
42
+ chi2Table: false,
43
+ periodicTable: false,
44
+ tTable: false,
45
+ zTable: false,
46
+ },
47
+ _multi: null,
48
+ answer: null,
49
+ hints: [],
50
+ itemDataVersion: {major: 0, minor: 1},
51
+ question: {
52
+ content:
53
+ "Catherine created a chart and a bar graph to show how many dogs of each breed the animal shelter placed into good homes last year. \n\nDog breed | Number of dogs \n:- | :-: \nBulldog | $32$ \nGreyhound | $72$ \nMastiff | $56$ \nCollie | $40$ \n\n**Label each bar on the bar graph.**\n\n[[☃ label-image 1]]\n\n\n\n",
54
+ images: {
55
+ "web+graphie://ka-perseus-graphie.s3.amazonaws.com/2f60b533892fe6cb9351f704ffdada8ae4bc655c":
56
+ {height: 365, width: 503},
57
+ "web+graphie://ka-perseus-graphie.s3.amazonaws.com/75f937c6ea133d655dd9f698a58182efd6cc85d4":
58
+ {height: 293, width: 331},
59
+ },
60
+ widgets: {
61
+ "label-image 1": {
62
+ alignment: "default",
63
+ graded: true,
64
+ static: false,
65
+ type: "label-image",
66
+ version: {major: 0, minor: 0},
67
+ options: {
68
+ choices: ["Bulldog", "Greyhound", "Mastiff", "Collie"],
69
+ hideChoicesFromInstructions: true,
70
+ imageAlt:
71
+ "A bar graph with four bar lines shows the horizontal axis labeled Dog breed and the vertical axis labeled Number of dogs. The vertical axis is labeled from the bottom of the axis to the top of the axis as follows: 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, and 80. The horizontal axis has, from left to right, four unlabeled bar lines as follows: the first unlabeled bar line extends to 56, the second unlabeled bar line extends to 72, the third unlabeled bar line extends to 40, and the fourth unlabeled bar line extends to 32.",
72
+ imageHeight: 293,
73
+ imageUrl:
74
+ "web+graphie://ka-perseus-graphie.s3.amazonaws.com/64ace45942546183c3dc842cb0e9e4f64a312727",
75
+ imageWidth: 331,
76
+ markers: [
77
+ {
78
+ answers: ["Bulldog"],
79
+ label: "The fourth unlabeled bar line.",
80
+ x: 87.3,
81
+ y: 90.2,
82
+ },
83
+ {
84
+ answers: ["Collie"],
85
+ label: "The third unlabeled bar line.",
86
+ x: 67.7,
87
+ y: 90.5,
88
+ },
89
+ {
90
+ answers: ["Greyhound"],
91
+ label: "The second unlabeled bar line.",
92
+ x: 47.7,
93
+ y: 90.2,
94
+ },
95
+ {
96
+ answers: ["Mastiff"],
97
+ label: "The first unlabeled bar line.",
98
+ x: 27.8,
99
+ y: 90.5,
100
+ },
101
+ ],
102
+ multipleAnswers: false,
103
+ static: false,
104
+ },
105
+ } as LabelImageWidget,
106
+ },
107
+ },
108
+ };
109
+
110
+ // Note that if this item is used, you _must_ first register the MockWidget
111
+ export const mockedItem: PerseusItem = {
112
+ // The mock widget type is not part of the PerseusWidget type union (and
113
+ // we don't want to make it such to avoid polluting our production types
114
+ // for test purposes) so we force TypeScript to accept it here.
115
+ // @ts-expect-error - TS2352 - Conversion of type '{ content: string; images: {}; widgets: { "mock-widget 1": { graded: true; version: { major: number; minor: number; }; static: false; type: "mock-widget"; options: { static: false; smiling: boolean; }; alignment: string; }; "mock-widget 2": { ...; }; "mock-widget 3": { ...; }; "mock-widget 4": { ...; }; }; }' to type 'PerseusRenderer' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
116
+ question: {
117
+ content:
118
+ "Mock widget ==> [[\u2603 mock-widget 1]] [[\u2603 mock-widget 2]] [[\u2603 mock-widget 3]] [[\u2603 mock-widget 4]]",
119
+ images: {},
120
+ widgets: {
121
+ "mock-widget 1": {
122
+ graded: true,
123
+ version: {major: 0, minor: 0},
124
+ static: false,
125
+ type: "mock-widget",
126
+ options: {static: false, smiling: true},
127
+ alignment: "default",
128
+ },
129
+ "mock-widget 2": {
130
+ graded: true,
131
+ version: {major: 0, minor: 0},
132
+ static: false,
133
+ type: "mock-widget",
134
+ options: {static: false, smiling: false},
135
+ alignment: "default",
136
+ },
137
+ "mock-widget 3": {
138
+ graded: true,
139
+ version: {major: 0, minor: 0},
140
+ static: false,
141
+ type: "mock-widget",
142
+ options: {static: false, smiling: true},
143
+ alignment: "default",
144
+ },
145
+ "mock-widget 4": {
146
+ graded: true,
147
+ version: {major: 0, minor: 0},
148
+ static: false,
149
+ type: "mock-widget",
150
+ options: {static: false, smiling: false},
151
+ alignment: "default",
152
+ },
153
+ },
154
+ } as PerseusRenderer,
155
+ hints: [],
156
+ answerArea: null,
157
+ _multi: null,
158
+ itemDataVersion: {major: 0, minor: 0},
159
+ answer: null,
160
+ };
161
+
162
+ export const itemWithLintingError: PerseusItem = {
163
+ question: {
164
+ content: "# h1s aren't allowed",
165
+ images: {},
166
+ widgets: {},
167
+ },
168
+ hints: [],
169
+ answerArea: null,
170
+ _multi: null,
171
+ itemDataVersion: {major: 0, minor: 0},
172
+ answer: null,
173
+ };
@@ -10,8 +10,9 @@ import {
10
10
  } from "../../../../testing/test-dependencies";
11
11
  import {
12
12
  itemWithInput,
13
+ itemWithLintingError,
13
14
  mockedItem,
14
- } from "../__testdata__/item-renderer.testdata";
15
+ } from "../__testdata__/server-item-renderer.testdata";
15
16
  import * as Dependencies from "../dependencies";
16
17
  import WrappedServerItemRenderer, {
17
18
  ServerItemRenderer,
@@ -529,4 +530,40 @@ describe("server item renderer", () => {
529
530
  expect(screen.getByRole("textbox")).toHaveValue("-42");
530
531
  });
531
532
  });
533
+
534
+ describe("content editing", () => {
535
+ it("shouldn't show linting errors when highlightLint is false", () => {
536
+ // Arrange and Act
537
+ renderQuestion(itemWithLintingError, undefined, {
538
+ linterContext: {
539
+ contentType: "exercise",
540
+ highlightLint: false,
541
+ paths: [],
542
+ stack: [],
543
+ },
544
+ });
545
+
546
+ expect(
547
+ screen.queryByText("Don't use level-1 headings", {
548
+ exact: false,
549
+ }),
550
+ ).not.toBeInTheDocument();
551
+ });
552
+
553
+ it("should show linting errors when highlightLint is true", () => {
554
+ // Arrange and Act
555
+ renderQuestion(itemWithLintingError, undefined, {
556
+ linterContext: {
557
+ contentType: "exercise",
558
+ highlightLint: true,
559
+ paths: [],
560
+ stack: [],
561
+ },
562
+ });
563
+
564
+ expect(
565
+ screen.getByText("Don't use level-1 headings", {exact: false}),
566
+ ).toBeInTheDocument();
567
+ });
568
+ });
532
569
  });
@@ -12,27 +12,25 @@ import {ClassNames as ApiClassNames, ApiOptions} from "./perseus-api";
12
12
  import Renderer from "./renderer";
13
13
  import Util from "./util";
14
14
 
15
- import type {KeypadProps} from "./mixins/provide-keypad";
16
15
  import type {PerseusRenderer} from "./perseus-types";
17
16
  import type {APIOptions, PerseusDependenciesV2} from "./types";
18
17
  import type {KeypadAPI} from "@khanacademy/math-input";
19
18
  import type {KeypadContextRendererInterface} from "@khanacademy/perseus-core";
20
19
  import type {LinterContextProps} from "@khanacademy/perseus-linter";
21
20
 
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
- };
21
+ type Props = Partial<React.ContextType<typeof DependenciesContext>> & {
22
+ apiOptions: APIOptions;
23
+ json: PerseusRenderer | ReadonlyArray<PerseusRenderer>;
24
+ // Whether to use the new Bibliotron styles for articles
25
+ /**
26
+ * @deprecated Does nothing
27
+ */
28
+ useNewStyles: boolean;
29
+ linterContext: LinterContextProps;
30
+ legacyPerseusLint?: ReadonlyArray<string>;
31
+ keypadElement?: KeypadAPI | null | undefined;
32
+ dependencies: PerseusDependenciesV2;
33
+ };
36
34
 
37
35
  type DefaultProps = {
38
36
  apiOptions: Props["apiOptions"];
package/src/components.ts CHANGED
@@ -12,5 +12,4 @@ export {default as PropCheckBox} from "./components/prop-check-box";
12
12
  export {default as RangeInput} from "./components/range-input";
13
13
  export {default as SvgImage} from "./components/svg-image";
14
14
  export {default as TextInput} from "./components/text-input";
15
- export {default as TexButtons} from "./components/tex-buttons";
16
15
  export {default as TextListEditor} from "./components/text-list-editor";
package/src/index.ts CHANGED
@@ -14,7 +14,6 @@ export {default as itemVersion} from "./item-version";
14
14
  * Renderers
15
15
  */
16
16
  export {default as ArticleRenderer} from "./article-renderer";
17
- export {default as ItemRenderer} from "./item-renderer";
18
17
  export {default as ServerItemRenderer} from "./server-item-renderer";
19
18
  export {default as HintsRenderer} from "./hints-renderer";
20
19
  export {default as HintRenderer} from "./hint-renderer";
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable @khanacademy/ts-no-error-suppressions */
2
+ import * as PerseusLinter from "@khanacademy/perseus-linter";
2
3
  import {StyleSheet, css} from "aphrodite";
3
4
  /**
4
5
  * A copy of the ItemRenderer which renders its question renderer and hints
@@ -20,7 +21,6 @@ import {ApiOptions} from "./perseus-api";
20
21
  import Renderer from "./renderer";
21
22
  import Util from "./util";
22
23
 
23
- import type {KeypadProps} from "./mixins/provide-keypad";
24
24
  import type {APIOptions, FocusPath, PerseusDependenciesV2} from "./types";
25
25
  import type {KeypadAPI} from "@khanacademy/math-input";
26
26
  import type {
@@ -28,22 +28,24 @@ import type {
28
28
  RendererInterface,
29
29
  KEScore,
30
30
  } from "@khanacademy/perseus-core";
31
+ import type {LinterContextProps} from "@khanacademy/perseus-linter";
32
+ import type {PropsFor} from "@khanacademy/wonder-blocks-core";
31
33
 
32
34
  const {mapObject} = Objective;
33
35
 
34
- type OwnProps = // These props are used by the ProvideKeypad mixin.
35
- KeypadProps & {
36
- apiOptions: APIOptions;
37
- hintsVisible?: number;
38
- item: {
39
- hints: ReadonlyArray<any>;
40
- question: any;
41
- };
42
- problemNum?: number;
43
- reviewMode?: boolean;
44
- keypadElement?: KeypadAPI | null | undefined;
45
- dependencies: PerseusDependenciesV2;
36
+ type OwnProps = {
37
+ apiOptions: APIOptions;
38
+ hintsVisible?: number;
39
+ item: {
40
+ hints: ReadonlyArray<any>;
41
+ question: any;
46
42
  };
43
+ linterContext: LinterContextProps;
44
+ problemNum?: number;
45
+ reviewMode?: boolean;
46
+ keypadElement?: KeypadAPI | null | undefined;
47
+ dependencies: PerseusDependenciesV2;
48
+ };
47
49
 
48
50
  type HOCProps = {
49
51
  onRendered: (isRendered: boolean) => void;
@@ -54,6 +56,7 @@ type Props = OwnProps & HOCProps;
54
56
  type DefaultProps = {
55
57
  apiOptions: Props["apiOptions"];
56
58
  onRendered: Props["onRendered"];
59
+ linterContext: Props["linterContext"];
57
60
  };
58
61
 
59
62
  type State = {
@@ -85,6 +88,7 @@ export class ServerItemRenderer
85
88
 
86
89
  static defaultProps: DefaultProps = {
87
90
  apiOptions: {} as any, // a deep default is done in `this.update()`
91
+ linterContext: PerseusLinter.linterContextDefault,
88
92
  onRendered: (isRendered: boolean) => {},
89
93
  };
90
94
 
@@ -417,6 +421,10 @@ export class ServerItemRenderer
417
421
  content={this.props.item.question.content}
418
422
  widgets={this.props.item.question.widgets}
419
423
  images={this.props.item.question.images}
424
+ linterContext={PerseusLinter.pushContextStack(
425
+ this.props.linterContext,
426
+ "question",
427
+ )}
420
428
  {...this.props.dependencies}
421
429
  />
422
430
  </AssetContext.Provider>
@@ -428,6 +436,10 @@ export class ServerItemRenderer
428
436
  hintsVisible={this.props.hintsVisible}
429
437
  apiOptions={apiOptions}
430
438
  ref={(elem) => (this.hintsRenderer = elem)}
439
+ linterContext={PerseusLinter.pushContextStack(
440
+ this.props.linterContext,
441
+ "hints",
442
+ )}
431
443
  />
432
444
  );
433
445
 
@@ -462,7 +474,7 @@ const styles = StyleSheet.create({
462
474
 
463
475
  const ref = React.forwardRef<
464
476
  ServerItemRenderer,
465
- Omit<React.ComponentProps<typeof ServerItemRenderer>, "onRendered">
477
+ Omit<PropsFor<typeof ServerItemRenderer>, "onRendered">
466
478
  >((props, ref) => (
467
479
  <LoadingContext.Consumer>
468
480
  {({onRendered}) => (
@@ -675,30 +675,6 @@ div.timeline-total {
675
675
  width: 100px;
676
676
  }
677
677
 
678
- #previous-step {
679
- margin: 5px;
680
- cursor: pointer;
681
- float: left;
682
- width: 100px;
683
- }
684
-
685
- #next-step {
686
- margin: 5px;
687
- cursor: pointer;
688
- float: right;
689
- width: 80px;
690
- }
691
-
692
- #previous-step span {
693
- background: url(/images/previous-step.png) no-repeat 0 50% transparent;
694
- padding: 4px 0 4px 25px;
695
- }
696
-
697
- #next-step span {
698
- background: url(/images/next-step.png) no-repeat 100% 50% transparent;
699
- padding: 4px 25px 4px 0;
700
- }
701
-
702
678
  #next-problem {
703
679
  margin: 5px;
704
680
  cursor: pointer;