@khanacademy/perseus-editor 17.8.1 → 17.9.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/index.js CHANGED
@@ -145,7 +145,7 @@ const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
145
145
 
146
146
  // This file is processed by a Rollup plugin (replace) to inject the production
147
147
  const libName = "@khanacademy/perseus-editor";
148
- const libVersion = "17.8.1";
148
+ const libVersion = "17.9.0";
149
149
  addLibraryVersionToPerseusDebug(libName, libVersion);
150
150
 
151
151
  const devices = {
@@ -20885,12 +20885,13 @@ class WidgetSelect extends React__namespace.Component {
20885
20885
  const addWidgetString = "Add a widget\u2026";
20886
20886
  return /*#__PURE__*/React__namespace.createElement("select", {
20887
20887
  value: "",
20888
- onChange: this.handleChange
20888
+ onChange: this.handleChange,
20889
+ "data-testid": "editor__widget-select"
20889
20890
  }, /*#__PURE__*/React__namespace.createElement("option", {
20890
20891
  value: ""
20891
20892
  }, addWidgetString), /*#__PURE__*/React__namespace.createElement("option", {
20892
20893
  disabled: true
20893
- }, "--"), ___default["default"].map(orderedWidgetNames, name => {
20894
+ }, "--"), orderedWidgetNames.map(name => {
20894
20895
  return /*#__PURE__*/React__namespace.createElement("option", {
20895
20896
  key: name,
20896
20897
  value: name
@@ -25250,17 +25251,25 @@ class ExpressionEditor extends React__namespace.Component {
25250
25251
  return perseus.Changeable.change.apply(_this, args);
25251
25252
  };
25252
25253
  })();
25253
- serialize = () => {
25254
- const formSerializables = ["value", "form", "simplify", "considered",
25255
- // it's a little weird to serialize the react key, but saves some
25256
- // effort reconstructing them when this item is loaded later.
25257
- "key"];
25258
- const serializables = ["answerForms", "buttonSets", "functions", "times", "visibleLabel", "ariaLabel"];
25259
- const answerForms = this.props.answerForms.map(form => {
25260
- return ___default["default"](form).pick(formSerializables);
25261
- });
25262
- return lens(this.props).set(["answerForms"], answerForms).mod([], props => ___default["default"](props).pick(serializables)).freeze();
25263
- };
25254
+ serialize() {
25255
+ const {
25256
+ answerForms,
25257
+ buttonSets,
25258
+ functions,
25259
+ times,
25260
+ visibleLabel,
25261
+ ariaLabel
25262
+ } = this.props;
25263
+ return {
25264
+ answerForms,
25265
+ buttonSets,
25266
+ functions,
25267
+ times,
25268
+ visibleLabel,
25269
+ ariaLabel,
25270
+ extraKeys: perseusCore.deriveExtraKeys(this.props)
25271
+ };
25272
+ }
25264
25273
  getSaveWarnings = () => {
25265
25274
  const issues = [];
25266
25275
  if (this.props.answerForms.length === 0) {
@@ -25329,12 +25338,27 @@ class ExpressionEditor extends React__namespace.Component {
25329
25338
 
25330
25339
  // This function is designed to update the answerForm property
25331
25340
  // with new data. This function should not be used to update any
25332
- // other properties within ExpressionEditor.
25341
+ // other properties within ExpressionEditor except extraKeys
25342
+ // which is derived from answerForms
25333
25343
  updateAnswerForm(i, props) {
25334
25344
  const answerForms = lens(this.props.answerForms).merge([i], props).freeze();
25335
- this.change({
25345
+
25346
+ // deriveExtraKeys defaults to using the `extraKeys` it was given
25347
+ // which in most case is what we want, but is not what we want
25348
+ // in the editing experience because we should recalculate them
25349
+ // when answers change
25350
+ const {
25351
+ extraKeys: _,
25352
+ ...restProps
25353
+ } = this.props;
25354
+ const extraKeys = perseusCore.deriveExtraKeys({
25355
+ ...restProps,
25336
25356
  answerForms
25337
25357
  });
25358
+ this.change({
25359
+ answerForms,
25360
+ extraKeys
25361
+ });
25338
25362
  }
25339
25363
  handleReorder = components => {
25340
25364
  const answerForms = components.map(component => {
@@ -36402,19 +36426,11 @@ class PythonProgramEditor extends React__namespace.Component {
36402
36426
  }
36403
36427
  }
36404
36428
 
36405
- /* eslint-disable jsx-a11y/anchor-is-valid, react/forbid-prop-types */
36429
+ /* eslint-disable jsx-a11y/anchor-is-valid */
36406
36430
  const {
36407
36431
  InlineIcon
36408
36432
  } = perseus.components;
36409
36433
  class ChoiceEditor extends React__namespace.Component {
36410
- static propTypes = {
36411
- apiOptions: perseus.ApiOptions.propTypes,
36412
- choice: PropTypes__default["default"].object,
36413
- showDelete: PropTypes__default["default"].bool,
36414
- onClueChange: PropTypes__default["default"].func,
36415
- onContentChange: PropTypes__default["default"].func,
36416
- onDelete: PropTypes__default["default"].func
36417
- };
36418
36434
  render() {
36419
36435
  const checkedClass = this.props.choice.correct ? "correct" : "incorrect";
36420
36436
  let placeholder = "Type a choice here...";
@@ -36462,25 +36478,6 @@ class ChoiceEditor extends React__namespace.Component {
36462
36478
  }
36463
36479
  }
36464
36480
  class RadioEditor extends React__namespace.Component {
36465
- static propTypes = {
36466
- ...perseus.Changeable.propTypes,
36467
- apiOptions: perseus.ApiOptions.propTypes,
36468
- choices: PropTypes__default["default"].arrayOf(PropTypes__default["default"].shape({
36469
- content: PropTypes__default["default"].string,
36470
- clue: PropTypes__default["default"].string,
36471
- correct: PropTypes__default["default"].bool
36472
- })),
36473
- displayCount: PropTypes__default["default"].number,
36474
- randomize: PropTypes__default["default"].bool,
36475
- hasNoneOfTheAbove: PropTypes__default["default"].bool,
36476
- multipleSelect: PropTypes__default["default"].bool,
36477
- countChoices: PropTypes__default["default"].bool,
36478
- // TODO(kevinb): DEPRECATED: This is be used to force deselectEnabled
36479
- // behavior on mobile but not on desktop. When enabled, the user can
36480
- // deselect a radio input by tapping on it again.
36481
- deselectEnabled: PropTypes__default["default"].bool,
36482
- static: PropTypes__default["default"].bool
36483
- };
36484
36481
  static widgetName = "radio";
36485
36482
  static defaultProps = perseusCore.radioLogic.defaultWidgetOptions;
36486
36483
  change = (() => {
@@ -36524,14 +36521,19 @@ class RadioEditor extends React__namespace.Component {
36524
36521
  let {
36525
36522
  checked
36526
36523
  } = _ref;
36527
- const choices = ___default["default"].map(this.props.choices, (choice, i) => {
36528
- return ___default["default"].extend({}, choice, {
36524
+ const choices = this.props.choices.map((choice, i) => {
36525
+ return {
36526
+ ...choice,
36529
36527
  correct: checked[i],
36530
36528
  content: choice.isNoneOfTheAbove && !checked[i] ? "" : choice.content
36531
- });
36529
+ };
36532
36530
  });
36533
36531
  this.props.onChange({
36534
- choices: choices
36532
+ choices: choices,
36533
+ numCorrect: perseusCore.deriveNumCorrect({
36534
+ ...this.props,
36535
+ choices
36536
+ })
36535
36537
  });
36536
36538
  };
36537
36539
  onContentChange = (choiceIndex, newContent) => {
@@ -36543,7 +36545,7 @@ class RadioEditor extends React__namespace.Component {
36543
36545
  choices: choices
36544
36546
  });
36545
36547
  };
36546
- onClueChange = (choiceIndex, newClue) => {
36548
+ onClueChange(choiceIndex, newClue) {
36547
36549
  const choices = this.props.choices.slice();
36548
36550
  choices[choiceIndex] = ___default["default"].extend({}, choices[choiceIndex], {
36549
36551
  clue: newClue
@@ -36554,7 +36556,7 @@ class RadioEditor extends React__namespace.Component {
36554
36556
  this.props.onChange({
36555
36557
  choices: choices
36556
36558
  });
36557
- };
36559
+ }
36558
36560
  onDelete = choiceIndex => {
36559
36561
  const choices = this.props.choices.slice();
36560
36562
  const deleted = choices[choiceIndex];
@@ -36568,7 +36570,8 @@ class RadioEditor extends React__namespace.Component {
36568
36570
  e.preventDefault();
36569
36571
  const choices = this.props.choices.slice();
36570
36572
  const newChoice = {
36571
- isNoneOfTheAbove: noneOfTheAbove
36573
+ isNoneOfTheAbove: noneOfTheAbove,
36574
+ content: ""
36572
36575
  };
36573
36576
  const addIndex = choices.length - (this.props.hasNoneOfTheAbove ? 1 : 0);
36574
36577
  choices.splice(addIndex, 0, newChoice);
@@ -36598,9 +36601,27 @@ class RadioEditor extends React__namespace.Component {
36598
36601
  }
36599
36602
  return [];
36600
36603
  };
36601
- serialize = () => {
36602
- return ___default["default"].pick(this.props, "choices", "randomize", "multipleSelect", "countChoices", "displayCount", "hasNoneOfTheAbove", "deselectEnabled");
36603
- };
36604
+ serialize() {
36605
+ const {
36606
+ choices,
36607
+ randomize,
36608
+ multipleSelect,
36609
+ countChoices,
36610
+ displayCount,
36611
+ hasNoneOfTheAbove,
36612
+ deselectEnabled
36613
+ } = this.props;
36614
+ return {
36615
+ choices,
36616
+ randomize,
36617
+ multipleSelect,
36618
+ countChoices,
36619
+ displayCount,
36620
+ hasNoneOfTheAbove,
36621
+ deselectEnabled,
36622
+ numCorrect: perseusCore.deriveNumCorrect(this.props)
36623
+ };
36624
+ }
36604
36625
  render() {
36605
36626
  const numCorrect = ___default["default"].reduce(this.props.choices, function (memo, choice) {
36606
36627
  return choice.correct ? memo + 1 : memo;
@@ -36658,12 +36679,12 @@ class RadioEditor extends React__namespace.Component {
36658
36679
  apiOptions: this.props.apiOptions,
36659
36680
  choice: choice,
36660
36681
  onContentChange: newProps => {
36661
- if ("content" in newProps) {
36682
+ if (newProps.content != null) {
36662
36683
  this.onContentChange(i, newProps.content);
36663
36684
  }
36664
36685
  },
36665
36686
  onClueChange: newProps => {
36666
- if ("content" in newProps) {
36687
+ if (newProps.content != null) {
36667
36688
  this.onClueChange(i, newProps.content);
36668
36689
  }
36669
36690
  },