@khanacademy/perseus 13.2.0 → 15.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 +47 -0
- package/dist/article-renderer.d.ts +1 -2
- package/dist/components/svg-image.d.ts +10 -9
- package/dist/es/index.js +51 -498
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +72 -511
- package/dist/index.js.map +1 -1
- package/dist/multi-items/item-types.d.ts +1 -1
- package/dist/multi-items/multi-renderer.d.ts +2 -2
- package/dist/perseus-api.d.ts +0 -3
- package/dist/server-item-renderer.d.ts +2 -15
- package/dist/strings.js +1 -0
- package/dist/styles/shared.d.ts +1 -0
- package/dist/types.d.ts +0 -2
- package/dist/util/a11y.d.ts +1 -0
- package/dist/widgets/expression.d.ts +0 -12
- package/dist/widgets/graded-group.d.ts +1 -1
- package/dist/widgets/label-image.d.ts +1 -1
- package/dist/widgets/matcher.d.ts +0 -1
- package/dist/widgets/passage.d.ts +1 -1
- package/dist/widgets/radio/widget.d.ts +1 -1
- package/dist/widgets/simulator.d.ts +0 -1
- package/dist/widgets/sorter.d.ts +0 -1
- package/package.json +8 -8
- package/src/__stories__/server-item-renderer.stories.tsx +1 -1
- package/src/__testdata__/server-item-renderer.testdata.ts +160 -0
- package/src/__tests__/article-renderer.test.tsx +2 -3
- package/src/__tests__/server-item-renderer.test.tsx +2 -2
- package/src/article-renderer.tsx +13 -15
- package/src/components/graph.tsx +14 -8
- package/src/components/svg-image.tsx +14 -12
- package/src/hints-renderer.tsx +1 -0
- package/src/index.ts +0 -1
- package/src/multi-items/item-types.ts +1 -1
- package/src/multi-items/multi-renderer.tsx +4 -4
- package/src/perseus-api.tsx +0 -6
- package/src/perseus-types.ts +8 -3
- package/src/server-item-renderer.tsx +12 -13
- package/src/types.ts +0 -5
- package/src/util.ts +1 -1
- package/src/widgets/__stories__/expression.stories.tsx +0 -1
- 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 -2
- package/src/widgets/__tests__/expression.test.tsx +4 -15
- package/src/widgets/__tests__/randomizers.test.ts +53 -0
- package/src/widgets/expression.tsx +1 -5
- package/src/widgets/graded-group.tsx +1 -1
- package/src/widgets/image.tsx +27 -20
- package/src/widgets/label-image/strings.ts +3 -1
- package/src/widgets/label-image.tsx +2 -2
- package/src/widgets/passage-ref.tsx +1 -1
- package/src/widgets/passage.tsx +1 -1
- package/src/widgets/radio/widget.tsx +1 -1
- package/src/widgets/radio.ts +1 -1
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/item-renderer.d.ts +0 -125
- package/dist/mixins/provide-keypad.d.ts +0 -39
- package/src/__stories__/item-renderer.stories.tsx +0 -30
- package/src/__testdata__/item-renderer.testdata.ts +0 -299
- package/src/__tests__/__snapshots__/item-renderer.test.tsx.snap +0 -224
- package/src/__tests__/item-renderer.test.tsx +0 -930
- package/src/item-renderer.tsx +0 -529
- package/src/mixins/provide-keypad.tsx +0 -138
|
@@ -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";
|
|
@@ -16,10 +16,10 @@ import FixedToResponsive from "./fixed-to-responsive";
|
|
|
16
16
|
import Graphie from "./graphie";
|
|
17
17
|
import ImageLoader from "./image-loader";
|
|
18
18
|
|
|
19
|
+
import type {ImageProps} from "./image-loader";
|
|
19
20
|
import type {Coord} from "../interactive2/types";
|
|
20
21
|
import type {Size} from "../perseus-types";
|
|
21
22
|
import type {Alignment, Dimensions} from "../types";
|
|
22
|
-
import type {ImageProps} from "./image-loader";
|
|
23
23
|
|
|
24
24
|
// Minimum image width to make an image appear as zoomable.
|
|
25
25
|
const ZOOMABLE_THRESHOLD = 700;
|
|
@@ -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,
|
package/src/hints-renderer.tsx
CHANGED
|
@@ -59,6 +59,7 @@ class HintsRenderer extends React.Component<Props, State> {
|
|
|
59
59
|
this._cacheHintImages();
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// eslint-disable-next-line react/no-unsafe
|
|
62
63
|
UNSAFE_componentWillReceiveProps(nextProps: Props) {
|
|
63
64
|
// @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
|
|
64
65
|
if (nextProps.hintsVisible - this.props.hintsVisible > 1) {
|
package/src/index.ts
CHANGED
|
@@ -14,7 +14,6 @@ export {default as itemVersion} from "./item-version";
|
|
|
14
14
|
* Renderers
|
|
15
15
|
*/
|
|
16
16
|
export {default as ArticleRenderer} from "./article-renderer";
|
|
17
|
-
export {default as ItemRenderer} from "./item-renderer";
|
|
18
17
|
export {default as ServerItemRenderer} from "./server-item-renderer";
|
|
19
18
|
export {default as HintsRenderer} from "./hints-renderer";
|
|
20
19
|
export {default as HintRenderer} from "./hint-renderer";
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* interface, so it's compatible with our tree traversal functions.
|
|
9
9
|
* - And the various types of nodes that compose a tree.
|
|
10
10
|
*/
|
|
11
|
-
import type {WidgetDict, ImageDict} from "../types";
|
|
12
11
|
import type {Tree, ArrayNode, ObjectNode} from "./tree-types";
|
|
12
|
+
import type {WidgetDict, ImageDict} from "../types";
|
|
13
13
|
|
|
14
14
|
export type ContentNode = {
|
|
15
15
|
// TODO(mdr): When we first drafted the multi-item feature, we named
|
|
@@ -50,6 +50,10 @@ import Util from "../util";
|
|
|
50
50
|
import {itemToTree} from "./items";
|
|
51
51
|
import {buildMapper} from "./trees";
|
|
52
52
|
|
|
53
|
+
import type {Item, ContentNode, HintNode, TagsNode} from "./item-types";
|
|
54
|
+
import type {Shape, ArrayShape} from "./shape-types";
|
|
55
|
+
import type {Tree} from "./tree-types";
|
|
56
|
+
import type {TreeMapper, ContentMapper, HintMapper, Path} from "./trees";
|
|
53
57
|
import type {Widget} from "../renderer";
|
|
54
58
|
import type {
|
|
55
59
|
APIOptions,
|
|
@@ -57,10 +61,6 @@ import type {
|
|
|
57
61
|
PerseusDependenciesV2,
|
|
58
62
|
PerseusScore,
|
|
59
63
|
} from "../types";
|
|
60
|
-
import type {Item, ContentNode, HintNode, TagsNode} from "./item-types";
|
|
61
|
-
import type {Shape, ArrayShape} from "./shape-types";
|
|
62
|
-
import type {Tree} from "./tree-types";
|
|
63
|
-
import type {TreeMapper, ContentMapper, HintMapper, Path} from "./trees";
|
|
64
64
|
import type {PropsFor} from "@khanacademy/wonder-blocks-core";
|
|
65
65
|
|
|
66
66
|
type Hint = any; // TODO(mdr)
|
package/src/perseus-api.tsx
CHANGED
|
@@ -100,11 +100,6 @@ export const ApiOptions = {
|
|
|
100
100
|
// Indicates whether or not to use mobile styling.
|
|
101
101
|
isMobile: PropTypes.bool,
|
|
102
102
|
|
|
103
|
-
// Whether to use v1 (Legacy) Keypad for MathInput mobile or
|
|
104
|
-
// use the new v2 Keypad
|
|
105
|
-
// TODO [LC-1088]: remove after v2 release
|
|
106
|
-
useV2Keypad: PropTypes.bool,
|
|
107
|
-
|
|
108
103
|
// A function, called with a bool indicating whether use of the
|
|
109
104
|
// drawing area (scratchpad) should be allowed/disallowed.
|
|
110
105
|
// Previously handled by `Khan.scratchpad.enable/disable`
|
|
@@ -150,7 +145,6 @@ export const ApiOptions = {
|
|
|
150
145
|
defaults: {
|
|
151
146
|
isArticle: false,
|
|
152
147
|
isMobile: false,
|
|
153
|
-
useV2Keypad: false,
|
|
154
148
|
onInputError: function () {},
|
|
155
149
|
onFocusChange: function () {},
|
|
156
150
|
GroupMetadataEditor: StubTagEditor,
|
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
|
|
|
@@ -20,7 +20,6 @@ import {ApiOptions} from "./perseus-api";
|
|
|
20
20
|
import Renderer from "./renderer";
|
|
21
21
|
import Util from "./util";
|
|
22
22
|
|
|
23
|
-
import type {KeypadProps} from "./mixins/provide-keypad";
|
|
24
23
|
import type {APIOptions, FocusPath, PerseusDependenciesV2} from "./types";
|
|
25
24
|
import type {KeypadAPI} from "@khanacademy/math-input";
|
|
26
25
|
import type {
|
|
@@ -31,19 +30,18 @@ import type {
|
|
|
31
30
|
|
|
32
31
|
const {mapObject} = Objective;
|
|
33
32
|
|
|
34
|
-
type OwnProps =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
question: any;
|
|
41
|
-
};
|
|
42
|
-
problemNum?: number;
|
|
43
|
-
reviewMode?: boolean;
|
|
44
|
-
keypadElement?: KeypadAPI | null | undefined;
|
|
45
|
-
dependencies: PerseusDependenciesV2;
|
|
33
|
+
type OwnProps = {
|
|
34
|
+
apiOptions: APIOptions;
|
|
35
|
+
hintsVisible?: number;
|
|
36
|
+
item: {
|
|
37
|
+
hints: ReadonlyArray<any>;
|
|
38
|
+
question: any;
|
|
46
39
|
};
|
|
40
|
+
problemNum?: number;
|
|
41
|
+
reviewMode?: boolean;
|
|
42
|
+
keypadElement?: KeypadAPI | null | undefined;
|
|
43
|
+
dependencies: PerseusDependenciesV2;
|
|
44
|
+
};
|
|
47
45
|
|
|
48
46
|
type HOCProps = {
|
|
49
47
|
onRendered: (isRendered: boolean) => void;
|
|
@@ -104,6 +102,7 @@ export class ServerItemRenderer
|
|
|
104
102
|
this._fullyRendered = false;
|
|
105
103
|
}
|
|
106
104
|
|
|
105
|
+
// eslint-disable-next-line react/no-unsafe
|
|
107
106
|
UNSAFE_componentWillReceiveProps(nextProps: Props) {
|
|
108
107
|
this.setState({
|
|
109
108
|
questionHighlightedWidgets: [],
|
package/src/types.ts
CHANGED
|
@@ -185,10 +185,6 @@ export type APIOptions = Readonly<{
|
|
|
185
185
|
// from the math-input repo instead of the existing perseus math
|
|
186
186
|
// input components.
|
|
187
187
|
customKeypad?: boolean;
|
|
188
|
-
// Whether to use v1 (Legacy) Keypad for MathInput mobile or
|
|
189
|
-
// use the new v2 Keypad
|
|
190
|
-
// TODO [LC-1088]: remove after v2 release
|
|
191
|
-
useV2Keypad?: boolean;
|
|
192
188
|
// If this is provided, it is called instead of appending an instance
|
|
193
189
|
// of `math-input`'s keypad to the body. This is used by the native
|
|
194
190
|
// apps so they can have the keypad be defined on the native side.
|
|
@@ -362,7 +358,6 @@ export type APIOptionsWithDefaults = Readonly<
|
|
|
362
358
|
inModal: NonNullable<APIOptions["inModal"]>;
|
|
363
359
|
isArticle: NonNullable<APIOptions["isArticle"]>;
|
|
364
360
|
isMobile: NonNullable<APIOptions["isMobile"]>;
|
|
365
|
-
useV2Keypad: NonNullable<APIOptions["useV2Keypad"]>;
|
|
366
361
|
onFocusChange: NonNullable<APIOptions["onFocusChange"]>;
|
|
367
362
|
onInputError: NonNullable<APIOptions["onInputError"]>;
|
|
368
363
|
readOnly: NonNullable<APIOptions["readOnly"]>;
|
package/src/util.ts
CHANGED
|
@@ -3,8 +3,8 @@ import _ from "underscore";
|
|
|
3
3
|
|
|
4
4
|
import {Errors} from "./logging/log";
|
|
5
5
|
import {PerseusError} from "./perseus-error";
|
|
6
|
-
import * as GraphieUtil from "./util.graphie";
|
|
7
6
|
import KhanAnswerTypes from "./util/answer-types";
|
|
7
|
+
import * as GraphieUtil from "./util.graphie";
|
|
8
8
|
|
|
9
9
|
import type {Range} from "./perseus-types";
|
|
10
10
|
import type {Widget, PerseusScore} from "./types";
|
|
@@ -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
|
+
};
|