@khanacademy/perseus 6.3.1 → 6.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.
- package/CHANGELOG.md +25 -0
- package/dist/es/index.js +109 -11
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +109 -10
- package/dist/index.js.flow +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interactive2/movable-polygon-options.d.ts +1 -1
- package/dist/interactive2/movable.d.ts +1 -1
- package/dist/perseus-types.d.ts +2 -1
- package/dist/perseus-types.js.flow +9 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.js.flow +2 -0
- package/package.json +3 -2
- package/src/__tests__/renderer.test.tsx +0 -1
- package/src/index.ts +1 -0
- package/src/interactive2/__tests__/movable-mock.ts +0 -1
- package/src/interactive2/movable-polygon-options.ts +1 -2
- package/src/interactive2/movable.ts +1 -2
- package/src/interactive2.ts +0 -3
- package/src/perseus-types.ts +7 -1
- package/src/renderer.tsx +1 -1
- package/src/types.ts +14 -0
- package/src/widgets/__testdata__/expression.testdata.ts +1 -1
- package/src/widgets/__tests__/expression.test.ts +36 -10
- package/src/widgets/__tests__/renderQuestion.tsx +0 -1
- package/src/widgets/expression.tsx +25 -0
- package/src/widgets/matrix.tsx +2 -4
- package/tsconfig-build.json +1 -0
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -28,7 +28,7 @@ declare const _default: {
|
|
|
28
28
|
readonly bound: (range: any, snap: any, paddingPx: number) => (arg1: Coord, arg2: Coord) => Coord;
|
|
29
29
|
};
|
|
30
30
|
onMove: {
|
|
31
|
-
readonly updatePoints: (coord: Coord, prevCoord: Coord) =>
|
|
31
|
+
readonly updatePoints: (coord: Coord, prevCoord: Coord) => void;
|
|
32
32
|
};
|
|
33
33
|
onMoveEnd: {
|
|
34
34
|
standard: null;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const Movable: (graphie: any, options: any) =>
|
|
1
|
+
declare const Movable: (graphie: any, options: any) => void;
|
|
2
2
|
export default Movable;
|
package/dist/perseus-types.d.ts
CHANGED
|
@@ -140,11 +140,12 @@ export type PerseusExpressionWidgetOptions = {
|
|
|
140
140
|
times: boolean;
|
|
141
141
|
buttonsVisible?: "always" | "never" | "focused";
|
|
142
142
|
};
|
|
143
|
+
export declare const PerseusExpressionAnswerFormConsidered: readonly ["correct", "wrong", "ungraded"];
|
|
143
144
|
export type PerseusExpressionAnswerForm = {
|
|
144
145
|
value: string;
|
|
145
146
|
form: boolean;
|
|
146
147
|
simplify: boolean;
|
|
147
|
-
considered:
|
|
148
|
+
considered: typeof PerseusExpressionAnswerFormConsidered[number];
|
|
148
149
|
key?: string;
|
|
149
150
|
};
|
|
150
151
|
export type PerseusGradedGroupWidgetOptions = {
|
|
@@ -258,11 +258,19 @@ export type PerseusExpressionWidgetOptions = {|
|
|
|
258
258
|
times: boolean,
|
|
259
259
|
buttonsVisible?: "always" | "never" | "focused",
|
|
260
260
|
|};
|
|
261
|
+
declare export var PerseusExpressionAnswerFormConsidered: [
|
|
262
|
+
"correct",
|
|
263
|
+
"wrong",
|
|
264
|
+
"ungraded"
|
|
265
|
+
];
|
|
261
266
|
export type PerseusExpressionAnswerForm = {|
|
|
262
267
|
value: string,
|
|
263
268
|
form: boolean,
|
|
264
269
|
simplify: boolean,
|
|
265
|
-
considered:
|
|
270
|
+
considered: $ElementType<
|
|
271
|
+
typeof PerseusExpressionAnswerFormConsidered,
|
|
272
|
+
number
|
|
273
|
+
>,
|
|
266
274
|
key?: string,
|
|
267
275
|
|};
|
|
268
276
|
export type PerseusGradedGroupWidgetOptions = {|
|
package/dist/types.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type { ILogger } from "./logging/log";
|
|
|
4
4
|
import type { Item } from "./multi-items/item-types";
|
|
5
5
|
import type { PerseusWidget } from "./perseus-types";
|
|
6
6
|
import type { SizeClass } from "./util/sizing-utils";
|
|
7
|
+
import type { SendEventFn } from "@khanacademy/perseus-core";
|
|
7
8
|
import type { Result } from "@khanacademy/wonder-blocks-data";
|
|
8
9
|
export type FocusPath = ReadonlyArray<string> | null | undefined;
|
|
9
10
|
type State = any;
|
|
@@ -171,6 +172,7 @@ export type PerseusDependencies = {
|
|
|
171
172
|
TeX: React.ComponentType<TeXProps>;
|
|
172
173
|
staticUrl: StaticUrlFn;
|
|
173
174
|
InitialRequestUrl: InitialRequestUrlInterface;
|
|
175
|
+
analytics: SendEventFn;
|
|
174
176
|
useVideo(id: string, kind: VideoKind): Result<{
|
|
175
177
|
video: VideoData | null | undefined;
|
|
176
178
|
}>;
|
package/dist/types.js.flow
CHANGED
|
@@ -10,6 +10,7 @@ import type { ILogger } from "./logging/log";
|
|
|
10
10
|
import type { Item } from "./multi-items/item-types";
|
|
11
11
|
import type { PerseusWidget } from "./perseus-types";
|
|
12
12
|
import type { SizeClass } from "./util/sizing-utils";
|
|
13
|
+
import type { SendEventFn } from "@khanacademy/perseus-core";
|
|
13
14
|
import type { Result } from "@khanacademy/wonder-blocks-data";
|
|
14
15
|
export type FocusPath = $ReadOnlyArray<string> | null | void;
|
|
15
16
|
declare type State = any;
|
|
@@ -201,6 +202,7 @@ export type PerseusDependencies = {|
|
|
|
201
202
|
TeX: React.ComponentType<TeXProps>,
|
|
202
203
|
staticUrl: StaticUrlFn,
|
|
203
204
|
InitialRequestUrl: InitialRequestUrlInterface,
|
|
205
|
+
analytics: SendEventFn,
|
|
204
206
|
useVideo(
|
|
205
207
|
id: string,
|
|
206
208
|
kind: VideoKind
|
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.
|
|
6
|
+
"version": "6.4.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@khanacademy/kas": "^0.3.1",
|
|
25
25
|
"@khanacademy/kmath": "^0.1.1",
|
|
26
|
-
"@khanacademy/math-input": "^
|
|
26
|
+
"@khanacademy/math-input": "^5.0.0",
|
|
27
|
+
"@khanacademy/perseus-core": "0.0.1",
|
|
27
28
|
"@khanacademy/perseus-linter": "^0.3.1",
|
|
28
29
|
"@khanacademy/pure-markdown": "^0.2.3",
|
|
29
30
|
"@khanacademy/simple-markdown": "^0.9.3"
|
package/src/index.ts
CHANGED
|
@@ -65,6 +65,7 @@ export {default as KhanMath} from "./util/math";
|
|
|
65
65
|
export {default as LoadingContext} from "./loading-context";
|
|
66
66
|
export {default as mediaQueries} from "./styles/media-queries";
|
|
67
67
|
export {default as PerseusMarkdown} from "./perseus-markdown";
|
|
68
|
+
export {PerseusExpressionAnswerFormConsidered} from "./perseus-types";
|
|
68
69
|
export {traverse} from "./traversal";
|
|
69
70
|
export {isItemRenderableByVersion} from "./renderability";
|
|
70
71
|
export {violatingWidgets} from "./a11y";
|
|
@@ -7,7 +7,6 @@ import _ from "underscore";
|
|
|
7
7
|
import Movable from "../movable";
|
|
8
8
|
|
|
9
9
|
const createMock = function (): any {
|
|
10
|
-
// @ts-expect-error [FEI-5003] - TS2350 - Only a void function can be called with the 'new' keyword.
|
|
11
10
|
const movable = new Movable(null, {
|
|
12
11
|
mouseTarget: null,
|
|
13
12
|
});
|
|
@@ -363,8 +363,7 @@ const constraints = {
|
|
|
363
363
|
constraints.standard = null;
|
|
364
364
|
|
|
365
365
|
const onMove = {
|
|
366
|
-
|
|
367
|
-
updatePoints: function (coord: Coord, prevCoord: Coord): undefined {
|
|
366
|
+
updatePoints: function (coord: Coord, prevCoord: Coord): void {
|
|
368
367
|
const actualDelta = kvector.subtract(coord, prevCoord);
|
|
369
368
|
// @ts-expect-error [FEI-5003] - TS2339 - Property 'state' does not exist on type '{ readonly updatePoints: (coord: Coord, prevCoord: Coord) => undefined; }'.
|
|
370
369
|
_.each(this.state.points, function (point) {
|
|
@@ -49,8 +49,7 @@ const DEFAULT_STATE = {
|
|
|
49
49
|
mouseTarget: null,
|
|
50
50
|
} as const;
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
const Movable = function (graphie: any, options: any): undefined {
|
|
52
|
+
const Movable = function (graphie: any, options: any): void {
|
|
54
53
|
// @ts-expect-error [FEI-5003] - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
55
54
|
_.extend(this, {
|
|
56
55
|
graphie: graphie,
|
package/src/interactive2.ts
CHANGED
|
@@ -7,19 +7,16 @@ import KhanColors from "./util/colors";
|
|
|
7
7
|
const Interactive2 = {
|
|
8
8
|
MovablePoint: MovablePoint,
|
|
9
9
|
addMovablePoint: function (graphie: any, options: any): any {
|
|
10
|
-
// @ts-expect-error [FEI-5003] - TS2350 - Only a void function can be called with the 'new' keyword.
|
|
11
10
|
const movable = new Movable(graphie, {});
|
|
12
11
|
return new MovablePoint(graphie, movable, options);
|
|
13
12
|
},
|
|
14
13
|
MovableLine: MovableLine,
|
|
15
14
|
addMovableLine: function (graphie: any, options: any): any {
|
|
16
|
-
// @ts-expect-error [FEI-5003] - TS2350 - Only a void function can be called with the 'new' keyword.
|
|
17
15
|
const movable = new Movable(graphie, {});
|
|
18
16
|
return new MovableLine(graphie, movable, options);
|
|
19
17
|
},
|
|
20
18
|
MovablePolygon: MovablePolygon,
|
|
21
19
|
addMovablePolygon: function (graphie: any, options: any): any {
|
|
22
|
-
// @ts-expect-error [FEI-5003] - TS2350 - Only a void function can be called with the 'new' keyword.
|
|
23
20
|
const movable = new Movable(graphie, {});
|
|
24
21
|
return new MovablePolygon(graphie, movable, options);
|
|
25
22
|
},
|
package/src/perseus-types.ts
CHANGED
|
@@ -327,6 +327,12 @@ export type PerseusExpressionWidgetOptions = {
|
|
|
327
327
|
buttonsVisible?: "always" | "never" | "focused";
|
|
328
328
|
};
|
|
329
329
|
|
|
330
|
+
export const PerseusExpressionAnswerFormConsidered = [
|
|
331
|
+
"correct",
|
|
332
|
+
"wrong",
|
|
333
|
+
"ungraded",
|
|
334
|
+
] as const;
|
|
335
|
+
|
|
330
336
|
export type PerseusExpressionAnswerForm = {
|
|
331
337
|
// The Katex form of the expression. e.g. "x\\cdot3=y"
|
|
332
338
|
value: string;
|
|
@@ -335,7 +341,7 @@ export type PerseusExpressionAnswerForm = {
|
|
|
335
341
|
// The answer expression must be fully expanded and simplified
|
|
336
342
|
simplify: boolean;
|
|
337
343
|
// Whether the form is considered "correct", "wrong", or "ungraded"
|
|
338
|
-
considered:
|
|
344
|
+
considered: typeof PerseusExpressionAnswerFormConsidered[number];
|
|
339
345
|
// A key to identify the answer form in a list
|
|
340
346
|
// NOTE: perseus-format.js says this is required even though it isn't necessary.
|
|
341
347
|
key?: string;
|
package/src/renderer.tsx
CHANGED
|
@@ -1770,7 +1770,7 @@ class Renderer extends React.Component<Props, State> {
|
|
|
1770
1770
|
return widgetIsGraded && !widgetIsStatic;
|
|
1771
1771
|
});
|
|
1772
1772
|
|
|
1773
|
-
const widgetScores: Record<string,
|
|
1773
|
+
const widgetScores: Record<string, PerseusScore> = {};
|
|
1774
1774
|
_.each(gradedWidgetIds, (id) => {
|
|
1775
1775
|
const props = widgetProps[id];
|
|
1776
1776
|
const widget = this.getWidgetInstance(id);
|
package/src/types.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type {ILogger} from "./logging/log";
|
|
|
5
5
|
import type {Item} from "./multi-items/item-types";
|
|
6
6
|
import type {PerseusWidget} from "./perseus-types";
|
|
7
7
|
import type {SizeClass} from "./util/sizing-utils";
|
|
8
|
+
import type {SendEventFn} from "@khanacademy/perseus-core";
|
|
8
9
|
import type {Result} from "@khanacademy/wonder-blocks-data";
|
|
9
10
|
|
|
10
11
|
export type FocusPath = ReadonlyArray<string> | null | undefined;
|
|
@@ -128,6 +129,7 @@ export type Path = ReadonlyArray<string>;
|
|
|
128
129
|
|
|
129
130
|
type StubTagEditorType = any; // from "./components/stub-tag-editor";
|
|
130
131
|
|
|
132
|
+
// APIOptions provides different ways to customize the behaviour of Perseus.
|
|
131
133
|
export type APIOptions = Readonly<{
|
|
132
134
|
isArticle?: boolean;
|
|
133
135
|
// This should actually be required since renderer.jsx sets defaults for
|
|
@@ -299,16 +301,26 @@ export type VideoKind = "YOUTUBE_ID" | "READABLE_ID";
|
|
|
299
301
|
// An object for dependency injection, to allow different clients
|
|
300
302
|
// to provide different methods for logging, translation, network
|
|
301
303
|
// requests, etc.
|
|
304
|
+
//
|
|
305
|
+
// NOTE: You should avoid adding new dependencies here as this type was added
|
|
306
|
+
// as a quick fix to get around the fact that some of the dependencies Perseus
|
|
307
|
+
// needs are used in places where neither `APIOptions` nor a React Context
|
|
308
|
+
// could be used. Aim to shrink the footprint of PerseusDependencies and try to
|
|
309
|
+
// use alternative methods where possible.
|
|
302
310
|
export type PerseusDependencies = {
|
|
303
311
|
// JIPT
|
|
304
312
|
JIPT: JIPT;
|
|
305
313
|
graphieMovablesJiptLabels: JiptLabelStore;
|
|
306
314
|
svgImageJiptLabels: JiptLabelStore;
|
|
307
315
|
rendererTranslationComponents: JiptTranslationComponents;
|
|
316
|
+
|
|
308
317
|
TeX: React.ComponentType<TeXProps>;
|
|
318
|
+
|
|
309
319
|
//misc
|
|
310
320
|
staticUrl: StaticUrlFn;
|
|
311
321
|
InitialRequestUrl: InitialRequestUrlInterface;
|
|
322
|
+
analytics: SendEventFn;
|
|
323
|
+
|
|
312
324
|
// video widget
|
|
313
325
|
// This is used as a hook to fetch data about a video which is used to
|
|
314
326
|
// add a link to the video transcript. The return value conforms to
|
|
@@ -320,7 +332,9 @@ export type PerseusDependencies = {
|
|
|
320
332
|
): Result<{
|
|
321
333
|
video: VideoData | null | undefined;
|
|
322
334
|
}>;
|
|
335
|
+
|
|
323
336
|
Log: ILogger;
|
|
337
|
+
|
|
324
338
|
// RequestInfo
|
|
325
339
|
isDevServer: boolean;
|
|
326
340
|
kaLocale: string;
|
|
@@ -5,7 +5,7 @@ import "@testing-library/jest-dom";
|
|
|
5
5
|
|
|
6
6
|
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
7
7
|
import * as Dependencies from "../../dependencies";
|
|
8
|
-
import {
|
|
8
|
+
import {PerseusItem} from "../../perseus-types";
|
|
9
9
|
import {
|
|
10
10
|
expressionItem2,
|
|
11
11
|
expressionItem3,
|
|
@@ -15,47 +15,73 @@ import {Expression} from "../expression";
|
|
|
15
15
|
|
|
16
16
|
import {renderQuestion} from "./renderQuestion";
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const assertComplete = (itemData: Item, input, isCorrect: boolean) => {
|
|
18
|
+
const assertComplete = (itemData: PerseusItem, input, isCorrect: boolean) => {
|
|
21
19
|
const {renderer} = renderQuestion(itemData.question);
|
|
22
20
|
userEvent.type(screen.getByRole("textbox"), input);
|
|
23
21
|
const [_, score] = renderer.guessAndScore();
|
|
22
|
+
|
|
24
23
|
expect(score).toMatchObject({
|
|
25
24
|
type: "points",
|
|
26
25
|
earned: isCorrect ? 1 : 0,
|
|
27
26
|
});
|
|
28
27
|
};
|
|
29
28
|
|
|
30
|
-
const assertCorrect = (itemData:
|
|
29
|
+
const assertCorrect = (itemData: PerseusItem, input) => {
|
|
31
30
|
assertComplete(itemData, input, true);
|
|
32
|
-
|
|
31
|
+
|
|
32
|
+
expect(testDependencies.analytics).toHaveBeenCalledWith({
|
|
33
|
+
type: "perseus:expression-evaluated",
|
|
34
|
+
payload: {
|
|
35
|
+
virtualKeypadVersion: "PERSEUS_MATH_INPUT",
|
|
36
|
+
result: "correct",
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const assertIncorrect = (itemData: PerseusItem, input: string) => {
|
|
33
42
|
assertComplete(itemData, input, false);
|
|
34
43
|
|
|
44
|
+
expect(testDependencies.analytics).toHaveBeenCalledWith({
|
|
45
|
+
type: "perseus:expression-evaluated",
|
|
46
|
+
payload: {
|
|
47
|
+
virtualKeypadVersion: "PERSEUS_MATH_INPUT",
|
|
48
|
+
result: "incorrect",
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
35
53
|
// TODO: actually Assert that message is being set on the score object.
|
|
36
|
-
const assertInvalid = (itemData:
|
|
54
|
+
const assertInvalid = (itemData: PerseusItem, input, message?: string) => {
|
|
37
55
|
const {renderer} = renderQuestion(itemData.question);
|
|
38
56
|
if (input.length) {
|
|
39
57
|
userEvent.type(screen.getByRole("textbox"), input);
|
|
40
58
|
}
|
|
41
59
|
const [_, score] = renderer.guessAndScore();
|
|
42
60
|
expect(score).toMatchObject({type: "invalid"});
|
|
61
|
+
|
|
62
|
+
expect(testDependencies.analytics).toHaveBeenCalledWith({
|
|
63
|
+
type: "perseus:expression-evaluated",
|
|
64
|
+
payload: {
|
|
65
|
+
virtualKeypadVersion: "PERSEUS_MATH_INPUT",
|
|
66
|
+
result: "invalid",
|
|
67
|
+
},
|
|
68
|
+
});
|
|
43
69
|
};
|
|
44
70
|
|
|
45
71
|
describe("Expression Widget", function () {
|
|
46
72
|
beforeEach(() => {
|
|
73
|
+
jest.spyOn(testDependencies, "analytics");
|
|
47
74
|
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
48
75
|
testDependencies,
|
|
49
76
|
);
|
|
50
|
-
registerAllWidgetsForTesting();
|
|
51
77
|
});
|
|
52
78
|
|
|
53
79
|
describe("grading", function () {
|
|
54
|
-
it("should not grade a thing that doesn't parse",
|
|
80
|
+
it("should not grade a thing that doesn't parse", () => {
|
|
55
81
|
assertInvalid(expressionItem2, "+++");
|
|
56
82
|
});
|
|
57
83
|
|
|
58
|
-
it("should not grade a thing that is empty",
|
|
84
|
+
it("should not grade a thing that is empty", () => {
|
|
59
85
|
assertInvalid(expressionItem2, "");
|
|
60
86
|
});
|
|
61
87
|
});
|
|
@@ -12,6 +12,7 @@ import Tooltip, {
|
|
|
12
12
|
HorizontalDirection,
|
|
13
13
|
VerticalDirection,
|
|
14
14
|
} from "../components/tooltip";
|
|
15
|
+
import {getDependencies} from "../dependencies";
|
|
15
16
|
import {iconExclamationSign} from "../icon-paths";
|
|
16
17
|
import {Errors as PerseusErrors, Log} from "../logging/log";
|
|
17
18
|
import * as Changeable from "../mixins/changeable";
|
|
@@ -24,6 +25,18 @@ import type {
|
|
|
24
25
|
} from "../perseus-types";
|
|
25
26
|
import type {PerseusScore, WidgetExports, WidgetProps} from "../types";
|
|
26
27
|
|
|
28
|
+
const sendExpressionEvaluatedEvent = (
|
|
29
|
+
result: "correct" | "incorrect" | "invalid",
|
|
30
|
+
) => {
|
|
31
|
+
getDependencies().analytics({
|
|
32
|
+
type: "perseus:expression-evaluated",
|
|
33
|
+
payload: {
|
|
34
|
+
virtualKeypadVersion: "PERSEUS_MATH_INPUT", // STOPSHIP: Or do we push that onto host application?
|
|
35
|
+
result,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
27
40
|
type InputPath = ReadonlyArray<string>;
|
|
28
41
|
|
|
29
42
|
const ERROR_MESSAGE = i18n._("Sorry, I don't understand that!");
|
|
@@ -198,6 +211,8 @@ export class Expression extends React.Component<Props, ExpressionState> {
|
|
|
198
211
|
// we did, whether it's considered correct, incorrect, or ungraded
|
|
199
212
|
if (!matchingAnswerForm) {
|
|
200
213
|
if (firstUngradedResult) {
|
|
214
|
+
sendExpressionEvaluatedEvent("invalid");
|
|
215
|
+
|
|
201
216
|
// While we didn't directly match with any answer form, we
|
|
202
217
|
// did at some point get an "ungraded" validation result,
|
|
203
218
|
// which might indicate e.g. a mismatch in variable casing.
|
|
@@ -212,6 +227,8 @@ export class Expression extends React.Component<Props, ExpressionState> {
|
|
|
212
227
|
};
|
|
213
228
|
}
|
|
214
229
|
if (allEmpty) {
|
|
230
|
+
sendExpressionEvaluatedEvent("invalid");
|
|
231
|
+
|
|
215
232
|
// If everything graded as empty, it's invalid.
|
|
216
233
|
return {
|
|
217
234
|
type: "invalid",
|
|
@@ -220,6 +237,7 @@ export class Expression extends React.Component<Props, ExpressionState> {
|
|
|
220
237
|
}
|
|
221
238
|
// We fell through all the possibilities and we're not empty,
|
|
222
239
|
// so the answer is considered incorrect.
|
|
240
|
+
sendExpressionEvaluatedEvent("incorrect");
|
|
223
241
|
return {
|
|
224
242
|
type: "points",
|
|
225
243
|
earned: 0,
|
|
@@ -234,6 +252,7 @@ export class Expression extends React.Component<Props, ExpressionState> {
|
|
|
234
252
|
userInput,
|
|
235
253
|
matchMessage,
|
|
236
254
|
);
|
|
255
|
+
sendExpressionEvaluatedEvent("invalid");
|
|
237
256
|
return {
|
|
238
257
|
type: "invalid",
|
|
239
258
|
message: apiResult === false ? null : matchMessage,
|
|
@@ -245,6 +264,12 @@ export class Expression extends React.Component<Props, ExpressionState> {
|
|
|
245
264
|
// TODO(eater): Seems silly to translate result to this
|
|
246
265
|
// invalid/points thing and immediately translate it back in
|
|
247
266
|
// ItemRenderer.scoreInput()
|
|
267
|
+
sendExpressionEvaluatedEvent(
|
|
268
|
+
matchingAnswerForm.considered === "correct"
|
|
269
|
+
? "correct"
|
|
270
|
+
: "incorrect",
|
|
271
|
+
);
|
|
272
|
+
|
|
248
273
|
return {
|
|
249
274
|
type: "points",
|
|
250
275
|
earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
|
package/src/widgets/matrix.tsx
CHANGED
|
@@ -503,16 +503,14 @@ class Matrix extends React.Component<Props, State> {
|
|
|
503
503
|
value,
|
|
504
504
|
cb,
|
|
505
505
|
) => {
|
|
506
|
-
const answers =
|
|
506
|
+
const answers = this.props.answers.map((answer) => [...answer]);
|
|
507
507
|
if (!answers[row]) {
|
|
508
508
|
answers[row] = [];
|
|
509
509
|
}
|
|
510
|
-
// @ts-expect-error [FEI-5003] - TS2571 - Object is of type 'unknown'.
|
|
511
510
|
answers[row][column] = value;
|
|
512
|
-
// @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call.
|
|
513
511
|
this.props.onChange(
|
|
514
512
|
{
|
|
515
|
-
answers
|
|
513
|
+
answers,
|
|
516
514
|
},
|
|
517
515
|
cb,
|
|
518
516
|
);
|
package/tsconfig-build.json
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
{"path": "../kas/tsconfig-build.json"},
|
|
18
18
|
{"path": "../kmath/tsconfig-build.json"},
|
|
19
19
|
{"path": "../math-input/tsconfig-build.json"},
|
|
20
|
+
{"path": "../perseus-core/tsconfig-build.json"},
|
|
20
21
|
{"path": "../perseus-linter/tsconfig-build.json"},
|
|
21
22
|
{"path": "../pure-markdown/tsconfig-build.json"},
|
|
22
23
|
{"path": "../simple-markdown/tsconfig-build.json"},
|