@khanacademy/perseus 13.2.0 → 14.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 +22 -0
- package/dist/components/svg-image.d.ts +10 -9
- package/dist/es/index.js +36 -31
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +60 -49
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/article-renderer.test.tsx +2 -3
- package/src/components/graph.tsx +14 -8
- package/src/components/svg-image.tsx +13 -11
- package/src/mixins/provide-keypad.tsx +0 -1
- package/src/perseus-types.ts +8 -3
- package/src/widgets/__stories__/random-widgets.stories.tsx +59 -0
- package/src/widgets/__stories__/test-keypad-context-wrapper.tsx +0 -1
- package/src/widgets/__testdata__/categorizer.testdata.ts +40 -0
- package/src/widgets/__testdata__/dropdown.testdata.ts +38 -0
- package/src/widgets/__testdata__/explanation.testdata.ts +30 -0
- package/src/widgets/__testdata__/expression.testdata.ts +82 -0
- package/src/widgets/__testdata__/interactive-graph-random.testdata.ts +269 -0
- package/src/widgets/__testdata__/radio.testdata.ts +63 -0
- package/src/widgets/__testdata__/randomizers.ts +51 -0
- package/src/widgets/__tests__/expression-mobile.test.tsx +0 -1
- package/src/widgets/__tests__/randomizers.test.ts +53 -0
- package/src/widgets/image.tsx +27 -20
- package/tsconfig-build.tsbuildinfo +1 -1
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": "14.0.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@khanacademy/kas": "^0.3.5",
|
|
26
26
|
"@khanacademy/kmath": "^0.1.6",
|
|
27
|
-
"@khanacademy/math-input": "^
|
|
27
|
+
"@khanacademy/math-input": "^16.0.0",
|
|
28
28
|
"@khanacademy/perseus-core": "1.3.0",
|
|
29
29
|
"@khanacademy/perseus-linter": "^0.3.8",
|
|
30
30
|
"@khanacademy/pure-markdown": "^0.2.11",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
StatefulKeypadContextProvider,
|
|
3
3
|
KeypadContext,
|
|
4
|
+
MobileKeypad,
|
|
4
5
|
} from "@khanacademy/math-input";
|
|
5
6
|
import {RenderStateRoot} from "@khanacademy/wonder-blocks-core";
|
|
6
7
|
import {screen, render, fireEvent, waitFor} from "@testing-library/react";
|
|
@@ -11,7 +12,6 @@ import {
|
|
|
11
12
|
testDependencies,
|
|
12
13
|
testDependenciesV2,
|
|
13
14
|
} from "../../../../testing/test-dependencies";
|
|
14
|
-
import KeypadSwitch from "../../../math-input/src/components/keypad-switch";
|
|
15
15
|
import {articleWithExpression} from "../__testdata__/article-renderer.testdata";
|
|
16
16
|
import ArticleRenderer from "../article-renderer";
|
|
17
17
|
import * as Dependencies from "../dependencies";
|
|
@@ -24,11 +24,10 @@ function KeypadWithContext() {
|
|
|
24
24
|
<KeypadContext.Consumer>
|
|
25
25
|
{({setKeypadElement}) => {
|
|
26
26
|
return (
|
|
27
|
-
<
|
|
27
|
+
<MobileKeypad
|
|
28
28
|
onElementMounted={setKeypadElement}
|
|
29
29
|
onDismiss={() => {}}
|
|
30
30
|
onAnalyticsEvent={async () => {}}
|
|
31
|
-
useV2Keypad
|
|
32
31
|
/>
|
|
33
32
|
);
|
|
34
33
|
}}
|
package/src/components/graph.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import * as React from "react";
|
|
|
5
5
|
import ReactDOM from "react-dom";
|
|
6
6
|
import _ from "underscore";
|
|
7
7
|
|
|
8
|
+
import AssetContext from "../asset-context";
|
|
8
9
|
import {interactiveSizes} from "../styles/constants";
|
|
9
10
|
import Util from "../util";
|
|
10
11
|
import GraphUtils from "../util/graph-utils";
|
|
@@ -395,14 +396,19 @@ class Graph extends React.Component<Props> {
|
|
|
395
396
|
if (imageData.url) {
|
|
396
397
|
const scale = this.props.box[0] / interactiveSizes.defaultBoxSize;
|
|
397
398
|
image = (
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
399
|
+
<AssetContext.Consumer>
|
|
400
|
+
{({setAssetStatus}) => (
|
|
401
|
+
<SvgImage
|
|
402
|
+
// @ts-expect-error - TS2741 - Property 'alt' is missing in type '{ src: any; width: any; height: any; scale: number; responsive: false; }' but required in type 'Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "children" | "height" | "width" | "title" | "alt" | "trackInteraction" | "preloader" | "allowFullBleed" | "extraGraphie" | "overrideAriaHidden">'.
|
|
403
|
+
src={imageData.url}
|
|
404
|
+
width={imageData.width}
|
|
405
|
+
height={imageData.height}
|
|
406
|
+
scale={scale}
|
|
407
|
+
responsive={false}
|
|
408
|
+
setAssetStatus={setAssetStatus}
|
|
409
|
+
/>
|
|
410
|
+
)}
|
|
411
|
+
</AssetContext.Consumer>
|
|
406
412
|
);
|
|
407
413
|
} else {
|
|
408
414
|
image = null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
2
|
-
/* eslint-disable
|
|
2
|
+
/* eslint-disable react/no-unsafe */
|
|
3
3
|
import {CircularSpinner} from "@khanacademy/wonder-blocks-progress-spinner";
|
|
4
4
|
import classNames from "classnames";
|
|
5
5
|
import $ from "jquery";
|
|
@@ -79,7 +79,7 @@ const doJSONP = function (url: string, options) {
|
|
|
79
79
|
// system (with file://). We replace urls that start with `web+graphie`
|
|
80
80
|
// in the perseus json with this `file+graphie` prefix to indicate that
|
|
81
81
|
// they should have the `file://` protocol instead of `https://`.
|
|
82
|
-
const svgLocalLabelsRegex = /^file\+graphie
|
|
82
|
+
const svgLocalLabelsRegex = /^file\+graphie:/;
|
|
83
83
|
const hashRegex = /\/([^/]+)$/;
|
|
84
84
|
|
|
85
85
|
function isImageProbablyPhotograph(imageUrl) {
|
|
@@ -180,6 +180,16 @@ type Props = {
|
|
|
180
180
|
setAssetStatus: (assetKey: string, loaded: boolean) => void;
|
|
181
181
|
};
|
|
182
182
|
|
|
183
|
+
type DefaultProps = {
|
|
184
|
+
constrainHeight: NonNullable<Props["constrainHeight"]>;
|
|
185
|
+
onUpdate: NonNullable<Props["onUpdate"]>;
|
|
186
|
+
responsive: NonNullable<Props["responsive"]>;
|
|
187
|
+
scale: NonNullable<Props["scale"]>;
|
|
188
|
+
setAssetStatus: NonNullable<Props["setAssetStatus"]>;
|
|
189
|
+
src: NonNullable<Props["src"]>;
|
|
190
|
+
zoomToFullSizeOnMobile: NonNullable<Props["zoomToFullSizeOnMobile"]>;
|
|
191
|
+
};
|
|
192
|
+
|
|
183
193
|
type Label = {
|
|
184
194
|
coordinates: ReadonlyArray<any>;
|
|
185
195
|
content: string;
|
|
@@ -212,15 +222,7 @@ type State = {
|
|
|
212
222
|
class SvgImage extends React.Component<Props, State> {
|
|
213
223
|
_isMounted: boolean;
|
|
214
224
|
|
|
215
|
-
static defaultProps: {
|
|
216
|
-
constrainHeight: boolean;
|
|
217
|
-
onUpdate: () => void;
|
|
218
|
-
responsive: boolean;
|
|
219
|
-
scale: number;
|
|
220
|
-
setAssetStatus: (src: string, status: boolean) => void;
|
|
221
|
-
src: string;
|
|
222
|
-
zoomToFullSizeOnMobile: boolean;
|
|
223
|
-
} = {
|
|
225
|
+
static defaultProps: DefaultProps = {
|
|
224
226
|
constrainHeight: false,
|
|
225
227
|
onUpdate: () => {},
|
|
226
228
|
responsive: true,
|
|
@@ -102,7 +102,6 @@ const ProvideKeypad = {
|
|
|
102
102
|
}}
|
|
103
103
|
// @ts-expect-error - TS2339 - Property 'props' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'.
|
|
104
104
|
style={_this.props.keypadStyle}
|
|
105
|
-
useV2Keypad={apiOptions.useV2Keypad}
|
|
106
105
|
onAnalyticsEvent={async () => {
|
|
107
106
|
// Intentionally left empty. This component is
|
|
108
107
|
// deprecated and so we don't want/need to instrument
|
package/src/perseus-types.ts
CHANGED
|
@@ -534,7 +534,7 @@ export type PerseusInteractiveGraphWidgetOptions = {
|
|
|
534
534
|
showTooltips?: boolean;
|
|
535
535
|
// The unit to show on the ruler. e.g. "mm", "cm", "m", "km", "in", "ft", "yd", "mi"
|
|
536
536
|
rulerLabel: string;
|
|
537
|
-
// How many ticks to show on the ruler. e.g. 1, 2, 4, 8, 10, 16
|
|
537
|
+
// How many ticks to show on the ruler. e.g. 1, 2, 4, 8, 10, 16. Must be an integer.
|
|
538
538
|
rulerTicks: number;
|
|
539
539
|
// The X and Y coordinate ranges for the view of the graph. default: [[-10, 10], [-10, 10]]
|
|
540
540
|
// NOTE(kevinb): perseus_data.go defines this as Array<Array<number>>
|
|
@@ -577,6 +577,7 @@ export type PerseusGraphTypeAngle = {
|
|
|
577
577
|
snapDegrees?: number;
|
|
578
578
|
// How to match the answer. If missing, defaults to exact matching.
|
|
579
579
|
match?: "congruent";
|
|
580
|
+
// must have 3 coords - ie [Coord, Coord, Coord]
|
|
580
581
|
coords?: ReadonlyArray<Coord>;
|
|
581
582
|
};
|
|
582
583
|
|
|
@@ -588,11 +589,13 @@ export type PerseusGraphTypeCircle = {
|
|
|
588
589
|
|
|
589
590
|
export type PerseusGraphTypeLinear = {
|
|
590
591
|
type: "linear";
|
|
592
|
+
// expects 2 coords
|
|
591
593
|
coords?: ReadonlyArray<Coord>;
|
|
592
594
|
} & PerseusGraphTypeCommon;
|
|
593
595
|
|
|
594
596
|
export type PerseusGraphTypeLinearSystem = {
|
|
595
597
|
type: "linear-system";
|
|
598
|
+
// expects 2 sets of 2 coords
|
|
596
599
|
coords?: ReadonlyArray<ReadonlyArray<Coord>>;
|
|
597
600
|
} & PerseusGraphTypeCommon;
|
|
598
601
|
|
|
@@ -620,6 +623,7 @@ export type PerseusGraphTypePolygon = {
|
|
|
620
623
|
|
|
621
624
|
export type PerseusGraphTypeQuadratic = {
|
|
622
625
|
type: "quadratic";
|
|
626
|
+
// expects a list of 3 coords
|
|
623
627
|
coords?: ReadonlyArray<Coord>;
|
|
624
628
|
} & PerseusGraphTypeCommon;
|
|
625
629
|
|
|
@@ -627,18 +631,19 @@ export type PerseusGraphTypeSegment = {
|
|
|
627
631
|
type: "segment";
|
|
628
632
|
// The number of segments if a "segment" type. default: 1. Max: 6
|
|
629
633
|
numSegments?: number;
|
|
630
|
-
//
|
|
631
|
-
// Length should match the `numSegments` value.
|
|
634
|
+
// Expects a list of Coord tuples. Length should match the `numSegments` value.
|
|
632
635
|
coords?: ReadonlyArray<ReadonlyArray<Coord>>;
|
|
633
636
|
} & PerseusGraphTypeCommon;
|
|
634
637
|
|
|
635
638
|
export type PerseusGraphTypeSinusoid = {
|
|
636
639
|
type: "sinusoid";
|
|
640
|
+
// Expects a list of 2 Coords
|
|
637
641
|
coords?: ReadonlyArray<Coord>;
|
|
638
642
|
} & PerseusGraphTypeCommon;
|
|
639
643
|
|
|
640
644
|
export type PerseusGraphTypeRay = {
|
|
641
645
|
type: "ray";
|
|
646
|
+
// Expects a list of 2 Coords
|
|
642
647
|
coords?: ReadonlyArray<Coord>;
|
|
643
648
|
} & PerseusGraphTypeCommon;
|
|
644
649
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Button from "@khanacademy/wonder-blocks-button";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import {RendererWithDebugUI} from "../../../../../testing/renderer-with-debug-ui";
|
|
5
|
+
import {randomCategorizerGenerator} from "../__testdata__/categorizer.testdata";
|
|
6
|
+
import {randomDropdownGenerator} from "../__testdata__/dropdown.testdata";
|
|
7
|
+
import {randomExplanationGenerator} from "../__testdata__/explanation.testdata";
|
|
8
|
+
import {randomExpressionGenerator} from "../__testdata__/expression.testdata";
|
|
9
|
+
import {randomInteractiveGraphGenerator} from "../__testdata__/interactive-graph-random.testdata";
|
|
10
|
+
import {randomRadioGenerator} from "../__testdata__/radio.testdata";
|
|
11
|
+
|
|
12
|
+
import type {PerseusRenderer} from "@khanacademy/perseus";
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
title: "Perseus/Randomized Widgets",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const RandomizedWidgetContainer = (
|
|
19
|
+
randomizer: () => PerseusRenderer,
|
|
20
|
+
): React.ReactElement => {
|
|
21
|
+
const [question, setQuestion] = React.useState(randomizer());
|
|
22
|
+
const randomize = () => {
|
|
23
|
+
setQuestion(randomizer());
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div>
|
|
28
|
+
<Button size="small" style={{marginBottom: 24}} onClick={randomize}>
|
|
29
|
+
Randomize
|
|
30
|
+
</Button>
|
|
31
|
+
|
|
32
|
+
<RendererWithDebugUI question={question} />
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const RandomCategorizer = (): React.ReactElement => {
|
|
38
|
+
return RandomizedWidgetContainer(randomCategorizerGenerator);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const RandomDropdown = (): React.ReactElement => {
|
|
42
|
+
return RandomizedWidgetContainer(randomDropdownGenerator);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const RandomExplanation = (): React.ReactElement => {
|
|
46
|
+
return RandomizedWidgetContainer(randomExplanationGenerator);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const RandomExpression = (): React.ReactElement => {
|
|
50
|
+
return RandomizedWidgetContainer(randomExpressionGenerator);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const RandomInteractiveGraph = (): React.ReactElement => {
|
|
54
|
+
return RandomizedWidgetContainer(randomInteractiveGraphGenerator);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const RandomRadio = (): React.ReactElement => {
|
|
58
|
+
return RandomizedWidgetContainer(randomRadioGenerator);
|
|
59
|
+
};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
arrayOfLength,
|
|
3
|
+
randomBoolean,
|
|
4
|
+
randomInteger,
|
|
5
|
+
randomSentence,
|
|
6
|
+
} from "./randomizers";
|
|
7
|
+
|
|
1
8
|
import type {PerseusRenderer} from "../../perseus-types";
|
|
2
9
|
|
|
3
10
|
export const question1: PerseusRenderer = {
|
|
@@ -47,3 +54,36 @@ export const question1: PerseusRenderer = {
|
|
|
47
54
|
},
|
|
48
55
|
},
|
|
49
56
|
};
|
|
57
|
+
|
|
58
|
+
export const randomCategorizerGenerator = (): PerseusRenderer => {
|
|
59
|
+
const questionText = randomSentence(35);
|
|
60
|
+
const numItems = randomInteger(2, 7);
|
|
61
|
+
const numCategories = randomInteger(2, 6);
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
content: `${questionText}\n\n\n[[\u2603 categorizer 1]]`,
|
|
65
|
+
images: {},
|
|
66
|
+
widgets: {
|
|
67
|
+
"categorizer 1": {
|
|
68
|
+
version: {major: 0, minor: 0},
|
|
69
|
+
type: "categorizer",
|
|
70
|
+
graded: randomBoolean(),
|
|
71
|
+
alignment: "default",
|
|
72
|
+
options: {
|
|
73
|
+
items: arrayOfLength(numItems).map(() =>
|
|
74
|
+
randomSentence(12),
|
|
75
|
+
),
|
|
76
|
+
values: arrayOfLength(numItems).map(() =>
|
|
77
|
+
randomInteger(0, numCategories - 1),
|
|
78
|
+
),
|
|
79
|
+
randomizeItems: randomBoolean(),
|
|
80
|
+
categories: arrayOfLength(numCategories).map(() =>
|
|
81
|
+
randomSentence(7),
|
|
82
|
+
),
|
|
83
|
+
highlightLint: randomBoolean(),
|
|
84
|
+
static: randomBoolean(0.05),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
arrayOfLength,
|
|
3
|
+
randomBoolean,
|
|
4
|
+
randomInteger,
|
|
5
|
+
randomSentence,
|
|
6
|
+
} from "./randomizers";
|
|
7
|
+
|
|
1
8
|
import type {PerseusRenderer} from "../../perseus-types";
|
|
2
9
|
|
|
3
10
|
export const question1: PerseusRenderer = {
|
|
@@ -31,3 +38,34 @@ export const question1: PerseusRenderer = {
|
|
|
31
38
|
},
|
|
32
39
|
},
|
|
33
40
|
};
|
|
41
|
+
|
|
42
|
+
export const randomDropdownGenerator = (): PerseusRenderer => {
|
|
43
|
+
const numChoices = randomInteger(2, 16);
|
|
44
|
+
const correctIndex = randomInteger(0, numChoices - 1);
|
|
45
|
+
return {
|
|
46
|
+
content: `${randomSentence(20)} [[☃ dropdown 1]] ${randomSentence(10)}`,
|
|
47
|
+
images: {},
|
|
48
|
+
widgets: {
|
|
49
|
+
"dropdown 1": {
|
|
50
|
+
type: "dropdown",
|
|
51
|
+
alignment: "default",
|
|
52
|
+
static: randomBoolean(0.05),
|
|
53
|
+
graded: randomBoolean(),
|
|
54
|
+
options: {
|
|
55
|
+
static: randomBoolean(0.05),
|
|
56
|
+
placeholder: randomSentence(10),
|
|
57
|
+
choices: arrayOfLength(numChoices).map((_, i) => {
|
|
58
|
+
return {
|
|
59
|
+
content: randomSentence(10),
|
|
60
|
+
correct: i === correctIndex,
|
|
61
|
+
};
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
version: {
|
|
65
|
+
major: 0,
|
|
66
|
+
minor: 0,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {randomBoolean, randomSentence} from "./randomizers";
|
|
2
|
+
|
|
1
3
|
import type {PerseusRenderer} from "../../perseus-types";
|
|
2
4
|
|
|
3
5
|
export const question1: PerseusRenderer = {
|
|
@@ -53,3 +55,31 @@ export const question2: PerseusRenderer = {
|
|
|
53
55
|
},
|
|
54
56
|
},
|
|
55
57
|
};
|
|
58
|
+
|
|
59
|
+
export const randomExplanationGenerator = (): PerseusRenderer => {
|
|
60
|
+
return {
|
|
61
|
+
content: `${randomSentence(
|
|
62
|
+
50,
|
|
63
|
+
)}\n[[\u2603 explanation 1]]\n${randomSentence(50)}`,
|
|
64
|
+
images: {},
|
|
65
|
+
widgets: {
|
|
66
|
+
"explanation 1": {
|
|
67
|
+
graded: randomBoolean(),
|
|
68
|
+
version: {
|
|
69
|
+
major: 0,
|
|
70
|
+
minor: 0,
|
|
71
|
+
},
|
|
72
|
+
static: randomBoolean(0.05),
|
|
73
|
+
type: "explanation",
|
|
74
|
+
options: {
|
|
75
|
+
hidePrompt: `${randomSentence(20)}`,
|
|
76
|
+
widgets: {},
|
|
77
|
+
explanation: `${randomSentence(50)}`,
|
|
78
|
+
static: randomBoolean(0.05),
|
|
79
|
+
showPrompt: `${randomSentence(7)}`,
|
|
80
|
+
},
|
|
81
|
+
alignment: "default",
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
};
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
arrayOfLength,
|
|
3
|
+
randomBoolean,
|
|
4
|
+
randomElement,
|
|
5
|
+
randomInteger,
|
|
6
|
+
randomLetter,
|
|
7
|
+
randomSentence,
|
|
8
|
+
randomWord,
|
|
9
|
+
} from "./randomizers";
|
|
10
|
+
|
|
1
11
|
import type {
|
|
2
12
|
PerseusExpressionWidgetOptions,
|
|
3
13
|
Version,
|
|
4
14
|
PerseusItem,
|
|
15
|
+
PerseusRenderer,
|
|
16
|
+
ExpressionWidget,
|
|
5
17
|
} from "../../perseus-types";
|
|
6
18
|
|
|
7
19
|
const createItemJson = (
|
|
@@ -96,3 +108,73 @@ export const expressionItem3: PerseusItem = createItemJson(
|
|
|
96
108
|
minor: 0,
|
|
97
109
|
},
|
|
98
110
|
);
|
|
111
|
+
|
|
112
|
+
export const randomExpressionGenerator = (): PerseusRenderer => {
|
|
113
|
+
const randomButtonSet = [
|
|
114
|
+
"basic",
|
|
115
|
+
"basic+div",
|
|
116
|
+
"trig",
|
|
117
|
+
"prealgebra",
|
|
118
|
+
"logarithms",
|
|
119
|
+
"basic relations",
|
|
120
|
+
"advanced relations",
|
|
121
|
+
]
|
|
122
|
+
.sort(() => {
|
|
123
|
+
return randomBoolean() ? 1 : -1;
|
|
124
|
+
})
|
|
125
|
+
.slice(0, randomInteger(1, 6));
|
|
126
|
+
|
|
127
|
+
const randomFunctionSet = arrayOfLength(randomInteger(0, 6)).map(
|
|
128
|
+
randomLetter,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
content: `${randomSentence(20)} [[☃ expression 1]]`,
|
|
133
|
+
images: {},
|
|
134
|
+
widgets: {
|
|
135
|
+
"expression 1": {
|
|
136
|
+
type: "expression",
|
|
137
|
+
graded: randomBoolean(),
|
|
138
|
+
version: {
|
|
139
|
+
major: 1,
|
|
140
|
+
minor: 0,
|
|
141
|
+
},
|
|
142
|
+
static: randomBoolean(0.05),
|
|
143
|
+
options: {
|
|
144
|
+
answerForms: [
|
|
145
|
+
{
|
|
146
|
+
considered: randomElement([
|
|
147
|
+
"correct",
|
|
148
|
+
"wrong",
|
|
149
|
+
"ungraded",
|
|
150
|
+
]),
|
|
151
|
+
form: randomBoolean(),
|
|
152
|
+
simplify: randomBoolean(),
|
|
153
|
+
value: randomWord(),
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
considered: randomElement([
|
|
157
|
+
"correct",
|
|
158
|
+
"wrong",
|
|
159
|
+
"ungraded",
|
|
160
|
+
]),
|
|
161
|
+
form: randomBoolean(),
|
|
162
|
+
simplify: randomBoolean(),
|
|
163
|
+
value: randomWord(),
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
times: randomBoolean(),
|
|
167
|
+
buttonSets: randomButtonSet,
|
|
168
|
+
functions: randomFunctionSet,
|
|
169
|
+
buttonsVisible: randomElement([
|
|
170
|
+
"always",
|
|
171
|
+
"never",
|
|
172
|
+
"focused",
|
|
173
|
+
undefined,
|
|
174
|
+
]),
|
|
175
|
+
alignment: "default",
|
|
176
|
+
},
|
|
177
|
+
} as ExpressionWidget,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
};
|