@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,395 @@
1
+ // @noflow
2
+ /**
3
+ * A generic tooltip library for React.js
4
+ *
5
+ * This should eventually end up in react-components
6
+ *
7
+ * Interface: ({a, b} means one of a or b)
8
+ * import Tooltip from "./tooltip.jsx";
9
+ * <Tooltip
10
+ * className="class-for-tooltip-contents"
11
+ * horizontalPosition="left" // one of "left", "right"
12
+ * horizontalAlign="left" // one of "left", "right"
13
+ * verticalPosition="bottom" // one of "top", "bottom"
14
+ * arrowSize={10} // arrow size in pixels
15
+ * borderColor="#ccc" // color of the border for the tooltip
16
+ * show={true} // whether the tooltip should currently be visible
17
+ * targetContainerStyle={targetContainerStyle}
18
+ * >
19
+ * <TargetElementOfTheTooltip />
20
+ * <TooltipContents1 />
21
+ * <TooltipContents2 />
22
+ * </Tooltip>
23
+ *
24
+ * To show/hide the tooltip, the parent component should call the
25
+ * .show() and .hide() methods of the tooltip when appropriate.
26
+ * (These are usually set up as handlers of events on the target element.)
27
+ *
28
+ * Notes:
29
+ * className should not specify a border; that is handled by borderColor
30
+ * so that the arrow and tooltip match
31
+ */
32
+
33
+ // __,,--``\\
34
+ // _,,-''`` \\ ,
35
+ // '----------_.------'-.___|\__
36
+ // _.--''`` `)__ )__ @\__
37
+ // ( .. ''---/___,,E/__,E'------`
38
+ // `-''`''
39
+ // Here be dragons.
40
+
41
+ // TODO(joel/aria) fix z-index issues https://s3.amazonaws.com/uploads.hipchat.com/6574/29028/yOApjwmgiMhEZYJ/Screen%20Shot%202014-05-30%20at%203.34.18%20PM.png
42
+ // z-index: 3 on perseus-formats-tooltip seemed to work
43
+
44
+ import createReactClass from "create-react-class";
45
+ import PropTypes from "prop-types";
46
+ import * as React from "react";
47
+ import ReactDOM from "react-dom";
48
+
49
+ const zIndex = 10;
50
+
51
+ const Triangle = createReactClass({
52
+ propTypes: {
53
+ color: PropTypes.string.isRequired,
54
+ left: PropTypes.number.isRequired,
55
+ top: PropTypes.number.isRequired,
56
+ width: PropTypes.number.isRequired,
57
+ height: PropTypes.number.isRequired,
58
+ horizontalDirection: PropTypes.oneOf(["left", "right"]).isRequired,
59
+ verticalDirection: PropTypes.oneOf(["top", "bottom"]).isRequired,
60
+ },
61
+
62
+ render: function () {
63
+ let borderLeft;
64
+ let borderRight;
65
+ let borderTop;
66
+ let borderBottom;
67
+
68
+ const hBorder = `${this.props.width}px solid transparent`;
69
+ if (this.props.horizontalDirection === "right") {
70
+ borderLeft = hBorder;
71
+ } else {
72
+ borderRight = hBorder;
73
+ }
74
+
75
+ const vBorder = `${this.props.height}px solid ${this.props.color}`;
76
+ if (this.props.verticalDirection === "top") {
77
+ borderTop = vBorder;
78
+ } else {
79
+ borderBottom = vBorder;
80
+ }
81
+
82
+ return (
83
+ <div
84
+ style={{
85
+ display: "block",
86
+ height: 0,
87
+ width: 0,
88
+ position: "absolute",
89
+ left: this.props.left,
90
+ top: this.props["top"],
91
+ borderLeft: borderLeft,
92
+ borderRight: borderRight,
93
+ borderTop: borderTop,
94
+ borderBottom: borderBottom,
95
+ }}
96
+ />
97
+ );
98
+ },
99
+ });
100
+
101
+ const TooltipArrow = createReactClass({
102
+ propTypes: {
103
+ position: PropTypes.string,
104
+ visibility: PropTypes.string,
105
+ left: PropTypes.number,
106
+ top: PropTypes.number,
107
+ color: PropTypes.string.isRequired, // a css color
108
+ border: PropTypes.string.isRequired, // a css color
109
+ width: PropTypes.number.isRequired,
110
+ height: PropTypes.number.isRequired,
111
+ horizontalDirection: PropTypes.oneOf(["left", "right"]).isRequired,
112
+ verticalDirection: PropTypes.oneOf(["top", "bottom"]).isRequired,
113
+ },
114
+
115
+ getDefaultProps: function () {
116
+ return {
117
+ position: "relative",
118
+ visibility: "visible",
119
+ left: 0,
120
+ top: 0,
121
+ };
122
+ },
123
+
124
+ // TODO(aria): Think about adding a box-shadow to the triangle here
125
+ // See http://css-tricks.com/triangle-with-shadow/
126
+ render: function () {
127
+ //const isRight = (this.props.horizontalDirection === "right");
128
+ const isTop = this.props.verticalDirection === "top";
129
+
130
+ const frontTopOffset = isTop ? 0 : 1;
131
+ const borderTopOffset = isTop ? 0 : -1;
132
+
133
+ return (
134
+ <div
135
+ style={{
136
+ display: "block",
137
+ position: this.props.position,
138
+ visibility: this.props.visibility,
139
+ left: this.props.left,
140
+ top: this.props["top"],
141
+ width: this.props.width + 2,
142
+ height: this.props.height + 1,
143
+ marginTop: -1,
144
+ marginBottom: -2,
145
+ zIndex: zIndex,
146
+ }}
147
+ >
148
+ {/* The background triangle used to create the effect of a
149
+ border around the foreground triangle*/}
150
+ <Triangle
151
+ horizontalDirection={this.props.horizontalDirection}
152
+ verticalDirection={this.props.verticalDirection}
153
+ color={this.props.border}
154
+ left={0}
155
+ top={borderTopOffset}
156
+ width={this.props.width + 2} // one extra for the diagonal
157
+ height={this.props.height + 2}
158
+ />
159
+ {/* The foreground triangle covers all but the left/right edges
160
+ of the background triangle */}
161
+ <Triangle
162
+ horizontalDirection={this.props.horizontalDirection}
163
+ verticalDirection={this.props.verticalDirection}
164
+ color={this.props.color}
165
+ left={1}
166
+ top={frontTopOffset}
167
+ width={this.props.width}
168
+ height={this.props.height}
169
+ />
170
+ </div>
171
+ );
172
+ },
173
+ });
174
+
175
+ const VERTICAL_CORNERS = {
176
+ top: {
177
+ top: "-100%",
178
+ },
179
+ bottom: {
180
+ top: 0,
181
+ },
182
+ };
183
+
184
+ const HORIZONTAL_CORNERS = {
185
+ left: {
186
+ targetLeft: 0,
187
+ },
188
+
189
+ right: {
190
+ targetLeft: "100%",
191
+ },
192
+ };
193
+
194
+ const HORIZONTAL_ALIGNMNENTS = {
195
+ left: {
196
+ tooltipLeft: 0,
197
+ arrowLeft: (arrowSize) => 0,
198
+ },
199
+ right: {
200
+ tooltipLeft: "-100%",
201
+ arrowLeft: (arrowSize) => -arrowSize - 2,
202
+ },
203
+ };
204
+
205
+ // eslint-disable-next-line react/no-unsafe
206
+ const Tooltip = createReactClass({
207
+ propTypes: {
208
+ show: PropTypes.bool.isRequired,
209
+ className: PropTypes.string,
210
+ arrowSize: PropTypes.number,
211
+ borderColor: PropTypes.string,
212
+ verticalPosition: PropTypes.oneOf(Object.keys(VERTICAL_CORNERS)),
213
+ horizontalPosition: PropTypes.oneOf(Object.keys(HORIZONTAL_CORNERS)),
214
+ horizontalAlign: PropTypes.oneOf(Object.keys(HORIZONTAL_ALIGNMNENTS)),
215
+ children: PropTypes.arrayOf(PropTypes.element).isRequired,
216
+ targetContainerStyle: PropTypes.any, // style object
217
+ },
218
+
219
+ getDefaultProps: function () {
220
+ return {
221
+ className: "",
222
+ arrowSize: 10,
223
+ borderColor: "#ccc",
224
+ verticalPosition: "bottom",
225
+ horizontalPosition: "left",
226
+ horizontalAlign: "left",
227
+ targetContainerStyle: {},
228
+ };
229
+ },
230
+
231
+ getInitialState: function () {
232
+ return {
233
+ height: null, // used for offsetting "top" positioned tooltips
234
+ };
235
+ },
236
+
237
+ componentDidMount: function () {
238
+ this._updateHeight();
239
+ },
240
+
241
+ UNSAFE_componentWillReceiveProps: function () {
242
+ // If the contents have changed, reset our measure of the height
243
+ this.setState({height: null});
244
+ },
245
+
246
+ componentDidUpdate: function () {
247
+ this._updateHeight();
248
+ },
249
+
250
+ _renderToolTipDiv: function (isTooltipAbove) {
251
+ const settings = Object.assign(
252
+ {},
253
+ HORIZONTAL_CORNERS[this.props.horizontalPosition],
254
+ HORIZONTAL_ALIGNMNENTS[this.props.horizontalAlign],
255
+ VERTICAL_CORNERS[this.props.verticalPosition],
256
+ );
257
+
258
+ let arrowAbove;
259
+ let arrowBelow;
260
+
261
+ if (isTooltipAbove) {
262
+ // We put an absolutely positioned arrow in the correct place
263
+ arrowAbove = (
264
+ <TooltipArrow
265
+ verticalDirection="top"
266
+ horizontalDirection={this.props.horizontalAlign}
267
+ position="absolute"
268
+ color="white"
269
+ border={this.props.borderColor}
270
+ left={settings.arrowLeft(this.props.arrowSize)}
271
+ top={-this.props.arrowSize + 2}
272
+ width={this.props.arrowSize}
273
+ height={this.props.arrowSize}
274
+ zIndex={zIndex}
275
+ />
276
+ );
277
+
278
+ // And we use a visibility: hidden arrow below to shift up the
279
+ // content by the correct amount
280
+ arrowBelow = (
281
+ <TooltipArrow
282
+ verticalDirection="top"
283
+ horizontalDirection={this.props.horizontalAlign}
284
+ visibility="hidden"
285
+ color="white"
286
+ border={this.props.borderColor}
287
+ left={settings.arrowLeft(this.props.arrowSize)}
288
+ top={-1}
289
+ width={this.props.arrowSize}
290
+ height={this.props.arrowSize}
291
+ zIndex={zIndex}
292
+ />
293
+ );
294
+ } else {
295
+ arrowAbove = (
296
+ <TooltipArrow
297
+ verticalDirection="bottom"
298
+ horizontalDirection={this.props.horizontalAlign}
299
+ color="white"
300
+ border={this.props.borderColor}
301
+ left={settings.arrowLeft(this.props.arrowSize)}
302
+ top={-1}
303
+ width={this.props.arrowSize}
304
+ height={this.props.arrowSize}
305
+ zIndex={zIndex}
306
+ />
307
+ );
308
+
309
+ arrowBelow = null;
310
+ }
311
+
312
+ /* A positioned div below the input to be the parent for our
313
+ tooltip */
314
+ return (
315
+ <div
316
+ style={{
317
+ position: "relative",
318
+ height: 0,
319
+ display: this.props.show ? "block" : "none",
320
+ }}
321
+ >
322
+ <div
323
+ // eslint-disable-next-line react/no-string-refs
324
+ ref="tooltipContainer"
325
+ className="tooltipContainer"
326
+ style={{
327
+ position: "absolute",
328
+ // height must start out undefined, not null, so that
329
+ // we can measure the actual height with jquery.
330
+ // This is used to position the tooltip with top: -100%
331
+ // when in verticalPosition: "top" mode
332
+ height: this.state.height || undefined,
333
+ left: settings.targetLeft,
334
+ }}
335
+ >
336
+ {arrowAbove}
337
+
338
+ {/* The contents of the tooltip */}
339
+ <div
340
+ className={this.props.className}
341
+ // eslint-disable-next-line react/no-string-refs
342
+ ref="tooltipContent"
343
+ style={{
344
+ position: "relative",
345
+ top: settings["top"],
346
+ left: settings.tooltipLeft,
347
+ border: "1px solid " + this.props.borderColor,
348
+ WebkitBoxShadow:
349
+ "0 1px 3px " + this.props.borderColor,
350
+ MozBoxShadow: "0 1px 3px " + this.props.borderColor,
351
+ boxShadow: "0 1px 3px " + this.props.borderColor,
352
+ zIndex: zIndex - 1,
353
+ }}
354
+ >
355
+ {this.props.children.slice(1)}
356
+ </div>
357
+
358
+ {arrowBelow}
359
+ </div>
360
+ </div>
361
+ );
362
+ },
363
+
364
+ _updateHeight: function () {
365
+ const height = ReactDOM.findDOMNode(
366
+ // eslint-disable-next-line react/no-string-refs
367
+ this.refs.tooltipContainer,
368
+ ).offsetHeight;
369
+ if (height !== this.state.height) {
370
+ this.setState({height});
371
+ }
372
+ },
373
+
374
+ render: function () {
375
+ const isTooltipAbove = this.props.verticalPosition === "top";
376
+
377
+ /* We wrap the entire output in a span so that it displays inline */
378
+ return (
379
+ <span>
380
+ {isTooltipAbove && this._renderToolTipDiv(isTooltipAbove)}
381
+
382
+ {/* We wrap our input in a div so that we can put the tooltip in a
383
+ div above/below it */}
384
+ <div style={this.props.targetContainerStyle}>
385
+ {this.props.children[0]}
386
+ </div>
387
+
388
+ {!isTooltipAbove && this._renderToolTipDiv()}
389
+ </span>
390
+ );
391
+ },
392
+ });
393
+
394
+ // Sorry. // Apology-Oriented-Programming
395
+ export default Tooltip;
@@ -0,0 +1,140 @@
1
+ // @flow
2
+ import * as React from "react";
3
+
4
+ import VisibilityObserver from "../visibility-observer.jsx";
5
+
6
+ import type {ScrollVisibility} from "../visibility-observer.jsx";
7
+
8
+ type StoryArgs = {||};
9
+
10
+ type Story = {|
11
+ title: string,
12
+ |};
13
+
14
+ export default ({
15
+ title: "Perseus/Components/Visibility Observer",
16
+ }: Story);
17
+
18
+ type Props = {||};
19
+ type State = {|
20
+ sectionVisibility: ScrollVisibility,
21
+ |};
22
+
23
+ class VisibilityTest extends React.Component<Props, State> {
24
+ state = {
25
+ sectionVisibility: "unknown",
26
+ };
27
+
28
+ handleVisibilityChanged = (visibility: ScrollVisibility) => {
29
+ this.setState({sectionVisibility: visibility});
30
+ };
31
+
32
+ render(): React.Node {
33
+ const {sectionVisibility} = this.state;
34
+
35
+ const visibleText =
36
+ sectionVisibility === "visible" ? "Visible" : "Not Visible";
37
+
38
+ return (
39
+ <div>
40
+ <p>Visibility: {visibleText}</p>
41
+
42
+ <p>
43
+ {sectionVisibility === "hidden-above"
44
+ ? "The Section IS UP HERE!!!"
45
+ : "---"}
46
+ </p>
47
+
48
+ <div
49
+ style={{
50
+ height: 150,
51
+ width: 800,
52
+ overflow: "scroll",
53
+ backgroundColor: "white",
54
+ scrollbars: "visible",
55
+ }}
56
+ >
57
+ <p>
58
+ Ocicat american shorthair. Egyptian mau himalayan or
59
+ grimalkin maine coon cheetah lion but himalayan.
60
+ American bobtail lion kitten. Kitty bombay puma.
61
+ Savannah scottish fold for tabby but sphynx for bombay
62
+ kitten ragdoll. Puma russian blue yet singapura birman
63
+ and kitty russian blue. Egyptian mau cheetah, donskoy
64
+ for himalayan singapura abyssinian . Singapura
65
+ himalayan. Malkin abyssinian but balinese but bobcat.
66
+ Leopard puma, norwegian forest mouser and turkish
67
+ angora. Lynx bengal tiger. American shorthair. Donskoy
68
+ ragdoll. Siberian turkish angora cougar and russian
69
+ blue. Burmese savannah and siamese norwegian forest yet
70
+ cheetah himalayan lion. Ocelot kitten or munchkin but
71
+ kitten savannah. Cougar manx or russian blue american
72
+ shorthair so jaguar but havana brown. Cheetah havana
73
+ brown manx. Tiger thai american bobtail. Ragdoll bengal
74
+ yet kitten, bengal grimalkin scottish fold. Abyssinian
75
+ jaguar. Turkish angora bengal but kitty sphynx.{" "}
76
+ </p>
77
+
78
+ <VisibilityObserver
79
+ scrollParentIsNotBody={true}
80
+ onChange={this.handleVisibilityChanged}
81
+ >
82
+ <p
83
+ style={{
84
+ backgroundColor: "#eeddee",
85
+ fontWeight: "bold",
86
+ }}
87
+ >
88
+ American shorthair devonshire rex but cheetah and
89
+ ragdoll but bombay. Kitty. Tomcat cornish rex
90
+ donskoy but tom donskoy havana brown. Jaguar sphynx.
91
+ Cheetah kitty for kitty, but balinese . British
92
+ shorthair cheetah devonshire rex birman panther.
93
+ Kitten tabby but siamese, sphynx. Bombay maine coon
94
+ so lynx and donskoy kitten, but cougar. Sphynx
95
+ sphynx, so american bobtail or mouser or puma.
96
+ Mouser munchkin and ragdoll yet mouser cheetah
97
+ balinese . Egyptian mau siamese mouser, cougar
98
+ ragdoll. Persian birman tabby. Norwegian forest
99
+ norwegian forest cornish rex bengal. Panther bombay.
100
+ Kitty siamese ragdoll and egyptian mau. Tabby.
101
+ Cheetah havana brown, kitty for havana brown.
102
+ Cornish rex siberian russian blue so persian but
103
+ thai burmese. Maine coon puma. Lynx.
104
+ </p>
105
+ </VisibilityObserver>
106
+
107
+ <p>
108
+ Jaguar tom but himalayan birman. Turkish angora balinese
109
+ grimalkin but birman or kitty or munchkin. American
110
+ shorthair bobcat. Donskoy cornish rex malkin. Ocicat
111
+ tabby or siamese, birman. Devonshire rex tomcat yet thai
112
+ jaguar mouser and donskoy, lynx. Grimalkin bombay.{" "}
113
+ </p>
114
+ <p>
115
+ Eat grass, throw it back up mice and cats are fats i
116
+ like to pets them they like to meow back, cough furball
117
+ into food bowl then scratch owner for a new one purr as
118
+ loud as possible, be the most annoying cat that you can,
119
+ and, knock everything off the table, lick the other
120
+ cats. Furrier and even more furrier hairball cat is
121
+ love, cat is life so ooh, are those your $250 dollar
122
+ sandals? lemme use that as my litter box. Lick arm hair
123
+ love to play with owner's hair tie hunt anything that
124
+ moves. Destroy couch as revenge when in doubt, wash.
125
+ </p>
126
+ </div>
127
+
128
+ <p>
129
+ {sectionVisibility === "hidden-below"
130
+ ? "The Section IS DOWN HERE!!!"
131
+ : "---"}
132
+ </p>
133
+ </div>
134
+ );
135
+ }
136
+ }
137
+
138
+ export const AlertWhenNotVisible = (args: StoryArgs): React.Node => {
139
+ return <VisibilityTest />;
140
+ };
@@ -0,0 +1,45 @@
1
+ // @flow
2
+ // Utility for finding the scroll parent of a chile
3
+ // Taken and updated to our standards from
4
+ // https://github.com/olahol/scrollparent.js
5
+
6
+ const getParents = function (node: Node, parents): $ReadOnlyArray<Node> {
7
+ if (!node.parentNode) {
8
+ return parents;
9
+ }
10
+
11
+ return getParents(node.parentNode, parents.concat([node]));
12
+ };
13
+
14
+ const style = function (node: Element, prop) {
15
+ return getComputedStyle(node).getPropertyValue(prop);
16
+ };
17
+
18
+ const overflow = function (node: Element) {
19
+ return (
20
+ style(node, "overflow") +
21
+ style(node, "overflow-y") +
22
+ style(node, "overflow-x")
23
+ );
24
+ };
25
+
26
+ const scroll = function (node: Element) {
27
+ return /(auto|scroll)/.test(overflow(node));
28
+ };
29
+
30
+ export const getScrollParent = function (node: Element): ?Element {
31
+ if (!(node instanceof HTMLElement)) {
32
+ return;
33
+ }
34
+
35
+ const parents = node.parentNode ? getParents(node.parentNode, []) : [];
36
+
37
+ for (let i = 0; i < parents.length; i += 1) {
38
+ const element = ((parents[i]: any): Element);
39
+ if (scroll(element)) {
40
+ return element;
41
+ }
42
+ }
43
+
44
+ return document.documentElement;
45
+ };
@@ -0,0 +1,109 @@
1
+ // @flow
2
+ /**
3
+ * VisibilityObserver watches a target element for changes to visibility,
4
+ * relative to a given root node.
5
+ *
6
+ * When the target element entirely disappears, or partially reappears, the
7
+ * caller is notified via the `onVisibilityChange` callback.
8
+ *
9
+ * Potential visibility changes include:
10
+ * - Being scrolled out of view, by any scroll container between the target
11
+ * and the root.
12
+ * - Style changes to the target element or an ancestor, like `display: none`.
13
+ *
14
+ * The VisibilityObserver relies on the IntersectionObserver, which does not
15
+ * exist on Safari and is buggy on Edge. Instead, we will polyfill the
16
+ * IntersectionObserver on those platforms.
17
+ *
18
+ * TODO(mdr): Currently, we emulate the API of IntersectionObserver. It might
19
+ * be nicer to instead use a more React-y API: let `targetElement` be a
20
+ * required prop, and render the element only when it's present. Then, we
21
+ * can use React lifecycle methods to set-up/tear-down handlers. Not sure
22
+ * that's _actually_ a net win, though, in complexity or performance - but
23
+ * worth thinking about if this API is causing too much entanglement!
24
+ */
25
+ // NOTE(jeresig): As of 2021-03-02 there are still a non-trivial number of
26
+ // pre-Safari 12.1 users and they need a polyfill for IntersectionObserver
27
+ // eslint-disable-next-line import/no-unassigned-import
28
+ import "intersection-observer";
29
+
30
+ export interface VisibilityObserver {
31
+ // Set the element whose visibility we're observing.
32
+ setTargetElement(targetElement: HTMLElement): void;
33
+
34
+ // Stop observing the previously-set target element, if any.
35
+ //
36
+ // When the target element unmounts, please call this method to clean up!
37
+ disconnect(): void;
38
+ }
39
+
40
+ type VisibilityChangeCallback = (
41
+ isVisible: boolean,
42
+ rootBounds: DOMRectReadOnly,
43
+ ) => void;
44
+
45
+ /**
46
+ * Create a new VisibilityObserver.
47
+ */
48
+ export function createVisibilityObserver(
49
+ rootElement: ?HTMLElement,
50
+ onVisibilityChange: VisibilityChangeCallback,
51
+ rootMargin?: string,
52
+ ): VisibilityObserver {
53
+ return new NativeVisibilityObserver(
54
+ rootElement,
55
+ onVisibilityChange,
56
+ rootMargin,
57
+ );
58
+ }
59
+
60
+ /**
61
+ * A VisibilityObserver that uses IntersectionObserver to leverage the
62
+ * browser's native ability to determine how much of an element is visible.
63
+ * https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
64
+ */
65
+ class NativeVisibilityObserver implements VisibilityObserver {
66
+ _intersectionObserver: IntersectionObserver;
67
+ _onVisibilityChange: VisibilityChangeCallback;
68
+
69
+ constructor(
70
+ rootElement: ?HTMLElement,
71
+ onVisibilityChange: VisibilityChangeCallback,
72
+ rootMargin?: string,
73
+ ) {
74
+ // Create an IntersectionObserver with a threshold of "epsilon", the
75
+ // smallest positive non-zero number. This means that, whenever the
76
+ // target element enters or exits total invisibility, the handler will
77
+ // be called.
78
+ this._intersectionObserver = new IntersectionObserver(
79
+ this._handleIntersection,
80
+ {
81
+ root: rootElement,
82
+ threshold: Number.EPSILON,
83
+ rootMargin,
84
+ },
85
+ );
86
+ this._onVisibilityChange = onVisibilityChange;
87
+ }
88
+
89
+ _handleIntersection = (
90
+ entries: $ReadOnlyArray<IntersectionObserverEntry>,
91
+ ) => {
92
+ // There should be exactly one entry, but let's be defensive and loop.
93
+ //
94
+ for (const entry of entries) {
95
+ this._onVisibilityChange(entry.isIntersecting, entry.rootBounds);
96
+ }
97
+ };
98
+
99
+ setTargetElement(targetElement: HTMLElement) {
100
+ // Because this class only watches one element at a time, we disconnect
101
+ // from all other observed elements first, just in case.
102
+ this._intersectionObserver.disconnect();
103
+ this._intersectionObserver.observe(targetElement);
104
+ }
105
+
106
+ disconnect() {
107
+ this._intersectionObserver.disconnect();
108
+ }
109
+ }