@khanacademy/perseus 15.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.
- package/CHANGELOG.md +21 -0
- package/dist/components.d.ts +0 -1
- package/dist/es/index.css +0 -90
- package/dist/es/index.js +6 -236
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +0 -90
- package/dist/index.js +6 -236
- package/dist/index.js.map +1 -1
- package/dist/server-item-renderer.d.ts +41 -1
- package/dist/strings.js +0 -23
- package/package.json +2 -2
- package/src/__stories__/server-item-renderer.stories.tsx +19 -0
- package/src/__testdata__/server-item-renderer.testdata.ts +13 -0
- package/src/__tests__/server-item-renderer.test.tsx +37 -0
- package/src/components.ts +0 -1
- package/src/server-item-renderer.tsx +15 -1
- package/src/styles/khan-exercise.css +0 -24
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/components/tex-buttons.d.ts +0 -25
- package/src/components/__stories__/text-buttons.stories.tsx +0 -54
- package/src/components/tex-buttons.tsx +0 -334
|
@@ -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
|
|
@@ -11,6 +12,7 @@ import Renderer from "./renderer";
|
|
|
11
12
|
import type { APIOptions, FocusPath, PerseusDependenciesV2 } from "./types";
|
|
12
13
|
import type { KeypadAPI } from "@khanacademy/math-input";
|
|
13
14
|
import type { KeypadContextRendererInterface, RendererInterface, KEScore } from "@khanacademy/perseus-core";
|
|
15
|
+
import type { LinterContextProps } from "@khanacademy/perseus-linter";
|
|
14
16
|
type OwnProps = {
|
|
15
17
|
apiOptions: APIOptions;
|
|
16
18
|
hintsVisible?: number;
|
|
@@ -18,6 +20,7 @@ type OwnProps = {
|
|
|
18
20
|
hints: ReadonlyArray<any>;
|
|
19
21
|
question: any;
|
|
20
22
|
};
|
|
23
|
+
linterContext: LinterContextProps;
|
|
21
24
|
problemNum?: number;
|
|
22
25
|
reviewMode?: boolean;
|
|
23
26
|
keypadElement?: KeypadAPI | null | undefined;
|
|
@@ -30,6 +33,7 @@ type Props = OwnProps & HOCProps;
|
|
|
30
33
|
type DefaultProps = {
|
|
31
34
|
apiOptions: Props["apiOptions"];
|
|
32
35
|
onRendered: Props["onRendered"];
|
|
36
|
+
linterContext: Props["linterContext"];
|
|
33
37
|
};
|
|
34
38
|
type State = {
|
|
35
39
|
questionCompleted: boolean;
|
|
@@ -100,5 +104,41 @@ export declare class ServerItemRenderer extends React.Component<Props, State> im
|
|
|
100
104
|
setAssetStatus: (assetKey: string, status: boolean) => void;
|
|
101
105
|
render(): React.ReactNode;
|
|
102
106
|
}
|
|
103
|
-
declare const ref: React.ForwardRefExoticComponent<Omit<OwnProps & HOCProps, "
|
|
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>>;
|
|
104
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": "
|
|
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.
|
|
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
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
|
+
};
|
|
@@ -158,3 +158,16 @@ export const mockedItem: PerseusItem = {
|
|
|
158
158
|
itemDataVersion: {major: 0, minor: 0},
|
|
159
159
|
answer: null,
|
|
160
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,6 +10,7 @@ import {
|
|
|
10
10
|
} from "../../../../testing/test-dependencies";
|
|
11
11
|
import {
|
|
12
12
|
itemWithInput,
|
|
13
|
+
itemWithLintingError,
|
|
13
14
|
mockedItem,
|
|
14
15
|
} from "../__testdata__/server-item-renderer.testdata";
|
|
15
16
|
import * as Dependencies from "../dependencies";
|
|
@@ -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
|
});
|
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";
|
|
@@ -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
|
|
@@ -27,6 +28,8 @@ import type {
|
|
|
27
28
|
RendererInterface,
|
|
28
29
|
KEScore,
|
|
29
30
|
} from "@khanacademy/perseus-core";
|
|
31
|
+
import type {LinterContextProps} from "@khanacademy/perseus-linter";
|
|
32
|
+
import type {PropsFor} from "@khanacademy/wonder-blocks-core";
|
|
30
33
|
|
|
31
34
|
const {mapObject} = Objective;
|
|
32
35
|
|
|
@@ -37,6 +40,7 @@ type OwnProps = {
|
|
|
37
40
|
hints: ReadonlyArray<any>;
|
|
38
41
|
question: any;
|
|
39
42
|
};
|
|
43
|
+
linterContext: LinterContextProps;
|
|
40
44
|
problemNum?: number;
|
|
41
45
|
reviewMode?: boolean;
|
|
42
46
|
keypadElement?: KeypadAPI | null | undefined;
|
|
@@ -52,6 +56,7 @@ type Props = OwnProps & HOCProps;
|
|
|
52
56
|
type DefaultProps = {
|
|
53
57
|
apiOptions: Props["apiOptions"];
|
|
54
58
|
onRendered: Props["onRendered"];
|
|
59
|
+
linterContext: Props["linterContext"];
|
|
55
60
|
};
|
|
56
61
|
|
|
57
62
|
type State = {
|
|
@@ -83,6 +88,7 @@ export class ServerItemRenderer
|
|
|
83
88
|
|
|
84
89
|
static defaultProps: DefaultProps = {
|
|
85
90
|
apiOptions: {} as any, // a deep default is done in `this.update()`
|
|
91
|
+
linterContext: PerseusLinter.linterContextDefault,
|
|
86
92
|
onRendered: (isRendered: boolean) => {},
|
|
87
93
|
};
|
|
88
94
|
|
|
@@ -415,6 +421,10 @@ export class ServerItemRenderer
|
|
|
415
421
|
content={this.props.item.question.content}
|
|
416
422
|
widgets={this.props.item.question.widgets}
|
|
417
423
|
images={this.props.item.question.images}
|
|
424
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
425
|
+
this.props.linterContext,
|
|
426
|
+
"question",
|
|
427
|
+
)}
|
|
418
428
|
{...this.props.dependencies}
|
|
419
429
|
/>
|
|
420
430
|
</AssetContext.Provider>
|
|
@@ -426,6 +436,10 @@ export class ServerItemRenderer
|
|
|
426
436
|
hintsVisible={this.props.hintsVisible}
|
|
427
437
|
apiOptions={apiOptions}
|
|
428
438
|
ref={(elem) => (this.hintsRenderer = elem)}
|
|
439
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
440
|
+
this.props.linterContext,
|
|
441
|
+
"hints",
|
|
442
|
+
)}
|
|
429
443
|
/>
|
|
430
444
|
);
|
|
431
445
|
|
|
@@ -460,7 +474,7 @@ const styles = StyleSheet.create({
|
|
|
460
474
|
|
|
461
475
|
const ref = React.forwardRef<
|
|
462
476
|
ServerItemRenderer,
|
|
463
|
-
Omit<
|
|
477
|
+
Omit<PropsFor<typeof ServerItemRenderer>, "onRendered">
|
|
464
478
|
>((props, ref) => (
|
|
465
479
|
<LoadingContext.Consumer>
|
|
466
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;
|