@khanacademy/perseus-editor 2.15.3 → 2.15.4
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 +29 -0
- package/dist/editor-page.d.ts +4 -10
- package/dist/editor.d.ts +2 -2
- package/dist/es/index.js +25 -14
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +25 -14
- package/dist/index.js.map +1 -1
- package/dist/widgets/interactive-graph-editor.d.ts +92 -92
- package/package.json +2 -2
- package/src/__stories__/editor-page.stories.tsx +65 -0
- package/src/__stories__/editor.stories.tsx +2 -4
- package/src/editor-page.tsx +5 -11
- package/src/editor.tsx +2 -2
- package/src/widgets/__stories__/categorizer-editor.stories.tsx +24 -0
- package/src/widgets/__stories__/definition-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/dropdown-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/explanation-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/input-number-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/matcher-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/number-line-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/numeric-input-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/radio-editor.stories.tsx +3 -3
- package/src/widgets/__stories__/sorter-editor.stories.tsx +18 -0
- package/src/widgets/__tests__/categorizer-editor.test.tsx +51 -0
- package/src/widgets/__tests__/definition-editor.test.tsx +41 -0
- package/src/widgets/__tests__/dropdown-editor.test.tsx +57 -0
- package/src/widgets/__tests__/explanation-editor.test.tsx +59 -0
- package/src/widgets/__tests__/input-number-editor.test.tsx +128 -0
- package/src/widgets/__tests__/matcher-editor.test.tsx +47 -0
- package/src/widgets/__tests__/number-line-editor.test.tsx +144 -0
- package/src/widgets/__tests__/numeric-input-editor.test.tsx +177 -0
- package/src/widgets/__tests__/radio-editor.test.tsx +118 -0
- package/src/widgets/__tests__/sorter-editor.test.tsx +55 -0
- package/src/widgets/dropdown-editor.tsx +1 -0
- package/src/widgets/input-number-editor.tsx +2 -0
- package/src/widgets/number-line-editor.tsx +16 -5
- package/src/widgets/numeric-input-editor.tsx +2 -0
- package/tsconfig-build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Perseus editors",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "2.15.
|
|
6
|
+
"version": "2.15.4",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@khanacademy/kas": "^0.3.7",
|
|
26
26
|
"@khanacademy/kmath": "^0.1.8",
|
|
27
|
-
"@khanacademy/perseus": "^17.
|
|
27
|
+
"@khanacademy/perseus": "^17.4.0",
|
|
28
28
|
"@khanacademy/perseus-core": "1.4.1"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {action} from "@storybook/addon-actions";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import {EditorPage} from "..";
|
|
5
|
+
import {registerAllWidgetsAndEditorsForTesting} from "../util/register-all-widgets-and-editors-for-testing";
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
DeviceType,
|
|
9
|
+
Hint,
|
|
10
|
+
PerseusAnswerArea,
|
|
11
|
+
PerseusRenderer,
|
|
12
|
+
} from "@khanacademy/perseus";
|
|
13
|
+
|
|
14
|
+
registerAllWidgetsAndEditorsForTesting(); // SIDE_EFFECTY!!!! :cry:
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
title: "Perseus/Editor/EditorPage",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const onChangeAction = action("onChange");
|
|
21
|
+
|
|
22
|
+
export const Demo = (): React.ReactElement => {
|
|
23
|
+
const [previewDevice, setPreviewDevice] =
|
|
24
|
+
React.useState<DeviceType>("phone");
|
|
25
|
+
const [jsonMode, setJsonMode] = React.useState<boolean | undefined>(false);
|
|
26
|
+
const [answerArea, setAnswerArea] = React.useState<
|
|
27
|
+
PerseusAnswerArea | undefined | null
|
|
28
|
+
>();
|
|
29
|
+
const [question, setQuestion] = React.useState<
|
|
30
|
+
PerseusRenderer | undefined
|
|
31
|
+
>();
|
|
32
|
+
const [hints, setHints] = React.useState<ReadonlyArray<Hint> | undefined>();
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<EditorPage
|
|
36
|
+
apiOptions={{isMobile: false}}
|
|
37
|
+
previewDevice={previewDevice}
|
|
38
|
+
onPreviewDeviceChange={(newDevice) => setPreviewDevice(newDevice)}
|
|
39
|
+
developerMode={true}
|
|
40
|
+
jsonMode={jsonMode}
|
|
41
|
+
answerArea={answerArea}
|
|
42
|
+
question={question}
|
|
43
|
+
hints={hints}
|
|
44
|
+
frameSource="about:blank"
|
|
45
|
+
previewURL="about:blank"
|
|
46
|
+
itemId="1"
|
|
47
|
+
onChange={(props) => {
|
|
48
|
+
onChangeAction(props);
|
|
49
|
+
|
|
50
|
+
if ("jsonMode" in props) {
|
|
51
|
+
setJsonMode(props.jsonMode);
|
|
52
|
+
}
|
|
53
|
+
if ("answerArea" in props) {
|
|
54
|
+
setAnswerArea(props.answerArea);
|
|
55
|
+
}
|
|
56
|
+
if ("question" in props) {
|
|
57
|
+
setQuestion(props.question);
|
|
58
|
+
}
|
|
59
|
+
if ("hints" in props) {
|
|
60
|
+
setHints(props.hints);
|
|
61
|
+
}
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
@@ -4,19 +4,17 @@ import {View} from "@khanacademy/wonder-blocks-core";
|
|
|
4
4
|
import {action} from "@storybook/addon-actions";
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
|
|
7
|
+
import {Editor} from "..";
|
|
7
8
|
import SideBySide from "../../../../testing/side-by-side";
|
|
8
9
|
import {question1} from "../__testdata__/input-number.testdata";
|
|
9
|
-
import Editor from "../editor";
|
|
10
10
|
import {registerAllWidgetsAndEditorsForTesting} from "../util/register-all-widgets-and-editors-for-testing";
|
|
11
11
|
|
|
12
12
|
import type {PerseusRenderer} from "@khanacademy/perseus";
|
|
13
13
|
|
|
14
|
-
import "../styles/perseus-editor.less";
|
|
15
|
-
|
|
16
14
|
registerAllWidgetsAndEditorsForTesting(); // SIDE_EFFECTY!!!! :cry:
|
|
17
15
|
|
|
18
16
|
export default {
|
|
19
|
-
title: "Perseus/Editor",
|
|
17
|
+
title: "Perseus/Editor/Editor",
|
|
20
18
|
};
|
|
21
19
|
|
|
22
20
|
export const Demo = (): React.ReactElement => {
|
package/src/editor-page.tsx
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
Hint,
|
|
16
16
|
ImageUploader,
|
|
17
17
|
Version,
|
|
18
|
+
PerseusItem,
|
|
18
19
|
} from "@khanacademy/perseus";
|
|
19
20
|
import type {KEScore} from "@khanacademy/perseus-core";
|
|
20
21
|
|
|
@@ -25,7 +26,7 @@ type Props = {
|
|
|
25
26
|
answerArea?: any; // related to the question,
|
|
26
27
|
// TODO(CP-4838): Should this be a required prop?
|
|
27
28
|
contentPaths?: ReadonlyArray<string>;
|
|
28
|
-
//
|
|
29
|
+
// "Power user" mode. Shows the raw JSON of the question.
|
|
29
30
|
developerMode: boolean;
|
|
30
31
|
// Source HTML for the iframe to render
|
|
31
32
|
frameSource: string;
|
|
@@ -53,15 +54,8 @@ type Props = {
|
|
|
53
54
|
previewURL: string;
|
|
54
55
|
};
|
|
55
56
|
|
|
56
|
-
type PerseusJson = {
|
|
57
|
-
question: any;
|
|
58
|
-
answerArea: any;
|
|
59
|
-
hints: ReadonlyArray<Hint>;
|
|
60
|
-
itemDataVersion?: Version;
|
|
61
|
-
};
|
|
62
|
-
|
|
63
57
|
type State = {
|
|
64
|
-
json:
|
|
58
|
+
json: PerseusItem;
|
|
65
59
|
gradeMessage: string;
|
|
66
60
|
wasAnswered: boolean;
|
|
67
61
|
highlightLint: boolean;
|
|
@@ -207,7 +201,7 @@ class EditorPage extends React.Component<Props, State> {
|
|
|
207
201
|
return issues1.concat(issues2);
|
|
208
202
|
}
|
|
209
203
|
|
|
210
|
-
serialize(options?: {keepDeletedWidgets?: boolean}): any |
|
|
204
|
+
serialize(options?: {keepDeletedWidgets?: boolean}): any | PerseusItem {
|
|
211
205
|
if (this.props.jsonMode) {
|
|
212
206
|
return this.state.json;
|
|
213
207
|
}
|
|
@@ -226,7 +220,7 @@ class EditorPage extends React.Component<Props, State> {
|
|
|
226
220
|
this.props.onChange(newProps, cb, silent);
|
|
227
221
|
};
|
|
228
222
|
|
|
229
|
-
changeJSON: (newJson:
|
|
223
|
+
changeJSON: (newJson: PerseusItem) => void = (newJson: PerseusItem) => {
|
|
230
224
|
this.setState({
|
|
231
225
|
json: newJson,
|
|
232
226
|
});
|
package/src/editor.tsx
CHANGED
|
@@ -132,7 +132,7 @@ type Props = Readonly<{
|
|
|
132
132
|
onChange: ChangeHandler;
|
|
133
133
|
}>;
|
|
134
134
|
|
|
135
|
-
type
|
|
135
|
+
type DefaultProps = {
|
|
136
136
|
content: string;
|
|
137
137
|
disabled: boolean;
|
|
138
138
|
images: Record<any, any>;
|
|
@@ -157,7 +157,7 @@ class Editor extends React.Component<Props, State> {
|
|
|
157
157
|
deferredChange: any | null | undefined;
|
|
158
158
|
widgetIds: any | null | undefined;
|
|
159
159
|
|
|
160
|
-
static defaultProps:
|
|
160
|
+
static defaultProps: DefaultProps = {
|
|
161
161
|
content: "",
|
|
162
162
|
placeholder: "",
|
|
163
163
|
widgets: {},
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {ApiOptions} from "@khanacademy/perseus";
|
|
2
|
+
import {action} from "@storybook/addon-actions";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import CategorizerEditor from "../categorizer-editor";
|
|
6
|
+
|
|
7
|
+
type StoryArgs = Record<any, any>;
|
|
8
|
+
|
|
9
|
+
type Story = {
|
|
10
|
+
title: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
title: "Perseus/Editor/Widgets/Categorizer Editor",
|
|
15
|
+
} as Story;
|
|
16
|
+
|
|
17
|
+
export const Default = (args: StoryArgs): React.ReactElement => {
|
|
18
|
+
return (
|
|
19
|
+
<CategorizerEditor
|
|
20
|
+
onChange={action("onChange")}
|
|
21
|
+
apiOptions={ApiOptions.defaults}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {action} from "@storybook/addon-actions";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import DefinitionEditor from "../definition-editor";
|
|
5
|
+
|
|
6
|
+
type StoryArgs = Record<any, any>;
|
|
7
|
+
|
|
8
|
+
type Story = {
|
|
9
|
+
title: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: "Perseus/Editor/Widgets/Definition Editor",
|
|
14
|
+
} as Story;
|
|
15
|
+
|
|
16
|
+
export const Default = (args: StoryArgs): React.ReactElement => {
|
|
17
|
+
return <DefinitionEditor onChange={action("onChange")} />;
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {action} from "@storybook/addon-actions";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import DropdownEditor from "../dropdown-editor";
|
|
5
|
+
|
|
6
|
+
type StoryArgs = Record<any, any>;
|
|
7
|
+
|
|
8
|
+
type Story = {
|
|
9
|
+
title: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: "Perseus/Editor/Widgets/Dropdown Editor",
|
|
14
|
+
} as Story;
|
|
15
|
+
|
|
16
|
+
export const Default = (args: StoryArgs): React.ReactElement => {
|
|
17
|
+
return <DropdownEditor onChange={action("onChange")} />;
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {action} from "@storybook/addon-actions";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import ExplanationEditor from "../explanation-editor";
|
|
5
|
+
|
|
6
|
+
type StoryArgs = Record<any, any>;
|
|
7
|
+
|
|
8
|
+
type Story = {
|
|
9
|
+
title: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: "Perseus/Editor/Widgets/Explanation Editor",
|
|
14
|
+
} as Story;
|
|
15
|
+
|
|
16
|
+
export const Default = (args: StoryArgs): React.ReactElement => {
|
|
17
|
+
return <ExplanationEditor onChange={action("onChange")} />;
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {action} from "@storybook/addon-actions";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import InputNumberEditor from "../input-number-editor";
|
|
5
|
+
|
|
6
|
+
type StoryArgs = Record<any, any>;
|
|
7
|
+
|
|
8
|
+
type Story = {
|
|
9
|
+
title: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: "Perseus/Editor/Widgets/InputNumber Editor",
|
|
14
|
+
} as Story;
|
|
15
|
+
|
|
16
|
+
export const Default = (args: StoryArgs): React.ReactElement => {
|
|
17
|
+
return <InputNumberEditor onChange={action("onChange")} />;
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {action} from "@storybook/addon-actions";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import MatcherEditor from "../matcher-editor";
|
|
5
|
+
|
|
6
|
+
type StoryArgs = Record<any, any>;
|
|
7
|
+
|
|
8
|
+
type Story = {
|
|
9
|
+
title: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: "Perseus/Editor/Widgets/Matcher Editor",
|
|
14
|
+
} as Story;
|
|
15
|
+
|
|
16
|
+
export const Default = (args: StoryArgs): React.ReactElement => {
|
|
17
|
+
return <MatcherEditor onChange={action("onChange")} />;
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {action} from "@storybook/addon-actions";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import NumberLineEditor from "../number-line-editor";
|
|
5
|
+
|
|
6
|
+
type StoryArgs = Record<any, any>;
|
|
7
|
+
|
|
8
|
+
type Story = {
|
|
9
|
+
title: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: "Perseus/Editor/Widgets/Number Line Editor",
|
|
14
|
+
} as Story;
|
|
15
|
+
|
|
16
|
+
export const Default = (args: StoryArgs): React.ReactElement => {
|
|
17
|
+
return <NumberLineEditor onChange={action("onChange")} />;
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {action} from "@storybook/addon-actions";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import NumericInputEditor from "../numeric-input-editor";
|
|
5
|
+
|
|
6
|
+
type StoryArgs = Record<any, any>;
|
|
7
|
+
|
|
8
|
+
type Story = {
|
|
9
|
+
title: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: "Perseus/Editor/Widgets/NumericInput Editor",
|
|
14
|
+
} as Story;
|
|
15
|
+
|
|
16
|
+
export const Default = (args: StoryArgs): React.ReactElement => {
|
|
17
|
+
return <NumericInputEditor onChange={action("onChange")} />;
|
|
18
|
+
};
|
|
@@ -97,12 +97,12 @@ class WithState extends React.Component<Empty, PerseusRadioWidgetOptions> {
|
|
|
97
97
|
<RadioEditor
|
|
98
98
|
{...this.state}
|
|
99
99
|
apiOptions={this.apiOptions}
|
|
100
|
-
onChange={(props) =>
|
|
100
|
+
onChange={(props) => {
|
|
101
101
|
this.setState({
|
|
102
102
|
...this._widget.serialize(),
|
|
103
103
|
...props,
|
|
104
|
-
})
|
|
105
|
-
}
|
|
104
|
+
});
|
|
105
|
+
}}
|
|
106
106
|
// @ts-expect-error [FEI-5003] - TS2322 - Type 'RadioEditor | null' is not assignable to type 'RadioEditor'.
|
|
107
107
|
ref={(widget) => (this._widget = widget)}
|
|
108
108
|
/>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {action} from "@storybook/addon-actions";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import SorterEditor from "../sorter-editor";
|
|
5
|
+
|
|
6
|
+
type StoryArgs = Record<any, any>;
|
|
7
|
+
|
|
8
|
+
type Story = {
|
|
9
|
+
title: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: "Perseus/Editor/Widgets/Sorter Editor",
|
|
14
|
+
} as Story;
|
|
15
|
+
|
|
16
|
+
export const Default = (args: StoryArgs): React.ReactElement => {
|
|
17
|
+
return <SorterEditor onChange={action("onChange")} />;
|
|
18
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {ApiOptions, Dependencies} from "@khanacademy/perseus";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom";
|
|
7
|
+
|
|
8
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
9
|
+
import CategorizerEditor from "../categorizer-editor";
|
|
10
|
+
|
|
11
|
+
describe("categorizer-editor", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
14
|
+
testDependencies,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should render", async () => {
|
|
19
|
+
render(
|
|
20
|
+
<CategorizerEditor
|
|
21
|
+
onChange={() => undefined}
|
|
22
|
+
apiOptions={ApiOptions.defaults}
|
|
23
|
+
/>,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(
|
|
27
|
+
await screen.findByText("Randomize item order"),
|
|
28
|
+
).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should be possible to change randomize item order", async () => {
|
|
32
|
+
const onChangeMock = jest.fn();
|
|
33
|
+
|
|
34
|
+
render(
|
|
35
|
+
<CategorizerEditor
|
|
36
|
+
onChange={onChangeMock}
|
|
37
|
+
apiOptions={ApiOptions.defaults}
|
|
38
|
+
/>,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
userEvent.click(
|
|
42
|
+
screen.getByRole("checkbox", {
|
|
43
|
+
name: "Randomize item order",
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(onChangeMock).toBeCalledWith(
|
|
48
|
+
expect.objectContaining({randomizeItems: true}),
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {Dependencies} from "@khanacademy/perseus";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom";
|
|
7
|
+
|
|
8
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
9
|
+
import DefinitionEditor from "../definition-editor";
|
|
10
|
+
|
|
11
|
+
describe("definition-editor", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
14
|
+
testDependencies,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should render", async () => {
|
|
19
|
+
render(<DefinitionEditor onChange={() => undefined} />);
|
|
20
|
+
|
|
21
|
+
expect(
|
|
22
|
+
await screen.findByText("Definition style guide"),
|
|
23
|
+
).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should be possible to change the definition", async () => {
|
|
27
|
+
const onChangeMock = jest.fn();
|
|
28
|
+
|
|
29
|
+
render(<DefinitionEditor onChange={onChangeMock} />);
|
|
30
|
+
|
|
31
|
+
const input = screen.getByRole("textbox", {
|
|
32
|
+
name: "Word to be defined:",
|
|
33
|
+
});
|
|
34
|
+
userEvent.type(input, "a");
|
|
35
|
+
|
|
36
|
+
expect(onChangeMock).toBeCalledWith(
|
|
37
|
+
expect.objectContaining({togglePrompt: "a"}),
|
|
38
|
+
undefined,
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {Dependencies} from "@khanacademy/perseus";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom";
|
|
7
|
+
|
|
8
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
9
|
+
import DropdownEditor from "../dropdown-editor";
|
|
10
|
+
|
|
11
|
+
describe("dropdown-editor", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
14
|
+
testDependencies,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should be possible to modify placeholder text", async () => {
|
|
19
|
+
const onChangeMock = jest.fn();
|
|
20
|
+
|
|
21
|
+
render(<DropdownEditor onChange={onChangeMock} />);
|
|
22
|
+
|
|
23
|
+
const input = screen.getByPlaceholderText("Placeholder value");
|
|
24
|
+
userEvent.type(input, "a");
|
|
25
|
+
|
|
26
|
+
expect(onChangeMock).toBeCalledWith({placeholder: "a"});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should be possible to delete choice", async () => {
|
|
30
|
+
const onChangeMock = jest.fn();
|
|
31
|
+
|
|
32
|
+
render(<DropdownEditor onChange={onChangeMock} />);
|
|
33
|
+
|
|
34
|
+
userEvent.click(screen.getByRole("link", {name: "Delete choice"}));
|
|
35
|
+
|
|
36
|
+
expect(onChangeMock).toBeCalledWith({choices: []});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should be possible to add choice", async () => {
|
|
40
|
+
const onChangeMock = jest.fn();
|
|
41
|
+
|
|
42
|
+
render(<DropdownEditor onChange={onChangeMock} />);
|
|
43
|
+
|
|
44
|
+
userEvent.click(screen.getByRole("link", {name: "Add a choice"}));
|
|
45
|
+
|
|
46
|
+
expect(onChangeMock).toBeCalledWith(
|
|
47
|
+
{
|
|
48
|
+
choices: [
|
|
49
|
+
{content: "", correct: false},
|
|
50
|
+
{content: "", correct: false},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
// there's some anonymous function that's also passed
|
|
54
|
+
expect.anything(),
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {Dependencies} from "@khanacademy/perseus";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom";
|
|
7
|
+
|
|
8
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
9
|
+
import ExplanationEditor from "../explanation-editor";
|
|
10
|
+
|
|
11
|
+
describe("explanation-editor", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
14
|
+
testDependencies,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should render", async () => {
|
|
19
|
+
render(<ExplanationEditor onChange={() => undefined} />);
|
|
20
|
+
|
|
21
|
+
expect(
|
|
22
|
+
await screen.findByText("Prompt to show explanation:"),
|
|
23
|
+
).toBeInTheDocument();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should be possible to change prompt to show explanation", async () => {
|
|
27
|
+
const onChangeMock = jest.fn();
|
|
28
|
+
|
|
29
|
+
render(<ExplanationEditor onChange={onChangeMock} />);
|
|
30
|
+
|
|
31
|
+
const input = screen.getByRole("textbox", {
|
|
32
|
+
name: "Prompt to show explanation:",
|
|
33
|
+
});
|
|
34
|
+
userEvent.type(input, "a");
|
|
35
|
+
|
|
36
|
+
expect(onChangeMock).toBeCalledWith(
|
|
37
|
+
// The dropdown initalizes with "Explain"
|
|
38
|
+
expect.objectContaining({showPrompt: "Explaina"}),
|
|
39
|
+
undefined,
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should be possible to change prompt to hide explanation", async () => {
|
|
44
|
+
const onChangeMock = jest.fn();
|
|
45
|
+
|
|
46
|
+
render(<ExplanationEditor onChange={onChangeMock} />);
|
|
47
|
+
|
|
48
|
+
const input = screen.getByRole("textbox", {
|
|
49
|
+
name: "Prompt to hide explanation:",
|
|
50
|
+
});
|
|
51
|
+
userEvent.type(input, "a");
|
|
52
|
+
|
|
53
|
+
expect(onChangeMock).toBeCalledWith(
|
|
54
|
+
// The dropdown initalizes with "Hide explain"
|
|
55
|
+
expect.objectContaining({hidePrompt: "Hide explanationa"}),
|
|
56
|
+
undefined,
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
});
|