@khanacademy/perseus 0.0.1 → 0.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 (503) hide show
  1. package/.babelrc.js +1 -1
  2. package/CHANGELOG.md +7 -0
  3. package/dist/es/index.js +15 -1
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.css +11934 -0
  6. package/dist/index.js +15 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/strings.js +148 -1
  9. package/package.json +40 -4
  10. package/src/README.md +7 -0
  11. package/src/__stories__/item-renderer.stories.jsx +27 -0
  12. package/src/__stories__/server-item-renderer.stories.jsx +27 -0
  13. package/src/__testdata__/item-renderer_testdata.js +161 -0
  14. package/src/__testdata__/renderer_testdata.js +123 -0
  15. package/src/__tests__/__snapshots__/item-renderer_test.jsx.snap +233 -0
  16. package/src/__tests__/__snapshots__/perseus-markdown_test.jsx.snap +516 -0
  17. package/src/__tests__/__snapshots__/pure-markdown_test.js.snap +113 -0
  18. package/src/__tests__/__snapshots__/renderer_test.jsx.snap +576 -0
  19. package/src/__tests__/__snapshots__/server-item-renderer_test.jsx.snap +306 -0
  20. package/src/__tests__/a11y_test.jsx +304 -0
  21. package/src/__tests__/item-renderer_test.jsx +892 -0
  22. package/src/__tests__/mock-asset-loading-widget.jsx +57 -0
  23. package/src/__tests__/mock-widget.jsx +35 -0
  24. package/src/__tests__/perseus-markdown_test.jsx +747 -0
  25. package/src/__tests__/pure-markdown_test.js +552 -0
  26. package/src/__tests__/renderability_test.jsx +481 -0
  27. package/src/__tests__/renderer-api_test.jsx +333 -0
  28. package/src/__tests__/renderer_test.jsx +1792 -0
  29. package/src/__tests__/server-item-renderer_test.jsx +516 -0
  30. package/src/__tests__/test-items/image-item.js +25 -0
  31. package/src/__tests__/test-items/input-number-1-item.js +27 -0
  32. package/src/__tests__/test-items/input-number-2-item.js +39 -0
  33. package/src/__tests__/test-items/table-item.js +30 -0
  34. package/src/__tests__/tex-wrangler_test.js +83 -0
  35. package/src/__tests__/util_test.js +41 -0
  36. package/src/a11y.js +46 -0
  37. package/src/all-widgets.js +9 -0
  38. package/src/article-renderer.jsx +215 -0
  39. package/src/asset-context.js +30 -0
  40. package/src/basic-widgets.js +13 -0
  41. package/src/components/__stories__/button-group.stories.jsx +61 -0
  42. package/src/components/__stories__/fixed-to-responsive.stories.jsx +132 -0
  43. package/src/components/__stories__/graph.stories.jsx +27 -0
  44. package/src/components/__stories__/graphie.stories.jsx +28 -0
  45. package/src/components/__stories__/hud.stories.jsx +36 -0
  46. package/src/components/__stories__/icon.stories.jsx +56 -0
  47. package/src/components/__stories__/image-loader.stories.jsx +60 -0
  48. package/src/components/__stories__/info-tip.stories.jsx +33 -0
  49. package/src/components/__stories__/inline-icon.stories.jsx +39 -0
  50. package/src/components/__stories__/input-with-examples.stories.jsx +51 -0
  51. package/src/components/__stories__/lint.stories.jsx +40 -0
  52. package/src/components/__stories__/math-input.stories.jsx +29 -0
  53. package/src/components/__stories__/math-output.stories.jsx +24 -0
  54. package/src/components/__stories__/multi-button-group.stories.jsx +69 -0
  55. package/src/components/__stories__/number-input.stories.jsx +42 -0
  56. package/src/components/__stories__/possible-answers.stories.jsx +22 -0
  57. package/src/components/__stories__/prop-check-box.stories.jsx +62 -0
  58. package/src/components/__stories__/range-input.stories.jsx +28 -0
  59. package/src/components/__stories__/simple-keypad-input.stories.jsx +28 -0
  60. package/src/components/__stories__/sortable.stories.jsx +73 -0
  61. package/src/components/__stories__/stub-tag-editor.stories.jsx +44 -0
  62. package/src/components/__stories__/svg-image.stories.jsx +60 -0
  63. package/src/components/__stories__/tex.stories.jsx +18 -0
  64. package/src/components/__stories__/text-buttons.stories.jsx +51 -0
  65. package/src/components/__stories__/text-input.stories.jsx +34 -0
  66. package/src/components/__stories__/text-list-editor.stories.jsx +33 -0
  67. package/src/components/__stories__/zoomable-tex.stories.jsx +39 -0
  68. package/src/components/__stories__/zoomable.stories.jsx +40 -0
  69. package/src/components/__testdata__/sorter_testdata.js +20 -0
  70. package/src/components/__tests__/__snapshots__/sortable_test.jsx.snap +79 -0
  71. package/src/components/__tests__/__snapshots__/sorter_test.jsx.snap +297 -0
  72. package/src/components/__tests__/number-input_test.jsx +112 -0
  73. package/src/components/__tests__/sortable_test.jsx +234 -0
  74. package/src/components/__tests__/sorter_test.jsx +111 -0
  75. package/src/components/__tests__/zoomable_test.jsx +388 -0
  76. package/src/components/button-group.jsx +131 -0
  77. package/src/components/constants.js +15 -0
  78. package/src/components/fixed-to-responsive.jsx +163 -0
  79. package/src/components/graph.jsx +414 -0
  80. package/src/components/graphie-classes.jsx +148 -0
  81. package/src/components/graphie-movables.jsx +342 -0
  82. package/src/components/graphie.jsx +353 -0
  83. package/src/components/highlighting/highlightable-content.jsx +245 -0
  84. package/src/components/highlighting/highlights.js +208 -0
  85. package/src/components/highlighting/ranges.js +299 -0
  86. package/src/components/highlighting/types.js +73 -0
  87. package/src/components/highlighting/ui/highlight-renderer.jsx +179 -0
  88. package/src/components/highlighting/ui/highlight-set-renderer.jsx +190 -0
  89. package/src/components/highlighting/ui/highlight-tooltip.jsx +153 -0
  90. package/src/components/highlighting/ui/highlighting-ui.jsx +163 -0
  91. package/src/components/highlighting/ui/selection-tracker.jsx +195 -0
  92. package/src/components/highlighting/ui/types.js +21 -0
  93. package/src/components/highlighting/ui/util.js +166 -0
  94. package/src/components/highlighting/word-indexer.jsx +155 -0
  95. package/src/components/hud.jsx +179 -0
  96. package/src/components/icon.jsx +149 -0
  97. package/src/components/image-loader.jsx +175 -0
  98. package/src/components/info-tip/info-tip.jsx +149 -0
  99. package/src/components/info-tip.jsx +38 -0
  100. package/src/components/inline-icon.jsx +81 -0
  101. package/src/components/input-with-examples.jsx +231 -0
  102. package/src/components/lint.jsx +456 -0
  103. package/src/components/math-input.jsx +282 -0
  104. package/src/components/math-output.jsx +128 -0
  105. package/src/components/multi-button-group.jsx +137 -0
  106. package/src/components/new-tooltip/README.md +110 -0
  107. package/src/components/new-tooltip/__stories__/flexible-position.stories.jsx +437 -0
  108. package/src/components/new-tooltip/__stories__/new-tooltip.stories.jsx +665 -0
  109. package/src/components/new-tooltip/__stories__/tooltip-arrow.stories.jsx +70 -0
  110. package/src/components/new-tooltip/__stories__/tooltip-bubble.stories.jsx +98 -0
  111. package/src/components/new-tooltip/__tests__/style-constants_test.js +8 -0
  112. package/src/components/new-tooltip/__tests__/util_test.js +47 -0
  113. package/src/components/new-tooltip/flexible-position.jsx +182 -0
  114. package/src/components/new-tooltip/new-tooltip.jsx +399 -0
  115. package/src/components/new-tooltip/style-constants.js +13 -0
  116. package/src/components/new-tooltip/tooltip-arrow.jsx +184 -0
  117. package/src/components/new-tooltip/tooltip-bubble.jsx +223 -0
  118. package/src/components/new-tooltip/tooltip-portal.jsx +101 -0
  119. package/src/components/new-tooltip/tooltip-positioner.jsx +529 -0
  120. package/src/components/new-tooltip/types.js +183 -0
  121. package/src/components/new-tooltip/util.js +27 -0
  122. package/src/components/number-input.jsx +253 -0
  123. package/src/components/possible-answers.jsx +36 -0
  124. package/src/components/prop-check-box.jsx +78 -0
  125. package/src/components/range-input.jsx +61 -0
  126. package/src/components/simple-keypad-input.jsx +91 -0
  127. package/src/components/sortable.jsx +872 -0
  128. package/src/components/stub-tag-editor.jsx +53 -0
  129. package/src/components/svg-image.jsx +762 -0
  130. package/src/components/tex-buttons.jsx +322 -0
  131. package/src/components/tex.jsx +70 -0
  132. package/src/components/text-input.jsx +82 -0
  133. package/src/components/text-list-editor.jsx +157 -0
  134. package/src/components/tooltip.jsx +395 -0
  135. package/src/components/visibility-observer/__stories__/visibility-observer.stories.jsx +140 -0
  136. package/src/components/visibility-observer/get-scroll-parent.js +45 -0
  137. package/src/components/visibility-observer/visibility-observer.js +109 -0
  138. package/src/components/visibility-observer/visibility-observer.jsx +123 -0
  139. package/src/components/zoomable-tex.jsx +70 -0
  140. package/src/components/zoomable.jsx +304 -0
  141. package/src/components.js +17 -0
  142. package/src/dependencies.js +22 -0
  143. package/src/error-boundary.jsx +48 -0
  144. package/src/extra-widgets.js +75 -0
  145. package/src/gorgon/README +40 -0
  146. package/src/gorgon/__tests__/matcher_test.js +497 -0
  147. package/src/gorgon/__tests__/rule_test.js +101 -0
  148. package/src/gorgon/__tests__/rules_test.js +487 -0
  149. package/src/gorgon/__tests__/selector-parser_test.js +52 -0
  150. package/src/gorgon/__tests__/tree-transformer_test.js +446 -0
  151. package/src/gorgon/gorgon.js +290 -0
  152. package/src/gorgon/proptypes.js +29 -0
  153. package/src/gorgon/rule.js +413 -0
  154. package/src/gorgon/rules/absolute-url.js +24 -0
  155. package/src/gorgon/rules/all-rules.js +72 -0
  156. package/src/gorgon/rules/blockquoted-math.js +10 -0
  157. package/src/gorgon/rules/blockquoted-widget.js +10 -0
  158. package/src/gorgon/rules/double-spacing-after-terminal.js +12 -0
  159. package/src/gorgon/rules/extra-content-spacing.js +12 -0
  160. package/src/gorgon/rules/heading-level-1.js +14 -0
  161. package/src/gorgon/rules/heading-level-skip.js +20 -0
  162. package/src/gorgon/rules/heading-sentence-case.js +11 -0
  163. package/src/gorgon/rules/heading-title-case.js +63 -0
  164. package/src/gorgon/rules/image-alt-text.js +21 -0
  165. package/src/gorgon/rules/image-in-table.js +10 -0
  166. package/src/gorgon/rules/image-spaces-around-urls.js +35 -0
  167. package/src/gorgon/rules/image-widget.js +50 -0
  168. package/src/gorgon/rules/link-click-here.js +11 -0
  169. package/src/gorgon/rules/lint-utils.js +48 -0
  170. package/src/gorgon/rules/long-paragraph.js +14 -0
  171. package/src/gorgon/rules/math-adjacent.js +10 -0
  172. package/src/gorgon/rules/math-align-extra-break.js +11 -0
  173. package/src/gorgon/rules/math-align-linebreaks.js +43 -0
  174. package/src/gorgon/rules/math-empty.js +10 -0
  175. package/src/gorgon/rules/math-font-size.js +12 -0
  176. package/src/gorgon/rules/math-frac.js +10 -0
  177. package/src/gorgon/rules/math-nested.js +11 -0
  178. package/src/gorgon/rules/math-starts-with-space.js +12 -0
  179. package/src/gorgon/rules/math-text-empty.js +10 -0
  180. package/src/gorgon/rules/math-without-dollars.js +14 -0
  181. package/src/gorgon/rules/nested-lists.js +11 -0
  182. package/src/gorgon/rules/profanity.js +10 -0
  183. package/src/gorgon/rules/table-missing-cells.js +20 -0
  184. package/src/gorgon/rules/unbalanced-code-delimiters.js +14 -0
  185. package/src/gorgon/rules/unescaped-dollar.js +10 -0
  186. package/src/gorgon/rules/widget-in-table.js +10 -0
  187. package/src/gorgon/selector.js +506 -0
  188. package/src/gorgon/tree-transformer.js +588 -0
  189. package/src/hint-renderer.jsx +159 -0
  190. package/src/hints-renderer.jsx +359 -0
  191. package/src/icon-paths.js +120 -0
  192. package/src/index.js +149 -2
  193. package/src/init.js +49 -0
  194. package/src/interaction-tracker.js +60 -0
  195. package/src/interactive2/__tests__/movable-mock.js +48 -0
  196. package/src/interactive2/__tests__/movable-point_test.js +146 -0
  197. package/src/interactive2/interactive-util.js +152 -0
  198. package/src/interactive2/movable-helper-methods.js +160 -0
  199. package/src/interactive2/movable-line-options.js +392 -0
  200. package/src/interactive2/movable-line.js +322 -0
  201. package/src/interactive2/movable-point-options.js +190 -0
  202. package/src/interactive2/movable-point.jsx +604 -0
  203. package/src/interactive2/movable-polygon-options.js +351 -0
  204. package/src/interactive2/movable-polygon.js +367 -0
  205. package/src/interactive2/movable.js +273 -0
  206. package/src/interactive2/objective_.js +79 -0
  207. package/src/interactive2/types.js +3 -0
  208. package/src/interactive2/wrapped-defaults.js +86 -0
  209. package/src/interactive2/wrapped-ellipse.js +87 -0
  210. package/src/interactive2/wrapped-line.js +109 -0
  211. package/src/interactive2/wrapped-path.js +47 -0
  212. package/src/interactive2.js +91 -0
  213. package/src/item-renderer.jsx +518 -0
  214. package/src/item-version.js +12 -0
  215. package/src/jipt-paragraphs.js +44 -0
  216. package/src/jquery.mobile.vmouse.js +545 -0
  217. package/src/keypad-context.js +33 -0
  218. package/src/loading-context.js +20 -0
  219. package/src/logging/log.js +107 -0
  220. package/src/logging/log_test.js +41 -0
  221. package/src/mixins/changeable.jsx +115 -0
  222. package/src/mixins/editor-jsonify.jsx +13 -0
  223. package/src/mixins/provide-keypad.jsx +131 -0
  224. package/src/mixins/widget-jsonify-deprecated.jsx +23 -0
  225. package/src/mixins/widget-prop-blacklist.js +36 -0
  226. package/src/multi-items/__stories__/multi-renderer.stories.js +88 -0
  227. package/src/multi-items/__testdata__/multi-renderer_testdata.js +174 -0
  228. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +994 -0
  229. package/src/multi-items/__tests__/items_test.js +245 -0
  230. package/src/multi-items/__tests__/multi-renderer_test.js +903 -0
  231. package/src/multi-items/__tests__/prop-type-builders_test.js +134 -0
  232. package/src/multi-items/__tests__/shapes_test.js +44 -0
  233. package/src/multi-items/__tests__/trees_test.js +316 -0
  234. package/src/multi-items/item-types.js +55 -0
  235. package/src/multi-items/items.js +176 -0
  236. package/src/multi-items/multi-renderer.jsx +542 -0
  237. package/src/multi-items/prop-type-builders.js +105 -0
  238. package/src/multi-items/shape-types.js +47 -0
  239. package/src/multi-items/shapes.js +53 -0
  240. package/src/multi-items/tree-types.js +42 -0
  241. package/src/multi-items/trees.js +345 -0
  242. package/src/multi-items.js +76 -0
  243. package/src/not-gorgon.js +129 -0
  244. package/src/perseus-api.jsx +270 -0
  245. package/src/perseus-env.js +26 -0
  246. package/src/perseus-error.js +19 -0
  247. package/src/perseus-markdown.jsx +434 -0
  248. package/src/perseus-types.js +1597 -0
  249. package/src/pure-markdown.js +346 -0
  250. package/src/question-paragraph.jsx +30 -0
  251. package/src/renderability.jsx +108 -0
  252. package/src/renderer.jsx +1931 -0
  253. package/src/server-item-renderer.jsx +467 -0
  254. package/src/sigfigs.jsx +492 -0
  255. package/src/styles/articles.less +748 -0
  256. package/src/styles/constants.js +119 -0
  257. package/src/styles/global-constants.js +350 -0
  258. package/src/styles/global-styles.js +307 -0
  259. package/src/styles/khan-exercise.css +1304 -0
  260. package/src/styles/media-queries.js +48 -0
  261. package/src/styles/perseus-renderer.less +1008 -0
  262. package/src/styles/shared.js +132 -0
  263. package/src/styles/util.less +47 -0
  264. package/src/styles/variables.boostrap.less +301 -0
  265. package/src/styles/variables.less +68 -0
  266. package/src/styles/variables.shared.less +356 -0
  267. package/src/styles/widgets/categorizer.less +39 -0
  268. package/src/styles/widgets/definition.less +86 -0
  269. package/src/styles/widgets/dropdown.less +3 -0
  270. package/src/styles/widgets/expression.less +147 -0
  271. package/src/styles/widgets/graded-group.less +36 -0
  272. package/src/styles/widgets/grapher.less +11 -0
  273. package/src/styles/widgets/group.less +19 -0
  274. package/src/styles/widgets/image.less +29 -0
  275. package/src/styles/widgets/interactive-graph.less +61 -0
  276. package/src/styles/widgets/label-image.less +41 -0
  277. package/src/styles/widgets/matcher.less +8 -0
  278. package/src/styles/widgets/matrix.less +120 -0
  279. package/src/styles/widgets/measurer.less +20 -0
  280. package/src/styles/widgets/orderer.less +97 -0
  281. package/src/styles/widgets/passage.less +151 -0
  282. package/src/styles/widgets/plotter.less +33 -0
  283. package/src/styles/widgets/radio.less +52 -0
  284. package/src/styles/widgets/reaction-diagram.less +35 -0
  285. package/src/styles/widgets/sequence.less +5 -0
  286. package/src/styles/widgets/sortable.less +127 -0
  287. package/src/styles/widgets/table.less +56 -0
  288. package/src/styles/widgets/transformer.less +28 -0
  289. package/src/styles/widgets/unit.less +14 -0
  290. package/src/tex-wrangler.js +176 -0
  291. package/src/traversal.jsx +141 -0
  292. package/src/types.js +538 -0
  293. package/src/util/a11y.js +24 -0
  294. package/src/util/answer-types.js +862 -0
  295. package/src/util/answer-types_test.js +115 -0
  296. package/src/util/colors.js +94 -0
  297. package/src/util/fix-passage-refs.jsx +122 -0
  298. package/src/util/graph-utils.js +16 -0
  299. package/src/util/graphie.js +1585 -0
  300. package/src/util/interactive.js +4071 -0
  301. package/src/util/katex-preprocess.js +16 -0
  302. package/src/util/math.js +296 -0
  303. package/src/util/mobile-native-utils.js +14 -0
  304. package/src/util/mobile-native-utils_test.js +24 -0
  305. package/src/util/register-all-widgets-for-testing.js +12 -0
  306. package/src/util/scroll-utils.js +162 -0
  307. package/src/util/sizing-utils.js +46 -0
  308. package/src/util/tex.js +203 -0
  309. package/src/util/url-utils.js +36 -0
  310. package/src/util.js +990 -0
  311. package/src/version.js +11 -0
  312. package/src/widget-container.jsx +174 -0
  313. package/src/widgets/__stories__/categorizer.stories.jsx +15 -0
  314. package/src/widgets/__stories__/cs-program.stories.jsx +15 -0
  315. package/src/widgets/__stories__/definition.stories.jsx +38 -0
  316. package/src/widgets/__stories__/dropdown.stories.jsx +15 -0
  317. package/src/widgets/__stories__/explanation.stories.jsx +19 -0
  318. package/src/widgets/__stories__/expression.stories.jsx +26 -0
  319. package/src/widgets/__stories__/graded-group-set.stories.jsx +32 -0
  320. package/src/widgets/__stories__/graded-group.stories.jsx +32 -0
  321. package/src/widgets/__stories__/grapher.stories.jsx +47 -0
  322. package/src/widgets/__stories__/group.stories.jsx +15 -0
  323. package/src/widgets/__stories__/iframe.stories.jsx +15 -0
  324. package/src/widgets/__stories__/image.stories.jsx +62 -0
  325. package/src/widgets/__stories__/input-number.stories.jsx +90 -0
  326. package/src/widgets/__stories__/interaction.stories.jsx +15 -0
  327. package/src/widgets/__stories__/interactive-graph.stories.jsx +61 -0
  328. package/src/widgets/__stories__/matcher.stories.jsx +15 -0
  329. package/src/widgets/__stories__/matrix.stories.jsx +15 -0
  330. package/src/widgets/__stories__/number-line.stories.jsx +15 -0
  331. package/src/widgets/__stories__/numeric-input.stories.jsx +19 -0
  332. package/src/widgets/__stories__/orderer.stories.jsx +15 -0
  333. package/src/widgets/__stories__/passage-ref.stories.jsx +19 -0
  334. package/src/widgets/__stories__/passage.stories.jsx +19 -0
  335. package/src/widgets/__stories__/radio.stories.jsx +82 -0
  336. package/src/widgets/__stories__/sorter.stories.jsx +15 -0
  337. package/src/widgets/__stories__/transformer.stories.jsx +15 -0
  338. package/src/widgets/__stories__/video.stories.jsx +15 -0
  339. package/src/widgets/__testdata__/categorizer_testdata.js +50 -0
  340. package/src/widgets/__testdata__/cs-program_testdata.js +28 -0
  341. package/src/widgets/__testdata__/dropdown_testdata.js +34 -0
  342. package/src/widgets/__testdata__/explanation_testdata.js +56 -0
  343. package/src/widgets/__testdata__/expression_testdata.js +95 -0
  344. package/src/widgets/__testdata__/graded-group-set_testdata.js +358 -0
  345. package/src/widgets/__testdata__/graded-group_testdata.js +79 -0
  346. package/src/widgets/__testdata__/grapher_testdata.js +376 -0
  347. package/src/widgets/__testdata__/group_testdata.js +156 -0
  348. package/src/widgets/__testdata__/iframe_testdata.js +36 -0
  349. package/src/widgets/__testdata__/image_testdata.js +38 -0
  350. package/src/widgets/__testdata__/input-number_testdata.js +73 -0
  351. package/src/widgets/__testdata__/interaction_testdata.js +264 -0
  352. package/src/widgets/__testdata__/interactive-graph_testdata.js +646 -0
  353. package/src/widgets/__testdata__/matcher_testdata.js +34 -0
  354. package/src/widgets/__testdata__/matrix_testdata.js +29 -0
  355. package/src/widgets/__testdata__/number-line_testdata.js +31 -0
  356. package/src/widgets/__testdata__/numeric-input_testdata.js +266 -0
  357. package/src/widgets/__testdata__/orderer_testdata.js +58 -0
  358. package/src/widgets/__testdata__/passage-ref_testdata.js +41 -0
  359. package/src/widgets/__testdata__/passage_testdata.js +71 -0
  360. package/src/widgets/__testdata__/radio_testdata.js +210 -0
  361. package/src/widgets/__testdata__/sorter_testdata.js +20 -0
  362. package/src/widgets/__testdata__/transformer_testdata.js +109 -0
  363. package/src/widgets/__testdata__/video_testdata.js +22 -0
  364. package/src/widgets/__tests__/__snapshots__/categorizer_test.jsx.snap +928 -0
  365. package/src/widgets/__tests__/__snapshots__/cs-program_test.jsx.snap +75 -0
  366. package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +99 -0
  367. package/src/widgets/__tests__/__snapshots__/dropdown_test.jsx.snap +143 -0
  368. package/src/widgets/__tests__/__snapshots__/explanation_test.js.snap +473 -0
  369. package/src/widgets/__tests__/__snapshots__/expression_test.jsx.snap +38 -0
  370. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +754 -0
  371. package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +310 -0
  372. package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +863 -0
  373. package/src/widgets/__tests__/__snapshots__/grapher_test.jsx.snap +2880 -0
  374. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +1154 -0
  375. package/src/widgets/__tests__/__snapshots__/iframe_test.jsx.snap +69 -0
  376. package/src/widgets/__tests__/__snapshots__/image_test.jsx.snap +388 -0
  377. package/src/widgets/__tests__/__snapshots__/input-number_test.jsx.snap +978 -0
  378. package/src/widgets/__tests__/__snapshots__/interaction_test.jsx.snap +311 -0
  379. package/src/widgets/__tests__/__snapshots__/interactive-graph_test.jsx.snap +2915 -0
  380. package/src/widgets/__tests__/__snapshots__/matcher_test.jsx.snap +1039 -0
  381. package/src/widgets/__tests__/__snapshots__/matrix_test.jsx.snap +450 -0
  382. package/src/widgets/__tests__/__snapshots__/number-line_test.jsx.snap +888 -0
  383. package/src/widgets/__tests__/__snapshots__/numeric-input_test.jsx.snap +930 -0
  384. package/src/widgets/__tests__/__snapshots__/orderer_test.jsx.snap +275 -0
  385. package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +446 -0
  386. package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +1988 -0
  387. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +7107 -0
  388. package/src/widgets/__tests__/__snapshots__/video_test.jsx.snap +163 -0
  389. package/src/widgets/__tests__/categorizer_test.jsx +205 -0
  390. package/src/widgets/__tests__/cs-program_test.jsx +45 -0
  391. package/src/widgets/__tests__/definition_test.js +152 -0
  392. package/src/widgets/__tests__/dropdown_test.jsx +106 -0
  393. package/src/widgets/__tests__/explanation_test.js +199 -0
  394. package/src/widgets/__tests__/expression_test.jsx +439 -0
  395. package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +33 -0
  396. package/src/widgets/__tests__/graded-group-set_test.jsx +270 -0
  397. package/src/widgets/__tests__/graded-group_test.jsx +233 -0
  398. package/src/widgets/__tests__/grapher.cypress.jsx +486 -0
  399. package/src/widgets/__tests__/grapher_test.jsx +35 -0
  400. package/src/widgets/__tests__/group_test.jsx +512 -0
  401. package/src/widgets/__tests__/iframe_test.jsx +47 -0
  402. package/src/widgets/__tests__/image_test.jsx +65 -0
  403. package/src/widgets/__tests__/input-number_test.jsx +360 -0
  404. package/src/widgets/__tests__/interaction_test.jsx +33 -0
  405. package/src/widgets/__tests__/interactive-graph.cypress.jsx +697 -0
  406. package/src/widgets/__tests__/interactive-graph_test.jsx +105 -0
  407. package/src/widgets/__tests__/matcher_test.jsx +157 -0
  408. package/src/widgets/__tests__/matrix_test.jsx +84 -0
  409. package/src/widgets/__tests__/number-line_test.jsx +81 -0
  410. package/src/widgets/__tests__/numeric-input_test.jsx +574 -0
  411. package/src/widgets/__tests__/orderer_test.jsx +75 -0
  412. package/src/widgets/__tests__/passage-ref_test.jsx +166 -0
  413. package/src/widgets/__tests__/passage_test.jsx +133 -0
  414. package/src/widgets/__tests__/radio/base-radio_test.jsx +82 -0
  415. package/src/widgets/__tests__/radio_test.jsx +651 -0
  416. package/src/widgets/__tests__/renderQuestion.jsx +65 -0
  417. package/src/widgets/__tests__/unit_test.jsx +144 -0
  418. package/src/widgets/__tests__/video_test.jsx +55 -0
  419. package/src/widgets/categorizer.jsx +338 -0
  420. package/src/widgets/cs-program.jsx +228 -0
  421. package/src/widgets/definition.jsx +141 -0
  422. package/src/widgets/dropdown.jsx +143 -0
  423. package/src/widgets/explanation.jsx +315 -0
  424. package/src/widgets/expression.jsx +654 -0
  425. package/src/widgets/graded-group-answer-bar.jsx +152 -0
  426. package/src/widgets/graded-group-set.jsx +285 -0
  427. package/src/widgets/graded-group.jsx +448 -0
  428. package/src/widgets/grapher/util.jsx +819 -0
  429. package/src/widgets/grapher.jsx +658 -0
  430. package/src/widgets/group.jsx +213 -0
  431. package/src/widgets/iframe.jsx +199 -0
  432. package/src/widgets/image.jsx +309 -0
  433. package/src/widgets/input-number.jsx +410 -0
  434. package/src/widgets/interaction/arrow-picker.jsx +39 -0
  435. package/src/widgets/interaction/color-picker.jsx +69 -0
  436. package/src/widgets/interaction/dash-picker.jsx +46 -0
  437. package/src/widgets/interaction/element-container.jsx +97 -0
  438. package/src/widgets/interaction.jsx +742 -0
  439. package/src/widgets/interactive-graph.jsx +2821 -0
  440. package/src/widgets/label-image/__stories__/answer-choices.stories.jsx +93 -0
  441. package/src/widgets/label-image/__stories__/marker.stories.jsx +172 -0
  442. package/src/widgets/label-image/answer-choices.jsx +311 -0
  443. package/src/widgets/label-image/marker.jsx +365 -0
  444. package/src/widgets/label-image/types.js +33 -0
  445. package/src/widgets/label-image.jsx +841 -0
  446. package/src/widgets/label-image_test.js +840 -0
  447. package/src/widgets/lights-puzzle.jsx +356 -0
  448. package/src/widgets/matcher.jsx +271 -0
  449. package/src/widgets/matrix.jsx +615 -0
  450. package/src/widgets/measurer.jsx +215 -0
  451. package/src/widgets/molecule/__tests__/layout_test.jsx +159 -0
  452. package/src/widgets/molecule/__tests__/parser_test.jsx +142 -0
  453. package/src/widgets/molecule/molecule-drawing.jsx +175 -0
  454. package/src/widgets/molecule/molecule-layout.jsx +367 -0
  455. package/src/widgets/molecule/smiles-parser.jsx +300 -0
  456. package/src/widgets/molecule.jsx +172 -0
  457. package/src/widgets/number-line.jsx +858 -0
  458. package/src/widgets/numeric-input.jsx +622 -0
  459. package/src/widgets/orderer.jsx +753 -0
  460. package/src/widgets/passage/__tests__/passage-markdown_test.jsx +382 -0
  461. package/src/widgets/passage/get-line-height-for-node.js +10 -0
  462. package/src/widgets/passage/passage-markdown.jsx +416 -0
  463. package/src/widgets/passage-ref-target.jsx +70 -0
  464. package/src/widgets/passage-ref.jsx +198 -0
  465. package/src/widgets/passage.jsx +601 -0
  466. package/src/widgets/plotter.jsx +1198 -0
  467. package/src/widgets/radio/__stories__/base-radio.stories.jsx +40 -0
  468. package/src/widgets/radio/__stories__/choice-icon.stories.jsx +157 -0
  469. package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +26 -0
  470. package/src/widgets/radio/__stories__/cross-out-button.stories.jsx +64 -0
  471. package/src/widgets/radio/__stories__/cross-out-menu.stories.jsx +59 -0
  472. package/src/widgets/radio/__stories__/cross-out-tooltip.stories.jsx +59 -0
  473. package/src/widgets/radio/__stories__/focus-ring.stories.jsx +38 -0
  474. package/src/widgets/radio/__stories__/option-status.stories.jsx +118 -0
  475. package/src/widgets/radio/__stories__/toggleable-radio-button.stories.jsx +28 -0
  476. package/src/widgets/radio/base-radio.jsx +642 -0
  477. package/src/widgets/radio/choice-icon.jsx +468 -0
  478. package/src/widgets/radio/choice-none-above.jsx +54 -0
  479. package/src/widgets/radio/choice.jsx +692 -0
  480. package/src/widgets/radio/choice.jsx.fixture.js +64 -0
  481. package/src/widgets/radio/cross-out-button.jsx +136 -0
  482. package/src/widgets/radio/cross-out-menu-wrapper.jsx +492 -0
  483. package/src/widgets/radio/cross-out-tooltip.jsx +129 -0
  484. package/src/widgets/radio/focus-ring.jsx +51 -0
  485. package/src/widgets/radio/option-status.jsx +89 -0
  486. package/src/widgets/radio/toggleable-radio-button.jsx +133 -0
  487. package/src/widgets/radio/util.js +32 -0
  488. package/src/widgets/radio/util_test.js +19 -0
  489. package/src/widgets/radio/widget.jsx +470 -0
  490. package/src/widgets/radio.jsx +156 -0
  491. package/src/widgets/reaction-diagram.jsx +136 -0
  492. package/src/widgets/sequence.jsx +140 -0
  493. package/src/widgets/simulator.jsx +808 -0
  494. package/src/widgets/sorter.jsx +146 -0
  495. package/src/widgets/table.jsx +395 -0
  496. package/src/widgets/transformer.jsx +2870 -0
  497. package/src/widgets/unit.jsx +371 -0
  498. package/src/widgets/video-transcript-link.jsx +84 -0
  499. package/src/widgets/video-transcript-link_test.js +211 -0
  500. package/src/widgets/video.jsx +131 -0
  501. package/src/widgets.js +431 -0
  502. package/src/zoom.js +539 -0
  503. package/src/__tests__/index.test.js +0 -14
@@ -0,0 +1,131 @@
1
+ // @flow
2
+ /**
3
+ * This is a video widget for embedding videos in articles.
4
+ */
5
+
6
+ import {View} from "@khanacademy/wonder-blocks-core";
7
+ import * as i18n from "@khanacademy/wonder-blocks-i18n";
8
+ import * as React from "react";
9
+ import _ from "underscore";
10
+
11
+ import FixedToResponsive from "../components/fixed-to-responsive.jsx";
12
+ import {getDependencies} from "../dependencies.js";
13
+ import * as Changeable from "../mixins/changeable.jsx";
14
+ import a11y from "../util/a11y.js";
15
+
16
+ import VideoTranscriptLink from "./video-transcript-link.jsx";
17
+
18
+ import type {PerseusVideoWidgetOptions} from "../perseus-types.js";
19
+ import type {
20
+ PerseusScore,
21
+ WidgetExports,
22
+ WidgetProps,
23
+ } from "../types.js";
24
+
25
+ // Current default is 720p, based on the typical videos we upload currently
26
+ const DEFAULT_WIDTH = 1280;
27
+ const DEFAULT_HEIGHT = 720;
28
+
29
+ const KA_EMBED = "{host}/embed_video?slug={slug}" + "&internal_video_only=1";
30
+ const IS_URL = /^https?:\/\//;
31
+ const IS_KA_SITE = /(khanacademy\.org|localhost)/;
32
+
33
+ type UserInput = null;
34
+ type Rubric = PerseusVideoWidgetOptions;
35
+ type RenderProps = PerseusVideoWidgetOptions; // exports has no 'transform'
36
+ type Props = {|
37
+ ...WidgetProps<RenderProps, Rubric>,
38
+ alignment: string, // Where does this get set?
39
+ |};
40
+
41
+ /**
42
+ * Video renderer.
43
+ */
44
+ class Video extends React.Component<Props> {
45
+ /**
46
+ * This is the widget's grading function.
47
+ * Points for videos are tallied by the embedded video itself, in the case
48
+ * of Khan Academy videos.
49
+ */
50
+ static validate(userInput: UserInput, rubric: Rubric): PerseusScore {
51
+ return {
52
+ type: "points",
53
+ earned: 0,
54
+ total: 0,
55
+ message: null,
56
+ };
57
+ }
58
+
59
+ getUserInput: () => ?void = () => {
60
+ return null;
61
+ };
62
+
63
+ simpleValidate: (Rubric) => PerseusScore = (rubric) => {
64
+ return Video.validate(null, rubric);
65
+ };
66
+
67
+ change: (...args: $ReadOnlyArray<mixed>) => $FlowFixMe = (...args) => {
68
+ // $FlowFixMe[incompatible-call]
69
+ return Changeable.change.apply(this, args);
70
+ };
71
+
72
+ render(): React.Node {
73
+ const {InitialRequestUrl} = getDependencies();
74
+
75
+ const location = this.props.location;
76
+ if (!location) {
77
+ return <div />;
78
+ }
79
+
80
+ let url;
81
+
82
+ if (IS_URL.test(location)) {
83
+ url = location;
84
+ } else {
85
+ url = KA_EMBED.replace("{slug}", location);
86
+ // eslint-disable-next-line static-service/require-safe-link-to
87
+ let embedHostname = "https://www.khanacademy.org";
88
+ if (IS_KA_SITE.test(InitialRequestUrl.host)) {
89
+ embedHostname = InitialRequestUrl.origin;
90
+ }
91
+ url = url.replace("{host}", embedHostname);
92
+ }
93
+
94
+ return (
95
+ <View>
96
+ <FixedToResponsive
97
+ width={DEFAULT_WIDTH}
98
+ height={DEFAULT_HEIGHT}
99
+ // The key is here for the benefit of the editor, to ensure that
100
+ // any changes cause a re-rendering of the frame.
101
+ key={location + this.props.alignment}
102
+ >
103
+ <View style={a11y.srOnly}>
104
+ {i18n._("Khan Academy video wrapper")}
105
+ </View>
106
+
107
+ <iframe
108
+ // TODO(joshuan): Consider not using iframes when we're
109
+ // loading this from webapp. This iframe is problematic
110
+ // for screenreaders.
111
+ className="perseus-video-widget"
112
+ sandbox="allow-same-origin allow-scripts"
113
+ width={DEFAULT_WIDTH}
114
+ height={DEFAULT_HEIGHT}
115
+ src={url}
116
+ allowFullScreen={true}
117
+ />
118
+ </FixedToResponsive>
119
+ <VideoTranscriptLink location={location} />
120
+ </View>
121
+ );
122
+ }
123
+ }
124
+
125
+ export default ({
126
+ name: "video",
127
+ displayName: "Video",
128
+ defaultAlignment: "block",
129
+ supportedAlignments: ["block", "float-left", "float-right", "full-width"],
130
+ widget: Video,
131
+ }: WidgetExports<typeof Video>);
package/src/widgets.js ADDED
@@ -0,0 +1,431 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import _ from "underscore";
4
+
5
+ import {Errors, Log} from "./logging/log.js";
6
+ import {PerseusError} from "./perseus-error.js";
7
+
8
+ import type {
9
+ Alignment,
10
+ Tracking,
11
+ Version,
12
+ WidgetExports,
13
+ WidgetInfo,
14
+ WidgetTransform,
15
+ } from "./types.js";
16
+
17
+ const DEFAULT_ALIGNMENT = "block";
18
+ // NOTE(kevinb): "default" is not one in `validAlignments`.
19
+ const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
20
+ const DEFAULT_STATIC = false;
21
+ const DEFAULT_TRACKING = "";
22
+ const DEFAULT_LINTABLE = false;
23
+
24
+ type Editor = $FlowFixMe;
25
+
26
+ const widgets: {|[string]: WidgetExports<>|} = {};
27
+ const editors = {};
28
+
29
+ // Widgets must be registered to avoid circular dependencies with the
30
+ // core Editor and Renderer components.
31
+ // TODO(jeremy): The widget name is already embedded in the WidgetExports type
32
+ // so could we drop the `name` parameter here?
33
+ export const registerWidget = (name: string, widget: WidgetExports<>) => {
34
+ widgets[name] = widget;
35
+ };
36
+
37
+ export const registerWidgets = (widgets: $ReadOnlyArray<WidgetExports<>>) => {
38
+ widgets.forEach((widget) => {
39
+ registerWidget(widget.name, widget);
40
+ });
41
+
42
+ validateAlignments();
43
+ };
44
+
45
+ export const registerEditors = (editorsToRegister: $ReadOnlyArray<Editor>) => {
46
+ editorsToRegister.forEach((editor) => {
47
+ if (!editor.widgetName) {
48
+ throw new PerseusError(
49
+ `Editor ${editor.displayName} doesn't have a widgetName property`,
50
+ Errors.Internal,
51
+ );
52
+ }
53
+ editors[editor.widgetName] = editor;
54
+ });
55
+ };
56
+
57
+ export const getWidget = (name: string): ?React.ComponentType<any> => {
58
+ // TODO(alex): Consider referring to these as renderers to avoid
59
+ // overloading "widget"
60
+ if (!_.has(widgets, name)) {
61
+ return null;
62
+ }
63
+
64
+ // Allow widgets to specify a widget directly or via a function
65
+ if (widgets[name].getWidget) {
66
+ return widgets[name].getWidget();
67
+ }
68
+ return widgets[name].widget;
69
+ };
70
+
71
+ export const getEditor = (name: string): ?Editor => {
72
+ return _.has(editors, name) ? editors[name] : null;
73
+ };
74
+
75
+ export const getTransform = (name: string): ?WidgetTransform => {
76
+ return _.has(widgets, name) ? widgets[name].transform || _.identity : null;
77
+ };
78
+
79
+ export const getVersion = (name: string): ?Version => {
80
+ const widgetInfo = widgets[name];
81
+ if (widgetInfo) {
82
+ return widgets[name].version || {major: 0, minor: 0};
83
+ }
84
+ return null;
85
+ };
86
+
87
+ export const getVersionVector = (): {|[string]: Version|} => {
88
+ const version = {};
89
+ _.each(_.keys(widgets), function (name) {
90
+ version[name] = getVersion(name);
91
+ });
92
+ return version;
93
+ };
94
+
95
+ export const getPublicWidgets = (): $ReadOnlyArray<WidgetExports<>> => {
96
+ // TODO(alex): Update underscore.js so that _.pick can take a function.
97
+ return _.pick(
98
+ widgets,
99
+ _.reject(_.keys(widgets), function (name) {
100
+ return widgets[name].hidden;
101
+ }),
102
+ );
103
+ };
104
+
105
+ export const isAccessible = (widgetInfo: WidgetInfo): boolean => {
106
+ const accessible = widgets[widgetInfo.type].accessible;
107
+ if (typeof accessible === "function") {
108
+ return accessible(widgetInfo.options);
109
+ }
110
+ return !!accessible;
111
+ };
112
+
113
+ export const getAllWidgetTypes = (): $ReadOnlyArray<string> => {
114
+ return _.keys(widgets);
115
+ };
116
+
117
+ export const upgradeWidgetInfoToLatestVersion = (
118
+ oldWidgetInfo: WidgetInfo,
119
+ ): WidgetInfo => {
120
+ const type = oldWidgetInfo.type;
121
+ // TODO(LP-10707): Remove unnecessary type checking (`type` is a string)
122
+ if (!_.isString(type)) {
123
+ throw new PerseusError(
124
+ "widget type must be a string, but was: " + type,
125
+ Errors.Internal,
126
+ );
127
+ }
128
+ const widgetExports = widgets[type];
129
+
130
+ if (widgetExports == null) {
131
+ // If we have a widget that isn't registered, we can't upgrade it
132
+ // TODO(aria): Figure out what the best thing to do here would be
133
+ return oldWidgetInfo;
134
+ }
135
+
136
+ // Unversioned widgets (pre-July 2014) are all implicitly 0.0
137
+ const initialVersion = oldWidgetInfo.version || {major: 0, minor: 0};
138
+ const latestVersion = widgetExports.version || {major: 0, minor: 0};
139
+
140
+ // If the widget version is later than what we understand (major
141
+ // version is higher than latest, or major versions are equal and minor
142
+ // version is higher than latest), don't perform any upgrades.
143
+ if (
144
+ initialVersion.major > latestVersion.major ||
145
+ (initialVersion.major === latestVersion.major &&
146
+ initialVersion.minor > latestVersion.minor)
147
+ ) {
148
+ return oldWidgetInfo;
149
+ }
150
+
151
+ // We do a clone here so that it's safe to mutate the input parameter
152
+ // in propUpgrades functions (which I will probably accidentally do at
153
+ // some point, and we would like to not break when that happens).
154
+ let newEditorProps = _.clone(oldWidgetInfo.options) || {};
155
+
156
+ const upgradePropsMap = widgetExports.propUpgrades || {};
157
+
158
+ // Empty props usually mean a newly created widget by the editor,
159
+ // and are always considerered up-to-date.
160
+ // Mostly, we'd rather not run upgrade functions on props that are
161
+ // not complete.
162
+ if (_.keys(newEditorProps).length !== 0) {
163
+ // We loop through all the versions after the current version of
164
+ // the loaded widget, up to and including the latest version of the
165
+ // loaded widget, and run the upgrade function to bring our loaded
166
+ // widget's props up to that version.
167
+ // There is a little subtlety here in that we call
168
+ // upgradePropsMap[1] to upgrade *to* version 1,
169
+ // (not from version 1).
170
+ for (
171
+ let nextVersion = initialVersion.major + 1;
172
+ nextVersion <= latestVersion.major;
173
+ nextVersion++
174
+ ) {
175
+ if (upgradePropsMap[String(nextVersion)]) {
176
+ newEditorProps =
177
+ upgradePropsMap[String(nextVersion)](newEditorProps);
178
+ } else {
179
+ // This is a Log.error because it is unlikely to be hit in
180
+ // local testing, and a Log.error is slightly less scary in
181
+ // prod than a `throw new Error`
182
+ Log.error(
183
+ "No upgrade found for widget. Cannot render.",
184
+ Errors.Internal,
185
+ {
186
+ loggedMetadata: {
187
+ type,
188
+ fromMajorVersion: nextVersion - 1,
189
+ toMajorVersion: nextVersion,
190
+ },
191
+ },
192
+ );
193
+ // But try to keep going anyways (yolo!)
194
+ // (Throwing an error here would just break the page
195
+ // silently anyways, so that doesn't seem much better
196
+ // than a halfhearted attempt to continue, however
197
+ // shallow...)
198
+ }
199
+ }
200
+ }
201
+
202
+ // Minor version upgrades (eg. new optional props) don't have
203
+ // transform functions. Instead, we fill in the new props with their
204
+ // defaults.
205
+ const defaultProps = type in editors ? editors[type].defaultProps : {};
206
+ newEditorProps = {
207
+ ...defaultProps,
208
+ ...newEditorProps,
209
+ };
210
+
211
+ let alignment = oldWidgetInfo.alignment;
212
+
213
+ // Widgets that support multiple alignments will "lock in" the
214
+ // alignment to the alignment that would be listed first in the
215
+ // select box. If the widget only supports one alignment, the
216
+ // alignment value will likely just end up as "default".
217
+ if (alignment == null || alignment === "default") {
218
+ alignment = getSupportedAlignments(type)[0];
219
+ }
220
+
221
+ let widgetStatic = oldWidgetInfo.static;
222
+
223
+ if (widgetStatic == null) {
224
+ widgetStatic = DEFAULT_STATIC;
225
+ }
226
+
227
+ return _.extend({}, oldWidgetInfo, {
228
+ // maintain other info, like type
229
+ // After upgrading we guarantee that the version is up-to-date
230
+ version: latestVersion,
231
+ // Default graded to true (so null/undefined becomes true):
232
+ graded: oldWidgetInfo.graded != null ? oldWidgetInfo.graded : true,
233
+ alignment: alignment,
234
+ static: widgetStatic,
235
+ options: newEditorProps,
236
+ });
237
+ };
238
+
239
+ export const getRendererPropsForWidgetInfo = (
240
+ widgetInfo: WidgetInfo,
241
+ problemNum?: number,
242
+ ): $FlowFixMe => {
243
+ const type = widgetInfo.type;
244
+ const widgetExports = widgets[type];
245
+ if (widgetExports == null) {
246
+ // The widget is not a registered widget
247
+ // It shouldn't matter what we return here, but for consistency
248
+ // we return the untransformed options, as if the widget did
249
+ // not have a transform defined.
250
+ return widgetInfo.options;
251
+ }
252
+ let transform;
253
+ if (widgetInfo.static) {
254
+ // There aren't a lot of real places where we'll have to default to
255
+ // _.identity, but it's theoretically possible if someone changes
256
+ // the JSON manually / we have to back out static support for a
257
+ // widget.
258
+ transform = getStaticTransform(type) || _.identity;
259
+ } else {
260
+ transform = widgetExports.transform || _.identity;
261
+ }
262
+ // widgetInfo.options are the widgetEditor's props:
263
+ return transform(widgetInfo.options, problemNum);
264
+ };
265
+
266
+ export const traverseChildWidgets = (
267
+ widgetInfo: WidgetInfo,
268
+ traverseRenderer: $FlowFixMe,
269
+ ): WidgetInfo => {
270
+ if (!traverseRenderer) {
271
+ throw new PerseusError(
272
+ "traverseRenderer must be provided, but was not",
273
+ Errors.Internal,
274
+ );
275
+ }
276
+
277
+ if (!widgetInfo || !widgetInfo.type || !widgets[widgetInfo.type]) {
278
+ return widgetInfo;
279
+ }
280
+
281
+ const widgetExports = widgets[widgetInfo.type];
282
+ const props = widgetInfo.options;
283
+
284
+ if (widgetExports.traverseChildWidgets && props) {
285
+ const newProps = widgetExports.traverseChildWidgets(
286
+ props,
287
+ traverseRenderer,
288
+ );
289
+ return _.extend({}, widgetInfo, {options: newProps});
290
+ }
291
+ return widgetInfo;
292
+ };
293
+
294
+ /**
295
+ * Handling for the optional alignments for widgets
296
+ * See widget-container.jsx for details on how alignments are implemented.
297
+ */
298
+
299
+ /**
300
+ * Returns the list of supported alignments for the given (string) widget
301
+ * type. This is used primarily at editing time to display the choices
302
+ * for the user.
303
+ *
304
+ * Supported alignments are given as an array of strings in the exports of
305
+ * a widget's module.
306
+ */
307
+ export const getSupportedAlignments = (
308
+ type: string,
309
+ ): $ReadOnlyArray<Alignment> => {
310
+ const widgetExport = widgets[type];
311
+ return (
312
+ (widgetExport && widgetExport.supportedAlignments) ||
313
+ DEFAULT_SUPPORTED_ALIGNMENTS
314
+ );
315
+ };
316
+
317
+ /**
318
+ * For the given (string) widget type, determine the default alignment for
319
+ * the widget. This is used at rendering time to go from "default" alignment
320
+ * to the actual alignment displayed on the screen.
321
+ *
322
+ * The default alignment is given either as a string (called
323
+ * `defaultAlignment`) or a function (called `getDefaultAlignment`) on
324
+ * the exports of a widget's module.
325
+ */
326
+ export const getDefaultAlignment = (type: string): Alignment => {
327
+ const widgetExports = widgets[type];
328
+ let alignment;
329
+ if (!widgetExports) {
330
+ return DEFAULT_ALIGNMENT;
331
+ }
332
+
333
+ if (widgetExports.getDefaultAlignment) {
334
+ alignment = widgetExports.getDefaultAlignment();
335
+ } else {
336
+ alignment = widgetExports.defaultAlignment;
337
+ }
338
+ return alignment || DEFAULT_ALIGNMENT;
339
+ };
340
+
341
+ const validAlignments: $ReadOnlyArray<Alignment> = [
342
+ "block",
343
+ "inline-block",
344
+ "inline",
345
+ "float-left",
346
+ "float-right",
347
+ "full-width",
348
+ ];
349
+
350
+ /**
351
+ * Used at startup to fail fast if an alignment given by a widget is
352
+ * invalid.
353
+ */
354
+ // TODO(alex): Change this to run as a testcase (vs. being run at runtime)
355
+ // TODO(LP-10707): I think this can be completely removed because our Flow types
356
+ // enforce this!
357
+ export const validateAlignments = () => {
358
+ _.each(widgets, function (widgetInfo) {
359
+ if (
360
+ widgetInfo.defaultAlignment &&
361
+ !_.contains(validAlignments, widgetInfo.defaultAlignment)
362
+ ) {
363
+ throw new PerseusError(
364
+ "Widget '" +
365
+ widgetInfo.displayName +
366
+ "' has an invalid defaultAlignment value: " +
367
+ widgetInfo.defaultAlignment,
368
+ Errors.InvalidInput,
369
+ );
370
+ }
371
+
372
+ if (widgetInfo.supportedAlignments) {
373
+ const unknownAlignments = _.difference(
374
+ widgetInfo.supportedAlignments,
375
+ validAlignments,
376
+ );
377
+
378
+ if (unknownAlignments.length) {
379
+ throw new PerseusError(
380
+ "Widget '" +
381
+ widgetInfo.displayName +
382
+ "' has an invalid value for supportedAlignments: " +
383
+ unknownAlignments.join(" "),
384
+ Errors.InvalidInput,
385
+ );
386
+ }
387
+ }
388
+ });
389
+ };
390
+
391
+ /**
392
+ * Handling for static mode for widgets that support it.
393
+ */
394
+
395
+ /**
396
+ * Returns true iff the widget supports static mode.
397
+ * A widget implicitly supports static mode if it exports a
398
+ * staticTransform function.
399
+ */
400
+ export const supportsStaticMode = (type: string): boolean => {
401
+ const widgetInfo = widgets[type];
402
+ return widgetInfo && widgetInfo.staticTransform != null;
403
+ };
404
+
405
+ /**
406
+ * Return the staticTransform function used to convert the editorProps to
407
+ * the rendered widget state.
408
+ */
409
+ export const getStaticTransform = (type: string): ?WidgetTransform => {
410
+ const widgetInfo = widgets[type];
411
+ return widgetInfo && widgetInfo.staticTransform;
412
+ };
413
+
414
+ /**
415
+ * Returns the tracking option for the widget. The default is "",
416
+ * which means simply to track interactions once. The other available
417
+ * option is "all" which means to track all interactions.
418
+ */
419
+ export const getTracking = (type: string): Tracking => {
420
+ const widgetExport = widgets[type];
421
+ return (widgetExport && widgetExport.tracking) || DEFAULT_TRACKING;
422
+ };
423
+
424
+ /**
425
+ * Returns true if this widget can include lintable markdown text
426
+ * and supports a highlightLint prop, or false otherwise.
427
+ */
428
+ export const isLintable = (type: string): boolean => {
429
+ const widgetExports = widgets[type];
430
+ return (widgetExports && widgetExports.isLintable) || DEFAULT_LINTABLE;
431
+ };