@khanacademy/perseus 7.2.1 → 9.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 (390) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/article-renderer.d.ts +27 -40
  3. package/dist/components/math-input.d.ts +1 -1
  4. package/dist/components/sortable.d.ts +1 -1
  5. package/dist/components/tex-buttons.d.ts +1 -1
  6. package/dist/dependencies.d.ts +7 -1
  7. package/dist/es/index.js +1205 -1218
  8. package/dist/es/index.js.map +1 -1
  9. package/dist/hints-renderer.d.ts +1 -1
  10. package/dist/index.js +1207 -1221
  11. package/dist/index.js.map +1 -1
  12. package/dist/interaction-tracker.d.ts +3 -3
  13. package/dist/item-renderer.d.ts +2 -1
  14. package/dist/mixins/provide-keypad.d.ts +15 -12
  15. package/dist/multi-items/multi-renderer.d.ts +2 -1
  16. package/dist/perseus-api.d.ts +2 -2
  17. package/dist/renderer.d.ts +1 -1
  18. package/dist/server-item-renderer.d.ts +3 -1
  19. package/dist/types.d.ts +32 -11
  20. package/dist/widgets/expression.d.ts +410 -13
  21. package/dist/widgets/graded-group.d.ts +3 -3
  22. package/dist/widgets/interactive-graph.d.ts +1 -1
  23. package/dist/widgets/sequence.d.ts +3 -3
  24. package/dist/widgets/table.d.ts +8 -1
  25. package/dist/widgets.d.ts +1 -1
  26. package/package.json +34 -34
  27. package/src/__stories__/article-renderer.stories.tsx +35 -2
  28. package/src/__testdata__/article-renderer.testdata.ts +74 -1
  29. package/src/__testdata__/item-renderer.testdata.ts +1 -1
  30. package/src/__testdata__/renderer.testdata.ts +1 -1
  31. package/src/__tests__/item-renderer.test.tsx +13 -2
  32. package/src/__tests__/mock-asset-loading-widget.tsx +1 -1
  33. package/src/__tests__/perseus-markdown.test.ts +17 -17
  34. package/src/__tests__/renderer-api.test.tsx +21 -21
  35. package/src/__tests__/renderer.test.tsx +13 -13
  36. package/src/__tests__/server-item-renderer.test.tsx +12 -4
  37. package/src/__tests__/tex-wrangler.test.ts +1 -1
  38. package/src/article-renderer.tsx +75 -66
  39. package/src/components/__tests__/number-input.test.tsx +4 -4
  40. package/src/components/__tests__/sortable.test.tsx +2 -2
  41. package/src/components/fixed-to-responsive.tsx +1 -1
  42. package/src/components/graph.tsx +11 -11
  43. package/src/components/graphie-classes.ts +7 -7
  44. package/src/components/graphie.tsx +14 -14
  45. package/src/components/highlighting/highlightable-content.tsx +1 -1
  46. package/src/components/highlighting/ui/highlight-renderer.tsx +1 -1
  47. package/src/components/highlighting/ui/util.ts +2 -2
  48. package/src/components/highlighting/word-indexer.tsx +4 -4
  49. package/src/components/icon.tsx +1 -1
  50. package/src/components/image-loader.tsx +4 -4
  51. package/src/components/input-with-examples.tsx +2 -2
  52. package/src/components/lint.tsx +1 -1
  53. package/src/components/math-input.tsx +5 -5
  54. package/src/components/math-output.tsx +1 -1
  55. package/src/components/multi-button-group.tsx +1 -1
  56. package/src/components/number-input.tsx +10 -10
  57. package/src/components/prop-check-box.tsx +1 -1
  58. package/src/components/simple-keypad-input.tsx +4 -4
  59. package/src/components/sortable.tsx +42 -41
  60. package/src/components/svg-image.tsx +12 -12
  61. package/src/components/tex-buttons.tsx +4 -3
  62. package/src/components/text-input.tsx +8 -8
  63. package/src/components/text-list-editor.tsx +11 -11
  64. package/src/components/visibility-observer/create-visibility-observer.ts +1 -1
  65. package/src/components/visibility-observer/visibility-observer.tsx +2 -2
  66. package/src/components/zoomable-tex.tsx +3 -3
  67. package/src/components/zoomable.tsx +8 -8
  68. package/src/dependencies.ts +16 -1
  69. package/src/hint-renderer.tsx +3 -3
  70. package/src/hints-renderer.tsx +11 -11
  71. package/src/init.ts +1 -1
  72. package/src/interaction-tracker.ts +7 -7
  73. package/src/interactive2/__tests__/movable-point.test.ts +1 -1
  74. package/src/interactive2/interactive-util.ts +6 -6
  75. package/src/interactive2/movable-helper-methods.ts +2 -2
  76. package/src/interactive2/movable-line-options.ts +37 -37
  77. package/src/interactive2/movable-line.ts +2 -2
  78. package/src/interactive2/movable-point-options.ts +29 -29
  79. package/src/interactive2/movable-point.tsx +2 -2
  80. package/src/interactive2/movable-polygon-options.ts +45 -45
  81. package/src/interactive2/movable-polygon.ts +2 -2
  82. package/src/interactive2/movable.ts +2 -2
  83. package/src/interactive2/objective_.ts +1 -1
  84. package/src/interactive2/wrapped-defaults.ts +2 -2
  85. package/src/interactive2/wrapped-ellipse.ts +9 -9
  86. package/src/interactive2/wrapped-line.ts +8 -8
  87. package/src/interactive2/wrapped-path.ts +5 -5
  88. package/src/item-renderer.tsx +66 -48
  89. package/src/mixins/changeable.ts +2 -2
  90. package/src/mixins/editor-jsonify.ts +1 -1
  91. package/src/mixins/provide-keypad.tsx +23 -37
  92. package/src/mixins/widget-jsonify-deprecated.ts +1 -1
  93. package/src/multi-items/__tests__/items.test.ts +1 -1
  94. package/src/multi-items/__tests__/multi-renderer.test.tsx +13 -8
  95. package/src/multi-items/__tests__/trees.test.ts +12 -12
  96. package/src/multi-items/item-types.ts +2 -2
  97. package/src/multi-items/items.ts +1 -1
  98. package/src/multi-items/multi-renderer.tsx +30 -18
  99. package/src/multi-items/prop-type-builders.ts +2 -2
  100. package/src/multi-items/tree-types.ts +4 -4
  101. package/src/multi-items/trees.ts +16 -16
  102. package/src/perseus-env.ts +2 -2
  103. package/src/perseus-markdown.tsx +5 -5
  104. package/src/renderer.tsx +39 -39
  105. package/src/server-item-renderer.tsx +26 -21
  106. package/src/sigfigs.ts +1 -1
  107. package/src/traversal.ts +1 -1
  108. package/src/types.ts +52 -16
  109. package/src/util/answer-types.ts +14 -14
  110. package/src/util/geometry.ts +7 -7
  111. package/src/util/graphie.ts +32 -32
  112. package/src/util/interactive.ts +118 -118
  113. package/src/util/math.ts +4 -4
  114. package/src/util/react-render.tsx +1 -1
  115. package/src/util/scroll-utils.ts +2 -2
  116. package/src/util/tex.ts +3 -3
  117. package/src/util.ts +15 -15
  118. package/src/widget-container.tsx +1 -1
  119. package/src/widgets/__stories__/definition.stories.tsx +8 -1
  120. package/src/widgets/__stories__/expression.stories.tsx +2 -2
  121. package/src/widgets/__stories__/test-keypad-context-wrapper.tsx +3 -3
  122. package/src/widgets/__testdata__/numeric-input.testdata.ts +1 -1
  123. package/src/widgets/__tests__/__snapshots__/video.test.ts.snap +2 -2
  124. package/src/widgets/__tests__/{expression.test.ts → expression.test.tsx} +77 -11
  125. package/src/widgets/__tests__/grapher.cypress.ts +32 -32
  126. package/src/widgets/__tests__/group.test.tsx +1 -1
  127. package/src/widgets/__tests__/interactive-graph.cypress.ts +40 -40
  128. package/src/widgets/__tests__/interactive-graph.test.ts +1 -1
  129. package/src/widgets/__tests__/numeric-input.test.ts +1 -1
  130. package/src/widgets/__tests__/passage.test.tsx +2 -2
  131. package/src/widgets/__tests__/plotter.test.tsx +2 -1
  132. package/src/widgets/__tests__/radio/base-radio.test.tsx +7 -7
  133. package/src/widgets/__tests__/renderQuestion.tsx +29 -21
  134. package/src/widgets/categorizer.tsx +4 -4
  135. package/src/widgets/cs-program.tsx +2 -2
  136. package/src/widgets/dropdown.tsx +1 -1
  137. package/src/widgets/expression.tsx +92 -57
  138. package/src/widgets/graded-group-set.tsx +5 -5
  139. package/src/widgets/graded-group.tsx +29 -21
  140. package/src/widgets/grapher/util.tsx +6 -6
  141. package/src/widgets/grapher.tsx +10 -10
  142. package/src/widgets/group.tsx +3 -3
  143. package/src/widgets/iframe.tsx +2 -2
  144. package/src/widgets/image.tsx +1 -1
  145. package/src/widgets/input-number.tsx +4 -4
  146. package/src/widgets/interaction.tsx +77 -77
  147. package/src/widgets/interactive-graph.tsx +109 -107
  148. package/src/widgets/label-image/marker.tsx +2 -2
  149. package/src/widgets/label-image.test.ts +1 -1
  150. package/src/widgets/label-image.tsx +7 -7
  151. package/src/widgets/lights-puzzle.tsx +2 -2
  152. package/src/widgets/matcher.tsx +6 -6
  153. package/src/widgets/matrix.tsx +20 -20
  154. package/src/widgets/measurer.tsx +3 -3
  155. package/src/widgets/molecule/__tests__/layout.test.ts +4 -4
  156. package/src/widgets/molecule/__tests__/parser.test.ts +14 -14
  157. package/src/widgets/molecule/smiles-parser.ts +3 -3
  158. package/src/widgets/molecule.tsx +4 -4
  159. package/src/widgets/number-line.tsx +11 -11
  160. package/src/widgets/numeric-input.tsx +11 -11
  161. package/src/widgets/orderer.tsx +38 -38
  162. package/src/widgets/passage/__tests__/passage-markdown.test.ts +1 -1
  163. package/src/widgets/passage/passage-markdown.tsx +4 -4
  164. package/src/widgets/passage-ref-target.tsx +1 -1
  165. package/src/widgets/passage-ref.tsx +4 -6
  166. package/src/widgets/passage.tsx +11 -11
  167. package/src/widgets/plotter.tsx +9 -9
  168. package/src/widgets/radio/base-radio.tsx +11 -11
  169. package/src/widgets/radio/choice-icon/choice-icon.tsx +2 -2
  170. package/src/widgets/radio/choice.tsx +1 -1
  171. package/src/widgets/radio/option-status.tsx +1 -1
  172. package/src/widgets/radio/widget.tsx +6 -6
  173. package/src/widgets/radio.ts +1 -1
  174. package/src/widgets/reaction-diagram.tsx +8 -8
  175. package/src/widgets/sequence.tsx +12 -4
  176. package/src/widgets/simulator.tsx +14 -14
  177. package/src/widgets/sorter.tsx +4 -4
  178. package/src/widgets/table.tsx +8 -8
  179. package/src/widgets/transformer.tsx +92 -92
  180. package/src/widgets/unit.tsx +17 -17
  181. package/src/widgets/video-transcript-link.test.tsx +3 -3
  182. package/src/widgets/video-transcript-link.tsx +1 -1
  183. package/src/widgets/video.tsx +1 -1
  184. package/src/widgets.ts +5 -5
  185. package/src/zoom.ts +22 -22
  186. package/tsconfig-build.tsbuildinfo +1 -1
  187. package/dist/a11y.js.flow +0 -11
  188. package/dist/all-widgets.js.flow +0 -33
  189. package/dist/article-renderer.js.flow +0 -73
  190. package/dist/asset-context.js.flow +0 -15
  191. package/dist/basic-widgets.js.flow +0 -33
  192. package/dist/components/button-group.js.flow +0 -33
  193. package/dist/components/constants.js.flow +0 -13
  194. package/dist/components/fixed-to-responsive.js.flow +0 -34
  195. package/dist/components/graph.js.flow +0 -66
  196. package/dist/components/graphie-classes.js.flow +0 -19
  197. package/dist/components/graphie-movables.js.flow +0 -22
  198. package/dist/components/graphie.js.flow +0 -69
  199. package/dist/components/highlighting/highlightable-content.js.flow +0 -56
  200. package/dist/components/highlighting/highlights.js.flow +0 -54
  201. package/dist/components/highlighting/ranges.js.flow +0 -60
  202. package/dist/components/highlighting/types.js.flow +0 -70
  203. package/dist/components/highlighting/ui/highlight-renderer.js.flow +0 -43
  204. package/dist/components/highlighting/ui/highlight-set-renderer.js.flow +0 -41
  205. package/dist/components/highlighting/ui/highlight-tooltip.js.flow +0 -22
  206. package/dist/components/highlighting/ui/highlighting-ui.js.flow +0 -30
  207. package/dist/components/highlighting/ui/selection-tracker.js.flow +0 -54
  208. package/dist/components/highlighting/ui/types.js.flow +0 -22
  209. package/dist/components/highlighting/ui/util.js.flow +0 -42
  210. package/dist/components/highlighting/word-indexer.js.flow +0 -21
  211. package/dist/components/hud.js.flow +0 -14
  212. package/dist/components/icon.js.flow +0 -31
  213. package/dist/components/image-loader.js.flow +0 -49
  214. package/dist/components/info-tip/info-tip.js.flow +0 -20
  215. package/dist/components/info-tip.js.flow +0 -18
  216. package/dist/components/inline-icon.js.flow +0 -39
  217. package/dist/components/input-with-examples.js.flow +0 -58
  218. package/dist/components/lint.js.flow +0 -58
  219. package/dist/components/math-input.js.flow +0 -49
  220. package/dist/components/math-output.js.flow +0 -33
  221. package/dist/components/multi-button-group.js.flow +0 -35
  222. package/dist/components/number-input.js.flow +0 -42
  223. package/dist/components/prop-check-box.js.flow +0 -8
  224. package/dist/components/range-input.js.flow +0 -20
  225. package/dist/components/simple-keypad-input.js.flow +0 -16
  226. package/dist/components/sortable.js.flow +0 -91
  227. package/dist/components/stub-tag-editor.js.flow +0 -18
  228. package/dist/components/svg-image.js.flow +0 -85
  229. package/dist/components/tex-buttons.js.flow +0 -26
  230. package/dist/components/tex.js.flow +0 -25
  231. package/dist/components/text-input.js.flow +0 -39
  232. package/dist/components/text-list-editor.js.flow +0 -22
  233. package/dist/components/tooltip.js.flow +0 -51
  234. package/dist/components/visibility-observer/create-visibility-observer.js.flow +0 -23
  235. package/dist/components/visibility-observer/get-scroll-parent.js.flow +0 -7
  236. package/dist/components/visibility-observer/visibility-observer.js.flow +0 -36
  237. package/dist/components/zoomable-tex.js.flow +0 -18
  238. package/dist/components/zoomable.js.flow +0 -70
  239. package/dist/components.js.flow +0 -22
  240. package/dist/definition-context.js.flow +0 -25
  241. package/dist/dependencies.js.flow +0 -9
  242. package/dist/error-boundary.js.flow +0 -19
  243. package/dist/extra-widgets.js.flow +0 -33
  244. package/dist/hint-renderer.js.flow +0 -32
  245. package/dist/hints-renderer.js.flow +0 -41
  246. package/dist/icon-paths.js.flow +0 -123
  247. package/dist/index.js.flow +0 -134
  248. package/dist/init.js.flow +0 -20
  249. package/dist/interaction-tracker.js.flow +0 -35
  250. package/dist/interactive2/interactive-util.js.flow +0 -50
  251. package/dist/interactive2/movable-helper-methods.js.flow +0 -15
  252. package/dist/interactive2/movable-line-options.js.flow +0 -44
  253. package/dist/interactive2/movable-line.js.flow +0 -8
  254. package/dist/interactive2/movable-point-options.js.flow +0 -44
  255. package/dist/interactive2/movable-point.js.flow +0 -8
  256. package/dist/interactive2/movable-polygon-options.js.flow +0 -47
  257. package/dist/interactive2/movable-polygon.js.flow +0 -8
  258. package/dist/interactive2/movable.js.flow +0 -8
  259. package/dist/interactive2/objective_.js.flow +0 -25
  260. package/dist/interactive2/types.js.flow +0 -8
  261. package/dist/interactive2/wrapped-defaults.js.flow +0 -11
  262. package/dist/interactive2/wrapped-ellipse.js.flow +0 -14
  263. package/dist/interactive2/wrapped-line.js.flow +0 -14
  264. package/dist/interactive2/wrapped-path.js.flow +0 -13
  265. package/dist/interactive2.js.flow +0 -16
  266. package/dist/item-renderer.js.flow +0 -132
  267. package/dist/item-version.js.flow +0 -11
  268. package/dist/jipt-paragraphs.js.flow +0 -14
  269. package/dist/loading-context.js.flow +0 -12
  270. package/dist/logging/log.js.flow +0 -62
  271. package/dist/mixins/changeable.js.flow +0 -33
  272. package/dist/mixins/editor-jsonify.js.flow +0 -10
  273. package/dist/mixins/provide-keypad.js.flow +0 -33
  274. package/dist/mixins/widget-jsonify-deprecated.js.flow +0 -11
  275. package/dist/mixins/widget-prop-denylist.js.flow +0 -20
  276. package/dist/multi-items/item-types.js.flow +0 -55
  277. package/dist/multi-items/items.js.flow +0 -66
  278. package/dist/multi-items/multi-renderer.js.flow +0 -213
  279. package/dist/multi-items/prop-type-builders.js.flow +0 -22
  280. package/dist/multi-items/shape-types.js.flow +0 -60
  281. package/dist/multi-items/shapes.js.flow +0 -25
  282. package/dist/multi-items/tree-types.js.flow +0 -45
  283. package/dist/multi-items/trees.js.flow +0 -147
  284. package/dist/multi-items.js.flow +0 -36
  285. package/dist/perseus-api.js.flow +0 -151
  286. package/dist/perseus-env.js.flow +0 -6
  287. package/dist/perseus-error.js.flow +0 -16
  288. package/dist/perseus-markdown.js.flow +0 -20
  289. package/dist/perseus-types.js.flow +0 -1041
  290. package/dist/question-paragraph.js.flow +0 -17
  291. package/dist/renderability.js.flow +0 -19
  292. package/dist/renderer.js.flow +0 -325
  293. package/dist/server-item-renderer.js.flow +0 -132
  294. package/dist/sigfigs.js.flow +0 -41
  295. package/dist/styles/constants.js.flow +0 -62
  296. package/dist/styles/global-constants.js.flow +0 -348
  297. package/dist/styles/global-styles.js.flow +0 -53
  298. package/dist/styles/media-queries.js.flow +0 -35
  299. package/dist/styles/shared.js.flow +0 -8
  300. package/dist/tex-wrangler.js.flow +0 -13
  301. package/dist/translation-linter.js.flow +0 -32
  302. package/dist/traversal.js.flow +0 -26
  303. package/dist/types.js.flow +0 -318
  304. package/dist/util/a11y.js.flow +0 -8
  305. package/dist/util/answer-types.js.flow +0 -49
  306. package/dist/util/colors.js.flow +0 -95
  307. package/dist/util/fix-passage-refs.js.flow +0 -8
  308. package/dist/util/geometry.js.flow +0 -37
  309. package/dist/util/graph-utils.js.flow +0 -9
  310. package/dist/util/graphie.js.flow +0 -8
  311. package/dist/util/interactive.js.flow +0 -11
  312. package/dist/util/katex-preprocess.js.flow +0 -12
  313. package/dist/util/math.js.flow +0 -62
  314. package/dist/util/mobile-native-utils.js.flow +0 -12
  315. package/dist/util/react-render.js.flow +0 -12
  316. package/dist/util/register-all-widgets-for-testing.js.flow +0 -7
  317. package/dist/util/scroll-utils.js.flow +0 -19
  318. package/dist/util/sizing-utils.js.flow +0 -20
  319. package/dist/util/tex.js.flow +0 -17
  320. package/dist/util/url-utils.js.flow +0 -11
  321. package/dist/util.graphie.js.flow +0 -14
  322. package/dist/util.js.flow +0 -319
  323. package/dist/version.js.flow +0 -17
  324. package/dist/widget-container.js.flow +0 -33
  325. package/dist/widgets/categorizer.js.flow +0 -70
  326. package/dist/widgets/cs-program.js.flow +0 -54
  327. package/dist/widgets/definition.js.flow +0 -57
  328. package/dist/widgets/dropdown.js.flow +0 -63
  329. package/dist/widgets/explanation.js.flow +0 -58
  330. package/dist/widgets/expression.js.flow +0 -116
  331. package/dist/widgets/graded-group-answer-bar.js.flow +0 -24
  332. package/dist/widgets/graded-group-set.js.flow +0 -65
  333. package/dist/widgets/graded-group.js.flow +0 -78
  334. package/dist/widgets/grapher/util.js.flow +0 -38
  335. package/dist/widgets/grapher.js.flow +0 -102
  336. package/dist/widgets/group.js.flow +0 -60
  337. package/dist/widgets/iframe.js.flow +0 -53
  338. package/dist/widgets/image.js.flow +0 -73
  339. package/dist/widgets/input-number.js.flow +0 -94
  340. package/dist/widgets/interaction/arrow-picker.js.flow +0 -21
  341. package/dist/widgets/interaction/color-picker.js.flow +0 -23
  342. package/dist/widgets/interaction/dash-picker.js.flow +0 -21
  343. package/dist/widgets/interaction/element-container.js.flow +0 -28
  344. package/dist/widgets/interaction.js.flow +0 -60
  345. package/dist/widgets/interactive-graph.js.flow +0 -239
  346. package/dist/widgets/label-image/answer-choices.js.flow +0 -55
  347. package/dist/widgets/label-image/marker.js.flow +0 -31
  348. package/dist/widgets/label-image/types.js.flow +0 -23
  349. package/dist/widgets/label-image.js.flow +0 -142
  350. package/dist/widgets/lights-puzzle.js.flow +0 -58
  351. package/dist/widgets/matcher.js.flow +0 -106
  352. package/dist/widgets/matrix.js.flow +0 -80
  353. package/dist/widgets/measurer.js.flow +0 -30
  354. package/dist/widgets/molecule/molecule-drawing.js.flow +0 -11
  355. package/dist/widgets/molecule/molecule-layout.js.flow +0 -96
  356. package/dist/widgets/molecule/smiles-parser.js.flow +0 -26
  357. package/dist/widgets/molecule.js.flow +0 -60
  358. package/dist/widgets/number-line.js.flow +0 -102
  359. package/dist/widgets/numeric-input.js.flow +0 -105
  360. package/dist/widgets/orderer.js.flow +0 -81
  361. package/dist/widgets/passage/passage-markdown.js.flow +0 -33
  362. package/dist/widgets/passage-ref-target.js.flow +0 -52
  363. package/dist/widgets/passage-ref.js.flow +0 -85
  364. package/dist/widgets/passage.js.flow +0 -157
  365. package/dist/widgets/plotter.js.flow +0 -112
  366. package/dist/widgets/radio/base-radio.js.flow +0 -50
  367. package/dist/widgets/radio/choice-a11y-text.js.flow +0 -14
  368. package/dist/widgets/radio/choice-icon/choice-icon.js.flow +0 -22
  369. package/dist/widgets/radio/choice-icon/cross-out-line.js.flow +0 -16
  370. package/dist/widgets/radio/choice-icon/shared-styles.js.flow +0 -11
  371. package/dist/widgets/radio/choice-none-above.js.flow +0 -16
  372. package/dist/widgets/radio/choice.js.flow +0 -29
  373. package/dist/widgets/radio/choice.jsx.fixture.js.flow +0 -7
  374. package/dist/widgets/radio/focus-ring.js.flow +0 -22
  375. package/dist/widgets/radio/option-status.js.flow +0 -16
  376. package/dist/widgets/radio/util.js.flow +0 -12
  377. package/dist/widgets/radio/widget.js.flow +0 -80
  378. package/dist/widgets/radio.js.flow +0 -31
  379. package/dist/widgets/reaction-diagram.js.flow +0 -46
  380. package/dist/widgets/sequence.js.flow +0 -53
  381. package/dist/widgets/simulator.js.flow +0 -107
  382. package/dist/widgets/sorter.js.flow +0 -102
  383. package/dist/widgets/table.js.flow +0 -67
  384. package/dist/widgets/transformer.js.flow +0 -127
  385. package/dist/widgets/unit.js.flow +0 -93
  386. package/dist/widgets/video-transcript-link.js.flow +0 -15
  387. package/dist/widgets/video.js.flow +0 -57
  388. package/dist/widgets.js.flow +0 -108
  389. package/dist/zoom.js.flow +0 -7
  390. package/src/multi-items/item-types.js.flow +0 -55
@@ -143,7 +143,7 @@ describe("passage widget", () => {
143
143
  const {renderer} = renderQuestion(question2, apiOptions);
144
144
  jest.runOnlyPendingTimers();
145
145
 
146
- // @ts-expect-error [FEI-5003] - TS2503 - Cannot find namespace 'PassageWidgetExport'
146
+ // @ts-expect-error - TS2503 - Cannot find namespace 'PassageWidgetExport'
147
147
  const [passage1]: [PassageWidgetExport.widget] =
148
148
  renderer.findWidgets("passage 1");
149
149
 
@@ -167,7 +167,7 @@ describe("passage widget", () => {
167
167
  const {renderer} = renderQuestion(question2, apiOptions);
168
168
  jest.runOnlyPendingTimers();
169
169
 
170
- // @ts-expect-error [FEI-5003] - TS2503 - Cannot find namespace 'PassageWidgetExport'
170
+ // @ts-expect-error - TS2503 - Cannot find namespace 'PassageWidgetExport'
171
171
  const [passage1]: [PassageWidgetExport.widget] =
172
172
  renderer.findWidgets("passage 1");
173
173
 
@@ -4,12 +4,13 @@ import React from "react";
4
4
 
5
5
  import {testDependencies} from "../../../../../testing/test-dependencies";
6
6
  import {setDependencies} from "../../dependencies";
7
- import {APIOptions} from "../../types";
8
7
  import {question1} from "../__testdata__/plotter.testdata";
9
8
  import {Plotter} from "../plotter";
10
9
 
11
10
  import {renderQuestion} from "./renderQuestion";
12
11
 
12
+ import type {APIOptions} from "../../types";
13
+
13
14
  describe("plotter widget", () => {
14
15
  beforeEach(() => {
15
16
  setDependencies(testDependencies);
@@ -87,7 +87,7 @@ describe("base-radio", () => {
87
87
  checked: ReadonlyArray<boolean>;
88
88
  crossedOut: ReadonlyArray<boolean>;
89
89
  }) => {
90
- // @ts-expect-error [FEI-5003] - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
90
+ // @ts-expect-error - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
91
91
  updatedValues = newValues;
92
92
  };
93
93
 
@@ -142,7 +142,7 @@ describe("base-radio", () => {
142
142
  checked: ReadonlyArray<boolean>;
143
143
  crossedOut: ReadonlyArray<boolean>;
144
144
  }) => {
145
- // @ts-expect-error [FEI-5003] - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
145
+ // @ts-expect-error - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
146
146
  updatedValues = newValues;
147
147
  };
148
148
 
@@ -173,7 +173,7 @@ describe("base-radio", () => {
173
173
  checked: ReadonlyArray<boolean>;
174
174
  crossedOut: ReadonlyArray<boolean>;
175
175
  }) => {
176
- // @ts-expect-error [FEI-5003] - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
176
+ // @ts-expect-error - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
177
177
  updatedValues = newValues;
178
178
  };
179
179
 
@@ -208,7 +208,7 @@ describe("base-radio", () => {
208
208
  checked: ReadonlyArray<boolean>;
209
209
  crossedOut: ReadonlyArray<boolean>;
210
210
  }) => {
211
- // @ts-expect-error [FEI-5003] - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
211
+ // @ts-expect-error - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
212
212
  updatedValues = newValues;
213
213
  };
214
214
 
@@ -259,7 +259,7 @@ describe("base-radio", () => {
259
259
  checked: ReadonlyArray<boolean>;
260
260
  crossedOut: ReadonlyArray<boolean>;
261
261
  }) => {
262
- // @ts-expect-error [FEI-5003] - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
262
+ // @ts-expect-error - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
263
263
  updatedValues = newValues;
264
264
  };
265
265
 
@@ -308,7 +308,7 @@ describe("base-radio", () => {
308
308
  checked: ReadonlyArray<boolean>;
309
309
  crossedOut: ReadonlyArray<boolean>;
310
310
  }) => {
311
- // @ts-expect-error [FEI-5003] - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
311
+ // @ts-expect-error - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
312
312
  updatedValues = newValues;
313
313
  };
314
314
 
@@ -358,7 +358,7 @@ describe("base-radio", () => {
358
358
  checked: ReadonlyArray<boolean>;
359
359
  crossedOut: ReadonlyArray<boolean>;
360
360
  }) => {
361
- // @ts-expect-error [FEI-5003] - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
361
+ // @ts-expect-error - TS2322 - Type '{ checked: readonly boolean[]; crossedOut: readonly boolean[]; }' is not assignable to type 'null'.
362
362
  updatedValues = newValues;
363
363
  };
364
364
 
@@ -2,6 +2,9 @@ import {RenderStateRoot} from "@khanacademy/wonder-blocks-core";
2
2
  import {render} from "@testing-library/react";
3
3
  import * as React from "react";
4
4
 
5
+ // eslint-disable-next-line import/no-relative-packages
6
+ import {testDependenciesV2} from "../../../../../testing/test-dependencies";
7
+ import {DependenciesContext} from "../../dependencies";
5
8
  import * as Perseus from "../../index";
6
9
  import {registerAllWidgetsForTesting} from "../../util/register-all-widgets-for-testing";
7
10
 
@@ -15,7 +18,7 @@ export const renderQuestion = (
15
18
  apiOptions: APIOptions = Object.freeze({}),
16
19
  extraProps?: JSX.LibraryManagedAttributes<
17
20
  typeof Perseus.Renderer,
18
- // @ts-expect-error [FEI-5003] - TS2344 - Type 'typeof Renderer' does not satisfy the constraint 'keyof IntrinsicElements | JSXElementConstructor<any>'.
21
+ // @ts-expect-error - TS2344 - Type 'typeof Renderer' does not satisfy the constraint 'keyof IntrinsicElements | JSXElementConstructor<any>'.
19
22
  React.ComponentProps<typeof Perseus.Renderer>
20
23
  >,
21
24
  ): {
@@ -25,25 +28,28 @@ export const renderQuestion = (
25
28
  question: PerseusRenderer,
26
29
  extraProps?: JSX.LibraryManagedAttributes<
27
30
  typeof Perseus.Renderer,
28
- // @ts-expect-error [FEI-5003] - TS2344 - Type 'typeof Renderer' does not satisfy the constraint 'keyof IntrinsicElements | JSXElementConstructor<any>'.
31
+ // @ts-expect-error - TS2344 - Type 'typeof Renderer' does not satisfy the constraint 'keyof IntrinsicElements | JSXElementConstructor<any>'.
29
32
  React.ComponentProps<typeof Perseus.Renderer>
30
33
  >,
31
34
  ) => void;
32
35
  unmount: RenderResult["unmount"];
33
36
  } => {
34
37
  registerAllWidgetsForTesting();
38
+
35
39
  let renderer: Perseus.Renderer | null = null;
36
40
  const {container, rerender, unmount} = render(
37
41
  <RenderStateRoot>
38
- <Perseus.Renderer
39
- ref={(node) => (renderer = node)}
40
- content={question.content}
41
- images={question.images}
42
- widgets={question.widgets}
43
- problemNum={0}
44
- apiOptions={apiOptions}
45
- {...extraProps}
46
- />
42
+ <DependenciesContext.Provider value={testDependenciesV2}>
43
+ <Perseus.Renderer
44
+ ref={(node) => (renderer = node)}
45
+ content={question.content}
46
+ images={question.images}
47
+ widgets={question.widgets}
48
+ problemNum={0}
49
+ apiOptions={apiOptions}
50
+ {...extraProps}
51
+ />
52
+ </DependenciesContext.Provider>
47
53
  </RenderStateRoot>,
48
54
  );
49
55
  if (!renderer) {
@@ -51,20 +57,22 @@ export const renderQuestion = (
51
57
  }
52
58
  const renderAgain = (
53
59
  question: PerseusRenderer,
54
- // @ts-expect-error [FEI-5003] - TS2344 - Type 'typeof Renderer' does not satisfy the constraint 'keyof IntrinsicElements | JSXElementConstructor<any>'.
60
+ // @ts-expect-error - TS2344 - Type 'typeof Renderer' does not satisfy the constraint 'keyof IntrinsicElements | JSXElementConstructor<any>'.
55
61
  extraProps: undefined | React.ComponentProps<typeof Perseus.Renderer>,
56
62
  ) => {
57
63
  rerender(
58
64
  <RenderStateRoot>
59
- <Perseus.Renderer
60
- ref={(node) => (renderer = node)}
61
- content={question.content}
62
- images={question.images}
63
- widgets={question.widgets}
64
- problemNum={0}
65
- apiOptions={apiOptions}
66
- {...extraProps}
67
- />
65
+ <DependenciesContext.Provider value={testDependenciesV2}>
66
+ <Perseus.Renderer
67
+ ref={(node) => (renderer = node)}
68
+ content={question.content}
69
+ images={question.images}
70
+ widgets={question.widgets}
71
+ problemNum={0}
72
+ apiOptions={apiOptions}
73
+ {...extraProps}
74
+ />
75
+ </DependenciesContext.Provider>
68
76
  </RenderStateRoot>,
69
77
  );
70
78
  if (!renderer) {
@@ -52,7 +52,7 @@ export class Categorizer extends React.Component<Props, State> {
52
52
  };
53
53
 
54
54
  change: (...args: ReadonlyArray<unknown>) => any = (...args) => {
55
- // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
55
+ // @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
56
56
  return Changeable.change.apply(this, args);
57
57
  };
58
58
 
@@ -68,7 +68,7 @@ export class Categorizer extends React.Component<Props, State> {
68
68
  const isMobile = this.props.apiOptions.isMobile;
69
69
  let indexedItems = this.props.items.map((item, n) => [item, n]);
70
70
  if (this.props.randomizeItems) {
71
- // @ts-expect-error [FEI-5003] - TS4104 - The type 'readonly (string | number)[][]' is 'readonly' and cannot be assigned to the mutable type '(string | number)[][]'. | TS2345 - Argument of type 'number | null | undefined' is not assignable to parameter of type 'number | RNG'.
71
+ // @ts-expect-error - TS4104 - The type 'readonly (string | number)[][]' is 'readonly' and cannot be assigned to the mutable type '(string | number)[][]'. | TS2345 - Argument of type 'number | null | undefined' is not assignable to parameter of type 'number | RNG'.
72
72
  indexedItems = Util.shuffle(indexedItems, this.props.problemNum);
73
73
  }
74
74
 
@@ -103,7 +103,7 @@ export class Categorizer extends React.Component<Props, State> {
103
103
  <tr key={itemNum}>
104
104
  <td>
105
105
  <Renderer
106
- // @ts-expect-error [FEI-5003] - TS2322 - Type 'string | number' is not assignable to type 'string | undefined'.
106
+ // @ts-expect-error - TS2322 - Type 'string | number' is not assignable to type 'string | undefined'.
107
107
  content={item}
108
108
  linterContext={this.props.linterContext}
109
109
  />
@@ -212,7 +212,7 @@ export class Categorizer extends React.Component<Props, State> {
212
212
 
213
213
  onChange: (arg1: number, arg2: number) => void = (itemNum, catNum) => {
214
214
  const values = [...this.props.values];
215
- // @ts-expect-error [FEI-5003] - TS2322 - Type 'number' is not assignable to type 'never'.
215
+ // @ts-expect-error - TS2322 - Type 'number' is not assignable to type 'never'.
216
216
  values[itemNum] = catNum;
217
217
  this.change("values", values);
218
218
  this.props.trackInteraction();
@@ -117,7 +117,7 @@ class CSProgram extends React.Component<any> {
117
117
  };
118
118
 
119
119
  change: (...args: ReadonlyArray<unknown>) => any = (...args) => {
120
- // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
120
+ // @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
121
121
  return Changeable.change.apply(this, args);
122
122
  };
123
123
 
@@ -159,7 +159,7 @@ class CSProgram extends React.Component<any> {
159
159
  url += "&buttons=yes";
160
160
  // Matches templates/scratchpads/embed_script.js
161
161
  // Toolbar height is 66, border height is 1 pixel
162
- // @ts-expect-error [FEI-5003] - TS2540 - Cannot assign to 'height' because it is a read-only property.
162
+ // @ts-expect-error - TS2540 - Cannot assign to 'height' because it is a read-only property.
163
163
  style.height += 67;
164
164
  } else {
165
165
  url += "&buttons=no";
@@ -53,7 +53,7 @@ class Dropdown extends React.Component<Props> {
53
53
  // TODO(LP-10797): This focus() call doesn't do anything because our
54
54
  // root element is a <div> and that cannot be focused without a
55
55
  // tabIndex.
56
- // @ts-expect-error [FEI-5003] - TS2531 - Object is possibly 'null'. | TS2339 - Property 'focus' does not exist on type 'Element | Text'.
56
+ // @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'focus' does not exist on type 'Element | Text'.
57
57
  ReactDOM.findDOMNode(this).focus();
58
58
  return true;
59
59
  };
@@ -1,5 +1,5 @@
1
1
  import * as KAS from "@khanacademy/kas";
2
- import {KeypadInput, KeypadType, Keys as Key} from "@khanacademy/math-input";
2
+ import {KeypadInput, KeypadType} from "@khanacademy/math-input";
3
3
  import {linterContextDefault} from "@khanacademy/perseus-linter";
4
4
  import * as i18n from "@khanacademy/wonder-blocks-i18n";
5
5
  import classNames from "classnames";
@@ -12,7 +12,7 @@ import Tooltip, {
12
12
  HorizontalDirection,
13
13
  VerticalDirection,
14
14
  } from "../components/tooltip";
15
- import {getDependencies} from "../dependencies";
15
+ import {useDependencies} from "../dependencies";
16
16
  import {iconExclamationSign} from "../icon-paths";
17
17
  import {Errors as PerseusErrors, Log} from "../logging/log";
18
18
  import * as Changeable from "../mixins/changeable";
@@ -23,18 +23,13 @@ import type {
23
23
  PerseusExpressionWidgetOptions,
24
24
  PerseusExpressionAnswerForm,
25
25
  } from "../perseus-types";
26
- import type {PerseusScore, WidgetExports, WidgetProps} from "../types";
27
-
28
- const sendExpressionEvaluatedEvent = (
29
- result: "correct" | "incorrect" | "invalid",
30
- ) => {
31
- getDependencies().analytics({
32
- type: "perseus:expression-evaluated",
33
- payload: {
34
- result,
35
- },
36
- });
37
- };
26
+ import type {
27
+ APIOptions,
28
+ PerseusScore,
29
+ WidgetExports,
30
+ WidgetProps,
31
+ } from "../types";
32
+ import type {Keys as Key} from "@khanacademy/math-input";
38
33
 
39
34
  type InputPath = ReadonlyArray<string>;
40
35
 
@@ -62,20 +57,43 @@ const insertBraces = (value) => {
62
57
  return value.replace(/([_^])([^{])/g, "$1{$2}");
63
58
  };
64
59
 
60
+ const deriveKeypadVersion = (apiOptions: APIOptions) => {
61
+ // We can derive which version of the keypad is in use. This is
62
+ // a bit tricky, but this code will be relatively short-lived
63
+ // as we coalesce onto the new, v2 Keypad, at which point we
64
+ // can remove this `virtualKeypadVersion` field entirely.
65
+ return apiOptions.nativeKeypadProxy != null
66
+ ? "REACT_NATIVE_KEYPAD"
67
+ : apiOptions.customKeypad === true
68
+ ? apiOptions.useV2Keypad === true
69
+ ? "MATH_INPUT_KEYPAD_V2"
70
+ : "MATH_INPUT_KEYPAD_V1"
71
+ : "PERSEUS_MATH_INPUT";
72
+ };
73
+
65
74
  type Rubric = PerseusExpressionWidgetOptions;
66
75
 
76
+ type RenderProps = {
77
+ buttonSets: PerseusExpressionWidgetOptions["buttonSets"];
78
+ buttonsVisible?: PerseusExpressionWidgetOptions["buttonsVisible"];
79
+ functions: PerseusExpressionWidgetOptions["functions"];
80
+ times: PerseusExpressionWidgetOptions["times"];
81
+ keypadConfiguration: ReturnType<typeof keypadConfigurationForProps>;
82
+ };
83
+
67
84
  type ExternalProps = WidgetProps<RenderProps, Rubric>;
68
85
 
69
- export type Props = ExternalProps & {
70
- apiOptions: NonNullable<ExternalProps["apiOptions"]>;
71
- buttonSets: NonNullable<ExternalProps["buttonSets"]>;
72
- functions: NonNullable<ExternalProps["functions"]>;
73
- linterContext: NonNullable<ExternalProps["linterContext"]>;
74
- onBlur: NonNullable<ExternalProps["onBlur"]>;
75
- onFocus: NonNullable<ExternalProps["onFocus"]>;
76
- times: NonNullable<ExternalProps["times"]>;
77
- value: string;
78
- };
86
+ export type Props = ExternalProps &
87
+ ReturnType<typeof useDependencies> & {
88
+ apiOptions: NonNullable<ExternalProps["apiOptions"]>;
89
+ buttonSets: NonNullable<ExternalProps["buttonSets"]>;
90
+ functions: NonNullable<ExternalProps["functions"]>;
91
+ linterContext: NonNullable<ExternalProps["linterContext"]>;
92
+ onBlur: NonNullable<ExternalProps["onBlur"]>;
93
+ onFocus: NonNullable<ExternalProps["onFocus"]>;
94
+ times: NonNullable<ExternalProps["times"]>;
95
+ value: string;
96
+ };
79
97
 
80
98
  export type ExpressionState = {
81
99
  showErrorTooltip: boolean;
@@ -126,7 +144,7 @@ export class Expression extends React.Component<Props, ExpressionState> {
126
144
  static validate(
127
145
  userInput: string,
128
146
  rubric: Rubric,
129
- // @ts-expect-error [FEI-5003] - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
147
+ // @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
130
148
  onInputError: OnInputErrorFunctionType = function () {},
131
149
  ): PerseusScore {
132
150
  const options = _.clone(rubric);
@@ -210,8 +228,6 @@ export class Expression extends React.Component<Props, ExpressionState> {
210
228
  // we did, whether it's considered correct, incorrect, or ungraded
211
229
  if (!matchingAnswerForm) {
212
230
  if (firstUngradedResult) {
213
- sendExpressionEvaluatedEvent("invalid");
214
-
215
231
  // While we didn't directly match with any answer form, we
216
232
  // did at some point get an "ungraded" validation result,
217
233
  // which might indicate e.g. a mismatch in variable casing.
@@ -226,8 +242,6 @@ export class Expression extends React.Component<Props, ExpressionState> {
226
242
  };
227
243
  }
228
244
  if (allEmpty) {
229
- sendExpressionEvaluatedEvent("invalid");
230
-
231
245
  // If everything graded as empty, it's invalid.
232
246
  return {
233
247
  type: "invalid",
@@ -236,7 +250,6 @@ export class Expression extends React.Component<Props, ExpressionState> {
236
250
  }
237
251
  // We fell through all the possibilities and we're not empty,
238
252
  // so the answer is considered incorrect.
239
- sendExpressionEvaluatedEvent("incorrect");
240
253
  return {
241
254
  type: "points",
242
255
  earned: 0,
@@ -251,7 +264,6 @@ export class Expression extends React.Component<Props, ExpressionState> {
251
264
  userInput,
252
265
  matchMessage,
253
266
  );
254
- sendExpressionEvaluatedEvent("invalid");
255
267
  return {
256
268
  type: "invalid",
257
269
  message: apiResult === false ? null : matchMessage,
@@ -263,12 +275,6 @@ export class Expression extends React.Component<Props, ExpressionState> {
263
275
  // TODO(eater): Seems silly to translate result to this
264
276
  // invalid/points thing and immediately translate it back in
265
277
  // ItemRenderer.scoreInput()
266
- sendExpressionEvaluatedEvent(
267
- matchingAnswerForm.considered === "correct"
268
- ? "correct"
269
- : "incorrect",
270
- );
271
-
272
278
  return {
273
279
  type: "points",
274
280
  earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
@@ -329,7 +335,7 @@ export class Expression extends React.Component<Props, ExpressionState> {
329
335
  ) {
330
336
  // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
331
337
  // eslint-disable-next-line no-restricted-syntax
332
- // @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call.
338
+ // @ts-expect-error - TS2769 - No overload matches this call.
333
339
  clearTimeout(this.errorTimeout);
334
340
 
335
341
  if (this.parse(this.props.value, this.props).parsed) {
@@ -339,7 +345,7 @@ export class Expression extends React.Component<Props, ExpressionState> {
339
345
  // Store timeout ID so that we can clear it above
340
346
  // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
341
347
  // eslint-disable-next-line no-restricted-syntax
342
- // @ts-expect-error [FEI-5003] - TS2322 - Type 'Timeout' is not assignable to type 'number'.
348
+ // @ts-expect-error - TS2322 - Type 'Timeout' is not assignable to type 'number'.
343
349
  this.errorTimeout = setTimeout(() => {
344
350
  const apiResult = this.props.apiOptions.onInputError(
345
351
  null, // reserved for some widget identifier
@@ -357,7 +363,7 @@ export class Expression extends React.Component<Props, ExpressionState> {
357
363
  componentWillUnmount: () => void = () => {
358
364
  // TODO(jeff, CP-3128): Use Wonder Blocks Timing API.
359
365
  // eslint-disable-next-line no-restricted-syntax
360
- // @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call.
366
+ // @ts-expect-error - TS2769 - No overload matches this call.
361
367
  clearTimeout(this.errorTimeout);
362
368
 
363
369
  this._isMounted = false;
@@ -368,7 +374,21 @@ export class Expression extends React.Component<Props, ExpressionState> {
368
374
  onInputError: OnInputErrorFunctionType,
369
375
  ) => PerseusScore = (rubric, onInputError) => {
370
376
  onInputError = onInputError || function () {};
371
- return Expression.validate(this.getUserInput(), rubric, onInputError);
377
+ const result = Expression.validate(
378
+ this.getUserInput(),
379
+ rubric,
380
+ onInputError,
381
+ );
382
+
383
+ this.sendExpressionEvaluatedEvent(
384
+ result.type === "invalid"
385
+ ? "invalid"
386
+ : result.earned === result.total
387
+ ? "correct"
388
+ : "incorrect",
389
+ );
390
+
391
+ return result;
372
392
  };
373
393
 
374
394
  getUserInput: () => string = () => {
@@ -413,7 +433,7 @@ export class Expression extends React.Component<Props, ExpressionState> {
413
433
  focus: () => boolean = () => {
414
434
  if (this.props.apiOptions.customKeypad) {
415
435
  // eslint-disable-next-line react/no-string-refs
416
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
436
+ // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
417
437
  this.refs.input.focus();
418
438
  } else {
419
439
  // The buttons are often on top of text you're trying to read, so
@@ -425,20 +445,20 @@ export class Expression extends React.Component<Props, ExpressionState> {
425
445
 
426
446
  focusInputPath: (inputPath: InputPath) => void = (inputPath: InputPath) => {
427
447
  // eslint-disable-next-line react/no-string-refs
428
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
448
+ // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
429
449
  this.refs.input.focus();
430
450
  };
431
451
 
432
452
  blurInputPath: (inputPath: InputPath) => void = (inputPath: InputPath) => {
433
453
  // eslint-disable-next-line react/no-string-refs
434
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
454
+ // @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
435
455
  this.refs.input.blur();
436
456
  };
437
457
 
438
458
  // HACK(joel)
439
459
  insert(keyPressed: Key) {
440
460
  // eslint-disable-next-line react/no-string-refs
441
- // @ts-expect-error [FEI-5003] - TS2339 - Property 'insert' does not exist on type 'ReactInstance'.
461
+ // @ts-expect-error - TS2339 - Property 'insert' does not exist on type 'ReactInstance'.
442
462
  this.refs.input.insert(keyPressed);
443
463
  }
444
464
 
@@ -470,6 +490,18 @@ export class Expression extends React.Component<Props, ExpressionState> {
470
490
  );
471
491
  };
472
492
 
493
+ sendExpressionEvaluatedEvent(result: "correct" | "incorrect" | "invalid") {
494
+ this.props.analytics.onAnalyticsEvent({
495
+ type: "perseus:expression-evaluated",
496
+ payload: {
497
+ result,
498
+ virtualKeypadVersion: deriveKeypadVersion(
499
+ this.props.apiOptions,
500
+ ),
501
+ },
502
+ });
503
+ }
504
+
473
505
  render():
474
506
  | React.ReactNode
475
507
  | React.ReactElement<React.ComponentProps<"div">> {
@@ -639,22 +671,25 @@ const propUpgrades = {
639
671
  }),
640
672
  } as const;
641
673
 
642
- type RenderProps = {
643
- buttonSets: any;
644
- buttonsVisible?: "always" | "focused" | "never";
645
- functions: ReadonlyArray<string>;
646
- keypadConfiguration: {
647
- extraKeys: ReadonlyArray<any | string>;
648
- keypadType: any;
649
- };
650
- times: boolean;
651
- };
674
+ const ExpressionWithDependencies = React.forwardRef<
675
+ Expression,
676
+ Omit<
677
+ JSX.LibraryManagedAttributes<
678
+ typeof Expression,
679
+ React.ComponentProps<typeof Expression>
680
+ >,
681
+ keyof ReturnType<typeof useDependencies>
682
+ >
683
+ >((props, ref) => {
684
+ const deps = useDependencies();
685
+ return <Expression ref={ref} analytics={deps.analytics} {...props} />;
686
+ });
652
687
 
653
688
  export default {
654
689
  name: "expression",
655
690
  displayName: "Expression / Equation",
656
691
  defaultAlignment: "inline-block",
657
- widget: Expression,
692
+ widget: ExpressionWithDependencies,
658
693
  transform: (widgetOptions: PerseusExpressionWidgetOptions): RenderProps => {
659
694
  const {times, functions, buttonSets, buttonsVisible} = widgetOptions;
660
695
  return {
@@ -670,4 +705,4 @@ export default {
670
705
 
671
706
  // For use by the editor
672
707
  isLintable: true,
673
- } as WidgetExports<typeof Expression>;
708
+ } as WidgetExports<typeof ExpressionWithDependencies>;
@@ -87,7 +87,7 @@ type State = {
87
87
  // TODO(jared): find a better name for this :) and for GradedGroup; the names
88
88
  // are currently a little confusing.
89
89
  class GradedGroupSet extends React.Component<Props, State> {
90
- // @ts-expect-error [FEI-5003] - TS2564 - Property '_childGroup' has no initializer and is not definitely assigned in the constructor.
90
+ // @ts-expect-error - TS2564 - Property '_childGroup' has no initializer and is not definitely assigned in the constructor.
91
91
  _childGroup: GradedGroup;
92
92
 
93
93
  static defaultProps: DefaultProps = {
@@ -105,7 +105,7 @@ class GradedGroupSet extends React.Component<Props, State> {
105
105
  }
106
106
 
107
107
  change: (...args: ReadonlyArray<unknown>) => any = (...args) => {
108
- // @ts-expect-error [FEI-5003] - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
108
+ // @ts-expect-error - TS2345 - Argument of type 'readonly unknown[]' is not assignable to parameter of type 'any[]'.
109
109
  return Changeable.change.apply(this, args);
110
110
  };
111
111
 
@@ -157,7 +157,7 @@ class GradedGroupSet extends React.Component<Props, State> {
157
157
  return (
158
158
  // TODO(jeremy): Don't spread this.props, instead
159
159
  // pass in all props GradedGroup needs explicilty
160
- // @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call.
160
+ // @ts-expect-error - TS2769 - No overload matches this call.
161
161
  <GradedGroup
162
162
  key={i}
163
163
  {...this.props}
@@ -200,10 +200,10 @@ class GradedGroupSet extends React.Component<Props, State> {
200
200
  </div>
201
201
  {/* TODO(jeremy): Don't spread this.props, instead
202
202
  pass in all props GradedGroup needs explicitly */}
203
- {/* @ts-expect-error [FEI-5003] - TS2769 - No overload matches this call. */}
203
+ {/* @ts-expect-error - TS2769 - No overload matches this call. */}
204
204
  <GradedGroup
205
205
  key={this.state.currentGroup}
206
- // @ts-expect-error [FEI-5003] - TS2322 - Type 'GradedGroup | null' is not assignable to type 'GradedGroup'.
206
+ // @ts-expect-error - TS2322 - Type 'GradedGroup | null' is not assignable to type 'GradedGroup'.
207
207
  // Type 'null' is not assignable to type 'GradedGroup'.
208
208
  ref={(comp) => (this._childGroup = comp)}
209
209
  // We should pass in the set of props explicitly