@khanacademy/perseus 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc.js +1 -1
- package/CHANGELOG.md +7 -0
- package/dist/es/index.js +15 -1
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +11934 -0
- package/dist/index.js +15 -1
- package/dist/index.js.map +1 -1
- package/dist/strings.js +148 -1
- package/package.json +40 -4
- package/src/README.md +7 -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 +163 -0
- package/src/components/graph.jsx +414 -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 +149 -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 +456 -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 +78 -0
- package/src/components/range-input.jsx +61 -0
- package/src/components/simple-keypad-input.jsx +91 -0
- package/src/components/sortable.jsx +872 -0
- package/src/components/stub-tag-editor.jsx +53 -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 +604 -0
- package/src/interactive2/movable-polygon-options.js +351 -0
- package/src/interactive2/movable-polygon.js +367 -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 +131 -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 +542 -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 +434 -0
- package/src/perseus-types.js +1597 -0
- package/src/pure-markdown.js +346 -0
- package/src/question-paragraph.jsx +30 -0
- package/src/renderability.jsx +108 -0
- package/src/renderer.jsx +1931 -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 +1008 -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 +1585 -0
- package/src/util/interactive.js +4071 -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__/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 +65 -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 +228 -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 +654 -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 +819 -0
- package/src/widgets/grapher.jsx +658 -0
- package/src/widgets/group.jsx +213 -0
- package/src/widgets/iframe.jsx +199 -0
- package/src/widgets/image.jsx +309 -0
- package/src/widgets/input-number.jsx +410 -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 +2821 -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 +615 -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 +136 -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 +395 -0
- package/src/widgets/transformer.jsx +2870 -0
- package/src/widgets/unit.jsx +371 -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 +131 -0
- package/src/widgets.js +431 -0
- package/src/zoom.js +539 -0
- package/src/__tests__/index.test.js +0 -14
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
type AriaLabelledBy = {|
|
|
3
|
+
// The unique identifier of a DOM node to use as a label.
|
|
4
|
+
// See aria-labelledby
|
|
5
|
+
id: string,
|
|
6
|
+
|};
|
|
7
|
+
|
|
8
|
+
type AriaSupport = {|
|
|
9
|
+
// The title of the tooltip.
|
|
10
|
+
// See documentation for aria-label and aria-labelledby.
|
|
11
|
+
title?: string | AriaLabelledBy,
|
|
12
|
+
|
|
13
|
+
// How assertive should the tooltip content be?
|
|
14
|
+
// Default is to not provide an aria-live attribute.
|
|
15
|
+
// See documentation for aria-live.
|
|
16
|
+
assertiveness?: "polite" | "assertive",
|
|
17
|
+
|};
|
|
18
|
+
|
|
19
|
+
export type NewTooltipSide = "top" | "bottom" | "left" | "right";
|
|
20
|
+
|
|
21
|
+
export type OffsetAnchor = "bubble" | "arrow";
|
|
22
|
+
|
|
23
|
+
export type NewTooltipProps = {|
|
|
24
|
+
// The content of the tooltip bubble.
|
|
25
|
+
content: React$Node,
|
|
26
|
+
|
|
27
|
+
// Which side of the target element to attach the tooltip to.
|
|
28
|
+
//
|
|
29
|
+
// This field is optional. In `defaultProps`, its value is "top".
|
|
30
|
+
side: NewTooltipSide,
|
|
31
|
+
|
|
32
|
+
// How much space to put between the tooltip bubble and the target element.
|
|
33
|
+
// (Or, if `offsetFrom` is "arrow", how much space to put between the
|
|
34
|
+
// tooltip _arrow_ and the target element.)
|
|
35
|
+
//
|
|
36
|
+
// Increasing this value moves the tooltip further away from the target
|
|
37
|
+
// element. Decreasing this value moves the tooltip closer to the target
|
|
38
|
+
// element, perhaps to the point of overlapping!
|
|
39
|
+
//
|
|
40
|
+
// This field is optional. In `defaultProps`, its value is 0.
|
|
41
|
+
offset: number,
|
|
42
|
+
|
|
43
|
+
// Whether the `offset` prop measures the distance between the tooltip
|
|
44
|
+
// _bubble_ and the target element, or the tooltip _arrow_ and the target
|
|
45
|
+
// element.
|
|
46
|
+
//
|
|
47
|
+
// If `offsetFrom` is "bubble", and `offset` is 0, then the edge of the
|
|
48
|
+
// tooltip bubble will exactly touch the edge of the target element, and
|
|
49
|
+
// the tooltip arrow will overlap the _inside_ of the target element.
|
|
50
|
+
//
|
|
51
|
+
// If `offsetFrom` is "arrow", and `offset` is 0, then the tip of the
|
|
52
|
+
// tooltip arrow will exactly touch the edge of the target element, and the
|
|
53
|
+
// target bubble won't overlap the target element at all.
|
|
54
|
+
//
|
|
55
|
+
// This field is optional. In `defaultProps`, its value is "bubble".
|
|
56
|
+
offsetFrom: OffsetAnchor,
|
|
57
|
+
|
|
58
|
+
// An invisible margin around the tooltip bubble, preventing it from
|
|
59
|
+
// touching the edges of the screen.
|
|
60
|
+
//
|
|
61
|
+
// This field is optional. In `defaultProps`, its value is 0.
|
|
62
|
+
tooltipMargin: number,
|
|
63
|
+
|
|
64
|
+
// Whether to have no padding between the tooltip content and the edge of
|
|
65
|
+
// the tooltip bubble.
|
|
66
|
+
//
|
|
67
|
+
// This field is optional. In `defaultProps`, its value is `false`.
|
|
68
|
+
noPadding: boolean,
|
|
69
|
+
|
|
70
|
+
// The tooltip's text color - or, if `inverted` is set, the tooltip's
|
|
71
|
+
// background color.
|
|
72
|
+
//
|
|
73
|
+
// This field is optional. In `defaultProps`, its value is gray17.
|
|
74
|
+
color: string,
|
|
75
|
+
|
|
76
|
+
// Whether to "invert" the colors of the tooltip - that is, swap the text
|
|
77
|
+
// color to white, and background color to gray17 (or the value of the
|
|
78
|
+
// `color` prop).
|
|
79
|
+
//
|
|
80
|
+
// This field is optional. In `defaultProps`, its value is `false`.
|
|
81
|
+
inverted: boolean,
|
|
82
|
+
|
|
83
|
+
// Whether the tooltip is dismissed. Optional.
|
|
84
|
+
//
|
|
85
|
+
// If provided, this becomes a "controlled component": the caller is
|
|
86
|
+
// responsible for managing dismissal state, and using the `onDismiss`
|
|
87
|
+
// callback to listen to changes. But the caller also has the option to
|
|
88
|
+
// change dismissal state themselves, in case they want to dismiss or
|
|
89
|
+
// undismiss the tooltip in response to another action at the call site.
|
|
90
|
+
//
|
|
91
|
+
// If not provided, this is an "uncontrolled component": dismissal state is
|
|
92
|
+
// managed by NewTooltip automatically. Most callers will want this default
|
|
93
|
+
// uncontrolled behavior :)
|
|
94
|
+
dismissed?: boolean,
|
|
95
|
+
|
|
96
|
+
// Whether to show a close button to dismiss the tooltip.
|
|
97
|
+
//
|
|
98
|
+
// This field is optional. In `defaultProps`, its value is `false`.
|
|
99
|
+
dismissOnClickClose: boolean,
|
|
100
|
+
|
|
101
|
+
// This callback is called when the tooltip is dismissed. Optional.
|
|
102
|
+
onDismiss?: () => mixed,
|
|
103
|
+
|
|
104
|
+
// A value passed to the data-test-id attribute of the tooltip bubble
|
|
105
|
+
testId?: string,
|
|
106
|
+
|
|
107
|
+
// Whether to show the tooltip on hover.
|
|
108
|
+
//
|
|
109
|
+
// This field is optional. In `defaultProps`, its value is `true`.
|
|
110
|
+
toggleOnHover: boolean,
|
|
111
|
+
|
|
112
|
+
// Whether to show the tooltip immediately on mount. If this flag is set,
|
|
113
|
+
// then the tooltip will remain open until it is dismissed, regardless of
|
|
114
|
+
// other state (like hover).
|
|
115
|
+
//
|
|
116
|
+
// This field is optional. In `defaultProps`, its value is `false`.
|
|
117
|
+
showOnMount: boolean,
|
|
118
|
+
|
|
119
|
+
// A media query to restrict this tooltip's visibility. If provided, this
|
|
120
|
+
// tooltip will only be visible when the media query is satisfied.
|
|
121
|
+
//
|
|
122
|
+
// This field is optional. If omitted, the tooltip's visibility will not
|
|
123
|
+
// depend on any media query.
|
|
124
|
+
//
|
|
125
|
+
// TODO(mdr): We're trying to solve a general styling API problem for
|
|
126
|
+
// shared components. I wonder how conditioning the tooltip on media
|
|
127
|
+
// query might fit into that long-term solution!
|
|
128
|
+
//
|
|
129
|
+
// For now, though, we're sticking this onto the NewTooltip API in a
|
|
130
|
+
// simplistic form, instead of waiting for a general solution to exist.
|
|
131
|
+
// Otherwise, the way to get media-conditioned tooltips is to render
|
|
132
|
+
// multiple copies of NewTooltip _and_ its children, which causes perf,
|
|
133
|
+
// a11y, and window-resize-consistency problems.
|
|
134
|
+
media?: string,
|
|
135
|
+
|
|
136
|
+
// This callback is called when the tooltip is clicked. Optional.
|
|
137
|
+
//
|
|
138
|
+
// The clickable area includes the tooltip bubble, the tooltip bubble's
|
|
139
|
+
// contents, and the tooltip arrow. If a callback is provided, these areas
|
|
140
|
+
// will additionally be styled as clickable, i.e., `cursor: pointer`.
|
|
141
|
+
onClick?: () => mixed,
|
|
142
|
+
|
|
143
|
+
// This callback is called when the cursor enters the tooltip. Optional.
|
|
144
|
+
//
|
|
145
|
+
// The hoverable area includes the tooltip bubble, the tooltip bubble's
|
|
146
|
+
// contents, and the tooltip arrow. If a callback is provided, these areas
|
|
147
|
+
// will additionally be styled as clickable, i.e., `cursor: pointer`.
|
|
148
|
+
//
|
|
149
|
+
// If the cursor moves from the tooltip bubble to the tooltip arrow, or
|
|
150
|
+
// vice-versa, you may receive a pair of `onMouseLeave` and `onMouseEnter`
|
|
151
|
+
// calls. Beware! (Thankfully, if all you do is call `setState`, React's
|
|
152
|
+
// `setState` batching should protect you from unnecessary updates.)
|
|
153
|
+
onMouseEnter?: () => mixed,
|
|
154
|
+
|
|
155
|
+
// This callback is called when the cursor enters the tooltip. Optional.
|
|
156
|
+
//
|
|
157
|
+
// The hoverable area includes the tooltip bubble, the tooltip bubble's
|
|
158
|
+
// contents, and the tooltip arrow. If a callback is provided, these areas
|
|
159
|
+
// will additionally be styled as clickable, i.e., `cursor: pointer`.
|
|
160
|
+
//
|
|
161
|
+
// If the cursor moves from the tooltip bubble to the tooltip arrow, or
|
|
162
|
+
// vice-versa, you may receive a pair of `onMouseLeave` and `onMouseEnter`
|
|
163
|
+
// calls. Beware! (Thankfully, if all you do is call `setState`, React's
|
|
164
|
+
// `setState` batching should protect you from unnecessary updates.)
|
|
165
|
+
onMouseLeave?: () => mixed,
|
|
166
|
+
|
|
167
|
+
// Provide various ARIA values to support accessibility of this tooltip.
|
|
168
|
+
// All tooltips should probably provide a header label (string or id of
|
|
169
|
+
// a node).
|
|
170
|
+
//
|
|
171
|
+
// If no title or title node id is provided, the tooltip contents are
|
|
172
|
+
// specified as the label.
|
|
173
|
+
//
|
|
174
|
+
// This property can also be used to specify the aria-live assertiveness.
|
|
175
|
+
// Permanently visible tooltips that don't change their contents
|
|
176
|
+
// may not want this as it will call attention to the tooltip on first
|
|
177
|
+
// display, which isn't necessarily valuable.
|
|
178
|
+
//
|
|
179
|
+
// If the content DOES change but you don't want an initial
|
|
180
|
+
// assert on first display, consider using some custom aria-live within the
|
|
181
|
+
// content of the tooltip rather than this setting.
|
|
182
|
+
a11y?: AriaSupport,
|
|
183
|
+
|};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
/**
|
|
3
|
+
* Utility functions for the NewTooltip component.
|
|
4
|
+
*/
|
|
5
|
+
import Color from "@khanacademy/wonder-blocks-color";
|
|
6
|
+
|
|
7
|
+
import type {NewTooltipProps} from "./types.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Given a set of NewTooltipProps, return the `backgroundColor` and `textColor`
|
|
11
|
+
* of the tooltip. Used in TooltipArrow and TooltipBubble.
|
|
12
|
+
*/
|
|
13
|
+
export function getTooltipColors(tooltipProps: {
|
|
14
|
+
color: NewTooltipProps["color"],
|
|
15
|
+
inverted: NewTooltipProps["inverted"],
|
|
16
|
+
...
|
|
17
|
+
}): {|
|
|
18
|
+
backgroundColor: string,
|
|
19
|
+
textColor: string,
|
|
20
|
+
|} {
|
|
21
|
+
const {color, inverted} = tooltipProps;
|
|
22
|
+
|
|
23
|
+
if (inverted) {
|
|
24
|
+
return {backgroundColor: color, textColor: Color.white};
|
|
25
|
+
}
|
|
26
|
+
return {backgroundColor: Color.white, textColor: color};
|
|
27
|
+
}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/* eslint-disable react/sort-comp */
|
|
2
|
+
// @flow
|
|
3
|
+
import classNames from "classnames";
|
|
4
|
+
import $ from "jquery";
|
|
5
|
+
import {number as knumber} from "kmath";
|
|
6
|
+
import PropTypes from "prop-types";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import ReactDOM from "react-dom";
|
|
9
|
+
import _ from "underscore";
|
|
10
|
+
|
|
11
|
+
import Util from "../util.js";
|
|
12
|
+
import KhanMath from "../util/math.js";
|
|
13
|
+
|
|
14
|
+
import type {Format} from "../util/math.js";
|
|
15
|
+
|
|
16
|
+
const {firstNumericalParse, captureScratchpadTouchStart} = Util;
|
|
17
|
+
const toNumericString = KhanMath.toNumericString;
|
|
18
|
+
const getNumericFormat = KhanMath.getNumericFormat;
|
|
19
|
+
|
|
20
|
+
/* An input box that accepts only numeric strings
|
|
21
|
+
*
|
|
22
|
+
* Calls onChange(value, format) for valid numbers.
|
|
23
|
+
* Reverts to the current value onBlur or on [ENTER],
|
|
24
|
+
* but maintains the format (i.e. 3/2, 1 1/2, 150%)
|
|
25
|
+
* Accepts empty input and sends it to onChange as null
|
|
26
|
+
* if no numeric placeholder is set.
|
|
27
|
+
* If given a checkValidity function, will turn
|
|
28
|
+
* the background/outline red when invalid
|
|
29
|
+
* If useArrowKeys is set to true, up/down arrows will
|
|
30
|
+
* increment/decrement integers
|
|
31
|
+
* Optionally takes a size ("mini", "small", "normal")
|
|
32
|
+
*/
|
|
33
|
+
class NumberInput extends React.Component<$FlowFixMe, $FlowFixMe> {
|
|
34
|
+
static propTypes = {
|
|
35
|
+
value: PropTypes.number,
|
|
36
|
+
format: PropTypes.string,
|
|
37
|
+
placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
38
|
+
onChange: PropTypes.func.isRequired,
|
|
39
|
+
onFormatChange: PropTypes.func,
|
|
40
|
+
checkValidity: PropTypes.func,
|
|
41
|
+
size: PropTypes.string,
|
|
42
|
+
label: PropTypes.oneOf(["put your labels outside your inputs!"]),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
static defaultProps: $FlowFixMe = {
|
|
46
|
+
value: null,
|
|
47
|
+
placeholder: null,
|
|
48
|
+
format: null,
|
|
49
|
+
onFormatChange: () => null,
|
|
50
|
+
checkValidity: () => true,
|
|
51
|
+
useArrowKeys: false,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
state: $FlowFixMe = {
|
|
55
|
+
format: this.props.format,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
render(): React.Node {
|
|
59
|
+
let classes = classNames({
|
|
60
|
+
"number-input": true,
|
|
61
|
+
"invalid-input": !this._checkValidity(this.props.value),
|
|
62
|
+
mini: this.props.size === "mini",
|
|
63
|
+
small: this.props.size === "small",
|
|
64
|
+
normal: this.props.size === "normal",
|
|
65
|
+
});
|
|
66
|
+
if (this.props.className != null) {
|
|
67
|
+
classes = classes + " " + this.props.className;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const {
|
|
71
|
+
// eslint-disable-next-line no-unused-vars
|
|
72
|
+
onFormatChange,
|
|
73
|
+
// eslint-disable-next-line no-unused-vars
|
|
74
|
+
checkValidity,
|
|
75
|
+
// eslint-disable-next-line no-unused-vars
|
|
76
|
+
useArrowKeys,
|
|
77
|
+
...restProps
|
|
78
|
+
} = this.props;
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<input
|
|
82
|
+
{...restProps}
|
|
83
|
+
className={classes}
|
|
84
|
+
type="text"
|
|
85
|
+
// eslint-disable-next-line react/no-string-refs
|
|
86
|
+
ref="input"
|
|
87
|
+
onChange={this._handleChange}
|
|
88
|
+
onFocus={this._handleFocus}
|
|
89
|
+
onBlur={this._handleBlur}
|
|
90
|
+
onKeyPress={this._handleBlur}
|
|
91
|
+
onKeyDown={this._onKeyDown}
|
|
92
|
+
onTouchStart={captureScratchpadTouchStart}
|
|
93
|
+
defaultValue={toNumericString(
|
|
94
|
+
this.props.value,
|
|
95
|
+
this.state.format,
|
|
96
|
+
)}
|
|
97
|
+
value={undefined}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
componentDidUpdate(prevProps: $FlowFixMe) {
|
|
103
|
+
if (!knumber.equal(this.getValue(), this.props.value)) {
|
|
104
|
+
this._setValue(this.props.value, this.state.format);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* Return the current "value" of this input
|
|
109
|
+
* If empty, it returns the placeholder (if it is a number) or null
|
|
110
|
+
*/
|
|
111
|
+
getValue: () => $FlowFixMe = () => {
|
|
112
|
+
return this.parseInputValue(
|
|
113
|
+
// $FlowFixMe[incompatible-use]
|
|
114
|
+
// $FlowFixMe[prop-missing]
|
|
115
|
+
ReactDOM.findDOMNode(this.refs.input).value, // eslint-disable-line react/no-string-refs
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/* Return the current string value of this input */
|
|
120
|
+
getStringValue: () => string = () => {
|
|
121
|
+
// $FlowFixMe[incompatible-use]
|
|
122
|
+
// $FlowFixMe[prop-missing]
|
|
123
|
+
return ReactDOM.findDOMNode(this.refs.input).value.toString(); // eslint-disable-line react/no-string-refs
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
parseInputValue: ($FlowFixMe) => $FlowFixMe = (value) => {
|
|
127
|
+
if (value === "") {
|
|
128
|
+
const placeholder = this.props.placeholder;
|
|
129
|
+
return _.isFinite(placeholder) ? +placeholder : null;
|
|
130
|
+
}
|
|
131
|
+
const result = firstNumericalParse(value);
|
|
132
|
+
return _.isFinite(result) ? result : this.props.value;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/* Set text input focus to this input */
|
|
136
|
+
focus: () => void = () => {
|
|
137
|
+
// $FlowFixMe[incompatible-use]
|
|
138
|
+
// $FlowFixMe[prop-missing]
|
|
139
|
+
ReactDOM.findDOMNode(this.refs.input).focus(); // eslint-disable-line react/no-string-refs
|
|
140
|
+
this._handleFocus();
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
blur: () => void = () => {
|
|
144
|
+
// $FlowFixMe[incompatible-use]
|
|
145
|
+
// $FlowFixMe[prop-missing]
|
|
146
|
+
ReactDOM.findDOMNode(this.refs.input).blur(); // eslint-disable-line react/no-string-refs
|
|
147
|
+
this._handleBlur();
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
setSelectionRange: (number, number) => $FlowFixMe = (
|
|
151
|
+
selectionStart,
|
|
152
|
+
selectionEnd,
|
|
153
|
+
) => {
|
|
154
|
+
// $FlowFixMe[incompatible-use]
|
|
155
|
+
// $FlowFixMe[prop-missing]
|
|
156
|
+
ReactDOM.findDOMNode(this).setSelectionRange(
|
|
157
|
+
selectionStart,
|
|
158
|
+
selectionEnd,
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
getSelectionStart: () => number = () => {
|
|
163
|
+
// $FlowFixMe[incompatible-use]
|
|
164
|
+
// $FlowFixMe[prop-missing]
|
|
165
|
+
return ReactDOM.findDOMNode(this).selectionStart;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
getSelectionEnd: () => number = () => {
|
|
169
|
+
// $FlowFixMe[incompatible-use]
|
|
170
|
+
// $FlowFixMe[prop-missing]
|
|
171
|
+
return ReactDOM.findDOMNode(this).selectionEnd;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
_checkValidity: ($FlowFixMe) => boolean = (value) => {
|
|
175
|
+
if (value == null) {
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const val = firstNumericalParse(value);
|
|
180
|
+
const checkValidity = this.props.checkValidity;
|
|
181
|
+
|
|
182
|
+
return _.isFinite(val) && checkValidity(val);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
_handleChange: (SyntheticInputEvent<>) => void = (e) => {
|
|
186
|
+
const text = e.target.value;
|
|
187
|
+
const value = this.parseInputValue(text);
|
|
188
|
+
const format = getNumericFormat(text);
|
|
189
|
+
|
|
190
|
+
this.props.onChange(value);
|
|
191
|
+
if (format) {
|
|
192
|
+
this.props.onFormatChange(value, format);
|
|
193
|
+
this.setState({format: format});
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
_handleFocus: () => void = () => {
|
|
198
|
+
if (this.props.onFocus) {
|
|
199
|
+
this.props.onFocus();
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
_handleBlur: (
|
|
204
|
+
e?: SyntheticFocusEvent<> | SyntheticKeyboardEvent<>,
|
|
205
|
+
) => void = (e) => {
|
|
206
|
+
// Only continue on blur or "enter"
|
|
207
|
+
if (e && e.type === "keypress" && e.keyCode !== 13) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this._setValue(this.props.value, this.state.format);
|
|
212
|
+
if (this.props.onBlur) {
|
|
213
|
+
this.props.onBlur();
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
_onKeyDown: (SyntheticKeyboardEvent<>) => void = (e) => {
|
|
218
|
+
if (this.props.onKeyDown) {
|
|
219
|
+
this.props.onKeyDown(e);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (
|
|
223
|
+
!this.props.useArrowKeys ||
|
|
224
|
+
!_.contains(["ArrowUp", "ArrowDown"], e.key)
|
|
225
|
+
) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let val = this.getValue();
|
|
230
|
+
if (val !== Math.floor(val)) {
|
|
231
|
+
return; // bail if not an integer
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (e.key === "ArrowUp") {
|
|
235
|
+
val = val + 1;
|
|
236
|
+
} else if (e.key === "ArrowDown") {
|
|
237
|
+
val = val - 1;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (this._checkValidity(val)) {
|
|
241
|
+
this.props.onChange(val);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
_setValue: (number, Format) => void = (val, format) => {
|
|
246
|
+
// eslint-disable-next-line react/no-string-refs
|
|
247
|
+
$(ReactDOM.findDOMNode(this.refs.input)).val(
|
|
248
|
+
toNumericString(val, format),
|
|
249
|
+
);
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export default NumberInput;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
/**
|
|
3
|
+
* In review mode (currently only visible in the sat-mission), NumericInput and
|
|
4
|
+
* InputNumber use this component to display the set of correct answers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
8
|
+
import PropTypes from "prop-types";
|
|
9
|
+
import * as React from "react";
|
|
10
|
+
import _ from "underscore";
|
|
11
|
+
|
|
12
|
+
class PossibleAnswers extends React.Component<$FlowFixMe> {
|
|
13
|
+
static propTypes = {
|
|
14
|
+
answers: PropTypes.arrayOf(PropTypes.string),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
render(): React.Node {
|
|
18
|
+
// It's redundant to show duplicate answers.
|
|
19
|
+
// So, remove duplicates from the given list of answer strings.
|
|
20
|
+
const answers = _.uniq(this.props.answers);
|
|
21
|
+
|
|
22
|
+
const answerComponents = _.map(answers, (answer) => {
|
|
23
|
+
// Plus, now that our answers are distinct, we can safely use the
|
|
24
|
+
// answer string as a key.
|
|
25
|
+
return <dd key={answer}>{answer}</dd>;
|
|
26
|
+
});
|
|
27
|
+
return (
|
|
28
|
+
<dl className="perseus-possible-answers">
|
|
29
|
+
<dt>{i18n.doNotTranslate("Correct Answer")}</dt>
|
|
30
|
+
{answerComponents}
|
|
31
|
+
</dl>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default PossibleAnswers;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/* eslint-disable @babel/no-invalid-this, react/sort-comp */
|
|
2
|
+
// @flow
|
|
3
|
+
import createReactClass from "create-react-class";
|
|
4
|
+
import PropTypes from "prop-types";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import _ from "underscore";
|
|
7
|
+
|
|
8
|
+
import {Errors} from "../logging/log.js";
|
|
9
|
+
import {PerseusError} from "../perseus-error.js";
|
|
10
|
+
|
|
11
|
+
/* A checkbox that syncs its value to props using the
|
|
12
|
+
* renderer's onChange method, and gets the prop name
|
|
13
|
+
* dynamically from its props list
|
|
14
|
+
*/
|
|
15
|
+
const PropCheckBox: any = createReactClass({
|
|
16
|
+
displayName: "PropCheckBox",
|
|
17
|
+
|
|
18
|
+
propTypes: {
|
|
19
|
+
labelAlignment: PropTypes.oneOf(["left", "right"]),
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
DEFAULT_PROPS: {
|
|
23
|
+
label: null,
|
|
24
|
+
onChange: null,
|
|
25
|
+
labelAlignment: "left",
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
getDefaultProps: function () {
|
|
29
|
+
return this.DEFAULT_PROPS;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
propName: function () {
|
|
33
|
+
const propName = _.find(
|
|
34
|
+
_.keys(this.props),
|
|
35
|
+
function (localPropName) {
|
|
36
|
+
return !_.has(this.DEFAULT_PROPS, localPropName);
|
|
37
|
+
},
|
|
38
|
+
this,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (!propName) {
|
|
42
|
+
throw new PerseusError(
|
|
43
|
+
"Attempted to create a PropCheckBox with no prop!",
|
|
44
|
+
Errors.InvalidInput,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return propName;
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
_labelAlignLeft: function () {
|
|
52
|
+
return this.props.labelAlignment === "left";
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
render: function () {
|
|
56
|
+
const propName = this.propName();
|
|
57
|
+
return (
|
|
58
|
+
<label>
|
|
59
|
+
{this._labelAlignLeft() && this.props.label}
|
|
60
|
+
<input
|
|
61
|
+
type="checkbox"
|
|
62
|
+
checked={this.props[propName]}
|
|
63
|
+
onChange={this.toggle}
|
|
64
|
+
/>
|
|
65
|
+
{!this._labelAlignLeft() && this.props.label}
|
|
66
|
+
</label>
|
|
67
|
+
);
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
toggle: function () {
|
|
71
|
+
const propName = this.propName();
|
|
72
|
+
const changes = {};
|
|
73
|
+
changes[propName] = !this.props[propName];
|
|
74
|
+
this.props.onChange(changes);
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export default PropCheckBox;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* eslint-disable react/forbid-prop-types, react/sort-comp */
|
|
2
|
+
// @flow
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import NumberInput from "../components/number-input.jsx";
|
|
7
|
+
|
|
8
|
+
const truth = () => true;
|
|
9
|
+
|
|
10
|
+
/* A minor abstraction on top of NumberInput for ranges
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
class RangeInput extends React.Component<$FlowFixMe> {
|
|
14
|
+
static propTypes = {
|
|
15
|
+
value: PropTypes.array.isRequired,
|
|
16
|
+
onChange: PropTypes.func.isRequired,
|
|
17
|
+
placeholder: PropTypes.array,
|
|
18
|
+
checkValidity: PropTypes.func,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
static defaultProps: $FlowFixMe = {
|
|
22
|
+
placeholder: [null, null],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
render(): React.Node {
|
|
26
|
+
const value = this.props.value;
|
|
27
|
+
const checkValidity = this.props.checkValidity || truth;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className="range-input">
|
|
31
|
+
<NumberInput
|
|
32
|
+
{...this.props}
|
|
33
|
+
value={value[0]}
|
|
34
|
+
checkValidity={(val) => checkValidity([val, value[1]])}
|
|
35
|
+
// eslint-disable-next-line react/jsx-no-bind
|
|
36
|
+
onChange={this.onChange.bind(this, 0)}
|
|
37
|
+
placeholder={this.props.placeholder[0]}
|
|
38
|
+
/>
|
|
39
|
+
<NumberInput
|
|
40
|
+
{...this.props}
|
|
41
|
+
value={value[1]}
|
|
42
|
+
checkValidity={(val) => checkValidity([value[0], val])}
|
|
43
|
+
// eslint-disable-next-line react/jsx-no-bind
|
|
44
|
+
onChange={this.onChange.bind(this, 1)}
|
|
45
|
+
placeholder={this.props.placeholder[1]}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onChange: (number, string) => void = (i, newVal) => {
|
|
52
|
+
const value = this.props.value;
|
|
53
|
+
if (i === 0) {
|
|
54
|
+
this.props.onChange([newVal, value[1]]);
|
|
55
|
+
} else {
|
|
56
|
+
this.props.onChange([value[0], newVal]);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export default RangeInput;
|