@khanacademy/perseus 0.0.1 → 0.2.1

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 (507) hide show
  1. package/.babelrc.js +1 -1
  2. package/CHANGELOG.md +29 -0
  3. package/dist/es/index.css +4361 -0
  4. package/dist/es/index.js +7 -1
  5. package/dist/es/index.js.map +1 -1
  6. package/dist/index.css +4361 -0
  7. package/dist/index.js +7 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/strings.js +148 -46
  10. package/package.json +50 -4
  11. package/src/README.md +35 -0
  12. package/src/__stories__/item-renderer.stories.jsx +27 -0
  13. package/src/__stories__/server-item-renderer.stories.jsx +27 -0
  14. package/src/__testdata__/item-renderer_testdata.js +161 -0
  15. package/src/__testdata__/renderer_testdata.js +123 -0
  16. package/src/__tests__/__snapshots__/item-renderer_test.jsx.snap +233 -0
  17. package/src/__tests__/__snapshots__/perseus-markdown_test.jsx.snap +516 -0
  18. package/src/__tests__/__snapshots__/pure-markdown_test.js.snap +113 -0
  19. package/src/__tests__/__snapshots__/renderer_test.jsx.snap +576 -0
  20. package/src/__tests__/__snapshots__/server-item-renderer_test.jsx.snap +306 -0
  21. package/src/__tests__/a11y_test.jsx +304 -0
  22. package/src/__tests__/item-renderer_test.jsx +892 -0
  23. package/src/__tests__/mock-asset-loading-widget.jsx +57 -0
  24. package/src/__tests__/mock-widget.jsx +35 -0
  25. package/src/__tests__/perseus-markdown_test.jsx +747 -0
  26. package/src/__tests__/pure-markdown_test.js +552 -0
  27. package/src/__tests__/renderability_test.jsx +481 -0
  28. package/src/__tests__/renderer-api_test.jsx +333 -0
  29. package/src/__tests__/renderer_test.jsx +1792 -0
  30. package/src/__tests__/server-item-renderer_test.jsx +516 -0
  31. package/src/__tests__/test-items/image-item.js +25 -0
  32. package/src/__tests__/test-items/input-number-1-item.js +27 -0
  33. package/src/__tests__/test-items/input-number-2-item.js +39 -0
  34. package/src/__tests__/test-items/table-item.js +30 -0
  35. package/src/__tests__/tex-wrangler_test.js +83 -0
  36. package/src/__tests__/util_test.js +41 -0
  37. package/src/a11y.js +46 -0
  38. package/src/all-widgets.js +9 -0
  39. package/src/article-renderer.jsx +215 -0
  40. package/src/asset-context.js +30 -0
  41. package/src/basic-widgets.js +13 -0
  42. package/src/components/__stories__/button-group.stories.jsx +61 -0
  43. package/src/components/__stories__/fixed-to-responsive.stories.jsx +132 -0
  44. package/src/components/__stories__/graph.stories.jsx +27 -0
  45. package/src/components/__stories__/graphie.stories.jsx +28 -0
  46. package/src/components/__stories__/hud.stories.jsx +36 -0
  47. package/src/components/__stories__/icon.stories.jsx +56 -0
  48. package/src/components/__stories__/image-loader.stories.jsx +60 -0
  49. package/src/components/__stories__/info-tip.stories.jsx +33 -0
  50. package/src/components/__stories__/inline-icon.stories.jsx +39 -0
  51. package/src/components/__stories__/input-with-examples.stories.jsx +51 -0
  52. package/src/components/__stories__/lint.stories.jsx +40 -0
  53. package/src/components/__stories__/math-input.stories.jsx +29 -0
  54. package/src/components/__stories__/math-output.stories.jsx +24 -0
  55. package/src/components/__stories__/multi-button-group.stories.jsx +69 -0
  56. package/src/components/__stories__/number-input.stories.jsx +42 -0
  57. package/src/components/__stories__/possible-answers.stories.jsx +22 -0
  58. package/src/components/__stories__/prop-check-box.stories.jsx +62 -0
  59. package/src/components/__stories__/range-input.stories.jsx +28 -0
  60. package/src/components/__stories__/simple-keypad-input.stories.jsx +28 -0
  61. package/src/components/__stories__/sortable.stories.jsx +73 -0
  62. package/src/components/__stories__/stub-tag-editor.stories.jsx +44 -0
  63. package/src/components/__stories__/svg-image.stories.jsx +60 -0
  64. package/src/components/__stories__/tex.stories.jsx +18 -0
  65. package/src/components/__stories__/text-buttons.stories.jsx +51 -0
  66. package/src/components/__stories__/text-input.stories.jsx +34 -0
  67. package/src/components/__stories__/text-list-editor.stories.jsx +33 -0
  68. package/src/components/__stories__/zoomable-tex.stories.jsx +39 -0
  69. package/src/components/__stories__/zoomable.stories.jsx +40 -0
  70. package/src/components/__testdata__/sorter_testdata.js +20 -0
  71. package/src/components/__tests__/__snapshots__/sortable_test.jsx.snap +79 -0
  72. package/src/components/__tests__/__snapshots__/sorter_test.jsx.snap +297 -0
  73. package/src/components/__tests__/number-input_test.jsx +112 -0
  74. package/src/components/__tests__/sortable_test.jsx +234 -0
  75. package/src/components/__tests__/sorter_test.jsx +111 -0
  76. package/src/components/__tests__/zoomable_test.jsx +388 -0
  77. package/src/components/button-group.jsx +131 -0
  78. package/src/components/constants.js +15 -0
  79. package/src/components/fixed-to-responsive.jsx +164 -0
  80. package/src/components/graph.jsx +415 -0
  81. package/src/components/graphie-classes.jsx +148 -0
  82. package/src/components/graphie-movables.jsx +342 -0
  83. package/src/components/graphie.jsx +353 -0
  84. package/src/components/highlighting/highlightable-content.jsx +245 -0
  85. package/src/components/highlighting/highlights.js +208 -0
  86. package/src/components/highlighting/ranges.js +299 -0
  87. package/src/components/highlighting/types.js +73 -0
  88. package/src/components/highlighting/ui/highlight-renderer.jsx +179 -0
  89. package/src/components/highlighting/ui/highlight-set-renderer.jsx +190 -0
  90. package/src/components/highlighting/ui/highlight-tooltip.jsx +153 -0
  91. package/src/components/highlighting/ui/highlighting-ui.jsx +163 -0
  92. package/src/components/highlighting/ui/selection-tracker.jsx +195 -0
  93. package/src/components/highlighting/ui/types.js +21 -0
  94. package/src/components/highlighting/ui/util.js +166 -0
  95. package/src/components/highlighting/word-indexer.jsx +155 -0
  96. package/src/components/hud.jsx +179 -0
  97. package/src/components/icon.jsx +149 -0
  98. package/src/components/image-loader.jsx +175 -0
  99. package/src/components/info-tip/info-tip.jsx +146 -0
  100. package/src/components/info-tip.jsx +38 -0
  101. package/src/components/inline-icon.jsx +81 -0
  102. package/src/components/input-with-examples.jsx +231 -0
  103. package/src/components/lint.jsx +454 -0
  104. package/src/components/math-input.jsx +282 -0
  105. package/src/components/math-output.jsx +128 -0
  106. package/src/components/multi-button-group.jsx +137 -0
  107. package/src/components/new-tooltip/README.md +110 -0
  108. package/src/components/new-tooltip/__stories__/flexible-position.stories.jsx +437 -0
  109. package/src/components/new-tooltip/__stories__/new-tooltip.stories.jsx +665 -0
  110. package/src/components/new-tooltip/__stories__/tooltip-arrow.stories.jsx +70 -0
  111. package/src/components/new-tooltip/__stories__/tooltip-bubble.stories.jsx +98 -0
  112. package/src/components/new-tooltip/__tests__/style-constants_test.js +8 -0
  113. package/src/components/new-tooltip/__tests__/util_test.js +47 -0
  114. package/src/components/new-tooltip/flexible-position.jsx +182 -0
  115. package/src/components/new-tooltip/new-tooltip.jsx +399 -0
  116. package/src/components/new-tooltip/style-constants.js +13 -0
  117. package/src/components/new-tooltip/tooltip-arrow.jsx +184 -0
  118. package/src/components/new-tooltip/tooltip-bubble.jsx +223 -0
  119. package/src/components/new-tooltip/tooltip-portal.jsx +101 -0
  120. package/src/components/new-tooltip/tooltip-positioner.jsx +529 -0
  121. package/src/components/new-tooltip/types.js +183 -0
  122. package/src/components/new-tooltip/util.js +27 -0
  123. package/src/components/number-input.jsx +253 -0
  124. package/src/components/possible-answers.jsx +36 -0
  125. package/src/components/prop-check-box.jsx +80 -0
  126. package/src/components/range-input.jsx +61 -0
  127. package/src/components/simple-keypad-input.jsx +91 -0
  128. package/src/components/sortable.jsx +873 -0
  129. package/src/components/stub-tag-editor.jsx +54 -0
  130. package/src/components/svg-image.jsx +762 -0
  131. package/src/components/tex-buttons.jsx +322 -0
  132. package/src/components/tex.jsx +70 -0
  133. package/src/components/text-input.jsx +82 -0
  134. package/src/components/text-list-editor.jsx +157 -0
  135. package/src/components/tooltip.jsx +395 -0
  136. package/src/components/visibility-observer/__stories__/visibility-observer.stories.jsx +140 -0
  137. package/src/components/visibility-observer/get-scroll-parent.js +45 -0
  138. package/src/components/visibility-observer/visibility-observer.js +109 -0
  139. package/src/components/visibility-observer/visibility-observer.jsx +123 -0
  140. package/src/components/zoomable-tex.jsx +70 -0
  141. package/src/components/zoomable.jsx +304 -0
  142. package/src/components.js +17 -0
  143. package/src/dependencies.js +22 -0
  144. package/src/error-boundary.jsx +48 -0
  145. package/src/extra-widgets.js +75 -0
  146. package/src/gorgon/README +40 -0
  147. package/src/gorgon/__tests__/matcher_test.js +497 -0
  148. package/src/gorgon/__tests__/rule_test.js +101 -0
  149. package/src/gorgon/__tests__/rules_test.js +487 -0
  150. package/src/gorgon/__tests__/selector-parser_test.js +52 -0
  151. package/src/gorgon/__tests__/tree-transformer_test.js +446 -0
  152. package/src/gorgon/gorgon.js +290 -0
  153. package/src/gorgon/proptypes.js +29 -0
  154. package/src/gorgon/rule.js +413 -0
  155. package/src/gorgon/rules/absolute-url.js +24 -0
  156. package/src/gorgon/rules/all-rules.js +72 -0
  157. package/src/gorgon/rules/blockquoted-math.js +10 -0
  158. package/src/gorgon/rules/blockquoted-widget.js +10 -0
  159. package/src/gorgon/rules/double-spacing-after-terminal.js +12 -0
  160. package/src/gorgon/rules/extra-content-spacing.js +12 -0
  161. package/src/gorgon/rules/heading-level-1.js +14 -0
  162. package/src/gorgon/rules/heading-level-skip.js +20 -0
  163. package/src/gorgon/rules/heading-sentence-case.js +11 -0
  164. package/src/gorgon/rules/heading-title-case.js +63 -0
  165. package/src/gorgon/rules/image-alt-text.js +21 -0
  166. package/src/gorgon/rules/image-in-table.js +10 -0
  167. package/src/gorgon/rules/image-spaces-around-urls.js +35 -0
  168. package/src/gorgon/rules/image-widget.js +50 -0
  169. package/src/gorgon/rules/link-click-here.js +11 -0
  170. package/src/gorgon/rules/lint-utils.js +48 -0
  171. package/src/gorgon/rules/long-paragraph.js +14 -0
  172. package/src/gorgon/rules/math-adjacent.js +10 -0
  173. package/src/gorgon/rules/math-align-extra-break.js +11 -0
  174. package/src/gorgon/rules/math-align-linebreaks.js +43 -0
  175. package/src/gorgon/rules/math-empty.js +10 -0
  176. package/src/gorgon/rules/math-font-size.js +12 -0
  177. package/src/gorgon/rules/math-frac.js +10 -0
  178. package/src/gorgon/rules/math-nested.js +11 -0
  179. package/src/gorgon/rules/math-starts-with-space.js +12 -0
  180. package/src/gorgon/rules/math-text-empty.js +10 -0
  181. package/src/gorgon/rules/math-without-dollars.js +14 -0
  182. package/src/gorgon/rules/nested-lists.js +11 -0
  183. package/src/gorgon/rules/profanity.js +10 -0
  184. package/src/gorgon/rules/table-missing-cells.js +20 -0
  185. package/src/gorgon/rules/unbalanced-code-delimiters.js +14 -0
  186. package/src/gorgon/rules/unescaped-dollar.js +10 -0
  187. package/src/gorgon/rules/widget-in-table.js +10 -0
  188. package/src/gorgon/selector.js +506 -0
  189. package/src/gorgon/tree-transformer.js +588 -0
  190. package/src/hint-renderer.jsx +159 -0
  191. package/src/hints-renderer.jsx +359 -0
  192. package/src/icon-paths.js +120 -0
  193. package/src/index.js +149 -2
  194. package/src/init.js +49 -0
  195. package/src/interaction-tracker.js +60 -0
  196. package/src/interactive2/__tests__/movable-mock.js +48 -0
  197. package/src/interactive2/__tests__/movable-point_test.js +146 -0
  198. package/src/interactive2/interactive-util.js +152 -0
  199. package/src/interactive2/movable-helper-methods.js +160 -0
  200. package/src/interactive2/movable-line-options.js +392 -0
  201. package/src/interactive2/movable-line.js +322 -0
  202. package/src/interactive2/movable-point-options.js +190 -0
  203. package/src/interactive2/movable-point.jsx +603 -0
  204. package/src/interactive2/movable-polygon-options.js +351 -0
  205. package/src/interactive2/movable-polygon.js +368 -0
  206. package/src/interactive2/movable.js +273 -0
  207. package/src/interactive2/objective_.js +79 -0
  208. package/src/interactive2/types.js +3 -0
  209. package/src/interactive2/wrapped-defaults.js +86 -0
  210. package/src/interactive2/wrapped-ellipse.js +87 -0
  211. package/src/interactive2/wrapped-line.js +109 -0
  212. package/src/interactive2/wrapped-path.js +47 -0
  213. package/src/interactive2.js +91 -0
  214. package/src/item-renderer.jsx +518 -0
  215. package/src/item-version.js +12 -0
  216. package/src/jipt-paragraphs.js +44 -0
  217. package/src/jquery.mobile.vmouse.js +545 -0
  218. package/src/keypad-context.js +33 -0
  219. package/src/loading-context.js +20 -0
  220. package/src/logging/log.js +107 -0
  221. package/src/logging/log_test.js +41 -0
  222. package/src/mixins/changeable.jsx +115 -0
  223. package/src/mixins/editor-jsonify.jsx +13 -0
  224. package/src/mixins/provide-keypad.jsx +129 -0
  225. package/src/mixins/widget-jsonify-deprecated.jsx +23 -0
  226. package/src/mixins/widget-prop-blacklist.js +36 -0
  227. package/src/multi-items/__stories__/multi-renderer.stories.js +88 -0
  228. package/src/multi-items/__testdata__/multi-renderer_testdata.js +174 -0
  229. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +994 -0
  230. package/src/multi-items/__tests__/items_test.js +245 -0
  231. package/src/multi-items/__tests__/multi-renderer_test.js +903 -0
  232. package/src/multi-items/__tests__/prop-type-builders_test.js +134 -0
  233. package/src/multi-items/__tests__/shapes_test.js +44 -0
  234. package/src/multi-items/__tests__/trees_test.js +316 -0
  235. package/src/multi-items/item-types.js +55 -0
  236. package/src/multi-items/items.js +176 -0
  237. package/src/multi-items/multi-renderer.jsx +543 -0
  238. package/src/multi-items/prop-type-builders.js +105 -0
  239. package/src/multi-items/shape-types.js +47 -0
  240. package/src/multi-items/shapes.js +53 -0
  241. package/src/multi-items/tree-types.js +42 -0
  242. package/src/multi-items/trees.js +345 -0
  243. package/src/multi-items.js +76 -0
  244. package/src/not-gorgon.js +129 -0
  245. package/src/perseus-api.jsx +270 -0
  246. package/src/perseus-env.js +26 -0
  247. package/src/perseus-error.js +19 -0
  248. package/src/perseus-markdown.jsx +433 -0
  249. package/src/perseus-types.js +1597 -0
  250. package/src/pure-markdown.js +347 -0
  251. package/src/question-paragraph.jsx +30 -0
  252. package/src/renderability.jsx +108 -0
  253. package/src/renderer.jsx +1932 -0
  254. package/src/server-item-renderer.jsx +467 -0
  255. package/src/sigfigs.jsx +492 -0
  256. package/src/styles/articles.less +748 -0
  257. package/src/styles/constants.js +119 -0
  258. package/src/styles/global-constants.js +350 -0
  259. package/src/styles/global-styles.js +307 -0
  260. package/src/styles/khan-exercise.css +1304 -0
  261. package/src/styles/media-queries.js +48 -0
  262. package/src/styles/perseus-renderer.less +1007 -0
  263. package/src/styles/shared.js +132 -0
  264. package/src/styles/util.less +47 -0
  265. package/src/styles/variables.boostrap.less +301 -0
  266. package/src/styles/variables.less +68 -0
  267. package/src/styles/variables.shared.less +356 -0
  268. package/src/styles/widgets/categorizer.less +39 -0
  269. package/src/styles/widgets/definition.less +86 -0
  270. package/src/styles/widgets/dropdown.less +3 -0
  271. package/src/styles/widgets/expression.less +147 -0
  272. package/src/styles/widgets/graded-group.less +36 -0
  273. package/src/styles/widgets/grapher.less +11 -0
  274. package/src/styles/widgets/group.less +19 -0
  275. package/src/styles/widgets/image.less +29 -0
  276. package/src/styles/widgets/interactive-graph.less +61 -0
  277. package/src/styles/widgets/label-image.less +41 -0
  278. package/src/styles/widgets/matcher.less +8 -0
  279. package/src/styles/widgets/matrix.less +120 -0
  280. package/src/styles/widgets/measurer.less +20 -0
  281. package/src/styles/widgets/orderer.less +97 -0
  282. package/src/styles/widgets/passage.less +151 -0
  283. package/src/styles/widgets/plotter.less +33 -0
  284. package/src/styles/widgets/radio.less +52 -0
  285. package/src/styles/widgets/reaction-diagram.less +35 -0
  286. package/src/styles/widgets/sequence.less +5 -0
  287. package/src/styles/widgets/sortable.less +127 -0
  288. package/src/styles/widgets/table.less +56 -0
  289. package/src/styles/widgets/transformer.less +28 -0
  290. package/src/styles/widgets/unit.less +14 -0
  291. package/src/tex-wrangler.js +176 -0
  292. package/src/traversal.jsx +141 -0
  293. package/src/types.js +538 -0
  294. package/src/util/a11y.js +24 -0
  295. package/src/util/answer-types.js +862 -0
  296. package/src/util/answer-types_test.js +115 -0
  297. package/src/util/colors.js +94 -0
  298. package/src/util/fix-passage-refs.jsx +122 -0
  299. package/src/util/graph-utils.js +16 -0
  300. package/src/util/graphie.js +1586 -0
  301. package/src/util/interactive.js +4078 -0
  302. package/src/util/katex-preprocess.js +16 -0
  303. package/src/util/math.js +296 -0
  304. package/src/util/mobile-native-utils.js +14 -0
  305. package/src/util/mobile-native-utils_test.js +24 -0
  306. package/src/util/register-all-widgets-for-testing.js +12 -0
  307. package/src/util/scroll-utils.js +162 -0
  308. package/src/util/sizing-utils.js +46 -0
  309. package/src/util/tex.js +203 -0
  310. package/src/util/url-utils.js +36 -0
  311. package/src/util.js +990 -0
  312. package/src/version.js +11 -0
  313. package/src/widget-container.jsx +174 -0
  314. package/src/widgets/__stories__/categorizer.stories.jsx +15 -0
  315. package/src/widgets/__stories__/cs-program.stories.jsx +15 -0
  316. package/src/widgets/__stories__/definition.stories.jsx +38 -0
  317. package/src/widgets/__stories__/dropdown.stories.jsx +15 -0
  318. package/src/widgets/__stories__/explanation.stories.jsx +19 -0
  319. package/src/widgets/__stories__/expression.stories.jsx +26 -0
  320. package/src/widgets/__stories__/graded-group-set.stories.jsx +32 -0
  321. package/src/widgets/__stories__/graded-group.stories.jsx +32 -0
  322. package/src/widgets/__stories__/grapher.stories.jsx +47 -0
  323. package/src/widgets/__stories__/group.stories.jsx +15 -0
  324. package/src/widgets/__stories__/iframe.stories.jsx +15 -0
  325. package/src/widgets/__stories__/image.stories.jsx +62 -0
  326. package/src/widgets/__stories__/input-number.stories.jsx +90 -0
  327. package/src/widgets/__stories__/interaction.stories.jsx +15 -0
  328. package/src/widgets/__stories__/interactive-graph.stories.jsx +61 -0
  329. package/src/widgets/__stories__/matcher.stories.jsx +15 -0
  330. package/src/widgets/__stories__/matrix.stories.jsx +15 -0
  331. package/src/widgets/__stories__/number-line.stories.jsx +15 -0
  332. package/src/widgets/__stories__/numeric-input.stories.jsx +19 -0
  333. package/src/widgets/__stories__/orderer.stories.jsx +15 -0
  334. package/src/widgets/__stories__/passage-ref.stories.jsx +19 -0
  335. package/src/widgets/__stories__/passage.stories.jsx +19 -0
  336. package/src/widgets/__stories__/radio.stories.jsx +82 -0
  337. package/src/widgets/__stories__/sorter.stories.jsx +15 -0
  338. package/src/widgets/__stories__/transformer.stories.jsx +15 -0
  339. package/src/widgets/__stories__/unit.stories.jsx +45 -0
  340. package/src/widgets/__stories__/video-transcript-link.stories.jsx +24 -0
  341. package/src/widgets/__stories__/video.stories.jsx +15 -0
  342. package/src/widgets/__testdata__/categorizer_testdata.js +50 -0
  343. package/src/widgets/__testdata__/cs-program_testdata.js +28 -0
  344. package/src/widgets/__testdata__/dropdown_testdata.js +34 -0
  345. package/src/widgets/__testdata__/explanation_testdata.js +56 -0
  346. package/src/widgets/__testdata__/expression_testdata.js +95 -0
  347. package/src/widgets/__testdata__/graded-group-set_testdata.js +358 -0
  348. package/src/widgets/__testdata__/graded-group_testdata.js +79 -0
  349. package/src/widgets/__testdata__/grapher_testdata.js +376 -0
  350. package/src/widgets/__testdata__/group_testdata.js +156 -0
  351. package/src/widgets/__testdata__/iframe_testdata.js +36 -0
  352. package/src/widgets/__testdata__/image_testdata.js +38 -0
  353. package/src/widgets/__testdata__/input-number_testdata.js +73 -0
  354. package/src/widgets/__testdata__/interaction_testdata.js +264 -0
  355. package/src/widgets/__testdata__/interactive-graph_testdata.js +646 -0
  356. package/src/widgets/__testdata__/matcher_testdata.js +34 -0
  357. package/src/widgets/__testdata__/matrix_testdata.js +29 -0
  358. package/src/widgets/__testdata__/number-line_testdata.js +31 -0
  359. package/src/widgets/__testdata__/numeric-input_testdata.js +266 -0
  360. package/src/widgets/__testdata__/orderer_testdata.js +58 -0
  361. package/src/widgets/__testdata__/passage-ref_testdata.js +41 -0
  362. package/src/widgets/__testdata__/passage_testdata.js +71 -0
  363. package/src/widgets/__testdata__/radio_testdata.js +210 -0
  364. package/src/widgets/__testdata__/sorter_testdata.js +20 -0
  365. package/src/widgets/__testdata__/transformer_testdata.js +109 -0
  366. package/src/widgets/__testdata__/video_testdata.js +22 -0
  367. package/src/widgets/__tests__/__snapshots__/categorizer_test.jsx.snap +928 -0
  368. package/src/widgets/__tests__/__snapshots__/cs-program_test.jsx.snap +75 -0
  369. package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +99 -0
  370. package/src/widgets/__tests__/__snapshots__/dropdown_test.jsx.snap +143 -0
  371. package/src/widgets/__tests__/__snapshots__/explanation_test.js.snap +473 -0
  372. package/src/widgets/__tests__/__snapshots__/expression_test.jsx.snap +38 -0
  373. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +754 -0
  374. package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +310 -0
  375. package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +863 -0
  376. package/src/widgets/__tests__/__snapshots__/grapher_test.jsx.snap +2880 -0
  377. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +1154 -0
  378. package/src/widgets/__tests__/__snapshots__/iframe_test.jsx.snap +69 -0
  379. package/src/widgets/__tests__/__snapshots__/image_test.jsx.snap +388 -0
  380. package/src/widgets/__tests__/__snapshots__/input-number_test.jsx.snap +978 -0
  381. package/src/widgets/__tests__/__snapshots__/interaction_test.jsx.snap +311 -0
  382. package/src/widgets/__tests__/__snapshots__/interactive-graph_test.jsx.snap +2915 -0
  383. package/src/widgets/__tests__/__snapshots__/matcher_test.jsx.snap +1039 -0
  384. package/src/widgets/__tests__/__snapshots__/matrix_test.jsx.snap +450 -0
  385. package/src/widgets/__tests__/__snapshots__/number-line_test.jsx.snap +888 -0
  386. package/src/widgets/__tests__/__snapshots__/numeric-input_test.jsx.snap +930 -0
  387. package/src/widgets/__tests__/__snapshots__/orderer_test.jsx.snap +275 -0
  388. package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +446 -0
  389. package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +1988 -0
  390. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +7107 -0
  391. package/src/widgets/__tests__/__snapshots__/video_test.jsx.snap +163 -0
  392. package/src/widgets/__tests__/categorizer_test.jsx +205 -0
  393. package/src/widgets/__tests__/cs-program_test.jsx +45 -0
  394. package/src/widgets/__tests__/definition_test.js +152 -0
  395. package/src/widgets/__tests__/dropdown_test.jsx +106 -0
  396. package/src/widgets/__tests__/explanation_test.js +199 -0
  397. package/src/widgets/__tests__/expression_test.jsx +439 -0
  398. package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +33 -0
  399. package/src/widgets/__tests__/graded-group-set_test.jsx +270 -0
  400. package/src/widgets/__tests__/graded-group_test.jsx +233 -0
  401. package/src/widgets/__tests__/grapher.cypress.jsx +486 -0
  402. package/src/widgets/__tests__/grapher_test.jsx +35 -0
  403. package/src/widgets/__tests__/group_test.jsx +512 -0
  404. package/src/widgets/__tests__/iframe_test.jsx +47 -0
  405. package/src/widgets/__tests__/image_test.jsx +65 -0
  406. package/src/widgets/__tests__/input-number_test.jsx +360 -0
  407. package/src/widgets/__tests__/interaction_test.jsx +33 -0
  408. package/src/widgets/__tests__/interactive-graph.cypress.jsx +697 -0
  409. package/src/widgets/__tests__/interactive-graph_test.jsx +105 -0
  410. package/src/widgets/__tests__/matcher_test.jsx +157 -0
  411. package/src/widgets/__tests__/matrix_test.jsx +84 -0
  412. package/src/widgets/__tests__/number-line_test.jsx +81 -0
  413. package/src/widgets/__tests__/numeric-input_test.jsx +574 -0
  414. package/src/widgets/__tests__/orderer_test.jsx +75 -0
  415. package/src/widgets/__tests__/passage-ref_test.jsx +166 -0
  416. package/src/widgets/__tests__/passage_test.jsx +133 -0
  417. package/src/widgets/__tests__/radio/base-radio_test.jsx +82 -0
  418. package/src/widgets/__tests__/radio_test.jsx +651 -0
  419. package/src/widgets/__tests__/renderQuestion.jsx +66 -0
  420. package/src/widgets/__tests__/unit_test.jsx +144 -0
  421. package/src/widgets/__tests__/video_test.jsx +55 -0
  422. package/src/widgets/categorizer.jsx +338 -0
  423. package/src/widgets/cs-program.jsx +227 -0
  424. package/src/widgets/definition.jsx +141 -0
  425. package/src/widgets/dropdown.jsx +143 -0
  426. package/src/widgets/explanation.jsx +315 -0
  427. package/src/widgets/expression.jsx +652 -0
  428. package/src/widgets/graded-group-answer-bar.jsx +152 -0
  429. package/src/widgets/graded-group-set.jsx +285 -0
  430. package/src/widgets/graded-group.jsx +448 -0
  431. package/src/widgets/grapher/util.jsx +806 -0
  432. package/src/widgets/grapher.jsx +663 -0
  433. package/src/widgets/group.jsx +213 -0
  434. package/src/widgets/iframe.jsx +198 -0
  435. package/src/widgets/image.jsx +310 -0
  436. package/src/widgets/input-number.jsx +411 -0
  437. package/src/widgets/interaction/arrow-picker.jsx +39 -0
  438. package/src/widgets/interaction/color-picker.jsx +69 -0
  439. package/src/widgets/interaction/dash-picker.jsx +46 -0
  440. package/src/widgets/interaction/element-container.jsx +97 -0
  441. package/src/widgets/interaction.jsx +742 -0
  442. package/src/widgets/interactive-graph.jsx +2820 -0
  443. package/src/widgets/label-image/__stories__/answer-choices.stories.jsx +93 -0
  444. package/src/widgets/label-image/__stories__/marker.stories.jsx +172 -0
  445. package/src/widgets/label-image/answer-choices.jsx +311 -0
  446. package/src/widgets/label-image/marker.jsx +365 -0
  447. package/src/widgets/label-image/types.js +33 -0
  448. package/src/widgets/label-image.jsx +841 -0
  449. package/src/widgets/label-image_test.js +840 -0
  450. package/src/widgets/lights-puzzle.jsx +356 -0
  451. package/src/widgets/matcher.jsx +271 -0
  452. package/src/widgets/matrix.jsx +616 -0
  453. package/src/widgets/measurer.jsx +215 -0
  454. package/src/widgets/molecule/__tests__/layout_test.jsx +159 -0
  455. package/src/widgets/molecule/__tests__/parser_test.jsx +142 -0
  456. package/src/widgets/molecule/molecule-drawing.jsx +175 -0
  457. package/src/widgets/molecule/molecule-layout.jsx +367 -0
  458. package/src/widgets/molecule/smiles-parser.jsx +300 -0
  459. package/src/widgets/molecule.jsx +172 -0
  460. package/src/widgets/number-line.jsx +858 -0
  461. package/src/widgets/numeric-input.jsx +622 -0
  462. package/src/widgets/orderer.jsx +753 -0
  463. package/src/widgets/passage/__tests__/passage-markdown_test.jsx +382 -0
  464. package/src/widgets/passage/get-line-height-for-node.js +10 -0
  465. package/src/widgets/passage/passage-markdown.jsx +416 -0
  466. package/src/widgets/passage-ref-target.jsx +70 -0
  467. package/src/widgets/passage-ref.jsx +198 -0
  468. package/src/widgets/passage.jsx +601 -0
  469. package/src/widgets/plotter.jsx +1198 -0
  470. package/src/widgets/radio/__stories__/base-radio.stories.jsx +40 -0
  471. package/src/widgets/radio/__stories__/choice-icon.stories.jsx +157 -0
  472. package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +26 -0
  473. package/src/widgets/radio/__stories__/cross-out-button.stories.jsx +64 -0
  474. package/src/widgets/radio/__stories__/cross-out-menu.stories.jsx +59 -0
  475. package/src/widgets/radio/__stories__/cross-out-tooltip.stories.jsx +59 -0
  476. package/src/widgets/radio/__stories__/focus-ring.stories.jsx +38 -0
  477. package/src/widgets/radio/__stories__/option-status.stories.jsx +118 -0
  478. package/src/widgets/radio/__stories__/toggleable-radio-button.stories.jsx +28 -0
  479. package/src/widgets/radio/base-radio.jsx +642 -0
  480. package/src/widgets/radio/choice-icon.jsx +468 -0
  481. package/src/widgets/radio/choice-none-above.jsx +54 -0
  482. package/src/widgets/radio/choice.jsx +692 -0
  483. package/src/widgets/radio/choice.jsx.fixture.js +64 -0
  484. package/src/widgets/radio/cross-out-button.jsx +136 -0
  485. package/src/widgets/radio/cross-out-menu-wrapper.jsx +492 -0
  486. package/src/widgets/radio/cross-out-tooltip.jsx +129 -0
  487. package/src/widgets/radio/focus-ring.jsx +51 -0
  488. package/src/widgets/radio/option-status.jsx +89 -0
  489. package/src/widgets/radio/toggleable-radio-button.jsx +133 -0
  490. package/src/widgets/radio/util.js +32 -0
  491. package/src/widgets/radio/util_test.js +19 -0
  492. package/src/widgets/radio/widget.jsx +470 -0
  493. package/src/widgets/radio.jsx +156 -0
  494. package/src/widgets/reaction-diagram.jsx +137 -0
  495. package/src/widgets/sequence.jsx +140 -0
  496. package/src/widgets/simulator.jsx +808 -0
  497. package/src/widgets/sorter.jsx +146 -0
  498. package/src/widgets/table.jsx +394 -0
  499. package/src/widgets/transformer.jsx +2876 -0
  500. package/src/widgets/unit.jsx +372 -0
  501. package/src/widgets/video-transcript-link.jsx +84 -0
  502. package/src/widgets/video-transcript-link_test.js +211 -0
  503. package/src/widgets/video.jsx +126 -0
  504. package/src/widgets.js +431 -0
  505. package/src/zoom.js +539 -0
  506. package/README.md +0 -3
  507. package/src/__tests__/index.test.js +0 -14
@@ -0,0 +1,53 @@
1
+ // @flow
2
+ /**
3
+ * Utility functions for constructing and inferring multi-item shapes.
4
+ *
5
+ * A shape is an object that serves as a runtime type declaration: it specifies
6
+ * a tree structure for a particular class of multi-item. See shape-types.js
7
+ * for further discussion.
8
+ *
9
+ * This module allows you to construct arbitrary Shape trees, by combining
10
+ * leaf node shapes like `content` and `hint` into composite shapes like
11
+ * `arrayOf(shape({foo: content, bar: hint}))`.
12
+ */
13
+ import type {
14
+ Shape,
15
+ ContentShape,
16
+ HintShape,
17
+ TagsShape,
18
+ ArrayShape,
19
+ ObjectShape,
20
+ } from "./shape-types.js";
21
+
22
+ /**
23
+ * These tools allow you to construct arbirtary shapes, by combining simple
24
+ * leaf shapes like `content` and `hint` into composite shapes like
25
+ * `arrayOf(shape({question: content, hints: arrayOf(hint)}))`.
26
+ */
27
+ const contentShape: ContentShape = {
28
+ type: "content",
29
+ };
30
+ const hintShape: HintShape = {
31
+ type: "hint",
32
+ };
33
+ const tagsShape: TagsShape = {
34
+ type: "tags",
35
+ };
36
+ const buildArrayShape = (elementShape: Shape): ArrayShape => ({
37
+ type: "array",
38
+ elementShape,
39
+ });
40
+ const buildObjectShape = (shape: {[k: string]: Shape, ...}): ObjectShape => ({
41
+ type: "object",
42
+ shape,
43
+ });
44
+ const hintsShape: ArrayShape = buildArrayShape(hintShape);
45
+
46
+ export default {
47
+ content: contentShape,
48
+ hint: hintShape,
49
+ hints: hintsShape,
50
+ tags: tagsShape,
51
+ arrayOf: buildArrayShape,
52
+ shape: buildObjectShape,
53
+ };
@@ -0,0 +1,42 @@
1
+ // @flow
2
+ /**
3
+ * Type definitions for generic trees that are shaped like multi-items.
4
+ *
5
+ * Multi-items are trees! But we also often have other trees that are shaped
6
+ * like multi-items - for example, if we map a multi-item tree into a tree of
7
+ * renderer info and state, and then map that again into a tree of just the
8
+ * renderer nodes, like we do in MultiRenderer.
9
+ *
10
+ * Therefore, we provide the type Tree<C, H>, which represents a tree that's
11
+ * shaped like a multi-item, but the data that lives at each type of leaf could
12
+ * be anything.
13
+ *
14
+ * So, in a Tree<C, H>, content leaf nodes have type C, and hint leaf nodes
15
+ * have type H. An ItemTree is a Tree<ContentNode, HintNode>.
16
+ *
17
+ * That is, we still preserve the distinction between node types, and thereby
18
+ * enforce the Tree's adherence to some multi-item Shape, but we're flexible
19
+ * about exactly what type of data the tree contains at its leaves.
20
+ *
21
+ * This enables us to write generic tree-traversal and tree-mapping functions,
22
+ * as you'll see in trees.js.
23
+ */
24
+
25
+ // TODO(jeremy): We lost "arrayness" in D69450. It'd be nice to restore that
26
+ // because right now ArrayNode doesn't add anything.
27
+ export type ArrayNode<C, H, T> = Tree<C, H, T>;
28
+ export type ObjectNode<C, H, T> = {[k: string]: Tree<C, H, T>, ...};
29
+
30
+ /**
31
+ * TODO(somewhatabstract, JIRA-XXXX):
32
+ * We should ditch this type or create some helpers for working with it.
33
+ * As it stands, consumers have to check each node's type before using it
34
+ * in order to ensure that flow can track its refinement and appropriate use.
35
+ * It's messy.
36
+ */
37
+ export type Tree<C, H, T> =
38
+ | C
39
+ | H
40
+ | T
41
+ | ArrayNode<C, H, T>
42
+ | ObjectNode<C, H, T>;
@@ -0,0 +1,345 @@
1
+ // @flow
2
+ /**
3
+ * Utility functions for manipulating multi-item-shaped trees.
4
+ *
5
+ * Multi-items are trees! But we also often have other trees that are shaped
6
+ * like multi-items - for example, if we map a multi-item tree into a tree of
7
+ * renderer info and state, and then map that again into a tree of just the
8
+ * renderer nodes, like we do in MultiRenderer. See tree-types.js for further
9
+ * discussion.
10
+ *
11
+ * These functions enable us to manipulate generic multi-item-shaped trees,
12
+ * regardless of what type of data they contain at their leaves. You can use
13
+ * the mapper functions to transform a tree into another tree of the same
14
+ * shape, or to discover all the nodes of a particular type.
15
+ *
16
+ * We expose two simple mapper functions (mapContentNodes and mapHintNodes),
17
+ * and also a more complex interface for creating a mapping over all of a
18
+ * tree's node types simultaneously:
19
+ *
20
+ * `buildMapper()` returns a TreeMapper object that allows you to build your
21
+ * mapper object one node type at a time. Then, you can execute your mapping by
22
+ * calling the `mapTree` method.
23
+ *
24
+ * For example:
25
+ * const renderers = buildMapper()
26
+ * .setContentMapper(this.renderContentNode)
27
+ * .setHintMapper(this.renderHintNode)
28
+ * .setTagsMapper(this.renderTagsNode)
29
+ * .setArrayMapper(this.hideSkippedQuestions)
30
+ * .mapTree(tree, shape);
31
+ *
32
+ * This will copy the given tree, apply the given transformations to the
33
+ * content, hint, and array nodes respectively, and return the resulting tree.
34
+ *
35
+ * For node types whose mappers aren't specified, we default to the identity
36
+ * function. (This builder interface enables us to implement that default
37
+ * behavior in a provably type-safe way, while not requiring the call site to
38
+ * be aware of all the node types. Hooray!)
39
+ *
40
+ * The call to `setArrayMapper` must come last, because the array mapper's
41
+ * argument types depend on the other mappers' types. See ArrayMapper for more
42
+ * details.
43
+ *
44
+ * WARNING: These functions trust that the provided tree conforms to the
45
+ * provided shape. If not, behavior is undefined and may not respect the type
46
+ * signatures specified here.
47
+ */
48
+
49
+ import {Errors} from "../logging/log.js";
50
+ import {PerseusError} from "../perseus-error.js";
51
+
52
+ import type {
53
+ Shape,
54
+ ContentShape,
55
+ HintShape,
56
+ TagsShape,
57
+ ArrayShape,
58
+ } from "./shape-types.js";
59
+ import type {Tree, ArrayNode, ObjectNode} from "./tree-types.js";
60
+
61
+ /**
62
+ * The sequence of edges that lead to a particular node in a Tree.
63
+ * Elements can be `string` to correspond to an ObjectNode key, or `number` to
64
+ * correspond to an ArrayNode index.
65
+ */
66
+ export type Path = $ReadOnlyArray<string | number>;
67
+
68
+ /**
69
+ * These are function interfaces for mapping over various types of tree nodes.
70
+ *
71
+ * ArrayMapper is a bit more complicated than the leaf node mappers. It's
72
+ * executed in the context of a `mapTree` call, after we've finished mapping
73
+ * its child nodes, so the function has access to both the resulting array
74
+ * (with mapped elements) and the original array (with the original untouched
75
+ * elements).
76
+ *
77
+ * The ArrayMapper then has the opportunity to apply a final transformation to
78
+ * the resulting array, like filtering certain elements or (in the hacky
79
+ * MultiRenderer case) attaching a `renderHints` method to arrays of hint
80
+ * renderers :)
81
+ *
82
+ * This is why `TreeMapper#setArrayMapper` must be called last: ArrayMapper's
83
+ * types depend on the ContentMapper and HintMapper's types. And, since you can
84
+ * only specify one mapper at a time in this builder interface (which is
85
+ * necessary to provide default mappers in a type-safe way), you need your
86
+ * dependencies to already be in place by the time you call `setArrayMapper`.
87
+ * Otherwise, we'd have to set the ArrayMapper and *hope* that you *eventually*
88
+ * provide a compatible ContentMapper and HintMapper, which is difficult to
89
+ * prove at compile time.
90
+ *
91
+ * There's no ObjectMapper here, but not for any particular reason. We just
92
+ * don't have a use case for it yet, so we haven't built it yet.
93
+ */
94
+ export type ContentMapper<CI, CO> = (
95
+ content: CI,
96
+ shape: ContentShape,
97
+ path: Path,
98
+ ) => CO;
99
+ export type HintMapper<HI, HO> = (hint: HI, shape: HintShape, path: Path) => HO;
100
+ export type TagsMapper<TI, TO> = (tag: TI, shape: TagsShape, path: Path) => TO;
101
+ export type ArrayMapper<CI, CO, HI, HO, TI, TO> = (
102
+ mappedArray: ArrayNode<CO, HO, TO>,
103
+ originalArray: ArrayNode<CI, HI, TI>,
104
+ shape: ArrayShape,
105
+ path: Path,
106
+ ) => ArrayNode<CO, HO, TO>;
107
+
108
+ /**
109
+ * A TreeMapper is a collection of node mappers, which, together, compose the
110
+ * behavior for mapping over an entire tree.
111
+ *
112
+ * This serves as the interface for the two TreeMapper classes, including both
113
+ * the internal mapper properties that we care about, and the `mapTree`
114
+ * function that the call site will use.
115
+ */
116
+ export interface TreeMapper<CI, CO, HI, HO, TI, TO> {
117
+ content: ContentMapper<CI, CO>;
118
+ hint: HintMapper<HI, HO>;
119
+ tags: TagsMapper<TI, TO>;
120
+ array: ArrayMapper<CI, CO, HI, HO, TI, TO>;
121
+ mapTree(tree: Tree<CI, HI, TI>, shape: Shape): Tree<CO, HO, TO>;
122
+ }
123
+
124
+ /**
125
+ * This is a TreeMapper that only has mappers specified for its leaf nodes; its
126
+ * array mapper is the identity function.
127
+ *
128
+ * This is the TreeMapper initially returned by `buildMapper`. It allows you to
129
+ * change the types of your ContentMapper and HintMapper, which is safe because
130
+ * none of the other mappers that depend on those types (aka ArrayMapper) have
131
+ * been specified yet. (Or, more specifically, the ArrayMapper is currently
132
+ * `identity`, which can trivially vary with the ContentMapper and HintMapper's
133
+ * types.)
134
+ *
135
+ * Once you call `setArrayMapper`, however, we move to the other class:
136
+ * TreeMapperForLeavesAndCollections.
137
+ */
138
+ class TreeMapperJustForLeaves<CI, CO, HI, HO, TI, TO> {
139
+ content: ContentMapper<CI, CO>;
140
+ hint: HintMapper<HI, HO>;
141
+ tags: TagsMapper<TI, TO>;
142
+ array: ArrayMapper<CI, CO, HI, HO, TI, TO>;
143
+
144
+ constructor(
145
+ content: ContentMapper<CI, CO>,
146
+ hint: HintMapper<HI, HO>,
147
+ tags: TagsMapper<TI, TO>,
148
+ ) {
149
+ this.content = content;
150
+ this.hint = hint;
151
+ this.tags = tags;
152
+ this.array = identity;
153
+ }
154
+
155
+ setContentMapper<CI2, CO2>(
156
+ newContentMapper: ContentMapper<CI2, CO2>,
157
+ ): TreeMapperJustForLeaves<CI2, CO2, HI, HO, TI, TO> {
158
+ return new TreeMapperJustForLeaves(
159
+ newContentMapper,
160
+ this.hint,
161
+ this.tags,
162
+ );
163
+ }
164
+
165
+ setHintMapper<HI2, HO2>(
166
+ newHintMapper: HintMapper<HI2, HO2>,
167
+ ): TreeMapperJustForLeaves<CI, CO, HI2, HO2, TI, TO> {
168
+ return new TreeMapperJustForLeaves(
169
+ this.content,
170
+ newHintMapper,
171
+ this.tags,
172
+ );
173
+ }
174
+
175
+ setTagsMapper<TI2, TO2>(
176
+ newTagsMapper: TagsMapper<TI2, TO2>,
177
+ ): TreeMapperJustForLeaves<CI, CO, HI, HO, TI2, TO2> {
178
+ return new TreeMapperJustForLeaves(
179
+ this.content,
180
+ this.hint,
181
+ newTagsMapper,
182
+ );
183
+ }
184
+
185
+ setArrayMapper(
186
+ newArrayMapper: ArrayMapper<CI, CO, HI, HO, TI, TO>,
187
+ ): TreeMapperForLeavesAndCollections<CI, CO, HI, HO, TI, TO> {
188
+ return new TreeMapperForLeavesAndCollections(
189
+ this.content,
190
+ this.hint,
191
+ this.tags,
192
+ newArrayMapper,
193
+ );
194
+ }
195
+
196
+ mapTree(tree: Tree<CI, HI, TI>, shape: Shape): Tree<CO, HO, TO> {
197
+ return mapTree(tree, shape, [], this);
198
+ }
199
+ }
200
+
201
+ /**
202
+ * This is a TreeMapper that already has an ArrayMapper specified, so its
203
+ * ContentMapper and HintMapper are now locked in.
204
+ */
205
+ class TreeMapperForLeavesAndCollections<CI, CO, HI, HO, TI, TO> {
206
+ content: ContentMapper<CI, CO>;
207
+ hint: HintMapper<HI, HO>;
208
+ tags: TagsMapper<TI, TO>;
209
+ array: ArrayMapper<CI, CO, HI, HO, TI, TO>;
210
+
211
+ constructor(
212
+ content: ContentMapper<CI, CO>,
213
+ hint: HintMapper<HI, HO>,
214
+ tags: TagsMapper<TI, TO>,
215
+ array: ArrayMapper<CI, CO, HI, HO, TI, TO>,
216
+ ) {
217
+ this.content = content;
218
+ this.hint = hint;
219
+ this.tags = tags;
220
+ this.array = array;
221
+ }
222
+
223
+ setArrayMapper(
224
+ newArrayMapper: ArrayMapper<CI, CO, HI, HO, TI, TO>,
225
+ ): TreeMapperForLeavesAndCollections<CI, CO, HI, HO, TI, TO> {
226
+ return new TreeMapperForLeavesAndCollections(
227
+ this.content,
228
+ this.hint,
229
+ this.tags,
230
+ newArrayMapper,
231
+ );
232
+ }
233
+
234
+ mapTree(tree: Tree<CI, HI, TI>, shape: Shape): Tree<CO, HO, TO> {
235
+ return mapTree(tree, shape, [], this);
236
+ }
237
+ }
238
+
239
+ function identity<T>(x: T): T {
240
+ return x;
241
+ }
242
+
243
+ /**
244
+ * Return a new TreeMapper that will perform a no-op transformation on an input
245
+ * tree. To make it useful, chain any combination of `setContentMapper`,
246
+ * `setHintMapper`, `setTagMapper`, and `setArrayMapper` to specify
247
+ * transformations for the individual node types.
248
+ */
249
+ export function buildMapper<C, C, H, H, T, T>(): TreeMapperJustForLeaves<
250
+ C,
251
+ C,
252
+ H,
253
+ H,
254
+ T,
255
+ T,
256
+ > {
257
+ return new TreeMapperJustForLeaves(identity, identity, identity);
258
+ }
259
+
260
+ /**
261
+ * Copy the given tree, apply the corresponding transformation specified in the
262
+ * TreeMapper to each node, and return the resulting tree.
263
+ */
264
+ function mapTree<CI, CO, HI, HO, TI, TO>(
265
+ tree: Tree<CI, HI, TI>,
266
+ shape: Shape,
267
+ path: Path,
268
+ mappers: TreeMapper<CI, CO, HI, HO, TI, TO>,
269
+ ): Tree<CO, HO, TO> {
270
+ // We trust the shape of the multi-item to match the shape provided at
271
+ // runtime. Therefore, in each shape branch, we cast the node to `any` and
272
+ // reinterpret it as the expected node type.
273
+ if (shape.type === "content") {
274
+ const content: CI = (tree: any);
275
+ return mappers.content(content, shape, path);
276
+ }
277
+ if (shape.type === "hint") {
278
+ const hint: HI = (tree: any);
279
+ return mappers.hint(hint, shape, path);
280
+ }
281
+ if (shape.type === "tags") {
282
+ const tags: TI = (tree: any);
283
+ return mappers.tags(tags, shape, path);
284
+ }
285
+ if (shape.type === "array") {
286
+ const array: ArrayNode<CI, HI, TI> = (tree: any);
287
+
288
+ if (!Array.isArray(array)) {
289
+ throw new PerseusError(
290
+ `Invalid object of type "${typeof array}" found at path ` +
291
+ `${["<root>"].concat(path).join(".")}. Expected array.`,
292
+ Errors.Internal,
293
+ );
294
+ }
295
+
296
+ const elementShape = shape.elementShape;
297
+ const mappedElements: ArrayNode<CO, HO, TO> = array.map((inner, i) =>
298
+ mapTree(inner, elementShape, path.concat(i), mappers),
299
+ );
300
+ return mappers.array(mappedElements, array, shape, path);
301
+ }
302
+ if (shape.type === "object") {
303
+ const object: ObjectNode<CI, HI, TI> = (tree: any);
304
+
305
+ if (object && typeof object !== "object") {
306
+ throw new PerseusError(
307
+ `Invalid object of type "${typeof object}" found at ` +
308
+ `path ${["<root>"].concat(path).join(".")}. Expected ` +
309
+ `"object" type.`,
310
+ Errors.InvalidInput,
311
+ );
312
+ }
313
+
314
+ const valueShapes = shape.shape;
315
+ if (!valueShapes) {
316
+ throw new PerseusError(
317
+ `Unexpected shape ${JSON.stringify(shape)} at path ` +
318
+ `${["<root>"].concat(path).join(".")}.`,
319
+ Errors.InvalidInput,
320
+ );
321
+ }
322
+ const newObject = {};
323
+ Object.keys(valueShapes).forEach((key) => {
324
+ if (!(key in object)) {
325
+ throw new PerseusError(
326
+ `Key "${key}" is missing from shape at path ` +
327
+ `${["<root>"].concat(path).join(".")}.`,
328
+ Errors.InvalidInput,
329
+ );
330
+ }
331
+
332
+ newObject[key] = mapTree(
333
+ object[key],
334
+ valueShapes[key],
335
+ path.concat(key),
336
+ mappers,
337
+ );
338
+ });
339
+ return newObject;
340
+ }
341
+ throw new PerseusError(
342
+ `unexpected shape type ${shape.type}`,
343
+ Errors.InvalidInput,
344
+ );
345
+ }
@@ -0,0 +1,76 @@
1
+ // @flow
2
+ /**
3
+ * This library provides support for Perseus multi-items: structured Perseus
4
+ * content that content creators can easily create, and that applications can
5
+ * easily render into different parts of the layout.
6
+ *
7
+ * For more details about application and motivation, see:
8
+ * https://sites.google.com/a/khanacademy.org/forge/for-developers/perseus-items-and-multi-items
9
+ *
10
+ * This file primarily exposes the `MultiRenderer` component, which performs
11
+ * multi-rendering. To multi-render a question, pass in the content of the item
12
+ * to the `MultiRenderer` component as a props. Then, pass in a function which
13
+ * takes an object of renderers (in the same structure as the content), and
14
+ * return a render tree. The `MultiRenderer` component will allow you to
15
+ * combine scores, serialized state, etc. without having to manually call on
16
+ * each of the functions. It also handles inter-widgets requests between the
17
+ * different renderers.
18
+ * For more details, see `multi-items/multi-renderer.jsx`.
19
+ *
20
+ * Example:
21
+ *
22
+ * item = {_multi: {
23
+ * left: <content data>,
24
+ * right: [<content data>, <content data>],
25
+ * }}
26
+ * shape = shapes.shape({
27
+ * left: shapes.content,
28
+ * right: shapes.arrayOf(shapes.content),
29
+ * })
30
+ *
31
+ * <MultiRenderer item={item} shape={shape}>
32
+ * {({renderers}) =>
33
+ * <div>
34
+ * <div id="left">{renderers.left}</div>
35
+ * <ul id="right">
36
+ * {renderers.right.map(r => <li>{r}</li>)}
37
+ * </ul>
38
+ * </div>
39
+ * }
40
+ * </MultiRenderer>
41
+ *
42
+ * This file also exposes `shapes`, which helps you construct a runtime type
43
+ * declaration for your particular class of multi-item. This can then be used
44
+ * to create a MultirendererEditor for your multi-item shape, and to validate
45
+ * that a multi-item conforms to the shape via `buildPropTypeForShape`.
46
+ * For more details, see `multi-items/shapes.js`.
47
+ *
48
+ * This file also exposes some utility functions for working with generic
49
+ * multi-items, like `findContentNodesInItem`, `findHintNodesInItem`,
50
+ * `inferItemShape`, and `buildEmptyItemForShape`.
51
+ * For more details, see `multi-items/items.js`.
52
+ */
53
+ import {
54
+ buildEmptyItemForShape,
55
+ findContentNodesInItem,
56
+ findHintNodesInItem,
57
+ inferItemShape,
58
+ } from "./multi-items/items.js";
59
+ import MultiRenderer from "./multi-items/multi-renderer.jsx";
60
+ import {buildPropTypeForShape} from "./multi-items/prop-type-builders.js";
61
+ import shapes from "./multi-items/shapes.js";
62
+
63
+ export default {
64
+ // Tools for rendering your multi-items
65
+ MultiRenderer,
66
+
67
+ // Tools for declaring your multi-item shapes
68
+ shapes,
69
+ buildPropTypeForShape,
70
+
71
+ // Tools for generically manipulating multi-items
72
+ buildEmptyItemForShape,
73
+ findContentNodesInItem,
74
+ findHintNodesInItem,
75
+ inferItemShape,
76
+ };
@@ -0,0 +1,129 @@
1
+ // @flow
2
+ import {debounce} from "underscore";
3
+
4
+ import Rule from "./gorgon/rule.js";
5
+
6
+ /**
7
+ * NotGorgon asynchronously calls Khan Academy's poentry linter.
8
+ *
9
+ * The poentry linter checks for issues that prevent content from being
10
+ * translated. This linter is KA-specific and so NotGorgon does not do anything
11
+ * in non-KA environments.
12
+ *
13
+ * NotGorgon is named as such because it is not Gorgon, Perseus' built-in
14
+ * linter.
15
+ */
16
+
17
+ // A LintCB is called once the linter has been run.
18
+ type LintCB = (errors: $ReadOnlyArray<string>) => void;
19
+
20
+ // msec to wait before we actually call the linter after receiving the
21
+ // last call. The timer is reset if the lint is called during the wait.
22
+ const DEBOUNCE_TIMEOUT = 1000;
23
+
24
+ export default class NotGorgon {
25
+ // To avoid linting when nothing has changed.
26
+ previousContent: ?string = null;
27
+
28
+ /**
29
+ * Runs the poentry linter on the given perseus string asynchronously.
30
+ *
31
+ * It doesn't do anything unless we're in a KA environment.
32
+ *
33
+ * onLintErrorsGenerated is called even if there are no errors. It is not
34
+ * called if perseusStr is unchanged from the last call.
35
+ *
36
+ * Linting is relatively expensive -- so we debounce linting each instance
37
+ * for some amount of time.
38
+ */
39
+ runLinter: any = debounce(
40
+ (perseusStr: string, onLintErrorsGenerated: LintCB) => {
41
+ // $FlowFixMe[cannot-resolve-name] -- TODO(joshuan): use an api flag instead?
42
+ if (typeof KA === "undefined") {
43
+ return;
44
+ }
45
+
46
+ if (perseusStr === this.previousContent) {
47
+ return;
48
+ }
49
+
50
+ this.previousContent = perseusStr;
51
+
52
+ if (perseusStr === "") {
53
+ onLintErrorsGenerated([]);
54
+ return;
55
+ }
56
+
57
+ fetch("/api/internal/translations/lint_poentry?preview=1&lang=en", {
58
+ headers: {"Content-Type": "application/json"},
59
+ body: JSON.stringify({
60
+ // The poentry linter verifies that the translation from
61
+ // the original text to the translated text is sane. We
62
+ // want to ensure that the translation from English to
63
+ // English is sane, so msgid === mstr.
64
+ msgid: perseusStr, // msgid is the original text
65
+ msgstr: perseusStr, // msgstr is the translated text
66
+ format: "perseus_text",
67
+ filename: "",
68
+ }),
69
+ method: "POST",
70
+ })
71
+ .then(
72
+ (response) => {
73
+ if (response.status >= 400) {
74
+ return {
75
+ status: "error",
76
+ message: "Could not run i18n linter.",
77
+ };
78
+ }
79
+ return response.json();
80
+ },
81
+ (rejection) => {
82
+ return {
83
+ status: "error",
84
+ message: "Could not run i18n linter.",
85
+ };
86
+ },
87
+ )
88
+ .then((json) => {
89
+ if (json.status === "error") {
90
+ onLintErrorsGenerated([
91
+ "Some part of this text makes it untranslatable. " +
92
+ "The specific message from the i18n linter was: " +
93
+ json.message.replace(/\n/g, " "),
94
+ ]);
95
+ } else {
96
+ onLintErrorsGenerated([]);
97
+ }
98
+ });
99
+ },
100
+ DEBOUNCE_TIMEOUT,
101
+ );
102
+
103
+ /**
104
+ * Applies an array of errors generated by linters without position
105
+ * information (NotGorgon and the legacy getSaveWarnings()) to the top
106
+ * of a Perseus tree.
107
+ */
108
+ applyLintErrors(
109
+ parsedMarkdown: any,
110
+ notGorgonLintErrors: $ReadOnlyArray<string>,
111
+ ) {
112
+ // These lint errors do not have position data associated with
113
+ // them, so we just plop them at the top.
114
+ if (notGorgonLintErrors.length) {
115
+ const errorText = notGorgonLintErrors.join("\n\n");
116
+ parsedMarkdown.unshift({
117
+ content: {
118
+ type: "text",
119
+ content: "",
120
+ },
121
+ insideTable: false,
122
+ message: errorText,
123
+ ruleName: "legacy-error",
124
+ severity: Rule.Severity.ERROR,
125
+ type: "lint",
126
+ });
127
+ }
128
+ }
129
+ }