@khanacademy/perseus 13.2.0 → 15.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/article-renderer.d.ts +1 -2
  3. package/dist/components/svg-image.d.ts +10 -9
  4. package/dist/es/index.js +51 -498
  5. package/dist/es/index.js.map +1 -1
  6. package/dist/index.d.ts +0 -1
  7. package/dist/index.js +72 -511
  8. package/dist/index.js.map +1 -1
  9. package/dist/multi-items/item-types.d.ts +1 -1
  10. package/dist/multi-items/multi-renderer.d.ts +2 -2
  11. package/dist/perseus-api.d.ts +0 -3
  12. package/dist/server-item-renderer.d.ts +2 -15
  13. package/dist/strings.js +1 -0
  14. package/dist/styles/shared.d.ts +1 -0
  15. package/dist/types.d.ts +0 -2
  16. package/dist/util/a11y.d.ts +1 -0
  17. package/dist/widgets/expression.d.ts +0 -12
  18. package/dist/widgets/graded-group.d.ts +1 -1
  19. package/dist/widgets/label-image.d.ts +1 -1
  20. package/dist/widgets/matcher.d.ts +0 -1
  21. package/dist/widgets/passage.d.ts +1 -1
  22. package/dist/widgets/radio/widget.d.ts +1 -1
  23. package/dist/widgets/simulator.d.ts +0 -1
  24. package/dist/widgets/sorter.d.ts +0 -1
  25. package/package.json +8 -8
  26. package/src/__stories__/server-item-renderer.stories.tsx +1 -1
  27. package/src/__testdata__/server-item-renderer.testdata.ts +160 -0
  28. package/src/__tests__/article-renderer.test.tsx +2 -3
  29. package/src/__tests__/server-item-renderer.test.tsx +2 -2
  30. package/src/article-renderer.tsx +13 -15
  31. package/src/components/graph.tsx +14 -8
  32. package/src/components/svg-image.tsx +14 -12
  33. package/src/hints-renderer.tsx +1 -0
  34. package/src/index.ts +0 -1
  35. package/src/multi-items/item-types.ts +1 -1
  36. package/src/multi-items/multi-renderer.tsx +4 -4
  37. package/src/perseus-api.tsx +0 -6
  38. package/src/perseus-types.ts +8 -3
  39. package/src/server-item-renderer.tsx +12 -13
  40. package/src/types.ts +0 -5
  41. package/src/util.ts +1 -1
  42. package/src/widgets/__stories__/expression.stories.tsx +0 -1
  43. package/src/widgets/__stories__/random-widgets.stories.tsx +59 -0
  44. package/src/widgets/__stories__/test-keypad-context-wrapper.tsx +0 -1
  45. package/src/widgets/__testdata__/categorizer.testdata.ts +40 -0
  46. package/src/widgets/__testdata__/dropdown.testdata.ts +38 -0
  47. package/src/widgets/__testdata__/explanation.testdata.ts +30 -0
  48. package/src/widgets/__testdata__/expression.testdata.ts +82 -0
  49. package/src/widgets/__testdata__/interactive-graph-random.testdata.ts +269 -0
  50. package/src/widgets/__testdata__/radio.testdata.ts +63 -0
  51. package/src/widgets/__testdata__/randomizers.ts +51 -0
  52. package/src/widgets/__tests__/expression-mobile.test.tsx +0 -2
  53. package/src/widgets/__tests__/expression.test.tsx +4 -15
  54. package/src/widgets/__tests__/randomizers.test.ts +53 -0
  55. package/src/widgets/expression.tsx +1 -5
  56. package/src/widgets/graded-group.tsx +1 -1
  57. package/src/widgets/image.tsx +27 -20
  58. package/src/widgets/label-image/strings.ts +3 -1
  59. package/src/widgets/label-image.tsx +2 -2
  60. package/src/widgets/passage-ref.tsx +1 -1
  61. package/src/widgets/passage.tsx +1 -1
  62. package/src/widgets/radio/widget.tsx +1 -1
  63. package/src/widgets/radio.ts +1 -1
  64. package/tsconfig-build.tsbuildinfo +1 -1
  65. package/dist/item-renderer.d.ts +0 -125
  66. package/dist/mixins/provide-keypad.d.ts +0 -39
  67. package/src/__stories__/item-renderer.stories.tsx +0 -30
  68. package/src/__testdata__/item-renderer.testdata.ts +0 -299
  69. package/src/__tests__/__snapshots__/item-renderer.test.tsx.snap +0 -224
  70. package/src/__tests__/item-renderer.test.tsx +0 -930
  71. package/src/item-renderer.tsx +0 -529
  72. package/src/mixins/provide-keypad.tsx +0 -138
package/dist/es/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as KAS from '@khanacademy/kas';
2
- import { keyTranslator, CursorContext, createMathField, mathQuillInstance, getCursorContext, DesktopKeypad, KeypadInput, KeypadType, KeyArray, keypadElementPropType, MobileKeypad } from '@khanacademy/math-input';
2
+ import { keyTranslator, CursorContext, createMathField, mathQuillInstance, getCursorContext, DesktopKeypad, KeypadInput, KeypadType, KeyArray, keypadElementPropType } from '@khanacademy/math-input';
3
3
  import * as PerseusLinter from '@khanacademy/perseus-linter';
4
4
  import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
5
5
  import { View, RenderStateRoot, Text } from '@khanacademy/wonder-blocks-core';
@@ -32,20 +32,17 @@ import Link from '@khanacademy/wonder-blocks-link';
32
32
  import { addLibraryVersionToPerseusDebug } from '@khanacademy/perseus-core';
33
33
 
34
34
  function _extends() {
35
- _extends = Object.assign || function (target) {
35
+ _extends = Object.assign ? Object.assign.bind() : function (target) {
36
36
  for (var i = 1; i < arguments.length; i++) {
37
37
  var source = arguments[i];
38
-
39
38
  for (var key in source) {
40
39
  if (Object.prototype.hasOwnProperty.call(source, key)) {
41
40
  target[key] = source[key];
42
41
  }
43
42
  }
44
43
  }
45
-
46
44
  return target;
47
45
  };
48
-
49
46
  return _extends.apply(this, arguments);
50
47
  }
51
48
 
@@ -54,13 +51,11 @@ function _objectWithoutPropertiesLoose(source, excluded) {
54
51
  var target = {};
55
52
  var sourceKeys = Object.keys(source);
56
53
  var key, i;
57
-
58
54
  for (i = 0; i < sourceKeys.length; i++) {
59
55
  key = sourceKeys[i];
60
56
  if (excluded.indexOf(key) >= 0) continue;
61
57
  target[key] = source[key];
62
58
  }
63
-
64
59
  return target;
65
60
  }
66
61
 
@@ -875,10 +870,6 @@ const ApiOptions = {
875
870
  nativeKeypadProxy: PropTypes.func,
876
871
  // Indicates whether or not to use mobile styling.
877
872
  isMobile: PropTypes.bool,
878
- // Whether to use v1 (Legacy) Keypad for MathInput mobile or
879
- // use the new v2 Keypad
880
- // TODO [LC-1088]: remove after v2 release
881
- useV2Keypad: PropTypes.bool,
882
873
  // A function, called with a bool indicating whether use of the
883
874
  // drawing area (scratchpad) should be allowed/disallowed.
884
875
  // Previously handled by `Khan.scratchpad.enable/disable`
@@ -917,7 +908,6 @@ const ApiOptions = {
917
908
  defaults: {
918
909
  isArticle: false,
919
910
  isMobile: false,
920
- useV2Keypad: false,
921
911
  onInputError: function () {},
922
912
  onFocusChange: function () {},
923
913
  GroupMetadataEditor: StubTagEditor,
@@ -1924,7 +1914,7 @@ const deriveKeypadVersion = apiOptions => {
1924
1914
  // a bit tricky, but this code will be relatively short-lived
1925
1915
  // as we coalesce onto the new, v2 Keypad, at which point we
1926
1916
  // can remove this `virtualKeypadVersion` field entirely.
1927
- return apiOptions.nativeKeypadProxy != null ? "REACT_NATIVE_KEYPAD" : apiOptions.customKeypad === true ? apiOptions.useV2Keypad === true ? "MATH_INPUT_KEYPAD_V2" : "MATH_INPUT_KEYPAD_V1" : "PERSEUS_MATH_INPUT";
1917
+ return apiOptions.nativeKeypadProxy != null ? "REACT_NATIVE_KEYPAD" : "MATH_INPUT_KEYPAD_V2";
1928
1918
  };
1929
1919
  // The new, MathQuill input expression widget
1930
1920
  class Expression extends React.Component {
@@ -10239,7 +10229,7 @@ const pluck = function pluck(table, subKey) {
10239
10229
  * });
10240
10230
  * {a: 2, b: 3}
10241
10231
  */
10242
- const mapObject$3 = function mapObject(obj, lambda) {
10232
+ const mapObject$2 = function mapObject(obj, lambda) {
10243
10233
  const result = {};
10244
10234
  _.each(_.keys(obj), function (key) {
10245
10235
  // @ts-expect-error - TS2345 - Argument of type 'string' is not assignable to parameter of type 'K'.
@@ -10265,7 +10255,7 @@ const mapObjectFromArray$1 = function mapObjectFromArray(arr, lambda) {
10265
10255
  };
10266
10256
  var objective_ = {
10267
10257
  pluck: pluck,
10268
- mapObject: mapObject$3,
10258
+ mapObject: mapObject$2,
10269
10259
  mapObjectFromArray: mapObjectFromArray$1
10270
10260
  };
10271
10261
 
@@ -16521,7 +16511,7 @@ const doJSONP = function doJSONP(url, options) {
16521
16511
  // system (with file://). We replace urls that start with `web+graphie`
16522
16512
  // in the perseus json with this `file+graphie` prefix to indicate that
16523
16513
  // they should have the `file://` protocol instead of `https://`.
16524
- const svgLocalLabelsRegex = /^file\+graphie\:/;
16514
+ const svgLocalLabelsRegex = /^file\+graphie:/;
16525
16515
  const hashRegex = /\/([^/]+)$/;
16526
16516
  function isImageProbablyPhotograph(imageUrl) {
16527
16517
  // TODO(david): Do an inventory to refine this heuristic. For example, what
@@ -18874,7 +18864,7 @@ WidgetContainer.defaultProps = {
18874
18864
  };
18875
18865
 
18876
18866
  const {
18877
- mapObject: mapObject$2,
18867
+ mapObject: mapObject$1,
18878
18868
  mapObjectFromArray
18879
18869
  } = objective_;
18880
18870
  const rContainsNonWhitespace = /\S/;
@@ -18942,7 +18932,7 @@ class Renderer extends React.Component {
18942
18932
  // @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; }'.
18943
18933
  this._getAllWidgetsInfo = props => {
18944
18934
  // @ts-expect-error - TS2345 - Argument of type '{ [key: string]: PerseusWidget; }' is not assignable to parameter of type 'Partial<Record<string, CategorizerWidget>>'.
18945
- return mapObject$2(props.widgets, (widgetInfo, widgetId) => {
18935
+ return mapObject$1(props.widgets, (widgetInfo, widgetId) => {
18946
18936
  if (!widgetInfo.type || !widgetInfo.alignment) {
18947
18937
  const newValues = {};
18948
18938
  if (!widgetInfo.type) {
@@ -18958,7 +18948,7 @@ class Renderer extends React.Component {
18958
18948
  };
18959
18949
  this._getAllWidgetsStartProps = (allWidgetInfo, props) => {
18960
18950
  // @ts-expect-error - TS2345 - Argument of type '{ [key: string]: PerseusWidget; }' is not assignable to parameter of type 'Partial<Record<string, CategorizerWidget>>'.
18961
- return mapObject$2(allWidgetInfo, widgetInfo => {
18951
+ return mapObject$1(allWidgetInfo, widgetInfo => {
18962
18952
  return getRendererPropsForWidgetInfo(widgetInfo, props.problemNum);
18963
18953
  });
18964
18954
  };
@@ -19073,7 +19063,7 @@ class Renderer extends React.Component {
19073
19063
  * state from that instead of the current widget props.
19074
19064
  */
19075
19065
  this.getSerializedState = widgetProps => {
19076
- return mapObject$2(widgetProps || this.state.widgetProps, (props, widgetId) => {
19066
+ return mapObject$1(widgetProps || this.state.widgetProps, (props, widgetId) => {
19077
19067
  const widget = this.getWidgetInstance(widgetId);
19078
19068
  if (widget && widget.getSerializedState) {
19079
19069
  return widget.getSerializedState();
@@ -19109,7 +19099,7 @@ class Renderer extends React.Component {
19109
19099
  }
19110
19100
  };
19111
19101
  this.setState({
19112
- widgetProps: mapObject$2(serializedState, (props, widgetId) => {
19102
+ widgetProps: mapObject$1(serializedState, (props, widgetId) => {
19113
19103
  const widget = this.getWidgetInstance(widgetId);
19114
19104
  if (widget && widget.restoreSerializedState) {
19115
19105
  // Note that we probably can't call
@@ -26697,29 +26687,32 @@ class ImageWidget extends React.Component {
26697
26687
  } = this.props;
26698
26688
  const backgroundImage = this.props.backgroundImage;
26699
26689
  if (backgroundImage.url) {
26700
- image = /*#__PURE__*/React.createElement(SvgImage, {
26701
- src: backgroundImage.url,
26690
+ const url = backgroundImage.url;
26691
+ image = /*#__PURE__*/React.createElement(context$1.Consumer, null, ({
26692
+ setAssetStatus
26693
+ }) => /*#__PURE__*/React.createElement(SvgImage, {
26694
+ src: url,
26702
26695
  alt:
26703
26696
  /* alt text is formatted in a sr-only
26704
- div next to the image in addition to
26705
- the alt attribute.
26706
- If there is no alt text at all,
26707
- we don't put an alt attribute on
26708
- the image, so that screen readers
26709
- know there's something they can't
26710
- read there :(.
26711
- NOTE: React <=0.13 (maybe later)
26712
- has a bug where it won't ever
26713
- remove an attribute, so if this
26714
- alt node is ever defined it's
26715
- not removed. This is sort of
26716
- dangerous, but we usually re-key
26717
- new renderers so that they're
26718
- rendered from scratch anyways,
26719
- so this shouldn't be a problem
26720
- in practice right now, although
26721
- it will exhibit weird behaviour
26722
- while editing. */
26697
+ div next to the image in addition to
26698
+ the alt attribute.
26699
+ If there is no alt text at all,
26700
+ we don't put an alt attribute on
26701
+ the image, so that screen readers
26702
+ know there's something they can't
26703
+ read there :(.
26704
+ NOTE: React <=0.13 (maybe later)
26705
+ has a bug where it won't ever
26706
+ remove an attribute, so if this
26707
+ alt node is ever defined it's
26708
+ not removed. This is sort of
26709
+ dangerous, but we usually re-key
26710
+ new renderers so that they're
26711
+ rendered from scratch anyways,
26712
+ so this shouldn't be a problem
26713
+ in practice right now, although
26714
+ it will exhibit weird behaviour
26715
+ while editing. */
26723
26716
  this.props.alt,
26724
26717
  overrideAriaHidden: true,
26725
26718
  width: backgroundImage.width,
@@ -26733,8 +26726,9 @@ class ImageWidget extends React.Component {
26733
26726
  trackInteraction: this.props.trackInteraction,
26734
26727
  zoomToFullSizeOnMobile: apiOptions.isMobile,
26735
26728
  constrainHeight: apiOptions.isMobile,
26736
- allowFullBleed: apiOptions.isMobile
26737
- });
26729
+ allowFullBleed: apiOptions.isMobile,
26730
+ setAssetStatus: setAssetStatus
26731
+ }));
26738
26732
  }
26739
26733
  if (this.props.alt) {
26740
26734
  alt = /*#__PURE__*/React.createElement("span", {
@@ -27734,16 +27728,18 @@ class Graph extends React.Component {
27734
27728
  const imageData = this.props.backgroundImage;
27735
27729
  if (imageData.url) {
27736
27730
  const scale = this.props.box[0] / interactiveSizes$1.defaultBoxSize;
27737
- image =
27738
- /*#__PURE__*/
27731
+ image = /*#__PURE__*/React.createElement(context$1.Consumer, null, ({
27732
+ setAssetStatus
27733
+ }) => /*#__PURE__*/React.createElement(SvgImage
27739
27734
  // @ts-expect-error - TS2741 - Property 'alt' is missing in type '{ src: any; width: any; height: any; scale: number; responsive: false; }' but required in type 'Pick<Readonly<Props> & Readonly<{ children?: ReactNode; }>, "children" | "height" | "width" | "title" | "alt" | "trackInteraction" | "preloader" | "allowFullBleed" | "extraGraphie" | "overrideAriaHidden">'.
27740
- React.createElement(SvgImage, {
27735
+ , {
27741
27736
  src: imageData.url,
27742
27737
  width: imageData.width,
27743
27738
  height: imageData.height,
27744
27739
  scale: scale,
27745
- responsive: false
27746
- });
27740
+ responsive: false,
27741
+ setAssetStatus: setAssetStatus
27742
+ }));
27747
27743
  } else {
27748
27744
  image = null;
27749
27745
  }
@@ -44083,7 +44079,7 @@ const init = function init(options) {
44083
44079
 
44084
44080
  // This file is processed by a Rollup plugin (replace) to inject the production
44085
44081
  const libName = "@khanacademy/perseus";
44086
- const libVersion = "13.2.0";
44082
+ const libVersion = "15.0.0";
44087
44083
  addLibraryVersionToPerseusDebug(libName, libVersion);
44088
44084
 
44089
44085
  const apiVersion = {
@@ -44421,6 +44417,8 @@ class HintsRenderer extends React.Component {
44421
44417
  componentDidMount() {
44422
44418
  this._cacheHintImages();
44423
44419
  }
44420
+
44421
+ // eslint-disable-next-line react/no-unsafe
44424
44422
  UNSAFE_componentWillReceiveProps(nextProps) {
44425
44423
  // @ts-expect-error - TS2532 - Object is possibly 'undefined'. | TS2532 - Object is possibly 'undefined'.
44426
44424
  if (nextProps.hintsVisible - this.props.hintsVisible > 1) {
@@ -44590,453 +44588,6 @@ const styles$4 = StyleSheet.create({
44590
44588
  }
44591
44589
  });
44592
44590
 
44593
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
44594
-
44595
- // This represents the shape of any component that uses this ProvideKeypad
44596
- // mixin.
44597
- // NOTE: This is not a real component. It's a collection of methods used to
44598
- // create and manage a Keypad instances.
44599
- // TODO(LP-10789): replace this with a React Context object to pass information
44600
- // between Perseus and the Keypad.
44601
- const ProvideKeypad = {
44602
- getInitialState() {
44603
- const _this = this;
44604
- let keypadElement = null;
44605
- if (_this.props.apiOptions && _this.props.apiOptions.customKeypad && _this.props.apiOptions.nativeKeypadProxy) {
44606
- keypadElement = _this.props.apiOptions.nativeKeypadProxy(
44607
- // @ts-expect-error - TS2339 - Property 'blur' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'. | TS2339 - Property 'blur' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'.
44608
- () => _this.blur && _this.blur());
44609
- }
44610
- return {
44611
- keypadElement
44612
- };
44613
- },
44614
- componentDidMount() {
44615
- const _this = this;
44616
- const apiOptions = _this.props.apiOptions;
44617
- if (apiOptions && apiOptions.customKeypad && !apiOptions.nativeKeypadProxy) {
44618
- var _document$body;
44619
- // TODO(charlie): Render this and the wrapped component in the same
44620
- // React tree. We may also want to add this keypad asynchronously
44621
- // or on-demand in the future.
44622
- _this._keypadContainer = document.createElement("div");
44623
- (_document$body = document.body) == null || _document$body.appendChild(_this._keypadContainer);
44624
- render( /*#__PURE__*/React.createElement(MobileKeypad, {
44625
- onElementMounted: element => {
44626
- // NOTE(kevinb): The reason why this setState works is
44627
- // b/c we're calling it manually from item-renderer.jsx
44628
- // and we're manually setting the 'this' by using 'call'
44629
- // @ts-expect-error - TS2339 - Property 'setState' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'.
44630
- _this.setState({
44631
- keypadElement: element
44632
- });
44633
- },
44634
- onDismiss: () => {
44635
- // @ts-expect-error - TS2339 - Property 'blur' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'. | TS2339 - Property 'blur' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'.
44636
- _this.blur && _this.blur();
44637
- }
44638
- // @ts-expect-error - TS2339 - Property 'props' does not exist on type '{ readonly propTypes: { readonly apiOptions: React.PropType<{ customKeypad?: boolean | undefined; nativeKeypadProxy?: ((...a: readonly any[]) => unknown) | undefined; }>; readonly keypadStyle: Requireable<any>; }; readonly getInitialState: () => { ...; }; readonly componentDidMount: () => void; readonly componentWil...'.
44639
- ,
44640
- style: _this.props.keypadStyle,
44641
- useV2Keypad: apiOptions.useV2Keypad,
44642
- onAnalyticsEvent: async () => {
44643
- // Intentionally left empty. This component is
44644
- // deprecated and so we don't want/need to instrument
44645
- // it.
44646
- }
44647
- }), _this._keypadContainer);
44648
- }
44649
- },
44650
- componentWillUnmount() {
44651
- const _this = this;
44652
- if (_this._keypadContainer) {
44653
- ReactDOM__default.unmountComponentAtNode(_this._keypadContainer);
44654
- if (_this._keypadContainer.parentNode) {
44655
- // Note ChildNode.remove() isn't available in older Android
44656
- // webviews.
44657
- _this._keypadContainer.parentNode.removeChild(_this._keypadContainer);
44658
- }
44659
- _this._keypadContainer = null;
44660
- }
44661
- },
44662
- keypadElement() {
44663
- const _this = this;
44664
- return _this.state.keypadElement;
44665
- }
44666
- };
44667
-
44668
- const {
44669
- mapObject: mapObject$1
44670
- } = objective_;
44671
- /**
44672
- * @depreciated use ServerItemRenderer
44673
- */
44674
- class ItemRenderer extends React.Component {
44675
- constructor(props) {
44676
- super(props);
44677
- // @ts-expect-error - TS2564 - Property 'questionRenderer' has no initializer and is not definitely assigned in the constructor.
44678
- this.questionRenderer = void 0;
44679
- this.hintsRenderer = void 0;
44680
- this._currentFocus = void 0;
44681
- this._handleFocusChange = (newFocus, oldFocus) => {
44682
- if (newFocus != null) {
44683
- this._setCurrentFocus(newFocus);
44684
- } else {
44685
- this._onRendererBlur(oldFocus);
44686
- }
44687
- };
44688
- // NOTE: this must remain as an arrow function because we pass it Renderer
44689
- this.handleInteractWithWidget = widgetId => {
44690
- const withRemoved = _.difference(this.state.questionHighlightedWidgets, [widgetId]);
44691
- this.setState({
44692
- questionCompleted: false,
44693
- questionHighlightedWidgets: withRemoved
44694
- });
44695
- if (this.props.apiOptions.interactionCallback) {
44696
- this.props.apiOptions.interactionCallback();
44697
- }
44698
- };
44699
- this.state = _extends({}, ProvideKeypad.getInitialState.call(this), {
44700
- hintsVisible: props.initialHintsVisible || 0,
44701
- questionCompleted: false,
44702
- questionHighlightedWidgets: []
44703
- });
44704
- }
44705
- componentDidMount() {
44706
- ProvideKeypad.componentDidMount.call(this);
44707
- if (this.props.controlPeripherals && this.props.apiOptions.setDrawingAreaAvailable) {
44708
- this.props.apiOptions.setDrawingAreaAvailable(true);
44709
- }
44710
- this._currentFocus = null;
44711
- this.update();
44712
- }
44713
- UNSAFE_componentWillReceiveProps(nextProps) {
44714
- this.setState({
44715
- questionHighlightedWidgets: []
44716
- });
44717
- }
44718
- componentDidUpdate() {
44719
- this.update();
44720
- }
44721
- componentWillUnmount() {
44722
- ProvideKeypad.componentWillUnmount.call(this);
44723
- const workArea = document.querySelector(this.props.workAreaSelector);
44724
- if (workArea != null) {
44725
- ReactDOM.unmountComponentAtNode(workArea);
44726
- }
44727
- const hintsArea = document.querySelector(this.props.hintsAreaSelector);
44728
- if (hintsArea != null) {
44729
- ReactDOM.unmountComponentAtNode(hintsArea);
44730
- }
44731
- if (this.props.controlPeripherals) {
44732
- const answerArea = this.props.item.answerArea || {};
44733
- if (answerArea.calculator) {
44734
- $("#calculator").hide();
44735
- }
44736
- if (answerArea.periodicTable) {
44737
- $(".periodic-table-info-box").hide();
44738
- }
44739
- if (answerArea.zTable) {
44740
- $(".z-table-info-box").hide();
44741
- }
44742
- if (answerArea.tTable) {
44743
- $(".t-table-info-box").hide();
44744
- }
44745
- if (answerArea.chi2Table) {
44746
- $(".chi2-table-info-box").hide();
44747
- }
44748
- }
44749
- }
44750
- keypadElement() {
44751
- return ProvideKeypad.keypadElement.call(this);
44752
- }
44753
- update() {
44754
- const apiOptions = _extends({}, ApiOptions.defaults, this.props.apiOptions, {
44755
- onFocusChange: this._handleFocusChange
44756
- });
44757
- const workArea = document.querySelector(this.props.workAreaSelector);
44758
- const hintsArea = document.querySelector(this.props.hintsAreaSelector);
44759
- if (!workArea || !hintsArea) {
44760
- return;
44761
- }
44762
-
44763
- // Since the item renderer works by rendering things into three divs
44764
- // that have completely different places in the DOM, we have to do this
44765
- // strangeness instead of relying on React's normal render() method.
44766
- // TODO(alpert): Figure out how to clean this up somehow
44767
- render( /*#__PURE__*/React.createElement(DependenciesContext.Provider, {
44768
- value: this.props.dependencies
44769
- }, /*#__PURE__*/React.createElement(Renderer, _extends({
44770
- ref: node => {
44771
- if (!node) {
44772
- return;
44773
- }
44774
- this.questionRenderer = node;
44775
-
44776
- // NOTE(jeremy): Why don't we just pass this into the
44777
- // renderer as a prop?
44778
- const {
44779
- answerableCallback
44780
- } = apiOptions;
44781
- if (answerableCallback) {
44782
- const isAnswerable = this.questionRenderer.emptyWidgets().length === 0;
44783
- answerableCallback(isAnswerable);
44784
- }
44785
- },
44786
- keypadElement: this.keypadElement(),
44787
- problemNum: this.props.problemNum,
44788
- onInteractWithWidget: this.handleInteractWithWidget,
44789
- highlightedWidgets: this.state.questionHighlightedWidgets,
44790
- apiOptions: apiOptions,
44791
- questionCompleted: this.state.questionCompleted,
44792
- reviewMode: this.props.reviewMode,
44793
- savedState: this.props.savedState,
44794
- linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "question")
44795
- }, this.props.item.question, {
44796
- legacyPerseusLint: this.props.legacyPerseusLint
44797
- }))),
44798
- // @ts-expect-error - TS2345 - Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.
44799
- workArea);
44800
- render( /*#__PURE__*/React.createElement(DependenciesContext.Provider, {
44801
- value: this.props.dependencies
44802
- }, /*#__PURE__*/React.createElement(HintsRenderer, {
44803
- ref: node => this.hintsRenderer = node,
44804
- hints: this.props.item.hints,
44805
- hintsVisible: this.state.hintsVisible,
44806
- apiOptions: apiOptions,
44807
- linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "hints")
44808
- })),
44809
- // @ts-expect-error - TS2345 - Argument of type 'Element' is not assignable to parameter of type 'HTMLElement'.
44810
- hintsArea);
44811
- const answerArea = this.props.item.answerArea || {};
44812
- if (this.props.controlPeripherals) {
44813
- $("#calculator").toggle(answerArea.calculator || false);
44814
- $(".periodic-table-info-box").toggle(answerArea.periodicTable || false);
44815
- $(".z-table-info-box").toggle(answerArea.zTable || false);
44816
- $(".t-table-info-box").toggle(answerArea.tTable || false);
44817
- $(".chi2-table-info-box").toggle(answerArea.chi2Table || false);
44818
- } else {
44819
- if (answerArea.calculator) {
44820
- this.props.onShowCalculator && this.props.onShowCalculator();
44821
- }
44822
- if (answerArea.periodicTable) {
44823
- this.props.onShowPeriodicTable && this.props.onShowPeriodicTable({
44824
- showKey: answerArea.periodicTableWithKey
44825
- });
44826
- }
44827
- if (answerArea.zTable) {
44828
- this.props.onShowZTable && this.props.onShowZTable();
44829
- }
44830
- if (answerArea.tTable) {
44831
- this.props.onShowTTable && this.props.onShowTTable();
44832
- }
44833
- if (answerArea.chi2Table) {
44834
- this.props.onShowChi2Table && this.props.onShowChi2Table();
44835
- }
44836
- }
44837
- }
44838
- // Sets the current focus path and element and send an onChangeFocus event
44839
- // back to our parent.
44840
- _setCurrentFocus(newFocus) {
44841
- const keypadElement = this.keypadElement();
44842
- // By the time this happens, newFocus cannot be a prefix of
44843
- // prevFocused, since we must have either been called from
44844
- // an onFocusChange within a renderer, which is only called when
44845
- // this is not a prefix, or between the question and answer areas,
44846
- // which can never prefix each other.
44847
- const prevFocus = this._currentFocus;
44848
- this._currentFocus = newFocus;
44849
-
44850
- // Determine whether the newly focused path represents an input.
44851
- const inputPaths = this.getInputPaths();
44852
- const didFocusInput = this._currentFocus && inputPaths.some(inputPath => {
44853
- return Util.inputPathsEqual(inputPath, this._currentFocus);
44854
- });
44855
- if (this.props.apiOptions.onFocusChange != null) {
44856
- this.props.apiOptions.onFocusChange(this._currentFocus, prevFocus, didFocusInput && keypadElement && keypadElement.getDOMNode());
44857
- }
44858
- if (keypadElement) {
44859
- if (didFocusInput) {
44860
- keypadElement.activate();
44861
- } else {
44862
- keypadElement.dismiss();
44863
- }
44864
- }
44865
- }
44866
- _onRendererBlur(blurPath) {
44867
- const blurringFocusPath = this._currentFocus;
44868
-
44869
- // Failsafe: abort if ID is different, because focus probably happened
44870
- // before blur.
44871
- if (!Util.inputPathsEqual(blurPath, blurringFocusPath)) {
44872
- return;
44873
- }
44874
-
44875
- // Wait until after any new focus events fire this tick before
44876
- // declaring that nothing is focused, since if there were a focus change
44877
- // across Renderers (e.g., from the HintsRenderer to the
44878
- // QuestionRenderer), we could receive the blur before the focus.
44879
- // TODO(jeff, CP-3128): Use Wonder Blocks Timing API
44880
- // eslint-disable-next-line no-restricted-syntax
44881
- setTimeout(() => {
44882
- if (Util.inputPathsEqual(this._currentFocus, blurringFocusPath)) {
44883
- this._setCurrentFocus(null);
44884
- }
44885
- });
44886
- }
44887
-
44888
- /**
44889
- * Accepts a question area widgetId, or an answer area widgetId of
44890
- * the form "answer-input-number 1", or the string "answer-area"
44891
- * for the whole answer area (if the answer area is a single widget).
44892
- */
44893
- _setWidgetProps(widgetId, newProps, callback) {
44894
- this.questionRenderer._setWidgetProps(widgetId, newProps, callback);
44895
- }
44896
-
44897
- /**
44898
- * Sets the input value into the widget found at `path`. The `focus`
44899
- * function provided is called when all affected widgets have been
44900
- * re-rendered with the new value.
44901
- */
44902
- setInputValue(path, newValue, focus) {
44903
- return this.questionRenderer.setInputValue(path, newValue, focus);
44904
- }
44905
- focusPath(path) {
44906
- return this.questionRenderer.focusPath(path);
44907
- }
44908
- blurPath(path) {
44909
- return this.questionRenderer.blurPath(path);
44910
- }
44911
- getDOMNodeForPath(path) {
44912
- return this.questionRenderer.getDOMNodeForPath(path);
44913
- }
44914
- getGrammarTypeForPath(path) {
44915
- return this.questionRenderer.getGrammarTypeForPath(path);
44916
- }
44917
- getInputPaths() {
44918
- const questionAreaInputPaths = this.questionRenderer.getInputPaths();
44919
- return questionAreaInputPaths;
44920
- }
44921
- focus() {
44922
- return this.questionRenderer.focus();
44923
- }
44924
- blur() {
44925
- if (this._currentFocus) {
44926
- this.blurPath(this._currentFocus);
44927
- }
44928
- }
44929
- showHint() {
44930
- if (this.state.hintsVisible < this.getNumHints()) {
44931
- this.setState({
44932
- hintsVisible: this.state.hintsVisible + 1
44933
- });
44934
- }
44935
- }
44936
- getNumHints() {
44937
- return this.props.item.hints.length;
44938
- }
44939
-
44940
- /**
44941
- * Grades the item.
44942
- *
44943
- * Returns a KE-style score of {
44944
- * empty: bool,
44945
- * correct: bool,
44946
- * message: string|null,
44947
- * guess: Array
44948
- * }
44949
- */
44950
- scoreInput() {
44951
- const guessAndScore = this.questionRenderer.guessAndScore();
44952
- const guess = guessAndScore[0];
44953
- const score = guessAndScore[1];
44954
-
44955
- // Continue to include an empty guess for the now defunct answer area.
44956
- // TODO(alex): Check whether we rely on the format here for
44957
- // analyzing ProblemLogs. If not, remove this layer.
44958
- const maxCompatGuess = [guess, []];
44959
- const keScore = Util.keScoreFromPerseusScore(score, maxCompatGuess, this.questionRenderer.getSerializedState());
44960
- const emptyQuestionAreaWidgets = this.questionRenderer.emptyWidgets();
44961
- this.setState({
44962
- questionCompleted: keScore.correct,
44963
- questionHighlightedWidgets: emptyQuestionAreaWidgets
44964
- });
44965
- return keScore;
44966
- }
44967
-
44968
- /**
44969
- * Returns an array of all widget IDs in the order they occur in
44970
- * the question content.
44971
- */
44972
- getWidgetIds() {
44973
- return this.questionRenderer.getWidgetIds();
44974
- }
44975
-
44976
- /**
44977
- * Returns an object mapping from widget ID to KE-style score.
44978
- * The keys of this object are the values of the array returned
44979
- * from `getWidgetIds`.
44980
- */
44981
- scoreWidgets() {
44982
- const qScore = this.questionRenderer.scoreWidgets();
44983
- const qGuess = this.questionRenderer.getUserInputForWidgets();
44984
- const state = this.questionRenderer.getSerializedState();
44985
- // @ts-expect-error - TS2322 - Type 'Partial<Record<string, KEScore>>' is not assignable to type '{ [id: string]: KEScore; }'. | TS2345 - Argument of type '{ [widgetId: string]: PerseusScore; }' is not assignable to parameter of type 'Partial<Record<string, { type: "invalid"; message?: string | null | undefined; suppressAlmostThere?: boolean | null | undefined; }>>'.
44986
- return mapObject$1(qScore, (score, id) => {
44987
- return Util.keScoreFromPerseusScore(score, qGuess[id], state[id]);
44988
- });
44989
- }
44990
-
44991
- /**
44992
- * Get a representation of the current state of the item.
44993
- */
44994
- getSerializedState() {
44995
- var _this$hintsRenderer;
44996
- return {
44997
- question: this.questionRenderer.getSerializedState(),
44998
- hints: (_this$hintsRenderer = this.hintsRenderer) == null ? void 0 : _this$hintsRenderer.getSerializedState()
44999
- };
45000
- }
45001
-
45002
- // TODO(kevinb): make the type for this state better
45003
- restoreSerializedState(state, callback) {
45004
- var _this$hintsRenderer2;
45005
- // We need to wait for both the question renderer and the hints
45006
- // renderer to finish restoring their states.
45007
- let numCallbacks = 2;
45008
- const fireCallback = () => {
45009
- --numCallbacks;
45010
- if (callback && numCallbacks === 0) {
45011
- callback();
45012
- }
45013
- };
45014
- this.questionRenderer.restoreSerializedState(state.question, fireCallback);
45015
- (_this$hintsRenderer2 = this.hintsRenderer) == null || _this$hintsRenderer2.restoreSerializedState(state.hints, fireCallback);
45016
- }
45017
- showRationalesForCurrentlySelectedChoices() {
45018
- this.questionRenderer.showRationalesForCurrentlySelectedChoices();
45019
- }
45020
- deselectIncorrectSelectedChoices() {
45021
- this.questionRenderer.deselectIncorrectSelectedChoices();
45022
- }
45023
- render() {
45024
- return /*#__PURE__*/React.createElement(DependenciesContext.Provider, {
45025
- value: this.props.dependencies
45026
- }, /*#__PURE__*/React.createElement("div", null));
45027
- }
45028
- }
45029
- ItemRenderer.defaultProps = {
45030
- apiOptions: ApiOptions.defaults,
45031
- // defaults are set in `this.update()`
45032
- controlPeripherals: true,
45033
- hintsAreaSelector: "#hintsarea",
45034
- initialHintsVisible: 0,
45035
- workAreaSelector: "#workarea",
45036
- reviewMode: false,
45037
- linterContext: PerseusLinter.linterContextDefault
45038
- };
45039
-
45040
44591
  /**
45041
44592
  * A React context for signaling when all of the components in a Perseus
45042
44593
  * renderer is fully rendered. This means that all images and math have
@@ -45102,6 +44653,8 @@ class ServerItemRenderer extends React.Component {
45102
44653
  this._currentFocus = null;
45103
44654
  this._fullyRendered = false;
45104
44655
  }
44656
+
44657
+ // eslint-disable-next-line react/no-unsafe
45105
44658
  UNSAFE_componentWillReceiveProps(nextProps) {
45106
44659
  this.setState({
45107
44660
  questionHighlightedWidgets: []
@@ -47308,5 +46861,5 @@ const EditorJsonify = {
47308
46861
  }
47309
46862
  };
47310
46863
 
47311
- export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemRenderer, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Transformer$1 as TransformerWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, displaySigFigs, globalConstants, globalStyles, iconPaths as icons, init, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, traverse, violatingWidgets, allWidgets as widgets };
46864
+ export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Errors, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusError, PerseusExpressionAnswerFormConsidered, PerseusMarkdown, Plotter$1 as PlotterWidget, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Transformer$1 as TransformerWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, displaySigFigs, globalConstants, globalStyles, iconPaths as icons, init, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, mediaQueries, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, traverse, violatingWidgets, allWidgets as widgets };
47312
46865
  //# sourceMappingURL=index.js.map