@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,129 @@
1
+ // @flow
2
+ /**
3
+ * The tooltip that appears when you open the `CrossOutMenu` on desktop,
4
+ * enabling the user to toggle a choice's `crossedOut` state.
5
+ */
6
+ import * as React from "react";
7
+
8
+ import NewTooltip from "../../components/new-tooltip/new-tooltip.jsx";
9
+ import mediaQueries from "../../styles/media-queries.js";
10
+
11
+ import CrossOutButton, {type CrossOutButtonProps} from "./cross-out-button.jsx";
12
+
13
+ export type CrossOutTooltipProps = {
14
+ children: React$Element<any>,
15
+ dismissed: boolean,
16
+ onDismiss: () => void,
17
+ // Props to pass directly to the CrossOutButton.
18
+ buttonProps: CrossOutButtonProps,
19
+ ...
20
+ };
21
+
22
+ export default class CrossOutTooltip extends React.PureComponent<CrossOutTooltipProps> {
23
+ /**
24
+ * When you press Tab or Shift-Tab from the tooltip button, we call this
25
+ * handler to dismiss the tooltip and bring you back to the menu button.
26
+ */
27
+ _handleKeyDown: (e: SyntheticKeyboardEvent<HTMLElement>) => void = (
28
+ e: SyntheticKeyboardEvent<HTMLElement>,
29
+ ) => {
30
+ // We don't need to check for the shift key _also_ being applied; we
31
+ // can just listen for all keystrokes where the primary key is Tab.
32
+ if (e.key === "Tab") {
33
+ this.props.onDismiss();
34
+ e.preventDefault();
35
+ }
36
+ };
37
+
38
+ _renderTooltipContent(): React.Element<"div"> {
39
+ return (
40
+ // The button is the main semantic click target of the tooltip,
41
+ // which can receive tab focus (and automatically does when the
42
+ // flag `usingKeyboardFocus` is set!). However, the entire tooltip
43
+ // serves as a click target as well; see the <NewTooltip />.
44
+ <div onKeyDown={this._handleKeyDown}>
45
+ <CrossOutButton {...this.props.buttonProps} />
46
+ </div>
47
+ );
48
+ }
49
+
50
+ render(): React.Element<"div"> {
51
+ return (
52
+ <div>
53
+ <NewTooltip
54
+ content={this._renderTooltipContent()}
55
+ offsetFrom="arrow"
56
+ offset={10}
57
+ dismissed={this.props.dismissed}
58
+ showOnMount={true}
59
+ toggleOnHover={false}
60
+ onClick={() => this.props.buttonProps.onClick()}
61
+ // On smOrSmaller, hide this tooltip, and use the slide
62
+ // panel in `CrossOutMenuWrapper` instead.
63
+ media={mediaQueries.mdOrLarger}
64
+ >
65
+ {this.props.children}
66
+ </NewTooltip>
67
+
68
+ {/* If the menu is open, register global dismissal handlers
69
+ * that listen for clicks elsewhere in the page, or Escape
70
+ * keypresses. This component renders nothing; it just
71
+ * registers/unregisters handlers on mount/unmount. */}
72
+ {!this.props.dismissed && (
73
+ <GlobalDismissalHandlers onDismiss={this.props.onDismiss} />
74
+ )}
75
+ </div>
76
+ );
77
+ }
78
+ }
79
+
80
+ /**
81
+ * A component that, when mounted, listens for dismissal-y events: clicking
82
+ * anywhere on the page, or pressing the Escape key. We handle these events by
83
+ * calling `onDismiss`.
84
+ *
85
+ * HACK(mdr): To generalize this component, we probably should ignore clicks
86
+ * inside the tooltip itself. However, conveniently, the `onClick` handler
87
+ * we receive from CrossOutMenu includes dismissal anyway. So, it's safe to
88
+ * dismiss on _any_ click, not just those outside the tooltip.
89
+ */
90
+ class GlobalDismissalHandlers extends React.PureComponent<{
91
+ onDismiss: () => void,
92
+ ...
93
+ }> {
94
+ componentDidMount() {
95
+ // Register these listeners after a tick, so they don't catch the click
96
+ // or keypress that _caused_ the tooltip to mount.
97
+ // TODO(jeff, CP-3128): Use Wonder Blocks Timing API
98
+ // eslint-disable-next-line no-restricted-syntax
99
+ setTimeout(() => {
100
+ window.addEventListener("click", this._handleGlobalClick);
101
+ window.addEventListener("keyup", this._handleGlobalKeyup);
102
+ }, 0);
103
+ }
104
+
105
+ componentWillUnmount() {
106
+ window.removeEventListener("click", this._handleGlobalClick);
107
+ window.removeEventListener("keyup", this._handleGlobalKeyup);
108
+ }
109
+
110
+ /**
111
+ * On clicking anywhere, trigger dismissal.
112
+ */
113
+ _handleGlobalClick = () => {
114
+ this.props.onDismiss();
115
+ };
116
+
117
+ /**
118
+ * If the key was Escape, trigger dismissal.
119
+ */
120
+ _handleGlobalKeyup = (e: KeyboardEvent) => {
121
+ if (e.key === "Escape") {
122
+ this.props.onDismiss();
123
+ }
124
+ };
125
+
126
+ render(): React.Node {
127
+ return null;
128
+ }
129
+ }
@@ -0,0 +1,51 @@
1
+ // @flow
2
+ /**
3
+ * Renders a circular selection ring around the child.
4
+ */
5
+
6
+ import {StyleSheet, css} from "aphrodite";
7
+ import * as React from "react";
8
+
9
+ import * as styleConstants from "../../styles/constants.js";
10
+
11
+ class FocusRing extends React.Component<{
12
+ children?: any,
13
+ // Whether the focus ring is visible. Allows for positioning
14
+ // the child identically regardless of whether the ring is visible.
15
+ visible: boolean,
16
+ // Color of the focus ring
17
+ color: string,
18
+ ...
19
+ }> {
20
+ static defaultProps: {|color: string, visible: boolean|} = {
21
+ visible: true,
22
+ color: styleConstants.kaGreen,
23
+ };
24
+
25
+ render(): React.Element<"span"> {
26
+ const borderColor = this.props.visible
27
+ ? this.props.color
28
+ : "transparent";
29
+ const style = {
30
+ borderColor: borderColor,
31
+ };
32
+ return (
33
+ <span className={css(styles.ring)} style={style}>
34
+ {this.props.children}
35
+ </span>
36
+ );
37
+ }
38
+ }
39
+
40
+ const styles = StyleSheet.create({
41
+ ring: {
42
+ margin: "auto",
43
+ display: "inline-block",
44
+ borderRadius: "50%",
45
+ borderWidth: 2,
46
+ padding: 2,
47
+ borderStyle: "solid",
48
+ },
49
+ });
50
+
51
+ export default FocusRing;
@@ -0,0 +1,89 @@
1
+ // @flow
2
+ /**
3
+ * Renders text indicating whether the choice was correct or
4
+ * not and whether the choice was selected or not.
5
+ * This information is redundant with that provided in the
6
+ * ChoiceIcon visualizations but is meant to make the distinctions
7
+ * between the states more immediately clear to users.
8
+ */
9
+
10
+ import * as i18n from "@khanacademy/wonder-blocks-i18n";
11
+ import {StyleSheet, css} from "aphrodite";
12
+ import * as React from "react";
13
+
14
+ import * as styleConstants from "../../styles/constants.js";
15
+
16
+ class OptionStatus extends React.Component<{
17
+ // Was this option the correct answer?
18
+ correct: boolean,
19
+ // Did the user select this option as the answer?
20
+ checked: boolean,
21
+ // Did the user cross out this option?
22
+ crossedOut: boolean,
23
+ // Did the user select this option as the answer earlier?
24
+ previouslyAnswered: boolean,
25
+ ...
26
+ }> {
27
+ _renderText(): string {
28
+ const {checked, correct, crossedOut} = this.props;
29
+ if (correct) {
30
+ // For correct answers, we surface checked _or_ crossedOut state,
31
+ // because any interaction with the correct answer is noteworthy!
32
+ if (checked) {
33
+ return i18n._("Correct (selected)");
34
+ }
35
+ if (crossedOut) {
36
+ return i18n._("Correct (but you crossed it out)");
37
+ }
38
+ return i18n._("Correct");
39
+ }
40
+ // But, for incorrect answers, we only surface checked state,
41
+ // because crossing out an incorrect answer is not noteworthy.
42
+ if (checked) {
43
+ return i18n._("Incorrect (selected)");
44
+ }
45
+ return i18n._("Incorrect");
46
+ }
47
+
48
+ render(): React.Element<"div"> {
49
+ const {checked, correct, previouslyAnswered} = this.props;
50
+
51
+ let textStyle;
52
+ if (correct) {
53
+ textStyle = styles.correct;
54
+ } else {
55
+ if (checked || previouslyAnswered) {
56
+ textStyle = styles.incorrectAnswered;
57
+ } else {
58
+ textStyle = styles.incorrect;
59
+ }
60
+ }
61
+
62
+ return (
63
+ <div className={css(styles.text, textStyle)}>
64
+ {this._renderText()}
65
+ </div>
66
+ );
67
+ }
68
+ }
69
+
70
+ const styles = StyleSheet.create({
71
+ text: {
72
+ alignItems: "center",
73
+ display: "flex",
74
+ fontSize: 12,
75
+ height: 32,
76
+ textTransform: "uppercase",
77
+ },
78
+ correct: {
79
+ color: styleConstants.kaGreen,
80
+ },
81
+ incorrectAnswered: {
82
+ color: styleConstants.warning1,
83
+ },
84
+ incorrect: {
85
+ color: styleConstants.gray68,
86
+ },
87
+ });
88
+
89
+ export default OptionStatus;
@@ -0,0 +1,133 @@
1
+ // @flow
2
+ import PropTypes from "prop-types";
3
+ import * as React from "react";
4
+ import _ from "underscore";
5
+
6
+ /**
7
+ * A radio button that can be unchecked by clicking it again.
8
+ *
9
+ * This component behaves much like a checkbox in that when you click it again
10
+ * (or hit space bar or select it in some other way), it will become unchecked.
11
+ * (Hopefully) it behaves exactly like a radio button in every other way though
12
+ * (arrow keys can still be used to select it within a group for example).
13
+ *
14
+ * Here's a summary of which of our event handlers fire for each way to select
15
+ * or deselect the button:
16
+ *
17
+ * - Clicking a button: handleClick, handleChange
18
+ * - Pressing spacebar with button focussed: handleKeyDown, handleKeyUp (we
19
+ * suppress the default behavior of some browsers to synthesize a click event
20
+ * here).
21
+ * - When radio button A is selected, pressing down arrow to select B:
22
+ * handleKeyDown (A), handleClick (B), handleChange (B), handleKeyUp (B)
23
+ * - Clicking with mac screenreader: handleClick, handleChange
24
+ */
25
+ class ToggleableRadioButton extends React.Component<$FlowFixMe, $FlowFixMe> {
26
+ _inputElement: ?HTMLInputElement;
27
+
28
+ static propTypes = {
29
+ // Whether the radio button should be checked or unchecked (this is a
30
+ // controlled component).
31
+ checked: PropTypes.bool.isRequired,
32
+
33
+ // A ref function to attach to the <input> element.
34
+ inputRef: PropTypes.func,
35
+
36
+ // A function that will be called whenever the radio button is checked
37
+ // or unchecked. It's possible for this to be called twice for a single
38
+ // checking or unchecking.
39
+ onChecked: PropTypes.func.isRequired,
40
+
41
+ // Callbacks for when the user presses the up/left arrow or down/right
42
+ // arrow (respectively) to navigate between choices. When called, the
43
+ // parent finds the prev/next choice, focuses the input, and checks the
44
+ // radio button if appropriate. (The reason we have custom behavior is
45
+ // that we actually _don't_ want to auto-select crossed-out choices!)
46
+ goToPrevChoice: PropTypes.func.isRequired,
47
+ goToNextChoice: PropTypes.func.isRequired,
48
+ };
49
+
50
+ handleClick: (SyntheticMouseEvent<>) => void = (event) => {
51
+ this.props.onChecked(!this.props.checked);
52
+
53
+ // NOTE(johnsullivan): Preventing default would make sense to do here
54
+ // because we're fully controlling the state of the check box and
55
+ // don't really want it to be (un)checked accidently. React
56
+ // requires that we *don't* call preventDefault from the onClick or
57
+ // onChecked handlers of a controlled component though.
58
+ };
59
+
60
+ handleKeyUp: (SyntheticKeyboardEvent<>) => void = (event) => {
61
+ // Make hitting the spacebar with the element selected equivalent to
62
+ // clicking it. Some browsers do this as part of the radio button's
63
+ // default behavior, but since some browsers don't we normalize the
64
+ // behavior here.
65
+ if (event.key === " ") {
66
+ this.props.onChecked(!this.props.checked);
67
+ event.preventDefault();
68
+ }
69
+ };
70
+
71
+ handleChange: (SyntheticInputEvent<>) => void = (event) => {
72
+ // If the checkbox is going from unchecked to checked, we'll handle it
73
+ // here.
74
+ // NOTE(johnsullivan): The onClick/onKeyUp handler most likely *also*
75
+ // handled this, but we're being defensive against browsers/devices
76
+ // that might not call those handlers like we'd expect. It's
77
+ // unclear to me whether this is strictly necessary.
78
+ if (!this.props.checked && event.target.checked) {
79
+ this.props.onChecked(true);
80
+ }
81
+ };
82
+
83
+ handleKeyDown: (SyntheticKeyboardEvent<>) => void = (event) => {
84
+ // This is necessary in order to prevent IE9 from creating a duplicate
85
+ // click event on the radio button when the space bar is hit.
86
+ if (event.key === " ") {
87
+ event.preventDefault();
88
+ } else if (event.key === "ArrowUp" || event.key === "ArrowLeft") {
89
+ this.props.goToPrevChoice();
90
+ event.preventDefault();
91
+ } else if (event.key === "ArrowDown" || event.key === "ArrowRight") {
92
+ this.props.goToNextChoice();
93
+ event.preventDefault();
94
+ }
95
+ };
96
+
97
+ /**
98
+ * We keep a reference to the input element ourselves, and provide it to
99
+ * the parent, too.
100
+ */
101
+ handleRef: (HTMLInputElement) => void = (inputElement) => {
102
+ this._inputElement = inputElement;
103
+ this.props.inputRef && this.props.inputRef(inputElement);
104
+ };
105
+
106
+ render(): React.Node {
107
+ const {
108
+ // eslint-disable-next-line no-unused-vars
109
+ inputRef,
110
+ // eslint-disable-next-line no-unused-vars
111
+ onChecked,
112
+ // eslint-disable-next-line no-unused-vars
113
+ goToPrevChoice,
114
+ // eslint-disable-next-line no-unused-vars
115
+ goToNextChoice,
116
+ ...otherProps
117
+ } = this.props;
118
+
119
+ return (
120
+ <input
121
+ {...otherProps}
122
+ type="radio"
123
+ onChange={this.handleChange}
124
+ onClick={this.handleClick}
125
+ onKeyDown={this.handleKeyDown}
126
+ onKeyUp={this.handleKeyUp}
127
+ ref={this.handleRef}
128
+ />
129
+ );
130
+ }
131
+ }
132
+
133
+ export default ToggleableRadioButton;
@@ -0,0 +1,32 @@
1
+ // @flow
2
+ import * as i18n from "@khanacademy/wonder-blocks-i18n";
3
+
4
+ /**
5
+ * Given a choice's position in the radio widget, return the corresponding
6
+ * "choice letter". (For example, in English, `getChoiceLetter(0)` is "A",
7
+ * `getChoiceLetter(1)` is "B", etc.)
8
+ */
9
+ export function getChoiceLetter(pos: number): string {
10
+ /* I18N: This is a list of single-character labels that will appear in
11
+ * front of multiple-choice options. For instance, a multiple-choice
12
+ * question with three options would display
13
+ * (A) first option
14
+ * (B) second option
15
+ * (C) third option
16
+ * There must be spaces between each of the different characters. The
17
+ * characters will show up next to options in the order that they are
18
+ * listed here. Most multiple choice questions have 5 or fewer options.
19
+ */
20
+ const lettersString = i18n._(
21
+ "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z",
22
+ );
23
+
24
+ const letters = lettersString.split(" ");
25
+
26
+ if (pos < letters.length) {
27
+ // If the position we need is listed in the localized string, use that.
28
+ return letters[pos];
29
+ }
30
+ // If we're out of letters, give up and return a space.
31
+ return " ";
32
+ }
@@ -0,0 +1,19 @@
1
+ // @flow
2
+ import {getChoiceLetter} from "./util.js";
3
+
4
+ describe("getChoiceLetter (in English)", () => {
5
+ it("returns the first 5 letters for most questions", () => {
6
+ expect(getChoiceLetter(0)).toEqual("A");
7
+ expect(getChoiceLetter(1)).toEqual("B");
8
+ expect(getChoiceLetter(2)).toEqual("C");
9
+ expect(getChoiceLetter(3)).toEqual("D");
10
+ expect(getChoiceLetter(4)).toEqual("E");
11
+ });
12
+
13
+ it("returns Z as the last letter, then spaces afterwards", () => {
14
+ expect(getChoiceLetter(25)).toEqual("Z");
15
+ for (let i = 26; i < 100; i++) {
16
+ expect(getChoiceLetter(i)).toEqual(" ");
17
+ }
18
+ });
19
+ });