@khanacademy/perseus 34.0.0 → 35.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/dist/es/index.js +641 -525
- package/dist/es/index.js.map +1 -1
- package/dist/es/strings.js +8 -2
- package/dist/es/strings.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +573 -447
- package/dist/index.js.map +1 -1
- package/dist/mixins/widget-prop-denylist.d.ts +0 -1
- package/dist/perseus-api.d.ts +4 -5
- package/dist/perseus-types.d.ts +6 -18
- package/dist/renderer.d.ts +4 -4
- package/dist/server-item-renderer.d.ts +4 -9
- package/dist/strings.d.ts +3 -0
- package/dist/strings.js +8 -2
- package/dist/strings.js.map +1 -1
- package/dist/types.d.ts +1 -3
- package/dist/validation.types.d.ts +111 -0
- package/dist/widgets/__shared__/noop-validator.d.ts +2 -2
- package/dist/widgets/categorizer/categorizer-validator.d.ts +5 -0
- package/dist/widgets/categorizer/categorizer.d.ts +6 -9
- package/dist/widgets/cs-program/cs-program.d.ts +4 -9
- package/dist/widgets/definition/definition.d.ts +6 -7
- package/dist/widgets/dropdown/dropdown-validator.d.ts +2 -2
- package/dist/widgets/dropdown/dropdown.d.ts +5 -5
- package/dist/widgets/explanation/explanation.d.ts +6 -7
- package/dist/widgets/expression/expression-validator.d.ts +2 -2
- package/dist/widgets/expression/expression.d.ts +38 -26
- package/dist/widgets/expression/expression.types.d.ts +0 -1
- package/dist/widgets/graded-group/graded-group.d.ts +2 -2
- package/dist/widgets/graded-group-set/graded-group-set.d.ts +2 -2
- package/dist/widgets/grapher/grapher.d.ts +6 -7
- package/dist/widgets/group/group.d.ts +3 -2
- package/dist/widgets/iframe/iframe.d.ts +4 -9
- package/dist/widgets/image/image.d.ts +6 -7
- package/dist/widgets/input-number/input-number-validator.d.ts +41 -0
- package/dist/widgets/input-number/input-number.d.ts +7 -10
- package/dist/widgets/interaction/interaction.d.ts +6 -6
- package/dist/widgets/interactive-graph.d.ts +9 -12
- package/dist/widgets/interactive-graphs/interactive-graph-question-builder.d.ts +15 -7
- package/dist/widgets/interactive-graphs/reducer/interactive-graph-action.d.ts +16 -1
- package/dist/widgets/interactive-graphs/stateful-mafs-graph.d.ts +5 -2
- package/dist/widgets/interactive-graphs/types.d.ts +3 -0
- package/dist/widgets/label-image/label-image-validator.d.ts +10 -0
- package/dist/widgets/label-image/label-image.d.ts +13 -10
- package/dist/widgets/label-image/types.d.ts +0 -4
- package/dist/widgets/matcher/matcher.d.ts +4 -4
- package/dist/widgets/matrix/matrix.d.ts +5 -5
- package/dist/widgets/molecule/molecule.d.ts +4 -3
- package/dist/widgets/number-line/number-line.d.ts +5 -3
- package/dist/widgets/numeric-input/numeric-input-validator.d.ts +2 -2
- package/dist/widgets/numeric-input/numeric-input.d.ts +7 -7
- package/dist/widgets/orderer/orderer.d.ts +4 -4
- package/dist/widgets/passage/passage.d.ts +6 -7
- package/dist/widgets/passage-ref/passage-ref.d.ts +6 -14
- package/dist/widgets/passage-ref-target/passage-ref-target.d.ts +4 -3
- package/dist/widgets/phet-simulation/phet-simulation.d.ts +2 -2
- package/dist/widgets/plotter/plotter.d.ts +5 -5
- package/dist/widgets/radio/radio-component.d.ts +6 -13
- package/dist/widgets/radio/radio-validator.d.ts +5 -0
- package/dist/widgets/sorter/sorter-validator.d.ts +4 -0
- package/dist/widgets/sorter/sorter.d.ts +7 -9
- package/dist/widgets/table/table.d.ts +5 -5
- package/dist/widgets/video/video.d.ts +5 -6
- package/package.json +1 -1
- package/dist/widgets/dropdown/dropdown.types.d.ts +0 -5
- package/dist/widgets/numeric-input/numeric-input.types.d.ts +0 -5
package/dist/index.js
CHANGED
|
@@ -366,7 +366,10 @@ const mockStrings = {
|
|
|
366
366
|
cos: "cos",
|
|
367
367
|
tan: "tan",
|
|
368
368
|
simulationLoadFail: "Sorry, this simulation cannot load.",
|
|
369
|
-
simulationLocaleWarning: "Sorry, this simulation isn't available in your language."
|
|
369
|
+
simulationLocaleWarning: "Sorry, this simulation isn't available in your language.",
|
|
370
|
+
addPoint: "Add Point",
|
|
371
|
+
removePoint: "Remove Point",
|
|
372
|
+
graphKeyboardPrompt: "Press Shift + Enter to interact with the graph"
|
|
370
373
|
};
|
|
371
374
|
|
|
372
375
|
/**
|
|
@@ -862,7 +865,7 @@ const denylist = [
|
|
|
862
865
|
// (technically the renderer still adds them)
|
|
863
866
|
"key", "ref",
|
|
864
867
|
// added by src/renderer.jsx
|
|
865
|
-
"containerSizeClass", "widgetId", "onChange", "problemNum", "apiOptions", "questionCompleted", "findWidgets",
|
|
868
|
+
"containerSizeClass", "widgetId", "onChange", "problemNum", "apiOptions", "widgetIsOpen", "questionCompleted", "findWidgets",
|
|
866
869
|
// added by src/editor.jsx, for widgets removing themselves
|
|
867
870
|
// this is soooo not the right place for this, but alas.
|
|
868
871
|
"onRemove",
|
|
@@ -870,17 +873,6 @@ const denylist = [
|
|
|
870
873
|
"id",
|
|
871
874
|
// Callbacks and items for interaction handling
|
|
872
875
|
"onBlur", "onFocus", "trackInteraction", "keypadElement"];
|
|
873
|
-
const removeDenylistProps = props => {
|
|
874
|
-
const newProps = {
|
|
875
|
-
...props
|
|
876
|
-
};
|
|
877
|
-
for (const prop of denylist) {
|
|
878
|
-
if (prop in newProps) {
|
|
879
|
-
delete newProps[prop];
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
return newProps;
|
|
883
|
-
};
|
|
884
876
|
|
|
885
877
|
/* eslint-disable @babel/no-invalid-this */
|
|
886
878
|
const USAGE = "Usage:\n" + " this.change({propName: 5}, callback);\n" + ' this.change("propName", 5, callback);\n' + ' this.change("propName")';
|
|
@@ -1142,8 +1134,6 @@ class StubTagEditor extends React__namespace.Component {
|
|
|
1142
1134
|
* * nothing if it is purely a bug fix.
|
|
1143
1135
|
*
|
|
1144
1136
|
* Callbacks passed to Renderer/ItemRenderer:
|
|
1145
|
-
* * onInputError:
|
|
1146
|
-
* Called when there is an error grading a widget
|
|
1147
1137
|
* * onFocusChange: (newFocusPath, oldFocusPath, keypadDOMNode)
|
|
1148
1138
|
* Called when the user focus changes. The first two parameters are `path`
|
|
1149
1139
|
* arrays uniquely identifying the respect inputs. The third parameter,
|
|
@@ -1166,7 +1156,6 @@ class StubTagEditor extends React__namespace.Component {
|
|
|
1166
1156
|
const ApiOptions = {
|
|
1167
1157
|
propTypes: PropTypes__default["default"].shape({
|
|
1168
1158
|
isArticle: PropTypes__default["default"].bool.isRequired,
|
|
1169
|
-
onInputError: PropTypes__default["default"].func.isRequired,
|
|
1170
1159
|
onFocusChange: PropTypes__default["default"].func.isRequired,
|
|
1171
1160
|
GroupMetadataEditor: PropTypes__default["default"].func.isRequired,
|
|
1172
1161
|
showAlignmentOptions: PropTypes__default["default"].bool.isRequired,
|
|
@@ -1250,7 +1239,6 @@ const ApiOptions = {
|
|
|
1250
1239
|
defaults: {
|
|
1251
1240
|
isArticle: false,
|
|
1252
1241
|
isMobile: false,
|
|
1253
|
-
onInputError: function () {},
|
|
1254
1242
|
onFocusChange: function () {},
|
|
1255
1243
|
GroupMetadataEditor: StubTagEditor,
|
|
1256
1244
|
showAlignmentOptions: false,
|
|
@@ -2262,10 +2250,7 @@ const getDecimalSeparator = locale => {
|
|
|
2262
2250
|
* show the user an error. TODO(joel) - what error?
|
|
2263
2251
|
* - Otherwise, pass through the resulting points and message.
|
|
2264
2252
|
*/
|
|
2265
|
-
function expressionValidator(userInput, rubric) {
|
|
2266
|
-
let onInputError = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {};
|
|
2267
|
-
let strings = arguments.length > 3 ? arguments[3] : undefined;
|
|
2268
|
-
let locale = arguments.length > 4 ? arguments[4] : undefined;
|
|
2253
|
+
function expressionValidator(userInput, rubric, strings, locale) {
|
|
2269
2254
|
const options = ___default["default"].clone(rubric);
|
|
2270
2255
|
___default["default"].extend(options, {
|
|
2271
2256
|
decimal_separator: getDecimalSeparator(locale)
|
|
@@ -2365,14 +2350,9 @@ function expressionValidator(userInput, rubric) {
|
|
|
2365
2350
|
};
|
|
2366
2351
|
}
|
|
2367
2352
|
if (matchingAnswerForm.considered === "ungraded") {
|
|
2368
|
-
// We matched an ungraded answer form - return "invalid", which
|
|
2369
|
-
// will let the user try again with no penalty
|
|
2370
|
-
const apiResult = onInputError(null,
|
|
2371
|
-
// Reserved for some widget identifier
|
|
2372
|
-
userInput, matchMessage);
|
|
2373
2353
|
return {
|
|
2374
2354
|
type: "invalid",
|
|
2375
|
-
message:
|
|
2355
|
+
message: matchMessage
|
|
2376
2356
|
};
|
|
2377
2357
|
}
|
|
2378
2358
|
// We matched a graded answer form, so we can now tell the user
|
|
@@ -2422,11 +2402,8 @@ class Expression extends React__namespace.Component {
|
|
|
2422
2402
|
_isMounted = false;
|
|
2423
2403
|
|
|
2424
2404
|
// TODO remove this in favor of just using expressionValidator
|
|
2425
|
-
static validate(userInput, rubric) {
|
|
2426
|
-
|
|
2427
|
-
let strings = arguments.length > 3 ? arguments[3] : undefined;
|
|
2428
|
-
let locale = arguments.length > 4 ? arguments[4] : undefined;
|
|
2429
|
-
return expressionValidator(userInput, rubric, onInputError, strings, locale);
|
|
2405
|
+
static validate(userInput, rubric, strings, locale) {
|
|
2406
|
+
return expressionValidator(userInput, rubric, strings, locale);
|
|
2430
2407
|
}
|
|
2431
2408
|
static getUserInputFromProps(props) {
|
|
2432
2409
|
return normalizeTex(props.value);
|
|
@@ -2487,14 +2464,9 @@ class Expression extends React__namespace.Component {
|
|
|
2487
2464
|
showErrorStyle: false
|
|
2488
2465
|
});
|
|
2489
2466
|
if (!this.parse(this.props.value, this.props).parsed) {
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
if (apiResult !== false) {
|
|
2494
|
-
this.setState({
|
|
2495
|
-
invalid: true
|
|
2496
|
-
});
|
|
2497
|
-
}
|
|
2467
|
+
this.setState({
|
|
2468
|
+
invalid: true
|
|
2469
|
+
});
|
|
2498
2470
|
}
|
|
2499
2471
|
}
|
|
2500
2472
|
};
|
|
@@ -2508,12 +2480,12 @@ class Expression extends React__namespace.Component {
|
|
|
2508
2480
|
});
|
|
2509
2481
|
}
|
|
2510
2482
|
};
|
|
2511
|
-
simpleValidate =
|
|
2483
|
+
simpleValidate = _ref => {
|
|
2512
2484
|
let {
|
|
2513
2485
|
scoring,
|
|
2514
2486
|
...rubric
|
|
2515
2487
|
} = _ref;
|
|
2516
|
-
const score = expressionValidator(this.getUserInput(), rubric,
|
|
2488
|
+
const score = expressionValidator(this.getUserInput(), rubric, this.context.strings, this.context.locale);
|
|
2517
2489
|
|
|
2518
2490
|
// "scoring" is a flag that indicates when we are checking answers.
|
|
2519
2491
|
// otherwise, we may just be checking validity after changes.
|
|
@@ -2528,9 +2500,9 @@ class Expression extends React__namespace.Component {
|
|
|
2528
2500
|
}
|
|
2529
2501
|
return score;
|
|
2530
2502
|
};
|
|
2531
|
-
getUserInput
|
|
2503
|
+
getUserInput() {
|
|
2532
2504
|
return Expression.getUserInputFromProps(this.props);
|
|
2533
|
-
}
|
|
2505
|
+
}
|
|
2534
2506
|
change = (() => {
|
|
2535
2507
|
var _this = this;
|
|
2536
2508
|
return function () {
|
|
@@ -14815,7 +14787,7 @@ const pluck = function (table, subKey) {
|
|
|
14815
14787
|
* });
|
|
14816
14788
|
* {a: 2, b: 3}
|
|
14817
14789
|
*/
|
|
14818
|
-
const mapObject$
|
|
14790
|
+
const mapObject$1 = function (obj, lambda) {
|
|
14819
14791
|
const result = {};
|
|
14820
14792
|
___default["default"].each(___default["default"].keys(obj), function (key) {
|
|
14821
14793
|
// @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'K'.
|
|
@@ -14841,7 +14813,7 @@ const mapObjectFromArray$1 = function (arr, lambda) {
|
|
|
14841
14813
|
};
|
|
14842
14814
|
var objective_ = {
|
|
14843
14815
|
pluck: pluck,
|
|
14844
|
-
mapObject: mapObject$
|
|
14816
|
+
mapObject: mapObject$1,
|
|
14845
14817
|
mapObjectFromArray: mapObjectFromArray$1
|
|
14846
14818
|
};
|
|
14847
14819
|
|
|
@@ -19789,7 +19761,7 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
19789
19761
|
}
|
|
19790
19762
|
|
|
19791
19763
|
const {
|
|
19792
|
-
mapObject
|
|
19764
|
+
mapObject,
|
|
19793
19765
|
mapObjectFromArray
|
|
19794
19766
|
} = objective_;
|
|
19795
19767
|
const rContainsNonWhitespace = /\S/;
|
|
@@ -19817,9 +19789,6 @@ const isIdPathPrefix = function (prefixArray, wholeArray) {
|
|
|
19817
19789
|
});
|
|
19818
19790
|
};
|
|
19819
19791
|
|
|
19820
|
-
// The return type for getUserInput. Widgets have full control of what is
|
|
19821
|
-
// returned so it's not easily typed (some widgets return a scalar (string),
|
|
19822
|
-
// some return a custom-built object
|
|
19823
19792
|
/**
|
|
19824
19793
|
* The Widget type represents the common API that the Renderer uses to interact
|
|
19825
19794
|
* with all widgets. All widgets must implement the methods in this API, unless
|
|
@@ -19829,6 +19798,7 @@ const isIdPathPrefix = function (prefixArray, wholeArray) {
|
|
|
19829
19798
|
* communicate with the individual widgets to coordinate actions such as
|
|
19830
19799
|
* scoring, state serialization/deserialization, and focus management.
|
|
19831
19800
|
*/
|
|
19801
|
+
|
|
19832
19802
|
class Renderer extends React__namespace.Component {
|
|
19833
19803
|
_currentFocus;
|
|
19834
19804
|
// @ts-expect-error - TS2564 - Property '_foundTextNodes' has no initializer and is not definitely assigned in the constructor.
|
|
@@ -20025,7 +19995,7 @@ class Renderer extends React__namespace.Component {
|
|
|
20025
19995
|
|
|
20026
19996
|
// @ts-expect-error - TS2322 - Type '(props: Props) => Partial<Record<string, CategorizerWidget | CSProgramWidget | DefinitionWidget | DropdownWidget | ... 35 more ... | VideoWidget>>' is not assignable to type '(props: Props) => { [key: string]: PerseusWidget; }'.
|
|
20027
19997
|
_getAllWidgetsInfo = props => {
|
|
20028
|
-
return mapObject
|
|
19998
|
+
return mapObject(props.widgets, (widgetInfo, widgetId) => {
|
|
20029
19999
|
if (!widgetInfo.type || !widgetInfo.alignment) {
|
|
20030
20000
|
const newValues = {};
|
|
20031
20001
|
if (!widgetInfo.type) {
|
|
@@ -20172,7 +20142,7 @@ class Renderer extends React__namespace.Component {
|
|
|
20172
20142
|
* state from that instead of the current widget props.
|
|
20173
20143
|
*/
|
|
20174
20144
|
getSerializedState = widgetProps => {
|
|
20175
|
-
return mapObject
|
|
20145
|
+
return mapObject(widgetProps || this.state.widgetProps, (props, widgetId) => {
|
|
20176
20146
|
const widget = this.getWidgetInstance(widgetId);
|
|
20177
20147
|
if (widget && widget.getSerializedState) {
|
|
20178
20148
|
return widget.getSerializedState();
|
|
@@ -20208,7 +20178,7 @@ class Renderer extends React__namespace.Component {
|
|
|
20208
20178
|
}
|
|
20209
20179
|
};
|
|
20210
20180
|
this.setState({
|
|
20211
|
-
widgetProps: mapObject
|
|
20181
|
+
widgetProps: mapObject(serializedState, (props, widgetId) => {
|
|
20212
20182
|
const widget = this.getWidgetInstance(widgetId);
|
|
20213
20183
|
if (widget && widget.restoreSerializedState) {
|
|
20214
20184
|
// Note that we probably can't call
|
|
@@ -21083,7 +21053,6 @@ class Renderer extends React__namespace.Component {
|
|
|
21083
21053
|
*/
|
|
21084
21054
|
scoreWidgets = () => {
|
|
21085
21055
|
const widgetProps = this.state.widgetInfo;
|
|
21086
|
-
const onInputError = this.getApiOptions().onInputError;
|
|
21087
21056
|
const gradedWidgetIds = ___default["default"].filter(this.widgetIds, id => {
|
|
21088
21057
|
const props = widgetProps[id];
|
|
21089
21058
|
const widgetIsGraded = props?.graded == null || props.graded;
|
|
@@ -21100,7 +21069,7 @@ class Renderer extends React__namespace.Component {
|
|
|
21100
21069
|
widgetScores[id] = widget.simpleValidate({
|
|
21101
21070
|
...props?.options,
|
|
21102
21071
|
scoring: true
|
|
21103
|
-
}
|
|
21072
|
+
});
|
|
21104
21073
|
}
|
|
21105
21074
|
});
|
|
21106
21075
|
return widgetScores;
|
|
@@ -21972,6 +21941,43 @@ const answerTypes = {
|
|
|
21972
21941
|
forms: "pi"
|
|
21973
21942
|
}
|
|
21974
21943
|
};
|
|
21944
|
+
function inputNumberValidator(state, rubric, strings) {
|
|
21945
|
+
if (rubric.answerType == null) {
|
|
21946
|
+
rubric.answerType = "number";
|
|
21947
|
+
}
|
|
21948
|
+
|
|
21949
|
+
// note(matthewc): this will get immediately parsed again by
|
|
21950
|
+
// `KhanAnswerTypes.number.convertToPredicate`, but a string is
|
|
21951
|
+
// expected here
|
|
21952
|
+
const stringValue = `${rubric.value}`;
|
|
21953
|
+
const val = KhanAnswerTypes.number.createValidatorFunctional(stringValue, {
|
|
21954
|
+
simplify: rubric.simplify,
|
|
21955
|
+
inexact: rubric.inexact || undefined,
|
|
21956
|
+
maxError: rubric.maxError,
|
|
21957
|
+
forms: answerTypes[rubric.answerType].forms
|
|
21958
|
+
}, strings);
|
|
21959
|
+
|
|
21960
|
+
// We may have received TeX; try to parse it before grading.
|
|
21961
|
+
// If `currentValue` is not TeX, this should be a no-op.
|
|
21962
|
+
const currentValue = ParseTex$1(state.currentValue);
|
|
21963
|
+
const result = val(currentValue);
|
|
21964
|
+
|
|
21965
|
+
// TODO(eater): Seems silly to translate result to this invalid/points
|
|
21966
|
+
// thing and immediately translate it back in ItemRenderer.scoreInput()
|
|
21967
|
+
if (result.empty) {
|
|
21968
|
+
return {
|
|
21969
|
+
type: "invalid",
|
|
21970
|
+
message: result.message
|
|
21971
|
+
};
|
|
21972
|
+
}
|
|
21973
|
+
return {
|
|
21974
|
+
type: "points",
|
|
21975
|
+
earned: result.correct ? 1 : 0,
|
|
21976
|
+
total: 1,
|
|
21977
|
+
message: result.message
|
|
21978
|
+
};
|
|
21979
|
+
}
|
|
21980
|
+
|
|
21975
21981
|
const formExamples$1 = {
|
|
21976
21982
|
integer: function (options, strings) {
|
|
21977
21983
|
return strings.integerExample;
|
|
@@ -22014,45 +22020,7 @@ class InputNumber extends React__namespace.Component {
|
|
|
22014
22020
|
linterContext: PerseusLinter.linterContextDefault
|
|
22015
22021
|
};
|
|
22016
22022
|
static validate(state, rubric, strings) {
|
|
22017
|
-
|
|
22018
|
-
if (rubric.answerType == null) {
|
|
22019
|
-
rubric.answerType = "number";
|
|
22020
|
-
}
|
|
22021
|
-
|
|
22022
|
-
// note(matthewc): this will get immediately parsed again by
|
|
22023
|
-
// `KhanAnswerTypes.number.convertToPredicate`, but a string is
|
|
22024
|
-
// expected here
|
|
22025
|
-
const stringValue = `${rubric.value}`;
|
|
22026
|
-
const val = KhanAnswerTypes.number.createValidatorFunctional(stringValue, {
|
|
22027
|
-
simplify: rubric.simplify,
|
|
22028
|
-
inexact: rubric.inexact || undefined,
|
|
22029
|
-
maxError: rubric.maxError,
|
|
22030
|
-
forms: answerTypes[rubric.answerType].forms
|
|
22031
|
-
}, strings);
|
|
22032
|
-
|
|
22033
|
-
// We may have received TeX; try to parse it before grading.
|
|
22034
|
-
// If `currentValue` is not TeX, this should be a no-op.
|
|
22035
|
-
const currentValue = ParseTex$1(state.currentValue);
|
|
22036
|
-
const result = val(currentValue);
|
|
22037
|
-
|
|
22038
|
-
// TODO(eater): Seems silly to translate result to this invalid/points
|
|
22039
|
-
// thing and immediately translate it back in ItemRenderer.scoreInput()
|
|
22040
|
-
if (result.empty) {
|
|
22041
|
-
// TODO(FEI-3867): remove null-check once we have APIOptionsInternal
|
|
22042
|
-
const apiResult = onInputError?.(null,
|
|
22043
|
-
// reserved for some widget identifier
|
|
22044
|
-
state.currentValue, result.message);
|
|
22045
|
-
return {
|
|
22046
|
-
type: "invalid",
|
|
22047
|
-
message: apiResult === false ? null : result.message
|
|
22048
|
-
};
|
|
22049
|
-
}
|
|
22050
|
-
return {
|
|
22051
|
-
type: "points",
|
|
22052
|
-
earned: result.correct ? 1 : 0,
|
|
22053
|
-
total: 1,
|
|
22054
|
-
message: result.message
|
|
22055
|
-
};
|
|
22023
|
+
return inputNumberValidator(state, rubric, strings);
|
|
22056
22024
|
}
|
|
22057
22025
|
static getUserInputFromProps(props) {
|
|
22058
22026
|
return {
|
|
@@ -22121,13 +22089,12 @@ class InputNumber extends React__namespace.Component {
|
|
|
22121
22089
|
currentValue: newValue
|
|
22122
22090
|
}, cb);
|
|
22123
22091
|
};
|
|
22124
|
-
getUserInput
|
|
22092
|
+
getUserInput() {
|
|
22125
22093
|
return InputNumber.getUserInputFromProps(this.props);
|
|
22126
|
-
}
|
|
22127
|
-
simpleValidate
|
|
22128
|
-
|
|
22129
|
-
|
|
22130
|
-
};
|
|
22094
|
+
}
|
|
22095
|
+
simpleValidate(rubric) {
|
|
22096
|
+
return inputNumberValidator(this.getUserInput(), rubric, this.context.strings);
|
|
22097
|
+
}
|
|
22131
22098
|
examples = () => {
|
|
22132
22099
|
const {
|
|
22133
22100
|
strings
|
|
@@ -22472,9 +22439,9 @@ class NumericInput extends React__namespace.Component {
|
|
|
22472
22439
|
const allFormsAccepted = answerFormNames.length >= Object.keys(formExamples).length;
|
|
22473
22440
|
return !noFormsAccepted && !allFormsAccepted;
|
|
22474
22441
|
};
|
|
22475
|
-
simpleValidate
|
|
22442
|
+
simpleValidate(rubric) {
|
|
22476
22443
|
return numericInputValidator(this.getUserInput(), rubric, this.context.strings);
|
|
22477
|
-
}
|
|
22444
|
+
}
|
|
22478
22445
|
focus = () => {
|
|
22479
22446
|
this.inputRef?.focus();
|
|
22480
22447
|
return true;
|
|
@@ -22501,9 +22468,9 @@ class NumericInput extends React__namespace.Component {
|
|
|
22501
22468
|
currentValue: newValue
|
|
22502
22469
|
}, cb);
|
|
22503
22470
|
};
|
|
22504
|
-
getUserInput
|
|
22471
|
+
getUserInput() {
|
|
22505
22472
|
return NumericInput.getUserInputFromProps(this.props);
|
|
22506
|
-
}
|
|
22473
|
+
}
|
|
22507
22474
|
handleChange = (newValue, cb) => {
|
|
22508
22475
|
this.props.onChange({
|
|
22509
22476
|
currentValue: newValue
|
|
@@ -22635,15 +22602,16 @@ var NumericInput$1 = {
|
|
|
22635
22602
|
* Several widgets don't have "right"/"wrong" validation logic,
|
|
22636
22603
|
* so this just says to move on past those widgets
|
|
22637
22604
|
*
|
|
22638
|
-
* TODO
|
|
22605
|
+
* TODO(LEMS-2396) widgets that use this probably shouldn't have any
|
|
22639
22606
|
* validation logic and the thing validating an exercise
|
|
22640
22607
|
* should just know to skip these
|
|
22641
22608
|
*/
|
|
22642
22609
|
function noopValidator() {
|
|
22610
|
+
let points = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
22643
22611
|
return {
|
|
22644
22612
|
type: "points",
|
|
22645
|
-
earned:
|
|
22646
|
-
total:
|
|
22613
|
+
earned: points,
|
|
22614
|
+
total: points,
|
|
22647
22615
|
message: null
|
|
22648
22616
|
};
|
|
22649
22617
|
}
|
|
@@ -22665,7 +22633,9 @@ class PassageRef extends React__namespace.Component {
|
|
|
22665
22633
|
lineRange: null,
|
|
22666
22634
|
content: null
|
|
22667
22635
|
};
|
|
22668
|
-
|
|
22636
|
+
|
|
22637
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
22638
|
+
static validate() {
|
|
22669
22639
|
return noopValidator();
|
|
22670
22640
|
}
|
|
22671
22641
|
componentDidMount() {
|
|
@@ -22687,11 +22657,10 @@ class PassageRef extends React__namespace.Component {
|
|
|
22687
22657
|
this._isMounted = false;
|
|
22688
22658
|
}
|
|
22689
22659
|
|
|
22690
|
-
// TODO(
|
|
22691
|
-
|
|
22692
|
-
|
|
22693
|
-
|
|
22694
|
-
};
|
|
22660
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
22661
|
+
getUserInput() {
|
|
22662
|
+
return null;
|
|
22663
|
+
}
|
|
22695
22664
|
change = (() => {
|
|
22696
22665
|
var _this = this;
|
|
22697
22666
|
return function () {
|
|
@@ -22727,9 +22696,11 @@ class PassageRef extends React__namespace.Component {
|
|
|
22727
22696
|
}
|
|
22728
22697
|
}
|
|
22729
22698
|
};
|
|
22730
|
-
|
|
22731
|
-
|
|
22732
|
-
|
|
22699
|
+
|
|
22700
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
22701
|
+
simpleValidate() {
|
|
22702
|
+
return noopValidator();
|
|
22703
|
+
}
|
|
22733
22704
|
render() {
|
|
22734
22705
|
const {
|
|
22735
22706
|
strings
|
|
@@ -24133,6 +24104,56 @@ const styles$t = aphrodite.StyleSheet.create({
|
|
|
24133
24104
|
}
|
|
24134
24105
|
});
|
|
24135
24106
|
|
|
24107
|
+
function radioValidator(userInput, rubric, strings) {
|
|
24108
|
+
const numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
24109
|
+
return sum + (selected ? 1 : 0);
|
|
24110
|
+
}, 0);
|
|
24111
|
+
if (numSelected === 0) {
|
|
24112
|
+
return {
|
|
24113
|
+
type: "invalid",
|
|
24114
|
+
message: null
|
|
24115
|
+
};
|
|
24116
|
+
}
|
|
24117
|
+
|
|
24118
|
+
// TODO: should numCorrect actually be on the rubric
|
|
24119
|
+
// instead of the userInput?
|
|
24120
|
+
if (userInput.numCorrect && userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
24121
|
+
return {
|
|
24122
|
+
type: "invalid",
|
|
24123
|
+
message: strings.chooseCorrectNum
|
|
24124
|
+
};
|
|
24125
|
+
// If NOTA and some other answer are checked, ...
|
|
24126
|
+
}
|
|
24127
|
+
|
|
24128
|
+
// TODO: should noneOfTheAboveSelected be replaced with a
|
|
24129
|
+
// combination of choicesSelected and noneOfTheAboveIndex?
|
|
24130
|
+
if (userInput.noneOfTheAboveSelected && numSelected > 1) {
|
|
24131
|
+
return {
|
|
24132
|
+
type: "invalid",
|
|
24133
|
+
message: strings.notNoneOfTheAbove
|
|
24134
|
+
};
|
|
24135
|
+
}
|
|
24136
|
+
const correct = userInput.choicesSelected.every((selected, i) => {
|
|
24137
|
+
let isCorrect;
|
|
24138
|
+
// TODO: should noneOfTheAboveIndex actually be on the rubric
|
|
24139
|
+
// instead of the userInput?
|
|
24140
|
+
if (userInput.noneOfTheAboveIndex === i) {
|
|
24141
|
+
isCorrect = rubric.choices.every((choice, j) => {
|
|
24142
|
+
return i === j || !choice.correct;
|
|
24143
|
+
});
|
|
24144
|
+
} else {
|
|
24145
|
+
isCorrect = !!rubric.choices[i].correct;
|
|
24146
|
+
}
|
|
24147
|
+
return isCorrect === selected;
|
|
24148
|
+
});
|
|
24149
|
+
return {
|
|
24150
|
+
type: "points",
|
|
24151
|
+
earned: correct ? 1 : 0,
|
|
24152
|
+
total: 1,
|
|
24153
|
+
message: null
|
|
24154
|
+
};
|
|
24155
|
+
}
|
|
24156
|
+
|
|
24136
24157
|
// RenderProps is the return type for radio.jsx#transform
|
|
24137
24158
|
|
|
24138
24159
|
class Radio$1 extends React__namespace.Component {
|
|
@@ -24148,46 +24169,7 @@ class Radio$1 extends React__namespace.Component {
|
|
|
24148
24169
|
showSolutions: "none"
|
|
24149
24170
|
};
|
|
24150
24171
|
static validate(userInput, rubric, strings) {
|
|
24151
|
-
|
|
24152
|
-
return sum + (selected ? 1 : 0);
|
|
24153
|
-
}, 0);
|
|
24154
|
-
if (numSelected === 0) {
|
|
24155
|
-
return {
|
|
24156
|
-
type: "invalid",
|
|
24157
|
-
message: null
|
|
24158
|
-
};
|
|
24159
|
-
}
|
|
24160
|
-
if (userInput.numCorrect && userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
24161
|
-
return {
|
|
24162
|
-
type: "invalid",
|
|
24163
|
-
message: strings.chooseCorrectNum
|
|
24164
|
-
};
|
|
24165
|
-
// If NOTA and some other answer are checked, ...
|
|
24166
|
-
}
|
|
24167
|
-
|
|
24168
|
-
if (userInput.noneOfTheAboveSelected && numSelected > 1) {
|
|
24169
|
-
return {
|
|
24170
|
-
type: "invalid",
|
|
24171
|
-
message: strings.notNoneOfTheAbove
|
|
24172
|
-
};
|
|
24173
|
-
}
|
|
24174
|
-
const correct = userInput.choicesSelected.every((selected, i) => {
|
|
24175
|
-
let isCorrect;
|
|
24176
|
-
if (userInput.noneOfTheAboveIndex === i) {
|
|
24177
|
-
isCorrect = rubric.choices.every((choice, j) => {
|
|
24178
|
-
return i === j || !choice.correct;
|
|
24179
|
-
});
|
|
24180
|
-
} else {
|
|
24181
|
-
isCorrect = !!rubric.choices[i].correct;
|
|
24182
|
-
}
|
|
24183
|
-
return isCorrect === selected;
|
|
24184
|
-
});
|
|
24185
|
-
return {
|
|
24186
|
-
type: "points",
|
|
24187
|
-
earned: correct ? 1 : 0,
|
|
24188
|
-
total: 1,
|
|
24189
|
-
message: null
|
|
24190
|
-
};
|
|
24172
|
+
return radioValidator(userInput, rubric, strings);
|
|
24191
24173
|
}
|
|
24192
24174
|
static getUserInputFromProps(props) {
|
|
24193
24175
|
// Return checked inputs in the form {choicesSelected: [bool]}. (Dear
|
|
@@ -24372,12 +24354,12 @@ class Radio$1 extends React__namespace.Component {
|
|
|
24372
24354
|
});
|
|
24373
24355
|
this.props.trackInteraction();
|
|
24374
24356
|
};
|
|
24375
|
-
getUserInput
|
|
24357
|
+
getUserInput() {
|
|
24376
24358
|
return Radio$1.getUserInputFromProps(this.props);
|
|
24377
|
-
}
|
|
24378
|
-
simpleValidate
|
|
24379
|
-
return
|
|
24380
|
-
}
|
|
24359
|
+
}
|
|
24360
|
+
simpleValidate(rubric) {
|
|
24361
|
+
return radioValidator(this.getUserInput(), rubric, this.context.strings);
|
|
24362
|
+
}
|
|
24381
24363
|
|
|
24382
24364
|
/**
|
|
24383
24365
|
* Turn on rationale display for the currently selected choices. Note that
|
|
@@ -24676,6 +24658,31 @@ var Radio = {
|
|
|
24676
24658
|
/* eslint-disable import/no-named-as-default */
|
|
24677
24659
|
var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
|
|
24678
24660
|
|
|
24661
|
+
function categorizerValidator(userInput, rubric, strings) {
|
|
24662
|
+
let completed = true;
|
|
24663
|
+
let allCorrect = true;
|
|
24664
|
+
rubric.values.forEach((value, i) => {
|
|
24665
|
+
if (userInput.values[i] == null) {
|
|
24666
|
+
completed = false;
|
|
24667
|
+
}
|
|
24668
|
+
if (userInput.values[i] !== value) {
|
|
24669
|
+
allCorrect = false;
|
|
24670
|
+
}
|
|
24671
|
+
});
|
|
24672
|
+
if (!completed) {
|
|
24673
|
+
return {
|
|
24674
|
+
type: "invalid",
|
|
24675
|
+
message: strings.invalidSelection
|
|
24676
|
+
};
|
|
24677
|
+
}
|
|
24678
|
+
return {
|
|
24679
|
+
type: "points",
|
|
24680
|
+
earned: allCorrect ? 1 : 0,
|
|
24681
|
+
total: 1,
|
|
24682
|
+
message: null
|
|
24683
|
+
};
|
|
24684
|
+
}
|
|
24685
|
+
|
|
24679
24686
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
24680
24687
|
class Categorizer extends React__namespace.Component {
|
|
24681
24688
|
static contextType = PerseusI18nContext;
|
|
@@ -24689,28 +24696,7 @@ class Categorizer extends React__namespace.Component {
|
|
|
24689
24696
|
uniqueId: ___default["default"].uniqueId("perseus_radio_")
|
|
24690
24697
|
};
|
|
24691
24698
|
static validate(userInput, rubric, strings) {
|
|
24692
|
-
|
|
24693
|
-
let allCorrect = true;
|
|
24694
|
-
rubric.values.forEach((value, i) => {
|
|
24695
|
-
if (userInput.values[i] == null) {
|
|
24696
|
-
completed = false;
|
|
24697
|
-
}
|
|
24698
|
-
if (userInput.values[i] !== value) {
|
|
24699
|
-
allCorrect = false;
|
|
24700
|
-
}
|
|
24701
|
-
});
|
|
24702
|
-
if (!completed) {
|
|
24703
|
-
return {
|
|
24704
|
-
type: "invalid",
|
|
24705
|
-
message: strings.invalidSelection
|
|
24706
|
-
};
|
|
24707
|
-
}
|
|
24708
|
-
return {
|
|
24709
|
-
type: "points",
|
|
24710
|
-
earned: allCorrect ? 1 : 0,
|
|
24711
|
-
total: 1,
|
|
24712
|
-
message: null
|
|
24713
|
-
};
|
|
24699
|
+
return categorizerValidator(userInput, rubric, strings);
|
|
24714
24700
|
}
|
|
24715
24701
|
static getUserInputFromProps(props) {
|
|
24716
24702
|
return {
|
|
@@ -24727,9 +24713,9 @@ class Categorizer extends React__namespace.Component {
|
|
|
24727
24713
|
return change.apply(_this, args);
|
|
24728
24714
|
};
|
|
24729
24715
|
})();
|
|
24730
|
-
getUserInput
|
|
24716
|
+
getUserInput() {
|
|
24731
24717
|
return Categorizer.getUserInputFromProps(this.props);
|
|
24732
|
-
}
|
|
24718
|
+
}
|
|
24733
24719
|
onChange(itemNum, catNum) {
|
|
24734
24720
|
const values = [...this.props.values];
|
|
24735
24721
|
// @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'never'.
|
|
@@ -24737,9 +24723,9 @@ class Categorizer extends React__namespace.Component {
|
|
|
24737
24723
|
this.change("values", values);
|
|
24738
24724
|
this.props.trackInteraction();
|
|
24739
24725
|
}
|
|
24740
|
-
simpleValidate
|
|
24741
|
-
return
|
|
24742
|
-
}
|
|
24726
|
+
simpleValidate(rubric) {
|
|
24727
|
+
return categorizerValidator(this.getUserInput(), rubric, this.context.strings);
|
|
24728
|
+
}
|
|
24743
24729
|
render() {
|
|
24744
24730
|
const self = this;
|
|
24745
24731
|
|
|
@@ -25108,15 +25094,19 @@ class Definition extends React__namespace.Component {
|
|
|
25108
25094
|
togglePrompt: "define me",
|
|
25109
25095
|
definition: "definition goes here"
|
|
25110
25096
|
};
|
|
25111
|
-
|
|
25097
|
+
|
|
25098
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
25099
|
+
static validate() {
|
|
25112
25100
|
return noopValidator();
|
|
25113
25101
|
}
|
|
25114
|
-
getUserInput
|
|
25102
|
+
getUserInput() {
|
|
25115
25103
|
return {};
|
|
25116
|
-
}
|
|
25117
|
-
|
|
25118
|
-
|
|
25119
|
-
|
|
25104
|
+
}
|
|
25105
|
+
|
|
25106
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
25107
|
+
simpleValidate() {
|
|
25108
|
+
return noopValidator();
|
|
25109
|
+
}
|
|
25120
25110
|
render() {
|
|
25121
25111
|
return /*#__PURE__*/React__namespace.createElement(DefinitionConsumer, null, _ref => {
|
|
25122
25112
|
let {
|
|
@@ -25182,18 +25172,13 @@ class DeprecatedStandin extends React__default["default"].Component {
|
|
|
25182
25172
|
static validate(userInput, rubric) {
|
|
25183
25173
|
// Since this mean to replace an existing widget the learner
|
|
25184
25174
|
// WILL earn points for this widget
|
|
25185
|
-
return
|
|
25186
|
-
type: "points",
|
|
25187
|
-
earned: 1,
|
|
25188
|
-
total: 1,
|
|
25189
|
-
message: null
|
|
25190
|
-
};
|
|
25175
|
+
return noopValidator(1);
|
|
25191
25176
|
}
|
|
25192
25177
|
getUserInput = () => {
|
|
25193
25178
|
return {};
|
|
25194
25179
|
};
|
|
25195
25180
|
simpleValidate = rubric => {
|
|
25196
|
-
return
|
|
25181
|
+
return noopValidator(1);
|
|
25197
25182
|
};
|
|
25198
25183
|
render() {
|
|
25199
25184
|
return /*#__PURE__*/React__default["default"].createElement("div", {
|
|
@@ -25259,14 +25244,14 @@ class Dropdown extends React__namespace.Component {
|
|
|
25259
25244
|
selected: selected
|
|
25260
25245
|
});
|
|
25261
25246
|
};
|
|
25262
|
-
getUserInput
|
|
25247
|
+
getUserInput() {
|
|
25263
25248
|
return {
|
|
25264
25249
|
value: this.props.selected
|
|
25265
25250
|
};
|
|
25266
|
-
}
|
|
25267
|
-
simpleValidate
|
|
25251
|
+
}
|
|
25252
|
+
simpleValidate(rubric) {
|
|
25268
25253
|
return dropdownValidator(this.getUserInput(), rubric);
|
|
25269
|
-
}
|
|
25254
|
+
}
|
|
25270
25255
|
render() {
|
|
25271
25256
|
const children = [/*#__PURE__*/React__namespace.createElement(wonderBlocksDropdown.OptionItem, {
|
|
25272
25257
|
key: "placeholder",
|
|
@@ -25331,7 +25316,9 @@ class Explanation extends React__namespace.Component {
|
|
|
25331
25316
|
widgets: {},
|
|
25332
25317
|
linterContext: PerseusLinter.linterContextDefault
|
|
25333
25318
|
};
|
|
25334
|
-
|
|
25319
|
+
|
|
25320
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
25321
|
+
static validate() {
|
|
25335
25322
|
return noopValidator();
|
|
25336
25323
|
}
|
|
25337
25324
|
componentDidMount() {
|
|
@@ -25355,12 +25342,14 @@ class Explanation extends React__namespace.Component {
|
|
|
25355
25342
|
});
|
|
25356
25343
|
this.props.trackInteraction();
|
|
25357
25344
|
};
|
|
25358
|
-
getUserInput
|
|
25345
|
+
getUserInput() {
|
|
25359
25346
|
return {};
|
|
25360
|
-
}
|
|
25361
|
-
|
|
25362
|
-
|
|
25363
|
-
|
|
25347
|
+
}
|
|
25348
|
+
|
|
25349
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
25350
|
+
simpleValidate() {
|
|
25351
|
+
return noopValidator();
|
|
25352
|
+
}
|
|
25364
25353
|
render() {
|
|
25365
25354
|
const promptText = this.state.expanded ? this.props.hidePrompt : this.props.showPrompt;
|
|
25366
25355
|
const caretIcon = this.state.expanded ? caretUp__default["default"] : caretDown__default["default"];
|
|
@@ -26991,9 +26980,6 @@ class FunctionGrapher extends React__namespace.Component {
|
|
|
26991
26980
|
}), this.props.model && this.renderPlot(), this.props.model && this.renderAsymptote(), this.props.model && points)));
|
|
26992
26981
|
}
|
|
26993
26982
|
}
|
|
26994
|
-
|
|
26995
|
-
// Really this is props.plot, I think
|
|
26996
|
-
|
|
26997
26983
|
/* Widget and editor. */
|
|
26998
26984
|
class Grapher extends React__namespace.Component {
|
|
26999
26985
|
horizHairline;
|
|
@@ -27228,9 +27214,9 @@ class Group extends React__namespace.Component {
|
|
|
27228
27214
|
return change.apply(_this, args);
|
|
27229
27215
|
};
|
|
27230
27216
|
})();
|
|
27231
|
-
getUserInput
|
|
27217
|
+
getUserInput() {
|
|
27232
27218
|
return this.rendererRef?.getUserInput();
|
|
27233
|
-
}
|
|
27219
|
+
}
|
|
27234
27220
|
getSerializedState = () => {
|
|
27235
27221
|
return this.rendererRef?.getSerializedState();
|
|
27236
27222
|
};
|
|
@@ -27240,9 +27226,9 @@ class Group extends React__namespace.Component {
|
|
|
27240
27226
|
// (all our changes were in state):
|
|
27241
27227
|
return null;
|
|
27242
27228
|
};
|
|
27243
|
-
simpleValidate
|
|
27229
|
+
simpleValidate() {
|
|
27244
27230
|
return this.rendererRef?.score();
|
|
27245
|
-
}
|
|
27231
|
+
}
|
|
27246
27232
|
|
|
27247
27233
|
// Mobile API:
|
|
27248
27234
|
getInputPaths = () => {
|
|
@@ -27538,7 +27524,9 @@ class ImageWidget extends React__namespace.Component {
|
|
|
27538
27524
|
caption: "",
|
|
27539
27525
|
linterContext: PerseusLinter.linterContextDefault
|
|
27540
27526
|
};
|
|
27541
|
-
|
|
27527
|
+
|
|
27528
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
27529
|
+
static validate() {
|
|
27542
27530
|
return noopValidator();
|
|
27543
27531
|
}
|
|
27544
27532
|
change = (() => {
|
|
@@ -27550,12 +27538,14 @@ class ImageWidget extends React__namespace.Component {
|
|
|
27550
27538
|
return change.apply(_this, args);
|
|
27551
27539
|
};
|
|
27552
27540
|
})();
|
|
27553
|
-
getUserInput
|
|
27541
|
+
getUserInput() {
|
|
27554
27542
|
return null;
|
|
27555
|
-
}
|
|
27556
|
-
|
|
27557
|
-
|
|
27558
|
-
|
|
27543
|
+
}
|
|
27544
|
+
|
|
27545
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
27546
|
+
simpleValidate() {
|
|
27547
|
+
return noopValidator();
|
|
27548
|
+
}
|
|
27559
27549
|
focus = () => {}; // no-op
|
|
27560
27550
|
|
|
27561
27551
|
render() {
|
|
@@ -27781,7 +27771,9 @@ class Interaction extends React__namespace.Component {
|
|
|
27781
27771
|
},
|
|
27782
27772
|
elements: []
|
|
27783
27773
|
};
|
|
27784
|
-
|
|
27774
|
+
|
|
27775
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
27776
|
+
static validate() {
|
|
27785
27777
|
return noopValidator();
|
|
27786
27778
|
}
|
|
27787
27779
|
state = {
|
|
@@ -27891,16 +27883,18 @@ class Interaction extends React__namespace.Component {
|
|
|
27891
27883
|
return change.apply(_this, args);
|
|
27892
27884
|
};
|
|
27893
27885
|
})();
|
|
27894
|
-
getUserInput
|
|
27886
|
+
getUserInput() {
|
|
27895
27887
|
// TODO(eater): Perhaps we want to be able to record the state of the
|
|
27896
27888
|
// user's interaction. Unfortunately sending all the props will
|
|
27897
27889
|
// probably make the attempt payload too large. So for now, don't send
|
|
27898
27890
|
// anything.
|
|
27899
27891
|
return {};
|
|
27900
|
-
}
|
|
27901
|
-
|
|
27902
|
-
|
|
27903
|
-
|
|
27892
|
+
}
|
|
27893
|
+
|
|
27894
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
27895
|
+
simpleValidate() {
|
|
27896
|
+
return noopValidator();
|
|
27897
|
+
}
|
|
27904
27898
|
render() {
|
|
27905
27899
|
const range = this.props.graph.range;
|
|
27906
27900
|
let labels = this.props.graph.labels;
|
|
@@ -29963,12 +29957,26 @@ function GraphLockedLabelsLayer(props) {
|
|
|
29963
29957
|
key: `label-${i}`
|
|
29964
29958
|
}, figure));
|
|
29965
29959
|
}
|
|
29966
|
-
if (
|
|
29960
|
+
if (
|
|
29961
|
+
// Point flag + point type
|
|
29962
|
+
flags?.["mafs"]?.["locked-point-labels"] && figure.type === "point" ||
|
|
29963
|
+
// Line flag + line type
|
|
29964
|
+
flags?.["mafs"]?.["locked-line-labels"] && figure.type === "line" ||
|
|
29965
|
+
// Vector flag + vector type
|
|
29966
|
+
flags?.["mafs"]?.["locked-vector-labels"] && figure.type === "vector" ||
|
|
29967
|
+
// Ellipse flag + ellipse type
|
|
29968
|
+
flags?.["mafs"]?.["locked-ellipse-labels"] && figure.type === "ellipse" ||
|
|
29969
|
+
// Function flag + function type
|
|
29970
|
+
flags?.["mafs"]?.["locked-function-labels"] && figure.type === "function") {
|
|
29967
29971
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, {
|
|
29968
|
-
key:
|
|
29969
|
-
}, figure.labels
|
|
29970
|
-
key:
|
|
29971
|
-
}, label))))
|
|
29972
|
+
key: i
|
|
29973
|
+
}, figure.labels?.map((label, j) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
29974
|
+
key: `${i}-label-${j}`
|
|
29975
|
+
}, label))), figure.type === "line" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, figure.points[0].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
29976
|
+
key: `locked-figure-${i}-point-1-label-${k}`
|
|
29977
|
+
}, label))), figure.points[1].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
|
|
29978
|
+
key: `locked-figure-${i}-point-2-label-${k}`
|
|
29979
|
+
}, label)))));
|
|
29972
29980
|
}
|
|
29973
29981
|
return null;
|
|
29974
29982
|
});
|
|
@@ -30308,7 +30316,9 @@ const GraphLockedLayer = props => {
|
|
|
30308
30316
|
|
|
30309
30317
|
const actions = {
|
|
30310
30318
|
global: {
|
|
30311
|
-
deleteIntent
|
|
30319
|
+
deleteIntent,
|
|
30320
|
+
changeInteractionMode,
|
|
30321
|
+
changeKeyboardInvitationVisibility
|
|
30312
30322
|
},
|
|
30313
30323
|
angle: {
|
|
30314
30324
|
movePoint
|
|
@@ -30400,6 +30410,20 @@ function clickPoint(index) {
|
|
|
30400
30410
|
index
|
|
30401
30411
|
};
|
|
30402
30412
|
}
|
|
30413
|
+
const CHANGE_INTERACTION_MODE = "point-change-interaction-mode";
|
|
30414
|
+
function changeInteractionMode(mode) {
|
|
30415
|
+
return {
|
|
30416
|
+
type: CHANGE_INTERACTION_MODE,
|
|
30417
|
+
mode
|
|
30418
|
+
};
|
|
30419
|
+
}
|
|
30420
|
+
const CHANGE_KEYBOARD_INVITATION_VISIBILITY = "change-keyboard-interaction-invitation-visibility";
|
|
30421
|
+
function changeKeyboardInvitationVisibility(shouldShow) {
|
|
30422
|
+
return {
|
|
30423
|
+
type: CHANGE_KEYBOARD_INVITATION_VISIBILITY,
|
|
30424
|
+
shouldShow
|
|
30425
|
+
};
|
|
30426
|
+
}
|
|
30403
30427
|
const MOVE_ALL = "move-all";
|
|
30404
30428
|
function moveAll(delta) {
|
|
30405
30429
|
return {
|
|
@@ -32068,7 +32092,7 @@ function Protractor() {
|
|
|
32068
32092
|
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
32069
32093
|
// Position the protractor in the center of the graph (horizontally), and
|
|
32070
32094
|
// 95% of the way to the bottom of the graph (vertically).
|
|
32071
|
-
const initialCenter = [lerp(xMin, xMax, 0.5), lerp(yMin, yMax, 0.
|
|
32095
|
+
const initialCenter = [lerp(xMin, xMax, 0.5), lerp(yMin, yMax, 0.25)];
|
|
32072
32096
|
const [center, setCenter] = React.useState(initialCenter);
|
|
32073
32097
|
const [rotationHandleOffset, setRotationHandleOffset] = React.useState(centerToRotationHandle);
|
|
32074
32098
|
const draggableRef = React.useRef(null);
|
|
@@ -32229,6 +32253,9 @@ const MafsGraph = props => {
|
|
|
32229
32253
|
x: viewboxX,
|
|
32230
32254
|
y: viewboxY
|
|
32231
32255
|
};
|
|
32256
|
+
const {
|
|
32257
|
+
strings
|
|
32258
|
+
} = usePerseusI18n();
|
|
32232
32259
|
return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
|
|
32233
32260
|
value: {
|
|
32234
32261
|
range: state.range,
|
|
@@ -32257,15 +32284,18 @@ const MafsGraph = props => {
|
|
|
32257
32284
|
height
|
|
32258
32285
|
},
|
|
32259
32286
|
onKeyUp: event => {
|
|
32260
|
-
|
|
32261
|
-
dispatch(actions.global.deleteIntent());
|
|
32262
|
-
graphRef.current?.focus();
|
|
32263
|
-
}
|
|
32287
|
+
handleKeyboardEvent(event, state, dispatch);
|
|
32264
32288
|
},
|
|
32265
32289
|
"aria-label": fullGraphAriaLabel,
|
|
32266
32290
|
"aria-describedby": fullGraphAriaDescription ? descriptionId : undefined,
|
|
32267
32291
|
ref: graphRef,
|
|
32268
|
-
tabIndex: 0
|
|
32292
|
+
tabIndex: 0,
|
|
32293
|
+
onFocus: event => {
|
|
32294
|
+
handleFocusEvent(event, state, dispatch);
|
|
32295
|
+
},
|
|
32296
|
+
onBlur: event => {
|
|
32297
|
+
handleBlurEvent(event, state, dispatch);
|
|
32298
|
+
}
|
|
32269
32299
|
}, fullGraphAriaDescription && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
32270
32300
|
id: descriptionId,
|
|
32271
32301
|
tabIndex: -1,
|
|
@@ -32327,35 +32357,75 @@ const MafsGraph = props => {
|
|
|
32327
32357
|
}, /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React__namespace.createElement(Protractor, null), renderGraph({
|
|
32328
32358
|
state,
|
|
32329
32359
|
dispatch
|
|
32330
|
-
})))))
|
|
32360
|
+
}))))), state.type === "point" && state.showKeyboardInteractionInvitation && /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
32361
|
+
style: {
|
|
32362
|
+
textAlign: "center",
|
|
32363
|
+
backgroundColor: "white",
|
|
32364
|
+
border: "1px solid #21242C52",
|
|
32365
|
+
padding: "16px 0",
|
|
32366
|
+
boxShadow: "0px 8px 8px 0px #21242C14",
|
|
32367
|
+
// This translates the box to the center of the
|
|
32368
|
+
// graph Then backs it off by half of its
|
|
32369
|
+
// overall height so it's perfectly centered
|
|
32370
|
+
top: "50%",
|
|
32371
|
+
transform: "translateY(-50%)"
|
|
32372
|
+
}
|
|
32373
|
+
}, /*#__PURE__*/React__namespace.createElement(wonderBlocksTypography.LabelMedium, null, strings.graphKeyboardPrompt))), renderGraphControls({
|
|
32331
32374
|
state,
|
|
32332
32375
|
dispatch,
|
|
32333
|
-
width
|
|
32376
|
+
width,
|
|
32377
|
+
strings
|
|
32334
32378
|
})));
|
|
32335
32379
|
};
|
|
32336
|
-
const renderPointGraphControls = props =>
|
|
32337
|
-
|
|
32338
|
-
|
|
32339
|
-
|
|
32340
|
-
|
|
32341
|
-
}
|
|
32342
|
-
|
|
32343
|
-
|
|
32344
|
-
|
|
32345
|
-
|
|
32346
|
-
|
|
32347
|
-
|
|
32348
|
-
|
|
32349
|
-
|
|
32350
|
-
|
|
32351
|
-
|
|
32352
|
-
|
|
32353
|
-
|
|
32380
|
+
const renderPointGraphControls = props => {
|
|
32381
|
+
const {
|
|
32382
|
+
interactionMode,
|
|
32383
|
+
showRemovePointButton,
|
|
32384
|
+
focusedPointIndex
|
|
32385
|
+
} = props.state;
|
|
32386
|
+
const {
|
|
32387
|
+
strings
|
|
32388
|
+
} = props;
|
|
32389
|
+
const shouldShowRemoveButton = showRemovePointButton && focusedPointIndex !== null;
|
|
32390
|
+
return /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
|
|
32391
|
+
style: {
|
|
32392
|
+
flexDirection: "row",
|
|
32393
|
+
width: props.width
|
|
32394
|
+
}
|
|
32395
|
+
}, interactionMode === "keyboard" && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
|
|
32396
|
+
kind: "secondary",
|
|
32397
|
+
style: {
|
|
32398
|
+
width: "100%",
|
|
32399
|
+
marginLeft: "20px"
|
|
32400
|
+
},
|
|
32401
|
+
tabIndex: 0,
|
|
32402
|
+
onClick: () => {
|
|
32403
|
+
props.dispatch(actions.pointGraph.addPoint([0, 0]));
|
|
32404
|
+
}
|
|
32405
|
+
}, strings.addPoint), interactionMode === "mouse" && /*#__PURE__*/React__namespace.createElement(Button__default["default"], {
|
|
32406
|
+
id: REMOVE_BUTTON_ID,
|
|
32407
|
+
kind: "secondary",
|
|
32408
|
+
color: "destructive"
|
|
32409
|
+
// This button is meant to be interacted with by the mouse only
|
|
32410
|
+
// Never allow learners to tab to this button
|
|
32411
|
+
,
|
|
32412
|
+
tabIndex: -1,
|
|
32413
|
+
style: {
|
|
32414
|
+
width: "100%",
|
|
32415
|
+
marginLeft: "20px",
|
|
32416
|
+
visibility: shouldShowRemoveButton ? "visible" : "hidden"
|
|
32417
|
+
},
|
|
32418
|
+
onClick: event => {
|
|
32419
|
+
props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));
|
|
32420
|
+
}
|
|
32421
|
+
}, strings.removePoint));
|
|
32422
|
+
};
|
|
32354
32423
|
const renderGraphControls = props => {
|
|
32355
32424
|
const {
|
|
32356
32425
|
state,
|
|
32357
32426
|
dispatch,
|
|
32358
|
-
width
|
|
32427
|
+
width,
|
|
32428
|
+
strings
|
|
32359
32429
|
} = props;
|
|
32360
32430
|
const {
|
|
32361
32431
|
type
|
|
@@ -32366,7 +32436,8 @@ const renderGraphControls = props => {
|
|
|
32366
32436
|
return renderPointGraphControls({
|
|
32367
32437
|
state,
|
|
32368
32438
|
dispatch,
|
|
32369
|
-
width
|
|
32439
|
+
width,
|
|
32440
|
+
strings
|
|
32370
32441
|
});
|
|
32371
32442
|
}
|
|
32372
32443
|
return null;
|
|
@@ -32374,6 +32445,34 @@ const renderGraphControls = props => {
|
|
|
32374
32445
|
return null;
|
|
32375
32446
|
}
|
|
32376
32447
|
};
|
|
32448
|
+
function handleFocusEvent(event, state, dispatch) {
|
|
32449
|
+
if (state.type === "point" && state.numPoints === "unlimited") {
|
|
32450
|
+
if (event.target.classList.contains("mafs-graph") && state.interactionMode === "mouse") {
|
|
32451
|
+
dispatch(actions.global.changeKeyboardInvitationVisibility(true));
|
|
32452
|
+
}
|
|
32453
|
+
}
|
|
32454
|
+
}
|
|
32455
|
+
function handleBlurEvent(event, state, dispatch) {
|
|
32456
|
+
if (state.type === "point" && state.numPoints === "unlimited") {
|
|
32457
|
+
dispatch(actions.global.changeKeyboardInvitationVisibility(false));
|
|
32458
|
+
}
|
|
32459
|
+
}
|
|
32460
|
+
function handleKeyboardEvent(event, state, dispatch) {
|
|
32461
|
+
if (state.type === "point" && state.numPoints === "unlimited") {
|
|
32462
|
+
if (event.key === "Backspace") {
|
|
32463
|
+
dispatch(actions.global.deleteIntent());
|
|
32464
|
+
|
|
32465
|
+
// After removing a point blur
|
|
32466
|
+
// It would be nice if this could focus on the graph but doing so
|
|
32467
|
+
// would trigger the message to prompt a learner to enter keyboard mode
|
|
32468
|
+
document.activeElement.blur();
|
|
32469
|
+
} else if (event.shiftKey && event.key === "Enter") {
|
|
32470
|
+
dispatch(actions.global.changeInteractionMode("keyboard"));
|
|
32471
|
+
} else if (state.interactionMode === "keyboard" && event.key === "a") {
|
|
32472
|
+
dispatch(actions.pointGraph.addPoint([0, 0]));
|
|
32473
|
+
}
|
|
32474
|
+
}
|
|
32475
|
+
}
|
|
32377
32476
|
|
|
32378
32477
|
// Calculate the difference between the min and max values of a range
|
|
32379
32478
|
const getRangeDiff = range => {
|
|
@@ -32543,7 +32642,9 @@ function initializeGraphState(params) {
|
|
|
32543
32642
|
coords: getPointCoords(graph, range, step),
|
|
32544
32643
|
numPoints: graph.numPoints || 0,
|
|
32545
32644
|
focusedPointIndex: null,
|
|
32546
|
-
showRemovePointButton: false
|
|
32645
|
+
showRemovePointButton: false,
|
|
32646
|
+
interactionMode: "mouse",
|
|
32647
|
+
showKeyboardInteractionInvitation: false
|
|
32547
32648
|
};
|
|
32548
32649
|
case "circle":
|
|
32549
32650
|
return {
|
|
@@ -32816,6 +32917,10 @@ function interactiveGraphReducer(state, action) {
|
|
|
32816
32917
|
return doDeleteIntent(state);
|
|
32817
32918
|
case CLICK_POINT:
|
|
32818
32919
|
return doClickPoint(state, action);
|
|
32920
|
+
case CHANGE_INTERACTION_MODE:
|
|
32921
|
+
return doChangeInteractionMode(state, action);
|
|
32922
|
+
case CHANGE_KEYBOARD_INVITATION_VISIBILITY:
|
|
32923
|
+
return doChangeKeyboardInvitationVisibility(state, action);
|
|
32819
32924
|
default:
|
|
32820
32925
|
throw new wonderStuffCore.UnreachableCaseError(action);
|
|
32821
32926
|
}
|
|
@@ -32867,6 +32972,33 @@ function doClickPoint(state, action) {
|
|
|
32867
32972
|
}
|
|
32868
32973
|
return state;
|
|
32869
32974
|
}
|
|
32975
|
+
function doChangeInteractionMode(state, action) {
|
|
32976
|
+
if (state.type !== "point") {
|
|
32977
|
+
return state;
|
|
32978
|
+
}
|
|
32979
|
+
if (state.numPoints === "unlimited") {
|
|
32980
|
+
const nextKeyboardInvitation = action.mode === "keyboard" ? false : state.showKeyboardInteractionInvitation;
|
|
32981
|
+
return {
|
|
32982
|
+
...state,
|
|
32983
|
+
interactionMode: action.mode,
|
|
32984
|
+
showKeyboardInteractionInvitation: nextKeyboardInvitation
|
|
32985
|
+
};
|
|
32986
|
+
}
|
|
32987
|
+
return state;
|
|
32988
|
+
}
|
|
32989
|
+
function doChangeKeyboardInvitationVisibility(state, action) {
|
|
32990
|
+
if (state.type !== "point") {
|
|
32991
|
+
return state;
|
|
32992
|
+
}
|
|
32993
|
+
if (state.numPoints === "unlimited") {
|
|
32994
|
+
return {
|
|
32995
|
+
...state,
|
|
32996
|
+
showKeyboardInteractionInvitation: action.shouldShow,
|
|
32997
|
+
hasBeenInteractedWith: true
|
|
32998
|
+
};
|
|
32999
|
+
}
|
|
33000
|
+
return state;
|
|
33001
|
+
}
|
|
32870
33002
|
function doMovePointInFigure(state, action) {
|
|
32871
33003
|
switch (state.type) {
|
|
32872
33004
|
case "segment":
|
|
@@ -33635,7 +33767,7 @@ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref)
|
|
|
33635
33767
|
const snapTo = graph.type === "polygon" ? graph.snapTo : null;
|
|
33636
33768
|
const showAngles = graph.type === "polygon" ? graph.showAngles : null;
|
|
33637
33769
|
const showSides = graph.type === "polygon" ? graph.showSides : null;
|
|
33638
|
-
const startCoords = "startCoords" in graph ? graph.startCoords :
|
|
33770
|
+
const startCoords = "startCoords" in graph ? graph.startCoords : undefined;
|
|
33639
33771
|
const originalPropsRef = React.useRef(props);
|
|
33640
33772
|
const latestPropsRef = wonderBlocksCore.useLatestRef(props);
|
|
33641
33773
|
React.useEffect(() => {
|
|
@@ -34923,17 +35055,17 @@ class InteractiveGraph extends React__namespace.Component {
|
|
|
34923
35055
|
type: "linear"
|
|
34924
35056
|
}
|
|
34925
35057
|
};
|
|
34926
|
-
getUserInput
|
|
34927
|
-
if (this.mafsRef.current) {
|
|
34928
|
-
return this.mafsRef.current.getUserInput
|
|
35058
|
+
getUserInput() {
|
|
35059
|
+
if (this.mafsRef.current?.getUserInput) {
|
|
35060
|
+
return this.mafsRef.current.getUserInput();
|
|
34929
35061
|
}
|
|
34930
|
-
if (this.legacyGraphRef.current) {
|
|
35062
|
+
if (this.legacyGraphRef.current?.getUserInput) {
|
|
34931
35063
|
return this.legacyGraphRef.current.getUserInput();
|
|
34932
35064
|
}
|
|
34933
35065
|
throw new perseusCore.PerseusError("Cannot getUserInput from a graph that has never rendered", perseusCore.Errors.NotAllowed);
|
|
34934
|
-
}
|
|
35066
|
+
}
|
|
34935
35067
|
simpleValidate(rubric) {
|
|
34936
|
-
return InteractiveGraph.validate(this.getUserInput
|
|
35068
|
+
return InteractiveGraph.validate(this.getUserInput(), rubric, this);
|
|
34937
35069
|
}
|
|
34938
35070
|
render() {
|
|
34939
35071
|
// Mafs shim
|
|
@@ -36600,6 +36732,57 @@ const styles$h = aphrodite.StyleSheet.create({
|
|
|
36600
36732
|
}
|
|
36601
36733
|
});
|
|
36602
36734
|
|
|
36735
|
+
// Question state for marker as result of user selected answers.
|
|
36736
|
+
|
|
36737
|
+
function scoreMarker(marker) {
|
|
36738
|
+
const score = {
|
|
36739
|
+
hasAnswers: false,
|
|
36740
|
+
isCorrect: false
|
|
36741
|
+
};
|
|
36742
|
+
if (marker.selected && marker.selected.length > 0) {
|
|
36743
|
+
score.hasAnswers = true;
|
|
36744
|
+
}
|
|
36745
|
+
if (marker.answers.length > 0) {
|
|
36746
|
+
if (marker.selected && marker.selected.length === marker.answers.length) {
|
|
36747
|
+
// All correct answers are selected by the user.
|
|
36748
|
+
score.isCorrect = marker.selected.every(choice => marker.answers.includes(choice));
|
|
36749
|
+
}
|
|
36750
|
+
} else if (!marker.selected || marker.selected.length === 0) {
|
|
36751
|
+
// Correct as no answers should be selected by the user.
|
|
36752
|
+
score.isCorrect = true;
|
|
36753
|
+
}
|
|
36754
|
+
return score;
|
|
36755
|
+
}
|
|
36756
|
+
function labelImageValidator(state, rubric) {
|
|
36757
|
+
let numAnswered = 0;
|
|
36758
|
+
let numCorrect = 0;
|
|
36759
|
+
for (const marker of state.markers) {
|
|
36760
|
+
const score = scoreMarker(marker);
|
|
36761
|
+
if (score.hasAnswers) {
|
|
36762
|
+
numAnswered++;
|
|
36763
|
+
}
|
|
36764
|
+
if (score.isCorrect) {
|
|
36765
|
+
numCorrect++;
|
|
36766
|
+
}
|
|
36767
|
+
}
|
|
36768
|
+
|
|
36769
|
+
// We expect all question markers to be answered before grading.
|
|
36770
|
+
if (numAnswered !== state.markers.length) {
|
|
36771
|
+
return {
|
|
36772
|
+
type: "invalid",
|
|
36773
|
+
message: null
|
|
36774
|
+
};
|
|
36775
|
+
}
|
|
36776
|
+
return {
|
|
36777
|
+
type: "points",
|
|
36778
|
+
// Markers with no expected answers are graded as correct if user
|
|
36779
|
+
// makes no answer selection.
|
|
36780
|
+
earned: numCorrect === state.markers.length ? 1 : 0,
|
|
36781
|
+
total: 1,
|
|
36782
|
+
message: null
|
|
36783
|
+
};
|
|
36784
|
+
}
|
|
36785
|
+
|
|
36603
36786
|
const BringToFront = {
|
|
36604
36787
|
boxShadow: `0 8px 8px ${wonderBlocksTokens.color.offBlack64}`,
|
|
36605
36788
|
zIndex: 1000
|
|
@@ -36860,61 +37043,15 @@ const styles$f = aphrodite.StyleSheet.create({
|
|
|
36860
37043
|
* No diagonals allowed.
|
|
36861
37044
|
*/
|
|
36862
37045
|
|
|
37046
|
+
// TODO: should this be using WidgetProps / PerseusLabelImageWidgetOptions?
|
|
37047
|
+
|
|
36863
37048
|
class LabelImage extends React__namespace.Component {
|
|
36864
37049
|
static contextType = PerseusI18nContext;
|
|
36865
37050
|
// The rendered markers on the question image for labeling.
|
|
36866
37051
|
_markers;
|
|
36867
37052
|
_mounted = false;
|
|
36868
|
-
static gradeMarker(marker) {
|
|
36869
|
-
const score = {
|
|
36870
|
-
hasAnswers: false,
|
|
36871
|
-
isCorrect: false
|
|
36872
|
-
};
|
|
36873
|
-
if (marker.selected && marker.selected.length > 0) {
|
|
36874
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'hasAnswers' because it is a read-only property.
|
|
36875
|
-
score.hasAnswers = true;
|
|
36876
|
-
}
|
|
36877
|
-
if (marker.answers.length > 0) {
|
|
36878
|
-
if (marker.selected && marker.selected.length === marker.answers.length) {
|
|
36879
|
-
// All correct answers are selected by the user.
|
|
36880
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'isCorrect' because it is a read-only property.
|
|
36881
|
-
score.isCorrect = marker.selected.every(choice => marker.answers.includes(choice));
|
|
36882
|
-
}
|
|
36883
|
-
} else if (!marker.selected || marker.selected.length === 0) {
|
|
36884
|
-
// Correct as no answers should be selected by the user.
|
|
36885
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'isCorrect' because it is a read-only property.
|
|
36886
|
-
score.isCorrect = true;
|
|
36887
|
-
}
|
|
36888
|
-
return score;
|
|
36889
|
-
}
|
|
36890
37053
|
static validate(state, rubric) {
|
|
36891
|
-
|
|
36892
|
-
let numCorrect = 0;
|
|
36893
|
-
for (const marker of state.markers) {
|
|
36894
|
-
const score = LabelImage.gradeMarker(marker);
|
|
36895
|
-
if (score.hasAnswers) {
|
|
36896
|
-
numAnswered++;
|
|
36897
|
-
}
|
|
36898
|
-
if (score.isCorrect) {
|
|
36899
|
-
numCorrect++;
|
|
36900
|
-
}
|
|
36901
|
-
}
|
|
36902
|
-
|
|
36903
|
-
// We expect all question markers to be answered before grading.
|
|
36904
|
-
if (numAnswered !== state.markers.length) {
|
|
36905
|
-
return {
|
|
36906
|
-
type: "invalid",
|
|
36907
|
-
message: null
|
|
36908
|
-
};
|
|
36909
|
-
}
|
|
36910
|
-
return {
|
|
36911
|
-
type: "points",
|
|
36912
|
-
// Markers with no expected answers are graded as correct if user
|
|
36913
|
-
// makes no answer selection.
|
|
36914
|
-
earned: numCorrect === state.markers.length ? 1 : 0,
|
|
36915
|
-
total: 1,
|
|
36916
|
-
message: null
|
|
36917
|
-
};
|
|
37054
|
+
return labelImageValidator(state);
|
|
36918
37055
|
}
|
|
36919
37056
|
|
|
36920
37057
|
/**
|
|
@@ -37109,7 +37246,7 @@ class LabelImage extends React__namespace.Component {
|
|
|
37109
37246
|
this._mounted = false;
|
|
37110
37247
|
}
|
|
37111
37248
|
simpleValidate(rubric) {
|
|
37112
|
-
return
|
|
37249
|
+
return labelImageValidator(this.getUserInput());
|
|
37113
37250
|
}
|
|
37114
37251
|
getUserInput() {
|
|
37115
37252
|
const {
|
|
@@ -37127,7 +37264,7 @@ class LabelImage extends React__namespace.Component {
|
|
|
37127
37264
|
onChange
|
|
37128
37265
|
} = this.props;
|
|
37129
37266
|
const updatedMarkers = markers.map(marker => {
|
|
37130
|
-
const score =
|
|
37267
|
+
const score = scoreMarker(marker);
|
|
37131
37268
|
return {
|
|
37132
37269
|
...marker,
|
|
37133
37270
|
// Reveal correctness state for markers with answers.
|
|
@@ -37276,7 +37413,7 @@ class LabelImage extends React__namespace.Component {
|
|
|
37276
37413
|
bottom: "top"
|
|
37277
37414
|
}[markerPosition];
|
|
37278
37415
|
}
|
|
37279
|
-
const score =
|
|
37416
|
+
const score = scoreMarker(marker);
|
|
37280
37417
|
// Once the question is answered, show markers
|
|
37281
37418
|
// with correct answers, otherwise passthrough
|
|
37282
37419
|
// the correctness state.
|
|
@@ -37482,9 +37619,7 @@ const LabelImageWithDependencies = /*#__PURE__*/React__namespace.forwardRef((pro
|
|
|
37482
37619
|
// methods and instead adjust Peresus to provide these facilities through
|
|
37483
37620
|
// instance methods on our Renderers.
|
|
37484
37621
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
37485
|
-
LabelImageWithDependencies.validate =
|
|
37486
|
-
// @ts-expect-error - TS2339 - Property 'gradeMarker' does not exist on type
|
|
37487
|
-
LabelImageWithDependencies.gradeMarker = LabelImage.gradeMarker;
|
|
37622
|
+
LabelImageWithDependencies.validate = labelImageValidator;
|
|
37488
37623
|
var LabelImage$1 = {
|
|
37489
37624
|
name: "label-image",
|
|
37490
37625
|
displayName: "Label Image",
|
|
@@ -38273,10 +38408,10 @@ class Matcher extends React__namespace.Component {
|
|
|
38273
38408
|
// @ts-expect-error - TS2339 - Property 'moveOptionToIndex' does not exist on type 'ReactInstance'.
|
|
38274
38409
|
this.refs.right.moveOptionToIndex(option, index);
|
|
38275
38410
|
};
|
|
38276
|
-
simpleValidate
|
|
38411
|
+
simpleValidate(rubric) {
|
|
38277
38412
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Matcher'.
|
|
38278
38413
|
return Matcher.validate(this.getUserInput(), rubric);
|
|
38279
|
-
}
|
|
38414
|
+
}
|
|
38280
38415
|
render() {
|
|
38281
38416
|
// To minimize layout shift, we display a spinner until our math
|
|
38282
38417
|
// renderer is ready to render the math inside the matcher. To
|
|
@@ -38519,7 +38654,9 @@ class Matrix extends React__namespace.Component {
|
|
|
38519
38654
|
hasEmptyCell = true;
|
|
38520
38655
|
}
|
|
38521
38656
|
if (!incorrectSize) {
|
|
38522
|
-
const validator = createValidator(
|
|
38657
|
+
const validator = createValidator(
|
|
38658
|
+
// @ts-expect-error - TS2345 - Argument of type 'number' is not assignable to parameter of type 'string'.
|
|
38659
|
+
solution[row][col], {
|
|
38523
38660
|
simplify: true
|
|
38524
38661
|
}, strings);
|
|
38525
38662
|
const result = validator(supplied[row][col]);
|
|
@@ -38691,14 +38828,14 @@ class Matrix extends React__namespace.Component {
|
|
|
38691
38828
|
}, cb);
|
|
38692
38829
|
this.props.trackInteraction();
|
|
38693
38830
|
};
|
|
38694
|
-
getUserInput
|
|
38831
|
+
getUserInput() {
|
|
38695
38832
|
return {
|
|
38696
38833
|
answers: this.props.answers
|
|
38697
38834
|
};
|
|
38698
|
-
}
|
|
38699
|
-
simpleValidate
|
|
38835
|
+
}
|
|
38836
|
+
simpleValidate(rubric) {
|
|
38700
38837
|
return Matrix.validate(this.getUserInput(), rubric, this.context.strings);
|
|
38701
|
-
}
|
|
38838
|
+
}
|
|
38702
38839
|
render() {
|
|
38703
38840
|
// Set the input sizes through JS so we can control the size of the
|
|
38704
38841
|
// brackets. (If we set them in CSS we won't know values until the
|
|
@@ -38981,9 +39118,8 @@ const Measurer = createReactClass__default["default"]({
|
|
|
38981
39118
|
getUserInput: function () {
|
|
38982
39119
|
return {};
|
|
38983
39120
|
},
|
|
38984
|
-
simpleValidate: function (
|
|
38985
|
-
|
|
38986
|
-
return Measurer.validate(this.getUserInput(), rubric);
|
|
39121
|
+
simpleValidate: function () {
|
|
39122
|
+
return noopValidator(1);
|
|
38987
39123
|
},
|
|
38988
39124
|
focus: $__default["default"].noop,
|
|
38989
39125
|
render: function () {
|
|
@@ -39012,14 +39148,7 @@ const Measurer = createReactClass__default["default"]({
|
|
|
39012
39148
|
}
|
|
39013
39149
|
});
|
|
39014
39150
|
___default["default"].extend(Measurer, {
|
|
39015
|
-
validate:
|
|
39016
|
-
return {
|
|
39017
|
-
type: "points",
|
|
39018
|
-
earned: 1,
|
|
39019
|
-
total: 1,
|
|
39020
|
-
message: null
|
|
39021
|
-
};
|
|
39022
|
-
}
|
|
39151
|
+
validate: noopValidator(1)
|
|
39023
39152
|
});
|
|
39024
39153
|
const propUpgrades = {
|
|
39025
39154
|
"1": v0props => {
|
|
@@ -39914,23 +40043,19 @@ class MoleculeWidget extends React__namespace.Component {
|
|
|
39914
40043
|
static defaultProps = {
|
|
39915
40044
|
rotationAngle: 0
|
|
39916
40045
|
};
|
|
39917
|
-
|
|
39918
|
-
|
|
39919
|
-
|
|
39920
|
-
|
|
39921
|
-
|
|
39922
|
-
|
|
39923
|
-
};
|
|
39924
|
-
};
|
|
39925
|
-
getUserInput = () => {
|
|
40046
|
+
|
|
40047
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
40048
|
+
simpleValidate() {
|
|
40049
|
+
return noopValidator();
|
|
40050
|
+
}
|
|
40051
|
+
getUserInput() {
|
|
39926
40052
|
return [];
|
|
39927
|
-
}
|
|
39928
|
-
|
|
39929
|
-
|
|
39930
|
-
|
|
39931
|
-
// return.
|
|
40053
|
+
}
|
|
40054
|
+
|
|
40055
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
40056
|
+
validate() {
|
|
39932
40057
|
return noopValidator();
|
|
39933
|
-
}
|
|
40058
|
+
}
|
|
39934
40059
|
render() {
|
|
39935
40060
|
return /*#__PURE__*/React__namespace.createElement(Molecule, {
|
|
39936
40061
|
widgetId: this.props.widgetId,
|
|
@@ -40071,6 +40196,9 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
40071
40196
|
});
|
|
40072
40197
|
return results;
|
|
40073
40198
|
});
|
|
40199
|
+
|
|
40200
|
+
// TODO: most widgets use some like Widget<Something, PerseusNumberLineWidgetOptions>
|
|
40201
|
+
// should this one?
|
|
40074
40202
|
class NumberLine extends React__namespace.Component {
|
|
40075
40203
|
static contextType = PerseusI18nContext;
|
|
40076
40204
|
static defaultProps = {
|
|
@@ -40372,18 +40500,18 @@ class NumberLine extends React__namespace.Component {
|
|
|
40372
40500
|
arrows: "->"
|
|
40373
40501
|
});
|
|
40374
40502
|
};
|
|
40375
|
-
getUserInput
|
|
40503
|
+
getUserInput() {
|
|
40376
40504
|
return {
|
|
40377
40505
|
numLinePosition: this.props.numLinePosition,
|
|
40378
40506
|
rel: this.props.isInequality ? this.props.rel : "eq",
|
|
40379
40507
|
numDivisions: this.props.numDivisions,
|
|
40380
40508
|
divisionRange: this.props.divisionRange
|
|
40381
40509
|
};
|
|
40382
|
-
}
|
|
40383
|
-
simpleValidate
|
|
40510
|
+
}
|
|
40511
|
+
simpleValidate(rubric) {
|
|
40384
40512
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof NumberLine'.
|
|
40385
40513
|
return NumberLine.validate(this.getUserInput(), rubric);
|
|
40386
|
-
}
|
|
40514
|
+
}
|
|
40387
40515
|
render() {
|
|
40388
40516
|
const {
|
|
40389
40517
|
strings
|
|
@@ -40443,6 +40571,8 @@ ___default["default"].extend(NumberLine, {
|
|
|
40443
40571
|
const correctRel = rubric.correctRel || "eq";
|
|
40444
40572
|
const correctPos = kmath.number.equal(state.numLinePosition, rubric.correctX || 0);
|
|
40445
40573
|
const outsideAllowedRange = state.numDivisions > divisionRange[1] || state.numDivisions < divisionRange[0];
|
|
40574
|
+
|
|
40575
|
+
// TODO: I don't think isTickCrtl is a thing anymore
|
|
40446
40576
|
if (state.isTickCrtl && outsideAllowedRange) {
|
|
40447
40577
|
return {
|
|
40448
40578
|
type: "invalid",
|
|
@@ -40695,8 +40825,8 @@ class Card extends React__namespace.Component {
|
|
|
40695
40825
|
}))));
|
|
40696
40826
|
}
|
|
40697
40827
|
}
|
|
40698
|
-
const NORMAL = "normal"
|
|
40699
|
-
|
|
40828
|
+
const NORMAL = "normal";
|
|
40829
|
+
const HORIZONTAL = "horizontal";
|
|
40700
40830
|
class Orderer extends React__namespace.Component {
|
|
40701
40831
|
static defaultProps = {
|
|
40702
40832
|
current: [],
|
|
@@ -40951,17 +41081,17 @@ class Orderer extends React__namespace.Component {
|
|
|
40951
41081
|
current: list
|
|
40952
41082
|
});
|
|
40953
41083
|
};
|
|
40954
|
-
getUserInput
|
|
41084
|
+
getUserInput() {
|
|
40955
41085
|
return {
|
|
40956
41086
|
current: ___default["default"].map(this.props.current, function (v) {
|
|
40957
41087
|
return v.content;
|
|
40958
41088
|
})
|
|
40959
41089
|
};
|
|
40960
|
-
}
|
|
40961
|
-
simpleValidate
|
|
41090
|
+
}
|
|
41091
|
+
simpleValidate(rubric) {
|
|
40962
41092
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Orderer'.
|
|
40963
41093
|
return Orderer.validate(this.getUserInput(), rubric);
|
|
40964
|
-
}
|
|
41094
|
+
}
|
|
40965
41095
|
render() {
|
|
40966
41096
|
// This is the card we are currently dragging
|
|
40967
41097
|
const dragging = this.state.dragging && /*#__PURE__*/React__namespace.createElement(Card
|
|
@@ -41067,14 +41197,14 @@ class Orderer extends React__namespace.Component {
|
|
|
41067
41197
|
}
|
|
41068
41198
|
}
|
|
41069
41199
|
___default["default"].extend(Orderer, {
|
|
41070
|
-
validate: function (
|
|
41071
|
-
if (
|
|
41200
|
+
validate: function (userInput, rubric) {
|
|
41201
|
+
if (userInput.current.length === 0) {
|
|
41072
41202
|
return {
|
|
41073
41203
|
type: "invalid",
|
|
41074
41204
|
message: null
|
|
41075
41205
|
};
|
|
41076
41206
|
}
|
|
41077
|
-
const correct = ___default["default"].isEqual(
|
|
41207
|
+
const correct = ___default["default"].isEqual(userInput.current, ___default["default"].pluck(rubric.correctOptions, "content"));
|
|
41078
41208
|
return {
|
|
41079
41209
|
type: "points",
|
|
41080
41210
|
earned: correct ? 1 : 0,
|
|
@@ -42896,7 +43026,9 @@ class Passage extends React__namespace.Component {
|
|
|
42896
43026
|
startLineNumbersAfter: 0,
|
|
42897
43027
|
stylesAreApplied: false
|
|
42898
43028
|
};
|
|
42899
|
-
|
|
43029
|
+
|
|
43030
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
43031
|
+
static validate() {
|
|
42900
43032
|
return noopValidator();
|
|
42901
43033
|
}
|
|
42902
43034
|
componentDidMount() {
|
|
@@ -43116,8 +43248,10 @@ class Passage extends React__namespace.Component {
|
|
|
43116
43248
|
getUserInput() {
|
|
43117
43249
|
return null;
|
|
43118
43250
|
}
|
|
43119
|
-
|
|
43120
|
-
|
|
43251
|
+
|
|
43252
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
43253
|
+
simpleValidate() {
|
|
43254
|
+
return noopValidator();
|
|
43121
43255
|
}
|
|
43122
43256
|
|
|
43123
43257
|
/**
|
|
@@ -43273,14 +43407,16 @@ class PassageRefTarget extends React__namespace.Component {
|
|
|
43273
43407
|
content: "",
|
|
43274
43408
|
linterContext: PerseusLinter.linterContextDefault
|
|
43275
43409
|
};
|
|
43276
|
-
|
|
43410
|
+
|
|
43411
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
43412
|
+
static validate() {
|
|
43277
43413
|
return noopValidator();
|
|
43278
43414
|
}
|
|
43279
43415
|
|
|
43280
43416
|
// TODO passage-ref-target isn't interactive; remove
|
|
43281
|
-
getUserInput
|
|
43282
|
-
return;
|
|
43283
|
-
}
|
|
43417
|
+
getUserInput() {
|
|
43418
|
+
return null;
|
|
43419
|
+
}
|
|
43284
43420
|
|
|
43285
43421
|
// TODO passage-ref-target isn't interactive; remove
|
|
43286
43422
|
change = (() => {
|
|
@@ -43293,10 +43429,10 @@ class PassageRefTarget extends React__namespace.Component {
|
|
|
43293
43429
|
};
|
|
43294
43430
|
})();
|
|
43295
43431
|
|
|
43296
|
-
// TODO
|
|
43297
|
-
simpleValidate
|
|
43298
|
-
return
|
|
43299
|
-
}
|
|
43432
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
43433
|
+
simpleValidate() {
|
|
43434
|
+
return noopValidator();
|
|
43435
|
+
}
|
|
43300
43436
|
render() {
|
|
43301
43437
|
return /*#__PURE__*/React__namespace.createElement(Renderer, {
|
|
43302
43438
|
content: this.props.content
|
|
@@ -43332,9 +43468,6 @@ var PassageRefTarget$1 = {
|
|
|
43332
43468
|
* This is a PhET simulation widget. It is used for rendering simulations
|
|
43333
43469
|
* from https://phet.colorado.edu/.
|
|
43334
43470
|
*/
|
|
43335
|
-
|
|
43336
|
-
// For returning user input, but currently the PhET widget
|
|
43337
|
-
// does not support accessing user input
|
|
43338
43471
|
// This renders the PhET sim
|
|
43339
43472
|
class PhetSimulation extends React__namespace.Component {
|
|
43340
43473
|
static contextType = PerseusI18nContext;
|
|
@@ -43357,6 +43490,8 @@ class PhetSimulation extends React__namespace.Component {
|
|
|
43357
43490
|
await this.updateSimState(this.props.url);
|
|
43358
43491
|
}
|
|
43359
43492
|
}
|
|
43493
|
+
|
|
43494
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
43360
43495
|
getUserInput() {
|
|
43361
43496
|
return null;
|
|
43362
43497
|
}
|
|
@@ -44306,13 +44441,13 @@ class Plotter extends React__namespace.Component {
|
|
|
44306
44441
|
});
|
|
44307
44442
|
});
|
|
44308
44443
|
};
|
|
44309
|
-
getUserInput
|
|
44444
|
+
getUserInput() {
|
|
44310
44445
|
return this.state.values;
|
|
44311
|
-
}
|
|
44312
|
-
simpleValidate
|
|
44446
|
+
}
|
|
44447
|
+
simpleValidate(rubric) {
|
|
44313
44448
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Plotter'.
|
|
44314
44449
|
return Plotter.validate(this.getUserInput(), rubric);
|
|
44315
|
-
}
|
|
44450
|
+
}
|
|
44316
44451
|
render() {
|
|
44317
44452
|
// TODO(kevinb) actually compute the size of the graphie correctly and
|
|
44318
44453
|
// make it that size so we don't have to add extra padding. The value
|
|
@@ -44331,8 +44466,8 @@ class Plotter extends React__namespace.Component {
|
|
|
44331
44466
|
}
|
|
44332
44467
|
}
|
|
44333
44468
|
___default["default"].extend(Plotter, {
|
|
44334
|
-
validate: function (
|
|
44335
|
-
if (deepEq(
|
|
44469
|
+
validate: function (userInput, rubric) {
|
|
44470
|
+
if (deepEq(userInput, rubric.starting)) {
|
|
44336
44471
|
return {
|
|
44337
44472
|
type: "invalid",
|
|
44338
44473
|
message: null
|
|
@@ -44340,7 +44475,7 @@ ___default["default"].extend(Plotter, {
|
|
|
44340
44475
|
}
|
|
44341
44476
|
return {
|
|
44342
44477
|
type: "points",
|
|
44343
|
-
earned: deepEq(
|
|
44478
|
+
earned: deepEq(userInput, rubric.correct) ? 1 : 0,
|
|
44344
44479
|
total: 1,
|
|
44345
44480
|
message: null
|
|
44346
44481
|
};
|
|
@@ -44403,6 +44538,28 @@ var PythonProgram$1 = {
|
|
|
44403
44538
|
widget: PythonProgram
|
|
44404
44539
|
};
|
|
44405
44540
|
|
|
44541
|
+
function sorterValidator(userInput, rubric) {
|
|
44542
|
+
// If the sorter widget hasn't been changed yet, we treat it as "empty" which
|
|
44543
|
+
// prevents the "Check" button from becoming active. We want the user
|
|
44544
|
+
// to make a change before trying to move forward. This makes an
|
|
44545
|
+
// assumption that the initial order isn't the correct order! However,
|
|
44546
|
+
// this should be rare if it happens, and interacting with the list
|
|
44547
|
+
// will enable the button, so they won't be locked out of progressing.
|
|
44548
|
+
if (!userInput.changed) {
|
|
44549
|
+
return {
|
|
44550
|
+
type: "invalid",
|
|
44551
|
+
message: null
|
|
44552
|
+
};
|
|
44553
|
+
}
|
|
44554
|
+
const correct = Util.deepEq(userInput.options, rubric.correct);
|
|
44555
|
+
return {
|
|
44556
|
+
type: "points",
|
|
44557
|
+
earned: correct ? 1 : 0,
|
|
44558
|
+
total: 1,
|
|
44559
|
+
message: null
|
|
44560
|
+
};
|
|
44561
|
+
}
|
|
44562
|
+
|
|
44406
44563
|
const {
|
|
44407
44564
|
shuffle
|
|
44408
44565
|
} = Util;
|
|
@@ -44416,14 +44573,8 @@ class Sorter extends React__namespace.Component {
|
|
|
44416
44573
|
onChange: function () {},
|
|
44417
44574
|
linterContext: PerseusLinter.linterContextDefault
|
|
44418
44575
|
};
|
|
44419
|
-
static validate(
|
|
44420
|
-
|
|
44421
|
-
return {
|
|
44422
|
-
type: "points",
|
|
44423
|
-
earned: correct ? 1 : 0,
|
|
44424
|
-
total: 1,
|
|
44425
|
-
message: null
|
|
44426
|
-
};
|
|
44576
|
+
static validate(userInput, rubric) {
|
|
44577
|
+
return sorterValidator(userInput, rubric);
|
|
44427
44578
|
}
|
|
44428
44579
|
state = {
|
|
44429
44580
|
changed: false
|
|
@@ -44453,33 +44604,23 @@ class Sorter extends React__namespace.Component {
|
|
|
44453
44604
|
}, 0);
|
|
44454
44605
|
});
|
|
44455
44606
|
};
|
|
44456
|
-
getUserInput
|
|
44607
|
+
getUserInput() {
|
|
44457
44608
|
// eslint-disable-next-line react/no-string-refs
|
|
44458
44609
|
// @ts-expect-error - TS2339 - Property 'getOptions' does not exist on type 'ReactInstance'.
|
|
44610
|
+
const options = this.refs.sortable.getOptions();
|
|
44459
44611
|
return {
|
|
44460
|
-
options
|
|
44612
|
+
options,
|
|
44613
|
+
changed: this.state.changed
|
|
44461
44614
|
};
|
|
44462
|
-
}
|
|
44615
|
+
}
|
|
44463
44616
|
moveOptionToIndex = (option, index) => {
|
|
44464
44617
|
// eslint-disable-next-line react/no-string-refs
|
|
44465
44618
|
// @ts-expect-error - TS2339 - Property 'moveOptionToIndex' does not exist on type 'ReactInstance'.
|
|
44466
44619
|
this.refs.sortable.moveOptionToIndex(option, index);
|
|
44467
44620
|
};
|
|
44468
|
-
simpleValidate
|
|
44469
|
-
|
|
44470
|
-
|
|
44471
|
-
// to make a change before trying to move forward. This makes an
|
|
44472
|
-
// assumption that the initial order isn't the correct order! However,
|
|
44473
|
-
// this should be rare if it happens, and interacting with the list
|
|
44474
|
-
// will enable the button, so they won't be locked out of progressing.
|
|
44475
|
-
if (!this.state.changed) {
|
|
44476
|
-
return {
|
|
44477
|
-
type: "invalid",
|
|
44478
|
-
message: null
|
|
44479
|
-
};
|
|
44480
|
-
}
|
|
44481
|
-
return Sorter.validate(this.getUserInput(), rubric);
|
|
44482
|
-
};
|
|
44621
|
+
simpleValidate(rubric) {
|
|
44622
|
+
return sorterValidator(this.getUserInput(), rubric);
|
|
44623
|
+
}
|
|
44483
44624
|
render() {
|
|
44484
44625
|
const options = shuffle(this.props.correct, this.props.problemNum, /* ensurePermuted */true);
|
|
44485
44626
|
const {
|
|
@@ -44559,9 +44700,9 @@ class Table extends React__namespace.Component {
|
|
|
44559
44700
|
_getColumns = () => {
|
|
44560
44701
|
return this.props.answers[0].length;
|
|
44561
44702
|
};
|
|
44562
|
-
getUserInput
|
|
44703
|
+
getUserInput() {
|
|
44563
44704
|
return ___default["default"].map(this.props.answers, ___default["default"].clone);
|
|
44564
|
-
}
|
|
44705
|
+
}
|
|
44565
44706
|
onValueChange = (row, column, eventOrValue) => {
|
|
44566
44707
|
const answers = ___default["default"].map(this.props.answers, ___default["default"].clone);
|
|
44567
44708
|
|
|
@@ -44581,10 +44722,10 @@ class Table extends React__namespace.Component {
|
|
|
44581
44722
|
headers: headers
|
|
44582
44723
|
});
|
|
44583
44724
|
};
|
|
44584
|
-
simpleValidate
|
|
44725
|
+
simpleValidate(rubric) {
|
|
44585
44726
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Table'.
|
|
44586
44727
|
return Table.validate(this.getUserInput(), rubric, this.context.strings);
|
|
44587
|
-
}
|
|
44728
|
+
}
|
|
44588
44729
|
_handleFocus = inputPath => {
|
|
44589
44730
|
this.props.onFocus(inputPath);
|
|
44590
44731
|
};
|
|
@@ -44880,16 +45021,18 @@ class Video extends React__namespace.Component {
|
|
|
44880
45021
|
* This is the widget's grading function.
|
|
44881
45022
|
* Points for videos are tallied by the embedded video itself, in the case
|
|
44882
45023
|
* of Khan Academy videos.
|
|
44883
|
-
*/
|
|
44884
|
-
static validate(
|
|
45024
|
+
*/ // TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
45025
|
+
static validate() {
|
|
44885
45026
|
return noopValidator();
|
|
44886
45027
|
}
|
|
44887
45028
|
getUserInput = () => {
|
|
44888
45029
|
return null;
|
|
44889
45030
|
};
|
|
44890
|
-
|
|
44891
|
-
|
|
44892
|
-
|
|
45031
|
+
|
|
45032
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
45033
|
+
simpleValidate() {
|
|
45034
|
+
return noopValidator();
|
|
45035
|
+
}
|
|
44893
45036
|
change = (() => {
|
|
44894
45037
|
var _this = this;
|
|
44895
45038
|
return function () {
|
|
@@ -45002,7 +45145,7 @@ const init = function (options) {
|
|
|
45002
45145
|
|
|
45003
45146
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
45004
45147
|
const libName = "@khanacademy/perseus";
|
|
45005
|
-
const libVersion = "
|
|
45148
|
+
const libVersion = "35.0.0";
|
|
45006
45149
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
45007
45150
|
|
|
45008
45151
|
const apiVersion = {
|
|
@@ -45556,9 +45699,6 @@ const defaultContext = {
|
|
|
45556
45699
|
};
|
|
45557
45700
|
const context = /*#__PURE__*/React__namespace.createContext(defaultContext);
|
|
45558
45701
|
|
|
45559
|
-
const {
|
|
45560
|
-
mapObject
|
|
45561
|
-
} = objective_;
|
|
45562
45702
|
class ServerItemRenderer extends React__namespace.Component {
|
|
45563
45703
|
static contextType = PerseusI18nContext;
|
|
45564
45704
|
// @ts-expect-error - TS2564 - Property 'questionRenderer' has no initializer and is not definitely assigned in the constructor.
|
|
@@ -45774,20 +45914,6 @@ class ServerItemRenderer extends React__namespace.Component {
|
|
|
45774
45914
|
return this.questionRenderer.getWidgetIds();
|
|
45775
45915
|
}
|
|
45776
45916
|
|
|
45777
|
-
/**
|
|
45778
|
-
* Returns an object mapping from widget ID to KE-style score.
|
|
45779
|
-
* The keys of this object are the values of the array returned
|
|
45780
|
-
* from `getWidgetIds`.
|
|
45781
|
-
*/
|
|
45782
|
-
scoreWidgets() {
|
|
45783
|
-
const qScore = this.questionRenderer.scoreWidgets();
|
|
45784
|
-
const qGuess = this.questionRenderer.getUserInputForWidgets();
|
|
45785
|
-
const state = this.questionRenderer.getSerializedState();
|
|
45786
|
-
return mapObject(qScore, (score, id) => {
|
|
45787
|
-
return Util.keScoreFromPerseusScore(score, qGuess[id], state);
|
|
45788
|
-
});
|
|
45789
|
-
}
|
|
45790
|
-
|
|
45791
45917
|
/**
|
|
45792
45918
|
* Get a representation of the current state of the item.
|
|
45793
45919
|
*/
|