@khanacademy/perseus 5.0.0 → 6.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.
Files changed (115) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/dist/components/fixed-to-responsive.d.ts +27 -1
  3. package/dist/components/fixed-to-responsive.js.flow +27 -1
  4. package/dist/components/graph.d.ts +42 -26
  5. package/dist/components/graph.js.flow +42 -28
  6. package/dist/components/graphie.d.ts +34 -22
  7. package/dist/components/graphie.js.flow +37 -24
  8. package/dist/components/hud.d.ts +8 -14
  9. package/dist/components/hud.js.flow +7 -14
  10. package/dist/components/inline-icon.d.ts +9 -19
  11. package/dist/components/inline-icon.js.flow +26 -13
  12. package/dist/components/lint.d.ts +41 -13
  13. package/dist/components/lint.js.flow +42 -13
  14. package/dist/components/math-output.d.ts +15 -11
  15. package/dist/components/math-output.js.flow +15 -11
  16. package/dist/components/sortable.d.ts +17 -9
  17. package/dist/components/sortable.js.flow +22 -9
  18. package/dist/components/svg-image.d.ts +5 -3
  19. package/dist/components/svg-image.js.flow +5 -3
  20. package/dist/components/tooltip.d.ts +45 -4
  21. package/dist/components/tooltip.js.flow +44 -31
  22. package/dist/es/index.css +0 -3
  23. package/dist/es/index.js +478 -649
  24. package/dist/es/index.js.map +1 -1
  25. package/dist/index.css +0 -3
  26. package/dist/index.js +482 -646
  27. package/dist/index.js.map +1 -1
  28. package/dist/perseus-types.d.ts +11 -9
  29. package/dist/perseus-types.js.flow +11 -9
  30. package/dist/renderer.d.ts +2 -2
  31. package/dist/renderer.js.flow +1 -3
  32. package/dist/styles/shared.d.ts +0 -1
  33. package/dist/styles/shared.js.flow +0 -1
  34. package/dist/types.d.ts +0 -10
  35. package/dist/types.js.flow +0 -13
  36. package/dist/util/a11y.d.ts +0 -1
  37. package/dist/util/a11y.js.flow +0 -1
  38. package/dist/util/interactive.d.ts +2 -0
  39. package/dist/util/interactive.js.flow +2 -0
  40. package/dist/util.d.ts +3 -3
  41. package/dist/util.js.flow +4 -3
  42. package/dist/widgets/grapher.d.ts +1 -1
  43. package/dist/widgets/grapher.js.flow +12 -3
  44. package/dist/widgets/matcher.d.ts +7 -3
  45. package/dist/widgets/matcher.js.flow +7 -3
  46. package/dist/widgets/number-line.d.ts +1 -1
  47. package/dist/widgets/number-line.js.flow +1 -1
  48. package/package.json +2 -2
  49. package/src/__tests__/__snapshots__/item-renderer.test.tsx.snap +151 -159
  50. package/src/__tests__/__snapshots__/renderer.test.tsx.snap +358 -394
  51. package/src/__tests__/__snapshots__/server-item-renderer.test.tsx.snap +169 -189
  52. package/src/components/__stories__/lint.stories.tsx +56 -21
  53. package/src/components/__stories__/sortable.stories.tsx +5 -5
  54. package/src/components/__stories__/tooltip.stories.tsx +52 -0
  55. package/src/components/__tests__/__snapshots__/sortable.test.tsx.snap +114 -30
  56. package/src/components/__tests__/__snapshots__/sorter.test.tsx.snap +172 -204
  57. package/src/components/__tests__/sortable.test.tsx +54 -16
  58. package/src/components/__tests__/sorter.test.tsx +12 -9
  59. package/src/components/fixed-to-responsive.tsx +43 -38
  60. package/src/components/graph.tsx +95 -81
  61. package/src/components/graphie.tsx +62 -32
  62. package/src/components/hud.tsx +104 -115
  63. package/src/components/inline-icon.tsx +15 -32
  64. package/src/components/input-with-examples.tsx +4 -4
  65. package/src/components/lint.tsx +47 -41
  66. package/src/components/math-output.tsx +17 -11
  67. package/src/components/simple-keypad-input.tsx +2 -2
  68. package/src/components/sortable.tsx +134 -94
  69. package/src/components/svg-image.tsx +10 -11
  70. package/src/components/tooltip.tsx +189 -171
  71. package/src/interactive2/types.ts +2 -2
  72. package/src/mixins/provide-keypad.tsx +2 -2
  73. package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +708 -764
  74. package/src/perseus-types.ts +13 -9
  75. package/src/renderer.tsx +11 -22
  76. package/src/types.ts +0 -18
  77. package/src/util/interactive.ts +18 -5
  78. package/src/util/tex.ts +13 -77
  79. package/src/util.ts +3 -4
  80. package/src/widgets/__stories__/test-keypad-context-wrapper.tsx +2 -2
  81. package/src/widgets/__tests__/__snapshots__/categorizer.test.ts.snap +717 -773
  82. package/src/widgets/__tests__/__snapshots__/cs-program.test.ts.snap +28 -36
  83. package/src/widgets/__tests__/__snapshots__/definition.test.ts.snap +52 -60
  84. package/src/widgets/__tests__/__snapshots__/dropdown.test.ts.snap +100 -108
  85. package/src/widgets/__tests__/__snapshots__/explanation.test.ts.snap +212 -260
  86. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt.test.ts.snap +613 -641
  87. package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +249 -261
  88. package/src/widgets/__tests__/__snapshots__/graded-group.test.ts.snap +671 -735
  89. package/src/widgets/__tests__/__snapshots__/grapher.test.ts.snap +2676 -2684
  90. package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +938 -1002
  91. package/src/widgets/__tests__/__snapshots__/iframe.test.ts.snap +28 -36
  92. package/src/widgets/__tests__/__snapshots__/image.test.ts.snap +227 -255
  93. package/src/widgets/__tests__/__snapshots__/input-number.test.ts.snap +353 -357
  94. package/src/widgets/__tests__/__snapshots__/interaction.test.ts.snap +255 -259
  95. package/src/widgets/__tests__/__snapshots__/interactive-graph.test.ts.snap +2401 -2433
  96. package/src/widgets/__tests__/__snapshots__/matcher.test.tsx.snap +600 -756
  97. package/src/widgets/__tests__/__snapshots__/matrix.test.ts.snap +420 -428
  98. package/src/widgets/__tests__/__snapshots__/number-line.test.ts.snap +762 -770
  99. package/src/widgets/__tests__/__snapshots__/numeric-input.test.ts.snap +373 -393
  100. package/src/widgets/__tests__/__snapshots__/orderer.test.ts.snap +116 -148
  101. package/src/widgets/__tests__/__snapshots__/passage-ref.test.ts.snap +289 -301
  102. package/src/widgets/__tests__/__snapshots__/passage.test.tsx.snap +1679 -1711
  103. package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +2692 -2960
  104. package/src/widgets/__tests__/__snapshots__/video.test.ts.snap +90 -98
  105. package/src/widgets/__tests__/matcher.test.tsx +33 -9
  106. package/src/widgets/expression.tsx +9 -6
  107. package/src/widgets/grapher.tsx +3 -3
  108. package/src/widgets/image.tsx +2 -2
  109. package/src/widgets/interactive-graph.tsx +5 -5
  110. package/src/widgets/matcher.tsx +43 -24
  111. package/src/widgets/number-line.tsx +1 -1
  112. package/src/widgets/simulator.tsx +4 -6
  113. package/{tsconfig.json → tsconfig-build.json} +6 -6
  114. package/tsconfig-build.tsbuildinfo +1 -0
  115. package/tsconfig.tsbuildinfo +0 -1
@@ -3,6 +3,9 @@
3
3
 
4
4
  import type {Coord} from "./interactive2/types";
5
5
 
6
+ export type Range = [number, number];
7
+ export type Size = [number, number];
8
+
6
9
  // TODO(FEI-5054): Figure out how to get global .d.ts files working with monorepos
7
10
  type Empty = Record<string, never>;
8
11
 
@@ -218,13 +221,14 @@ export type PerseusWidget =
218
221
  | UnitInputWidget
219
222
  | VideoWidget;
220
223
 
224
+ // A background image applied to various widgets.
221
225
  export type PerseusImageBackground = {
222
226
  // The URL of the image
223
227
  url: string | null | undefined;
224
228
  // The width of the image
225
- width: number;
229
+ width?: number;
226
230
  // The height of the image
227
- height: number;
231
+ height?: number;
228
232
  // The top offset of the image
229
233
  // NOTE: perseus_data.go says this is required, but nullable, even though
230
234
  // it isn't necessary at all.
@@ -475,10 +479,10 @@ export type PerseusImageWidgetOptions = {
475
479
  labels?: ReadonlyArray<PerseusImageLabel>;
476
480
  // The range on the image render for labels
477
481
  // NOTE: perseus_data.go says this is required even though it isn't necessary.
478
- range?: ReadonlyArray<ReadonlyArray<number>>;
482
+ range?: [Range, Range];
479
483
  // The box on the image render for labels. The same as backgroundImage.width and backgroundImage.height
480
484
  // NOTE: perseus_data.go says this is required even though it isn't necessary.
481
- box?: ReadonlyArray<number>;
485
+ box?: Size;
482
486
  };
483
487
 
484
488
  export type PerseusImageLabel = {
@@ -1134,17 +1138,17 @@ export type PerseusInteractionGraph = {
1134
1138
  // "canvas", "graph"
1135
1139
  editableSettings?: ReadonlyArray<"canvas" | "graph">;
1136
1140
  // The Grid Canvas size. e.g. [400, 140]
1137
- box: ReadonlyArray<number>;
1141
+ box: Size;
1138
1142
  // The Axis labels. e.g. ["x", "y"]
1139
1143
  labels: ReadonlyArray<string>;
1140
1144
  // The Axis ranges. e.g. [[-10, 10], [-10, 10]]
1141
- range: ReadonlyArray<ReadonlyArray<number>>;
1145
+ range: [Range, Range];
1142
1146
  // The steps in the grid. default [1, 1]
1143
- gridStep: ReadonlyArray<number>;
1147
+ gridStep: Readonly<[number, number]>;
1144
1148
  // What to show on the graph. "graph", "grid", or "none"
1145
1149
  markings: string;
1146
1150
  // The snap steps. default [0.5, 0.5]
1147
- snapStep?: ReadonlyArray<number>;
1151
+ snapStep?: Readonly<[number, number]>;
1148
1152
  // Whether the grid is valid or not. Do the numbers all make sense?
1149
1153
  // NOTE(jeremy) The editor for this widget sometimes stores the graph
1150
1154
  // editor validation error message into this field. It seems innocuous
@@ -1162,7 +1166,7 @@ export type PerseusInteractionGraph = {
1162
1166
  // How many ticks to show on the ruler. e.g. 1, 2, 4, 8, 10, 16
1163
1167
  rulerTicks?: number;
1164
1168
  // This controls the number (and position) of the tick marks for the X and Y axis. e.g. [1, 1]
1165
- tickStep: ReadonlyArray<number>;
1169
+ tickStep: Readonly<[number, number]>;
1166
1170
  };
1167
1171
 
1168
1172
  export type PerseusInteractionElement =
package/src/renderer.tsx CHANGED
@@ -35,7 +35,6 @@ import type {
35
35
  FilterCriterion,
36
36
  FocusPath,
37
37
  LinterContextProps,
38
- PerseusDependencies,
39
38
  PerseusScore,
40
39
  WidgetInfo,
41
40
  WidgetProps,
@@ -223,10 +222,7 @@ class Renderer extends React.Component<Props, State> {
223
222
  // The i18n linter.
224
223
  _translationLinter: TranslationLinter;
225
224
 
226
- // @ts-expect-error [FEI-5003] - TS2564 - Property 'lastRenderedMarkdown' has no initializer and is not definitely assigned in the constructor.
227
- lastRenderedMarkdown: React.ReactElement<
228
- React.ComponentProps<PerseusDependencies["KatexProvider"]>
229
- >;
225
+ lastRenderedMarkdown: React.ReactNode;
230
226
  // @ts-expect-error [FEI-5003] - TS2564 - Property 'reuseMarkdown' has no initializer and is not definitely assigned in the constructor.
231
227
  reuseMarkdown: boolean;
232
228
  // @ts-expect-error [FEI-5003] - TS2564 - Property 'translationIndex' has no initializer and is not definitely assigned in the constructor.
@@ -1848,7 +1844,6 @@ class Renderer extends React.Component<Props, State> {
1848
1844
 
1849
1845
  render(): React.ReactNode {
1850
1846
  const apiOptions = this.getApiOptions();
1851
- const {KatexProvider} = getDependencies();
1852
1847
 
1853
1848
  if (this.reuseMarkdown) {
1854
1849
  return this.lastRenderedMarkdown;
@@ -1896,17 +1891,13 @@ class Renderer extends React.Component<Props, State> {
1896
1891
  // calls its before_dom_insert we can lookup this component by
1897
1892
  // this attribute and render the text with markdown.
1898
1893
  return (
1899
- <KatexProvider>
1900
- <DefinitionProvider>
1901
- <div
1902
- data-perseus-component-index={
1903
- this.translationIndex
1904
- }
1905
- >
1906
- {content}
1907
- </div>
1908
- </DefinitionProvider>
1909
- </KatexProvider>
1894
+ <DefinitionProvider>
1895
+ <div
1896
+ data-perseus-component-index={this.translationIndex}
1897
+ >
1898
+ {content}
1899
+ </div>
1900
+ </DefinitionProvider>
1910
1901
  );
1911
1902
  }
1912
1903
  }
@@ -1963,11 +1954,9 @@ class Renderer extends React.Component<Props, State> {
1963
1954
  });
1964
1955
 
1965
1956
  this.lastRenderedMarkdown = (
1966
- <KatexProvider>
1967
- <DefinitionProvider>
1968
- <div className={className}>{markdownContents}</div>
1969
- </DefinitionProvider>
1970
- </KatexProvider>
1957
+ <DefinitionProvider>
1958
+ <div className={className}>{markdownContents}</div>
1959
+ </DefinitionProvider>
1971
1960
  );
1972
1961
  return this.lastRenderedMarkdown;
1973
1962
  }
package/src/types.ts CHANGED
@@ -1,7 +1,3 @@
1
- // eslint-disable-next-line import/no-unresolved
2
- import katex from "katex";
3
- // eslint-disable-next-line import/no-unresolved
4
- import katexA11y from "katex/dist/contrib/render-a11y-string";
5
1
  import * as React from "react";
6
2
 
7
3
  import type {SerializedHighlightSet} from "./components/highlighting/types";
@@ -309,20 +305,6 @@ export type PerseusDependencies = {
309
305
  graphieMovablesJiptLabels: JiptLabelStore;
310
306
  svgImageJiptLabels: JiptLabelStore;
311
307
  rendererTranslationComponents: JiptTranslationComponents;
312
- // KaTeX related dependencies
313
- getKaTeX: () => Promise<katex>;
314
- getRenderA11yString: () => Promise<katexA11y>;
315
- loadMathjax: () => Promise<unknown>;
316
- logKaTeXError: (
317
- expression: string,
318
- error: Error,
319
- ) => Promise<Response | null | undefined>;
320
- KatexProvider: React.ComponentType<{
321
- children: React.ReactNode;
322
- }>;
323
- // TODO(kevinb): Update `getKaTeX` to return the version of `katex` that
324
- // webapp (or mobile) wants perseus to use.
325
- shouldUseFutureKaTeX: (flag: boolean) => void;
326
308
  TeX: React.ComponentType<TeXProps>;
327
309
  //misc
328
310
  staticUrl: StaticUrlFn;
@@ -36,6 +36,8 @@ import KhanMath from "./math";
36
36
 
37
37
  import type {Coord} from "../interactive2/types";
38
38
 
39
+ export type MouseHandler = (position: Coord) => void;
40
+
39
41
  const {getCanUse3dTransform} = InteractiveUtil;
40
42
 
41
43
  function sum(array: any) {
@@ -109,7 +111,15 @@ _.extend(GraphUtils.Graphie.prototype, {
109
111
  // in the way of mouse events. This adds another SVG element on top
110
112
  // of everything else where we can add invisible shapes with mouse
111
113
  // handlers wherever we want.
112
- addMouseLayer: function (options) {
114
+ addMouseLayer: function (options: {
115
+ onClick?: MouseHandler;
116
+ onMouseMove?: MouseHandler;
117
+ onMouseDown?: MouseHandler;
118
+ onMouseOver?: MouseHandler;
119
+ onMouseOut?: MouseHandler;
120
+ allowScratchpad?: boolean;
121
+ setDrawingAreaAvailable?: (available: boolean) => void;
122
+ }) {
113
123
  const graph = this;
114
124
  options = _.extend(
115
125
  {
@@ -149,10 +159,11 @@ _.extend(GraphUtils.Graphie.prototype, {
149
159
  isClickingCanvas = true;
150
160
 
151
161
  if (options.onMouseMove) {
162
+ const handler = options.onMouseMove;
152
163
  $(document).bind("vmousemove.mouseLayer", function (e) {
153
164
  if (isClickingCanvas) {
154
165
  e.preventDefault();
155
- options.onMouseMove(graph.getMouseCoord(e));
166
+ handler(graph.getMouseCoord(e));
156
167
  }
157
168
  });
158
169
  }
@@ -170,18 +181,20 @@ _.extend(GraphUtils.Graphie.prototype, {
170
181
  }
171
182
  });
172
183
  if (options.onMouseOver) {
184
+ const handler = options.onMouseOver;
173
185
  $(graph.mouselayer.canvas).on("vmouseover", function (e) {
174
- options.onMouseOver(graph.getMouseCoord(e));
186
+ handler(graph.getMouseCoord(e));
175
187
  });
176
188
  }
177
189
  if (options.onMouseOut) {
190
+ const handler = options.onMouseOut;
178
191
  $(graph.mouselayer.canvas).on("vmouseout", function (e) {
179
- options.onMouseOut(graph.getMouseCoord(e));
192
+ handler(graph.getMouseCoord(e));
180
193
  });
181
194
  }
182
195
  }
183
196
  if (!options.allowScratchpad) {
184
- options.setDrawingAreaAvailable(false);
197
+ options.setDrawingAreaAvailable?.(false);
185
198
  }
186
199
 
187
200
  // Add mouse and visible wrapper layers for DOM-node-wrapped movables
package/src/util/tex.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import $ from "jquery";
2
+ import * as React from "react";
2
3
 
3
4
  import {getDependencies} from "../dependencies";
4
5
 
5
6
  import KhanMath from "./math";
7
+ import reactRender from "./react-render";
6
8
 
7
9
  declare const MathJax: any;
8
10
 
@@ -94,84 +96,18 @@ export default {
94
96
  // Store the formula that we're using
95
97
  $elem.attr("data-math-formula", text);
96
98
 
97
- const katex = await getDependencies().getKaTeX();
98
- // Try to process the nodes with KaTeX first
99
- try {
100
- // Don't use the `trust: true` setting with KaTeX. There is a
101
- // security issue with it as outlined in https://hackerone.com/reports/844216.
102
- // See https://khanacademy.atlassian.net/browse/FEI-2225 for
103
- // additional context.
104
- katex.render(text, $katexHolder[0], {colorIsTextColor: true});
105
- // If that worked, and we previously formatted with
106
- // mathjax, do some mathjax cleanup
107
- if ($elem.attr("data-math-type") === "mathjax") {
108
- // Remove the old mathjax stuff
109
- if (typeof MathJax !== "undefined") {
110
- const jax = MathJax.Hub.getJaxFor(script);
111
- if (jax) {
112
- const e = jax.SourceElement();
113
- if (
114
- e.previousSibling &&
115
- e.previousSibling.className
116
- ) {
117
- jax.Remove();
118
- }
119
- }
99
+ const {TeX} = await getDependencies();
100
+ // We use createElement instead of JSX here because we can't name this file tex.tsx;
101
+ // that name is already taken.
102
+ reactRender(
103
+ React.createElement(TeX, {children: text}),
104
+ $katexHolder[0],
105
+ () => {
106
+ if (callback) {
107
+ doCallback(elem, callback);
120
108
  }
121
- }
122
- $elem.attr("data-math-type", "katex");
123
- // Call the callback
124
- if (callback) {
125
- doCallback(elem, callback);
126
- }
127
- return;
128
- } catch (err: any) {
129
- getDependencies().logKaTeXError(text, err);
130
-
131
- if (err instanceof katex.ParseError) {
132
- throw err;
133
- }
134
- }
135
-
136
- // Otherwise, fallback to MathJax
137
-
138
- // (Note: we don't need to do any katex cleanup here, because
139
- // KaTeX is smart and cleans itself up)
140
- $elem.attr("data-math-type", "mathjax");
141
- // Update the script tag, or add one if necessary
142
- if (!script) {
143
- $mathjaxHolder.append(
144
- "<script type='math/tex'>" +
145
- text.replace(/<\//g, "< /") +
146
- "</script>",
147
- );
148
- } else {
149
- if ("text" in script) {
150
- // IE8, etc
151
- script.text = text;
152
- } else {
153
- script.textContent = text;
154
- }
155
- }
156
- if (typeof MathJax !== "undefined") {
157
- // Put the process, a debug log, and the callback into the
158
- // MathJax queue
159
- MathJax.Hub.Queue([
160
- "Reprocess",
161
- MathJax.Hub,
162
- $mathjaxHolder[0],
163
- ]);
164
- if (callback) {
165
- MathJax.Hub.Queue(function () {
166
- const cb = MathJax.Callback(function () {});
167
- doCallback(elem, function () {
168
- callback();
169
- cb();
170
- });
171
- return cb;
172
- });
173
- }
174
- }
109
+ },
110
+ );
175
111
  }
176
112
  },
177
113
 
package/src/util.ts CHANGED
@@ -5,6 +5,7 @@ import {Errors} from "./logging/log";
5
5
  import {PerseusError} from "./perseus-error";
6
6
  import KhanAnswerTypes from "./util/answer-types";
7
7
 
8
+ import type {Range} from "./perseus-types";
8
9
  import type {Widget, PerseusScore, KEScore} from "./types";
9
10
 
10
11
  type WordPosition = {
@@ -37,7 +38,7 @@ type QueryParams = {
37
38
  [param: string]: string;
38
39
  };
39
40
 
40
- type Position = {
41
+ export type Position = {
41
42
  top: number;
42
43
  left: number;
43
44
  };
@@ -495,8 +496,6 @@ function tickStepFromNumTicks(span: number, numTicks: number): number {
495
496
  return step;
496
497
  }
497
498
 
498
- type Range = [number, number]; // [min, max]
499
-
500
499
  const constrainTickStep = (step: number, range: Range): number => {
501
500
  const span = range[1] - range[0];
502
501
  const numTicks = span / step;
@@ -519,7 +518,7 @@ const constrainTickStep = (step: number, range: Range): number => {
519
518
  * Specifically, we aim for 10 or fewer ticks per graph axis.
520
519
  */
521
520
  function constrainedTickStepsFromTickSteps(
522
- tickSteps: Coordinates,
521
+ tickSteps: [number, number],
523
522
  ranges: [Range, Range],
524
523
  ): Coordinates {
525
524
  return [
@@ -1,4 +1,4 @@
1
- import {Keypad} from "@khanacademy/math-input";
1
+ import {LegacyKeypad} from "@khanacademy/math-input";
2
2
  import {View} from "@khanacademy/wonder-blocks-core";
3
3
  import {StyleSheet} from "aphrodite";
4
4
  import * as React from "react";
@@ -10,7 +10,7 @@ const Footer = (): React.ReactElement => {
10
10
  <View style={styles.keypadContainer}>
11
11
  <KeypadContext.Consumer>
12
12
  {({keypadElement, setKeypadElement, renderer}) => (
13
- <Keypad
13
+ <LegacyKeypad
14
14
  onElementMounted={setKeypadElement}
15
15
  onDismiss={() => renderer && renderer.blur()}
16
16
  style={styles.keypad}