@khanacademy/perseus 16.0.0 → 17.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 +15 -0
- package/dist/article-renderer.d.ts +1 -2
- package/dist/components.d.ts +0 -1
- package/dist/es/index.css +0 -90
- package/dist/es/index.js +15 -691
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +0 -90
- package/dist/index.d.ts +0 -1
- package/dist/index.js +13 -692
- package/dist/index.js.map +1 -1
- package/dist/server-item-renderer.d.ts +42 -15
- package/dist/strings.js +0 -23
- package/package.json +2 -2
- package/src/__stories__/server-item-renderer.stories.tsx +20 -1
- package/src/__testdata__/server-item-renderer.testdata.ts +173 -0
- package/src/__tests__/server-item-renderer.test.tsx +38 -1
- package/src/article-renderer.tsx +13 -15
- package/src/components.ts +0 -1
- package/src/index.ts +0 -1
- package/src/server-item-renderer.tsx +26 -14
- package/src/styles/khan-exercise.css +0 -24
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/components/tex-buttons.d.ts +0 -25
- 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/components/__stories__/text-buttons.stories.tsx +0 -54
- package/src/components/tex-buttons.tsx +0 -334
- package/src/item-renderer.tsx +0 -529
- package/src/mixins/provide-keypad.tsx +0 -137
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import type { LegacyButtonSets } from "../perseus-types";
|
|
3
|
-
import type { Keys } from "@khanacademy/math-input";
|
|
4
|
-
type ButtonSet = (props: any) => [
|
|
5
|
-
React.ReactNode,
|
|
6
|
-
Keys,
|
|
7
|
-
string
|
|
8
|
-
];
|
|
9
|
-
type ButtonSets = {
|
|
10
|
-
readonly [key in LegacyButtonSets[number]]: ReadonlyArray<ButtonSet>;
|
|
11
|
-
};
|
|
12
|
-
declare const buttonSets: ButtonSets;
|
|
13
|
-
export type ButtonSetsType = ReadonlyArray<keyof typeof buttonSets>;
|
|
14
|
-
type Props = {
|
|
15
|
-
sets: ButtonSetsType;
|
|
16
|
-
onInsert: (key: Keys) => void;
|
|
17
|
-
className?: string;
|
|
18
|
-
convertDotToTimes?: boolean;
|
|
19
|
-
};
|
|
20
|
-
declare class TexButtons extends React.Component<Props> {
|
|
21
|
-
static buttonSets: typeof buttonSets;
|
|
22
|
-
static buttonSetsType: any;
|
|
23
|
-
render(): React.ReactNode;
|
|
24
|
-
}
|
|
25
|
-
export default TexButtons;
|
package/dist/item-renderer.d.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import HintsRenderer from "./hints-renderer";
|
|
3
|
-
import Renderer from "./renderer";
|
|
4
|
-
import type { KeypadProps } from "./mixins/provide-keypad";
|
|
5
|
-
import type { PerseusItem } from "./perseus-types";
|
|
6
|
-
import type { APIOptions, FocusPath, PerseusDependenciesV2 } from "./types";
|
|
7
|
-
import type { KEScore } from "@khanacademy/perseus-core";
|
|
8
|
-
import type { LinterContextProps } from "@khanacademy/perseus-linter";
|
|
9
|
-
type Props = // These props are used by the ProvideKeypad mixin.
|
|
10
|
-
KeypadProps & {
|
|
11
|
-
apiOptions: APIOptions;
|
|
12
|
-
controlPeripherals?: boolean;
|
|
13
|
-
workAreaSelector: string;
|
|
14
|
-
hintsAreaSelector: string;
|
|
15
|
-
initialHintsVisible?: number;
|
|
16
|
-
item: PerseusItem | any;
|
|
17
|
-
onShowCalculator?: () => unknown;
|
|
18
|
-
onShowChi2Table?: () => unknown;
|
|
19
|
-
onShowPeriodicTable?: (options?: {
|
|
20
|
-
showKey: boolean;
|
|
21
|
-
}) => unknown;
|
|
22
|
-
onShowTTable?: () => unknown;
|
|
23
|
-
onShowZTable?: () => unknown;
|
|
24
|
-
problemNum: number;
|
|
25
|
-
reviewMode: boolean;
|
|
26
|
-
savedState: any;
|
|
27
|
-
linterContext: LinterContextProps;
|
|
28
|
-
legacyPerseusLint?: ReadonlyArray<string>;
|
|
29
|
-
dependencies: PerseusDependenciesV2;
|
|
30
|
-
};
|
|
31
|
-
type DefaultProps = {
|
|
32
|
-
apiOptions: Props["apiOptions"];
|
|
33
|
-
controlPeripherals: Props["controlPeripherals"];
|
|
34
|
-
hintsAreaSelector: Props["hintsAreaSelector"];
|
|
35
|
-
initialHintsVisible: Props["initialHintsVisible"];
|
|
36
|
-
linterContext: Props["linterContext"];
|
|
37
|
-
reviewMode: Props["reviewMode"];
|
|
38
|
-
workAreaSelector: Props["workAreaSelector"];
|
|
39
|
-
};
|
|
40
|
-
type State = {
|
|
41
|
-
keypadElement: HTMLElement | null | undefined;
|
|
42
|
-
hintsVisible: number;
|
|
43
|
-
questionCompleted: boolean;
|
|
44
|
-
questionHighlightedWidgets: ReadonlyArray<string>;
|
|
45
|
-
};
|
|
46
|
-
type SerializedState = {
|
|
47
|
-
hints: any;
|
|
48
|
-
question: {
|
|
49
|
-
[id: string]: any;
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
/**
|
|
53
|
-
* @depreciated use ServerItemRenderer
|
|
54
|
-
*/
|
|
55
|
-
declare class ItemRenderer extends React.Component<Props, State> {
|
|
56
|
-
questionRenderer: Renderer;
|
|
57
|
-
hintsRenderer: React.ElementRef<typeof HintsRenderer> | null | undefined;
|
|
58
|
-
_currentFocus: FocusPath;
|
|
59
|
-
static defaultProps: DefaultProps;
|
|
60
|
-
constructor(props: Props);
|
|
61
|
-
componentDidMount(): void;
|
|
62
|
-
UNSAFE_componentWillReceiveProps(nextProps: Props): void;
|
|
63
|
-
componentDidUpdate(): void;
|
|
64
|
-
componentWillUnmount(): void;
|
|
65
|
-
keypadElement(): any;
|
|
66
|
-
update(): void;
|
|
67
|
-
_handleFocusChange: (newFocus: FocusPath, oldFocus: FocusPath) => void;
|
|
68
|
-
_setCurrentFocus(newFocus: FocusPath): void;
|
|
69
|
-
_onRendererBlur(blurPath: FocusPath): void;
|
|
70
|
-
/**
|
|
71
|
-
* Accepts a question area widgetId, or an answer area widgetId of
|
|
72
|
-
* the form "answer-input-number 1", or the string "answer-area"
|
|
73
|
-
* for the whole answer area (if the answer area is a single widget).
|
|
74
|
-
*/
|
|
75
|
-
_setWidgetProps(widgetId: string, newProps: any, callback: any): void;
|
|
76
|
-
/**
|
|
77
|
-
* Sets the input value into the widget found at `path`. The `focus`
|
|
78
|
-
* function provided is called when all affected widgets have been
|
|
79
|
-
* re-rendered with the new value.
|
|
80
|
-
*/
|
|
81
|
-
setInputValue(path: FocusPath, newValue: string, focus: () => void): void;
|
|
82
|
-
focusPath(path: FocusPath): void;
|
|
83
|
-
blurPath(path: FocusPath): void;
|
|
84
|
-
getDOMNodeForPath(path: FocusPath): Element | Text | null | undefined;
|
|
85
|
-
getGrammarTypeForPath(path: FocusPath): string | null | undefined;
|
|
86
|
-
getInputPaths(): ReadonlyArray<FocusPath>;
|
|
87
|
-
handleInteractWithWidget: (widgetId: string) => void;
|
|
88
|
-
focus(): boolean | null | undefined;
|
|
89
|
-
blur(): void;
|
|
90
|
-
showHint(): void;
|
|
91
|
-
getNumHints(): number;
|
|
92
|
-
/**
|
|
93
|
-
* Grades the item.
|
|
94
|
-
*
|
|
95
|
-
* Returns a KE-style score of {
|
|
96
|
-
* empty: bool,
|
|
97
|
-
* correct: bool,
|
|
98
|
-
* message: string|null,
|
|
99
|
-
* guess: Array
|
|
100
|
-
* }
|
|
101
|
-
*/
|
|
102
|
-
scoreInput(): KEScore;
|
|
103
|
-
/**
|
|
104
|
-
* Returns an array of all widget IDs in the order they occur in
|
|
105
|
-
* the question content.
|
|
106
|
-
*/
|
|
107
|
-
getWidgetIds(): ReadonlyArray<string>;
|
|
108
|
-
/**
|
|
109
|
-
* Returns an object mapping from widget ID to KE-style score.
|
|
110
|
-
* The keys of this object are the values of the array returned
|
|
111
|
-
* from `getWidgetIds`.
|
|
112
|
-
*/
|
|
113
|
-
scoreWidgets(): {
|
|
114
|
-
[id: string]: KEScore;
|
|
115
|
-
};
|
|
116
|
-
/**
|
|
117
|
-
* Get a representation of the current state of the item.
|
|
118
|
-
*/
|
|
119
|
-
getSerializedState(): SerializedState;
|
|
120
|
-
restoreSerializedState(state: SerializedState, callback?: () => void): void;
|
|
121
|
-
showRationalesForCurrentlySelectedChoices(): void;
|
|
122
|
-
deselectIncorrectSelectedChoices(): void;
|
|
123
|
-
render(): React.ReactNode;
|
|
124
|
-
}
|
|
125
|
-
export default ItemRenderer;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A mixin that renders a custom software keypad in additional to the base
|
|
3
|
-
* component. The base component will receive blur events when the keypad is
|
|
4
|
-
* dismissed and can access the keypad element itself so as to manage its
|
|
5
|
-
* activation and dismissal.
|
|
6
|
-
*
|
|
7
|
-
* TODO(charlie): This would make a nicer higher-order component, except that
|
|
8
|
-
* we need to expose methods on the base component (i.e., `ItemRenderer`). When
|
|
9
|
-
* `ItemRenderer` and friends are written as ES6 Classes, we can have them
|
|
10
|
-
* extend a `ProvideKeypad` component instead of using this mixin.
|
|
11
|
-
*/
|
|
12
|
-
import type { APIOptions } from "../types";
|
|
13
|
-
import type { CSSProperties } from "aphrodite";
|
|
14
|
-
export type KeypadProps = {
|
|
15
|
-
keypadStyle?: CSSProperties;
|
|
16
|
-
};
|
|
17
|
-
export type KeypadApiOptions = {
|
|
18
|
-
customKeypad: boolean;
|
|
19
|
-
nativeKeypadProxy: any;
|
|
20
|
-
};
|
|
21
|
-
type KeypadMixinHostComponent = {
|
|
22
|
-
_keypadContainer?: HTMLDivElement | null;
|
|
23
|
-
props: {
|
|
24
|
-
apiOptions: APIOptions;
|
|
25
|
-
blur?: () => void;
|
|
26
|
-
};
|
|
27
|
-
state: {
|
|
28
|
-
keypadElement: any | null;
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
declare const ProvideKeypad: {
|
|
32
|
-
readonly getInitialState: (this: KeypadMixinHostComponent) => {
|
|
33
|
-
keypadElement: any | null;
|
|
34
|
-
};
|
|
35
|
-
readonly componentDidMount: (this: KeypadMixinHostComponent) => void;
|
|
36
|
-
readonly componentWillUnmount: (this: KeypadMixinHostComponent) => void;
|
|
37
|
-
readonly keypadElement: (this: KeypadMixinHostComponent) => any;
|
|
38
|
-
};
|
|
39
|
-
export default ProvideKeypad;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import {ItemRendererWithDebugUI} from "../../../../testing/item-renderer-with-debug-ui";
|
|
4
|
-
import {
|
|
5
|
-
itemWithInput,
|
|
6
|
-
labelImageItem,
|
|
7
|
-
codeblockItem,
|
|
8
|
-
} from "../__testdata__/item-renderer.testdata";
|
|
9
|
-
|
|
10
|
-
type StoryArgs = Record<any, any>;
|
|
11
|
-
|
|
12
|
-
type Story = {
|
|
13
|
-
title: string;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export default {
|
|
17
|
-
title: "Perseus/Renderers/Item Renderer",
|
|
18
|
-
} as Story;
|
|
19
|
-
|
|
20
|
-
export const InputNumberItem = (args: StoryArgs): React.ReactElement => {
|
|
21
|
-
return <ItemRendererWithDebugUI item={itemWithInput} />;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export const LabelImageItem = (args: StoryArgs): React.ReactElement => {
|
|
25
|
-
return <ItemRendererWithDebugUI item={labelImageItem} />;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export const CodeblockItem = (args: StoryArgs): React.ReactElement => {
|
|
29
|
-
return <ItemRendererWithDebugUI item={codeblockItem} />;
|
|
30
|
-
};
|
|
@@ -1,299 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
InputNumberWidget,
|
|
3
|
-
LabelImageWidget,
|
|
4
|
-
PerseusItem,
|
|
5
|
-
PerseusRenderer,
|
|
6
|
-
} from "../perseus-types";
|
|
7
|
-
|
|
8
|
-
export const codeblockItem: PerseusItem = {
|
|
9
|
-
question: {
|
|
10
|
-
content:
|
|
11
|
-
'Charlotte is writing code to turn sentences into Pig Latin.\n\nThis is what she has so far:\n\n```\nsentence ← ""\nword1 ← "Hello"\nfirstLetter1 ← SUBSTRING(word1, 1, 1)\notherLetters1 ← SUBSTRING(word1, 2, LENGTH(word1) - 1)\npigLatin1 ← CONCAT(otherLetters1, firstLetter1, "ay")\nsentence ← CONCAT(sentence, pigLatin1, " ")\nword2 ← "Mister"\nfirstLetter2 ← SUBSTRING(word2, 1, 1)\notherLetters2 ← SUBSTRING(word2, 2, LENGTH(word2) - 1)\npigLatin2 ← CONCAT(otherLetters2, firstLetter2, "ay")\nsentence ← CONCAT(sentence, pigLatin2, " ")\nword3 ← "Rogers"\nfirstLetter3 ← SUBSTRING(word3, 1, 1)\notherLetters3 ← SUBSTRING(word3, 2, LENGTH(word3) - 1)\npigLatin3 ← CONCAT(otherLetters3, firstLetter3, "ay")\nsentence ← CONCAT(sentence, pigLatin3, " ")\nDISPLAY(sentence)\n```\n\nThe code relies on two string procedures:\n\nName | Description\n- | - | -\n`CONCATENATE (string1, string2, ...)` | Returns a single string that combines the provided strings together in order.\n`SUBSTRING (string, startPos, numChars)` | Returns a substring of `string` starting at `startPos` of length `numChars`. The first character in the string is at position 1.\n\nA friend points out that she can reduce the complexity of her code by using the abstractions of lists and loops. \n\nCharlotte decides to "refactor" the code, to rewrite it so that it produces the same output but is structured better.\n\n**Which of these is the best refactor of the code?**\n\n[[☃ radio 1]]\n\n🤔[[☃ explanation 1]]',
|
|
12
|
-
images: {},
|
|
13
|
-
widgets: {
|
|
14
|
-
"radio 1": {
|
|
15
|
-
type: "radio",
|
|
16
|
-
alignment: "default",
|
|
17
|
-
static: false,
|
|
18
|
-
graded: true,
|
|
19
|
-
options: {
|
|
20
|
-
choices: [
|
|
21
|
-
{
|
|
22
|
-
content:
|
|
23
|
-
'```\nwords ← ["Hello", "Mister", "Rogers"]\nsentence ← ""\nFOR EACH word IN words\n{\n firstLetter ← SUBSTRING(word, 1, 1)\n otherLetters ← SUBSTRING(word, 2, LENGTH(word) - 1)\n pigLatin ← CONCAT(otherLetters, firstLetter, "ay")\n sentence ← CONCAT(sentence, pigLatin, " ")\n}\nDISPLAY(sentence)\n```',
|
|
24
|
-
correct: true,
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
content:
|
|
28
|
-
'```\nwords ← ["Hello", "Mister", "Rogers"]\nsentence ← ""\nFOR EACH word IN words\n{\n firstLetter ← SUBSTRING(words[1], 1, 1)\n otherLetters ← SUBSTRING(words[1], 2, LENGTH(words[1]) - 1)\n pigLatin ← CONCAT(otherLetters, firstLetter, "ay")\n sentence ← CONCAT(sentence, pigLatin, " ")\n}\nDISPLAY(sentence)\n```',
|
|
29
|
-
correct: false,
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
isNoneOfTheAbove: false,
|
|
33
|
-
content:
|
|
34
|
-
'```\nwords ← ["Hello", "Mister", "Rogers"]\nFOR EACH word IN words\n{\n sentence ← ""\n firstLetter ← SUBSTRING(word, 1, 1)\n otherLetters ← SUBSTRING(word, 2, LENGTH(word) - 1)\n pigLatin ← CONCAT(otherLetters, firstLetter, "ay")\n sentence ← CONCAT(sentence, pigLatin, " ")\n}\nDISPLAY(sentence)\n```',
|
|
35
|
-
correct: false,
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
isNoneOfTheAbove: false,
|
|
39
|
-
content:
|
|
40
|
-
'```\nwords ← ["Hello", "Mister", "Rogers"]\nsentence ← ""\nFOR EACH word IN words\n{\n firstLetter ← SUBSTRING(word, 1, 1)\n otherLetters ← SUBSTRING(word, 2, LENGTH(word) - 1)\n pigLatin ← CONCAT(otherLetters, firstLetter, "ay")\n sentence ← CONCAT(sentence, pigLatin, " ")\n DISPLAY(sentence)\n}\n```',
|
|
41
|
-
correct: false,
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
isNoneOfTheAbove: false,
|
|
45
|
-
content:
|
|
46
|
-
'```\nwords ← ["Hello", "Mister", "Rogers"]\nFOR EACH word IN words\n{\n sentence ← ""\n firstLetter ← SUBSTRING(word, 1, 1)\n otherLetters ← SUBSTRING(word, 2, LENGTH(word) - 1)\n pigLatin ← CONCAT(otherLetters, firstLetter, "ay")\n sentence ← CONCAT(sentence, pigLatin, " ")\n DISPLAY(sentence)\n}\n```',
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
randomize: true,
|
|
50
|
-
multipleSelect: false,
|
|
51
|
-
countChoices: false,
|
|
52
|
-
displayCount: null,
|
|
53
|
-
hasNoneOfTheAbove: false,
|
|
54
|
-
deselectEnabled: false,
|
|
55
|
-
},
|
|
56
|
-
version: {
|
|
57
|
-
major: 1,
|
|
58
|
-
minor: 0,
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
"explanation 1": {
|
|
62
|
-
type: "explanation",
|
|
63
|
-
alignment: "default",
|
|
64
|
-
static: false,
|
|
65
|
-
graded: true,
|
|
66
|
-
options: {
|
|
67
|
-
static: false,
|
|
68
|
-
showPrompt: "What language is the code in?",
|
|
69
|
-
hidePrompt: "Hide explanation",
|
|
70
|
-
explanation:
|
|
71
|
-
"This code is written in **pseudocode**. You can't actually run pseudocode, but we use it for planning programs and for thinking about code independently of specific programming language. \n\nAP CSP exam questions use pseudocode, so it's helpful to become familiar with it.\n\nHere is a reference for the pseudocode used here:\n\n****\n\n`a ← expression`\n\nEvaluates `expression` and assigns the result to the variable `a`.\n\n****\n\n`list ← [value1, value2, value3]`\n\nCreates a list with 3 values. The first value is located at index 1, the second at index 2, the third at index 3.\n\n****\n\n```\nFOR EACH value IN list\n{\n <instructions>\n}\n```\n\nAssigns `value` to the value of each element in `list` sequentially, starting from the first element and ending with the last element. Executes the code in `instructions` once for each `value`.\n",
|
|
72
|
-
widgets: {},
|
|
73
|
-
},
|
|
74
|
-
version: {
|
|
75
|
-
major: 0,
|
|
76
|
-
minor: 0,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
answerArea: {
|
|
82
|
-
calculator: false,
|
|
83
|
-
chi2Table: false,
|
|
84
|
-
periodicTable: false,
|
|
85
|
-
tTable: false,
|
|
86
|
-
zTable: false,
|
|
87
|
-
},
|
|
88
|
-
itemDataVersion: {
|
|
89
|
-
major: 0,
|
|
90
|
-
minor: 1,
|
|
91
|
-
},
|
|
92
|
-
hints: [
|
|
93
|
-
{
|
|
94
|
-
replace: false,
|
|
95
|
-
content:
|
|
96
|
-
'The first step in refactoring this code is to recognize the related variables that can be combined into a list.\n\nIn this case, that\'s `word1`, `word2`, and `word3` from the original code. We can tell they\'re related from the naming, but more importantly, from the way the same code is repeated for each of them.\n\nWe\'ll store the values of those 3 variables in a single list:\n\n```words ← ["Hello", "Mister", "Rogers"]```',
|
|
97
|
-
images: {},
|
|
98
|
-
widgets: {},
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
replace: false,
|
|
102
|
-
content:
|
|
103
|
-
"The next step in refactoring this code is to identify the code that is only executed once versus the code that is executed for each element in that list.",
|
|
104
|
-
images: {},
|
|
105
|
-
widgets: {},
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
replace: false,
|
|
109
|
-
content:
|
|
110
|
-
'We can see that there are 3 blocks of code that deal with each of the original variables.\n\nCode for `word1`:\n\n```\nword1 ← "Hello"\nfirstLetter1 ← SUBSTRING(word1, 1, 1)\notherLetters1 ← SUBSTRING(word1, 2, LENGTH(word1) - 1)\npigLatin1 ← CONCAT(otherLetters1, firstLetter1, "ay")\nsentence ← CONCAT(sentence, pigLatin1, " ")\n```\n\n\nCode for `word2`:\n\n```\nword2 ← "Mister"\nfirstLetter2 ← SUBSTRING(word2, 1, 1)\notherLetters2 ← SUBSTRING(word2, 2, LENGTH(word2) - 1)\npigLatin2 ← CONCAT(otherLetters2, firstLetter2, "ay")\nsentence ← CONCAT(sentence, pigLatin2, " ")\n```\n\nCode for `word3`:\n\n```\nword3 ← "Rogers"\nfirstLetter3 ← SUBSTRING(word3, 1, 1)\notherLetters3 ← SUBSTRING(word3, 2, LENGTH(word3) - 1)\npigLatin3 ← CONCAT(otherLetters3, firstLetter3, "ay")\nsentence ← CONCAT(sentence, pigLatin3, " ")\n```\n\nEach of these blocks of code do the same operations:\n\n1. store the word in a variable\n2. store a substring of the word, the first letter in it\n3. store another substring of the word, the rest of the letters\n4. store a new string that concatenates the two substrings and "ay"\n5. concatenate that string with the existing `sentence`',
|
|
111
|
-
images: {},
|
|
112
|
-
widgets: {},
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
replace: false,
|
|
116
|
-
content:
|
|
117
|
-
'Now that we\'ve put the variables into a list, we can use a loop to execute those same operations on each element:\n\n```\nFOR EACH word in words\n{\n firstLetter ← SUBSTRING(word, 1, 1)\n otherLetters ← SUBSTRING(word, 2, LENGTH(word) - 1)\n pigLatin ← CONCAT(otherLetters, firstLetter, "ay")\n sentence ← CONCAT(sentence, pigLatin, " ")\n}\n```\n\nYou\'ll notice there are only four lines of code inside the loop, versus the five lines in the grouped blocks from before. That\'s because the `FOR EACH` header takes care of storing the current list element in `word`. The value of `word` will first be "Hello", then "Mister", then "Rogers", according to the order of the values in the list.\n',
|
|
118
|
-
images: {},
|
|
119
|
-
widgets: {},
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
replace: false,
|
|
123
|
-
content:
|
|
124
|
-
'There are two lines of code that we haven\'t refactored yet.\n\nOne is the very first line of the original code:\n\n`sentence ← ""`\n\nIn the new code, this needs to happen _before_ the `FOR EACH` loop, since the value of `sentence` is manipulated inside the loop.',
|
|
125
|
-
images: {},
|
|
126
|
-
widgets: {},
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
replace: false,
|
|
130
|
-
content:
|
|
131
|
-
"The remaining line is the very last line of the original code:\n\n```DISPLAY(sentence)```\n\nIn the new code, this needs to happen _after_ the `FOR EACH` loop, since it should display the value after all the concatenations happen.",
|
|
132
|
-
images: {},
|
|
133
|
-
widgets: {},
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
replace: false,
|
|
137
|
-
content:
|
|
138
|
-
'Thus, this is the best refactor of the original code to use lists and loops:\n\n```\nwords ← ["Hello", "Mister", "Rogers"]\nsentence ← ""\nFOR EACH word IN words\n{\n firstLetter ← SUBSTRING(word, 1, 1)\n otherLetters ← SUBSTRING(word, 2, LENGTH(word) - 1)\n pigLatin ← CONCAT(otherLetters, firstLetter, "ay")\n sentence ← CONCAT(sentence, pigLatin, " ")\n}\nDISPLAY(sentence)\n```',
|
|
139
|
-
images: {},
|
|
140
|
-
widgets: {},
|
|
141
|
-
},
|
|
142
|
-
],
|
|
143
|
-
_multi: null,
|
|
144
|
-
answer: null,
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
export const itemWithInput: PerseusItem = {
|
|
148
|
-
question: {
|
|
149
|
-
content:
|
|
150
|
-
"Enter the number $$-42$$ in the box: [[\u2603 input-number 1]]",
|
|
151
|
-
images: {},
|
|
152
|
-
widgets: {
|
|
153
|
-
"input-number 1": {
|
|
154
|
-
type: "input-number",
|
|
155
|
-
graded: true,
|
|
156
|
-
options: {
|
|
157
|
-
answerType: "number",
|
|
158
|
-
value: "-42",
|
|
159
|
-
simplify: "required",
|
|
160
|
-
size: "normal",
|
|
161
|
-
inexact: false,
|
|
162
|
-
maxError: 0.1,
|
|
163
|
-
},
|
|
164
|
-
} as InputNumberWidget,
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
hints: [
|
|
168
|
-
{content: "Hint #1", images: {}, widgets: {}},
|
|
169
|
-
{content: "Hint #2", images: {}, widgets: {}},
|
|
170
|
-
{content: "Hint #3", images: {}, widgets: {}},
|
|
171
|
-
],
|
|
172
|
-
answerArea: null,
|
|
173
|
-
_multi: null,
|
|
174
|
-
itemDataVersion: {major: 0, minor: 0},
|
|
175
|
-
answer: null,
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
export const labelImageItem: PerseusItem = {
|
|
179
|
-
answerArea: {
|
|
180
|
-
calculator: false,
|
|
181
|
-
chi2Table: false,
|
|
182
|
-
periodicTable: false,
|
|
183
|
-
tTable: false,
|
|
184
|
-
zTable: false,
|
|
185
|
-
},
|
|
186
|
-
_multi: null,
|
|
187
|
-
answer: null,
|
|
188
|
-
hints: [],
|
|
189
|
-
itemDataVersion: {major: 0, minor: 1},
|
|
190
|
-
question: {
|
|
191
|
-
content:
|
|
192
|
-
"Catherine created a chart and a bar graph to show how many dogs of each breed the animal shelter placed into good homes last year. \n\nDog breed | Number of dogs \n:- | :-: \nBulldog | $32$ \nGreyhound | $72$ \nMastiff | $56$ \nCollie | $40$ \n\n**Label each bar on the bar graph.**\n\n[[☃ label-image 1]]\n\n\n\n",
|
|
193
|
-
images: {
|
|
194
|
-
"web+graphie://ka-perseus-graphie.s3.amazonaws.com/2f60b533892fe6cb9351f704ffdada8ae4bc655c":
|
|
195
|
-
{height: 365, width: 503},
|
|
196
|
-
"web+graphie://ka-perseus-graphie.s3.amazonaws.com/75f937c6ea133d655dd9f698a58182efd6cc85d4":
|
|
197
|
-
{height: 293, width: 331},
|
|
198
|
-
},
|
|
199
|
-
widgets: {
|
|
200
|
-
"label-image 1": {
|
|
201
|
-
alignment: "default",
|
|
202
|
-
graded: true,
|
|
203
|
-
static: false,
|
|
204
|
-
type: "label-image",
|
|
205
|
-
version: {major: 0, minor: 0},
|
|
206
|
-
options: {
|
|
207
|
-
choices: ["Bulldog", "Greyhound", "Mastiff", "Collie"],
|
|
208
|
-
hideChoicesFromInstructions: true,
|
|
209
|
-
imageAlt:
|
|
210
|
-
"A bar graph with four bar lines shows the horizontal axis labeled Dog breed and the vertical axis labeled Number of dogs. The vertical axis is labeled from the bottom of the axis to the top of the axis as follows: 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, and 80. The horizontal axis has, from left to right, four unlabeled bar lines as follows: the first unlabeled bar line extends to 56, the second unlabeled bar line extends to 72, the third unlabeled bar line extends to 40, and the fourth unlabeled bar line extends to 32.",
|
|
211
|
-
imageHeight: 293,
|
|
212
|
-
imageUrl:
|
|
213
|
-
"web+graphie://ka-perseus-graphie.s3.amazonaws.com/64ace45942546183c3dc842cb0e9e4f64a312727",
|
|
214
|
-
imageWidth: 331,
|
|
215
|
-
markers: [
|
|
216
|
-
{
|
|
217
|
-
answers: ["Bulldog"],
|
|
218
|
-
label: "The fourth unlabeled bar line.",
|
|
219
|
-
x: 87.3,
|
|
220
|
-
y: 90.2,
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
answers: ["Collie"],
|
|
224
|
-
label: "The third unlabeled bar line.",
|
|
225
|
-
x: 67.7,
|
|
226
|
-
y: 90.5,
|
|
227
|
-
},
|
|
228
|
-
{
|
|
229
|
-
answers: ["Greyhound"],
|
|
230
|
-
label: "The second unlabeled bar line.",
|
|
231
|
-
x: 47.7,
|
|
232
|
-
y: 90.2,
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
answers: ["Mastiff"],
|
|
236
|
-
label: "The first unlabeled bar line.",
|
|
237
|
-
x: 27.8,
|
|
238
|
-
y: 90.5,
|
|
239
|
-
},
|
|
240
|
-
],
|
|
241
|
-
multipleAnswers: false,
|
|
242
|
-
static: false,
|
|
243
|
-
},
|
|
244
|
-
} as LabelImageWidget,
|
|
245
|
-
},
|
|
246
|
-
},
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
// Note that if this item is used, you _must_ first register the MockWidget
|
|
250
|
-
export const mockedItem: PerseusItem = {
|
|
251
|
-
// The mock widget type is not part of the PerseusWidget type union (and
|
|
252
|
-
// we don't want to make it such to avoid polluting our production types
|
|
253
|
-
// for test purposes) so we force TypeScript to accept it here.
|
|
254
|
-
// @ts-expect-error - TS2352 - Conversion of type '{ content: string; images: {}; widgets: { "mock-widget 1": { graded: true; version: { major: number; minor: number; }; static: false; type: "mock-widget"; options: { static: false; smiling: boolean; }; alignment: string; }; "mock-widget 2": { ...; }; "mock-widget 3": { ...; }; "mock-widget 4": { ...; }; }; }' to type 'PerseusRenderer' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
|
|
255
|
-
question: {
|
|
256
|
-
content:
|
|
257
|
-
"Mock widget ==> [[\u2603 mock-widget 1]] [[\u2603 mock-widget 2]] [[\u2603 mock-widget 3]] [[\u2603 mock-widget 4]]",
|
|
258
|
-
images: {},
|
|
259
|
-
widgets: {
|
|
260
|
-
"mock-widget 1": {
|
|
261
|
-
graded: true,
|
|
262
|
-
version: {major: 0, minor: 0},
|
|
263
|
-
static: false,
|
|
264
|
-
type: "mock-widget",
|
|
265
|
-
options: {static: false, smiling: true},
|
|
266
|
-
alignment: "default",
|
|
267
|
-
},
|
|
268
|
-
"mock-widget 2": {
|
|
269
|
-
graded: true,
|
|
270
|
-
version: {major: 0, minor: 0},
|
|
271
|
-
static: false,
|
|
272
|
-
type: "mock-widget",
|
|
273
|
-
options: {static: false, smiling: false},
|
|
274
|
-
alignment: "default",
|
|
275
|
-
},
|
|
276
|
-
"mock-widget 3": {
|
|
277
|
-
graded: true,
|
|
278
|
-
version: {major: 0, minor: 0},
|
|
279
|
-
static: false,
|
|
280
|
-
type: "mock-widget",
|
|
281
|
-
options: {static: false, smiling: true},
|
|
282
|
-
alignment: "default",
|
|
283
|
-
},
|
|
284
|
-
"mock-widget 4": {
|
|
285
|
-
graded: true,
|
|
286
|
-
version: {major: 0, minor: 0},
|
|
287
|
-
static: false,
|
|
288
|
-
type: "mock-widget",
|
|
289
|
-
options: {static: false, smiling: false},
|
|
290
|
-
alignment: "default",
|
|
291
|
-
},
|
|
292
|
-
},
|
|
293
|
-
} as PerseusRenderer,
|
|
294
|
-
hints: [],
|
|
295
|
-
answerArea: null,
|
|
296
|
-
_multi: null,
|
|
297
|
-
itemDataVersion: {major: 0, minor: 0},
|
|
298
|
-
answer: null,
|
|
299
|
-
};
|