@khanacademy/perseus-editor 2.12.1 → 2.14.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 +29 -0
- package/dist/es/index.js +40 -48
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +38 -50
- package/dist/index.js.map +1 -1
- package/dist/item-extras-editor.d.ts +12 -12
- package/dist/widgets/expression-editor.d.ts +1 -5
- package/dist/widgets/label-image/behavior.d.ts +4 -1
- package/package.json +9 -9
- package/src/__tests__/items-extras-editors.test.tsx +68 -0
- package/src/item-extras-editor.tsx +17 -13
- package/src/widgets/__tests__/expression-editor.test.tsx +347 -0
- package/src/widgets/expression-editor.tsx +34 -70
- package/src/widgets/label-image/__stories__/behavior.stories.tsx +1 -1
- package/src/widgets/label-image/behavior.tsx +32 -0
- package/src/widgets/label-image-editor.tsx +1 -0
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -17,7 +17,7 @@ import SortableArea from "../components/sortable";
|
|
|
17
17
|
|
|
18
18
|
import type {PerseusExpressionWidgetOptions} from "@khanacademy/perseus";
|
|
19
19
|
|
|
20
|
-
const {InfoTip, PropCheckBox
|
|
20
|
+
const {InfoTip, PropCheckBox} = components;
|
|
21
21
|
|
|
22
22
|
type Props = {
|
|
23
23
|
widgetId?: any;
|
|
@@ -36,6 +36,15 @@ type DefaultProps = {
|
|
|
36
36
|
functions: Props["functions"];
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
const buttonSetsList: any = [
|
|
40
|
+
"basic",
|
|
41
|
+
"trig",
|
|
42
|
+
"prealgebra",
|
|
43
|
+
"logarithms",
|
|
44
|
+
"basic relations",
|
|
45
|
+
"advanced relations",
|
|
46
|
+
];
|
|
47
|
+
|
|
39
48
|
const parseAnswerKey = ({key}: AnswerForm): number => {
|
|
40
49
|
// We don't throw here because there is data stored in some
|
|
41
50
|
// exercises/articles where the answer forms don't have a key. If we throw,
|
|
@@ -67,11 +76,7 @@ const _makeNewKey = (answerForms: ReadonlyArray<AnswerForm>) => {
|
|
|
67
76
|
return usedKeys.length;
|
|
68
77
|
};
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
isTex: boolean;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
class ExpressionEditor extends React.Component<Props, State> {
|
|
79
|
+
class ExpressionEditor extends React.Component<Props> {
|
|
75
80
|
static widgetName = "expression" as const;
|
|
76
81
|
|
|
77
82
|
static defaultProps: DefaultProps = {
|
|
@@ -81,29 +86,6 @@ class ExpressionEditor extends React.Component<Props, State> {
|
|
|
81
86
|
functions: ["f", "g", "h"],
|
|
82
87
|
};
|
|
83
88
|
|
|
84
|
-
constructor(props: Props) {
|
|
85
|
-
super(props);
|
|
86
|
-
// Is the format of `value` TeX or plain text?
|
|
87
|
-
// TODO(alex): Remove after backfilling everything to TeX
|
|
88
|
-
// TODO(joel) - sucks if you edit some expression without
|
|
89
|
-
// backslashes or curly braces, then come back to the question and
|
|
90
|
-
// it's surprisingly not TeX anymore.
|
|
91
|
-
|
|
92
|
-
let isTex;
|
|
93
|
-
// default to TeX if new;
|
|
94
|
-
if (props.answerForms.length === 0) {
|
|
95
|
-
isTex = true;
|
|
96
|
-
} else {
|
|
97
|
-
isTex = props.answerForms.some((form) => {
|
|
98
|
-
const {value} = form;
|
|
99
|
-
// only TeX has backslashes and curly braces
|
|
100
|
-
return value.indexOf("\\") !== -1 || value.indexOf("{") !== -1;
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
this.state = {isTex};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
89
|
change(...args) {
|
|
108
90
|
return Changeable.change.apply(this, args);
|
|
109
91
|
}
|
|
@@ -156,38 +138,31 @@ class ExpressionEditor extends React.Component<Props, State> {
|
|
|
156
138
|
);
|
|
157
139
|
|
|
158
140
|
// checkboxes to choose which sets of input buttons are shown
|
|
159
|
-
const buttonSetChoices =
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
{name}
|
|
180
|
-
</label>
|
|
181
|
-
);
|
|
182
|
-
},
|
|
183
|
-
);
|
|
141
|
+
const buttonSetChoices = buttonSetsList.map((name) => {
|
|
142
|
+
// The first one gets special cased to always be checked, disabled,
|
|
143
|
+
// and float left.
|
|
144
|
+
const isFirst = name === "basic";
|
|
145
|
+
const checked =
|
|
146
|
+
this.props.buttonSets.includes(
|
|
147
|
+
name as PerseusExpressionWidgetOptions["buttonSets"][number],
|
|
148
|
+
) || isFirst;
|
|
149
|
+
return (
|
|
150
|
+
<label className="button-set-label" key={name}>
|
|
151
|
+
<input
|
|
152
|
+
type="checkbox"
|
|
153
|
+
checked={checked}
|
|
154
|
+
disabled={isFirst}
|
|
155
|
+
onChange={() => this.handleButtonSet(name)}
|
|
156
|
+
/>
|
|
157
|
+
{name}
|
|
158
|
+
</label>
|
|
159
|
+
);
|
|
160
|
+
});
|
|
184
161
|
|
|
185
162
|
const {TeX} = Dependencies.getDependencies(); // OldExpression only
|
|
186
163
|
|
|
187
|
-
buttonSetChoices.
|
|
188
|
-
|
|
189
|
-
1,
|
|
190
|
-
<label key="show-div">
|
|
164
|
+
buttonSetChoices.unshift(
|
|
165
|
+
<label className="button-set-label" key="show-div">
|
|
191
166
|
<input type="checkbox" onChange={this.handleToggleDiv} />
|
|
192
167
|
<span className="show-div-button">
|
|
193
168
|
show <TeX>\div</TeX> button
|
|
@@ -240,15 +215,6 @@ class ExpressionEditor extends React.Component<Props, State> {
|
|
|
240
215
|
{buttonSetChoices}
|
|
241
216
|
</div>
|
|
242
217
|
|
|
243
|
-
{this.state.isTex && (
|
|
244
|
-
<TexButtons
|
|
245
|
-
className="math-input-buttons"
|
|
246
|
-
sets={this.props.buttonSets}
|
|
247
|
-
convertDotToTimes={this.props.times}
|
|
248
|
-
onInsert={this.handleTexInsert}
|
|
249
|
-
/>
|
|
250
|
-
)}
|
|
251
|
-
|
|
252
218
|
<h3 className="expression-editor-h3">Answers</h3>
|
|
253
219
|
|
|
254
220
|
<p style={{margin: "4px 0"}}>
|
|
@@ -397,9 +363,7 @@ class ExpressionEditor extends React.Component<Props, State> {
|
|
|
397
363
|
|
|
398
364
|
// called when the selected buttonset changes
|
|
399
365
|
handleButtonSet: (changingName: string) => void = (changingName) => {
|
|
400
|
-
const buttonSetNames =
|
|
401
|
-
TexButtons.buttonSets,
|
|
402
|
-
) as LegacyButtonSet[];
|
|
366
|
+
const buttonSetNames = buttonSetsList;
|
|
403
367
|
|
|
404
368
|
// Filter to preserve order - using .union and .difference would always
|
|
405
369
|
// move the last added button set to the end.
|
|
@@ -10,21 +10,32 @@ import Checkbox from "../../components/checkbox";
|
|
|
10
10
|
|
|
11
11
|
const {colors, typography} = globalStyles;
|
|
12
12
|
|
|
13
|
+
export type PreferredPopoverDirection =
|
|
14
|
+
| "NONE"
|
|
15
|
+
| "UP"
|
|
16
|
+
| "DOWN"
|
|
17
|
+
| "LEFT"
|
|
18
|
+
| "RIGHT";
|
|
19
|
+
|
|
13
20
|
type Props = {
|
|
14
21
|
// Whether multiple answer choices may be selected for markers.
|
|
15
22
|
multipleAnswers: boolean;
|
|
16
23
|
// Whether to hide answer choices from user instructions.
|
|
17
24
|
hideChoicesFromInstructions: boolean;
|
|
25
|
+
|
|
26
|
+
preferredPopoverDirection: PreferredPopoverDirection;
|
|
18
27
|
// Callback for when widget options change.
|
|
19
28
|
onChange: (options: {
|
|
20
29
|
multipleAnswers?: boolean;
|
|
21
30
|
hideChoicesFromInstructions?: boolean;
|
|
31
|
+
preferredPopoverDirection?: PreferredPopoverDirection;
|
|
22
32
|
}) => void;
|
|
23
33
|
};
|
|
24
34
|
|
|
25
35
|
const Behavior = ({
|
|
26
36
|
multipleAnswers,
|
|
27
37
|
hideChoicesFromInstructions,
|
|
38
|
+
preferredPopoverDirection,
|
|
28
39
|
onChange,
|
|
29
40
|
}: Props): React.ReactElement => (
|
|
30
41
|
<div>
|
|
@@ -59,6 +70,27 @@ const Behavior = ({
|
|
|
59
70
|
Do not display answer choices in instructions
|
|
60
71
|
</span>
|
|
61
72
|
</li>
|
|
73
|
+
|
|
74
|
+
<li className={css(styles.option)}>
|
|
75
|
+
<span className={css(styles.label)}>
|
|
76
|
+
Preferred pop-over direction
|
|
77
|
+
</span>
|
|
78
|
+
<select
|
|
79
|
+
value={preferredPopoverDirection}
|
|
80
|
+
onChange={(e) => {
|
|
81
|
+
onChange({
|
|
82
|
+
preferredPopoverDirection: e.target
|
|
83
|
+
.value as PreferredPopoverDirection,
|
|
84
|
+
});
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<option value="NONE"> No Preference </option>
|
|
88
|
+
<option value="UP"> Up </option>
|
|
89
|
+
<option value="DOWN"> Down </option>
|
|
90
|
+
<option value="LEFT"> Left </option>
|
|
91
|
+
<option value="RIGHT"> Right </option>
|
|
92
|
+
</select>
|
|
93
|
+
</li>
|
|
62
94
|
</ul>
|
|
63
95
|
</div>
|
|
64
96
|
);
|
|
@@ -229,6 +229,7 @@ class LabelImageEditor extends React.Component<Props> {
|
|
|
229
229
|
<div className={css(styles.largeSpacer)} />
|
|
230
230
|
|
|
231
231
|
<Behavior
|
|
232
|
+
preferredPopoverDirection="NONE"
|
|
232
233
|
multipleAnswers={multipleAnswers}
|
|
233
234
|
hideChoicesFromInstructions={hideChoicesFromInstructions}
|
|
234
235
|
onChange={this.handleBehaviorChange}
|