@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.
- package/.babelrc.js +1 -1
- package/CHANGELOG.md +29 -0
- package/dist/es/index.css +4361 -0
- package/dist/es/index.js +7 -1
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +4361 -0
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/strings.js +148 -46
- package/package.json +50 -4
- package/src/README.md +35 -0
- package/src/__stories__/item-renderer.stories.jsx +27 -0
- package/src/__stories__/server-item-renderer.stories.jsx +27 -0
- package/src/__testdata__/item-renderer_testdata.js +161 -0
- package/src/__testdata__/renderer_testdata.js +123 -0
- package/src/__tests__/__snapshots__/item-renderer_test.jsx.snap +233 -0
- package/src/__tests__/__snapshots__/perseus-markdown_test.jsx.snap +516 -0
- package/src/__tests__/__snapshots__/pure-markdown_test.js.snap +113 -0
- package/src/__tests__/__snapshots__/renderer_test.jsx.snap +576 -0
- package/src/__tests__/__snapshots__/server-item-renderer_test.jsx.snap +306 -0
- package/src/__tests__/a11y_test.jsx +304 -0
- package/src/__tests__/item-renderer_test.jsx +892 -0
- package/src/__tests__/mock-asset-loading-widget.jsx +57 -0
- package/src/__tests__/mock-widget.jsx +35 -0
- package/src/__tests__/perseus-markdown_test.jsx +747 -0
- package/src/__tests__/pure-markdown_test.js +552 -0
- package/src/__tests__/renderability_test.jsx +481 -0
- package/src/__tests__/renderer-api_test.jsx +333 -0
- package/src/__tests__/renderer_test.jsx +1792 -0
- package/src/__tests__/server-item-renderer_test.jsx +516 -0
- package/src/__tests__/test-items/image-item.js +25 -0
- package/src/__tests__/test-items/input-number-1-item.js +27 -0
- package/src/__tests__/test-items/input-number-2-item.js +39 -0
- package/src/__tests__/test-items/table-item.js +30 -0
- package/src/__tests__/tex-wrangler_test.js +83 -0
- package/src/__tests__/util_test.js +41 -0
- package/src/a11y.js +46 -0
- package/src/all-widgets.js +9 -0
- package/src/article-renderer.jsx +215 -0
- package/src/asset-context.js +30 -0
- package/src/basic-widgets.js +13 -0
- package/src/components/__stories__/button-group.stories.jsx +61 -0
- package/src/components/__stories__/fixed-to-responsive.stories.jsx +132 -0
- package/src/components/__stories__/graph.stories.jsx +27 -0
- package/src/components/__stories__/graphie.stories.jsx +28 -0
- package/src/components/__stories__/hud.stories.jsx +36 -0
- package/src/components/__stories__/icon.stories.jsx +56 -0
- package/src/components/__stories__/image-loader.stories.jsx +60 -0
- package/src/components/__stories__/info-tip.stories.jsx +33 -0
- package/src/components/__stories__/inline-icon.stories.jsx +39 -0
- package/src/components/__stories__/input-with-examples.stories.jsx +51 -0
- package/src/components/__stories__/lint.stories.jsx +40 -0
- package/src/components/__stories__/math-input.stories.jsx +29 -0
- package/src/components/__stories__/math-output.stories.jsx +24 -0
- package/src/components/__stories__/multi-button-group.stories.jsx +69 -0
- package/src/components/__stories__/number-input.stories.jsx +42 -0
- package/src/components/__stories__/possible-answers.stories.jsx +22 -0
- package/src/components/__stories__/prop-check-box.stories.jsx +62 -0
- package/src/components/__stories__/range-input.stories.jsx +28 -0
- package/src/components/__stories__/simple-keypad-input.stories.jsx +28 -0
- package/src/components/__stories__/sortable.stories.jsx +73 -0
- package/src/components/__stories__/stub-tag-editor.stories.jsx +44 -0
- package/src/components/__stories__/svg-image.stories.jsx +60 -0
- package/src/components/__stories__/tex.stories.jsx +18 -0
- package/src/components/__stories__/text-buttons.stories.jsx +51 -0
- package/src/components/__stories__/text-input.stories.jsx +34 -0
- package/src/components/__stories__/text-list-editor.stories.jsx +33 -0
- package/src/components/__stories__/zoomable-tex.stories.jsx +39 -0
- package/src/components/__stories__/zoomable.stories.jsx +40 -0
- package/src/components/__testdata__/sorter_testdata.js +20 -0
- package/src/components/__tests__/__snapshots__/sortable_test.jsx.snap +79 -0
- package/src/components/__tests__/__snapshots__/sorter_test.jsx.snap +297 -0
- package/src/components/__tests__/number-input_test.jsx +112 -0
- package/src/components/__tests__/sortable_test.jsx +234 -0
- package/src/components/__tests__/sorter_test.jsx +111 -0
- package/src/components/__tests__/zoomable_test.jsx +388 -0
- package/src/components/button-group.jsx +131 -0
- package/src/components/constants.js +15 -0
- package/src/components/fixed-to-responsive.jsx +164 -0
- package/src/components/graph.jsx +415 -0
- package/src/components/graphie-classes.jsx +148 -0
- package/src/components/graphie-movables.jsx +342 -0
- package/src/components/graphie.jsx +353 -0
- package/src/components/highlighting/highlightable-content.jsx +245 -0
- package/src/components/highlighting/highlights.js +208 -0
- package/src/components/highlighting/ranges.js +299 -0
- package/src/components/highlighting/types.js +73 -0
- package/src/components/highlighting/ui/highlight-renderer.jsx +179 -0
- package/src/components/highlighting/ui/highlight-set-renderer.jsx +190 -0
- package/src/components/highlighting/ui/highlight-tooltip.jsx +153 -0
- package/src/components/highlighting/ui/highlighting-ui.jsx +163 -0
- package/src/components/highlighting/ui/selection-tracker.jsx +195 -0
- package/src/components/highlighting/ui/types.js +21 -0
- package/src/components/highlighting/ui/util.js +166 -0
- package/src/components/highlighting/word-indexer.jsx +155 -0
- package/src/components/hud.jsx +179 -0
- package/src/components/icon.jsx +149 -0
- package/src/components/image-loader.jsx +175 -0
- package/src/components/info-tip/info-tip.jsx +146 -0
- package/src/components/info-tip.jsx +38 -0
- package/src/components/inline-icon.jsx +81 -0
- package/src/components/input-with-examples.jsx +231 -0
- package/src/components/lint.jsx +454 -0
- package/src/components/math-input.jsx +282 -0
- package/src/components/math-output.jsx +128 -0
- package/src/components/multi-button-group.jsx +137 -0
- package/src/components/new-tooltip/README.md +110 -0
- package/src/components/new-tooltip/__stories__/flexible-position.stories.jsx +437 -0
- package/src/components/new-tooltip/__stories__/new-tooltip.stories.jsx +665 -0
- package/src/components/new-tooltip/__stories__/tooltip-arrow.stories.jsx +70 -0
- package/src/components/new-tooltip/__stories__/tooltip-bubble.stories.jsx +98 -0
- package/src/components/new-tooltip/__tests__/style-constants_test.js +8 -0
- package/src/components/new-tooltip/__tests__/util_test.js +47 -0
- package/src/components/new-tooltip/flexible-position.jsx +182 -0
- package/src/components/new-tooltip/new-tooltip.jsx +399 -0
- package/src/components/new-tooltip/style-constants.js +13 -0
- package/src/components/new-tooltip/tooltip-arrow.jsx +184 -0
- package/src/components/new-tooltip/tooltip-bubble.jsx +223 -0
- package/src/components/new-tooltip/tooltip-portal.jsx +101 -0
- package/src/components/new-tooltip/tooltip-positioner.jsx +529 -0
- package/src/components/new-tooltip/types.js +183 -0
- package/src/components/new-tooltip/util.js +27 -0
- package/src/components/number-input.jsx +253 -0
- package/src/components/possible-answers.jsx +36 -0
- package/src/components/prop-check-box.jsx +80 -0
- package/src/components/range-input.jsx +61 -0
- package/src/components/simple-keypad-input.jsx +91 -0
- package/src/components/sortable.jsx +873 -0
- package/src/components/stub-tag-editor.jsx +54 -0
- package/src/components/svg-image.jsx +762 -0
- package/src/components/tex-buttons.jsx +322 -0
- package/src/components/tex.jsx +70 -0
- package/src/components/text-input.jsx +82 -0
- package/src/components/text-list-editor.jsx +157 -0
- package/src/components/tooltip.jsx +395 -0
- package/src/components/visibility-observer/__stories__/visibility-observer.stories.jsx +140 -0
- package/src/components/visibility-observer/get-scroll-parent.js +45 -0
- package/src/components/visibility-observer/visibility-observer.js +109 -0
- package/src/components/visibility-observer/visibility-observer.jsx +123 -0
- package/src/components/zoomable-tex.jsx +70 -0
- package/src/components/zoomable.jsx +304 -0
- package/src/components.js +17 -0
- package/src/dependencies.js +22 -0
- package/src/error-boundary.jsx +48 -0
- package/src/extra-widgets.js +75 -0
- package/src/gorgon/README +40 -0
- package/src/gorgon/__tests__/matcher_test.js +497 -0
- package/src/gorgon/__tests__/rule_test.js +101 -0
- package/src/gorgon/__tests__/rules_test.js +487 -0
- package/src/gorgon/__tests__/selector-parser_test.js +52 -0
- package/src/gorgon/__tests__/tree-transformer_test.js +446 -0
- package/src/gorgon/gorgon.js +290 -0
- package/src/gorgon/proptypes.js +29 -0
- package/src/gorgon/rule.js +413 -0
- package/src/gorgon/rules/absolute-url.js +24 -0
- package/src/gorgon/rules/all-rules.js +72 -0
- package/src/gorgon/rules/blockquoted-math.js +10 -0
- package/src/gorgon/rules/blockquoted-widget.js +10 -0
- package/src/gorgon/rules/double-spacing-after-terminal.js +12 -0
- package/src/gorgon/rules/extra-content-spacing.js +12 -0
- package/src/gorgon/rules/heading-level-1.js +14 -0
- package/src/gorgon/rules/heading-level-skip.js +20 -0
- package/src/gorgon/rules/heading-sentence-case.js +11 -0
- package/src/gorgon/rules/heading-title-case.js +63 -0
- package/src/gorgon/rules/image-alt-text.js +21 -0
- package/src/gorgon/rules/image-in-table.js +10 -0
- package/src/gorgon/rules/image-spaces-around-urls.js +35 -0
- package/src/gorgon/rules/image-widget.js +50 -0
- package/src/gorgon/rules/link-click-here.js +11 -0
- package/src/gorgon/rules/lint-utils.js +48 -0
- package/src/gorgon/rules/long-paragraph.js +14 -0
- package/src/gorgon/rules/math-adjacent.js +10 -0
- package/src/gorgon/rules/math-align-extra-break.js +11 -0
- package/src/gorgon/rules/math-align-linebreaks.js +43 -0
- package/src/gorgon/rules/math-empty.js +10 -0
- package/src/gorgon/rules/math-font-size.js +12 -0
- package/src/gorgon/rules/math-frac.js +10 -0
- package/src/gorgon/rules/math-nested.js +11 -0
- package/src/gorgon/rules/math-starts-with-space.js +12 -0
- package/src/gorgon/rules/math-text-empty.js +10 -0
- package/src/gorgon/rules/math-without-dollars.js +14 -0
- package/src/gorgon/rules/nested-lists.js +11 -0
- package/src/gorgon/rules/profanity.js +10 -0
- package/src/gorgon/rules/table-missing-cells.js +20 -0
- package/src/gorgon/rules/unbalanced-code-delimiters.js +14 -0
- package/src/gorgon/rules/unescaped-dollar.js +10 -0
- package/src/gorgon/rules/widget-in-table.js +10 -0
- package/src/gorgon/selector.js +506 -0
- package/src/gorgon/tree-transformer.js +588 -0
- package/src/hint-renderer.jsx +159 -0
- package/src/hints-renderer.jsx +359 -0
- package/src/icon-paths.js +120 -0
- package/src/index.js +149 -2
- package/src/init.js +49 -0
- package/src/interaction-tracker.js +60 -0
- package/src/interactive2/__tests__/movable-mock.js +48 -0
- package/src/interactive2/__tests__/movable-point_test.js +146 -0
- package/src/interactive2/interactive-util.js +152 -0
- package/src/interactive2/movable-helper-methods.js +160 -0
- package/src/interactive2/movable-line-options.js +392 -0
- package/src/interactive2/movable-line.js +322 -0
- package/src/interactive2/movable-point-options.js +190 -0
- package/src/interactive2/movable-point.jsx +603 -0
- package/src/interactive2/movable-polygon-options.js +351 -0
- package/src/interactive2/movable-polygon.js +368 -0
- package/src/interactive2/movable.js +273 -0
- package/src/interactive2/objective_.js +79 -0
- package/src/interactive2/types.js +3 -0
- package/src/interactive2/wrapped-defaults.js +86 -0
- package/src/interactive2/wrapped-ellipse.js +87 -0
- package/src/interactive2/wrapped-line.js +109 -0
- package/src/interactive2/wrapped-path.js +47 -0
- package/src/interactive2.js +91 -0
- package/src/item-renderer.jsx +518 -0
- package/src/item-version.js +12 -0
- package/src/jipt-paragraphs.js +44 -0
- package/src/jquery.mobile.vmouse.js +545 -0
- package/src/keypad-context.js +33 -0
- package/src/loading-context.js +20 -0
- package/src/logging/log.js +107 -0
- package/src/logging/log_test.js +41 -0
- package/src/mixins/changeable.jsx +115 -0
- package/src/mixins/editor-jsonify.jsx +13 -0
- package/src/mixins/provide-keypad.jsx +129 -0
- package/src/mixins/widget-jsonify-deprecated.jsx +23 -0
- package/src/mixins/widget-prop-blacklist.js +36 -0
- package/src/multi-items/__stories__/multi-renderer.stories.js +88 -0
- package/src/multi-items/__testdata__/multi-renderer_testdata.js +174 -0
- package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +994 -0
- package/src/multi-items/__tests__/items_test.js +245 -0
- package/src/multi-items/__tests__/multi-renderer_test.js +903 -0
- package/src/multi-items/__tests__/prop-type-builders_test.js +134 -0
- package/src/multi-items/__tests__/shapes_test.js +44 -0
- package/src/multi-items/__tests__/trees_test.js +316 -0
- package/src/multi-items/item-types.js +55 -0
- package/src/multi-items/items.js +176 -0
- package/src/multi-items/multi-renderer.jsx +543 -0
- package/src/multi-items/prop-type-builders.js +105 -0
- package/src/multi-items/shape-types.js +47 -0
- package/src/multi-items/shapes.js +53 -0
- package/src/multi-items/tree-types.js +42 -0
- package/src/multi-items/trees.js +345 -0
- package/src/multi-items.js +76 -0
- package/src/not-gorgon.js +129 -0
- package/src/perseus-api.jsx +270 -0
- package/src/perseus-env.js +26 -0
- package/src/perseus-error.js +19 -0
- package/src/perseus-markdown.jsx +433 -0
- package/src/perseus-types.js +1597 -0
- package/src/pure-markdown.js +347 -0
- package/src/question-paragraph.jsx +30 -0
- package/src/renderability.jsx +108 -0
- package/src/renderer.jsx +1932 -0
- package/src/server-item-renderer.jsx +467 -0
- package/src/sigfigs.jsx +492 -0
- package/src/styles/articles.less +748 -0
- package/src/styles/constants.js +119 -0
- package/src/styles/global-constants.js +350 -0
- package/src/styles/global-styles.js +307 -0
- package/src/styles/khan-exercise.css +1304 -0
- package/src/styles/media-queries.js +48 -0
- package/src/styles/perseus-renderer.less +1007 -0
- package/src/styles/shared.js +132 -0
- package/src/styles/util.less +47 -0
- package/src/styles/variables.boostrap.less +301 -0
- package/src/styles/variables.less +68 -0
- package/src/styles/variables.shared.less +356 -0
- package/src/styles/widgets/categorizer.less +39 -0
- package/src/styles/widgets/definition.less +86 -0
- package/src/styles/widgets/dropdown.less +3 -0
- package/src/styles/widgets/expression.less +147 -0
- package/src/styles/widgets/graded-group.less +36 -0
- package/src/styles/widgets/grapher.less +11 -0
- package/src/styles/widgets/group.less +19 -0
- package/src/styles/widgets/image.less +29 -0
- package/src/styles/widgets/interactive-graph.less +61 -0
- package/src/styles/widgets/label-image.less +41 -0
- package/src/styles/widgets/matcher.less +8 -0
- package/src/styles/widgets/matrix.less +120 -0
- package/src/styles/widgets/measurer.less +20 -0
- package/src/styles/widgets/orderer.less +97 -0
- package/src/styles/widgets/passage.less +151 -0
- package/src/styles/widgets/plotter.less +33 -0
- package/src/styles/widgets/radio.less +52 -0
- package/src/styles/widgets/reaction-diagram.less +35 -0
- package/src/styles/widgets/sequence.less +5 -0
- package/src/styles/widgets/sortable.less +127 -0
- package/src/styles/widgets/table.less +56 -0
- package/src/styles/widgets/transformer.less +28 -0
- package/src/styles/widgets/unit.less +14 -0
- package/src/tex-wrangler.js +176 -0
- package/src/traversal.jsx +141 -0
- package/src/types.js +538 -0
- package/src/util/a11y.js +24 -0
- package/src/util/answer-types.js +862 -0
- package/src/util/answer-types_test.js +115 -0
- package/src/util/colors.js +94 -0
- package/src/util/fix-passage-refs.jsx +122 -0
- package/src/util/graph-utils.js +16 -0
- package/src/util/graphie.js +1586 -0
- package/src/util/interactive.js +4078 -0
- package/src/util/katex-preprocess.js +16 -0
- package/src/util/math.js +296 -0
- package/src/util/mobile-native-utils.js +14 -0
- package/src/util/mobile-native-utils_test.js +24 -0
- package/src/util/register-all-widgets-for-testing.js +12 -0
- package/src/util/scroll-utils.js +162 -0
- package/src/util/sizing-utils.js +46 -0
- package/src/util/tex.js +203 -0
- package/src/util/url-utils.js +36 -0
- package/src/util.js +990 -0
- package/src/version.js +11 -0
- package/src/widget-container.jsx +174 -0
- package/src/widgets/__stories__/categorizer.stories.jsx +15 -0
- package/src/widgets/__stories__/cs-program.stories.jsx +15 -0
- package/src/widgets/__stories__/definition.stories.jsx +38 -0
- package/src/widgets/__stories__/dropdown.stories.jsx +15 -0
- package/src/widgets/__stories__/explanation.stories.jsx +19 -0
- package/src/widgets/__stories__/expression.stories.jsx +26 -0
- package/src/widgets/__stories__/graded-group-set.stories.jsx +32 -0
- package/src/widgets/__stories__/graded-group.stories.jsx +32 -0
- package/src/widgets/__stories__/grapher.stories.jsx +47 -0
- package/src/widgets/__stories__/group.stories.jsx +15 -0
- package/src/widgets/__stories__/iframe.stories.jsx +15 -0
- package/src/widgets/__stories__/image.stories.jsx +62 -0
- package/src/widgets/__stories__/input-number.stories.jsx +90 -0
- package/src/widgets/__stories__/interaction.stories.jsx +15 -0
- package/src/widgets/__stories__/interactive-graph.stories.jsx +61 -0
- package/src/widgets/__stories__/matcher.stories.jsx +15 -0
- package/src/widgets/__stories__/matrix.stories.jsx +15 -0
- package/src/widgets/__stories__/number-line.stories.jsx +15 -0
- package/src/widgets/__stories__/numeric-input.stories.jsx +19 -0
- package/src/widgets/__stories__/orderer.stories.jsx +15 -0
- package/src/widgets/__stories__/passage-ref.stories.jsx +19 -0
- package/src/widgets/__stories__/passage.stories.jsx +19 -0
- package/src/widgets/__stories__/radio.stories.jsx +82 -0
- package/src/widgets/__stories__/sorter.stories.jsx +15 -0
- package/src/widgets/__stories__/transformer.stories.jsx +15 -0
- package/src/widgets/__stories__/unit.stories.jsx +45 -0
- package/src/widgets/__stories__/video-transcript-link.stories.jsx +24 -0
- package/src/widgets/__stories__/video.stories.jsx +15 -0
- package/src/widgets/__testdata__/categorizer_testdata.js +50 -0
- package/src/widgets/__testdata__/cs-program_testdata.js +28 -0
- package/src/widgets/__testdata__/dropdown_testdata.js +34 -0
- package/src/widgets/__testdata__/explanation_testdata.js +56 -0
- package/src/widgets/__testdata__/expression_testdata.js +95 -0
- package/src/widgets/__testdata__/graded-group-set_testdata.js +358 -0
- package/src/widgets/__testdata__/graded-group_testdata.js +79 -0
- package/src/widgets/__testdata__/grapher_testdata.js +376 -0
- package/src/widgets/__testdata__/group_testdata.js +156 -0
- package/src/widgets/__testdata__/iframe_testdata.js +36 -0
- package/src/widgets/__testdata__/image_testdata.js +38 -0
- package/src/widgets/__testdata__/input-number_testdata.js +73 -0
- package/src/widgets/__testdata__/interaction_testdata.js +264 -0
- package/src/widgets/__testdata__/interactive-graph_testdata.js +646 -0
- package/src/widgets/__testdata__/matcher_testdata.js +34 -0
- package/src/widgets/__testdata__/matrix_testdata.js +29 -0
- package/src/widgets/__testdata__/number-line_testdata.js +31 -0
- package/src/widgets/__testdata__/numeric-input_testdata.js +266 -0
- package/src/widgets/__testdata__/orderer_testdata.js +58 -0
- package/src/widgets/__testdata__/passage-ref_testdata.js +41 -0
- package/src/widgets/__testdata__/passage_testdata.js +71 -0
- package/src/widgets/__testdata__/radio_testdata.js +210 -0
- package/src/widgets/__testdata__/sorter_testdata.js +20 -0
- package/src/widgets/__testdata__/transformer_testdata.js +109 -0
- package/src/widgets/__testdata__/video_testdata.js +22 -0
- package/src/widgets/__tests__/__snapshots__/categorizer_test.jsx.snap +928 -0
- package/src/widgets/__tests__/__snapshots__/cs-program_test.jsx.snap +75 -0
- package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +99 -0
- package/src/widgets/__tests__/__snapshots__/dropdown_test.jsx.snap +143 -0
- package/src/widgets/__tests__/__snapshots__/explanation_test.js.snap +473 -0
- package/src/widgets/__tests__/__snapshots__/expression_test.jsx.snap +38 -0
- package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +754 -0
- package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +310 -0
- package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +863 -0
- package/src/widgets/__tests__/__snapshots__/grapher_test.jsx.snap +2880 -0
- package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +1154 -0
- package/src/widgets/__tests__/__snapshots__/iframe_test.jsx.snap +69 -0
- package/src/widgets/__tests__/__snapshots__/image_test.jsx.snap +388 -0
- package/src/widgets/__tests__/__snapshots__/input-number_test.jsx.snap +978 -0
- package/src/widgets/__tests__/__snapshots__/interaction_test.jsx.snap +311 -0
- package/src/widgets/__tests__/__snapshots__/interactive-graph_test.jsx.snap +2915 -0
- package/src/widgets/__tests__/__snapshots__/matcher_test.jsx.snap +1039 -0
- package/src/widgets/__tests__/__snapshots__/matrix_test.jsx.snap +450 -0
- package/src/widgets/__tests__/__snapshots__/number-line_test.jsx.snap +888 -0
- package/src/widgets/__tests__/__snapshots__/numeric-input_test.jsx.snap +930 -0
- package/src/widgets/__tests__/__snapshots__/orderer_test.jsx.snap +275 -0
- package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +446 -0
- package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +1988 -0
- package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +7107 -0
- package/src/widgets/__tests__/__snapshots__/video_test.jsx.snap +163 -0
- package/src/widgets/__tests__/categorizer_test.jsx +205 -0
- package/src/widgets/__tests__/cs-program_test.jsx +45 -0
- package/src/widgets/__tests__/definition_test.js +152 -0
- package/src/widgets/__tests__/dropdown_test.jsx +106 -0
- package/src/widgets/__tests__/explanation_test.js +199 -0
- package/src/widgets/__tests__/expression_test.jsx +439 -0
- package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +33 -0
- package/src/widgets/__tests__/graded-group-set_test.jsx +270 -0
- package/src/widgets/__tests__/graded-group_test.jsx +233 -0
- package/src/widgets/__tests__/grapher.cypress.jsx +486 -0
- package/src/widgets/__tests__/grapher_test.jsx +35 -0
- package/src/widgets/__tests__/group_test.jsx +512 -0
- package/src/widgets/__tests__/iframe_test.jsx +47 -0
- package/src/widgets/__tests__/image_test.jsx +65 -0
- package/src/widgets/__tests__/input-number_test.jsx +360 -0
- package/src/widgets/__tests__/interaction_test.jsx +33 -0
- package/src/widgets/__tests__/interactive-graph.cypress.jsx +697 -0
- package/src/widgets/__tests__/interactive-graph_test.jsx +105 -0
- package/src/widgets/__tests__/matcher_test.jsx +157 -0
- package/src/widgets/__tests__/matrix_test.jsx +84 -0
- package/src/widgets/__tests__/number-line_test.jsx +81 -0
- package/src/widgets/__tests__/numeric-input_test.jsx +574 -0
- package/src/widgets/__tests__/orderer_test.jsx +75 -0
- package/src/widgets/__tests__/passage-ref_test.jsx +166 -0
- package/src/widgets/__tests__/passage_test.jsx +133 -0
- package/src/widgets/__tests__/radio/base-radio_test.jsx +82 -0
- package/src/widgets/__tests__/radio_test.jsx +651 -0
- package/src/widgets/__tests__/renderQuestion.jsx +66 -0
- package/src/widgets/__tests__/unit_test.jsx +144 -0
- package/src/widgets/__tests__/video_test.jsx +55 -0
- package/src/widgets/categorizer.jsx +338 -0
- package/src/widgets/cs-program.jsx +227 -0
- package/src/widgets/definition.jsx +141 -0
- package/src/widgets/dropdown.jsx +143 -0
- package/src/widgets/explanation.jsx +315 -0
- package/src/widgets/expression.jsx +652 -0
- package/src/widgets/graded-group-answer-bar.jsx +152 -0
- package/src/widgets/graded-group-set.jsx +285 -0
- package/src/widgets/graded-group.jsx +448 -0
- package/src/widgets/grapher/util.jsx +806 -0
- package/src/widgets/grapher.jsx +663 -0
- package/src/widgets/group.jsx +213 -0
- package/src/widgets/iframe.jsx +198 -0
- package/src/widgets/image.jsx +310 -0
- package/src/widgets/input-number.jsx +411 -0
- package/src/widgets/interaction/arrow-picker.jsx +39 -0
- package/src/widgets/interaction/color-picker.jsx +69 -0
- package/src/widgets/interaction/dash-picker.jsx +46 -0
- package/src/widgets/interaction/element-container.jsx +97 -0
- package/src/widgets/interaction.jsx +742 -0
- package/src/widgets/interactive-graph.jsx +2820 -0
- package/src/widgets/label-image/__stories__/answer-choices.stories.jsx +93 -0
- package/src/widgets/label-image/__stories__/marker.stories.jsx +172 -0
- package/src/widgets/label-image/answer-choices.jsx +311 -0
- package/src/widgets/label-image/marker.jsx +365 -0
- package/src/widgets/label-image/types.js +33 -0
- package/src/widgets/label-image.jsx +841 -0
- package/src/widgets/label-image_test.js +840 -0
- package/src/widgets/lights-puzzle.jsx +356 -0
- package/src/widgets/matcher.jsx +271 -0
- package/src/widgets/matrix.jsx +616 -0
- package/src/widgets/measurer.jsx +215 -0
- package/src/widgets/molecule/__tests__/layout_test.jsx +159 -0
- package/src/widgets/molecule/__tests__/parser_test.jsx +142 -0
- package/src/widgets/molecule/molecule-drawing.jsx +175 -0
- package/src/widgets/molecule/molecule-layout.jsx +367 -0
- package/src/widgets/molecule/smiles-parser.jsx +300 -0
- package/src/widgets/molecule.jsx +172 -0
- package/src/widgets/number-line.jsx +858 -0
- package/src/widgets/numeric-input.jsx +622 -0
- package/src/widgets/orderer.jsx +753 -0
- package/src/widgets/passage/__tests__/passage-markdown_test.jsx +382 -0
- package/src/widgets/passage/get-line-height-for-node.js +10 -0
- package/src/widgets/passage/passage-markdown.jsx +416 -0
- package/src/widgets/passage-ref-target.jsx +70 -0
- package/src/widgets/passage-ref.jsx +198 -0
- package/src/widgets/passage.jsx +601 -0
- package/src/widgets/plotter.jsx +1198 -0
- package/src/widgets/radio/__stories__/base-radio.stories.jsx +40 -0
- package/src/widgets/radio/__stories__/choice-icon.stories.jsx +157 -0
- package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +26 -0
- package/src/widgets/radio/__stories__/cross-out-button.stories.jsx +64 -0
- package/src/widgets/radio/__stories__/cross-out-menu.stories.jsx +59 -0
- package/src/widgets/radio/__stories__/cross-out-tooltip.stories.jsx +59 -0
- package/src/widgets/radio/__stories__/focus-ring.stories.jsx +38 -0
- package/src/widgets/radio/__stories__/option-status.stories.jsx +118 -0
- package/src/widgets/radio/__stories__/toggleable-radio-button.stories.jsx +28 -0
- package/src/widgets/radio/base-radio.jsx +642 -0
- package/src/widgets/radio/choice-icon.jsx +468 -0
- package/src/widgets/radio/choice-none-above.jsx +54 -0
- package/src/widgets/radio/choice.jsx +692 -0
- package/src/widgets/radio/choice.jsx.fixture.js +64 -0
- package/src/widgets/radio/cross-out-button.jsx +136 -0
- package/src/widgets/radio/cross-out-menu-wrapper.jsx +492 -0
- package/src/widgets/radio/cross-out-tooltip.jsx +129 -0
- package/src/widgets/radio/focus-ring.jsx +51 -0
- package/src/widgets/radio/option-status.jsx +89 -0
- package/src/widgets/radio/toggleable-radio-button.jsx +133 -0
- package/src/widgets/radio/util.js +32 -0
- package/src/widgets/radio/util_test.js +19 -0
- package/src/widgets/radio/widget.jsx +470 -0
- package/src/widgets/radio.jsx +156 -0
- package/src/widgets/reaction-diagram.jsx +137 -0
- package/src/widgets/sequence.jsx +140 -0
- package/src/widgets/simulator.jsx +808 -0
- package/src/widgets/sorter.jsx +146 -0
- package/src/widgets/table.jsx +394 -0
- package/src/widgets/transformer.jsx +2876 -0
- package/src/widgets/unit.jsx +372 -0
- package/src/widgets/video-transcript-link.jsx +84 -0
- package/src/widgets/video-transcript-link_test.js +211 -0
- package/src/widgets/video.jsx +126 -0
- package/src/widgets.js +431 -0
- package/src/zoom.js +539 -0
- package/README.md +0 -3
- package/src/__tests__/index.test.js +0 -14
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
/**
|
|
3
|
+
* The tooltip that appears when you open the `CrossOutMenu` on desktop,
|
|
4
|
+
* enabling the user to toggle a choice's `crossedOut` state.
|
|
5
|
+
*/
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
|
|
8
|
+
import NewTooltip from "../../components/new-tooltip/new-tooltip.jsx";
|
|
9
|
+
import mediaQueries from "../../styles/media-queries.js";
|
|
10
|
+
|
|
11
|
+
import CrossOutButton, {type CrossOutButtonProps} from "./cross-out-button.jsx";
|
|
12
|
+
|
|
13
|
+
export type CrossOutTooltipProps = {
|
|
14
|
+
children: React$Element<any>,
|
|
15
|
+
dismissed: boolean,
|
|
16
|
+
onDismiss: () => void,
|
|
17
|
+
// Props to pass directly to the CrossOutButton.
|
|
18
|
+
buttonProps: CrossOutButtonProps,
|
|
19
|
+
...
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default class CrossOutTooltip extends React.PureComponent<CrossOutTooltipProps> {
|
|
23
|
+
/**
|
|
24
|
+
* When you press Tab or Shift-Tab from the tooltip button, we call this
|
|
25
|
+
* handler to dismiss the tooltip and bring you back to the menu button.
|
|
26
|
+
*/
|
|
27
|
+
_handleKeyDown: (e: SyntheticKeyboardEvent<HTMLElement>) => void = (
|
|
28
|
+
e: SyntheticKeyboardEvent<HTMLElement>,
|
|
29
|
+
) => {
|
|
30
|
+
// We don't need to check for the shift key _also_ being applied; we
|
|
31
|
+
// can just listen for all keystrokes where the primary key is Tab.
|
|
32
|
+
if (e.key === "Tab") {
|
|
33
|
+
this.props.onDismiss();
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
_renderTooltipContent(): React.Element<"div"> {
|
|
39
|
+
return (
|
|
40
|
+
// The button is the main semantic click target of the tooltip,
|
|
41
|
+
// which can receive tab focus (and automatically does when the
|
|
42
|
+
// flag `usingKeyboardFocus` is set!). However, the entire tooltip
|
|
43
|
+
// serves as a click target as well; see the <NewTooltip />.
|
|
44
|
+
<div onKeyDown={this._handleKeyDown}>
|
|
45
|
+
<CrossOutButton {...this.props.buttonProps} />
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
render(): React.Element<"div"> {
|
|
51
|
+
return (
|
|
52
|
+
<div>
|
|
53
|
+
<NewTooltip
|
|
54
|
+
content={this._renderTooltipContent()}
|
|
55
|
+
offsetFrom="arrow"
|
|
56
|
+
offset={10}
|
|
57
|
+
dismissed={this.props.dismissed}
|
|
58
|
+
showOnMount={true}
|
|
59
|
+
toggleOnHover={false}
|
|
60
|
+
onClick={() => this.props.buttonProps.onClick()}
|
|
61
|
+
// On smOrSmaller, hide this tooltip, and use the slide
|
|
62
|
+
// panel in `CrossOutMenuWrapper` instead.
|
|
63
|
+
media={mediaQueries.mdOrLarger}
|
|
64
|
+
>
|
|
65
|
+
{this.props.children}
|
|
66
|
+
</NewTooltip>
|
|
67
|
+
|
|
68
|
+
{/* If the menu is open, register global dismissal handlers
|
|
69
|
+
* that listen for clicks elsewhere in the page, or Escape
|
|
70
|
+
* keypresses. This component renders nothing; it just
|
|
71
|
+
* registers/unregisters handlers on mount/unmount. */}
|
|
72
|
+
{!this.props.dismissed && (
|
|
73
|
+
<GlobalDismissalHandlers onDismiss={this.props.onDismiss} />
|
|
74
|
+
)}
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A component that, when mounted, listens for dismissal-y events: clicking
|
|
82
|
+
* anywhere on the page, or pressing the Escape key. We handle these events by
|
|
83
|
+
* calling `onDismiss`.
|
|
84
|
+
*
|
|
85
|
+
* HACK(mdr): To generalize this component, we probably should ignore clicks
|
|
86
|
+
* inside the tooltip itself. However, conveniently, the `onClick` handler
|
|
87
|
+
* we receive from CrossOutMenu includes dismissal anyway. So, it's safe to
|
|
88
|
+
* dismiss on _any_ click, not just those outside the tooltip.
|
|
89
|
+
*/
|
|
90
|
+
class GlobalDismissalHandlers extends React.PureComponent<{
|
|
91
|
+
onDismiss: () => void,
|
|
92
|
+
...
|
|
93
|
+
}> {
|
|
94
|
+
componentDidMount() {
|
|
95
|
+
// Register these listeners after a tick, so they don't catch the click
|
|
96
|
+
// or keypress that _caused_ the tooltip to mount.
|
|
97
|
+
// TODO(jeff, CP-3128): Use Wonder Blocks Timing API
|
|
98
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
window.addEventListener("click", this._handleGlobalClick);
|
|
101
|
+
window.addEventListener("keyup", this._handleGlobalKeyup);
|
|
102
|
+
}, 0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
componentWillUnmount() {
|
|
106
|
+
window.removeEventListener("click", this._handleGlobalClick);
|
|
107
|
+
window.removeEventListener("keyup", this._handleGlobalKeyup);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* On clicking anywhere, trigger dismissal.
|
|
112
|
+
*/
|
|
113
|
+
_handleGlobalClick = () => {
|
|
114
|
+
this.props.onDismiss();
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* If the key was Escape, trigger dismissal.
|
|
119
|
+
*/
|
|
120
|
+
_handleGlobalKeyup = (e: KeyboardEvent) => {
|
|
121
|
+
if (e.key === "Escape") {
|
|
122
|
+
this.props.onDismiss();
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
render(): React.Node {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
/**
|
|
3
|
+
* Renders a circular selection ring around the child.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {StyleSheet, css} from "aphrodite";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
import * as styleConstants from "../../styles/constants.js";
|
|
10
|
+
|
|
11
|
+
class FocusRing extends React.Component<{
|
|
12
|
+
children?: any,
|
|
13
|
+
// Whether the focus ring is visible. Allows for positioning
|
|
14
|
+
// the child identically regardless of whether the ring is visible.
|
|
15
|
+
visible: boolean,
|
|
16
|
+
// Color of the focus ring
|
|
17
|
+
color: string,
|
|
18
|
+
...
|
|
19
|
+
}> {
|
|
20
|
+
static defaultProps: {|color: string, visible: boolean|} = {
|
|
21
|
+
visible: true,
|
|
22
|
+
color: styleConstants.kaGreen,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
render(): React.Element<"span"> {
|
|
26
|
+
const borderColor = this.props.visible
|
|
27
|
+
? this.props.color
|
|
28
|
+
: "transparent";
|
|
29
|
+
const style = {
|
|
30
|
+
borderColor: borderColor,
|
|
31
|
+
};
|
|
32
|
+
return (
|
|
33
|
+
<span className={css(styles.ring)} style={style}>
|
|
34
|
+
{this.props.children}
|
|
35
|
+
</span>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const styles = StyleSheet.create({
|
|
41
|
+
ring: {
|
|
42
|
+
margin: "auto",
|
|
43
|
+
display: "inline-block",
|
|
44
|
+
borderRadius: "50%",
|
|
45
|
+
borderWidth: 2,
|
|
46
|
+
padding: 2,
|
|
47
|
+
borderStyle: "solid",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export default FocusRing;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
/**
|
|
3
|
+
* Renders text indicating whether the choice was correct or
|
|
4
|
+
* not and whether the choice was selected or not.
|
|
5
|
+
* This information is redundant with that provided in the
|
|
6
|
+
* ChoiceIcon visualizations but is meant to make the distinctions
|
|
7
|
+
* between the states more immediately clear to users.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
11
|
+
import {StyleSheet, css} from "aphrodite";
|
|
12
|
+
import * as React from "react";
|
|
13
|
+
|
|
14
|
+
import * as styleConstants from "../../styles/constants.js";
|
|
15
|
+
|
|
16
|
+
class OptionStatus extends React.Component<{
|
|
17
|
+
// Was this option the correct answer?
|
|
18
|
+
correct: boolean,
|
|
19
|
+
// Did the user select this option as the answer?
|
|
20
|
+
checked: boolean,
|
|
21
|
+
// Did the user cross out this option?
|
|
22
|
+
crossedOut: boolean,
|
|
23
|
+
// Did the user select this option as the answer earlier?
|
|
24
|
+
previouslyAnswered: boolean,
|
|
25
|
+
...
|
|
26
|
+
}> {
|
|
27
|
+
_renderText(): string {
|
|
28
|
+
const {checked, correct, crossedOut} = this.props;
|
|
29
|
+
if (correct) {
|
|
30
|
+
// For correct answers, we surface checked _or_ crossedOut state,
|
|
31
|
+
// because any interaction with the correct answer is noteworthy!
|
|
32
|
+
if (checked) {
|
|
33
|
+
return i18n._("Correct (selected)");
|
|
34
|
+
}
|
|
35
|
+
if (crossedOut) {
|
|
36
|
+
return i18n._("Correct (but you crossed it out)");
|
|
37
|
+
}
|
|
38
|
+
return i18n._("Correct");
|
|
39
|
+
}
|
|
40
|
+
// But, for incorrect answers, we only surface checked state,
|
|
41
|
+
// because crossing out an incorrect answer is not noteworthy.
|
|
42
|
+
if (checked) {
|
|
43
|
+
return i18n._("Incorrect (selected)");
|
|
44
|
+
}
|
|
45
|
+
return i18n._("Incorrect");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
render(): React.Element<"div"> {
|
|
49
|
+
const {checked, correct, previouslyAnswered} = this.props;
|
|
50
|
+
|
|
51
|
+
let textStyle;
|
|
52
|
+
if (correct) {
|
|
53
|
+
textStyle = styles.correct;
|
|
54
|
+
} else {
|
|
55
|
+
if (checked || previouslyAnswered) {
|
|
56
|
+
textStyle = styles.incorrectAnswered;
|
|
57
|
+
} else {
|
|
58
|
+
textStyle = styles.incorrect;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div className={css(styles.text, textStyle)}>
|
|
64
|
+
{this._renderText()}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const styles = StyleSheet.create({
|
|
71
|
+
text: {
|
|
72
|
+
alignItems: "center",
|
|
73
|
+
display: "flex",
|
|
74
|
+
fontSize: 12,
|
|
75
|
+
height: 32,
|
|
76
|
+
textTransform: "uppercase",
|
|
77
|
+
},
|
|
78
|
+
correct: {
|
|
79
|
+
color: styleConstants.kaGreen,
|
|
80
|
+
},
|
|
81
|
+
incorrectAnswered: {
|
|
82
|
+
color: styleConstants.warning1,
|
|
83
|
+
},
|
|
84
|
+
incorrect: {
|
|
85
|
+
color: styleConstants.gray68,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export default OptionStatus;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import _ from "underscore";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A radio button that can be unchecked by clicking it again.
|
|
8
|
+
*
|
|
9
|
+
* This component behaves much like a checkbox in that when you click it again
|
|
10
|
+
* (or hit space bar or select it in some other way), it will become unchecked.
|
|
11
|
+
* (Hopefully) it behaves exactly like a radio button in every other way though
|
|
12
|
+
* (arrow keys can still be used to select it within a group for example).
|
|
13
|
+
*
|
|
14
|
+
* Here's a summary of which of our event handlers fire for each way to select
|
|
15
|
+
* or deselect the button:
|
|
16
|
+
*
|
|
17
|
+
* - Clicking a button: handleClick, handleChange
|
|
18
|
+
* - Pressing spacebar with button focussed: handleKeyDown, handleKeyUp (we
|
|
19
|
+
* suppress the default behavior of some browsers to synthesize a click event
|
|
20
|
+
* here).
|
|
21
|
+
* - When radio button A is selected, pressing down arrow to select B:
|
|
22
|
+
* handleKeyDown (A), handleClick (B), handleChange (B), handleKeyUp (B)
|
|
23
|
+
* - Clicking with mac screenreader: handleClick, handleChange
|
|
24
|
+
*/
|
|
25
|
+
class ToggleableRadioButton extends React.Component<$FlowFixMe, $FlowFixMe> {
|
|
26
|
+
_inputElement: ?HTMLInputElement;
|
|
27
|
+
|
|
28
|
+
static propTypes = {
|
|
29
|
+
// Whether the radio button should be checked or unchecked (this is a
|
|
30
|
+
// controlled component).
|
|
31
|
+
checked: PropTypes.bool.isRequired,
|
|
32
|
+
|
|
33
|
+
// A ref function to attach to the <input> element.
|
|
34
|
+
inputRef: PropTypes.func,
|
|
35
|
+
|
|
36
|
+
// A function that will be called whenever the radio button is checked
|
|
37
|
+
// or unchecked. It's possible for this to be called twice for a single
|
|
38
|
+
// checking or unchecking.
|
|
39
|
+
onChecked: PropTypes.func.isRequired,
|
|
40
|
+
|
|
41
|
+
// Callbacks for when the user presses the up/left arrow or down/right
|
|
42
|
+
// arrow (respectively) to navigate between choices. When called, the
|
|
43
|
+
// parent finds the prev/next choice, focuses the input, and checks the
|
|
44
|
+
// radio button if appropriate. (The reason we have custom behavior is
|
|
45
|
+
// that we actually _don't_ want to auto-select crossed-out choices!)
|
|
46
|
+
goToPrevChoice: PropTypes.func.isRequired,
|
|
47
|
+
goToNextChoice: PropTypes.func.isRequired,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
handleClick: (SyntheticMouseEvent<>) => void = (event) => {
|
|
51
|
+
this.props.onChecked(!this.props.checked);
|
|
52
|
+
|
|
53
|
+
// NOTE(johnsullivan): Preventing default would make sense to do here
|
|
54
|
+
// because we're fully controlling the state of the check box and
|
|
55
|
+
// don't really want it to be (un)checked accidently. React
|
|
56
|
+
// requires that we *don't* call preventDefault from the onClick or
|
|
57
|
+
// onChecked handlers of a controlled component though.
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
handleKeyUp: (SyntheticKeyboardEvent<>) => void = (event) => {
|
|
61
|
+
// Make hitting the spacebar with the element selected equivalent to
|
|
62
|
+
// clicking it. Some browsers do this as part of the radio button's
|
|
63
|
+
// default behavior, but since some browsers don't we normalize the
|
|
64
|
+
// behavior here.
|
|
65
|
+
if (event.key === " ") {
|
|
66
|
+
this.props.onChecked(!this.props.checked);
|
|
67
|
+
event.preventDefault();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
handleChange: (SyntheticInputEvent<>) => void = (event) => {
|
|
72
|
+
// If the checkbox is going from unchecked to checked, we'll handle it
|
|
73
|
+
// here.
|
|
74
|
+
// NOTE(johnsullivan): The onClick/onKeyUp handler most likely *also*
|
|
75
|
+
// handled this, but we're being defensive against browsers/devices
|
|
76
|
+
// that might not call those handlers like we'd expect. It's
|
|
77
|
+
// unclear to me whether this is strictly necessary.
|
|
78
|
+
if (!this.props.checked && event.target.checked) {
|
|
79
|
+
this.props.onChecked(true);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
handleKeyDown: (SyntheticKeyboardEvent<>) => void = (event) => {
|
|
84
|
+
// This is necessary in order to prevent IE9 from creating a duplicate
|
|
85
|
+
// click event on the radio button when the space bar is hit.
|
|
86
|
+
if (event.key === " ") {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
} else if (event.key === "ArrowUp" || event.key === "ArrowLeft") {
|
|
89
|
+
this.props.goToPrevChoice();
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
} else if (event.key === "ArrowDown" || event.key === "ArrowRight") {
|
|
92
|
+
this.props.goToNextChoice();
|
|
93
|
+
event.preventDefault();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* We keep a reference to the input element ourselves, and provide it to
|
|
99
|
+
* the parent, too.
|
|
100
|
+
*/
|
|
101
|
+
handleRef: (HTMLInputElement) => void = (inputElement) => {
|
|
102
|
+
this._inputElement = inputElement;
|
|
103
|
+
this.props.inputRef && this.props.inputRef(inputElement);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
render(): React.Node {
|
|
107
|
+
const {
|
|
108
|
+
// eslint-disable-next-line no-unused-vars
|
|
109
|
+
inputRef,
|
|
110
|
+
// eslint-disable-next-line no-unused-vars
|
|
111
|
+
onChecked,
|
|
112
|
+
// eslint-disable-next-line no-unused-vars
|
|
113
|
+
goToPrevChoice,
|
|
114
|
+
// eslint-disable-next-line no-unused-vars
|
|
115
|
+
goToNextChoice,
|
|
116
|
+
...otherProps
|
|
117
|
+
} = this.props;
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<input
|
|
121
|
+
{...otherProps}
|
|
122
|
+
type="radio"
|
|
123
|
+
onChange={this.handleChange}
|
|
124
|
+
onClick={this.handleClick}
|
|
125
|
+
onKeyDown={this.handleKeyDown}
|
|
126
|
+
onKeyUp={this.handleKeyUp}
|
|
127
|
+
ref={this.handleRef}
|
|
128
|
+
/>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default ToggleableRadioButton;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Given a choice's position in the radio widget, return the corresponding
|
|
6
|
+
* "choice letter". (For example, in English, `getChoiceLetter(0)` is "A",
|
|
7
|
+
* `getChoiceLetter(1)` is "B", etc.)
|
|
8
|
+
*/
|
|
9
|
+
export function getChoiceLetter(pos: number): string {
|
|
10
|
+
/* I18N: This is a list of single-character labels that will appear in
|
|
11
|
+
* front of multiple-choice options. For instance, a multiple-choice
|
|
12
|
+
* question with three options would display
|
|
13
|
+
* (A) first option
|
|
14
|
+
* (B) second option
|
|
15
|
+
* (C) third option
|
|
16
|
+
* There must be spaces between each of the different characters. The
|
|
17
|
+
* characters will show up next to options in the order that they are
|
|
18
|
+
* listed here. Most multiple choice questions have 5 or fewer options.
|
|
19
|
+
*/
|
|
20
|
+
const lettersString = i18n._(
|
|
21
|
+
"A B C D E F G H I J K L M N O P Q R S T U V W X Y Z",
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const letters = lettersString.split(" ");
|
|
25
|
+
|
|
26
|
+
if (pos < letters.length) {
|
|
27
|
+
// If the position we need is listed in the localized string, use that.
|
|
28
|
+
return letters[pos];
|
|
29
|
+
}
|
|
30
|
+
// If we're out of letters, give up and return a space.
|
|
31
|
+
return " ";
|
|
32
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import {getChoiceLetter} from "./util.js";
|
|
3
|
+
|
|
4
|
+
describe("getChoiceLetter (in English)", () => {
|
|
5
|
+
it("returns the first 5 letters for most questions", () => {
|
|
6
|
+
expect(getChoiceLetter(0)).toEqual("A");
|
|
7
|
+
expect(getChoiceLetter(1)).toEqual("B");
|
|
8
|
+
expect(getChoiceLetter(2)).toEqual("C");
|
|
9
|
+
expect(getChoiceLetter(3)).toEqual("D");
|
|
10
|
+
expect(getChoiceLetter(4)).toEqual("E");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("returns Z as the last letter, then spaces afterwards", () => {
|
|
14
|
+
expect(getChoiceLetter(25)).toEqual("Z");
|
|
15
|
+
for (let i = 26; i < 100; i++) {
|
|
16
|
+
expect(getChoiceLetter(i)).toEqual(" ");
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
});
|