@khanacademy/perseus 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/es/index.css +15 -8
- package/dist/es/index.js +225 -194
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +15 -8
- package/dist/index.js +229 -196
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/number-input.jsx +2 -2
- package/src/components/simple-keypad-input.jsx +18 -21
- package/src/index.js +1 -1
- package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +356 -65
- package/src/multi-items/__tests__/multi-renderer_test.js +9 -9
- package/src/perseus-types.js +18 -7
- package/src/styles/perseus-renderer.less +8 -2
- package/src/styles/widgets/passage.less +9 -5
- package/src/util/answer-types.js +2 -3
- package/src/util/math.js +3 -10
- package/src/widgets/__stories__/numeric-input.stories.jsx +43 -2
- package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +4 -4
- package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +226 -65
- package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +2581 -679
- package/src/widgets/__tests__/group_test.jsx +4 -4
- package/src/widgets/__tests__/numeric-input_test.jsx +12 -8
- package/src/widgets/__tests__/passage_test.jsx +81 -0
- package/src/widgets/__tests__/radio/base-radio_test.jsx +55 -7
- package/src/widgets/__tests__/radio/choice_test.jsx +164 -63
- package/src/widgets/__tests__/radio_test.jsx +39 -15
- package/src/widgets/definition.jsx +1 -1
- package/src/widgets/group.jsx +84 -102
- package/src/widgets/input-number.jsx +6 -1
- package/src/widgets/numeric-input.jsx +87 -80
- package/src/widgets/radio/base-radio.jsx +3 -1
- package/src/widgets/radio/choice-a11y-text.js +62 -0
- package/src/widgets/radio/choice-icon/choice-icon.jsx +0 -68
- package/src/widgets/radio/choice-icon/library-choice-icon.jsx +5 -10
- package/src/widgets/radio/choice-icon/sat-choice-icon.jsx +3 -12
- package/src/widgets/radio/choice.jsx +46 -3
package/dist/es/index.js
CHANGED
|
@@ -1476,8 +1476,7 @@ var KhanAnswerTypes = {
|
|
|
1476
1476
|
*/
|
|
1477
1477
|
number: {
|
|
1478
1478
|
convertToPredicate: function convertToPredicate(correctAnswer, options) {
|
|
1479
|
-
|
|
1480
|
-
var correctFloat = parseFloat($.trim(correctAnswer));
|
|
1479
|
+
var correctFloat = parseFloat(correctAnswer);
|
|
1481
1480
|
return [function (guess, maxError) {
|
|
1482
1481
|
return Math.abs(guess - correctFloat) < maxError;
|
|
1483
1482
|
}, $.extend({}, options, {
|
|
@@ -22955,35 +22954,36 @@ _defineProperty(PossibleAnswers, "propTypes", {
|
|
|
22955
22954
|
});
|
|
22956
22955
|
|
|
22957
22956
|
var _excluded$3 = ["keypadElement", "onFocus", "value"];
|
|
22958
|
-
|
|
22959
|
-
|
|
22960
|
-
|
|
22961
|
-
|
|
22962
|
-
|
|
22963
|
-
|
|
22964
|
-
|
|
22965
|
-
componentDidMount
|
|
22957
|
+
class SimpleKeypadInput extends React.Component {
|
|
22958
|
+
constructor() {
|
|
22959
|
+
super(...arguments);
|
|
22960
|
+
|
|
22961
|
+
_defineProperty(this, "_isMounted", false);
|
|
22962
|
+
}
|
|
22963
|
+
|
|
22964
|
+
componentDidMount() {
|
|
22966
22965
|
// TODO(scottgrant): This is a hack to remove the deprecated call to
|
|
22967
22966
|
// this.isMounted() but is still considered an anti-pattern.
|
|
22968
22967
|
this._isMounted = true;
|
|
22969
|
-
}
|
|
22970
|
-
|
|
22968
|
+
}
|
|
22969
|
+
|
|
22970
|
+
componentWillUnmount() {
|
|
22971
22971
|
this._isMounted = false;
|
|
22972
|
-
}
|
|
22972
|
+
}
|
|
22973
22973
|
|
|
22974
22974
|
focus() {
|
|
22975
22975
|
// $FlowFixMe[object-this-reference]
|
|
22976
22976
|
this.refs.input.focus(); // eslint-disable-line react/no-string-refs
|
|
22977
|
-
}
|
|
22977
|
+
}
|
|
22978
22978
|
|
|
22979
22979
|
blur() {
|
|
22980
22980
|
// $FlowFixMe[object-this-reference]
|
|
22981
22981
|
this.refs.input.blur(); // eslint-disable-line react/no-string-refs
|
|
22982
|
-
}
|
|
22982
|
+
}
|
|
22983
22983
|
|
|
22984
|
-
getValue
|
|
22984
|
+
getValue() {
|
|
22985
22985
|
return this.props.value;
|
|
22986
|
-
}
|
|
22986
|
+
}
|
|
22987
22987
|
|
|
22988
22988
|
render() {
|
|
22989
22989
|
// $FlowFixMe[object-this-reference]
|
|
@@ -23022,7 +23022,12 @@ var SimpleKeypadInput = createReactClass({
|
|
|
23022
23022
|
}, rest));
|
|
23023
23023
|
}
|
|
23024
23024
|
|
|
23025
|
-
}
|
|
23025
|
+
}
|
|
23026
|
+
SimpleKeypadInput.propTypes = {
|
|
23027
|
+
keypadElement: keypadElementPropType,
|
|
23028
|
+
onFocus: PropTypes.func,
|
|
23029
|
+
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
|
|
23030
|
+
};
|
|
23026
23031
|
|
|
23027
23032
|
var ParseTex$1 = TexWrangler.parseTex;
|
|
23028
23033
|
var answerTypes = {
|
|
@@ -23277,9 +23282,13 @@ class InputNumber extends React.Component {
|
|
|
23277
23282
|
|
|
23278
23283
|
if (rubric.answerType == null) {
|
|
23279
23284
|
rubric.answerType = "number";
|
|
23280
|
-
}
|
|
23285
|
+
} // note(matthewc): this will get immediately parsed again by
|
|
23286
|
+
// `KhanAnswerTypes.number.convertToPredicate`, but a string is
|
|
23287
|
+
// expected here
|
|
23288
|
+
|
|
23281
23289
|
|
|
23282
|
-
var
|
|
23290
|
+
var stringValue = "".concat(rubric.value);
|
|
23291
|
+
var val = KhanAnswerTypes.number.createValidatorFunctional(stringValue, {
|
|
23283
23292
|
simplify: rubric.simplify,
|
|
23284
23293
|
inexact: rubric.inexact || undefined,
|
|
23285
23294
|
maxError: rubric.maxError,
|
|
@@ -23405,6 +23414,8 @@ class NumericInput extends React.Component {
|
|
|
23405
23414
|
constructor() {
|
|
23406
23415
|
super(...arguments);
|
|
23407
23416
|
|
|
23417
|
+
_defineProperty(this, "inputRef", void 0);
|
|
23418
|
+
|
|
23408
23419
|
_defineProperty(this, "state", {
|
|
23409
23420
|
// keeps track of the other set of values when switching
|
|
23410
23421
|
// between 0 and finite solutions
|
|
@@ -23423,9 +23434,8 @@ class NumericInput extends React.Component {
|
|
|
23423
23434
|
correct = score.type === "points" && score.earned === score.total;
|
|
23424
23435
|
|
|
23425
23436
|
if (!correct) {
|
|
23426
|
-
var correctAnswers =
|
|
23427
|
-
|
|
23428
|
-
var answerStrings = _.map(correctAnswers, answer => {
|
|
23437
|
+
var correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
|
|
23438
|
+
var answerStrings = correctAnswers.map(answer => {
|
|
23429
23439
|
// Figure out how this answer is supposed to be
|
|
23430
23440
|
// displayed
|
|
23431
23441
|
var format = "decimal";
|
|
@@ -23446,7 +23456,6 @@ class NumericInput extends React.Component {
|
|
|
23446
23456
|
|
|
23447
23457
|
return answerString;
|
|
23448
23458
|
});
|
|
23449
|
-
|
|
23450
23459
|
answerBlurb = /*#__PURE__*/React.createElement(PossibleAnswers, {
|
|
23451
23460
|
answers: answerStrings
|
|
23452
23461
|
});
|
|
@@ -23460,7 +23469,7 @@ class NumericInput extends React.Component {
|
|
|
23460
23469
|
var _this$props$answerFor;
|
|
23461
23470
|
|
|
23462
23471
|
// if the set of specified forms are empty, allow all forms
|
|
23463
|
-
var forms = ((_this$props$answerFor = this.props.answerForms) === null || _this$props$answerFor === void 0 ? void 0 : _this$props$answerFor.length) !== 0 ? this.props.answerForms :
|
|
23472
|
+
var forms = ((_this$props$answerFor = this.props.answerForms) === null || _this$props$answerFor === void 0 ? void 0 : _this$props$answerFor.length) !== 0 ? this.props.answerForms : Object.keys(formExamples).map(name => {
|
|
23464
23473
|
return {
|
|
23465
23474
|
name: name,
|
|
23466
23475
|
simplify: "required"
|
|
@@ -23486,8 +23495,7 @@ class NumericInput extends React.Component {
|
|
|
23486
23495
|
|
|
23487
23496
|
var answerFormNames = _.uniq((_this$props$answerFor3 = this.props.answerForms) === null || _this$props$answerFor3 === void 0 ? void 0 : _this$props$answerFor3.map(form => form.name));
|
|
23488
23497
|
|
|
23489
|
-
var allFormsAccepted = answerFormNames.length >=
|
|
23490
|
-
|
|
23498
|
+
var allFormsAccepted = answerFormNames.length >= Object.keys(formExamples).length;
|
|
23491
23499
|
return !noFormsAccepted && !allFormsAccepted;
|
|
23492
23500
|
});
|
|
23493
23501
|
|
|
@@ -23496,20 +23504,22 @@ class NumericInput extends React.Component {
|
|
|
23496
23504
|
});
|
|
23497
23505
|
|
|
23498
23506
|
_defineProperty(this, "focus", () => {
|
|
23499
|
-
|
|
23500
|
-
|
|
23507
|
+
var _this$inputRef;
|
|
23508
|
+
|
|
23509
|
+
(_this$inputRef = this.inputRef) === null || _this$inputRef === void 0 ? void 0 : _this$inputRef.focus();
|
|
23501
23510
|
return true;
|
|
23502
23511
|
});
|
|
23503
23512
|
|
|
23504
|
-
_defineProperty(this, "focusInputPath",
|
|
23505
|
-
|
|
23506
|
-
|
|
23507
|
-
this.
|
|
23513
|
+
_defineProperty(this, "focusInputPath", () => {
|
|
23514
|
+
var _this$inputRef2;
|
|
23515
|
+
|
|
23516
|
+
(_this$inputRef2 = this.inputRef) === null || _this$inputRef2 === void 0 ? void 0 : _this$inputRef2.focus();
|
|
23508
23517
|
});
|
|
23509
23518
|
|
|
23510
|
-
_defineProperty(this, "blurInputPath",
|
|
23511
|
-
|
|
23512
|
-
|
|
23519
|
+
_defineProperty(this, "blurInputPath", () => {
|
|
23520
|
+
var _this$inputRef3;
|
|
23521
|
+
|
|
23522
|
+
(_this$inputRef3 = this.inputRef) === null || _this$inputRef3 === void 0 ? void 0 : _this$inputRef3.blur();
|
|
23513
23523
|
});
|
|
23514
23524
|
|
|
23515
23525
|
_defineProperty(this, "getInputPaths", () => {
|
|
@@ -23577,9 +23587,8 @@ class NumericInput extends React.Component {
|
|
|
23577
23587
|
}
|
|
23578
23588
|
|
|
23579
23589
|
static getOneCorrectAnswerFromRubric(rubric) {
|
|
23580
|
-
var correctAnswers =
|
|
23581
|
-
|
|
23582
|
-
var answerStrings = _.map(correctAnswers, answer => {
|
|
23590
|
+
var correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
|
|
23591
|
+
var answerStrings = correctAnswers.map(answer => {
|
|
23583
23592
|
// Figure out how this answer is supposed to be
|
|
23584
23593
|
// displayed
|
|
23585
23594
|
var format = "decimal";
|
|
@@ -23609,31 +23618,30 @@ class NumericInput extends React.Component {
|
|
|
23609
23618
|
}
|
|
23610
23619
|
|
|
23611
23620
|
static validate(useInput, rubric) {
|
|
23612
|
-
var allAnswerForms =
|
|
23613
|
-
|
|
23614
|
-
var createValidator = answer =>
|
|
23615
|
-
|
|
23616
|
-
|
|
23617
|
-
|
|
23618
|
-
|
|
23619
|
-
|
|
23620
|
-
|
|
23621
|
-
|
|
23621
|
+
var allAnswerForms = answerFormButtons.map(e => e["value"]);
|
|
23622
|
+
|
|
23623
|
+
var createValidator = answer => {
|
|
23624
|
+
var stringAnswer = "".concat(answer.value);
|
|
23625
|
+
return KhanAnswerTypes.number.createValidatorFunctional(stringAnswer, {
|
|
23626
|
+
message: answer.message,
|
|
23627
|
+
simplify: answer.status === "correct" ? answer.simplify : "optional",
|
|
23628
|
+
inexact: true,
|
|
23629
|
+
// TODO(merlob) backfill / delete
|
|
23630
|
+
maxError: answer.maxError,
|
|
23631
|
+
forms: answer.strict && answer.answerForms && answer.answerForms.length !== 0 ? answer.answerForms : allAnswerForms
|
|
23632
|
+
});
|
|
23633
|
+
}; // We may have received TeX; try to parse it before grading.
|
|
23622
23634
|
// If `currentValue` is not TeX, this should be a no-op.
|
|
23623
23635
|
|
|
23624
23636
|
|
|
23625
23637
|
var currentValue = ParseTex(useInput.currentValue);
|
|
23626
|
-
|
|
23627
|
-
var correctAnswers = _.where(rubric.answers, {
|
|
23628
|
-
status: "correct"
|
|
23629
|
-
});
|
|
23630
|
-
|
|
23638
|
+
var correctAnswers = rubric.answers.filter(answer => answer.status === "correct");
|
|
23631
23639
|
var normalizedAnswerExpected = correctAnswers.every(answer => Math.abs(answer.value) <= 1); // Look through all correct answers for one that matches either
|
|
23632
23640
|
// precisely or approximately and return the appropriate message:
|
|
23633
23641
|
// - if precise, return the message that the answer came with
|
|
23634
23642
|
// - if it needs to be simplified, etc., show that message
|
|
23635
23643
|
|
|
23636
|
-
var result =
|
|
23644
|
+
var result = correctAnswers.map(answer => {
|
|
23637
23645
|
// The coefficient is an attribute of the widget
|
|
23638
23646
|
var localValue = currentValue;
|
|
23639
23647
|
|
|
@@ -23647,22 +23655,17 @@ class NumericInput extends React.Component {
|
|
|
23647
23655
|
|
|
23648
23656
|
var validate = createValidator(answer);
|
|
23649
23657
|
return validate(maybeParsePercentInput(localValue, normalizedAnswerExpected));
|
|
23650
|
-
})
|
|
23658
|
+
}).find(match => match.correct || match.empty);
|
|
23651
23659
|
|
|
23652
23660
|
if (!result) {
|
|
23653
23661
|
// Otherwise, if the guess is not correct
|
|
23654
|
-
var otherAnswers = [].concat(
|
|
23655
|
-
status: "ungraded"
|
|
23656
|
-
}), _.where(rubric.answers, {
|
|
23657
|
-
status: "wrong"
|
|
23658
|
-
})); // Look through all other answers and if one matches either
|
|
23662
|
+
var otherAnswers = [].concat(rubric.answers.filter(answer => answer.status === "ungraded"), rubric.answers.filter(answer => answer.status === "wrong")); // Look through all other answers and if one matches either
|
|
23659
23663
|
// precisely or approximately return the answer's message
|
|
23660
23664
|
|
|
23661
|
-
var match =
|
|
23665
|
+
var match = otherAnswers.find(answer => {
|
|
23662
23666
|
var validate = createValidator(answer);
|
|
23663
23667
|
return validate(maybeParsePercentInput(currentValue, normalizedAnswerExpected)).correct;
|
|
23664
23668
|
});
|
|
23665
|
-
|
|
23666
23669
|
result = {
|
|
23667
23670
|
empty: match ? match.status === "ungraded" : false,
|
|
23668
23671
|
correct: match ? match.status === "correct" : false,
|
|
@@ -23709,9 +23712,8 @@ class NumericInput extends React.Component {
|
|
|
23709
23712
|
|
|
23710
23713
|
if (this.props.apiOptions.customKeypad) {
|
|
23711
23714
|
// TODO(charlie): Support "Review Mode".
|
|
23712
|
-
return maybeRightAlignKeypadInput( /*#__PURE__*/React.createElement(SimpleKeypadInput
|
|
23713
|
-
|
|
23714
|
-
ref: "input",
|
|
23715
|
+
return maybeRightAlignKeypadInput( /*#__PURE__*/React.createElement(SimpleKeypadInput, {
|
|
23716
|
+
ref: _ref => this.inputRef = _ref,
|
|
23715
23717
|
value: this.props.currentValue,
|
|
23716
23718
|
keypadElement: this.props.keypadElement,
|
|
23717
23719
|
onChange: this.handleChange,
|
|
@@ -23720,9 +23722,8 @@ class NumericInput extends React.Component {
|
|
|
23720
23722
|
}));
|
|
23721
23723
|
}
|
|
23722
23724
|
|
|
23723
|
-
var input = /*#__PURE__*/React.createElement(InputWithExamples
|
|
23724
|
-
|
|
23725
|
-
ref: "input",
|
|
23725
|
+
var input = /*#__PURE__*/React.createElement(InputWithExamples, {
|
|
23726
|
+
ref: _ref2 => this.inputRef = _ref2,
|
|
23726
23727
|
value: this.props.currentValue,
|
|
23727
23728
|
onChange: this.handleChange,
|
|
23728
23729
|
className: classNames(classes),
|
|
@@ -23789,7 +23790,7 @@ var unionAnswerForms = function unionAnswerForms(answerFormsList) {
|
|
|
23789
23790
|
// two elements are equal, and returns a list of unique elements. This is
|
|
23790
23791
|
// just a helper function here, but works generally.
|
|
23791
23792
|
var uniqueBy = function uniqueBy(list, iteratee) {
|
|
23792
|
-
return
|
|
23793
|
+
return list.reduce((uniqueList, element) => {
|
|
23793
23794
|
// For each element, decide whether it's already in the list of
|
|
23794
23795
|
// unique items.
|
|
23795
23796
|
var inList = _.find(uniqueList, iteratee.bind(null, element));
|
|
@@ -23803,13 +23804,13 @@ var unionAnswerForms = function unionAnswerForms(answerFormsList) {
|
|
|
23803
23804
|
}; // Pull out all of the forms from the different lists.
|
|
23804
23805
|
|
|
23805
23806
|
|
|
23806
|
-
var allForms =
|
|
23807
|
-
|
|
23807
|
+
var allForms = answerFormsList.flat(); // Pull out the unique forms using uniqueBy.
|
|
23808
23808
|
|
|
23809
23809
|
var uniqueForms = uniqueBy(allForms, _.isEqual); // Sort them by the order they appear in the `formExamples` list.
|
|
23810
23810
|
|
|
23811
|
+
var formExampleKeys = Object.keys(formExamples);
|
|
23811
23812
|
return _.sortBy(uniqueForms, form => {
|
|
23812
|
-
return
|
|
23813
|
+
return formExampleKeys.indexOf(form.name);
|
|
23813
23814
|
});
|
|
23814
23815
|
};
|
|
23815
23816
|
// This function checks if the user inputted a percent value, parsing
|
|
@@ -23818,7 +23819,7 @@ var unionAnswerForms = function unionAnswerForms(answerFormsList) {
|
|
|
23818
23819
|
// can accept several input forms with or without "%", the decision
|
|
23819
23820
|
// to parse based on the presence of "%" in the input, is so that we
|
|
23820
23821
|
// don't accidently scale the user typed value before grading, CP-930.
|
|
23821
|
-
|
|
23822
|
+
function maybeParsePercentInput(inputValue, normalizedAnswerExpected) {
|
|
23822
23823
|
// If the input value is not a string ending with "%", then there's
|
|
23823
23824
|
// nothing more to do. The value will be graded as inputted by user.
|
|
23824
23825
|
if (!(typeof inputValue === "string" && inputValue.endsWith("%"))) {
|
|
@@ -23843,13 +23844,13 @@ var maybeParsePercentInput = (inputValue, normalizedAnswerExpected) => {
|
|
|
23843
23844
|
|
|
23844
23845
|
|
|
23845
23846
|
return value;
|
|
23846
|
-
}
|
|
23847
|
+
}
|
|
23847
23848
|
|
|
23848
23849
|
var propsTransform = function propsTransform(widgetOptions) {
|
|
23849
23850
|
var rendererProps = _.extend(_.omit(widgetOptions, "answers"), {
|
|
23850
23851
|
answerForms: unionAnswerForms( // Pull out the name of each form and whether that form has
|
|
23851
23852
|
// required simplification.
|
|
23852
|
-
|
|
23853
|
+
widgetOptions.answers.map(answer => {
|
|
23853
23854
|
return _.map(answer.answerForms, form => {
|
|
23854
23855
|
return {
|
|
23855
23856
|
simplify: answer.simplify,
|
|
@@ -24393,6 +24394,66 @@ _defineProperty(Icon, "defaultProps", {
|
|
|
24393
24394
|
color: "currentColor"
|
|
24394
24395
|
});
|
|
24395
24396
|
|
|
24397
|
+
function getA11yText(letter, checked, correct, crossedOut, showCorrectness) {
|
|
24398
|
+
// There are two pieces of metadata we want to add to each a11yText:
|
|
24399
|
+
// whether the answer was checked/crossed-out/neither, and whether the
|
|
24400
|
+
// answer is correct/incorrect/not-yet-revealed.
|
|
24401
|
+
//
|
|
24402
|
+
// Translation is tricky for cross-product situations like this, so
|
|
24403
|
+
// we've just enumerated all 9 possibilities as separate strings.
|
|
24404
|
+
if (showCorrectness && correct) {
|
|
24405
|
+
if (checked) {
|
|
24406
|
+
return i18n._("(Choice %(letter)s, Checked, Correct)", {
|
|
24407
|
+
letter
|
|
24408
|
+
});
|
|
24409
|
+
}
|
|
24410
|
+
|
|
24411
|
+
if (crossedOut) {
|
|
24412
|
+
return i18n._("(Choice %(letter)s, Crossed out, Correct)", {
|
|
24413
|
+
letter
|
|
24414
|
+
});
|
|
24415
|
+
}
|
|
24416
|
+
|
|
24417
|
+
return i18n._("(Choice %(letter)s, Correct)", {
|
|
24418
|
+
letter
|
|
24419
|
+
});
|
|
24420
|
+
}
|
|
24421
|
+
|
|
24422
|
+
if (showCorrectness && !correct) {
|
|
24423
|
+
if (checked) {
|
|
24424
|
+
return i18n._("(Choice %(letter)s, Checked, Incorrect)", {
|
|
24425
|
+
letter
|
|
24426
|
+
});
|
|
24427
|
+
}
|
|
24428
|
+
|
|
24429
|
+
if (crossedOut) {
|
|
24430
|
+
return i18n._("(Choice %(letter)s, Crossed out, Incorrect)", {
|
|
24431
|
+
letter
|
|
24432
|
+
});
|
|
24433
|
+
}
|
|
24434
|
+
|
|
24435
|
+
return i18n._("(Choice %(letter)s, Incorrect)", {
|
|
24436
|
+
letter
|
|
24437
|
+
});
|
|
24438
|
+
}
|
|
24439
|
+
|
|
24440
|
+
if (checked) {
|
|
24441
|
+
return i18n._("(Choice %(letter)s, Checked)", {
|
|
24442
|
+
letter
|
|
24443
|
+
});
|
|
24444
|
+
}
|
|
24445
|
+
|
|
24446
|
+
if (crossedOut) {
|
|
24447
|
+
return i18n._("(Choice %(letter)s, Crossed out)", {
|
|
24448
|
+
letter
|
|
24449
|
+
});
|
|
24450
|
+
}
|
|
24451
|
+
|
|
24452
|
+
return i18n._("(Choice %(letter)s)", {
|
|
24453
|
+
letter
|
|
24454
|
+
});
|
|
24455
|
+
}
|
|
24456
|
+
|
|
24396
24457
|
/**
|
|
24397
24458
|
* Given a choice's position in the radio widget, return the corresponding
|
|
24398
24459
|
* "choice letter". (For example, in English, `getChoiceLetter(0)` is "A",
|
|
@@ -24579,7 +24640,6 @@ function getDynamicStyles(checked, showCorrectness, pressed, multipleSelect, pri
|
|
|
24579
24640
|
|
|
24580
24641
|
function LibraryChoiceIcon(props) {
|
|
24581
24642
|
var {
|
|
24582
|
-
a11yText,
|
|
24583
24643
|
checked,
|
|
24584
24644
|
crossedOut,
|
|
24585
24645
|
showCorrectness,
|
|
@@ -24607,15 +24667,11 @@ function LibraryChoiceIcon(props) {
|
|
|
24607
24667
|
// radio icon
|
|
24608
24668
|
,
|
|
24609
24669
|
"data-is-radio-icon": true
|
|
24610
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
24611
|
-
className: "perseus-sr-only"
|
|
24612
|
-
}, a11yText), /*#__PURE__*/React.createElement("div", {
|
|
24613
|
-
"aria-hidden": true
|
|
24614
24670
|
}, /*#__PURE__*/React.createElement(ChoiceInner, {
|
|
24615
24671
|
letter: letter,
|
|
24616
24672
|
showCorrectness: showCorrectness,
|
|
24617
24673
|
correct: correct
|
|
24618
|
-
})))
|
|
24674
|
+
}))), crossedOut && /*#__PURE__*/React.createElement(CrossOutLine, {
|
|
24619
24675
|
color: dynamicStyles.borderColor
|
|
24620
24676
|
}));
|
|
24621
24677
|
}
|
|
@@ -24705,7 +24761,6 @@ function constructSATStyles(reviewMode, correct, checked, multipleSelect) {
|
|
|
24705
24761
|
function SATChoiceIcon(props) {
|
|
24706
24762
|
var {
|
|
24707
24763
|
letter,
|
|
24708
|
-
a11yText,
|
|
24709
24764
|
crossedOut,
|
|
24710
24765
|
reviewMode,
|
|
24711
24766
|
correct,
|
|
@@ -24733,11 +24788,7 @@ function SATChoiceIcon(props) {
|
|
|
24733
24788
|
color
|
|
24734
24789
|
},
|
|
24735
24790
|
className: css(styles$u.letter)
|
|
24736
|
-
}, /*#__PURE__*/React.createElement("span", {
|
|
24737
|
-
className: "perseus-sr-only"
|
|
24738
|
-
}, a11yText), /*#__PURE__*/React.createElement("span", {
|
|
24739
|
-
"aria-hidden": "true"
|
|
24740
|
-
}, letter)), crossedOut && /*#__PURE__*/React.createElement(CrossOutLine, {
|
|
24791
|
+
}, /*#__PURE__*/React.createElement("span", null, letter)), crossedOut && /*#__PURE__*/React.createElement(CrossOutLine, {
|
|
24741
24792
|
color: borderColor,
|
|
24742
24793
|
sat: true
|
|
24743
24794
|
}));
|
|
@@ -24774,66 +24825,6 @@ var styles$u = StyleSheet.create({
|
|
|
24774
24825
|
|
|
24775
24826
|
/* Component for rendering a letter icon in radio choice */
|
|
24776
24827
|
|
|
24777
|
-
function getA11yText(letter, checked, correct, crossedOut, showCorrectness) {
|
|
24778
|
-
// There are two pieces of metadata we want to add to each a11yText:
|
|
24779
|
-
// whether the answer was checked/crossed-out/neither, and whether the
|
|
24780
|
-
// answer is correct/incorrect/not-yet-revealed.
|
|
24781
|
-
//
|
|
24782
|
-
// Translation is tricky for cross-product situations like this, so
|
|
24783
|
-
// we've just enumerated all 9 possibilities as separate strings.
|
|
24784
|
-
if (showCorrectness && correct) {
|
|
24785
|
-
if (checked) {
|
|
24786
|
-
return i18n._("(Choice %(letter)s, Checked, Correct)", {
|
|
24787
|
-
letter
|
|
24788
|
-
});
|
|
24789
|
-
}
|
|
24790
|
-
|
|
24791
|
-
if (crossedOut) {
|
|
24792
|
-
return i18n._("(Choice %(letter)s, Crossed out, Correct)", {
|
|
24793
|
-
letter
|
|
24794
|
-
});
|
|
24795
|
-
}
|
|
24796
|
-
|
|
24797
|
-
return i18n._("(Choice %(letter)s, Correct)", {
|
|
24798
|
-
letter
|
|
24799
|
-
});
|
|
24800
|
-
}
|
|
24801
|
-
|
|
24802
|
-
if (showCorrectness && !correct) {
|
|
24803
|
-
if (checked) {
|
|
24804
|
-
return i18n._("(Choice %(letter)s, Checked, Incorrect)", {
|
|
24805
|
-
letter
|
|
24806
|
-
});
|
|
24807
|
-
}
|
|
24808
|
-
|
|
24809
|
-
if (crossedOut) {
|
|
24810
|
-
return i18n._("(Choice %(letter)s, Crossed out, Incorrect)", {
|
|
24811
|
-
letter
|
|
24812
|
-
});
|
|
24813
|
-
}
|
|
24814
|
-
|
|
24815
|
-
return i18n._("(Choice %(letter)s, Incorrect)", {
|
|
24816
|
-
letter
|
|
24817
|
-
});
|
|
24818
|
-
}
|
|
24819
|
-
|
|
24820
|
-
if (checked) {
|
|
24821
|
-
return i18n._("(Choice %(letter)s, Checked)", {
|
|
24822
|
-
letter
|
|
24823
|
-
});
|
|
24824
|
-
}
|
|
24825
|
-
|
|
24826
|
-
if (crossedOut) {
|
|
24827
|
-
return i18n._("(Choice %(letter)s, Crossed out)", {
|
|
24828
|
-
letter
|
|
24829
|
-
});
|
|
24830
|
-
}
|
|
24831
|
-
|
|
24832
|
-
return i18n._("(Choice %(letter)s)", {
|
|
24833
|
-
letter
|
|
24834
|
-
});
|
|
24835
|
-
}
|
|
24836
|
-
|
|
24837
24828
|
function ChoiceIcon(props) {
|
|
24838
24829
|
var {
|
|
24839
24830
|
pos,
|
|
@@ -24852,12 +24843,10 @@ function ChoiceIcon(props) {
|
|
|
24852
24843
|
transparentBackground
|
|
24853
24844
|
} = props;
|
|
24854
24845
|
var letter = getChoiceLetter(pos);
|
|
24855
|
-
var a11yText = getA11yText(letter, checked, correct, crossedOut, showCorrectness);
|
|
24856
24846
|
|
|
24857
24847
|
if (product === "sat") {
|
|
24858
24848
|
return /*#__PURE__*/React.createElement(SATChoiceIcon, {
|
|
24859
24849
|
letter: letter,
|
|
24860
|
-
a11yText: a11yText,
|
|
24861
24850
|
reviewMode: reviewMode,
|
|
24862
24851
|
checked: checked,
|
|
24863
24852
|
correct: correct,
|
|
@@ -24868,7 +24857,6 @@ function ChoiceIcon(props) {
|
|
|
24868
24857
|
|
|
24869
24858
|
return /*#__PURE__*/React.createElement(LibraryChoiceIcon, {
|
|
24870
24859
|
letter: letter,
|
|
24871
|
-
a11yText: a11yText,
|
|
24872
24860
|
reviewMode: reviewMode,
|
|
24873
24861
|
checked: checked,
|
|
24874
24862
|
crossedOut: crossedOut,
|
|
@@ -24979,6 +24967,15 @@ var ellipsisHorizontalIcon = {
|
|
|
24979
24967
|
width: 100,
|
|
24980
24968
|
height: 27.284
|
|
24981
24969
|
};
|
|
24970
|
+
// Note(TB): Received errors when using useUniqueIdWithMock
|
|
24971
|
+
// so created this workaround function. Will update when
|
|
24972
|
+
// useUniqueIdWithMock is available.
|
|
24973
|
+
// https://khanacademy.atlassian.net/browse/FEI-4861?atlOrigin=eyJpIjoiNDJlZWMwNjM1NWJhNDBkMWFjY2FmN2I0ZjcxZmQxOGUiLCJwIjoiaiJ9
|
|
24974
|
+
var id = 0;
|
|
24975
|
+
|
|
24976
|
+
function uniqueId() {
|
|
24977
|
+
return "choice-".concat(id++);
|
|
24978
|
+
}
|
|
24982
24979
|
|
|
24983
24980
|
function Choice(props) {
|
|
24984
24981
|
var {
|
|
@@ -25035,6 +25032,9 @@ function Choice(props) {
|
|
|
25035
25032
|
// content library.
|
|
25036
25033
|
|
|
25037
25034
|
var showDimmed = !apiOptions.satStyling && !reviewMode && apiOptions.readOnly || crossedOut;
|
|
25035
|
+
var letter = getChoiceLetter(pos);
|
|
25036
|
+
var a11yText = getA11yText(letter, checked, correct, crossedOut, showCorrectness);
|
|
25037
|
+
var choiceId = uniqueId();
|
|
25038
25038
|
return /*#__PURE__*/React.createElement("div", {
|
|
25039
25039
|
style: {
|
|
25040
25040
|
dispay: "flex",
|
|
@@ -25047,7 +25047,26 @@ function Choice(props) {
|
|
|
25047
25047
|
width: "100%",
|
|
25048
25048
|
opacity: showDimmed ? 0.5 : 1.0
|
|
25049
25049
|
}
|
|
25050
|
-
}, /*#__PURE__*/React.createElement(
|
|
25050
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
25051
|
+
className: "perseus-sr-only"
|
|
25052
|
+
}, /*#__PURE__*/React.createElement("input", {
|
|
25053
|
+
type: multipleSelect ? "checkbox" : "radio",
|
|
25054
|
+
id: choiceId,
|
|
25055
|
+
checked: checked,
|
|
25056
|
+
onClick: () => {
|
|
25057
|
+
// If we're checking a crossed-out option, let's
|
|
25058
|
+
// also uncross it.
|
|
25059
|
+
sendChange({
|
|
25060
|
+
checked: !checked,
|
|
25061
|
+
crossedOut: false
|
|
25062
|
+
});
|
|
25063
|
+
},
|
|
25064
|
+
onChange: () => {},
|
|
25065
|
+
disabled: disabled || apiOptions.staticRender || apiOptions.readOnly,
|
|
25066
|
+
tabIndex: -1
|
|
25067
|
+
}), /*#__PURE__*/React.createElement("label", {
|
|
25068
|
+
htmlFor: choiceId
|
|
25069
|
+
}, a11yText, " \xA0 ", content)), /*#__PURE__*/React.createElement(Clickable, {
|
|
25051
25070
|
onClick: () => {
|
|
25052
25071
|
// If we're checking a crossed-out option, let's
|
|
25053
25072
|
// also uncross it.
|
|
@@ -25058,13 +25077,11 @@ function Choice(props) {
|
|
|
25058
25077
|
},
|
|
25059
25078
|
className: descriptionClassName,
|
|
25060
25079
|
disabled: disabled || apiOptions.staticRender || apiOptions.readOnly,
|
|
25061
|
-
"aria-label": "Select Choice ".concat(getChoiceLetter(pos)),
|
|
25062
|
-
"aria-checked": checked ? "true" : "false",
|
|
25063
|
-
role: "checkbox",
|
|
25064
25080
|
style: {
|
|
25065
25081
|
flex: 1
|
|
25066
25082
|
},
|
|
25067
|
-
ref: forwardedRef
|
|
25083
|
+
ref: forwardedRef,
|
|
25084
|
+
"aria-hidden": "true"
|
|
25068
25085
|
}, _ref => {
|
|
25069
25086
|
var _apiOptions$styling;
|
|
25070
25087
|
|
|
@@ -25409,7 +25426,8 @@ function BaseRadio(props) {
|
|
|
25409
25426
|
}, /*#__PURE__*/React.createElement("legend", {
|
|
25410
25427
|
className: "perseus-sr-only"
|
|
25411
25428
|
}, instructions), shouldShowInstructions && /*#__PURE__*/React.createElement("div", {
|
|
25412
|
-
className: instructionsClassName
|
|
25429
|
+
className: instructionsClassName,
|
|
25430
|
+
"aria-hidden": "true"
|
|
25413
25431
|
}, instructions), /*#__PURE__*/React.createElement("ul", {
|
|
25414
25432
|
className: className,
|
|
25415
25433
|
style: {
|
|
@@ -26685,7 +26703,7 @@ class Definition extends React.Component {
|
|
|
26685
26703
|
}, /*#__PURE__*/React.createElement("span", {
|
|
26686
26704
|
className: "perseus-widget-definition"
|
|
26687
26705
|
}, /*#__PURE__*/React.createElement(Button, {
|
|
26688
|
-
size: "
|
|
26706
|
+
size: "medium",
|
|
26689
26707
|
kind: "tertiary",
|
|
26690
26708
|
onClick: () => {
|
|
26691
26709
|
this.props.trackInteraction();
|
|
@@ -28902,6 +28920,8 @@ class Group extends React.Component {
|
|
|
28902
28920
|
super(...arguments);
|
|
28903
28921
|
_this = this;
|
|
28904
28922
|
|
|
28923
|
+
_defineProperty(this, "rendererRef", void 0);
|
|
28924
|
+
|
|
28905
28925
|
_defineProperty(this, "change", function () {
|
|
28906
28926
|
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
28907
28927
|
args[_key] = arguments[_key];
|
|
@@ -28911,56 +28931,66 @@ class Group extends React.Component {
|
|
|
28911
28931
|
});
|
|
28912
28932
|
|
|
28913
28933
|
_defineProperty(this, "getUserInput", () => {
|
|
28914
|
-
|
|
28915
|
-
|
|
28934
|
+
var _this$rendererRef;
|
|
28935
|
+
|
|
28936
|
+
return (_this$rendererRef = this.rendererRef) === null || _this$rendererRef === void 0 ? void 0 : _this$rendererRef.getUserInput();
|
|
28916
28937
|
});
|
|
28917
28938
|
|
|
28918
28939
|
_defineProperty(this, "getSerializedState", () => {
|
|
28919
|
-
|
|
28920
|
-
|
|
28940
|
+
var _this$rendererRef2;
|
|
28941
|
+
|
|
28942
|
+
return (_this$rendererRef2 = this.rendererRef) === null || _this$rendererRef2 === void 0 ? void 0 : _this$rendererRef2.getSerializedState();
|
|
28921
28943
|
});
|
|
28922
28944
|
|
|
28923
28945
|
_defineProperty(this, "restoreSerializedState", (state, callback) => {
|
|
28924
|
-
|
|
28925
|
-
|
|
28946
|
+
var _this$rendererRef3;
|
|
28947
|
+
|
|
28948
|
+
(_this$rendererRef3 = this.rendererRef) === null || _this$rendererRef3 === void 0 ? void 0 : _this$rendererRef3.restoreSerializedState(state, callback); // Tell our renderer that we have no props to change
|
|
28926
28949
|
// (all our changes were in state):
|
|
28927
28950
|
|
|
28928
28951
|
return null;
|
|
28929
28952
|
});
|
|
28930
28953
|
|
|
28931
28954
|
_defineProperty(this, "simpleValidate", rubric => {
|
|
28932
|
-
|
|
28933
|
-
|
|
28955
|
+
var _this$rendererRef4;
|
|
28956
|
+
|
|
28957
|
+
return (_this$rendererRef4 = this.rendererRef) === null || _this$rendererRef4 === void 0 ? void 0 : _this$rendererRef4.score();
|
|
28934
28958
|
});
|
|
28935
28959
|
|
|
28936
28960
|
_defineProperty(this, "getInputPaths", () => {
|
|
28937
|
-
|
|
28938
|
-
|
|
28961
|
+
var _this$rendererRef5;
|
|
28962
|
+
|
|
28963
|
+
return (_this$rendererRef5 = this.rendererRef) === null || _this$rendererRef5 === void 0 ? void 0 : _this$rendererRef5.getInputPaths();
|
|
28939
28964
|
});
|
|
28940
28965
|
|
|
28941
|
-
_defineProperty(this, "setInputValue", (path, newValue,
|
|
28942
|
-
|
|
28943
|
-
|
|
28966
|
+
_defineProperty(this, "setInputValue", (path, newValue, callback) => {
|
|
28967
|
+
var _this$rendererRef6;
|
|
28968
|
+
|
|
28969
|
+
return (_this$rendererRef6 = this.rendererRef) === null || _this$rendererRef6 === void 0 ? void 0 : _this$rendererRef6.setInputValue(path, newValue, callback);
|
|
28944
28970
|
});
|
|
28945
28971
|
|
|
28946
28972
|
_defineProperty(this, "focus", () => {
|
|
28947
|
-
|
|
28948
|
-
|
|
28973
|
+
var _this$rendererRef7;
|
|
28974
|
+
|
|
28975
|
+
return (_this$rendererRef7 = this.rendererRef) === null || _this$rendererRef7 === void 0 ? void 0 : _this$rendererRef7.focus();
|
|
28949
28976
|
});
|
|
28950
28977
|
|
|
28951
28978
|
_defineProperty(this, "focusInputPath", path => {
|
|
28952
|
-
|
|
28953
|
-
|
|
28979
|
+
var _this$rendererRef8;
|
|
28980
|
+
|
|
28981
|
+
(_this$rendererRef8 = this.rendererRef) === null || _this$rendererRef8 === void 0 ? void 0 : _this$rendererRef8.focusPath(path);
|
|
28954
28982
|
});
|
|
28955
28983
|
|
|
28956
28984
|
_defineProperty(this, "blurInputPath", path => {
|
|
28957
|
-
|
|
28958
|
-
|
|
28985
|
+
var _this$rendererRef9;
|
|
28986
|
+
|
|
28987
|
+
(_this$rendererRef9 = this.rendererRef) === null || _this$rendererRef9 === void 0 ? void 0 : _this$rendererRef9.blurPath(path);
|
|
28959
28988
|
});
|
|
28960
28989
|
|
|
28961
28990
|
_defineProperty(this, "showRationalesForCurrentlySelectedChoices", () => {
|
|
28962
|
-
|
|
28963
|
-
|
|
28991
|
+
var _this$rendererRef10;
|
|
28992
|
+
|
|
28993
|
+
(_this$rendererRef10 = this.rendererRef) === null || _this$rendererRef10 === void 0 ? void 0 : _this$rendererRef10.showRationalesForCurrentlySelectedChoices();
|
|
28964
28994
|
});
|
|
28965
28995
|
}
|
|
28966
28996
|
|
|
@@ -28972,7 +29002,9 @@ class Group extends React.Component {
|
|
|
28972
29002
|
}
|
|
28973
29003
|
|
|
28974
29004
|
render() {
|
|
28975
|
-
var
|
|
29005
|
+
var _this$rendererRef11;
|
|
29006
|
+
|
|
29007
|
+
var apiOptions = _objectSpread2(_objectSpread2(_objectSpread2({}, ApiOptions.defaults), this.props.apiOptions), {}, {
|
|
28976
29008
|
// Api Rewriting to support correct onFocus/onBlur
|
|
28977
29009
|
// events for the mobile API
|
|
28978
29010
|
onFocusChange: (newFocus, oldFocus) => {
|
|
@@ -28994,8 +29026,8 @@ class Group extends React.Component {
|
|
|
28994
29026
|
// really we should have a more unidirectional flow. TODO(marcia): fix.
|
|
28995
29027
|
|
|
28996
29028
|
|
|
28997
|
-
var
|
|
28998
|
-
|
|
29029
|
+
var groupWidgets = this.props.findWidgets("group");
|
|
29030
|
+
var number = groupWidgets.indexOf(this);
|
|
28999
29031
|
var problemNumComponent = this.props.apiOptions.groupAnnotator(number, this.props.widgetId); // This is a little strange because the id of the widget that actually
|
|
29000
29032
|
// changed is going to be lost in favor of the group widget's id. The
|
|
29001
29033
|
// widgets prop also wasn't actually changed, and this only serves to
|
|
@@ -29003,15 +29035,12 @@ class Group extends React.Component {
|
|
|
29003
29035
|
// has occurred.
|
|
29004
29036
|
|
|
29005
29037
|
var onInteractWithWidget = id => {
|
|
29006
|
-
|
|
29007
|
-
|
|
29008
|
-
// eslint-disable-next-line react/no-string-refs
|
|
29009
|
-
this.change("widgets", this.refs.renderer.props.widgets);
|
|
29038
|
+
if (this.rendererRef) {
|
|
29039
|
+
this.change("widgets", this.rendererRef.props.widgets);
|
|
29010
29040
|
}
|
|
29011
|
-
};
|
|
29012
|
-
|
|
29041
|
+
};
|
|
29013
29042
|
|
|
29014
|
-
var score = this.
|
|
29043
|
+
var score = (_this$rendererRef11 = this.rendererRef) === null || _this$rendererRef11 === void 0 ? void 0 : _this$rendererRef11.score();
|
|
29015
29044
|
var isValid = score && score.type !== "invalid";
|
|
29016
29045
|
var isInvalid = score && score.type === "invalid"; // TODO(mdr): Widgets inside this Renderer are not discoverable through
|
|
29017
29046
|
// the parent Renderer's `findWidgets` function.
|
|
@@ -29022,15 +29051,17 @@ class Group extends React.Component {
|
|
|
29022
29051
|
"perseus-group-valid-answer": isValid,
|
|
29023
29052
|
"perseus-group-invalid-answer": isInvalid
|
|
29024
29053
|
})
|
|
29025
|
-
}, problemNumComponent, /*#__PURE__*/React.createElement(Renderer,
|
|
29026
|
-
|
|
29027
|
-
|
|
29054
|
+
}, problemNumComponent, /*#__PURE__*/React.createElement(Renderer, {
|
|
29055
|
+
content: this.props.content,
|
|
29056
|
+
widgets: this.props.widgets,
|
|
29057
|
+
images: this.props.images,
|
|
29058
|
+
ref: _ref => this.rendererRef = _ref,
|
|
29028
29059
|
apiOptions: apiOptions,
|
|
29029
29060
|
findExternalWidgets: this.props.findWidgets,
|
|
29030
29061
|
reviewMode: !!this.props.reviewModeRubric,
|
|
29031
29062
|
onInteractWithWidget: onInteractWithWidget,
|
|
29032
29063
|
linterContext: this.props.linterContext
|
|
29033
|
-
})
|
|
29064
|
+
}), this.props.icon && /*#__PURE__*/React.createElement("div", {
|
|
29034
29065
|
className: "group-icon"
|
|
29035
29066
|
}, this.props.icon));
|
|
29036
29067
|
}
|
|
@@ -29045,7 +29076,7 @@ _defineProperty(Group, "defaultProps", {
|
|
|
29045
29076
|
});
|
|
29046
29077
|
|
|
29047
29078
|
var traverseChildWidgets$1 = function traverseChildWidgets(props, traverseRenderer) {
|
|
29048
|
-
return
|
|
29079
|
+
return _objectSpread2(_objectSpread2({}, props), traverseRenderer(props));
|
|
29049
29080
|
};
|
|
29050
29081
|
|
|
29051
29082
|
var Group$1 = {
|