@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/es/index.js
CHANGED
|
@@ -246,7 +246,10 @@ const mockStrings = {
|
|
|
246
246
|
cos: "cos",
|
|
247
247
|
tan: "tan",
|
|
248
248
|
simulationLoadFail: "Sorry, this simulation cannot load.",
|
|
249
|
-
simulationLocaleWarning: "Sorry, this simulation isn't available in your language."
|
|
249
|
+
simulationLocaleWarning: "Sorry, this simulation isn't available in your language.",
|
|
250
|
+
addPoint: "Add Point",
|
|
251
|
+
removePoint: "Remove Point",
|
|
252
|
+
graphKeyboardPrompt: "Press Shift + Enter to interact with the graph"
|
|
250
253
|
};
|
|
251
254
|
|
|
252
255
|
/**
|
|
@@ -749,7 +752,7 @@ const denylist = [
|
|
|
749
752
|
// (technically the renderer still adds them)
|
|
750
753
|
"key", "ref",
|
|
751
754
|
// added by src/renderer.jsx
|
|
752
|
-
"containerSizeClass", "widgetId", "onChange", "problemNum", "apiOptions", "questionCompleted", "findWidgets",
|
|
755
|
+
"containerSizeClass", "widgetId", "onChange", "problemNum", "apiOptions", "widgetIsOpen", "questionCompleted", "findWidgets",
|
|
753
756
|
// added by src/editor.jsx, for widgets removing themselves
|
|
754
757
|
// this is soooo not the right place for this, but alas.
|
|
755
758
|
"onRemove",
|
|
@@ -757,15 +760,6 @@ const denylist = [
|
|
|
757
760
|
"id",
|
|
758
761
|
// Callbacks and items for interaction handling
|
|
759
762
|
"onBlur", "onFocus", "trackInteraction", "keypadElement"];
|
|
760
|
-
const removeDenylistProps = props => {
|
|
761
|
-
const newProps = _extends({}, props);
|
|
762
|
-
for (const prop of denylist) {
|
|
763
|
-
if (prop in newProps) {
|
|
764
|
-
delete newProps[prop];
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
return newProps;
|
|
768
|
-
};
|
|
769
763
|
|
|
770
764
|
/* eslint-disable @babel/no-invalid-this */
|
|
771
765
|
const USAGE = "Usage:\n" + " this.change({propName: 5}, callback);\n" + ' this.change("propName", 5, callback);\n' + ' this.change("propName")';
|
|
@@ -1030,8 +1024,6 @@ StubTagEditor.defaultProps = {
|
|
|
1030
1024
|
* * nothing if it is purely a bug fix.
|
|
1031
1025
|
*
|
|
1032
1026
|
* Callbacks passed to Renderer/ItemRenderer:
|
|
1033
|
-
* * onInputError:
|
|
1034
|
-
* Called when there is an error grading a widget
|
|
1035
1027
|
* * onFocusChange: (newFocusPath, oldFocusPath, keypadDOMNode)
|
|
1036
1028
|
* Called when the user focus changes. The first two parameters are `path`
|
|
1037
1029
|
* arrays uniquely identifying the respect inputs. The third parameter,
|
|
@@ -1054,7 +1046,6 @@ StubTagEditor.defaultProps = {
|
|
|
1054
1046
|
const ApiOptions = {
|
|
1055
1047
|
propTypes: PropTypes.shape({
|
|
1056
1048
|
isArticle: PropTypes.bool.isRequired,
|
|
1057
|
-
onInputError: PropTypes.func.isRequired,
|
|
1058
1049
|
onFocusChange: PropTypes.func.isRequired,
|
|
1059
1050
|
GroupMetadataEditor: PropTypes.func.isRequired,
|
|
1060
1051
|
showAlignmentOptions: PropTypes.bool.isRequired,
|
|
@@ -1138,7 +1129,6 @@ const ApiOptions = {
|
|
|
1138
1129
|
defaults: {
|
|
1139
1130
|
isArticle: false,
|
|
1140
1131
|
isMobile: false,
|
|
1141
|
-
onInputError: function () {},
|
|
1142
1132
|
onFocusChange: function () {},
|
|
1143
1133
|
GroupMetadataEditor: StubTagEditor,
|
|
1144
1134
|
showAlignmentOptions: false,
|
|
@@ -2150,9 +2140,7 @@ const getDecimalSeparator = locale => {
|
|
|
2150
2140
|
* show the user an error. TODO(joel) - what error?
|
|
2151
2141
|
* - Otherwise, pass through the resulting points and message.
|
|
2152
2142
|
*/
|
|
2153
|
-
function expressionValidator(userInput, rubric,
|
|
2154
|
-
// @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
|
|
2155
|
-
onInputError = function () {}, strings, locale) {
|
|
2143
|
+
function expressionValidator(userInput, rubric, strings, locale) {
|
|
2156
2144
|
const options = _.clone(rubric);
|
|
2157
2145
|
_.extend(options, {
|
|
2158
2146
|
decimal_separator: getDecimalSeparator(locale)
|
|
@@ -2252,14 +2240,9 @@ onInputError = function () {}, strings, locale) {
|
|
|
2252
2240
|
};
|
|
2253
2241
|
}
|
|
2254
2242
|
if (matchingAnswerForm.considered === "ungraded") {
|
|
2255
|
-
// We matched an ungraded answer form - return "invalid", which
|
|
2256
|
-
// will let the user try again with no penalty
|
|
2257
|
-
const apiResult = onInputError(null,
|
|
2258
|
-
// Reserved for some widget identifier
|
|
2259
|
-
userInput, matchMessage);
|
|
2260
2243
|
return {
|
|
2261
2244
|
type: "invalid",
|
|
2262
|
-
message:
|
|
2245
|
+
message: matchMessage
|
|
2263
2246
|
};
|
|
2264
2247
|
}
|
|
2265
2248
|
// We matched a graded answer form, so we can now tell the user
|
|
@@ -2348,14 +2331,9 @@ class Expression extends React.Component {
|
|
|
2348
2331
|
showErrorStyle: false
|
|
2349
2332
|
});
|
|
2350
2333
|
if (!this.parse(this.props.value, this.props).parsed) {
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
if (apiResult !== false) {
|
|
2355
|
-
this.setState({
|
|
2356
|
-
invalid: true
|
|
2357
|
-
});
|
|
2358
|
-
}
|
|
2334
|
+
this.setState({
|
|
2335
|
+
invalid: true
|
|
2336
|
+
});
|
|
2359
2337
|
}
|
|
2360
2338
|
}
|
|
2361
2339
|
};
|
|
@@ -2369,12 +2347,12 @@ class Expression extends React.Component {
|
|
|
2369
2347
|
});
|
|
2370
2348
|
}
|
|
2371
2349
|
};
|
|
2372
|
-
this.simpleValidate =
|
|
2350
|
+
this.simpleValidate = _ref => {
|
|
2373
2351
|
let {
|
|
2374
2352
|
scoring
|
|
2375
2353
|
} = _ref,
|
|
2376
2354
|
rubric = _objectWithoutPropertiesLoose(_ref, _excluded$5);
|
|
2377
|
-
const score = expressionValidator(this.getUserInput(), rubric,
|
|
2355
|
+
const score = expressionValidator(this.getUserInput(), rubric, this.context.strings, this.context.locale);
|
|
2378
2356
|
|
|
2379
2357
|
// "scoring" is a flag that indicates when we are checking answers.
|
|
2380
2358
|
// otherwise, we may just be checking validity after changes.
|
|
@@ -2390,9 +2368,6 @@ class Expression extends React.Component {
|
|
|
2390
2368
|
}
|
|
2391
2369
|
return score;
|
|
2392
2370
|
};
|
|
2393
|
-
this.getUserInput = () => {
|
|
2394
|
-
return Expression.getUserInputFromProps(this.props);
|
|
2395
|
-
};
|
|
2396
2371
|
this.change = (...args) => {
|
|
2397
2372
|
return change.apply(this, args);
|
|
2398
2373
|
};
|
|
@@ -2465,10 +2440,8 @@ class Expression extends React.Component {
|
|
|
2465
2440
|
};
|
|
2466
2441
|
}
|
|
2467
2442
|
// TODO remove this in favor of just using expressionValidator
|
|
2468
|
-
static validate(userInput, rubric,
|
|
2469
|
-
|
|
2470
|
-
onInputError = function () {}, strings, locale) {
|
|
2471
|
-
return expressionValidator(userInput, rubric, onInputError, strings, locale);
|
|
2443
|
+
static validate(userInput, rubric, strings, locale) {
|
|
2444
|
+
return expressionValidator(userInput, rubric, strings, locale);
|
|
2472
2445
|
}
|
|
2473
2446
|
static getUserInputFromProps(props) {
|
|
2474
2447
|
return normalizeTex(props.value);
|
|
@@ -2482,6 +2455,9 @@ class Expression extends React.Component {
|
|
|
2482
2455
|
}
|
|
2483
2456
|
//#endregion
|
|
2484
2457
|
|
|
2458
|
+
getUserInput() {
|
|
2459
|
+
return Expression.getUserInputFromProps(this.props);
|
|
2460
|
+
}
|
|
2485
2461
|
// HACK(joel)
|
|
2486
2462
|
insert(keyPressed) {
|
|
2487
2463
|
// eslint-disable-next-line react/no-string-refs
|
|
@@ -14677,7 +14653,7 @@ const pluck = function pluck(table, subKey) {
|
|
|
14677
14653
|
* });
|
|
14678
14654
|
* {a: 2, b: 3}
|
|
14679
14655
|
*/
|
|
14680
|
-
const mapObject$
|
|
14656
|
+
const mapObject$1 = function mapObject(obj, lambda) {
|
|
14681
14657
|
const result = {};
|
|
14682
14658
|
_.each(_.keys(obj), function (key) {
|
|
14683
14659
|
// @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'K'.
|
|
@@ -14703,7 +14679,7 @@ const mapObjectFromArray$1 = function mapObjectFromArray(arr, lambda) {
|
|
|
14703
14679
|
};
|
|
14704
14680
|
var objective_ = {
|
|
14705
14681
|
pluck: pluck,
|
|
14706
|
-
mapObject: mapObject$
|
|
14682
|
+
mapObject: mapObject$1,
|
|
14707
14683
|
mapObjectFromArray: mapObjectFromArray$1
|
|
14708
14684
|
};
|
|
14709
14685
|
|
|
@@ -19596,7 +19572,7 @@ WidgetContainer.defaultProps = {
|
|
|
19596
19572
|
};
|
|
19597
19573
|
|
|
19598
19574
|
const {
|
|
19599
|
-
mapObject
|
|
19575
|
+
mapObject,
|
|
19600
19576
|
mapObjectFromArray
|
|
19601
19577
|
} = objective_;
|
|
19602
19578
|
const rContainsNonWhitespace = /\S/;
|
|
@@ -19624,9 +19600,6 @@ const isIdPathPrefix = function isIdPathPrefix(prefixArray, wholeArray) {
|
|
|
19624
19600
|
});
|
|
19625
19601
|
};
|
|
19626
19602
|
|
|
19627
|
-
// The return type for getUserInput. Widgets have full control of what is
|
|
19628
|
-
// returned so it's not easily typed (some widgets return a scalar (string),
|
|
19629
|
-
// some return a custom-built object
|
|
19630
19603
|
/**
|
|
19631
19604
|
* The Widget type represents the common API that the Renderer uses to interact
|
|
19632
19605
|
* with all widgets. All widgets must implement the methods in this API, unless
|
|
@@ -19636,6 +19609,7 @@ const isIdPathPrefix = function isIdPathPrefix(prefixArray, wholeArray) {
|
|
|
19636
19609
|
* communicate with the individual widgets to coordinate actions such as
|
|
19637
19610
|
* scoring, state serialization/deserialization, and focus management.
|
|
19638
19611
|
*/
|
|
19612
|
+
|
|
19639
19613
|
class Renderer extends React.Component {
|
|
19640
19614
|
constructor(_props) {
|
|
19641
19615
|
super(_props);
|
|
@@ -19670,7 +19644,7 @@ class Renderer extends React.Component {
|
|
|
19670
19644
|
};
|
|
19671
19645
|
// @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; }'.
|
|
19672
19646
|
this._getAllWidgetsInfo = props => {
|
|
19673
|
-
return mapObject
|
|
19647
|
+
return mapObject(props.widgets, (widgetInfo, widgetId) => {
|
|
19674
19648
|
if (!widgetInfo.type || !widgetInfo.alignment) {
|
|
19675
19649
|
const newValues = {};
|
|
19676
19650
|
if (!widgetInfo.type) {
|
|
@@ -19811,7 +19785,7 @@ class Renderer extends React.Component {
|
|
|
19811
19785
|
* state from that instead of the current widget props.
|
|
19812
19786
|
*/
|
|
19813
19787
|
this.getSerializedState = widgetProps => {
|
|
19814
|
-
return mapObject
|
|
19788
|
+
return mapObject(widgetProps || this.state.widgetProps, (props, widgetId) => {
|
|
19815
19789
|
const widget = this.getWidgetInstance(widgetId);
|
|
19816
19790
|
if (widget && widget.getSerializedState) {
|
|
19817
19791
|
return widget.getSerializedState();
|
|
@@ -19847,7 +19821,7 @@ class Renderer extends React.Component {
|
|
|
19847
19821
|
}
|
|
19848
19822
|
};
|
|
19849
19823
|
this.setState({
|
|
19850
|
-
widgetProps: mapObject
|
|
19824
|
+
widgetProps: mapObject(serializedState, (props, widgetId) => {
|
|
19851
19825
|
const widget = this.getWidgetInstance(widgetId);
|
|
19852
19826
|
if (widget && widget.restoreSerializedState) {
|
|
19853
19827
|
// Note that we probably can't call
|
|
@@ -20681,7 +20655,6 @@ class Renderer extends React.Component {
|
|
|
20681
20655
|
*/
|
|
20682
20656
|
this.scoreWidgets = () => {
|
|
20683
20657
|
const widgetProps = this.state.widgetInfo;
|
|
20684
|
-
const onInputError = this.getApiOptions().onInputError;
|
|
20685
20658
|
const gradedWidgetIds = _.filter(this.widgetIds, id => {
|
|
20686
20659
|
const props = widgetProps[id];
|
|
20687
20660
|
const widgetIsGraded = (props == null ? void 0 : props.graded) == null || props.graded;
|
|
@@ -20697,7 +20670,7 @@ class Renderer extends React.Component {
|
|
|
20697
20670
|
if (widget && widget.simpleValidate) {
|
|
20698
20671
|
widgetScores[id] = widget.simpleValidate(_extends({}, props == null ? void 0 : props.options, {
|
|
20699
20672
|
scoring: true
|
|
20700
|
-
})
|
|
20673
|
+
}));
|
|
20701
20674
|
}
|
|
20702
20675
|
});
|
|
20703
20676
|
return widgetScores;
|
|
@@ -21738,6 +21711,43 @@ const answerTypes = {
|
|
|
21738
21711
|
forms: "pi"
|
|
21739
21712
|
}
|
|
21740
21713
|
};
|
|
21714
|
+
function inputNumberValidator(state, rubric, strings) {
|
|
21715
|
+
if (rubric.answerType == null) {
|
|
21716
|
+
rubric.answerType = "number";
|
|
21717
|
+
}
|
|
21718
|
+
|
|
21719
|
+
// note(matthewc): this will get immediately parsed again by
|
|
21720
|
+
// `KhanAnswerTypes.number.convertToPredicate`, but a string is
|
|
21721
|
+
// expected here
|
|
21722
|
+
const stringValue = `${rubric.value}`;
|
|
21723
|
+
const val = KhanAnswerTypes.number.createValidatorFunctional(stringValue, {
|
|
21724
|
+
simplify: rubric.simplify,
|
|
21725
|
+
inexact: rubric.inexact || undefined,
|
|
21726
|
+
maxError: rubric.maxError,
|
|
21727
|
+
forms: answerTypes[rubric.answerType].forms
|
|
21728
|
+
}, strings);
|
|
21729
|
+
|
|
21730
|
+
// We may have received TeX; try to parse it before grading.
|
|
21731
|
+
// If `currentValue` is not TeX, this should be a no-op.
|
|
21732
|
+
const currentValue = ParseTex$1(state.currentValue);
|
|
21733
|
+
const result = val(currentValue);
|
|
21734
|
+
|
|
21735
|
+
// TODO(eater): Seems silly to translate result to this invalid/points
|
|
21736
|
+
// thing and immediately translate it back in ItemRenderer.scoreInput()
|
|
21737
|
+
if (result.empty) {
|
|
21738
|
+
return {
|
|
21739
|
+
type: "invalid",
|
|
21740
|
+
message: result.message
|
|
21741
|
+
};
|
|
21742
|
+
}
|
|
21743
|
+
return {
|
|
21744
|
+
type: "points",
|
|
21745
|
+
earned: result.correct ? 1 : 0,
|
|
21746
|
+
total: 1,
|
|
21747
|
+
message: result.message
|
|
21748
|
+
};
|
|
21749
|
+
}
|
|
21750
|
+
|
|
21741
21751
|
const formExamples$1 = {
|
|
21742
21752
|
integer: function (options, strings) {
|
|
21743
21753
|
return strings.integerExample;
|
|
@@ -21823,13 +21833,6 @@ class InputNumber extends React.Component {
|
|
|
21823
21833
|
currentValue: newValue
|
|
21824
21834
|
}, cb);
|
|
21825
21835
|
};
|
|
21826
|
-
this.getUserInput = () => {
|
|
21827
|
-
return InputNumber.getUserInputFromProps(this.props);
|
|
21828
|
-
};
|
|
21829
|
-
this.simpleValidate = (rubric, onInputError) => {
|
|
21830
|
-
onInputError = onInputError || function () {};
|
|
21831
|
-
return InputNumber.validate(this.getUserInput(), rubric, this.context.strings, onInputError);
|
|
21832
|
-
};
|
|
21833
21836
|
this.examples = () => {
|
|
21834
21837
|
const {
|
|
21835
21838
|
strings
|
|
@@ -21840,45 +21843,8 @@ class InputNumber extends React.Component {
|
|
|
21840
21843
|
return [strings.yourAnswer].concat(examples);
|
|
21841
21844
|
};
|
|
21842
21845
|
}
|
|
21843
|
-
static validate(state, rubric, strings
|
|
21844
|
-
|
|
21845
|
-
rubric.answerType = "number";
|
|
21846
|
-
}
|
|
21847
|
-
|
|
21848
|
-
// note(matthewc): this will get immediately parsed again by
|
|
21849
|
-
// `KhanAnswerTypes.number.convertToPredicate`, but a string is
|
|
21850
|
-
// expected here
|
|
21851
|
-
const stringValue = `${rubric.value}`;
|
|
21852
|
-
const val = KhanAnswerTypes.number.createValidatorFunctional(stringValue, {
|
|
21853
|
-
simplify: rubric.simplify,
|
|
21854
|
-
inexact: rubric.inexact || undefined,
|
|
21855
|
-
maxError: rubric.maxError,
|
|
21856
|
-
forms: answerTypes[rubric.answerType].forms
|
|
21857
|
-
}, strings);
|
|
21858
|
-
|
|
21859
|
-
// We may have received TeX; try to parse it before grading.
|
|
21860
|
-
// If `currentValue` is not TeX, this should be a no-op.
|
|
21861
|
-
const currentValue = ParseTex$1(state.currentValue);
|
|
21862
|
-
const result = val(currentValue);
|
|
21863
|
-
|
|
21864
|
-
// TODO(eater): Seems silly to translate result to this invalid/points
|
|
21865
|
-
// thing and immediately translate it back in ItemRenderer.scoreInput()
|
|
21866
|
-
if (result.empty) {
|
|
21867
|
-
// TODO(FEI-3867): remove null-check once we have APIOptionsInternal
|
|
21868
|
-
const apiResult = onInputError == null ? void 0 : onInputError(null,
|
|
21869
|
-
// reserved for some widget identifier
|
|
21870
|
-
state.currentValue, result.message);
|
|
21871
|
-
return {
|
|
21872
|
-
type: "invalid",
|
|
21873
|
-
message: apiResult === false ? null : result.message
|
|
21874
|
-
};
|
|
21875
|
-
}
|
|
21876
|
-
return {
|
|
21877
|
-
type: "points",
|
|
21878
|
-
earned: result.correct ? 1 : 0,
|
|
21879
|
-
total: 1,
|
|
21880
|
-
message: result.message
|
|
21881
|
-
};
|
|
21846
|
+
static validate(state, rubric, strings) {
|
|
21847
|
+
return inputNumberValidator(state, rubric, strings);
|
|
21882
21848
|
}
|
|
21883
21849
|
static getUserInputFromProps(props) {
|
|
21884
21850
|
return {
|
|
@@ -21895,6 +21861,12 @@ class InputNumber extends React.Component {
|
|
|
21895
21861
|
}
|
|
21896
21862
|
return answerString;
|
|
21897
21863
|
}
|
|
21864
|
+
getUserInput() {
|
|
21865
|
+
return InputNumber.getUserInputFromProps(this.props);
|
|
21866
|
+
}
|
|
21867
|
+
simpleValidate(rubric) {
|
|
21868
|
+
return inputNumberValidator(this.getUserInput(), rubric, this.context.strings);
|
|
21869
|
+
}
|
|
21898
21870
|
render() {
|
|
21899
21871
|
if (this.props.apiOptions.customKeypad) {
|
|
21900
21872
|
// TODO(charlie): Support "Review Mode".
|
|
@@ -22198,9 +22170,6 @@ class NumericInput extends React.Component {
|
|
|
22198
22170
|
const allFormsAccepted = answerFormNames.length >= Object.keys(formExamples).length;
|
|
22199
22171
|
return !noFormsAccepted && !allFormsAccepted;
|
|
22200
22172
|
};
|
|
22201
|
-
this.simpleValidate = rubric => {
|
|
22202
|
-
return numericInputValidator(this.getUserInput(), rubric, this.context.strings);
|
|
22203
|
-
};
|
|
22204
22173
|
this.focus = () => {
|
|
22205
22174
|
var _this$inputRef;
|
|
22206
22175
|
(_this$inputRef = this.inputRef) == null || _this$inputRef.focus();
|
|
@@ -22230,9 +22199,6 @@ class NumericInput extends React.Component {
|
|
|
22230
22199
|
currentValue: newValue
|
|
22231
22200
|
}, cb);
|
|
22232
22201
|
};
|
|
22233
|
-
this.getUserInput = () => {
|
|
22234
|
-
return NumericInput.getUserInputFromProps(this.props);
|
|
22235
|
-
};
|
|
22236
22202
|
this.handleChange = (newValue, cb) => {
|
|
22237
22203
|
this.props.onChange({
|
|
22238
22204
|
currentValue: newValue
|
|
@@ -22282,6 +22248,12 @@ class NumericInput extends React.Component {
|
|
|
22282
22248
|
static validate(userInput, rubric, strings) {
|
|
22283
22249
|
return numericInputValidator(userInput, rubric, strings);
|
|
22284
22250
|
}
|
|
22251
|
+
simpleValidate(rubric) {
|
|
22252
|
+
return numericInputValidator(this.getUserInput(), rubric, this.context.strings);
|
|
22253
|
+
}
|
|
22254
|
+
getUserInput() {
|
|
22255
|
+
return NumericInput.getUserInputFromProps(this.props);
|
|
22256
|
+
}
|
|
22285
22257
|
render() {
|
|
22286
22258
|
let labelText = this.props.labelText;
|
|
22287
22259
|
if (labelText == null || labelText === "") {
|
|
@@ -22412,15 +22384,15 @@ var NumericInput$1 = {
|
|
|
22412
22384
|
* Several widgets don't have "right"/"wrong" validation logic,
|
|
22413
22385
|
* so this just says to move on past those widgets
|
|
22414
22386
|
*
|
|
22415
|
-
* TODO
|
|
22387
|
+
* TODO(LEMS-2396) widgets that use this probably shouldn't have any
|
|
22416
22388
|
* validation logic and the thing validating an exercise
|
|
22417
22389
|
* should just know to skip these
|
|
22418
22390
|
*/
|
|
22419
|
-
function noopValidator() {
|
|
22391
|
+
function noopValidator(points = 0) {
|
|
22420
22392
|
return {
|
|
22421
22393
|
type: "points",
|
|
22422
|
-
earned:
|
|
22423
|
-
total:
|
|
22394
|
+
earned: points,
|
|
22395
|
+
total: points,
|
|
22424
22396
|
message: null
|
|
22425
22397
|
};
|
|
22426
22398
|
}
|
|
@@ -22438,11 +22410,6 @@ class PassageRef extends React.Component {
|
|
|
22438
22410
|
lineRange: null,
|
|
22439
22411
|
content: null
|
|
22440
22412
|
};
|
|
22441
|
-
// TODO(tamarab): getUserInput needs to be updated to only return
|
|
22442
|
-
// props input by the user. Currently returns all the widget's props.
|
|
22443
|
-
this.getUserInput = () => {
|
|
22444
|
-
return removeDenylistProps(this.props);
|
|
22445
|
-
};
|
|
22446
22413
|
this.change = (...args) => {
|
|
22447
22414
|
return change.apply(this, args);
|
|
22448
22415
|
};
|
|
@@ -22472,11 +22439,9 @@ class PassageRef extends React.Component {
|
|
|
22472
22439
|
}
|
|
22473
22440
|
}
|
|
22474
22441
|
};
|
|
22475
|
-
this.simpleValidate = rubric => {
|
|
22476
|
-
return PassageRef.validate(this.getUserInput(), rubric);
|
|
22477
|
-
};
|
|
22478
22442
|
}
|
|
22479
|
-
|
|
22443
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
22444
|
+
static validate() {
|
|
22480
22445
|
return noopValidator();
|
|
22481
22446
|
}
|
|
22482
22447
|
componentDidMount() {
|
|
@@ -22497,6 +22462,15 @@ class PassageRef extends React.Component {
|
|
|
22497
22462
|
window.removeEventListener("resize", this._throttledUpdateRange);
|
|
22498
22463
|
this._isMounted = false;
|
|
22499
22464
|
}
|
|
22465
|
+
|
|
22466
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
22467
|
+
getUserInput() {
|
|
22468
|
+
return null;
|
|
22469
|
+
}
|
|
22470
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
22471
|
+
simpleValidate() {
|
|
22472
|
+
return noopValidator();
|
|
22473
|
+
}
|
|
22500
22474
|
render() {
|
|
22501
22475
|
const {
|
|
22502
22476
|
strings
|
|
@@ -23898,6 +23872,56 @@ const styles$t = StyleSheet.create({
|
|
|
23898
23872
|
}
|
|
23899
23873
|
});
|
|
23900
23874
|
|
|
23875
|
+
function radioValidator(userInput, rubric, strings) {
|
|
23876
|
+
const numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
23877
|
+
return sum + (selected ? 1 : 0);
|
|
23878
|
+
}, 0);
|
|
23879
|
+
if (numSelected === 0) {
|
|
23880
|
+
return {
|
|
23881
|
+
type: "invalid",
|
|
23882
|
+
message: null
|
|
23883
|
+
};
|
|
23884
|
+
}
|
|
23885
|
+
|
|
23886
|
+
// TODO: should numCorrect actually be on the rubric
|
|
23887
|
+
// instead of the userInput?
|
|
23888
|
+
if (userInput.numCorrect && userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
23889
|
+
return {
|
|
23890
|
+
type: "invalid",
|
|
23891
|
+
message: strings.chooseCorrectNum
|
|
23892
|
+
};
|
|
23893
|
+
// If NOTA and some other answer are checked, ...
|
|
23894
|
+
}
|
|
23895
|
+
|
|
23896
|
+
// TODO: should noneOfTheAboveSelected be replaced with a
|
|
23897
|
+
// combination of choicesSelected and noneOfTheAboveIndex?
|
|
23898
|
+
if (userInput.noneOfTheAboveSelected && numSelected > 1) {
|
|
23899
|
+
return {
|
|
23900
|
+
type: "invalid",
|
|
23901
|
+
message: strings.notNoneOfTheAbove
|
|
23902
|
+
};
|
|
23903
|
+
}
|
|
23904
|
+
const correct = userInput.choicesSelected.every((selected, i) => {
|
|
23905
|
+
let isCorrect;
|
|
23906
|
+
// TODO: should noneOfTheAboveIndex actually be on the rubric
|
|
23907
|
+
// instead of the userInput?
|
|
23908
|
+
if (userInput.noneOfTheAboveIndex === i) {
|
|
23909
|
+
isCorrect = rubric.choices.every((choice, j) => {
|
|
23910
|
+
return i === j || !choice.correct;
|
|
23911
|
+
});
|
|
23912
|
+
} else {
|
|
23913
|
+
isCorrect = !!rubric.choices[i].correct;
|
|
23914
|
+
}
|
|
23915
|
+
return isCorrect === selected;
|
|
23916
|
+
});
|
|
23917
|
+
return {
|
|
23918
|
+
type: "points",
|
|
23919
|
+
earned: correct ? 1 : 0,
|
|
23920
|
+
total: 1,
|
|
23921
|
+
message: null
|
|
23922
|
+
};
|
|
23923
|
+
}
|
|
23924
|
+
|
|
23901
23925
|
// RenderProps is the return type for radio.jsx#transform
|
|
23902
23926
|
|
|
23903
23927
|
class Radio$1 extends React.Component {
|
|
@@ -23992,12 +24016,6 @@ class Radio$1 extends React.Component {
|
|
|
23992
24016
|
});
|
|
23993
24017
|
this.props.trackInteraction();
|
|
23994
24018
|
};
|
|
23995
|
-
this.getUserInput = () => {
|
|
23996
|
-
return Radio$1.getUserInputFromProps(this.props);
|
|
23997
|
-
};
|
|
23998
|
-
this.simpleValidate = rubric => {
|
|
23999
|
-
return Radio$1.validate(this.getUserInput(), rubric, this.context.strings);
|
|
24000
|
-
};
|
|
24001
24019
|
/**
|
|
24002
24020
|
* Turn on rationale display for the currently selected choices. Note that
|
|
24003
24021
|
* this leaves rationales on for choices that are already showing
|
|
@@ -24058,46 +24076,7 @@ class Radio$1 extends React.Component {
|
|
|
24058
24076
|
};
|
|
24059
24077
|
}
|
|
24060
24078
|
static validate(userInput, rubric, strings) {
|
|
24061
|
-
|
|
24062
|
-
return sum + (selected ? 1 : 0);
|
|
24063
|
-
}, 0);
|
|
24064
|
-
if (numSelected === 0) {
|
|
24065
|
-
return {
|
|
24066
|
-
type: "invalid",
|
|
24067
|
-
message: null
|
|
24068
|
-
};
|
|
24069
|
-
}
|
|
24070
|
-
if (userInput.numCorrect && userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
24071
|
-
return {
|
|
24072
|
-
type: "invalid",
|
|
24073
|
-
message: strings.chooseCorrectNum
|
|
24074
|
-
};
|
|
24075
|
-
// If NOTA and some other answer are checked, ...
|
|
24076
|
-
}
|
|
24077
|
-
|
|
24078
|
-
if (userInput.noneOfTheAboveSelected && numSelected > 1) {
|
|
24079
|
-
return {
|
|
24080
|
-
type: "invalid",
|
|
24081
|
-
message: strings.notNoneOfTheAbove
|
|
24082
|
-
};
|
|
24083
|
-
}
|
|
24084
|
-
const correct = userInput.choicesSelected.every((selected, i) => {
|
|
24085
|
-
let isCorrect;
|
|
24086
|
-
if (userInput.noneOfTheAboveIndex === i) {
|
|
24087
|
-
isCorrect = rubric.choices.every((choice, j) => {
|
|
24088
|
-
return i === j || !choice.correct;
|
|
24089
|
-
});
|
|
24090
|
-
} else {
|
|
24091
|
-
isCorrect = !!rubric.choices[i].correct;
|
|
24092
|
-
}
|
|
24093
|
-
return isCorrect === selected;
|
|
24094
|
-
});
|
|
24095
|
-
return {
|
|
24096
|
-
type: "points",
|
|
24097
|
-
earned: correct ? 1 : 0,
|
|
24098
|
-
total: 1,
|
|
24099
|
-
message: null
|
|
24100
|
-
};
|
|
24079
|
+
return radioValidator(userInput, rubric, strings);
|
|
24101
24080
|
}
|
|
24102
24081
|
static getUserInputFromProps(props) {
|
|
24103
24082
|
// Return checked inputs in the form {choicesSelected: [bool]}. (Dear
|
|
@@ -24186,6 +24165,12 @@ class Radio$1 extends React.Component {
|
|
|
24186
24165
|
registerFocusFunction(fun) {
|
|
24187
24166
|
this.focusFunction = fun;
|
|
24188
24167
|
}
|
|
24168
|
+
getUserInput() {
|
|
24169
|
+
return Radio$1.getUserInputFromProps(this.props);
|
|
24170
|
+
}
|
|
24171
|
+
simpleValidate(rubric) {
|
|
24172
|
+
return radioValidator(this.getUserInput(), rubric, this.context.strings);
|
|
24173
|
+
}
|
|
24189
24174
|
render() {
|
|
24190
24175
|
const {
|
|
24191
24176
|
choices
|
|
@@ -24426,6 +24411,31 @@ var Radio = {
|
|
|
24426
24411
|
/* eslint-disable import/no-named-as-default */
|
|
24427
24412
|
var basicWidgets = [Radio, InputNumber$1, NumericInput$1, Expression$1];
|
|
24428
24413
|
|
|
24414
|
+
function categorizerValidator(userInput, rubric, strings) {
|
|
24415
|
+
let completed = true;
|
|
24416
|
+
let allCorrect = true;
|
|
24417
|
+
rubric.values.forEach((value, i) => {
|
|
24418
|
+
if (userInput.values[i] == null) {
|
|
24419
|
+
completed = false;
|
|
24420
|
+
}
|
|
24421
|
+
if (userInput.values[i] !== value) {
|
|
24422
|
+
allCorrect = false;
|
|
24423
|
+
}
|
|
24424
|
+
});
|
|
24425
|
+
if (!completed) {
|
|
24426
|
+
return {
|
|
24427
|
+
type: "invalid",
|
|
24428
|
+
message: strings.invalidSelection
|
|
24429
|
+
};
|
|
24430
|
+
}
|
|
24431
|
+
return {
|
|
24432
|
+
type: "points",
|
|
24433
|
+
earned: allCorrect ? 1 : 0,
|
|
24434
|
+
total: 1,
|
|
24435
|
+
message: null
|
|
24436
|
+
};
|
|
24437
|
+
}
|
|
24438
|
+
|
|
24429
24439
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
24430
24440
|
class Categorizer extends React.Component {
|
|
24431
24441
|
constructor(..._args) {
|
|
@@ -24437,42 +24447,18 @@ class Categorizer extends React.Component {
|
|
|
24437
24447
|
// @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
|
|
24438
24448
|
return change.apply(this, args);
|
|
24439
24449
|
};
|
|
24440
|
-
this.getUserInput = () => {
|
|
24441
|
-
return Categorizer.getUserInputFromProps(this.props);
|
|
24442
|
-
};
|
|
24443
|
-
this.simpleValidate = rubric => {
|
|
24444
|
-
return Categorizer.validate(this.getUserInput(), rubric, this.context.strings);
|
|
24445
|
-
};
|
|
24446
24450
|
}
|
|
24447
24451
|
static validate(userInput, rubric, strings) {
|
|
24448
|
-
|
|
24449
|
-
let allCorrect = true;
|
|
24450
|
-
rubric.values.forEach((value, i) => {
|
|
24451
|
-
if (userInput.values[i] == null) {
|
|
24452
|
-
completed = false;
|
|
24453
|
-
}
|
|
24454
|
-
if (userInput.values[i] !== value) {
|
|
24455
|
-
allCorrect = false;
|
|
24456
|
-
}
|
|
24457
|
-
});
|
|
24458
|
-
if (!completed) {
|
|
24459
|
-
return {
|
|
24460
|
-
type: "invalid",
|
|
24461
|
-
message: strings.invalidSelection
|
|
24462
|
-
};
|
|
24463
|
-
}
|
|
24464
|
-
return {
|
|
24465
|
-
type: "points",
|
|
24466
|
-
earned: allCorrect ? 1 : 0,
|
|
24467
|
-
total: 1,
|
|
24468
|
-
message: null
|
|
24469
|
-
};
|
|
24452
|
+
return categorizerValidator(userInput, rubric, strings);
|
|
24470
24453
|
}
|
|
24471
24454
|
static getUserInputFromProps(props) {
|
|
24472
24455
|
return {
|
|
24473
24456
|
values: props.values
|
|
24474
24457
|
};
|
|
24475
24458
|
}
|
|
24459
|
+
getUserInput() {
|
|
24460
|
+
return Categorizer.getUserInputFromProps(this.props);
|
|
24461
|
+
}
|
|
24476
24462
|
onChange(itemNum, catNum) {
|
|
24477
24463
|
const values = [...this.props.values];
|
|
24478
24464
|
// @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'never'.
|
|
@@ -24480,6 +24466,9 @@ class Categorizer extends React.Component {
|
|
|
24480
24466
|
this.change("values", values);
|
|
24481
24467
|
this.props.trackInteraction();
|
|
24482
24468
|
}
|
|
24469
|
+
simpleValidate(rubric) {
|
|
24470
|
+
return categorizerValidator(this.getUserInput(), rubric, this.context.strings);
|
|
24471
|
+
}
|
|
24483
24472
|
render() {
|
|
24484
24473
|
const self = this;
|
|
24485
24474
|
|
|
@@ -24846,16 +24835,16 @@ var CSProgram$1 = {
|
|
|
24846
24835
|
};
|
|
24847
24836
|
|
|
24848
24837
|
class Definition extends React.Component {
|
|
24849
|
-
|
|
24850
|
-
|
|
24851
|
-
|
|
24852
|
-
return {};
|
|
24853
|
-
};
|
|
24854
|
-
this.simpleValidate = rubric => {
|
|
24855
|
-
return Definition.validate(this.getUserInput(), rubric);
|
|
24856
|
-
};
|
|
24838
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
24839
|
+
static validate() {
|
|
24840
|
+
return noopValidator();
|
|
24857
24841
|
}
|
|
24858
|
-
|
|
24842
|
+
getUserInput() {
|
|
24843
|
+
return {};
|
|
24844
|
+
}
|
|
24845
|
+
|
|
24846
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
24847
|
+
simpleValidate() {
|
|
24859
24848
|
return noopValidator();
|
|
24860
24849
|
}
|
|
24861
24850
|
render() {
|
|
@@ -24924,18 +24913,13 @@ class DeprecatedStandin extends React__default.Component {
|
|
|
24924
24913
|
return {};
|
|
24925
24914
|
};
|
|
24926
24915
|
this.simpleValidate = rubric => {
|
|
24927
|
-
return
|
|
24916
|
+
return noopValidator(1);
|
|
24928
24917
|
};
|
|
24929
24918
|
}
|
|
24930
24919
|
static validate(userInput, rubric) {
|
|
24931
24920
|
// Since this mean to replace an existing widget the learner
|
|
24932
24921
|
// WILL earn points for this widget
|
|
24933
|
-
return
|
|
24934
|
-
type: "points",
|
|
24935
|
-
earned: 1,
|
|
24936
|
-
total: 1,
|
|
24937
|
-
message: null
|
|
24938
|
-
};
|
|
24922
|
+
return noopValidator(1);
|
|
24939
24923
|
}
|
|
24940
24924
|
render() {
|
|
24941
24925
|
return /*#__PURE__*/React__default.createElement("div", {
|
|
@@ -24995,18 +24979,18 @@ class Dropdown extends React.Component {
|
|
|
24995
24979
|
selected: selected
|
|
24996
24980
|
});
|
|
24997
24981
|
};
|
|
24998
|
-
this.getUserInput = () => {
|
|
24999
|
-
return {
|
|
25000
|
-
value: this.props.selected
|
|
25001
|
-
};
|
|
25002
|
-
};
|
|
25003
|
-
this.simpleValidate = rubric => {
|
|
25004
|
-
return dropdownValidator(this.getUserInput(), rubric);
|
|
25005
|
-
};
|
|
25006
24982
|
}
|
|
25007
24983
|
static validate(userInput, rubric) {
|
|
25008
24984
|
return dropdownValidator(userInput, rubric);
|
|
25009
24985
|
}
|
|
24986
|
+
getUserInput() {
|
|
24987
|
+
return {
|
|
24988
|
+
value: this.props.selected
|
|
24989
|
+
};
|
|
24990
|
+
}
|
|
24991
|
+
simpleValidate(rubric) {
|
|
24992
|
+
return dropdownValidator(this.getUserInput(), rubric);
|
|
24993
|
+
}
|
|
25010
24994
|
render() {
|
|
25011
24995
|
const children = [/*#__PURE__*/React.createElement(OptionItem, {
|
|
25012
24996
|
key: "placeholder",
|
|
@@ -25080,14 +25064,9 @@ class Explanation extends React.Component {
|
|
|
25080
25064
|
});
|
|
25081
25065
|
this.props.trackInteraction();
|
|
25082
25066
|
};
|
|
25083
|
-
this.getUserInput = () => {
|
|
25084
|
-
return {};
|
|
25085
|
-
};
|
|
25086
|
-
this.simpleValidate = rubric => {
|
|
25087
|
-
return Explanation.validate(this.getUserInput(), rubric);
|
|
25088
|
-
};
|
|
25089
25067
|
}
|
|
25090
|
-
|
|
25068
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
25069
|
+
static validate() {
|
|
25091
25070
|
return noopValidator();
|
|
25092
25071
|
}
|
|
25093
25072
|
componentDidMount() {
|
|
@@ -25096,6 +25075,14 @@ class Explanation extends React.Component {
|
|
|
25096
25075
|
componentWillUnmount() {
|
|
25097
25076
|
this._mounted = false;
|
|
25098
25077
|
}
|
|
25078
|
+
getUserInput() {
|
|
25079
|
+
return {};
|
|
25080
|
+
}
|
|
25081
|
+
|
|
25082
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
25083
|
+
simpleValidate() {
|
|
25084
|
+
return noopValidator();
|
|
25085
|
+
}
|
|
25099
25086
|
render() {
|
|
25100
25087
|
const promptText = this.state.expanded ? this.props.hidePrompt : this.props.showPrompt;
|
|
25101
25088
|
const caretIcon = this.state.expanded ? caretUp : caretDown;
|
|
@@ -26715,7 +26702,7 @@ FunctionGrapher.defaultProps = {
|
|
|
26715
26702
|
coords: null,
|
|
26716
26703
|
asymptote: null,
|
|
26717
26704
|
isMobile: false
|
|
26718
|
-
};
|
|
26705
|
+
};
|
|
26719
26706
|
/* Widget and editor. */
|
|
26720
26707
|
class Grapher extends React.Component {
|
|
26721
26708
|
constructor(...args) {
|
|
@@ -26931,49 +26918,41 @@ class Group extends React.Component {
|
|
|
26931
26918
|
this.change = (...args) => {
|
|
26932
26919
|
return change.apply(this, args);
|
|
26933
26920
|
};
|
|
26934
|
-
this.getUserInput = () => {
|
|
26935
|
-
var _this$rendererRef;
|
|
26936
|
-
return (_this$rendererRef = this.rendererRef) == null ? void 0 : _this$rendererRef.getUserInput();
|
|
26937
|
-
};
|
|
26938
26921
|
this.getSerializedState = () => {
|
|
26939
|
-
var _this$
|
|
26940
|
-
return (_this$
|
|
26922
|
+
var _this$rendererRef;
|
|
26923
|
+
return (_this$rendererRef = this.rendererRef) == null ? void 0 : _this$rendererRef.getSerializedState();
|
|
26941
26924
|
};
|
|
26942
26925
|
this.restoreSerializedState = (state, callback) => {
|
|
26943
|
-
var _this$
|
|
26944
|
-
(_this$
|
|
26926
|
+
var _this$rendererRef2;
|
|
26927
|
+
(_this$rendererRef2 = this.rendererRef) == null || _this$rendererRef2.restoreSerializedState(state, callback);
|
|
26945
26928
|
// Tell our renderer that we have no props to change
|
|
26946
26929
|
// (all our changes were in state):
|
|
26947
26930
|
return null;
|
|
26948
26931
|
};
|
|
26949
|
-
this.simpleValidate = rubric => {
|
|
26950
|
-
var _this$rendererRef4;
|
|
26951
|
-
return (_this$rendererRef4 = this.rendererRef) == null ? void 0 : _this$rendererRef4.score();
|
|
26952
|
-
};
|
|
26953
26932
|
// Mobile API:
|
|
26954
26933
|
this.getInputPaths = () => {
|
|
26955
|
-
var _this$
|
|
26956
|
-
return (_this$
|
|
26934
|
+
var _this$rendererRef3;
|
|
26935
|
+
return (_this$rendererRef3 = this.rendererRef) == null ? void 0 : _this$rendererRef3.getInputPaths();
|
|
26957
26936
|
};
|
|
26958
26937
|
this.setInputValue = (path, newValue, callback) => {
|
|
26959
|
-
var _this$
|
|
26960
|
-
return (_this$
|
|
26938
|
+
var _this$rendererRef4;
|
|
26939
|
+
return (_this$rendererRef4 = this.rendererRef) == null ? void 0 : _this$rendererRef4.setInputValue(path, newValue, callback);
|
|
26961
26940
|
};
|
|
26962
26941
|
this.focus = () => {
|
|
26963
|
-
var _this$
|
|
26964
|
-
return (_this$
|
|
26942
|
+
var _this$rendererRef5;
|
|
26943
|
+
return (_this$rendererRef5 = this.rendererRef) == null ? void 0 : _this$rendererRef5.focus();
|
|
26965
26944
|
};
|
|
26966
26945
|
this.focusInputPath = path => {
|
|
26967
|
-
var _this$
|
|
26968
|
-
(_this$
|
|
26946
|
+
var _this$rendererRef6;
|
|
26947
|
+
(_this$rendererRef6 = this.rendererRef) == null || _this$rendererRef6.focusPath(path);
|
|
26969
26948
|
};
|
|
26970
26949
|
this.blurInputPath = path => {
|
|
26971
|
-
var _this$
|
|
26972
|
-
(_this$
|
|
26950
|
+
var _this$rendererRef7;
|
|
26951
|
+
(_this$rendererRef7 = this.rendererRef) == null || _this$rendererRef7.blurPath(path);
|
|
26973
26952
|
};
|
|
26974
26953
|
this.showRationalesForCurrentlySelectedChoices = () => {
|
|
26975
|
-
var _this$
|
|
26976
|
-
(_this$
|
|
26954
|
+
var _this$rendererRef8;
|
|
26955
|
+
(_this$rendererRef8 = this.rendererRef) == null || _this$rendererRef8.showRationalesForCurrentlySelectedChoices();
|
|
26977
26956
|
};
|
|
26978
26957
|
}
|
|
26979
26958
|
componentDidMount() {
|
|
@@ -26982,6 +26961,14 @@ class Group extends React.Component {
|
|
|
26982
26961
|
// the group with the correct number.
|
|
26983
26962
|
this.forceUpdate();
|
|
26984
26963
|
}
|
|
26964
|
+
getUserInput() {
|
|
26965
|
+
var _this$rendererRef9;
|
|
26966
|
+
return (_this$rendererRef9 = this.rendererRef) == null ? void 0 : _this$rendererRef9.getUserInput();
|
|
26967
|
+
}
|
|
26968
|
+
simpleValidate() {
|
|
26969
|
+
var _this$rendererRef10;
|
|
26970
|
+
return (_this$rendererRef10 = this.rendererRef) == null ? void 0 : _this$rendererRef10.score();
|
|
26971
|
+
}
|
|
26985
26972
|
render() {
|
|
26986
26973
|
var _this$rendererRef11;
|
|
26987
26974
|
const apiOptions = _extends({}, ApiOptions.defaults, this.props.apiOptions, {
|
|
@@ -27249,15 +27236,18 @@ class ImageWidget extends React.Component {
|
|
|
27249
27236
|
this.change = (...args) => {
|
|
27250
27237
|
return change.apply(this, args);
|
|
27251
27238
|
};
|
|
27252
|
-
this.getUserInput = () => {
|
|
27253
|
-
return null;
|
|
27254
|
-
};
|
|
27255
|
-
this.simpleValidate = rubric => {
|
|
27256
|
-
return ImageWidget.validate(this.getUserInput(), rubric);
|
|
27257
|
-
};
|
|
27258
27239
|
this.focus = () => {};
|
|
27259
27240
|
}
|
|
27260
|
-
|
|
27241
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
27242
|
+
static validate() {
|
|
27243
|
+
return noopValidator();
|
|
27244
|
+
}
|
|
27245
|
+
getUserInput() {
|
|
27246
|
+
return null;
|
|
27247
|
+
}
|
|
27248
|
+
|
|
27249
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
27250
|
+
simpleValidate() {
|
|
27261
27251
|
return noopValidator();
|
|
27262
27252
|
}
|
|
27263
27253
|
// no-op
|
|
@@ -27567,18 +27557,9 @@ class Interaction extends React.Component {
|
|
|
27567
27557
|
this.change = (...args) => {
|
|
27568
27558
|
return change.apply(this, args);
|
|
27569
27559
|
};
|
|
27570
|
-
this.getUserInput = () => {
|
|
27571
|
-
// TODO(eater): Perhaps we want to be able to record the state of the
|
|
27572
|
-
// user's interaction. Unfortunately sending all the props will
|
|
27573
|
-
// probably make the attempt payload too large. So for now, don't send
|
|
27574
|
-
// anything.
|
|
27575
|
-
return {};
|
|
27576
|
-
};
|
|
27577
|
-
this.simpleValidate = rubric => {
|
|
27578
|
-
return Interaction.validate(this.getUserInput(), rubric);
|
|
27579
|
-
};
|
|
27580
27560
|
}
|
|
27581
|
-
|
|
27561
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
27562
|
+
static validate() {
|
|
27582
27563
|
return noopValidator();
|
|
27583
27564
|
}
|
|
27584
27565
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
@@ -27599,6 +27580,18 @@ class Interaction extends React.Component {
|
|
|
27599
27580
|
});
|
|
27600
27581
|
}
|
|
27601
27582
|
}
|
|
27583
|
+
getUserInput() {
|
|
27584
|
+
// TODO(eater): Perhaps we want to be able to record the state of the
|
|
27585
|
+
// user's interaction. Unfortunately sending all the props will
|
|
27586
|
+
// probably make the attempt payload too large. So for now, don't send
|
|
27587
|
+
// anything.
|
|
27588
|
+
return {};
|
|
27589
|
+
}
|
|
27590
|
+
|
|
27591
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
27592
|
+
simpleValidate() {
|
|
27593
|
+
return noopValidator();
|
|
27594
|
+
}
|
|
27602
27595
|
render() {
|
|
27603
27596
|
const range = this.props.graph.range;
|
|
27604
27597
|
let labels = this.props.graph.labels;
|
|
@@ -29649,18 +29642,33 @@ function GraphLockedLabelsLayer(props) {
|
|
|
29649
29642
|
lockedFigures
|
|
29650
29643
|
} = props;
|
|
29651
29644
|
return lockedFigures.map((figure, i) => {
|
|
29652
|
-
var _flags$mafs;
|
|
29645
|
+
var _flags$mafs, _flags$mafs2, _flags$mafs3, _flags$mafs4, _flags$mafs5;
|
|
29653
29646
|
if (figure.type === "label") {
|
|
29654
29647
|
return /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
29655
29648
|
key: `label-${i}`
|
|
29656
29649
|
}, figure));
|
|
29657
29650
|
}
|
|
29658
|
-
if (
|
|
29651
|
+
if (
|
|
29652
|
+
// Point flag + point type
|
|
29653
|
+
flags != null && (_flags$mafs = flags["mafs"]) != null && _flags$mafs["locked-point-labels"] && figure.type === "point" ||
|
|
29654
|
+
// Line flag + line type
|
|
29655
|
+
flags != null && (_flags$mafs2 = flags["mafs"]) != null && _flags$mafs2["locked-line-labels"] && figure.type === "line" ||
|
|
29656
|
+
// Vector flag + vector type
|
|
29657
|
+
flags != null && (_flags$mafs3 = flags["mafs"]) != null && _flags$mafs3["locked-vector-labels"] && figure.type === "vector" ||
|
|
29658
|
+
// Ellipse flag + ellipse type
|
|
29659
|
+
flags != null && (_flags$mafs4 = flags["mafs"]) != null && _flags$mafs4["locked-ellipse-labels"] && figure.type === "ellipse" ||
|
|
29660
|
+
// Function flag + function type
|
|
29661
|
+
flags != null && (_flags$mafs5 = flags["mafs"]) != null && _flags$mafs5["locked-function-labels"] && figure.type === "function") {
|
|
29662
|
+
var _figure$labels, _figure$points$0$labe, _figure$points$1$labe;
|
|
29659
29663
|
return /*#__PURE__*/React.createElement(React.Fragment, {
|
|
29660
|
-
key:
|
|
29661
|
-
}, figure.labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
29662
|
-
key:
|
|
29663
|
-
}, label))))
|
|
29664
|
+
key: i
|
|
29665
|
+
}, (_figure$labels = figure.labels) == null ? void 0 : _figure$labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
29666
|
+
key: `${i}-label-${j}`
|
|
29667
|
+
}, label))), figure.type === "line" && /*#__PURE__*/React.createElement(React.Fragment, null, (_figure$points$0$labe = figure.points[0].labels) == null ? void 0 : _figure$points$0$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
29668
|
+
key: `locked-figure-${i}-point-1-label-${k}`
|
|
29669
|
+
}, label))), (_figure$points$1$labe = figure.points[1].labels) == null ? void 0 : _figure$points$1$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
|
|
29670
|
+
key: `locked-figure-${i}-point-2-label-${k}`
|
|
29671
|
+
}, label)))));
|
|
29664
29672
|
}
|
|
29665
29673
|
return null;
|
|
29666
29674
|
});
|
|
@@ -30000,7 +30008,9 @@ const GraphLockedLayer = props => {
|
|
|
30000
30008
|
|
|
30001
30009
|
const actions = {
|
|
30002
30010
|
global: {
|
|
30003
|
-
deleteIntent
|
|
30011
|
+
deleteIntent,
|
|
30012
|
+
changeInteractionMode,
|
|
30013
|
+
changeKeyboardInvitationVisibility
|
|
30004
30014
|
},
|
|
30005
30015
|
angle: {
|
|
30006
30016
|
movePoint
|
|
@@ -30092,6 +30102,20 @@ function clickPoint(index) {
|
|
|
30092
30102
|
index
|
|
30093
30103
|
};
|
|
30094
30104
|
}
|
|
30105
|
+
const CHANGE_INTERACTION_MODE = "point-change-interaction-mode";
|
|
30106
|
+
function changeInteractionMode(mode) {
|
|
30107
|
+
return {
|
|
30108
|
+
type: CHANGE_INTERACTION_MODE,
|
|
30109
|
+
mode
|
|
30110
|
+
};
|
|
30111
|
+
}
|
|
30112
|
+
const CHANGE_KEYBOARD_INVITATION_VISIBILITY = "change-keyboard-interaction-invitation-visibility";
|
|
30113
|
+
function changeKeyboardInvitationVisibility(shouldShow) {
|
|
30114
|
+
return {
|
|
30115
|
+
type: CHANGE_KEYBOARD_INVITATION_VISIBILITY,
|
|
30116
|
+
shouldShow
|
|
30117
|
+
};
|
|
30118
|
+
}
|
|
30095
30119
|
const MOVE_ALL = "move-all";
|
|
30096
30120
|
function moveAll(delta) {
|
|
30097
30121
|
return {
|
|
@@ -31740,7 +31764,7 @@ function Protractor() {
|
|
|
31740
31764
|
const [[xMin, xMax], [yMin, yMax]] = range;
|
|
31741
31765
|
// Position the protractor in the center of the graph (horizontally), and
|
|
31742
31766
|
// 95% of the way to the bottom of the graph (vertically).
|
|
31743
|
-
const initialCenter = [lerp(xMin, xMax, 0.5), lerp(yMin, yMax, 0.
|
|
31767
|
+
const initialCenter = [lerp(xMin, xMax, 0.5), lerp(yMin, yMax, 0.25)];
|
|
31744
31768
|
const [center, setCenter] = useState(initialCenter);
|
|
31745
31769
|
const [rotationHandleOffset, setRotationHandleOffset] = useState(centerToRotationHandle);
|
|
31746
31770
|
const draggableRef = useRef(null);
|
|
@@ -31902,6 +31926,9 @@ const MafsGraph = props => {
|
|
|
31902
31926
|
x: viewboxX,
|
|
31903
31927
|
y: viewboxY
|
|
31904
31928
|
};
|
|
31929
|
+
const {
|
|
31930
|
+
strings
|
|
31931
|
+
} = usePerseusI18n();
|
|
31905
31932
|
return /*#__PURE__*/React.createElement(GraphConfigContext.Provider, {
|
|
31906
31933
|
value: {
|
|
31907
31934
|
range: state.range,
|
|
@@ -31930,16 +31957,18 @@ const MafsGraph = props => {
|
|
|
31930
31957
|
height
|
|
31931
31958
|
},
|
|
31932
31959
|
onKeyUp: event => {
|
|
31933
|
-
|
|
31934
|
-
var _graphRef$current;
|
|
31935
|
-
dispatch(actions.global.deleteIntent());
|
|
31936
|
-
(_graphRef$current = graphRef.current) == null || _graphRef$current.focus();
|
|
31937
|
-
}
|
|
31960
|
+
handleKeyboardEvent(event, state, dispatch);
|
|
31938
31961
|
},
|
|
31939
31962
|
"aria-label": fullGraphAriaLabel,
|
|
31940
31963
|
"aria-describedby": fullGraphAriaDescription ? descriptionId : undefined,
|
|
31941
31964
|
ref: graphRef,
|
|
31942
|
-
tabIndex: 0
|
|
31965
|
+
tabIndex: 0,
|
|
31966
|
+
onFocus: event => {
|
|
31967
|
+
handleFocusEvent(event, state, dispatch);
|
|
31968
|
+
},
|
|
31969
|
+
onBlur: event => {
|
|
31970
|
+
handleBlurEvent(event, state, dispatch);
|
|
31971
|
+
}
|
|
31943
31972
|
}, fullGraphAriaDescription && /*#__PURE__*/React.createElement(View, {
|
|
31944
31973
|
id: descriptionId,
|
|
31945
31974
|
tabIndex: -1,
|
|
@@ -32001,35 +32030,75 @@ const MafsGraph = props => {
|
|
|
32001
32030
|
}, /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.showProtractor && /*#__PURE__*/React.createElement(Protractor, null), renderGraph({
|
|
32002
32031
|
state,
|
|
32003
32032
|
dispatch
|
|
32004
|
-
})))))
|
|
32033
|
+
}))))), state.type === "point" && state.showKeyboardInteractionInvitation && /*#__PURE__*/React.createElement(View, {
|
|
32034
|
+
style: {
|
|
32035
|
+
textAlign: "center",
|
|
32036
|
+
backgroundColor: "white",
|
|
32037
|
+
border: "1px solid #21242C52",
|
|
32038
|
+
padding: "16px 0",
|
|
32039
|
+
boxShadow: "0px 8px 8px 0px #21242C14",
|
|
32040
|
+
// This translates the box to the center of the
|
|
32041
|
+
// graph Then backs it off by half of its
|
|
32042
|
+
// overall height so it's perfectly centered
|
|
32043
|
+
top: "50%",
|
|
32044
|
+
transform: "translateY(-50%)"
|
|
32045
|
+
}
|
|
32046
|
+
}, /*#__PURE__*/React.createElement(LabelMedium, null, strings.graphKeyboardPrompt))), renderGraphControls({
|
|
32005
32047
|
state,
|
|
32006
32048
|
dispatch,
|
|
32007
|
-
width
|
|
32049
|
+
width,
|
|
32050
|
+
strings
|
|
32008
32051
|
})));
|
|
32009
32052
|
};
|
|
32010
|
-
const renderPointGraphControls = props =>
|
|
32011
|
-
|
|
32012
|
-
|
|
32013
|
-
|
|
32014
|
-
|
|
32015
|
-
}
|
|
32016
|
-
|
|
32017
|
-
|
|
32018
|
-
|
|
32019
|
-
|
|
32020
|
-
|
|
32021
|
-
|
|
32022
|
-
|
|
32023
|
-
|
|
32024
|
-
|
|
32025
|
-
|
|
32026
|
-
|
|
32027
|
-
|
|
32053
|
+
const renderPointGraphControls = props => {
|
|
32054
|
+
const {
|
|
32055
|
+
interactionMode,
|
|
32056
|
+
showRemovePointButton,
|
|
32057
|
+
focusedPointIndex
|
|
32058
|
+
} = props.state;
|
|
32059
|
+
const {
|
|
32060
|
+
strings
|
|
32061
|
+
} = props;
|
|
32062
|
+
const shouldShowRemoveButton = showRemovePointButton && focusedPointIndex !== null;
|
|
32063
|
+
return /*#__PURE__*/React.createElement(View, {
|
|
32064
|
+
style: {
|
|
32065
|
+
flexDirection: "row",
|
|
32066
|
+
width: props.width
|
|
32067
|
+
}
|
|
32068
|
+
}, interactionMode === "keyboard" && /*#__PURE__*/React.createElement(Button, {
|
|
32069
|
+
kind: "secondary",
|
|
32070
|
+
style: {
|
|
32071
|
+
width: "100%",
|
|
32072
|
+
marginLeft: "20px"
|
|
32073
|
+
},
|
|
32074
|
+
tabIndex: 0,
|
|
32075
|
+
onClick: () => {
|
|
32076
|
+
props.dispatch(actions.pointGraph.addPoint([0, 0]));
|
|
32077
|
+
}
|
|
32078
|
+
}, strings.addPoint), interactionMode === "mouse" && /*#__PURE__*/React.createElement(Button, {
|
|
32079
|
+
id: REMOVE_BUTTON_ID,
|
|
32080
|
+
kind: "secondary",
|
|
32081
|
+
color: "destructive"
|
|
32082
|
+
// This button is meant to be interacted with by the mouse only
|
|
32083
|
+
// Never allow learners to tab to this button
|
|
32084
|
+
,
|
|
32085
|
+
tabIndex: -1,
|
|
32086
|
+
style: {
|
|
32087
|
+
width: "100%",
|
|
32088
|
+
marginLeft: "20px",
|
|
32089
|
+
visibility: shouldShowRemoveButton ? "visible" : "hidden"
|
|
32090
|
+
},
|
|
32091
|
+
onClick: event => {
|
|
32092
|
+
props.dispatch(actions.pointGraph.removePoint(props.state.focusedPointIndex));
|
|
32093
|
+
}
|
|
32094
|
+
}, strings.removePoint));
|
|
32095
|
+
};
|
|
32028
32096
|
const renderGraphControls = props => {
|
|
32029
32097
|
const {
|
|
32030
32098
|
state,
|
|
32031
32099
|
dispatch,
|
|
32032
|
-
width
|
|
32100
|
+
width,
|
|
32101
|
+
strings
|
|
32033
32102
|
} = props;
|
|
32034
32103
|
const {
|
|
32035
32104
|
type
|
|
@@ -32040,7 +32109,8 @@ const renderGraphControls = props => {
|
|
|
32040
32109
|
return renderPointGraphControls({
|
|
32041
32110
|
state,
|
|
32042
32111
|
dispatch,
|
|
32043
|
-
width
|
|
32112
|
+
width,
|
|
32113
|
+
strings
|
|
32044
32114
|
});
|
|
32045
32115
|
}
|
|
32046
32116
|
return null;
|
|
@@ -32048,6 +32118,34 @@ const renderGraphControls = props => {
|
|
|
32048
32118
|
return null;
|
|
32049
32119
|
}
|
|
32050
32120
|
};
|
|
32121
|
+
function handleFocusEvent(event, state, dispatch) {
|
|
32122
|
+
if (state.type === "point" && state.numPoints === "unlimited") {
|
|
32123
|
+
if (event.target.classList.contains("mafs-graph") && state.interactionMode === "mouse") {
|
|
32124
|
+
dispatch(actions.global.changeKeyboardInvitationVisibility(true));
|
|
32125
|
+
}
|
|
32126
|
+
}
|
|
32127
|
+
}
|
|
32128
|
+
function handleBlurEvent(event, state, dispatch) {
|
|
32129
|
+
if (state.type === "point" && state.numPoints === "unlimited") {
|
|
32130
|
+
dispatch(actions.global.changeKeyboardInvitationVisibility(false));
|
|
32131
|
+
}
|
|
32132
|
+
}
|
|
32133
|
+
function handleKeyboardEvent(event, state, dispatch) {
|
|
32134
|
+
if (state.type === "point" && state.numPoints === "unlimited") {
|
|
32135
|
+
if (event.key === "Backspace") {
|
|
32136
|
+
dispatch(actions.global.deleteIntent());
|
|
32137
|
+
|
|
32138
|
+
// After removing a point blur
|
|
32139
|
+
// It would be nice if this could focus on the graph but doing so
|
|
32140
|
+
// would trigger the message to prompt a learner to enter keyboard mode
|
|
32141
|
+
document.activeElement.blur();
|
|
32142
|
+
} else if (event.shiftKey && event.key === "Enter") {
|
|
32143
|
+
dispatch(actions.global.changeInteractionMode("keyboard"));
|
|
32144
|
+
} else if (state.interactionMode === "keyboard" && event.key === "a") {
|
|
32145
|
+
dispatch(actions.pointGraph.addPoint([0, 0]));
|
|
32146
|
+
}
|
|
32147
|
+
}
|
|
32148
|
+
}
|
|
32051
32149
|
|
|
32052
32150
|
// Calculate the difference between the min and max values of a range
|
|
32053
32151
|
const getRangeDiff = range => {
|
|
@@ -32212,7 +32310,9 @@ function initializeGraphState(params) {
|
|
|
32212
32310
|
coords: getPointCoords(graph, range, step),
|
|
32213
32311
|
numPoints: graph.numPoints || 0,
|
|
32214
32312
|
focusedPointIndex: null,
|
|
32215
|
-
showRemovePointButton: false
|
|
32313
|
+
showRemovePointButton: false,
|
|
32314
|
+
interactionMode: "mouse",
|
|
32315
|
+
showKeyboardInteractionInvitation: false
|
|
32216
32316
|
});
|
|
32217
32317
|
case "circle":
|
|
32218
32318
|
return _extends({}, shared, {
|
|
@@ -32482,6 +32582,10 @@ function interactiveGraphReducer(state, action) {
|
|
|
32482
32582
|
return doDeleteIntent(state);
|
|
32483
32583
|
case CLICK_POINT:
|
|
32484
32584
|
return doClickPoint(state, action);
|
|
32585
|
+
case CHANGE_INTERACTION_MODE:
|
|
32586
|
+
return doChangeInteractionMode(state, action);
|
|
32587
|
+
case CHANGE_KEYBOARD_INVITATION_VISIBILITY:
|
|
32588
|
+
return doChangeKeyboardInvitationVisibility(state, action);
|
|
32485
32589
|
default:
|
|
32486
32590
|
throw new UnreachableCaseError(action);
|
|
32487
32591
|
}
|
|
@@ -32530,6 +32634,31 @@ function doClickPoint(state, action) {
|
|
|
32530
32634
|
}
|
|
32531
32635
|
return state;
|
|
32532
32636
|
}
|
|
32637
|
+
function doChangeInteractionMode(state, action) {
|
|
32638
|
+
if (state.type !== "point") {
|
|
32639
|
+
return state;
|
|
32640
|
+
}
|
|
32641
|
+
if (state.numPoints === "unlimited") {
|
|
32642
|
+
const nextKeyboardInvitation = action.mode === "keyboard" ? false : state.showKeyboardInteractionInvitation;
|
|
32643
|
+
return _extends({}, state, {
|
|
32644
|
+
interactionMode: action.mode,
|
|
32645
|
+
showKeyboardInteractionInvitation: nextKeyboardInvitation
|
|
32646
|
+
});
|
|
32647
|
+
}
|
|
32648
|
+
return state;
|
|
32649
|
+
}
|
|
32650
|
+
function doChangeKeyboardInvitationVisibility(state, action) {
|
|
32651
|
+
if (state.type !== "point") {
|
|
32652
|
+
return state;
|
|
32653
|
+
}
|
|
32654
|
+
if (state.numPoints === "unlimited") {
|
|
32655
|
+
return _extends({}, state, {
|
|
32656
|
+
showKeyboardInteractionInvitation: action.shouldShow,
|
|
32657
|
+
hasBeenInteractedWith: true
|
|
32658
|
+
});
|
|
32659
|
+
}
|
|
32660
|
+
return state;
|
|
32661
|
+
}
|
|
32533
32662
|
function doMovePointInFigure(state, action) {
|
|
32534
32663
|
switch (state.type) {
|
|
32535
32664
|
case "segment":
|
|
@@ -33266,7 +33395,7 @@ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
33266
33395
|
const snapTo = graph.type === "polygon" ? graph.snapTo : null;
|
|
33267
33396
|
const showAngles = graph.type === "polygon" ? graph.showAngles : null;
|
|
33268
33397
|
const showSides = graph.type === "polygon" ? graph.showSides : null;
|
|
33269
|
-
const startCoords = "startCoords" in graph ? graph.startCoords :
|
|
33398
|
+
const startCoords = "startCoords" in graph ? graph.startCoords : undefined;
|
|
33270
33399
|
const originalPropsRef = useRef(props);
|
|
33271
33400
|
const latestPropsRef = useLatestRef(props);
|
|
33272
33401
|
useEffect(() => {
|
|
@@ -34565,20 +34694,19 @@ class InteractiveGraph extends React.Component {
|
|
|
34565
34694
|
super(...args);
|
|
34566
34695
|
this.legacyGraphRef = /*#__PURE__*/React.createRef();
|
|
34567
34696
|
this.mafsRef = /*#__PURE__*/React.createRef();
|
|
34568
|
-
|
|
34569
|
-
|
|
34570
|
-
|
|
34571
|
-
|
|
34572
|
-
|
|
34573
|
-
|
|
34574
|
-
|
|
34575
|
-
|
|
34576
|
-
|
|
34577
|
-
|
|
34697
|
+
}
|
|
34698
|
+
getUserInput() {
|
|
34699
|
+
var _this$mafsRef$current, _this$legacyGraphRef$;
|
|
34700
|
+
if ((_this$mafsRef$current = this.mafsRef.current) != null && _this$mafsRef$current.getUserInput) {
|
|
34701
|
+
return this.mafsRef.current.getUserInput();
|
|
34702
|
+
}
|
|
34703
|
+
if ((_this$legacyGraphRef$ = this.legacyGraphRef.current) != null && _this$legacyGraphRef$.getUserInput) {
|
|
34704
|
+
return this.legacyGraphRef.current.getUserInput();
|
|
34705
|
+
}
|
|
34706
|
+
throw new PerseusError("Cannot getUserInput from a graph that has never rendered", Errors.NotAllowed);
|
|
34578
34707
|
}
|
|
34579
34708
|
simpleValidate(rubric) {
|
|
34580
|
-
|
|
34581
|
-
return InteractiveGraph.validate((_this$getUserInput = this.getUserInput) == null ? void 0 : _this$getUserInput.call(this), rubric, this);
|
|
34709
|
+
return InteractiveGraph.validate(this.getUserInput(), rubric, this);
|
|
34582
34710
|
}
|
|
34583
34711
|
render() {
|
|
34584
34712
|
var _this$props$apiOption;
|
|
@@ -36256,6 +36384,57 @@ const styles$h = StyleSheet.create({
|
|
|
36256
36384
|
}
|
|
36257
36385
|
});
|
|
36258
36386
|
|
|
36387
|
+
// Question state for marker as result of user selected answers.
|
|
36388
|
+
|
|
36389
|
+
function scoreMarker(marker) {
|
|
36390
|
+
const score = {
|
|
36391
|
+
hasAnswers: false,
|
|
36392
|
+
isCorrect: false
|
|
36393
|
+
};
|
|
36394
|
+
if (marker.selected && marker.selected.length > 0) {
|
|
36395
|
+
score.hasAnswers = true;
|
|
36396
|
+
}
|
|
36397
|
+
if (marker.answers.length > 0) {
|
|
36398
|
+
if (marker.selected && marker.selected.length === marker.answers.length) {
|
|
36399
|
+
// All correct answers are selected by the user.
|
|
36400
|
+
score.isCorrect = marker.selected.every(choice => marker.answers.includes(choice));
|
|
36401
|
+
}
|
|
36402
|
+
} else if (!marker.selected || marker.selected.length === 0) {
|
|
36403
|
+
// Correct as no answers should be selected by the user.
|
|
36404
|
+
score.isCorrect = true;
|
|
36405
|
+
}
|
|
36406
|
+
return score;
|
|
36407
|
+
}
|
|
36408
|
+
function labelImageValidator(state, rubric) {
|
|
36409
|
+
let numAnswered = 0;
|
|
36410
|
+
let numCorrect = 0;
|
|
36411
|
+
for (const marker of state.markers) {
|
|
36412
|
+
const score = scoreMarker(marker);
|
|
36413
|
+
if (score.hasAnswers) {
|
|
36414
|
+
numAnswered++;
|
|
36415
|
+
}
|
|
36416
|
+
if (score.isCorrect) {
|
|
36417
|
+
numCorrect++;
|
|
36418
|
+
}
|
|
36419
|
+
}
|
|
36420
|
+
|
|
36421
|
+
// We expect all question markers to be answered before grading.
|
|
36422
|
+
if (numAnswered !== state.markers.length) {
|
|
36423
|
+
return {
|
|
36424
|
+
type: "invalid",
|
|
36425
|
+
message: null
|
|
36426
|
+
};
|
|
36427
|
+
}
|
|
36428
|
+
return {
|
|
36429
|
+
type: "points",
|
|
36430
|
+
// Markers with no expected answers are graded as correct if user
|
|
36431
|
+
// makes no answer selection.
|
|
36432
|
+
earned: numCorrect === state.markers.length ? 1 : 0,
|
|
36433
|
+
total: 1,
|
|
36434
|
+
message: null
|
|
36435
|
+
};
|
|
36436
|
+
}
|
|
36437
|
+
|
|
36259
36438
|
const BringToFront = {
|
|
36260
36439
|
boxShadow: `0 8px 8px ${color.offBlack64}`,
|
|
36261
36440
|
zIndex: 1000
|
|
@@ -36514,57 +36693,11 @@ const styles$f = StyleSheet.create({
|
|
|
36514
36693
|
* No diagonals allowed.
|
|
36515
36694
|
*/
|
|
36516
36695
|
|
|
36696
|
+
// TODO: should this be using WidgetProps / PerseusLabelImageWidgetOptions?
|
|
36697
|
+
|
|
36517
36698
|
class LabelImage extends React.Component {
|
|
36518
|
-
static gradeMarker(marker) {
|
|
36519
|
-
const score = {
|
|
36520
|
-
hasAnswers: false,
|
|
36521
|
-
isCorrect: false
|
|
36522
|
-
};
|
|
36523
|
-
if (marker.selected && marker.selected.length > 0) {
|
|
36524
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'hasAnswers' because it is a read-only property.
|
|
36525
|
-
score.hasAnswers = true;
|
|
36526
|
-
}
|
|
36527
|
-
if (marker.answers.length > 0) {
|
|
36528
|
-
if (marker.selected && marker.selected.length === marker.answers.length) {
|
|
36529
|
-
// All correct answers are selected by the user.
|
|
36530
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'isCorrect' because it is a read-only property.
|
|
36531
|
-
score.isCorrect = marker.selected.every(choice => marker.answers.includes(choice));
|
|
36532
|
-
}
|
|
36533
|
-
} else if (!marker.selected || marker.selected.length === 0) {
|
|
36534
|
-
// Correct as no answers should be selected by the user.
|
|
36535
|
-
// @ts-expect-error - TS2540 - Cannot assign to 'isCorrect' because it is a read-only property.
|
|
36536
|
-
score.isCorrect = true;
|
|
36537
|
-
}
|
|
36538
|
-
return score;
|
|
36539
|
-
}
|
|
36540
36699
|
static validate(state, rubric) {
|
|
36541
|
-
|
|
36542
|
-
let numCorrect = 0;
|
|
36543
|
-
for (const marker of state.markers) {
|
|
36544
|
-
const score = LabelImage.gradeMarker(marker);
|
|
36545
|
-
if (score.hasAnswers) {
|
|
36546
|
-
numAnswered++;
|
|
36547
|
-
}
|
|
36548
|
-
if (score.isCorrect) {
|
|
36549
|
-
numCorrect++;
|
|
36550
|
-
}
|
|
36551
|
-
}
|
|
36552
|
-
|
|
36553
|
-
// We expect all question markers to be answered before grading.
|
|
36554
|
-
if (numAnswered !== state.markers.length) {
|
|
36555
|
-
return {
|
|
36556
|
-
type: "invalid",
|
|
36557
|
-
message: null
|
|
36558
|
-
};
|
|
36559
|
-
}
|
|
36560
|
-
return {
|
|
36561
|
-
type: "points",
|
|
36562
|
-
// Markers with no expected answers are graded as correct if user
|
|
36563
|
-
// makes no answer selection.
|
|
36564
|
-
earned: numCorrect === state.markers.length ? 1 : 0,
|
|
36565
|
-
total: 1,
|
|
36566
|
-
message: null
|
|
36567
|
-
};
|
|
36700
|
+
return labelImageValidator(state);
|
|
36568
36701
|
}
|
|
36569
36702
|
|
|
36570
36703
|
/**
|
|
@@ -36762,7 +36895,7 @@ class LabelImage extends React.Component {
|
|
|
36762
36895
|
this._mounted = false;
|
|
36763
36896
|
}
|
|
36764
36897
|
simpleValidate(rubric) {
|
|
36765
|
-
return
|
|
36898
|
+
return labelImageValidator(this.getUserInput());
|
|
36766
36899
|
}
|
|
36767
36900
|
getUserInput() {
|
|
36768
36901
|
const {
|
|
@@ -36780,7 +36913,7 @@ class LabelImage extends React.Component {
|
|
|
36780
36913
|
onChange
|
|
36781
36914
|
} = this.props;
|
|
36782
36915
|
const updatedMarkers = markers.map(marker => {
|
|
36783
|
-
const score =
|
|
36916
|
+
const score = scoreMarker(marker);
|
|
36784
36917
|
return _extends({}, marker, {
|
|
36785
36918
|
// Reveal correctness state for markers with answers.
|
|
36786
36919
|
showCorrectness: score.hasAnswers ? score.isCorrect ? "correct" : "incorrect" : undefined
|
|
@@ -36927,7 +37060,7 @@ class LabelImage extends React.Component {
|
|
|
36927
37060
|
bottom: "top"
|
|
36928
37061
|
}[markerPosition];
|
|
36929
37062
|
}
|
|
36930
|
-
const score =
|
|
37063
|
+
const score = scoreMarker(marker);
|
|
36931
37064
|
// Once the question is answered, show markers
|
|
36932
37065
|
// with correct answers, otherwise passthrough
|
|
36933
37066
|
// the correctness state.
|
|
@@ -37126,9 +37259,7 @@ const LabelImageWithDependencies = /*#__PURE__*/React.forwardRef((props, ref) =>
|
|
|
37126
37259
|
// methods and instead adjust Peresus to provide these facilities through
|
|
37127
37260
|
// instance methods on our Renderers.
|
|
37128
37261
|
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
37129
|
-
LabelImageWithDependencies.validate =
|
|
37130
|
-
// @ts-expect-error - TS2339 - Property 'gradeMarker' does not exist on type
|
|
37131
|
-
LabelImageWithDependencies.gradeMarker = LabelImage.gradeMarker;
|
|
37262
|
+
LabelImageWithDependencies.validate = labelImageValidator;
|
|
37132
37263
|
var LabelImage$1 = {
|
|
37133
37264
|
name: "label-image",
|
|
37134
37265
|
displayName: "Label Image",
|
|
@@ -37908,10 +38039,10 @@ class Matcher extends React.Component {
|
|
|
37908
38039
|
// @ts-expect-error - TS2339 - Property 'moveOptionToIndex' does not exist on type 'ReactInstance'.
|
|
37909
38040
|
this.refs.right.moveOptionToIndex(option, index);
|
|
37910
38041
|
};
|
|
37911
|
-
|
|
37912
|
-
|
|
37913
|
-
|
|
37914
|
-
|
|
38042
|
+
}
|
|
38043
|
+
simpleValidate(rubric) {
|
|
38044
|
+
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Matcher'.
|
|
38045
|
+
return Matcher.validate(this.getUserInput(), rubric);
|
|
37915
38046
|
}
|
|
37916
38047
|
render() {
|
|
37917
38048
|
// To minimize layout shift, we display a spinner until our math
|
|
@@ -38275,14 +38406,6 @@ class Matrix extends React.Component {
|
|
|
38275
38406
|
}, cb);
|
|
38276
38407
|
this.props.trackInteraction();
|
|
38277
38408
|
};
|
|
38278
|
-
this.getUserInput = () => {
|
|
38279
|
-
return {
|
|
38280
|
-
answers: this.props.answers
|
|
38281
|
-
};
|
|
38282
|
-
};
|
|
38283
|
-
this.simpleValidate = rubric => {
|
|
38284
|
-
return Matrix.validate(this.getUserInput(), rubric, this.context.strings);
|
|
38285
|
-
};
|
|
38286
38409
|
}
|
|
38287
38410
|
static validate(state, rubric, strings) {
|
|
38288
38411
|
const solution = rubric.answers;
|
|
@@ -38300,7 +38423,9 @@ class Matrix extends React.Component {
|
|
|
38300
38423
|
hasEmptyCell = true;
|
|
38301
38424
|
}
|
|
38302
38425
|
if (!incorrectSize) {
|
|
38303
|
-
const validator = createValidator(
|
|
38426
|
+
const validator = createValidator(
|
|
38427
|
+
// @ts-expect-error - TS2345 - Argument of type 'number' is not assignable to parameter of type 'string'.
|
|
38428
|
+
solution[row][col], {
|
|
38304
38429
|
simplify: true
|
|
38305
38430
|
}, strings);
|
|
38306
38431
|
const result = validator(supplied[row][col]);
|
|
@@ -38339,6 +38464,14 @@ class Matrix extends React.Component {
|
|
|
38339
38464
|
// Used in the `onBlur` and `onFocus` handlers
|
|
38340
38465
|
this.cursorPosition = [0, 0];
|
|
38341
38466
|
}
|
|
38467
|
+
getUserInput() {
|
|
38468
|
+
return {
|
|
38469
|
+
answers: this.props.answers
|
|
38470
|
+
};
|
|
38471
|
+
}
|
|
38472
|
+
simpleValidate(rubric) {
|
|
38473
|
+
return Matrix.validate(this.getUserInput(), rubric, this.context.strings);
|
|
38474
|
+
}
|
|
38342
38475
|
render() {
|
|
38343
38476
|
// Set the input sizes through JS so we can control the size of the
|
|
38344
38477
|
// brackets. (If we set them in CSS we won't know values until the
|
|
@@ -38629,9 +38762,8 @@ const Measurer = createReactClass({
|
|
|
38629
38762
|
getUserInput: function () {
|
|
38630
38763
|
return {};
|
|
38631
38764
|
},
|
|
38632
|
-
simpleValidate: function (
|
|
38633
|
-
|
|
38634
|
-
return Measurer.validate(this.getUserInput(), rubric);
|
|
38765
|
+
simpleValidate: function () {
|
|
38766
|
+
return noopValidator(1);
|
|
38635
38767
|
},
|
|
38636
38768
|
focus: $.noop,
|
|
38637
38769
|
render: function () {
|
|
@@ -38660,14 +38792,7 @@ const Measurer = createReactClass({
|
|
|
38660
38792
|
}
|
|
38661
38793
|
});
|
|
38662
38794
|
_.extend(Measurer, {
|
|
38663
|
-
validate:
|
|
38664
|
-
return {
|
|
38665
|
-
type: "points",
|
|
38666
|
-
earned: 1,
|
|
38667
|
-
total: 1,
|
|
38668
|
-
message: null
|
|
38669
|
-
};
|
|
38670
|
-
}
|
|
38795
|
+
validate: noopValidator(1)
|
|
38671
38796
|
});
|
|
38672
38797
|
const propUpgrades = {
|
|
38673
38798
|
"1": v0props => {
|
|
@@ -39557,25 +39682,17 @@ class Molecule extends React.Component {
|
|
|
39557
39682
|
}
|
|
39558
39683
|
Molecule.contextType = PerseusI18nContext;
|
|
39559
39684
|
class MoleculeWidget extends React.Component {
|
|
39560
|
-
|
|
39561
|
-
|
|
39562
|
-
|
|
39563
|
-
|
|
39564
|
-
|
|
39565
|
-
|
|
39566
|
-
|
|
39567
|
-
|
|
39568
|
-
|
|
39569
|
-
|
|
39570
|
-
|
|
39571
|
-
return [];
|
|
39572
|
-
};
|
|
39573
|
-
this.validate = (state, rubric) => {
|
|
39574
|
-
// TODO(colin): this is here as part of the interface for a component.
|
|
39575
|
-
// Figure out if there is something more appropriate that this should
|
|
39576
|
-
// return.
|
|
39577
|
-
return noopValidator();
|
|
39578
|
-
};
|
|
39685
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
39686
|
+
simpleValidate() {
|
|
39687
|
+
return noopValidator();
|
|
39688
|
+
}
|
|
39689
|
+
getUserInput() {
|
|
39690
|
+
return [];
|
|
39691
|
+
}
|
|
39692
|
+
|
|
39693
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
39694
|
+
validate() {
|
|
39695
|
+
return noopValidator();
|
|
39579
39696
|
}
|
|
39580
39697
|
render() {
|
|
39581
39698
|
return /*#__PURE__*/React.createElement(Molecule, {
|
|
@@ -39720,6 +39837,9 @@ const TickMarks = Graphie.createSimpleClass((graphie, props) => {
|
|
|
39720
39837
|
});
|
|
39721
39838
|
return results;
|
|
39722
39839
|
});
|
|
39840
|
+
|
|
39841
|
+
// TODO: most widgets use some like Widget<Something, PerseusNumberLineWidgetOptions>
|
|
39842
|
+
// should this one?
|
|
39723
39843
|
class NumberLine extends React.Component {
|
|
39724
39844
|
constructor(..._args) {
|
|
39725
39845
|
super(..._args);
|
|
@@ -39999,19 +40119,19 @@ class NumberLine extends React.Component {
|
|
|
39999
40119
|
arrows: "->"
|
|
40000
40120
|
});
|
|
40001
40121
|
};
|
|
40002
|
-
|
|
40003
|
-
|
|
40004
|
-
|
|
40005
|
-
|
|
40006
|
-
|
|
40007
|
-
|
|
40008
|
-
|
|
40009
|
-
};
|
|
40010
|
-
this.simpleValidate = rubric => {
|
|
40011
|
-
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof NumberLine'.
|
|
40012
|
-
return NumberLine.validate(this.getUserInput(), rubric);
|
|
40122
|
+
}
|
|
40123
|
+
getUserInput() {
|
|
40124
|
+
return {
|
|
40125
|
+
numLinePosition: this.props.numLinePosition,
|
|
40126
|
+
rel: this.props.isInequality ? this.props.rel : "eq",
|
|
40127
|
+
numDivisions: this.props.numDivisions,
|
|
40128
|
+
divisionRange: this.props.divisionRange
|
|
40013
40129
|
};
|
|
40014
40130
|
}
|
|
40131
|
+
simpleValidate(rubric) {
|
|
40132
|
+
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof NumberLine'.
|
|
40133
|
+
return NumberLine.validate(this.getUserInput(), rubric);
|
|
40134
|
+
}
|
|
40015
40135
|
render() {
|
|
40016
40136
|
const {
|
|
40017
40137
|
strings
|
|
@@ -40086,6 +40206,8 @@ _.extend(NumberLine, {
|
|
|
40086
40206
|
const correctRel = rubric.correctRel || "eq";
|
|
40087
40207
|
const correctPos = number.equal(state.numLinePosition, rubric.correctX || 0);
|
|
40088
40208
|
const outsideAllowedRange = state.numDivisions > divisionRange[1] || state.numDivisions < divisionRange[0];
|
|
40209
|
+
|
|
40210
|
+
// TODO: I don't think isTickCrtl is a thing anymore
|
|
40089
40211
|
if (state.isTickCrtl && outsideAllowedRange) {
|
|
40090
40212
|
return {
|
|
40091
40213
|
type: "invalid",
|
|
@@ -40342,8 +40464,8 @@ Card.defaultProps = {
|
|
|
40342
40464
|
animating: false,
|
|
40343
40465
|
linterContext: linterContextDefault
|
|
40344
40466
|
};
|
|
40345
|
-
const NORMAL = "normal"
|
|
40346
|
-
|
|
40467
|
+
const NORMAL = "normal";
|
|
40468
|
+
const HORIZONTAL = "horizontal";
|
|
40347
40469
|
class Orderer extends React.Component {
|
|
40348
40470
|
constructor(...args) {
|
|
40349
40471
|
super(...args);
|
|
@@ -40584,17 +40706,6 @@ class Orderer extends React.Component {
|
|
|
40584
40706
|
current: list
|
|
40585
40707
|
});
|
|
40586
40708
|
};
|
|
40587
|
-
this.getUserInput = () => {
|
|
40588
|
-
return {
|
|
40589
|
-
current: _.map(this.props.current, function (v) {
|
|
40590
|
-
return v.content;
|
|
40591
|
-
})
|
|
40592
|
-
};
|
|
40593
|
-
};
|
|
40594
|
-
this.simpleValidate = rubric => {
|
|
40595
|
-
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Orderer'.
|
|
40596
|
-
return Orderer.validate(this.getUserInput(), rubric);
|
|
40597
|
-
};
|
|
40598
40709
|
}
|
|
40599
40710
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
40600
40711
|
if (!_.isEqual(this.props.current, nextProps.current)) {
|
|
@@ -40603,6 +40714,17 @@ class Orderer extends React.Component {
|
|
|
40603
40714
|
});
|
|
40604
40715
|
}
|
|
40605
40716
|
}
|
|
40717
|
+
getUserInput() {
|
|
40718
|
+
return {
|
|
40719
|
+
current: _.map(this.props.current, function (v) {
|
|
40720
|
+
return v.content;
|
|
40721
|
+
})
|
|
40722
|
+
};
|
|
40723
|
+
}
|
|
40724
|
+
simpleValidate(rubric) {
|
|
40725
|
+
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Orderer'.
|
|
40726
|
+
return Orderer.validate(this.getUserInput(), rubric);
|
|
40727
|
+
}
|
|
40606
40728
|
render() {
|
|
40607
40729
|
// This is the card we are currently dragging
|
|
40608
40730
|
const dragging = this.state.dragging && /*#__PURE__*/React.createElement(Card
|
|
@@ -40716,14 +40838,14 @@ Orderer.defaultProps = {
|
|
|
40716
40838
|
linterContext: linterContextDefault
|
|
40717
40839
|
};
|
|
40718
40840
|
_.extend(Orderer, {
|
|
40719
|
-
validate: function (
|
|
40720
|
-
if (
|
|
40841
|
+
validate: function (userInput, rubric) {
|
|
40842
|
+
if (userInput.current.length === 0) {
|
|
40721
40843
|
return {
|
|
40722
40844
|
type: "invalid",
|
|
40723
40845
|
message: null
|
|
40724
40846
|
};
|
|
40725
40847
|
}
|
|
40726
|
-
const correct = _.isEqual(
|
|
40848
|
+
const correct = _.isEqual(userInput.current, _.pluck(rubric.correctOptions, "content"));
|
|
40727
40849
|
return {
|
|
40728
40850
|
type: "points",
|
|
40729
40851
|
earned: correct ? 1 : 0,
|
|
@@ -42539,7 +42661,8 @@ class Passage extends React.Component {
|
|
|
42539
42661
|
});
|
|
42540
42662
|
};
|
|
42541
42663
|
}
|
|
42542
|
-
|
|
42664
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
42665
|
+
static validate() {
|
|
42543
42666
|
return noopValidator();
|
|
42544
42667
|
}
|
|
42545
42668
|
componentDidMount() {
|
|
@@ -42755,8 +42878,10 @@ class Passage extends React.Component {
|
|
|
42755
42878
|
getUserInput() {
|
|
42756
42879
|
return null;
|
|
42757
42880
|
}
|
|
42758
|
-
|
|
42759
|
-
|
|
42881
|
+
|
|
42882
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
42883
|
+
simpleValidate() {
|
|
42884
|
+
return noopValidator();
|
|
42760
42885
|
}
|
|
42761
42886
|
|
|
42762
42887
|
/**
|
|
@@ -42919,19 +43044,21 @@ class PassageRefTarget extends React.Component {
|
|
|
42919
43044
|
constructor(..._args) {
|
|
42920
43045
|
super(..._args);
|
|
42921
43046
|
// TODO passage-ref-target isn't interactive; remove
|
|
42922
|
-
this.getUserInput = () => {
|
|
42923
|
-
return;
|
|
42924
|
-
};
|
|
42925
|
-
// TODO passage-ref-target isn't interactive; remove
|
|
42926
43047
|
this.change = (...args) => {
|
|
42927
43048
|
return change.apply(this, args);
|
|
42928
43049
|
};
|
|
42929
|
-
// TODO passage-ref-target isn't interactive; remove
|
|
42930
|
-
this.simpleValidate = rubric => {
|
|
42931
|
-
return PassageRefTarget.validate(this.getUserInput(), rubric);
|
|
42932
|
-
};
|
|
42933
43050
|
}
|
|
42934
|
-
|
|
43051
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
43052
|
+
static validate() {
|
|
43053
|
+
return noopValidator();
|
|
43054
|
+
}
|
|
43055
|
+
|
|
43056
|
+
// TODO passage-ref-target isn't interactive; remove
|
|
43057
|
+
getUserInput() {
|
|
43058
|
+
return null;
|
|
43059
|
+
}
|
|
43060
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
43061
|
+
simpleValidate() {
|
|
42935
43062
|
return noopValidator();
|
|
42936
43063
|
}
|
|
42937
43064
|
render() {
|
|
@@ -42974,9 +43101,6 @@ var PassageRefTarget$1 = {
|
|
|
42974
43101
|
* This is a PhET simulation widget. It is used for rendering simulations
|
|
42975
43102
|
* from https://phet.colorado.edu/.
|
|
42976
43103
|
*/
|
|
42977
|
-
|
|
42978
|
-
// For returning user input, but currently the PhET widget
|
|
42979
|
-
// does not support accessing user input
|
|
42980
43104
|
// This renders the PhET sim
|
|
42981
43105
|
class PhetSimulation extends React.Component {
|
|
42982
43106
|
constructor(props) {
|
|
@@ -43025,6 +43149,8 @@ class PhetSimulation extends React.Component {
|
|
|
43025
43149
|
await this.updateSimState(this.props.url);
|
|
43026
43150
|
}
|
|
43027
43151
|
}
|
|
43152
|
+
|
|
43153
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
43028
43154
|
getUserInput() {
|
|
43029
43155
|
return null;
|
|
43030
43156
|
}
|
|
@@ -43910,13 +44036,6 @@ class Plotter extends React.Component {
|
|
|
43910
44036
|
});
|
|
43911
44037
|
});
|
|
43912
44038
|
};
|
|
43913
|
-
this.getUserInput = () => {
|
|
43914
|
-
return this.state.values;
|
|
43915
|
-
};
|
|
43916
|
-
this.simpleValidate = rubric => {
|
|
43917
|
-
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Plotter'.
|
|
43918
|
-
return Plotter.validate(this.getUserInput(), rubric);
|
|
43919
|
-
};
|
|
43920
44039
|
}
|
|
43921
44040
|
componentDidMount() {
|
|
43922
44041
|
this._isMounted = true;
|
|
@@ -43941,6 +44060,13 @@ class Plotter extends React.Component {
|
|
|
43941
44060
|
componentWillUnmount() {
|
|
43942
44061
|
this._isMounted = false;
|
|
43943
44062
|
}
|
|
44063
|
+
getUserInput() {
|
|
44064
|
+
return this.state.values;
|
|
44065
|
+
}
|
|
44066
|
+
simpleValidate(rubric) {
|
|
44067
|
+
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Plotter'.
|
|
44068
|
+
return Plotter.validate(this.getUserInput(), rubric);
|
|
44069
|
+
}
|
|
43944
44070
|
render() {
|
|
43945
44071
|
// TODO(kevinb) actually compute the size of the graphie correctly and
|
|
43946
44072
|
// make it that size so we don't have to add extra padding. The value
|
|
@@ -43973,8 +44099,8 @@ Plotter.defaultProps = {
|
|
|
43973
44099
|
labelInterval: 1
|
|
43974
44100
|
};
|
|
43975
44101
|
_.extend(Plotter, {
|
|
43976
|
-
validate: function (
|
|
43977
|
-
if (deepEq(
|
|
44102
|
+
validate: function (userInput, rubric) {
|
|
44103
|
+
if (deepEq(userInput, rubric.starting)) {
|
|
43978
44104
|
return {
|
|
43979
44105
|
type: "invalid",
|
|
43980
44106
|
message: null
|
|
@@ -43982,7 +44108,7 @@ _.extend(Plotter, {
|
|
|
43982
44108
|
}
|
|
43983
44109
|
return {
|
|
43984
44110
|
type: "points",
|
|
43985
|
-
earned: deepEq(
|
|
44111
|
+
earned: deepEq(userInput, rubric.correct) ? 1 : 0,
|
|
43986
44112
|
total: 1,
|
|
43987
44113
|
message: null
|
|
43988
44114
|
};
|
|
@@ -44045,6 +44171,28 @@ var PythonProgram$1 = {
|
|
|
44045
44171
|
widget: PythonProgram
|
|
44046
44172
|
};
|
|
44047
44173
|
|
|
44174
|
+
function sorterValidator(userInput, rubric) {
|
|
44175
|
+
// If the sorter widget hasn't been changed yet, we treat it as "empty" which
|
|
44176
|
+
// prevents the "Check" button from becoming active. We want the user
|
|
44177
|
+
// to make a change before trying to move forward. This makes an
|
|
44178
|
+
// assumption that the initial order isn't the correct order! However,
|
|
44179
|
+
// this should be rare if it happens, and interacting with the list
|
|
44180
|
+
// will enable the button, so they won't be locked out of progressing.
|
|
44181
|
+
if (!userInput.changed) {
|
|
44182
|
+
return {
|
|
44183
|
+
type: "invalid",
|
|
44184
|
+
message: null
|
|
44185
|
+
};
|
|
44186
|
+
}
|
|
44187
|
+
const correct = Util.deepEq(userInput.options, rubric.correct);
|
|
44188
|
+
return {
|
|
44189
|
+
type: "points",
|
|
44190
|
+
earned: correct ? 1 : 0,
|
|
44191
|
+
total: 1,
|
|
44192
|
+
message: null
|
|
44193
|
+
};
|
|
44194
|
+
}
|
|
44195
|
+
|
|
44048
44196
|
const {
|
|
44049
44197
|
shuffle
|
|
44050
44198
|
} = Util;
|
|
@@ -44074,42 +44222,14 @@ class Sorter extends React.Component {
|
|
|
44074
44222
|
}, 0);
|
|
44075
44223
|
});
|
|
44076
44224
|
};
|
|
44077
|
-
this.getUserInput = () => {
|
|
44078
|
-
// eslint-disable-next-line react/no-string-refs
|
|
44079
|
-
// @ts-expect-error - TS2339 - Property 'getOptions' does not exist on type 'ReactInstance'.
|
|
44080
|
-
return {
|
|
44081
|
-
options: this.refs.sortable.getOptions()
|
|
44082
|
-
};
|
|
44083
|
-
};
|
|
44084
44225
|
this.moveOptionToIndex = (option, index) => {
|
|
44085
44226
|
// eslint-disable-next-line react/no-string-refs
|
|
44086
44227
|
// @ts-expect-error - TS2339 - Property 'moveOptionToIndex' does not exist on type 'ReactInstance'.
|
|
44087
44228
|
this.refs.sortable.moveOptionToIndex(option, index);
|
|
44088
44229
|
};
|
|
44089
|
-
this.simpleValidate = rubric => {
|
|
44090
|
-
// If this widget hasn't been changed yet, we treat it as "empty" which
|
|
44091
|
-
// prevents the "Check" button from becoming active. We want the user
|
|
44092
|
-
// to make a change before trying to move forward. This makes an
|
|
44093
|
-
// assumption that the initial order isn't the correct order! However,
|
|
44094
|
-
// this should be rare if it happens, and interacting with the list
|
|
44095
|
-
// will enable the button, so they won't be locked out of progressing.
|
|
44096
|
-
if (!this.state.changed) {
|
|
44097
|
-
return {
|
|
44098
|
-
type: "invalid",
|
|
44099
|
-
message: null
|
|
44100
|
-
};
|
|
44101
|
-
}
|
|
44102
|
-
return Sorter.validate(this.getUserInput(), rubric);
|
|
44103
|
-
};
|
|
44104
44230
|
}
|
|
44105
|
-
static validate(
|
|
44106
|
-
|
|
44107
|
-
return {
|
|
44108
|
-
type: "points",
|
|
44109
|
-
earned: correct ? 1 : 0,
|
|
44110
|
-
total: 1,
|
|
44111
|
-
message: null
|
|
44112
|
-
};
|
|
44231
|
+
static validate(userInput, rubric) {
|
|
44232
|
+
return sorterValidator(userInput, rubric);
|
|
44113
44233
|
}
|
|
44114
44234
|
componentDidMount() {
|
|
44115
44235
|
this._isMounted = true;
|
|
@@ -44117,6 +44237,18 @@ class Sorter extends React.Component {
|
|
|
44117
44237
|
componentWillUnmount() {
|
|
44118
44238
|
this._isMounted = false;
|
|
44119
44239
|
}
|
|
44240
|
+
getUserInput() {
|
|
44241
|
+
// eslint-disable-next-line react/no-string-refs
|
|
44242
|
+
// @ts-expect-error - TS2339 - Property 'getOptions' does not exist on type 'ReactInstance'.
|
|
44243
|
+
const options = this.refs.sortable.getOptions();
|
|
44244
|
+
return {
|
|
44245
|
+
options,
|
|
44246
|
+
changed: this.state.changed
|
|
44247
|
+
};
|
|
44248
|
+
}
|
|
44249
|
+
simpleValidate(rubric) {
|
|
44250
|
+
return sorterValidator(this.getUserInput(), rubric);
|
|
44251
|
+
}
|
|
44120
44252
|
render() {
|
|
44121
44253
|
const options = shuffle(this.props.correct, this.props.problemNum, /* ensurePermuted */true);
|
|
44122
44254
|
const {
|
|
@@ -44189,9 +44321,6 @@ class Table extends React.Component {
|
|
|
44189
44321
|
this._getColumns = () => {
|
|
44190
44322
|
return this.props.answers[0].length;
|
|
44191
44323
|
};
|
|
44192
|
-
this.getUserInput = () => {
|
|
44193
|
-
return _.map(this.props.answers, _.clone);
|
|
44194
|
-
};
|
|
44195
44324
|
this.onValueChange = (row, column, eventOrValue) => {
|
|
44196
44325
|
const answers = _.map(this.props.answers, _.clone);
|
|
44197
44326
|
|
|
@@ -44211,10 +44340,6 @@ class Table extends React.Component {
|
|
|
44211
44340
|
headers: headers
|
|
44212
44341
|
});
|
|
44213
44342
|
};
|
|
44214
|
-
this.simpleValidate = rubric => {
|
|
44215
|
-
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Table'.
|
|
44216
|
-
return Table.validate(this.getUserInput(), rubric, this.context.strings);
|
|
44217
|
-
};
|
|
44218
44343
|
this._handleFocus = inputPath => {
|
|
44219
44344
|
this.props.onFocus(inputPath);
|
|
44220
44345
|
};
|
|
@@ -44281,6 +44406,13 @@ class Table extends React.Component {
|
|
|
44281
44406
|
}, cb);
|
|
44282
44407
|
};
|
|
44283
44408
|
}
|
|
44409
|
+
getUserInput() {
|
|
44410
|
+
return _.map(this.props.answers, _.clone);
|
|
44411
|
+
}
|
|
44412
|
+
simpleValidate(rubric) {
|
|
44413
|
+
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type 'typeof Table'.
|
|
44414
|
+
return Table.validate(this.getUserInput(), rubric, this.context.strings);
|
|
44415
|
+
}
|
|
44284
44416
|
render() {
|
|
44285
44417
|
const rows = this._getRows();
|
|
44286
44418
|
const columns = this._getColumns();
|
|
@@ -44529,9 +44661,6 @@ class Video extends React.Component {
|
|
|
44529
44661
|
this.getUserInput = () => {
|
|
44530
44662
|
return null;
|
|
44531
44663
|
};
|
|
44532
|
-
this.simpleValidate = rubric => {
|
|
44533
|
-
return Video.validate(null, rubric);
|
|
44534
|
-
};
|
|
44535
44664
|
this.change = (...args) => {
|
|
44536
44665
|
// @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
|
|
44537
44666
|
return change.apply(this, args);
|
|
@@ -44541,8 +44670,12 @@ class Video extends React.Component {
|
|
|
44541
44670
|
* This is the widget's grading function.
|
|
44542
44671
|
* Points for videos are tallied by the embedded video itself, in the case
|
|
44543
44672
|
* of Khan Academy videos.
|
|
44544
|
-
*/
|
|
44545
|
-
static validate(
|
|
44673
|
+
*/ // TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
44674
|
+
static validate() {
|
|
44675
|
+
return noopValidator();
|
|
44676
|
+
}
|
|
44677
|
+
// TODO (LEMS-2396): remove validation logic from widgets that don't validate
|
|
44678
|
+
simpleValidate() {
|
|
44546
44679
|
return noopValidator();
|
|
44547
44680
|
}
|
|
44548
44681
|
render() {
|
|
@@ -44648,7 +44781,7 @@ const init = function init(options) {
|
|
|
44648
44781
|
|
|
44649
44782
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44650
44783
|
const libName = "@khanacademy/perseus";
|
|
44651
|
-
const libVersion = "
|
|
44784
|
+
const libVersion = "35.0.0";
|
|
44652
44785
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44653
44786
|
|
|
44654
44787
|
const apiVersion = {
|
|
@@ -45199,9 +45332,6 @@ const defaultContext = {
|
|
|
45199
45332
|
};
|
|
45200
45333
|
const context = /*#__PURE__*/React.createContext(defaultContext);
|
|
45201
45334
|
|
|
45202
|
-
const {
|
|
45203
|
-
mapObject
|
|
45204
|
-
} = objective_;
|
|
45205
45335
|
class ServerItemRenderer extends React.Component {
|
|
45206
45336
|
constructor(props) {
|
|
45207
45337
|
super(props);
|
|
@@ -45421,20 +45551,6 @@ class ServerItemRenderer extends React.Component {
|
|
|
45421
45551
|
return this.questionRenderer.getWidgetIds();
|
|
45422
45552
|
}
|
|
45423
45553
|
|
|
45424
|
-
/**
|
|
45425
|
-
* Returns an object mapping from widget ID to KE-style score.
|
|
45426
|
-
* The keys of this object are the values of the array returned
|
|
45427
|
-
* from `getWidgetIds`.
|
|
45428
|
-
*/
|
|
45429
|
-
scoreWidgets() {
|
|
45430
|
-
const qScore = this.questionRenderer.scoreWidgets();
|
|
45431
|
-
const qGuess = this.questionRenderer.getUserInputForWidgets();
|
|
45432
|
-
const state = this.questionRenderer.getSerializedState();
|
|
45433
|
-
return mapObject(qScore, (score, id) => {
|
|
45434
|
-
return Util.keScoreFromPerseusScore(score, qGuess[id], state);
|
|
45435
|
-
});
|
|
45436
|
-
}
|
|
45437
|
-
|
|
45438
45554
|
/**
|
|
45439
45555
|
* Get a representation of the current state of the item.
|
|
45440
45556
|
*/
|