@khanacademy/perseus 13.0.0 → 13.2.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 +32 -0
- package/dist/es/index.css +0 -7
- package/dist/es/index.js +13 -1
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +0 -7
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/renderer.d.ts +2 -2
- package/dist/widgets/matcher.d.ts +1 -1
- package/package.json +35 -35
- package/src/__stories__/hints-renderer.stories.tsx +41 -44
- package/src/__tests__/__snapshots__/renderer.test.tsx.snap +9 -33
- package/src/__tests__/renderer-api.test.tsx +35 -52
- package/src/renderer.tsx +2 -2
- package/src/styles/khan-exercise.css +0 -2
- package/src/styles/perseus-renderer.less +0 -7
- package/src/widgets/__stories__/video.stories.tsx +9 -1
- package/src/widgets/__testdata__/video.testdata.ts +40 -0
- package/src/widgets/__tests__/__snapshots__/dropdown.test.ts.snap +6 -22
- package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt.test.ts.snap +3 -3
- package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +1 -1
- package/src/widgets/__tests__/__snapshots__/graded-group.test.ts.snap +1 -1
- package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +4 -4
- package/src/widgets/__tests__/matcher.test.tsx +1 -1
- package/src/widgets/__tests__/video.test.ts +17 -1
- package/src/widgets/matcher.tsx +1 -1
- package/src/widgets/radio/choice.tsx +1 -0
- package/src/widgets/video.tsx +11 -0
- package/tsconfig-build.tsbuildinfo +1 -1
package/dist/renderer.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ export type Widget = {
|
|
|
33
33
|
blurInputPath?: (path: FocusPath) => null;
|
|
34
34
|
focusInputPath?: (path: FocusPath) => null;
|
|
35
35
|
getInputPaths?: () => ReadonlyArray<FocusPath>;
|
|
36
|
-
setInputValue?: (path: FocusPath, newValue: string, focus
|
|
36
|
+
setInputValue?: (path: FocusPath, newValue: string, focus?: () => unknown) => void;
|
|
37
37
|
getUserInput?: () => WidgetUserInput | null | undefined;
|
|
38
38
|
simpleValidate?: (options?: any, onOutputError?: (widgetId: any, value: string, message?: string | null | undefined) => unknown | null | undefined) => PerseusScore;
|
|
39
39
|
showRationalesForCurrentlySelectedChoices?: (options?: any) => void;
|
|
@@ -210,7 +210,7 @@ declare class Renderer extends React.Component<Props, State> {
|
|
|
210
210
|
serialize: () => Record<any, any>;
|
|
211
211
|
emptyWidgets: () => any;
|
|
212
212
|
_setWidgetProps: SetWidgetPropsFn;
|
|
213
|
-
setInputValue: (path: FocusPath, newValue: string, focus
|
|
213
|
+
setInputValue: (path: FocusPath, newValue: string, focus?: () => unknown) => void;
|
|
214
214
|
/**
|
|
215
215
|
* Returns an array of the widget `.getUserInput()` results
|
|
216
216
|
*/
|
|
@@ -6,7 +6,7 @@ type State = {
|
|
|
6
6
|
rightHeight: number;
|
|
7
7
|
texRendererLoaded: boolean;
|
|
8
8
|
};
|
|
9
|
-
declare class Matcher extends React.Component<any, any> {
|
|
9
|
+
export declare class Matcher extends React.Component<any, any> {
|
|
10
10
|
static propTypes: {
|
|
11
11
|
apiOptions: PropTypes.Validator<NonNullable<PropTypes.InferProps<{
|
|
12
12
|
isArticle: PropTypes.Validator<boolean>;
|
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": "13.
|
|
6
|
+
"version": "13.2.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -24,29 +24,29 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@khanacademy/kas": "^0.3.5",
|
|
26
26
|
"@khanacademy/kmath": "^0.1.6",
|
|
27
|
-
"@khanacademy/math-input": "^15.
|
|
27
|
+
"@khanacademy/math-input": "^15.1.0",
|
|
28
28
|
"@khanacademy/perseus-core": "1.3.0",
|
|
29
29
|
"@khanacademy/perseus-linter": "^0.3.8",
|
|
30
|
-
"@khanacademy/pure-markdown": "^0.2.
|
|
31
|
-
"@khanacademy/simple-markdown": "^0.
|
|
30
|
+
"@khanacademy/pure-markdown": "^0.2.11",
|
|
31
|
+
"@khanacademy/simple-markdown": "^0.11.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@khanacademy/wonder-blocks-button": "^
|
|
35
|
-
"@khanacademy/wonder-blocks-clickable": "^4.0.
|
|
36
|
-
"@khanacademy/wonder-blocks-color": "^
|
|
37
|
-
"@khanacademy/wonder-blocks-core": "^6.
|
|
38
|
-
"@khanacademy/wonder-blocks-data": "^
|
|
39
|
-
"@khanacademy/wonder-blocks-dropdown": "^3.
|
|
40
|
-
"@khanacademy/wonder-blocks-form": "^4.3.
|
|
34
|
+
"@khanacademy/wonder-blocks-button": "^6.2.2",
|
|
35
|
+
"@khanacademy/wonder-blocks-clickable": "^4.0.11",
|
|
36
|
+
"@khanacademy/wonder-blocks-color": "^3.0.0",
|
|
37
|
+
"@khanacademy/wonder-blocks-core": "^6.3.0",
|
|
38
|
+
"@khanacademy/wonder-blocks-data": "^13.0.5",
|
|
39
|
+
"@khanacademy/wonder-blocks-dropdown": "^3.1.8",
|
|
40
|
+
"@khanacademy/wonder-blocks-form": "^4.3.16",
|
|
41
41
|
"@khanacademy/wonder-blocks-i18n": "^2.0.2",
|
|
42
|
-
"@khanacademy/wonder-blocks-layout": "^2.0.
|
|
43
|
-
"@khanacademy/wonder-blocks-link": "^
|
|
44
|
-
"@khanacademy/wonder-blocks-modal": "^4.0.
|
|
45
|
-
"@khanacademy/wonder-blocks-popover": "^3.0.
|
|
46
|
-
"@khanacademy/wonder-blocks-progress-spinner": "^2.0.
|
|
47
|
-
"@khanacademy/wonder-blocks-spacing": "^
|
|
48
|
-
"@khanacademy/wonder-blocks-tooltip": "^2.1.
|
|
49
|
-
"@khanacademy/wonder-blocks-typography": "^2.1.
|
|
42
|
+
"@khanacademy/wonder-blocks-layout": "^2.0.24",
|
|
43
|
+
"@khanacademy/wonder-blocks-link": "^6.0.2",
|
|
44
|
+
"@khanacademy/wonder-blocks-modal": "^4.0.35",
|
|
45
|
+
"@khanacademy/wonder-blocks-popover": "^3.0.16",
|
|
46
|
+
"@khanacademy/wonder-blocks-progress-spinner": "^2.0.24",
|
|
47
|
+
"@khanacademy/wonder-blocks-spacing": "^4.0.1",
|
|
48
|
+
"@khanacademy/wonder-blocks-tooltip": "^2.1.18",
|
|
49
|
+
"@khanacademy/wonder-blocks-typography": "^2.1.9",
|
|
50
50
|
"@khanacademy/wonder-stuff-core": "^1.5.1",
|
|
51
51
|
"@phosphor-icons/core": "^2.0.2",
|
|
52
52
|
"aphrodite": "^1.2.5",
|
|
@@ -59,29 +59,29 @@
|
|
|
59
59
|
"katex": "^0.11.1",
|
|
60
60
|
"lodash.debounce": "^4.0.8",
|
|
61
61
|
"mathquill": "git+https://git@github.com/Khan/mathquill.git#48410e80d760bbd5105544d4a4ab459a28dc2cbc",
|
|
62
|
-
"perseus-build-settings": "^0.
|
|
62
|
+
"perseus-build-settings": "^0.3.0",
|
|
63
63
|
"prop-types": "^15.6.1",
|
|
64
64
|
"react": "16.14.0",
|
|
65
65
|
"react-dom": "16.14.0",
|
|
66
66
|
"underscore": "^1.4.4"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
|
-
"@khanacademy/wonder-blocks-button": "^
|
|
70
|
-
"@khanacademy/wonder-blocks-clickable": "^4.0.
|
|
71
|
-
"@khanacademy/wonder-blocks-color": "^
|
|
72
|
-
"@khanacademy/wonder-blocks-core": "^6.
|
|
73
|
-
"@khanacademy/wonder-blocks-data": "^
|
|
74
|
-
"@khanacademy/wonder-blocks-dropdown": "^3.
|
|
75
|
-
"@khanacademy/wonder-blocks-form": "^4.3.
|
|
69
|
+
"@khanacademy/wonder-blocks-button": "^6.2.2",
|
|
70
|
+
"@khanacademy/wonder-blocks-clickable": "^4.0.11",
|
|
71
|
+
"@khanacademy/wonder-blocks-color": "^3.0.0",
|
|
72
|
+
"@khanacademy/wonder-blocks-core": "^6.3.0",
|
|
73
|
+
"@khanacademy/wonder-blocks-data": "^13.0.5",
|
|
74
|
+
"@khanacademy/wonder-blocks-dropdown": "^3.1.8",
|
|
75
|
+
"@khanacademy/wonder-blocks-form": "^4.3.16",
|
|
76
76
|
"@khanacademy/wonder-blocks-i18n": "^2.0.2",
|
|
77
|
-
"@khanacademy/wonder-blocks-layout": "^2.0.
|
|
78
|
-
"@khanacademy/wonder-blocks-link": "^
|
|
79
|
-
"@khanacademy/wonder-blocks-modal": "^4.0.
|
|
80
|
-
"@khanacademy/wonder-blocks-popover": "^3.0.
|
|
81
|
-
"@khanacademy/wonder-blocks-progress-spinner": "^2.0.
|
|
82
|
-
"@khanacademy/wonder-blocks-spacing": "^
|
|
83
|
-
"@khanacademy/wonder-blocks-tooltip": "^2.1.
|
|
84
|
-
"@khanacademy/wonder-blocks-typography": "^2.1.
|
|
77
|
+
"@khanacademy/wonder-blocks-layout": "^2.0.24",
|
|
78
|
+
"@khanacademy/wonder-blocks-link": "^6.0.2",
|
|
79
|
+
"@khanacademy/wonder-blocks-modal": "^4.0.35",
|
|
80
|
+
"@khanacademy/wonder-blocks-popover": "^3.0.16",
|
|
81
|
+
"@khanacademy/wonder-blocks-progress-spinner": "^2.0.24",
|
|
82
|
+
"@khanacademy/wonder-blocks-spacing": "^4.0.1",
|
|
83
|
+
"@khanacademy/wonder-blocks-tooltip": "^2.1.18",
|
|
84
|
+
"@khanacademy/wonder-blocks-typography": "^2.1.9",
|
|
85
85
|
"@khanacademy/wonder-stuff-core": "^1.5.1",
|
|
86
86
|
"@phosphor-icons/core": "^2.0.2",
|
|
87
87
|
"aphrodite": "^1.2.5",
|
|
@@ -3,56 +3,53 @@ import React from "react";
|
|
|
3
3
|
|
|
4
4
|
import HintsRenderer from "../hints-renderer";
|
|
5
5
|
|
|
6
|
-
type
|
|
7
|
-
hints: ReadonlyArray<any>;
|
|
8
|
-
hintsVisible: number;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
type Story = {
|
|
12
|
-
title: string;
|
|
13
|
-
argTypes: any;
|
|
14
|
-
};
|
|
6
|
+
import type {Meta, StoryObj} from "@storybook/react";
|
|
15
7
|
|
|
16
|
-
|
|
8
|
+
const meta: Meta<typeof HintsRenderer> = {
|
|
17
9
|
title: "Perseus/Renderers/Hints Renderer",
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
widgets: {},
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
content: "this is hint 2",
|
|
30
|
-
images: {},
|
|
31
|
-
replace: false,
|
|
32
|
-
widgets: {},
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
content: "this is hint 3",
|
|
36
|
-
images: {},
|
|
37
|
-
replace: false,
|
|
38
|
-
widgets: {},
|
|
39
|
-
},
|
|
40
|
-
],
|
|
10
|
+
component: HintsRenderer,
|
|
11
|
+
decorators: [
|
|
12
|
+
(Story) => {
|
|
13
|
+
return (
|
|
14
|
+
<View style={{left: 80}}>
|
|
15
|
+
<Story />
|
|
16
|
+
</View>
|
|
17
|
+
);
|
|
41
18
|
},
|
|
19
|
+
],
|
|
20
|
+
argTypes: {
|
|
42
21
|
hintsVisible: {
|
|
43
|
-
control: {
|
|
22
|
+
control: {min: 0},
|
|
44
23
|
defaultValue: 3,
|
|
45
24
|
},
|
|
46
25
|
},
|
|
47
|
-
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default meta;
|
|
48
29
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
30
|
+
type Story = StoryObj<typeof HintsRenderer>;
|
|
31
|
+
|
|
32
|
+
export const Interactive: Story = {
|
|
33
|
+
args: {
|
|
34
|
+
hints: [
|
|
35
|
+
{
|
|
36
|
+
content: "this is hint 1",
|
|
37
|
+
images: {},
|
|
38
|
+
replace: false,
|
|
39
|
+
widgets: {},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
content: "this is hint 2",
|
|
43
|
+
images: {},
|
|
44
|
+
replace: false,
|
|
45
|
+
widgets: {},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
content: "this is hint 3",
|
|
49
|
+
images: {},
|
|
50
|
+
replace: false,
|
|
51
|
+
widgets: {},
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
58
55
|
};
|
|
@@ -367,18 +367,10 @@ exports[`renderer snapshots correct answer: correct answer 1`] = `
|
|
|
367
367
|
>
|
|
368
368
|
less than or equal to
|
|
369
369
|
</span>
|
|
370
|
-
<
|
|
370
|
+
<span
|
|
371
371
|
aria-hidden="true"
|
|
372
|
-
class="svg_aeiopc-o_O-caret_ntkmq"
|
|
373
|
-
|
|
374
|
-
viewBox="0 0 16 16"
|
|
375
|
-
width="16"
|
|
376
|
-
>
|
|
377
|
-
<path
|
|
378
|
-
d="M8 8.586l3.293-3.293a1 1 0 0 1 1.414 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 1.414-1.414L8 8.586z"
|
|
379
|
-
fill="rgba(33,36,44,0.64)"
|
|
380
|
-
/>
|
|
381
|
-
</svg>
|
|
372
|
+
class="svg_aeiopc-o_O-caret_ntkmq-o_O-inlineStyles_13xtep5"
|
|
373
|
+
/>
|
|
382
374
|
</button>
|
|
383
375
|
</div>
|
|
384
376
|
</div>
|
|
@@ -440,18 +432,10 @@ exports[`renderer snapshots incorrect answer: incorrect answer 1`] = `
|
|
|
440
432
|
>
|
|
441
433
|
greater than or equal to
|
|
442
434
|
</span>
|
|
443
|
-
<
|
|
435
|
+
<span
|
|
444
436
|
aria-hidden="true"
|
|
445
|
-
class="svg_aeiopc-o_O-caret_ntkmq"
|
|
446
|
-
|
|
447
|
-
viewBox="0 0 16 16"
|
|
448
|
-
width="16"
|
|
449
|
-
>
|
|
450
|
-
<path
|
|
451
|
-
d="M8 8.586l3.293-3.293a1 1 0 0 1 1.414 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 1.414-1.414L8 8.586z"
|
|
452
|
-
fill="rgba(33,36,44,0.64)"
|
|
453
|
-
/>
|
|
454
|
-
</svg>
|
|
437
|
+
class="svg_aeiopc-o_O-caret_ntkmq-o_O-inlineStyles_13xtep5"
|
|
438
|
+
/>
|
|
455
439
|
</button>
|
|
456
440
|
</div>
|
|
457
441
|
</div>
|
|
@@ -513,18 +497,10 @@ exports[`renderer snapshots initial render: initial render 1`] = `
|
|
|
513
497
|
>
|
|
514
498
|
greater/less than or equal to
|
|
515
499
|
</span>
|
|
516
|
-
<
|
|
500
|
+
<span
|
|
517
501
|
aria-hidden="true"
|
|
518
|
-
class="svg_aeiopc-o_O-caret_ntkmq"
|
|
519
|
-
|
|
520
|
-
viewBox="0 0 16 16"
|
|
521
|
-
width="16"
|
|
522
|
-
>
|
|
523
|
-
<path
|
|
524
|
-
d="M8 8.586l3.293-3.293a1 1 0 0 1 1.414 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 1.414-1.414L8 8.586z"
|
|
525
|
-
fill="rgba(33,36,44,0.64)"
|
|
526
|
-
/>
|
|
527
|
-
</svg>
|
|
502
|
+
class="svg_aeiopc-o_O-caret_ntkmq-o_O-inlineStyles_13xtep5"
|
|
503
|
+
/>
|
|
528
504
|
</button>
|
|
529
505
|
</div>
|
|
530
506
|
</div>
|
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import {describe, beforeEach, it} from "@jest/globals";
|
|
2
|
-
import {RenderStateRoot} from "@khanacademy/wonder-blocks-core";
|
|
3
|
-
import {mount} from "enzyme"; // eslint-disable-line no-restricted-imports
|
|
4
2
|
import $ from "jquery";
|
|
5
3
|
import * as React from "react";
|
|
6
4
|
import ReactDOM from "react-dom";
|
|
7
5
|
import TestUtils from "react-dom/test-utils";
|
|
8
6
|
import _ from "underscore";
|
|
9
|
-
import "jest-enzyme";
|
|
10
7
|
|
|
11
8
|
import {testDependencies} from "../../../../testing/test-dependencies";
|
|
12
9
|
import * as Dependencies from "../dependencies";
|
|
13
10
|
import {ClassNames} from "../perseus-api";
|
|
14
11
|
import Renderer from "../renderer";
|
|
15
12
|
import {registerAllWidgetsForTesting} from "../util/register-all-widgets-for-testing";
|
|
13
|
+
import {renderQuestion} from "../widgets/__tests__/renderQuestion";
|
|
16
14
|
|
|
17
15
|
import imageItem from "./test-items/image-item";
|
|
18
16
|
import inputNumber1Item from "./test-items/input-number-1-item";
|
|
19
17
|
import inputNumber2Item from "./test-items/input-number-2-item";
|
|
20
18
|
import tableItem from "./test-items/table-item";
|
|
21
19
|
|
|
22
|
-
import type {APIOptions} from "../types";
|
|
23
|
-
|
|
24
20
|
const itemWidget = inputNumber1Item;
|
|
25
21
|
|
|
26
22
|
// Returns a promise that will resolve shortly after the end of this
|
|
@@ -33,26 +29,6 @@ const delayedPromise = (value: undefined) => {
|
|
|
33
29
|
return deferred.promise();
|
|
34
30
|
};
|
|
35
31
|
|
|
36
|
-
const renderQuestionArea = function (item, apiOptions?: APIOptions): any {
|
|
37
|
-
const wrapper = mount(
|
|
38
|
-
<RenderStateRoot>
|
|
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>
|
|
50
|
-
</RenderStateRoot>,
|
|
51
|
-
{includeDefaultTestHarness: false},
|
|
52
|
-
);
|
|
53
|
-
return wrapper.childAt(0).instance();
|
|
54
|
-
};
|
|
55
|
-
|
|
56
32
|
describe("Perseus API", function () {
|
|
57
33
|
beforeEach(() => {
|
|
58
34
|
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
@@ -63,34 +39,40 @@ describe("Perseus API", function () {
|
|
|
63
39
|
|
|
64
40
|
describe("setInputValue", function () {
|
|
65
41
|
it("should be able to produce a correctly graded value", function () {
|
|
66
|
-
|
|
67
|
-
renderer
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
42
|
+
// Arrange
|
|
43
|
+
const {renderer} = renderQuestion(inputNumber1Item.question);
|
|
44
|
+
|
|
45
|
+
// Act
|
|
46
|
+
renderer.setInputValue(["input-number 1"], "5", () => undefined);
|
|
47
|
+
|
|
48
|
+
// Assert
|
|
49
|
+
expect(renderer).toHaveBeenAnsweredCorrectly();
|
|
71
50
|
});
|
|
72
51
|
|
|
73
52
|
it("should be able to produce a wrong value", function () {
|
|
74
|
-
|
|
75
|
-
renderer
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
53
|
+
// Arrange
|
|
54
|
+
const {renderer} = renderQuestion(inputNumber1Item.question);
|
|
55
|
+
|
|
56
|
+
// Act
|
|
57
|
+
renderer.setInputValue(["input-number 1"], "3");
|
|
58
|
+
|
|
59
|
+
// Assert
|
|
60
|
+
expect(renderer).toHaveBeenAnsweredIncorrectly();
|
|
79
61
|
});
|
|
80
62
|
|
|
81
63
|
it("should be able to produce an empty score", function () {
|
|
82
|
-
|
|
64
|
+
// Arrange
|
|
65
|
+
const {renderer} = renderQuestion(inputNumber1Item.question);
|
|
66
|
+
|
|
83
67
|
renderer.setInputValue(["input-number 1"], "3");
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
expect(score.earned).toBe(0);
|
|
68
|
+
expect(renderer).toHaveBeenAnsweredIncorrectly();
|
|
69
|
+
|
|
87
70
|
renderer.setInputValue(["input-number 1"], "");
|
|
88
|
-
|
|
89
|
-
expect(score.type).toBe("invalid");
|
|
71
|
+
expect(renderer).toHaveInvalidInput();
|
|
90
72
|
});
|
|
91
73
|
|
|
92
74
|
it("should be able to accept a callback", function (done) {
|
|
93
|
-
const renderer =
|
|
75
|
+
const {renderer} = renderQuestion(inputNumber1Item.question);
|
|
94
76
|
renderer.setInputValue(["input-number 1"], "3", function () {
|
|
95
77
|
const guess = renderer.getUserInput()[0];
|
|
96
78
|
expect(guess.currentValue).toBe("3");
|
|
@@ -102,13 +84,13 @@ describe("Perseus API", function () {
|
|
|
102
84
|
|
|
103
85
|
describe("getInputPaths", function () {
|
|
104
86
|
it("should be able to find all the input widgets", function () {
|
|
105
|
-
const renderer =
|
|
87
|
+
const {renderer} = renderQuestion(inputNumber2Item.question);
|
|
106
88
|
const numPaths = renderer.getInputPaths().length;
|
|
107
89
|
expect(numPaths).toBe(2);
|
|
108
90
|
});
|
|
109
91
|
|
|
110
92
|
it("should be able to find all inputs within widgets", function () {
|
|
111
|
-
const renderer =
|
|
93
|
+
const {renderer} = renderQuestion(tableItem.question);
|
|
112
94
|
const numPaths = renderer.getInputPaths().length;
|
|
113
95
|
expect(numPaths).toBe(8);
|
|
114
96
|
});
|
|
@@ -116,7 +98,7 @@ describe("Perseus API", function () {
|
|
|
116
98
|
|
|
117
99
|
describe("getDOMNodeForPath", function () {
|
|
118
100
|
it("should find one DOM node per <input>", function () {
|
|
119
|
-
const renderer =
|
|
101
|
+
const {renderer} = renderQuestion(inputNumber2Item.question);
|
|
120
102
|
const inputPaths = renderer.getInputPaths();
|
|
121
103
|
const allInputs = TestUtils.scryRenderedDOMComponentsWithTag(
|
|
122
104
|
renderer,
|
|
@@ -126,13 +108,14 @@ describe("Perseus API", function () {
|
|
|
126
108
|
});
|
|
127
109
|
|
|
128
110
|
it("should find the right DOM nodes for the <input>s", function () {
|
|
129
|
-
const renderer =
|
|
111
|
+
const {renderer} = renderQuestion(inputNumber2Item.question);
|
|
130
112
|
const inputPaths = renderer.getInputPaths();
|
|
131
113
|
const allInputs = TestUtils.scryRenderedDOMComponentsWithTag(
|
|
132
114
|
renderer,
|
|
133
115
|
"input",
|
|
134
116
|
);
|
|
135
117
|
_.each(inputPaths, (inputPath, i) => {
|
|
118
|
+
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
136
119
|
const $node = $(renderer.getDOMNodeForPath(inputPath));
|
|
137
120
|
// @ts-expect-error - TS2769 - No overload matches this call.
|
|
138
121
|
const $input = $(ReactDOM.findDOMNode(allInputs[i]));
|
|
@@ -145,13 +128,13 @@ describe("Perseus API", function () {
|
|
|
145
128
|
describe("onInputError", function () {
|
|
146
129
|
it("should call a callback when grading an empty input-number", function () {
|
|
147
130
|
let wasCalled;
|
|
148
|
-
const renderer =
|
|
131
|
+
const {renderer} = renderQuestion(inputNumber1Item.question, {
|
|
149
132
|
onInputError: function (widgetId) {
|
|
150
133
|
wasCalled = true;
|
|
151
134
|
},
|
|
152
135
|
});
|
|
153
|
-
|
|
154
|
-
expect(
|
|
136
|
+
|
|
137
|
+
expect(renderer).toHaveInvalidInput();
|
|
155
138
|
expect(wasCalled).toBe(true);
|
|
156
139
|
});
|
|
157
140
|
});
|
|
@@ -164,7 +147,7 @@ describe("Perseus API", function () {
|
|
|
164
147
|
// version
|
|
165
148
|
expect(ClassNames.FOCUSED).toBe("perseus-focused");
|
|
166
149
|
|
|
167
|
-
const renderer =
|
|
150
|
+
const {renderer} = renderQuestion(inputNumber1Item.question);
|
|
168
151
|
|
|
169
152
|
const input =
|
|
170
153
|
// @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'querySelector' does not exist on type 'Element | Text'.
|
|
@@ -191,7 +174,7 @@ describe("Perseus API", function () {
|
|
|
191
174
|
let callCount = 0;
|
|
192
175
|
let newFocusResult;
|
|
193
176
|
let oldFocusResult;
|
|
194
|
-
const renderer =
|
|
177
|
+
const {renderer} = renderQuestion(inputNumber1Item.question, {
|
|
195
178
|
onFocusChange: function (newFocus, oldFocus) {
|
|
196
179
|
callCount++;
|
|
197
180
|
newFocusResult = newFocus;
|
|
@@ -235,7 +218,7 @@ describe("Perseus API", function () {
|
|
|
235
218
|
let callCount = 0;
|
|
236
219
|
let newFocusResult;
|
|
237
220
|
let oldFocusResult;
|
|
238
|
-
const renderer =
|
|
221
|
+
const {renderer} = renderQuestion(inputNumber2Item.question, {
|
|
239
222
|
onFocusChange: function (newFocus, oldFocus) {
|
|
240
223
|
callCount++;
|
|
241
224
|
newFocusResult = newFocus;
|
package/src/renderer.tsx
CHANGED
|
@@ -132,7 +132,7 @@ export type Widget = {
|
|
|
132
132
|
path: FocusPath,
|
|
133
133
|
newValue: string,
|
|
134
134
|
// TODO(jeremy): I think this is actually a callback
|
|
135
|
-
focus
|
|
135
|
+
focus?: () => unknown,
|
|
136
136
|
) => void;
|
|
137
137
|
getUserInput?: () => WidgetUserInput | null | undefined;
|
|
138
138
|
simpleValidate?: (
|
|
@@ -1701,7 +1701,7 @@ class Renderer extends React.Component<Props, State> {
|
|
|
1701
1701
|
setInputValue: (
|
|
1702
1702
|
path: FocusPath,
|
|
1703
1703
|
newValue: string,
|
|
1704
|
-
focus
|
|
1704
|
+
focus?: () => unknown,
|
|
1705
1705
|
) => void = (path, newValue, focus) => {
|
|
1706
1706
|
// @ts-expect-error - TS2345 - Argument of type 'FocusPath' is not assignable to parameter of type 'List<any>'.
|
|
1707
1707
|
const widgetId = _.first(path);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
3
|
import {RendererWithDebugUI} from "../../../../../testing/renderer-with-debug-ui";
|
|
4
|
-
import {question1} from "../__testdata__/video.testdata";
|
|
4
|
+
import {question1, question2, question3} from "../__testdata__/video.testdata";
|
|
5
5
|
|
|
6
6
|
export default {
|
|
7
7
|
title: "Perseus/Widgets/Video",
|
|
@@ -12,3 +12,11 @@ type StoryArgs = Record<any, any>;
|
|
|
12
12
|
export const Question1 = (args: StoryArgs): React.ReactElement => {
|
|
13
13
|
return <RendererWithDebugUI question={question1} />;
|
|
14
14
|
};
|
|
15
|
+
|
|
16
|
+
export const Question2 = (args: StoryArgs): React.ReactElement => {
|
|
17
|
+
return <RendererWithDebugUI question={question2} />;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const Question3 = (args: StoryArgs): React.ReactElement => {
|
|
21
|
+
return <RendererWithDebugUI question={question3} />;
|
|
22
|
+
};
|
|
@@ -19,3 +19,43 @@ export const question1: PerseusRenderer = {
|
|
|
19
19
|
},
|
|
20
20
|
replace: false,
|
|
21
21
|
};
|
|
22
|
+
|
|
23
|
+
export const question2: PerseusRenderer = {
|
|
24
|
+
content:
|
|
25
|
+
"Watch the WMF at Angkor Wat video to find the answer.\n\n[[\u2603 video 1]]\n\n",
|
|
26
|
+
images: {},
|
|
27
|
+
widgets: {
|
|
28
|
+
"video 1": {
|
|
29
|
+
graded: true,
|
|
30
|
+
version: {major: 0, minor: 0},
|
|
31
|
+
static: false,
|
|
32
|
+
type: "video",
|
|
33
|
+
options: {
|
|
34
|
+
static: false,
|
|
35
|
+
location: "https://player.vimeo.com/video/4754041",
|
|
36
|
+
},
|
|
37
|
+
alignment: "block",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
replace: false,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const question3: PerseusRenderer = {
|
|
44
|
+
content:
|
|
45
|
+
"Watch the WMF at Angkor Wat video to find the answer.\n\n[[\u2603 video 1]]\n\n",
|
|
46
|
+
images: {},
|
|
47
|
+
widgets: {
|
|
48
|
+
"video 1": {
|
|
49
|
+
graded: true,
|
|
50
|
+
version: {major: 0, minor: 0},
|
|
51
|
+
static: false,
|
|
52
|
+
type: "video",
|
|
53
|
+
options: {
|
|
54
|
+
static: false,
|
|
55
|
+
location: "https://player.vimeo.com/video/4754041?h=64fbc32a6e",
|
|
56
|
+
},
|
|
57
|
+
alignment: "block",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
replace: false,
|
|
61
|
+
};
|
|
@@ -40,18 +40,10 @@ exports[`Dropdown widget should snapshot when opened: dropdown open 1`] = `
|
|
|
40
40
|
>
|
|
41
41
|
greater/less than or equal to
|
|
42
42
|
</span>
|
|
43
|
-
<
|
|
43
|
+
<span
|
|
44
44
|
aria-hidden="true"
|
|
45
|
-
class="svg_aeiopc-o_O-caret_ntkmq"
|
|
46
|
-
|
|
47
|
-
viewBox="0 0 16 16"
|
|
48
|
-
width="16"
|
|
49
|
-
>
|
|
50
|
-
<path
|
|
51
|
-
d="M8 8.586l3.293-3.293a1 1 0 0 1 1.414 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 1.414-1.414L8 8.586z"
|
|
52
|
-
fill="rgba(33,36,44,0.64)"
|
|
53
|
-
/>
|
|
54
|
-
</svg>
|
|
45
|
+
class="svg_aeiopc-o_O-caret_ntkmq-o_O-inlineStyles_13xtep5"
|
|
46
|
+
/>
|
|
55
47
|
</button>
|
|
56
48
|
</div>
|
|
57
49
|
</div>
|
|
@@ -113,18 +105,10 @@ exports[`Dropdown widget should snapshot: initial render 1`] = `
|
|
|
113
105
|
>
|
|
114
106
|
greater/less than or equal to
|
|
115
107
|
</span>
|
|
116
|
-
<
|
|
108
|
+
<span
|
|
117
109
|
aria-hidden="true"
|
|
118
|
-
class="svg_aeiopc-o_O-caret_ntkmq"
|
|
119
|
-
|
|
120
|
-
viewBox="0 0 16 16"
|
|
121
|
-
width="16"
|
|
122
|
-
>
|
|
123
|
-
<path
|
|
124
|
-
d="M8 8.586l3.293-3.293a1 1 0 0 1 1.414 1.414l-4 4a1 1 0 0 1-1.414 0l-4-4a1 1 0 0 1 1.414-1.414L8 8.586z"
|
|
125
|
-
fill="rgba(33,36,44,0.64)"
|
|
126
|
-
/>
|
|
127
|
-
</svg>
|
|
110
|
+
class="svg_aeiopc-o_O-caret_ntkmq-o_O-inlineStyles_13xtep5"
|
|
111
|
+
/>
|
|
128
112
|
</button>
|
|
129
113
|
</div>
|
|
130
114
|
</div>
|