@khanacademy/perseus 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (503) hide show
  1. package/.babelrc.js +1 -1
  2. package/CHANGELOG.md +7 -0
  3. package/dist/es/index.js +15 -1
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.css +11934 -0
  6. package/dist/index.js +15 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/strings.js +148 -1
  9. package/package.json +40 -4
  10. package/src/README.md +7 -0
  11. package/src/__stories__/item-renderer.stories.jsx +27 -0
  12. package/src/__stories__/server-item-renderer.stories.jsx +27 -0
  13. package/src/__testdata__/item-renderer_testdata.js +161 -0
  14. package/src/__testdata__/renderer_testdata.js +123 -0
  15. package/src/__tests__/__snapshots__/item-renderer_test.jsx.snap +233 -0
  16. package/src/__tests__/__snapshots__/perseus-markdown_test.jsx.snap +516 -0
  17. package/src/__tests__/__snapshots__/pure-markdown_test.js.snap +113 -0
  18. package/src/__tests__/__snapshots__/renderer_test.jsx.snap +576 -0
  19. package/src/__tests__/__snapshots__/server-item-renderer_test.jsx.snap +306 -0
  20. package/src/__tests__/a11y_test.jsx +304 -0
  21. package/src/__tests__/item-renderer_test.jsx +892 -0
  22. package/src/__tests__/mock-asset-loading-widget.jsx +57 -0
  23. package/src/__tests__/mock-widget.jsx +35 -0
  24. package/src/__tests__/perseus-markdown_test.jsx +747 -0
  25. package/src/__tests__/pure-markdown_test.js +552 -0
  26. package/src/__tests__/renderability_test.jsx +481 -0
  27. package/src/__tests__/renderer-api_test.jsx +333 -0
  28. package/src/__tests__/renderer_test.jsx +1792 -0
  29. package/src/__tests__/server-item-renderer_test.jsx +516 -0
  30. package/src/__tests__/test-items/image-item.js +25 -0
  31. package/src/__tests__/test-items/input-number-1-item.js +27 -0
  32. package/src/__tests__/test-items/input-number-2-item.js +39 -0
  33. package/src/__tests__/test-items/table-item.js +30 -0
  34. package/src/__tests__/tex-wrangler_test.js +83 -0
  35. package/src/__tests__/util_test.js +41 -0
  36. package/src/a11y.js +46 -0
  37. package/src/all-widgets.js +9 -0
  38. package/src/article-renderer.jsx +215 -0
  39. package/src/asset-context.js +30 -0
  40. package/src/basic-widgets.js +13 -0
  41. package/src/components/__stories__/button-group.stories.jsx +61 -0
  42. package/src/components/__stories__/fixed-to-responsive.stories.jsx +132 -0
  43. package/src/components/__stories__/graph.stories.jsx +27 -0
  44. package/src/components/__stories__/graphie.stories.jsx +28 -0
  45. package/src/components/__stories__/hud.stories.jsx +36 -0
  46. package/src/components/__stories__/icon.stories.jsx +56 -0
  47. package/src/components/__stories__/image-loader.stories.jsx +60 -0
  48. package/src/components/__stories__/info-tip.stories.jsx +33 -0
  49. package/src/components/__stories__/inline-icon.stories.jsx +39 -0
  50. package/src/components/__stories__/input-with-examples.stories.jsx +51 -0
  51. package/src/components/__stories__/lint.stories.jsx +40 -0
  52. package/src/components/__stories__/math-input.stories.jsx +29 -0
  53. package/src/components/__stories__/math-output.stories.jsx +24 -0
  54. package/src/components/__stories__/multi-button-group.stories.jsx +69 -0
  55. package/src/components/__stories__/number-input.stories.jsx +42 -0
  56. package/src/components/__stories__/possible-answers.stories.jsx +22 -0
  57. package/src/components/__stories__/prop-check-box.stories.jsx +62 -0
  58. package/src/components/__stories__/range-input.stories.jsx +28 -0
  59. package/src/components/__stories__/simple-keypad-input.stories.jsx +28 -0
  60. package/src/components/__stories__/sortable.stories.jsx +73 -0
  61. package/src/components/__stories__/stub-tag-editor.stories.jsx +44 -0
  62. package/src/components/__stories__/svg-image.stories.jsx +60 -0
  63. package/src/components/__stories__/tex.stories.jsx +18 -0
  64. package/src/components/__stories__/text-buttons.stories.jsx +51 -0
  65. package/src/components/__stories__/text-input.stories.jsx +34 -0
  66. package/src/components/__stories__/text-list-editor.stories.jsx +33 -0
  67. package/src/components/__stories__/zoomable-tex.stories.jsx +39 -0
  68. package/src/components/__stories__/zoomable.stories.jsx +40 -0
  69. package/src/components/__testdata__/sorter_testdata.js +20 -0
  70. package/src/components/__tests__/__snapshots__/sortable_test.jsx.snap +79 -0
  71. package/src/components/__tests__/__snapshots__/sorter_test.jsx.snap +297 -0
  72. package/src/components/__tests__/number-input_test.jsx +112 -0
  73. package/src/components/__tests__/sortable_test.jsx +234 -0
  74. package/src/components/__tests__/sorter_test.jsx +111 -0
  75. package/src/components/__tests__/zoomable_test.jsx +388 -0
  76. package/src/components/button-group.jsx +131 -0
  77. package/src/components/constants.js +15 -0
  78. package/src/components/fixed-to-responsive.jsx +163 -0
  79. package/src/components/graph.jsx +414 -0
  80. package/src/components/graphie-classes.jsx +148 -0
  81. package/src/components/graphie-movables.jsx +342 -0
  82. package/src/components/graphie.jsx +353 -0
  83. package/src/components/highlighting/highlightable-content.jsx +245 -0
  84. package/src/components/highlighting/highlights.js +208 -0
  85. package/src/components/highlighting/ranges.js +299 -0
  86. package/src/components/highlighting/types.js +73 -0
  87. package/src/components/highlighting/ui/highlight-renderer.jsx +179 -0
  88. package/src/components/highlighting/ui/highlight-set-renderer.jsx +190 -0
  89. package/src/components/highlighting/ui/highlight-tooltip.jsx +153 -0
  90. package/src/components/highlighting/ui/highlighting-ui.jsx +163 -0
  91. package/src/components/highlighting/ui/selection-tracker.jsx +195 -0
  92. package/src/components/highlighting/ui/types.js +21 -0
  93. package/src/components/highlighting/ui/util.js +166 -0
  94. package/src/components/highlighting/word-indexer.jsx +155 -0
  95. package/src/components/hud.jsx +179 -0
  96. package/src/components/icon.jsx +149 -0
  97. package/src/components/image-loader.jsx +175 -0
  98. package/src/components/info-tip/info-tip.jsx +149 -0
  99. package/src/components/info-tip.jsx +38 -0
  100. package/src/components/inline-icon.jsx +81 -0
  101. package/src/components/input-with-examples.jsx +231 -0
  102. package/src/components/lint.jsx +456 -0
  103. package/src/components/math-input.jsx +282 -0
  104. package/src/components/math-output.jsx +128 -0
  105. package/src/components/multi-button-group.jsx +137 -0
  106. package/src/components/new-tooltip/README.md +110 -0
  107. package/src/components/new-tooltip/__stories__/flexible-position.stories.jsx +437 -0
  108. package/src/components/new-tooltip/__stories__/new-tooltip.stories.jsx +665 -0
  109. package/src/components/new-tooltip/__stories__/tooltip-arrow.stories.jsx +70 -0
  110. package/src/components/new-tooltip/__stories__/tooltip-bubble.stories.jsx +98 -0
  111. package/src/components/new-tooltip/__tests__/style-constants_test.js +8 -0
  112. package/src/components/new-tooltip/__tests__/util_test.js +47 -0
  113. package/src/components/new-tooltip/flexible-position.jsx +182 -0
  114. package/src/components/new-tooltip/new-tooltip.jsx +399 -0
  115. package/src/components/new-tooltip/style-constants.js +13 -0
  116. package/src/components/new-tooltip/tooltip-arrow.jsx +184 -0
  117. package/src/components/new-tooltip/tooltip-bubble.jsx +223 -0
  118. package/src/components/new-tooltip/tooltip-portal.jsx +101 -0
  119. package/src/components/new-tooltip/tooltip-positioner.jsx +529 -0
  120. package/src/components/new-tooltip/types.js +183 -0
  121. package/src/components/new-tooltip/util.js +27 -0
  122. package/src/components/number-input.jsx +253 -0
  123. package/src/components/possible-answers.jsx +36 -0
  124. package/src/components/prop-check-box.jsx +78 -0
  125. package/src/components/range-input.jsx +61 -0
  126. package/src/components/simple-keypad-input.jsx +91 -0
  127. package/src/components/sortable.jsx +872 -0
  128. package/src/components/stub-tag-editor.jsx +53 -0
  129. package/src/components/svg-image.jsx +762 -0
  130. package/src/components/tex-buttons.jsx +322 -0
  131. package/src/components/tex.jsx +70 -0
  132. package/src/components/text-input.jsx +82 -0
  133. package/src/components/text-list-editor.jsx +157 -0
  134. package/src/components/tooltip.jsx +395 -0
  135. package/src/components/visibility-observer/__stories__/visibility-observer.stories.jsx +140 -0
  136. package/src/components/visibility-observer/get-scroll-parent.js +45 -0
  137. package/src/components/visibility-observer/visibility-observer.js +109 -0
  138. package/src/components/visibility-observer/visibility-observer.jsx +123 -0
  139. package/src/components/zoomable-tex.jsx +70 -0
  140. package/src/components/zoomable.jsx +304 -0
  141. package/src/components.js +17 -0
  142. package/src/dependencies.js +22 -0
  143. package/src/error-boundary.jsx +48 -0
  144. package/src/extra-widgets.js +75 -0
  145. package/src/gorgon/README +40 -0
  146. package/src/gorgon/__tests__/matcher_test.js +497 -0
  147. package/src/gorgon/__tests__/rule_test.js +101 -0
  148. package/src/gorgon/__tests__/rules_test.js +487 -0
  149. package/src/gorgon/__tests__/selector-parser_test.js +52 -0
  150. package/src/gorgon/__tests__/tree-transformer_test.js +446 -0
  151. package/src/gorgon/gorgon.js +290 -0
  152. package/src/gorgon/proptypes.js +29 -0
  153. package/src/gorgon/rule.js +413 -0
  154. package/src/gorgon/rules/absolute-url.js +24 -0
  155. package/src/gorgon/rules/all-rules.js +72 -0
  156. package/src/gorgon/rules/blockquoted-math.js +10 -0
  157. package/src/gorgon/rules/blockquoted-widget.js +10 -0
  158. package/src/gorgon/rules/double-spacing-after-terminal.js +12 -0
  159. package/src/gorgon/rules/extra-content-spacing.js +12 -0
  160. package/src/gorgon/rules/heading-level-1.js +14 -0
  161. package/src/gorgon/rules/heading-level-skip.js +20 -0
  162. package/src/gorgon/rules/heading-sentence-case.js +11 -0
  163. package/src/gorgon/rules/heading-title-case.js +63 -0
  164. package/src/gorgon/rules/image-alt-text.js +21 -0
  165. package/src/gorgon/rules/image-in-table.js +10 -0
  166. package/src/gorgon/rules/image-spaces-around-urls.js +35 -0
  167. package/src/gorgon/rules/image-widget.js +50 -0
  168. package/src/gorgon/rules/link-click-here.js +11 -0
  169. package/src/gorgon/rules/lint-utils.js +48 -0
  170. package/src/gorgon/rules/long-paragraph.js +14 -0
  171. package/src/gorgon/rules/math-adjacent.js +10 -0
  172. package/src/gorgon/rules/math-align-extra-break.js +11 -0
  173. package/src/gorgon/rules/math-align-linebreaks.js +43 -0
  174. package/src/gorgon/rules/math-empty.js +10 -0
  175. package/src/gorgon/rules/math-font-size.js +12 -0
  176. package/src/gorgon/rules/math-frac.js +10 -0
  177. package/src/gorgon/rules/math-nested.js +11 -0
  178. package/src/gorgon/rules/math-starts-with-space.js +12 -0
  179. package/src/gorgon/rules/math-text-empty.js +10 -0
  180. package/src/gorgon/rules/math-without-dollars.js +14 -0
  181. package/src/gorgon/rules/nested-lists.js +11 -0
  182. package/src/gorgon/rules/profanity.js +10 -0
  183. package/src/gorgon/rules/table-missing-cells.js +20 -0
  184. package/src/gorgon/rules/unbalanced-code-delimiters.js +14 -0
  185. package/src/gorgon/rules/unescaped-dollar.js +10 -0
  186. package/src/gorgon/rules/widget-in-table.js +10 -0
  187. package/src/gorgon/selector.js +506 -0
  188. package/src/gorgon/tree-transformer.js +588 -0
  189. package/src/hint-renderer.jsx +159 -0
  190. package/src/hints-renderer.jsx +359 -0
  191. package/src/icon-paths.js +120 -0
  192. package/src/index.js +149 -2
  193. package/src/init.js +49 -0
  194. package/src/interaction-tracker.js +60 -0
  195. package/src/interactive2/__tests__/movable-mock.js +48 -0
  196. package/src/interactive2/__tests__/movable-point_test.js +146 -0
  197. package/src/interactive2/interactive-util.js +152 -0
  198. package/src/interactive2/movable-helper-methods.js +160 -0
  199. package/src/interactive2/movable-line-options.js +392 -0
  200. package/src/interactive2/movable-line.js +322 -0
  201. package/src/interactive2/movable-point-options.js +190 -0
  202. package/src/interactive2/movable-point.jsx +604 -0
  203. package/src/interactive2/movable-polygon-options.js +351 -0
  204. package/src/interactive2/movable-polygon.js +367 -0
  205. package/src/interactive2/movable.js +273 -0
  206. package/src/interactive2/objective_.js +79 -0
  207. package/src/interactive2/types.js +3 -0
  208. package/src/interactive2/wrapped-defaults.js +86 -0
  209. package/src/interactive2/wrapped-ellipse.js +87 -0
  210. package/src/interactive2/wrapped-line.js +109 -0
  211. package/src/interactive2/wrapped-path.js +47 -0
  212. package/src/interactive2.js +91 -0
  213. package/src/item-renderer.jsx +518 -0
  214. package/src/item-version.js +12 -0
  215. package/src/jipt-paragraphs.js +44 -0
  216. package/src/jquery.mobile.vmouse.js +545 -0
  217. package/src/keypad-context.js +33 -0
  218. package/src/loading-context.js +20 -0
  219. package/src/logging/log.js +107 -0
  220. package/src/logging/log_test.js +41 -0
  221. package/src/mixins/changeable.jsx +115 -0
  222. package/src/mixins/editor-jsonify.jsx +13 -0
  223. package/src/mixins/provide-keypad.jsx +131 -0
  224. package/src/mixins/widget-jsonify-deprecated.jsx +23 -0
  225. package/src/mixins/widget-prop-blacklist.js +36 -0
  226. package/src/multi-items/__stories__/multi-renderer.stories.js +88 -0
  227. package/src/multi-items/__testdata__/multi-renderer_testdata.js +174 -0
  228. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +994 -0
  229. package/src/multi-items/__tests__/items_test.js +245 -0
  230. package/src/multi-items/__tests__/multi-renderer_test.js +903 -0
  231. package/src/multi-items/__tests__/prop-type-builders_test.js +134 -0
  232. package/src/multi-items/__tests__/shapes_test.js +44 -0
  233. package/src/multi-items/__tests__/trees_test.js +316 -0
  234. package/src/multi-items/item-types.js +55 -0
  235. package/src/multi-items/items.js +176 -0
  236. package/src/multi-items/multi-renderer.jsx +542 -0
  237. package/src/multi-items/prop-type-builders.js +105 -0
  238. package/src/multi-items/shape-types.js +47 -0
  239. package/src/multi-items/shapes.js +53 -0
  240. package/src/multi-items/tree-types.js +42 -0
  241. package/src/multi-items/trees.js +345 -0
  242. package/src/multi-items.js +76 -0
  243. package/src/not-gorgon.js +129 -0
  244. package/src/perseus-api.jsx +270 -0
  245. package/src/perseus-env.js +26 -0
  246. package/src/perseus-error.js +19 -0
  247. package/src/perseus-markdown.jsx +434 -0
  248. package/src/perseus-types.js +1597 -0
  249. package/src/pure-markdown.js +346 -0
  250. package/src/question-paragraph.jsx +30 -0
  251. package/src/renderability.jsx +108 -0
  252. package/src/renderer.jsx +1931 -0
  253. package/src/server-item-renderer.jsx +467 -0
  254. package/src/sigfigs.jsx +492 -0
  255. package/src/styles/articles.less +748 -0
  256. package/src/styles/constants.js +119 -0
  257. package/src/styles/global-constants.js +350 -0
  258. package/src/styles/global-styles.js +307 -0
  259. package/src/styles/khan-exercise.css +1304 -0
  260. package/src/styles/media-queries.js +48 -0
  261. package/src/styles/perseus-renderer.less +1008 -0
  262. package/src/styles/shared.js +132 -0
  263. package/src/styles/util.less +47 -0
  264. package/src/styles/variables.boostrap.less +301 -0
  265. package/src/styles/variables.less +68 -0
  266. package/src/styles/variables.shared.less +356 -0
  267. package/src/styles/widgets/categorizer.less +39 -0
  268. package/src/styles/widgets/definition.less +86 -0
  269. package/src/styles/widgets/dropdown.less +3 -0
  270. package/src/styles/widgets/expression.less +147 -0
  271. package/src/styles/widgets/graded-group.less +36 -0
  272. package/src/styles/widgets/grapher.less +11 -0
  273. package/src/styles/widgets/group.less +19 -0
  274. package/src/styles/widgets/image.less +29 -0
  275. package/src/styles/widgets/interactive-graph.less +61 -0
  276. package/src/styles/widgets/label-image.less +41 -0
  277. package/src/styles/widgets/matcher.less +8 -0
  278. package/src/styles/widgets/matrix.less +120 -0
  279. package/src/styles/widgets/measurer.less +20 -0
  280. package/src/styles/widgets/orderer.less +97 -0
  281. package/src/styles/widgets/passage.less +151 -0
  282. package/src/styles/widgets/plotter.less +33 -0
  283. package/src/styles/widgets/radio.less +52 -0
  284. package/src/styles/widgets/reaction-diagram.less +35 -0
  285. package/src/styles/widgets/sequence.less +5 -0
  286. package/src/styles/widgets/sortable.less +127 -0
  287. package/src/styles/widgets/table.less +56 -0
  288. package/src/styles/widgets/transformer.less +28 -0
  289. package/src/styles/widgets/unit.less +14 -0
  290. package/src/tex-wrangler.js +176 -0
  291. package/src/traversal.jsx +141 -0
  292. package/src/types.js +538 -0
  293. package/src/util/a11y.js +24 -0
  294. package/src/util/answer-types.js +862 -0
  295. package/src/util/answer-types_test.js +115 -0
  296. package/src/util/colors.js +94 -0
  297. package/src/util/fix-passage-refs.jsx +122 -0
  298. package/src/util/graph-utils.js +16 -0
  299. package/src/util/graphie.js +1585 -0
  300. package/src/util/interactive.js +4071 -0
  301. package/src/util/katex-preprocess.js +16 -0
  302. package/src/util/math.js +296 -0
  303. package/src/util/mobile-native-utils.js +14 -0
  304. package/src/util/mobile-native-utils_test.js +24 -0
  305. package/src/util/register-all-widgets-for-testing.js +12 -0
  306. package/src/util/scroll-utils.js +162 -0
  307. package/src/util/sizing-utils.js +46 -0
  308. package/src/util/tex.js +203 -0
  309. package/src/util/url-utils.js +36 -0
  310. package/src/util.js +990 -0
  311. package/src/version.js +11 -0
  312. package/src/widget-container.jsx +174 -0
  313. package/src/widgets/__stories__/categorizer.stories.jsx +15 -0
  314. package/src/widgets/__stories__/cs-program.stories.jsx +15 -0
  315. package/src/widgets/__stories__/definition.stories.jsx +38 -0
  316. package/src/widgets/__stories__/dropdown.stories.jsx +15 -0
  317. package/src/widgets/__stories__/explanation.stories.jsx +19 -0
  318. package/src/widgets/__stories__/expression.stories.jsx +26 -0
  319. package/src/widgets/__stories__/graded-group-set.stories.jsx +32 -0
  320. package/src/widgets/__stories__/graded-group.stories.jsx +32 -0
  321. package/src/widgets/__stories__/grapher.stories.jsx +47 -0
  322. package/src/widgets/__stories__/group.stories.jsx +15 -0
  323. package/src/widgets/__stories__/iframe.stories.jsx +15 -0
  324. package/src/widgets/__stories__/image.stories.jsx +62 -0
  325. package/src/widgets/__stories__/input-number.stories.jsx +90 -0
  326. package/src/widgets/__stories__/interaction.stories.jsx +15 -0
  327. package/src/widgets/__stories__/interactive-graph.stories.jsx +61 -0
  328. package/src/widgets/__stories__/matcher.stories.jsx +15 -0
  329. package/src/widgets/__stories__/matrix.stories.jsx +15 -0
  330. package/src/widgets/__stories__/number-line.stories.jsx +15 -0
  331. package/src/widgets/__stories__/numeric-input.stories.jsx +19 -0
  332. package/src/widgets/__stories__/orderer.stories.jsx +15 -0
  333. package/src/widgets/__stories__/passage-ref.stories.jsx +19 -0
  334. package/src/widgets/__stories__/passage.stories.jsx +19 -0
  335. package/src/widgets/__stories__/radio.stories.jsx +82 -0
  336. package/src/widgets/__stories__/sorter.stories.jsx +15 -0
  337. package/src/widgets/__stories__/transformer.stories.jsx +15 -0
  338. package/src/widgets/__stories__/video.stories.jsx +15 -0
  339. package/src/widgets/__testdata__/categorizer_testdata.js +50 -0
  340. package/src/widgets/__testdata__/cs-program_testdata.js +28 -0
  341. package/src/widgets/__testdata__/dropdown_testdata.js +34 -0
  342. package/src/widgets/__testdata__/explanation_testdata.js +56 -0
  343. package/src/widgets/__testdata__/expression_testdata.js +95 -0
  344. package/src/widgets/__testdata__/graded-group-set_testdata.js +358 -0
  345. package/src/widgets/__testdata__/graded-group_testdata.js +79 -0
  346. package/src/widgets/__testdata__/grapher_testdata.js +376 -0
  347. package/src/widgets/__testdata__/group_testdata.js +156 -0
  348. package/src/widgets/__testdata__/iframe_testdata.js +36 -0
  349. package/src/widgets/__testdata__/image_testdata.js +38 -0
  350. package/src/widgets/__testdata__/input-number_testdata.js +73 -0
  351. package/src/widgets/__testdata__/interaction_testdata.js +264 -0
  352. package/src/widgets/__testdata__/interactive-graph_testdata.js +646 -0
  353. package/src/widgets/__testdata__/matcher_testdata.js +34 -0
  354. package/src/widgets/__testdata__/matrix_testdata.js +29 -0
  355. package/src/widgets/__testdata__/number-line_testdata.js +31 -0
  356. package/src/widgets/__testdata__/numeric-input_testdata.js +266 -0
  357. package/src/widgets/__testdata__/orderer_testdata.js +58 -0
  358. package/src/widgets/__testdata__/passage-ref_testdata.js +41 -0
  359. package/src/widgets/__testdata__/passage_testdata.js +71 -0
  360. package/src/widgets/__testdata__/radio_testdata.js +210 -0
  361. package/src/widgets/__testdata__/sorter_testdata.js +20 -0
  362. package/src/widgets/__testdata__/transformer_testdata.js +109 -0
  363. package/src/widgets/__testdata__/video_testdata.js +22 -0
  364. package/src/widgets/__tests__/__snapshots__/categorizer_test.jsx.snap +928 -0
  365. package/src/widgets/__tests__/__snapshots__/cs-program_test.jsx.snap +75 -0
  366. package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +99 -0
  367. package/src/widgets/__tests__/__snapshots__/dropdown_test.jsx.snap +143 -0
  368. package/src/widgets/__tests__/__snapshots__/explanation_test.js.snap +473 -0
  369. package/src/widgets/__tests__/__snapshots__/expression_test.jsx.snap +38 -0
  370. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +754 -0
  371. package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +310 -0
  372. package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +863 -0
  373. package/src/widgets/__tests__/__snapshots__/grapher_test.jsx.snap +2880 -0
  374. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +1154 -0
  375. package/src/widgets/__tests__/__snapshots__/iframe_test.jsx.snap +69 -0
  376. package/src/widgets/__tests__/__snapshots__/image_test.jsx.snap +388 -0
  377. package/src/widgets/__tests__/__snapshots__/input-number_test.jsx.snap +978 -0
  378. package/src/widgets/__tests__/__snapshots__/interaction_test.jsx.snap +311 -0
  379. package/src/widgets/__tests__/__snapshots__/interactive-graph_test.jsx.snap +2915 -0
  380. package/src/widgets/__tests__/__snapshots__/matcher_test.jsx.snap +1039 -0
  381. package/src/widgets/__tests__/__snapshots__/matrix_test.jsx.snap +450 -0
  382. package/src/widgets/__tests__/__snapshots__/number-line_test.jsx.snap +888 -0
  383. package/src/widgets/__tests__/__snapshots__/numeric-input_test.jsx.snap +930 -0
  384. package/src/widgets/__tests__/__snapshots__/orderer_test.jsx.snap +275 -0
  385. package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +446 -0
  386. package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +1988 -0
  387. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +7107 -0
  388. package/src/widgets/__tests__/__snapshots__/video_test.jsx.snap +163 -0
  389. package/src/widgets/__tests__/categorizer_test.jsx +205 -0
  390. package/src/widgets/__tests__/cs-program_test.jsx +45 -0
  391. package/src/widgets/__tests__/definition_test.js +152 -0
  392. package/src/widgets/__tests__/dropdown_test.jsx +106 -0
  393. package/src/widgets/__tests__/explanation_test.js +199 -0
  394. package/src/widgets/__tests__/expression_test.jsx +439 -0
  395. package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +33 -0
  396. package/src/widgets/__tests__/graded-group-set_test.jsx +270 -0
  397. package/src/widgets/__tests__/graded-group_test.jsx +233 -0
  398. package/src/widgets/__tests__/grapher.cypress.jsx +486 -0
  399. package/src/widgets/__tests__/grapher_test.jsx +35 -0
  400. package/src/widgets/__tests__/group_test.jsx +512 -0
  401. package/src/widgets/__tests__/iframe_test.jsx +47 -0
  402. package/src/widgets/__tests__/image_test.jsx +65 -0
  403. package/src/widgets/__tests__/input-number_test.jsx +360 -0
  404. package/src/widgets/__tests__/interaction_test.jsx +33 -0
  405. package/src/widgets/__tests__/interactive-graph.cypress.jsx +697 -0
  406. package/src/widgets/__tests__/interactive-graph_test.jsx +105 -0
  407. package/src/widgets/__tests__/matcher_test.jsx +157 -0
  408. package/src/widgets/__tests__/matrix_test.jsx +84 -0
  409. package/src/widgets/__tests__/number-line_test.jsx +81 -0
  410. package/src/widgets/__tests__/numeric-input_test.jsx +574 -0
  411. package/src/widgets/__tests__/orderer_test.jsx +75 -0
  412. package/src/widgets/__tests__/passage-ref_test.jsx +166 -0
  413. package/src/widgets/__tests__/passage_test.jsx +133 -0
  414. package/src/widgets/__tests__/radio/base-radio_test.jsx +82 -0
  415. package/src/widgets/__tests__/radio_test.jsx +651 -0
  416. package/src/widgets/__tests__/renderQuestion.jsx +65 -0
  417. package/src/widgets/__tests__/unit_test.jsx +144 -0
  418. package/src/widgets/__tests__/video_test.jsx +55 -0
  419. package/src/widgets/categorizer.jsx +338 -0
  420. package/src/widgets/cs-program.jsx +228 -0
  421. package/src/widgets/definition.jsx +141 -0
  422. package/src/widgets/dropdown.jsx +143 -0
  423. package/src/widgets/explanation.jsx +315 -0
  424. package/src/widgets/expression.jsx +654 -0
  425. package/src/widgets/graded-group-answer-bar.jsx +152 -0
  426. package/src/widgets/graded-group-set.jsx +285 -0
  427. package/src/widgets/graded-group.jsx +448 -0
  428. package/src/widgets/grapher/util.jsx +819 -0
  429. package/src/widgets/grapher.jsx +658 -0
  430. package/src/widgets/group.jsx +213 -0
  431. package/src/widgets/iframe.jsx +199 -0
  432. package/src/widgets/image.jsx +309 -0
  433. package/src/widgets/input-number.jsx +410 -0
  434. package/src/widgets/interaction/arrow-picker.jsx +39 -0
  435. package/src/widgets/interaction/color-picker.jsx +69 -0
  436. package/src/widgets/interaction/dash-picker.jsx +46 -0
  437. package/src/widgets/interaction/element-container.jsx +97 -0
  438. package/src/widgets/interaction.jsx +742 -0
  439. package/src/widgets/interactive-graph.jsx +2821 -0
  440. package/src/widgets/label-image/__stories__/answer-choices.stories.jsx +93 -0
  441. package/src/widgets/label-image/__stories__/marker.stories.jsx +172 -0
  442. package/src/widgets/label-image/answer-choices.jsx +311 -0
  443. package/src/widgets/label-image/marker.jsx +365 -0
  444. package/src/widgets/label-image/types.js +33 -0
  445. package/src/widgets/label-image.jsx +841 -0
  446. package/src/widgets/label-image_test.js +840 -0
  447. package/src/widgets/lights-puzzle.jsx +356 -0
  448. package/src/widgets/matcher.jsx +271 -0
  449. package/src/widgets/matrix.jsx +615 -0
  450. package/src/widgets/measurer.jsx +215 -0
  451. package/src/widgets/molecule/__tests__/layout_test.jsx +159 -0
  452. package/src/widgets/molecule/__tests__/parser_test.jsx +142 -0
  453. package/src/widgets/molecule/molecule-drawing.jsx +175 -0
  454. package/src/widgets/molecule/molecule-layout.jsx +367 -0
  455. package/src/widgets/molecule/smiles-parser.jsx +300 -0
  456. package/src/widgets/molecule.jsx +172 -0
  457. package/src/widgets/number-line.jsx +858 -0
  458. package/src/widgets/numeric-input.jsx +622 -0
  459. package/src/widgets/orderer.jsx +753 -0
  460. package/src/widgets/passage/__tests__/passage-markdown_test.jsx +382 -0
  461. package/src/widgets/passage/get-line-height-for-node.js +10 -0
  462. package/src/widgets/passage/passage-markdown.jsx +416 -0
  463. package/src/widgets/passage-ref-target.jsx +70 -0
  464. package/src/widgets/passage-ref.jsx +198 -0
  465. package/src/widgets/passage.jsx +601 -0
  466. package/src/widgets/plotter.jsx +1198 -0
  467. package/src/widgets/radio/__stories__/base-radio.stories.jsx +40 -0
  468. package/src/widgets/radio/__stories__/choice-icon.stories.jsx +157 -0
  469. package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +26 -0
  470. package/src/widgets/radio/__stories__/cross-out-button.stories.jsx +64 -0
  471. package/src/widgets/radio/__stories__/cross-out-menu.stories.jsx +59 -0
  472. package/src/widgets/radio/__stories__/cross-out-tooltip.stories.jsx +59 -0
  473. package/src/widgets/radio/__stories__/focus-ring.stories.jsx +38 -0
  474. package/src/widgets/radio/__stories__/option-status.stories.jsx +118 -0
  475. package/src/widgets/radio/__stories__/toggleable-radio-button.stories.jsx +28 -0
  476. package/src/widgets/radio/base-radio.jsx +642 -0
  477. package/src/widgets/radio/choice-icon.jsx +468 -0
  478. package/src/widgets/radio/choice-none-above.jsx +54 -0
  479. package/src/widgets/radio/choice.jsx +692 -0
  480. package/src/widgets/radio/choice.jsx.fixture.js +64 -0
  481. package/src/widgets/radio/cross-out-button.jsx +136 -0
  482. package/src/widgets/radio/cross-out-menu-wrapper.jsx +492 -0
  483. package/src/widgets/radio/cross-out-tooltip.jsx +129 -0
  484. package/src/widgets/radio/focus-ring.jsx +51 -0
  485. package/src/widgets/radio/option-status.jsx +89 -0
  486. package/src/widgets/radio/toggleable-radio-button.jsx +133 -0
  487. package/src/widgets/radio/util.js +32 -0
  488. package/src/widgets/radio/util_test.js +19 -0
  489. package/src/widgets/radio/widget.jsx +470 -0
  490. package/src/widgets/radio.jsx +156 -0
  491. package/src/widgets/reaction-diagram.jsx +136 -0
  492. package/src/widgets/sequence.jsx +140 -0
  493. package/src/widgets/simulator.jsx +808 -0
  494. package/src/widgets/sorter.jsx +146 -0
  495. package/src/widgets/table.jsx +395 -0
  496. package/src/widgets/transformer.jsx +2870 -0
  497. package/src/widgets/unit.jsx +371 -0
  498. package/src/widgets/video-transcript-link.jsx +84 -0
  499. package/src/widgets/video-transcript-link_test.js +211 -0
  500. package/src/widgets/video.jsx +131 -0
  501. package/src/widgets.js +431 -0
  502. package/src/zoom.js +539 -0
  503. package/src/__tests__/index.test.js +0 -14
@@ -0,0 +1,692 @@
1
+ /* eslint-disable react/no-unsafe */
2
+ // @flow
3
+ import {StyleSheet, css} from "aphrodite";
4
+ import classNames from "classnames";
5
+ import PropTypes from "prop-types";
6
+ import * as React from "react";
7
+ import _ from "underscore";
8
+
9
+ import {ClassNames} from "../../perseus-api.jsx";
10
+ import * as styleConstants from "../../styles/constants.js";
11
+ import mediaQueries from "../../styles/media-queries.js";
12
+ import sharedStyles from "../../styles/shared.js";
13
+
14
+ import ChoiceIcon from "./choice-icon.jsx";
15
+ import CrossOutMenuWrapper from "./cross-out-menu-wrapper.jsx";
16
+ import OptionStatus from "./option-status.jsx";
17
+ import ToggleableRadioButton from "./toggleable-radio-button.jsx";
18
+
19
+ const focusedStyleMixin = {
20
+ backgroundColor: styleConstants.satSelectedBackgroundColor,
21
+ outline: `2px solid ${styleConstants.satBlue}`,
22
+ // Render the outline higher than the next element's border
23
+ zIndex: 1,
24
+ };
25
+
26
+ const intermediateCheckboxPadding = `16px 16px`;
27
+ const intermediateCheckboxPaddingPhone = `12px 16px`;
28
+
29
+ export const TouchIgnoreTimeout = 10;
30
+
31
+ type State = {|
32
+ isInputFocused: boolean,
33
+ isInputActive: boolean,
34
+ |};
35
+
36
+ class Choice extends React.Component<$FlowFixMe, State> {
37
+ justFinishedTouch: boolean;
38
+ justFinishedTimeoutID: TimeoutID;
39
+ _input: ?HTMLInputElement;
40
+
41
+ static propTypes = {
42
+ // TODO(kevinb) use Options.propTypes from perseus-api.jsx
43
+ // This change will also require make sure that item-renderer.jsx and
44
+ // server-item-renderer.jsx have appropriate defaults for apiOptions
45
+ // because many of the properties on Options.propTypes are required.
46
+ apiOptions: PropTypes.shape({
47
+ satStyling: PropTypes.bool,
48
+ isMobile: PropTypes.bool,
49
+ styling: PropTypes.shape({
50
+ radioStyleVersion: PropTypes.oneOf(["intermediate", "final"]),
51
+ primaryProductColor: PropTypes.string,
52
+ }),
53
+ readOnly: PropTypes.bool,
54
+ }),
55
+ checked: PropTypes.bool,
56
+ className: PropTypes.string,
57
+ rationale: PropTypes.node,
58
+ content: PropTypes.node,
59
+ correct: PropTypes.bool,
60
+ deselectEnabled: PropTypes.bool,
61
+ disabled: PropTypes.bool,
62
+ editMode: PropTypes.bool,
63
+ groupName: PropTypes.string,
64
+ isLastChoice: PropTypes.bool, // Needed for border styling
65
+ // This indicates the position of the choice relative to others
66
+ // (so that we can display a nice little (A), (B), etc. next to it)
67
+ // Also used to generate an id for each input.
68
+ pos: PropTypes.number,
69
+ reviewMode: PropTypes.bool,
70
+ showRationale: PropTypes.bool,
71
+ showCorrectness: PropTypes.bool,
72
+ type: PropTypes.string,
73
+
74
+ // Indicates whether the user has "crossed out" this choice, meaning
75
+ // that they don't think it's correct. This value does not affect
76
+ // scoring or other behavior; it's just a note for the user's
77
+ // reference.
78
+ crossedOut: PropTypes.bool,
79
+
80
+ // Indicates that the user has previously selected this answer. These
81
+ // answers may be rendered orange in review, rather than grey if
82
+ // incorrect.
83
+ previouslyAnswered: PropTypes.bool,
84
+
85
+ // A callback indicating that this choice has changed. Its argument is
86
+ // an object with two keys: `checked` and `crossedOut`. Each contains a
87
+ // boolean value specifying the new checked and crossed-out value of
88
+ // this choice.
89
+ onChange: PropTypes.func,
90
+
91
+ // Callbacks for when the user presses the up arrow or down arrow
92
+ // (respectively) to navigate between choices. When called, the parent
93
+ // finds the prev/next choice, focuses the input, and checks the radio
94
+ // button if appropriate. (The reason we have custom behavior is that
95
+ // we actually _don't_ want to auto-select crossed-out choices!)
96
+ goToPrevChoice: PropTypes.func.isRequired,
97
+ goToNextChoice: PropTypes.func.isRequired,
98
+ };
99
+
100
+ static defaultProps: $FlowFixMe = {
101
+ apiOptions: {
102
+ styling: {},
103
+ },
104
+ checked: false,
105
+ classSet: {},
106
+ correct: false,
107
+ disabled: false,
108
+ editMode: false,
109
+ onChange: function () {},
110
+ showRationale: false,
111
+ type: "radio",
112
+ pos: 0,
113
+ previouslyAnswered: false,
114
+ };
115
+
116
+ state: $FlowFixMe = {
117
+ isInputFocused: false,
118
+ isInputActive: false,
119
+ };
120
+
121
+ UNSAFE_componentWillUpdate(nextProps: $FlowFixMe) {
122
+ if (this.state.isInputFocused && nextProps.disabled) {
123
+ this.setState({
124
+ isInputFocused: false,
125
+ });
126
+ }
127
+ }
128
+
129
+ componentWillUnmount() {
130
+ if (this.justFinishedTimeoutID != null) {
131
+ // TODO(jeff, CP-3128): Use Wonder Blocks Timing API
132
+ // eslint-disable-next-line no-restricted-syntax
133
+ clearTimeout(this.justFinishedTimeoutID);
134
+ }
135
+ }
136
+
137
+ onInputFocus: () => void = () => {
138
+ this.setState({isInputFocused: true});
139
+ };
140
+
141
+ onInputBlur: () => void = () => {
142
+ this.setState({isInputFocused: false});
143
+ };
144
+
145
+ onInputMouseDown: (SyntheticMouseEvent<> | SyntheticTouchEvent<>) => void =
146
+ (e) => {
147
+ if (e.type === "mousedown" && this.justFinishedTouch) {
148
+ return;
149
+ }
150
+
151
+ this.setState({isInputActive: true});
152
+
153
+ // Simulate Chrome's radio button behavior in all browsers: when the
154
+ // mouse goes down or up, the radio button should become focused.
155
+ // That way, the newly-selected answer becomes highlighted after click.
156
+ if (this.props.apiOptions.satStyling && this._input) {
157
+ this._input.focus();
158
+ }
159
+ };
160
+
161
+ onInputMouseUp: (SyntheticMouseEvent<> | SyntheticTouchEvent<>) => void = (
162
+ e,
163
+ ) => {
164
+ if (e.type === "mouseup" && this.justFinishedTouch) {
165
+ return;
166
+ }
167
+
168
+ // NOTE(emily): We do some special handling here of touch events to
169
+ // make the "active" effect look better. In particular, when you click
170
+ // using touch events, we get a series of events going
171
+ // touchstart -> (delay) -> touchend -> mousedown -> mouseup -> click
172
+ // In order to make sure that we don't turn the active state off and on
173
+ // and off again during the touchend -> mousedown -> mouseup series, we
174
+ // set a flag (this.justFinishedTouch) after the touchend, and ignore
175
+ // the mousedown and mouseup events. Then, a little while later, we
176
+ // turn the flag off. Instead of turning the active state off right at
177
+ // the beginning, we wait for a little bit to sync it up better with
178
+ // the click event.
179
+ if (e.type === "touchend") {
180
+ this.justFinishedTouch = true;
181
+
182
+ // TODO(jeff, CP-3128): Use Wonder Blocks Timing API
183
+ /* eslint-disable no-restricted-syntax */
184
+ if (this.justFinishedTimeoutID != null) {
185
+ clearTimeout(this.justFinishedTimeoutID);
186
+ }
187
+ this.justFinishedTimeoutID = setTimeout(() => {
188
+ this.setState({isInputActive: false});
189
+ this.justFinishedTouch = false;
190
+ }, TouchIgnoreTimeout);
191
+ /* eslint-enable no-restricted-syntax */
192
+ return;
193
+ }
194
+
195
+ this.setState({isInputActive: false});
196
+
197
+ // Simulate Chrome's radio button behavior in all browsers: when the
198
+ // mouse goes down or up, the radio button should become focused.
199
+ // That way, the newly-selected answer becomes highlighted after click.
200
+ if (this.props.apiOptions.satStyling && this._input) {
201
+ this._input.focus();
202
+ }
203
+ };
204
+
205
+ onInputMouseOut: () => void = () => {
206
+ this.setState({isInputActive: false});
207
+ };
208
+
209
+ inputRef: ($FlowFixMe) => void = (ref) => {
210
+ this._input = ref;
211
+ };
212
+
213
+ renderOptionStatus: () => React.Node = () => {
214
+ const {correct, checked, crossedOut, reviewMode, previouslyAnswered} =
215
+ this.props;
216
+
217
+ // Option status is exclued for SAT
218
+ if (this.props.apiOptions.satStyling) {
219
+ return null;
220
+ }
221
+
222
+ // Option status is shown only in review mode, or for incorrectly
223
+ // answered items.
224
+ if (!reviewMode && !previouslyAnswered) {
225
+ return null;
226
+ }
227
+
228
+ return (
229
+ <OptionStatus
230
+ checked={checked}
231
+ correct={correct}
232
+ crossedOut={crossedOut}
233
+ previouslyAnswered={previouslyAnswered}
234
+ />
235
+ );
236
+ };
237
+
238
+ renderChoiceIcon: () => React.Node = () => {
239
+ const {radioStyleVersion, primaryProductColor} =
240
+ this.props.apiOptions.styling;
241
+ const finalStyles =
242
+ typeof radioStyleVersion === "undefined"
243
+ ? false
244
+ : radioStyleVersion === "final";
245
+
246
+ if (!finalStyles && !this.props.apiOptions.satStyling) {
247
+ return null;
248
+ }
249
+
250
+ return (
251
+ <ChoiceIcon
252
+ pos={this.props.pos}
253
+ correct={this.props.correct}
254
+ crossedOut={this.props.crossedOut}
255
+ pressed={this.state.isInputActive}
256
+ focused={this.state.isInputFocused}
257
+ checked={this.props.checked}
258
+ showCorrectness={this.props.showCorrectness}
259
+ reviewMode={this.props.reviewMode}
260
+ product={this.props.apiOptions.satStyling ? "sat" : "library"}
261
+ primaryProductColor={primaryProductColor}
262
+ previouslyAnswered={this.props.previouslyAnswered}
263
+ />
264
+ );
265
+ };
266
+
267
+ _setChecked: (boolean) => void = (willBeChecked) => {
268
+ if (willBeChecked) {
269
+ // If we're checking a crossed-out option, let's also uncross it.
270
+ this._sendChange({checked: true, crossedOut: false});
271
+ } else {
272
+ this._sendChange({checked: false});
273
+ }
274
+ };
275
+
276
+ // NOTE(mdr): This method expects to be auto-bound. If this component is
277
+ // converted to an ES6 class, take care to auto-bind this method!
278
+ _setCrossedOut: (boolean) => void = (willBeCrossedOut) => {
279
+ if (willBeCrossedOut) {
280
+ // If we're crossing out a checked option, let's also uncheck it.
281
+ this._sendChange({checked: false, crossedOut: true});
282
+ } else {
283
+ this._sendChange({crossedOut: false});
284
+ }
285
+ };
286
+
287
+ // Call `this.props.onChange` with the given values. Any keys that are not
288
+ // specified will be filled in with the current value. (For example, if
289
+ // `checked` is specified but `crossedOut` is not, then `crossedOut` will
290
+ // be filled in with `this.props.crossedOut`.)
291
+ //
292
+ // This enables us to use shorthand inside this component, while
293
+ // maintaining a consistent API for the parent.
294
+ _sendChange: ($FlowFixMe) => void = (newValues) => {
295
+ const checked =
296
+ newValues.checked != null ? newValues.checked : this.props.checked;
297
+
298
+ const crossedOut =
299
+ newValues.crossedOut != null
300
+ ? newValues.crossedOut
301
+ : this.props.crossedOut;
302
+
303
+ this.props.onChange({checked, crossedOut});
304
+ };
305
+
306
+ /**
307
+ * Public method. Focus the choice's <input> element.
308
+ */
309
+ focusInput: () => void = () => {
310
+ this._input?.focus();
311
+ };
312
+
313
+ render(): React.Node {
314
+ const sat = this.props.apiOptions.satStyling;
315
+ const isMobile = this.props.apiOptions.isMobile;
316
+
317
+ const {radioStyleVersion} = this.props.apiOptions.styling;
318
+ const finalStyles =
319
+ typeof radioStyleVersion === "undefined"
320
+ ? false
321
+ : radioStyleVersion === "final";
322
+
323
+ const className = classNames(
324
+ this.props.className,
325
+ "checkbox-label",
326
+ css(
327
+ styles.label,
328
+ isMobile && sharedStyles.disableTextSelection,
329
+ !sat && styles.responsiveLabel,
330
+ sat && styles.satLabel,
331
+ ),
332
+ );
333
+
334
+ const satRadioMenuContainer = classNames(
335
+ css(
336
+ this.state.isInputFocused && styles.satDescriptionInputFocused,
337
+ this.state.isInputActive && styles.satDescriptionInputActive,
338
+ ),
339
+ );
340
+
341
+ // If we're showing an answer to be incorrect, we render it as
342
+ // unchecked, regardless of the `checked` state we've stored.
343
+ //
344
+ // This is because the existence of a selected radio button makes it
345
+ // impossible to tab-navigate to any other choices (standard radio
346
+ // behavior), but the selected answer is now disabled and not tabbable,
347
+ // which means that non-mouse/non-touch users can't select another
348
+ // answer to try again!
349
+ //
350
+ // (This behavior is only necessary for type "radio", but we also apply
351
+ // it for type "checkbox", for consistency.)
352
+ const showingIncorrectness =
353
+ this.props.showCorrectness && !this.props.correct;
354
+ const inputIsChecked = this.props.checked && !showingIncorrectness;
355
+
356
+ // There's two different input components we could use (the builtin
357
+ // input component, or the ToggleableRadioButton component). These are
358
+ // the props that we will pass to either.
359
+ const commonInputProps = {
360
+ type: this.props.type,
361
+ name: this.props.groupName,
362
+ id: `${this.props.groupName}-choice-${this.props.pos}`,
363
+ checked: inputIsChecked,
364
+ disabled: this.props.disabled,
365
+ onFocus: this.onInputFocus,
366
+ onBlur: this.onInputBlur,
367
+ className: css(
368
+ // intermediate styles are not different for radio and
369
+ // checkbox, and have a separate active state.
370
+ !finalStyles && sharedStyles.perseusInteractive,
371
+ !finalStyles && styles.input,
372
+ !finalStyles && sharedStyles.responsiveInput,
373
+ !finalStyles && !sat && sharedStyles.responsiveRadioInput,
374
+ !finalStyles &&
375
+ !sat &&
376
+ this.state.isInputActive &&
377
+ sharedStyles.responsiveRadioInputActive,
378
+ finalStyles && sharedStyles.perseusSrOnly,
379
+ sat && sharedStyles.perseusSrOnly,
380
+ sat && this.props.reviewMode && styles.satReviewInput,
381
+ ),
382
+ };
383
+
384
+ let input = null;
385
+ if (this.props.type === "radio") {
386
+ // This is a special radio button that allows a user to deselect
387
+ // it by merely clicking/selecting it again.
388
+ input = (
389
+ <ToggleableRadioButton
390
+ onChecked={(willBeChecked) =>
391
+ this._setChecked(willBeChecked)
392
+ }
393
+ goToPrevChoice={this.props.goToPrevChoice}
394
+ goToNextChoice={this.props.goToNextChoice}
395
+ inputRef={this.inputRef}
396
+ {...commonInputProps}
397
+ />
398
+ );
399
+ } else {
400
+ input = (
401
+ <input
402
+ onChange={(event) => this._setChecked(event.target.checked)}
403
+ ref={this.inputRef}
404
+ {...commonInputProps}
405
+ />
406
+ );
407
+ }
408
+
409
+ const {reviewMode, correct, checked} = this.props;
410
+ // HACK: while most of the styling for rendering SAT items is handled
411
+ // via aphrodite, we also need to assign normal CSS classnames here to
412
+ // special-case the coloring of MathJax formulas (see .MathJax .math in
413
+ // stylesheets/task-package/tasks.less)
414
+ const satCorrectChoice = sat && reviewMode && correct;
415
+ const satIncorrectChecked = sat && reviewMode && !correct && checked;
416
+ const descriptionClassName = classNames(
417
+ "description",
418
+ satCorrectChoice && "sat-correct",
419
+ satIncorrectChecked && "sat-incorrect",
420
+ css(
421
+ !sat && styles.description,
422
+ sat && styles.satDescription,
423
+ satCorrectChoice && styles.satDescriptionCorrect,
424
+ satCorrectChoice &&
425
+ checked &&
426
+ styles.satDescriptionCorrectChecked,
427
+ satIncorrectChecked && styles.satDescriptionIncorrectChecked,
428
+ ),
429
+ );
430
+
431
+ const checkboxContentClassName = classNames(
432
+ "checkbox",
433
+ css(
434
+ sharedStyles.perseusInteractive,
435
+ !sat && styles.choiceIconWrapper,
436
+ sat && styles.satCheckboxOptionContent,
437
+ ),
438
+ );
439
+
440
+ const checkboxAndOptionClassName = classNames(
441
+ "checkbox-and-option",
442
+ css(
443
+ !sat && styles.intermediateResponsiveCheckbox,
444
+ !sat &&
445
+ reviewMode &&
446
+ styles.intermediateResponsiveCheckboxReview,
447
+ ),
448
+ );
449
+
450
+ const rationaleClassName = classNames(
451
+ "perseus-radio-rationale-content",
452
+ css(
453
+ styles.rationale,
454
+ !sat && styles.nonSatRationale,
455
+ sat && styles.satReviewRationale,
456
+ ),
457
+ );
458
+
459
+ // In edit mode, we must allow selection of the contentEditable
460
+ // element inside, therefore we cannot use a label, which makes
461
+ // selection of anything inside automatically select the input
462
+ // element instead
463
+ const LabelOrDiv = this.props.editMode ? "div" : "label";
464
+
465
+ // We only show cross-out in certain products, and
466
+ // only when you're still _answering_ the question. We also show this
467
+ // for the SAT product area.
468
+ const crossOutEnabled = this.props.apiOptions.crossOutEnabled;
469
+ const showCrossOutMenu = crossOutEnabled && !this.props.showCorrectness;
470
+
471
+ // We want to show the choices as dimmed out when the choices are
472
+ // disabled. However, we don't want to do this in the SAT product and
473
+ // we also don't want to do this when we're in review mode in the
474
+ // content library.
475
+ const showDimmed =
476
+ (!sat && !reviewMode && this.props.apiOptions.readOnly) ||
477
+ (showCrossOutMenu && this.props.crossedOut);
478
+
479
+ return (
480
+ <div className={sat ? satRadioMenuContainer : undefined}>
481
+ <CrossOutMenuWrapper
482
+ enabled={showCrossOutMenu}
483
+ pos={this.props.pos}
484
+ primaryProductColor={
485
+ this.props.apiOptions.styling.primaryProductColor
486
+ }
487
+ crossedOut={this.props.crossedOut}
488
+ onCrossedOutChange={this._setCrossedOut}
489
+ isSatProduct={sat}
490
+ >
491
+ <LabelOrDiv
492
+ htmlFor={
493
+ !this.props.editMode
494
+ ? commonInputProps.id
495
+ : undefined
496
+ }
497
+ className={className}
498
+ style={{opacity: showDimmed ? 0.5 : 1.0}}
499
+ >
500
+ <div
501
+ className={descriptionClassName}
502
+ onMouseDown={this.onInputMouseDown}
503
+ onMouseUp={this.onInputMouseUp}
504
+ onMouseOut={this.onInputMouseOut}
505
+ onTouchStart={this.onInputMouseDown}
506
+ onTouchEnd={this.onInputMouseUp}
507
+ >
508
+ <div className={checkboxAndOptionClassName}>
509
+ <span className={checkboxContentClassName}>
510
+ {input}
511
+ {this.renderChoiceIcon()}
512
+ </span>
513
+ {/* A pseudo-label. <label> is slightly broken on iOS,
514
+ so this works around that. Unfortunately, it is
515
+ simplest to just work around that everywhere. */}
516
+ <span
517
+ className={classNames(
518
+ ClassNames.RADIO.OPTION_CONTENT,
519
+ ClassNames.INTERACTIVE,
520
+ css(
521
+ sat && styles.satRadioOptionContent,
522
+ sat &&
523
+ reviewMode &&
524
+ styles.satReviewRadioOptionContent,
525
+ ),
526
+ )}
527
+ style={{cursor: "default"}}
528
+ >
529
+ <div
530
+ className={css(
531
+ styles.optionStatusContainer,
532
+ )}
533
+ >
534
+ {this.renderOptionStatus()}
535
+ </div>
536
+ <div>{this.props.content}</div>
537
+ </span>
538
+ </div>
539
+ {this.props.showRationale && (
540
+ <div
541
+ className={rationaleClassName}
542
+ data-test-id={`perseus-radio-rationale-content-${this.props.pos}`}
543
+ >
544
+ {this.props.rationale}
545
+ </div>
546
+ )}
547
+ </div>
548
+ </LabelOrDiv>
549
+ </CrossOutMenuWrapper>
550
+ </div>
551
+ );
552
+ }
553
+ }
554
+
555
+ const styles = StyleSheet.create({
556
+ description: {
557
+ display: "inline-block",
558
+ width: "100%",
559
+ },
560
+
561
+ satDescription: {
562
+ display: "block",
563
+ position: "relative",
564
+ boxSizing: "border-box",
565
+ cursor: "pointer",
566
+ marginLeft: 0,
567
+ padding: "17px 14px",
568
+ },
569
+
570
+ satDescriptionInputFocused: {
571
+ ...focusedStyleMixin,
572
+ display: "block",
573
+ },
574
+
575
+ satDescriptionInputActive: {
576
+ ...focusedStyleMixin,
577
+ backgroundColor: styleConstants.satActiveBackgroundColor,
578
+ },
579
+
580
+ satDescriptionCorrect: {
581
+ color: styleConstants.satCorrectColor,
582
+ },
583
+
584
+ satDescriptionCorrectChecked: {
585
+ backgroundColor: styleConstants.satCorrectBackgroundColor,
586
+ },
587
+
588
+ satDescriptionIncorrectChecked: {
589
+ color: styleConstants.satIncorrectColor,
590
+ backgroundColor: styleConstants.satIncorrectBackgroundColor,
591
+ },
592
+
593
+ input: {
594
+ display: "inline-block",
595
+ width: 20,
596
+ margin: 3,
597
+ marginLeft: -20,
598
+ marginRight: 0,
599
+ float: "none",
600
+ },
601
+
602
+ satReviewInput: {
603
+ pointerEvents: "none",
604
+ },
605
+
606
+ satRadioOptionContent: {
607
+ userSelect: "text",
608
+ display: "block",
609
+ marginLeft: 45,
610
+ // Overriding here, not sure why typically set
611
+ // to "cursor: default" in js
612
+ cursor: "inherit",
613
+ },
614
+
615
+ satReviewRadioOptionContent: {
616
+ fontWeight: "bold",
617
+ },
618
+
619
+ satCheckboxOptionContent: {
620
+ position: "absolute",
621
+ display: "block",
622
+ top: "50%",
623
+ margin: "-16px 0 0 0",
624
+ width: "auto",
625
+ },
626
+
627
+ choiceIconWrapper: {
628
+ display: "flex",
629
+ marginRight: 12,
630
+
631
+ // NOTE(mdr): Without this style, the bubbles shrink on iOS
632
+ // when answer text gets long.
633
+ flexShrink: 0,
634
+ },
635
+
636
+ optionStatusContainer: {
637
+ display: "block",
638
+ },
639
+
640
+ rationale: {
641
+ display: "block",
642
+ },
643
+
644
+ nonSatRationale: {
645
+ padding: intermediateCheckboxPadding,
646
+ paddingTop: 0,
647
+ marginLeft: 44,
648
+ [mediaQueries.smOrSmaller]: {
649
+ padding: intermediateCheckboxPaddingPhone,
650
+ paddingTop: 0,
651
+ },
652
+ },
653
+
654
+ satReviewRationale: {
655
+ marginTop: 13,
656
+ marginLeft: 45,
657
+ },
658
+
659
+ label: {
660
+ display: "block",
661
+ transition: "opacity 0.2s ease-out",
662
+ },
663
+
664
+ responsiveLabel: {
665
+ WebkitTapHighlightColor: "transparent",
666
+ display: "flex",
667
+
668
+ // Ensure that all items consume the full height of the choice.
669
+ // That way, the cross-out menu click target is full-height,
670
+ // which makes it easier to click!
671
+ alignItems: "stretch",
672
+ },
673
+
674
+ satLabel: {
675
+ cursor: "pointer",
676
+ },
677
+
678
+ intermediateResponsiveCheckbox: {
679
+ display: "flex",
680
+ alignItems: "center",
681
+
682
+ padding: intermediateCheckboxPadding,
683
+ [mediaQueries.smOrSmaller]: {
684
+ padding: intermediateCheckboxPaddingPhone,
685
+ },
686
+ },
687
+ intermediateResponsiveCheckboxReview: {
688
+ alignItems: "flex-start",
689
+ },
690
+ });
691
+
692
+ export default Choice;