@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/es/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import _extends from '@babel/runtime/helpers/extends';
2
2
  import { components, Widgets, iconChevronDown, iconTrash, WIDGET_PROP_DENYLIST, Util, Log, PerseusMarkdown, preprocessTex, ApiOptions, itemDataVersion, ClassNames, usePerseusI18n, Renderer, Categorizer as Categorizer$1, Changeable, EditorJsonify, Dependencies, Expression, interactiveSizes, GrapherWidget, containerSizeClass, getInteractiveBoxFromSizeClass, GrapherUtil as GrapherUtil$1, PerseusI18nContext, KhanColors, mathOnlyParser, getAngleCoords, getPolygonCoords, getPointCoords, getQuadraticCoords, getSinusoidCoords, getCircleCoords, getLinearSystemCoords, getSegmentCoords, getLineCoords, InteractiveGraphWidget, bodyXsmallBold, MatrixWidget, makeSafeUrl, PlotterWidget, BaseRadio, TableWidget, widgets } from '@khanacademy/perseus';
3
3
  export { widgets } from '@khanacademy/perseus';
4
- import { upgradeWidgetInfoToLatestVersion, CoreWidgetRegistry, PerseusError, Errors, ItemExtras, categorizerLogic, csProgramLogic, definitionLogic, dropdownLogic, explanationLogic, expressionLogic, PerseusExpressionAnswerFormConsidered, gradedGroupLogic, gradedGroupSetLogic, grapherLogic, GrapherUtil, groupLogic, iframeLogic, imageLogic, inputNumberLogic, interactionLogic, lockedFigureColors, lockedFigureFillStyles, interactiveGraphLogic, labelImageLogic, matcherLogic, matrixLogic, getMatrixSize, measurerLogic, numberLineLogic, numericInputLogic, ordererLogic, passageLogic, passageRefLogic, passageRefTargetLogic, phetSimulationLogic, plotterLogic, plotterPlotTypes, pythonProgramLogic, radioLogic, sorterLogic, tableLogic, videoLogic } from '@khanacademy/perseus-core';
4
+ import { upgradeWidgetInfoToLatestVersion, CoreWidgetRegistry, PerseusError, Errors, ItemExtras, categorizerLogic, csProgramLogic, definitionLogic, dropdownLogic, explanationLogic, expressionLogic, deriveExtraKeys, PerseusExpressionAnswerFormConsidered, gradedGroupLogic, gradedGroupSetLogic, grapherLogic, GrapherUtil, groupLogic, iframeLogic, imageLogic, inputNumberLogic, interactionLogic, lockedFigureColors, lockedFigureFillStyles, interactiveGraphLogic, labelImageLogic, matcherLogic, matrixLogic, getMatrixSize, measurerLogic, numberLineLogic, numericInputLogic, ordererLogic, passageLogic, passageRefLogic, passageRefTargetLogic, phetSimulationLogic, plotterLogic, plotterPlotTypes, pythonProgramLogic, radioLogic, deriveNumCorrect, sorterLogic, tableLogic, videoLogic } from '@khanacademy/perseus-core';
5
5
  import * as React from 'react';
6
6
  import { useId, useRef, useEffect, useState } from 'react';
7
7
  import _ from 'underscore';
@@ -94,7 +94,7 @@ const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
94
94
 
95
95
  // This file is processed by a Rollup plugin (replace) to inject the production
96
96
  const libName = "@khanacademy/perseus-editor";
97
- const libVersion = "17.8.1";
97
+ const libVersion = "17.9.0";
98
98
  addLibraryVersionToPerseusDebug(libName, libVersion);
99
99
 
100
100
  const devices = {
@@ -20557,7 +20557,7 @@ katex.__defineMacro("\\pu", function (context) {
20557
20557
  // a mathematical minus, U+2212. So we need that extra 0.56.
20558
20558
  katex.__defineMacro("\\tripledash", "{\\vphantom{-}\\raisebox{2.56mu}{$\\mkern2mu" + "\\tiny\\text{-}\\mkern1mu\\text{-}\\mkern1mu\\text{-}\\mkern2mu$}}");
20559
20559
 
20560
- const _excluded$4 = ["component", "shouldDragHighlight"];
20560
+ const _excluded$5 = ["component", "shouldDragHighlight"];
20561
20561
  class DragTarget extends React.Component {
20562
20562
  constructor(props) {
20563
20563
  super(props);
@@ -20605,7 +20605,7 @@ class DragTarget extends React.Component {
20605
20605
  component: Component
20606
20606
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
20607
20607
  } = _this$props,
20608
- forwardProps = _objectWithoutPropertiesLoose(_this$props, _excluded$4);
20608
+ forwardProps = _objectWithoutPropertiesLoose(_this$props, _excluded$5);
20609
20609
  return /*#__PURE__*/React.createElement(Component, _extends({}, forwardProps, {
20610
20610
  style: Object.assign({}, this.props.style, opacity),
20611
20611
  onDrop: this.handleDrop,
@@ -20657,7 +20657,7 @@ const iconTablet = {
20657
20657
  height: 100
20658
20658
  };
20659
20659
 
20660
- const _excluded$3 = ["label"];
20660
+ const _excluded$4 = ["label"];
20661
20661
  const {
20662
20662
  InlineIcon: InlineIcon$8
20663
20663
  } = components;
@@ -20796,7 +20796,7 @@ function LabeledSwitch$1(props) {
20796
20796
  const {
20797
20797
  label
20798
20798
  } = props,
20799
- switchProps = _objectWithoutPropertiesLoose(props, _excluded$3);
20799
+ switchProps = _objectWithoutPropertiesLoose(props, _excluded$4);
20800
20800
  const id = useId();
20801
20801
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("label", {
20802
20802
  htmlFor: id
@@ -20833,12 +20833,13 @@ class WidgetSelect extends React.Component {
20833
20833
  const addWidgetString = "Add a widget\u2026";
20834
20834
  return /*#__PURE__*/React.createElement("select", {
20835
20835
  value: "",
20836
- onChange: this.handleChange
20836
+ onChange: this.handleChange,
20837
+ "data-testid": "editor__widget-select"
20837
20838
  }, /*#__PURE__*/React.createElement("option", {
20838
20839
  value: ""
20839
20840
  }, addWidgetString), /*#__PURE__*/React.createElement("option", {
20840
20841
  disabled: true
20841
- }, "--"), _.map(orderedWidgetNames, name => {
20842
+ }, "--"), orderedWidgetNames.map(name => {
20842
20843
  return /*#__PURE__*/React.createElement("option", {
20843
20844
  key: name,
20844
20845
  value: name
@@ -25149,6 +25150,7 @@ const styles$O = StyleSheet.create({
25149
25150
  }
25150
25151
  });
25151
25152
 
25153
+ const _excluded$3 = ["extraKeys"];
25152
25154
  const {
25153
25155
  InfoTip: InfoTip$l
25154
25156
  } = components;
@@ -25194,17 +25196,6 @@ class ExpressionEditor extends React.Component {
25194
25196
  this.change = (...args) => {
25195
25197
  return Changeable.change.apply(this, args);
25196
25198
  };
25197
- this.serialize = () => {
25198
- const formSerializables = ["value", "form", "simplify", "considered",
25199
- // it's a little weird to serialize the react key, but saves some
25200
- // effort reconstructing them when this item is loaded later.
25201
- "key"];
25202
- const serializables = ["answerForms", "buttonSets", "functions", "times", "visibleLabel", "ariaLabel"];
25203
- const answerForms = this.props.answerForms.map(form => {
25204
- return _(form).pick(formSerializables);
25205
- });
25206
- return lens(this.props).set(["answerForms"], answerForms).mod([], props => _(props).pick(serializables)).freeze();
25207
- };
25208
25199
  this.getSaveWarnings = () => {
25209
25200
  const issues = [];
25210
25201
  if (this.props.answerForms.length === 0) {
@@ -25336,13 +25327,44 @@ class ExpressionEditor extends React.Component {
25336
25327
  functionsInternal: this.props.functions.join(" ")
25337
25328
  };
25338
25329
  }
25330
+ serialize() {
25331
+ const {
25332
+ answerForms,
25333
+ buttonSets,
25334
+ functions,
25335
+ times,
25336
+ visibleLabel,
25337
+ ariaLabel
25338
+ } = this.props;
25339
+ return {
25340
+ answerForms,
25341
+ buttonSets,
25342
+ functions,
25343
+ times,
25344
+ visibleLabel,
25345
+ ariaLabel,
25346
+ extraKeys: deriveExtraKeys(this.props)
25347
+ };
25348
+ }
25339
25349
  // This function is designed to update the answerForm property
25340
25350
  // with new data. This function should not be used to update any
25341
- // other properties within ExpressionEditor.
25351
+ // other properties within ExpressionEditor except extraKeys
25352
+ // which is derived from answerForms
25342
25353
  updateAnswerForm(i, props) {
25343
25354
  const answerForms = lens(this.props.answerForms).merge([i], props).freeze();
25344
- this.change({
25355
+
25356
+ // deriveExtraKeys defaults to using the `extraKeys` it was given
25357
+ // which in most case is what we want, but is not what we want
25358
+ // in the editing experience because we should recalculate them
25359
+ // when answers change
25360
+ const _this$props = this.props,
25361
+ restProps = _objectWithoutPropertiesLoose(_this$props, _excluded$3);
25362
+ const extraKeys = deriveExtraKeys(_extends({}, restProps, {
25345
25363
  answerForms
25364
+ }));
25365
+ this.change({
25366
+ answerForms,
25367
+ extraKeys
25346
25368
  });
25347
25369
  }
25348
25370
  changeSimplify(key, simplify) {
@@ -36147,14 +36169,6 @@ class ChoiceEditor extends React.Component {
36147
36169
  }, clueEditor), this.props.showDelete && deleteLink);
36148
36170
  }
36149
36171
  }
36150
- ChoiceEditor.propTypes = {
36151
- apiOptions: ApiOptions.propTypes,
36152
- choice: PropTypes.object,
36153
- showDelete: PropTypes.bool,
36154
- onClueChange: PropTypes.func,
36155
- onContentChange: PropTypes.func,
36156
- onDelete: PropTypes.func
36157
- };
36158
36172
  class RadioEditor extends React.Component {
36159
36173
  constructor(..._args) {
36160
36174
  super(..._args);
@@ -36192,14 +36206,17 @@ class RadioEditor extends React.Component {
36192
36206
  this.onChange = ({
36193
36207
  checked
36194
36208
  }) => {
36195
- const choices = _.map(this.props.choices, (choice, i) => {
36196
- return _.extend({}, choice, {
36209
+ const choices = this.props.choices.map((choice, i) => {
36210
+ return _extends({}, choice, {
36197
36211
  correct: checked[i],
36198
36212
  content: choice.isNoneOfTheAbove && !checked[i] ? "" : choice.content
36199
36213
  });
36200
36214
  });
36201
36215
  this.props.onChange({
36202
- choices: choices
36216
+ choices: choices,
36217
+ numCorrect: deriveNumCorrect(_extends({}, this.props, {
36218
+ choices
36219
+ }))
36203
36220
  });
36204
36221
  };
36205
36222
  this.onContentChange = (choiceIndex, newContent) => {
@@ -36211,18 +36228,6 @@ class RadioEditor extends React.Component {
36211
36228
  choices: choices
36212
36229
  });
36213
36230
  };
36214
- this.onClueChange = (choiceIndex, newClue) => {
36215
- const choices = this.props.choices.slice();
36216
- choices[choiceIndex] = _.extend({}, choices[choiceIndex], {
36217
- clue: newClue
36218
- });
36219
- if (newClue === "") {
36220
- delete choices[choiceIndex].clue;
36221
- }
36222
- this.props.onChange({
36223
- choices: choices
36224
- });
36225
- };
36226
36231
  this.onDelete = choiceIndex => {
36227
36232
  const choices = this.props.choices.slice();
36228
36233
  const deleted = choices[choiceIndex];
@@ -36236,7 +36241,8 @@ class RadioEditor extends React.Component {
36236
36241
  e.preventDefault();
36237
36242
  const choices = this.props.choices.slice();
36238
36243
  const newChoice = {
36239
- isNoneOfTheAbove: noneOfTheAbove
36244
+ isNoneOfTheAbove: noneOfTheAbove,
36245
+ content: ""
36240
36246
  };
36241
36247
  const addIndex = choices.length - (this.props.hasNoneOfTheAbove ? 1 : 0);
36242
36248
  choices.splice(addIndex, 0, newChoice);
@@ -36266,8 +36272,38 @@ class RadioEditor extends React.Component {
36266
36272
  }
36267
36273
  return [];
36268
36274
  };
36269
- this.serialize = () => {
36270
- return _.pick(this.props, "choices", "randomize", "multipleSelect", "countChoices", "displayCount", "hasNoneOfTheAbove", "deselectEnabled");
36275
+ }
36276
+ onClueChange(choiceIndex, newClue) {
36277
+ const choices = this.props.choices.slice();
36278
+ choices[choiceIndex] = _.extend({}, choices[choiceIndex], {
36279
+ clue: newClue
36280
+ });
36281
+ if (newClue === "") {
36282
+ delete choices[choiceIndex].clue;
36283
+ }
36284
+ this.props.onChange({
36285
+ choices: choices
36286
+ });
36287
+ }
36288
+ serialize() {
36289
+ const {
36290
+ choices,
36291
+ randomize,
36292
+ multipleSelect,
36293
+ countChoices,
36294
+ displayCount,
36295
+ hasNoneOfTheAbove,
36296
+ deselectEnabled
36297
+ } = this.props;
36298
+ return {
36299
+ choices,
36300
+ randomize,
36301
+ multipleSelect,
36302
+ countChoices,
36303
+ displayCount,
36304
+ hasNoneOfTheAbove,
36305
+ deselectEnabled,
36306
+ numCorrect: deriveNumCorrect(this.props)
36271
36307
  };
36272
36308
  }
36273
36309
  render() {
@@ -36327,12 +36363,12 @@ class RadioEditor extends React.Component {
36327
36363
  apiOptions: this.props.apiOptions,
36328
36364
  choice: choice,
36329
36365
  onContentChange: newProps => {
36330
- if ("content" in newProps) {
36366
+ if (newProps.content != null) {
36331
36367
  this.onContentChange(i, newProps.content);
36332
36368
  }
36333
36369
  },
36334
36370
  onClueChange: newProps => {
36335
- if ("content" in newProps) {
36371
+ if (newProps.content != null) {
36336
36372
  this.onClueChange(i, newProps.content);
36337
36373
  }
36338
36374
  },
@@ -36361,24 +36397,6 @@ class RadioEditor extends React.Component {
36361
36397
  }, /*#__PURE__*/React.createElement(InlineIcon, iconPlus), " None of the above", " ")));
36362
36398
  }
36363
36399
  }
36364
- RadioEditor.propTypes = _extends({}, Changeable.propTypes, {
36365
- apiOptions: ApiOptions.propTypes,
36366
- choices: PropTypes.arrayOf(PropTypes.shape({
36367
- content: PropTypes.string,
36368
- clue: PropTypes.string,
36369
- correct: PropTypes.bool
36370
- })),
36371
- displayCount: PropTypes.number,
36372
- randomize: PropTypes.bool,
36373
- hasNoneOfTheAbove: PropTypes.bool,
36374
- multipleSelect: PropTypes.bool,
36375
- countChoices: PropTypes.bool,
36376
- // TODO(kevinb): DEPRECATED: This is be used to force deselectEnabled
36377
- // behavior on mobile but not on desktop. When enabled, the user can
36378
- // deselect a radio input by tapping on it again.
36379
- deselectEnabled: PropTypes.bool,
36380
- static: PropTypes.bool
36381
- });
36382
36400
  RadioEditor.widgetName = "radio";
36383
36401
  RadioEditor.defaultProps = radioLogic.defaultWidgetOptions;
36384
36402