@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,131 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
/**
|
|
3
|
+
* This is a video widget for embedding videos in articles.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {View} from "@khanacademy/wonder-blocks-core";
|
|
7
|
+
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import _ from "underscore";
|
|
10
|
+
|
|
11
|
+
import FixedToResponsive from "../components/fixed-to-responsive.jsx";
|
|
12
|
+
import {getDependencies} from "../dependencies.js";
|
|
13
|
+
import * as Changeable from "../mixins/changeable.jsx";
|
|
14
|
+
import a11y from "../util/a11y.js";
|
|
15
|
+
|
|
16
|
+
import VideoTranscriptLink from "./video-transcript-link.jsx";
|
|
17
|
+
|
|
18
|
+
import type {PerseusVideoWidgetOptions} from "../perseus-types.js";
|
|
19
|
+
import type {
|
|
20
|
+
PerseusScore,
|
|
21
|
+
WidgetExports,
|
|
22
|
+
WidgetProps,
|
|
23
|
+
} from "../types.js";
|
|
24
|
+
|
|
25
|
+
// Current default is 720p, based on the typical videos we upload currently
|
|
26
|
+
const DEFAULT_WIDTH = 1280;
|
|
27
|
+
const DEFAULT_HEIGHT = 720;
|
|
28
|
+
|
|
29
|
+
const KA_EMBED = "{host}/embed_video?slug={slug}" + "&internal_video_only=1";
|
|
30
|
+
const IS_URL = /^https?:\/\//;
|
|
31
|
+
const IS_KA_SITE = /(khanacademy\.org|localhost)/;
|
|
32
|
+
|
|
33
|
+
type UserInput = null;
|
|
34
|
+
type Rubric = PerseusVideoWidgetOptions;
|
|
35
|
+
type RenderProps = PerseusVideoWidgetOptions; // exports has no 'transform'
|
|
36
|
+
type Props = {|
|
|
37
|
+
...WidgetProps<RenderProps, Rubric>,
|
|
38
|
+
alignment: string, // Where does this get set?
|
|
39
|
+
|};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Video renderer.
|
|
43
|
+
*/
|
|
44
|
+
class Video extends React.Component<Props> {
|
|
45
|
+
/**
|
|
46
|
+
* This is the widget's grading function.
|
|
47
|
+
* Points for videos are tallied by the embedded video itself, in the case
|
|
48
|
+
* of Khan Academy videos.
|
|
49
|
+
*/
|
|
50
|
+
static validate(userInput: UserInput, rubric: Rubric): PerseusScore {
|
|
51
|
+
return {
|
|
52
|
+
type: "points",
|
|
53
|
+
earned: 0,
|
|
54
|
+
total: 0,
|
|
55
|
+
message: null,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getUserInput: () => ?void = () => {
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
simpleValidate: (Rubric) => PerseusScore = (rubric) => {
|
|
64
|
+
return Video.validate(null, rubric);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
change: (...args: $ReadOnlyArray<mixed>) => $FlowFixMe = (...args) => {
|
|
68
|
+
// $FlowFixMe[incompatible-call]
|
|
69
|
+
return Changeable.change.apply(this, args);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
render(): React.Node {
|
|
73
|
+
const {InitialRequestUrl} = getDependencies();
|
|
74
|
+
|
|
75
|
+
const location = this.props.location;
|
|
76
|
+
if (!location) {
|
|
77
|
+
return <div />;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let url;
|
|
81
|
+
|
|
82
|
+
if (IS_URL.test(location)) {
|
|
83
|
+
url = location;
|
|
84
|
+
} else {
|
|
85
|
+
url = KA_EMBED.replace("{slug}", location);
|
|
86
|
+
// eslint-disable-next-line static-service/require-safe-link-to
|
|
87
|
+
let embedHostname = "https://www.khanacademy.org";
|
|
88
|
+
if (IS_KA_SITE.test(InitialRequestUrl.host)) {
|
|
89
|
+
embedHostname = InitialRequestUrl.origin;
|
|
90
|
+
}
|
|
91
|
+
url = url.replace("{host}", embedHostname);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<View>
|
|
96
|
+
<FixedToResponsive
|
|
97
|
+
width={DEFAULT_WIDTH}
|
|
98
|
+
height={DEFAULT_HEIGHT}
|
|
99
|
+
// The key is here for the benefit of the editor, to ensure that
|
|
100
|
+
// any changes cause a re-rendering of the frame.
|
|
101
|
+
key={location + this.props.alignment}
|
|
102
|
+
>
|
|
103
|
+
<View style={a11y.srOnly}>
|
|
104
|
+
{i18n._("Khan Academy video wrapper")}
|
|
105
|
+
</View>
|
|
106
|
+
|
|
107
|
+
<iframe
|
|
108
|
+
// TODO(joshuan): Consider not using iframes when we're
|
|
109
|
+
// loading this from webapp. This iframe is problematic
|
|
110
|
+
// for screenreaders.
|
|
111
|
+
className="perseus-video-widget"
|
|
112
|
+
sandbox="allow-same-origin allow-scripts"
|
|
113
|
+
width={DEFAULT_WIDTH}
|
|
114
|
+
height={DEFAULT_HEIGHT}
|
|
115
|
+
src={url}
|
|
116
|
+
allowFullScreen={true}
|
|
117
|
+
/>
|
|
118
|
+
</FixedToResponsive>
|
|
119
|
+
<VideoTranscriptLink location={location} />
|
|
120
|
+
</View>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default ({
|
|
126
|
+
name: "video",
|
|
127
|
+
displayName: "Video",
|
|
128
|
+
defaultAlignment: "block",
|
|
129
|
+
supportedAlignments: ["block", "float-left", "float-right", "full-width"],
|
|
130
|
+
widget: Video,
|
|
131
|
+
}: WidgetExports<typeof Video>);
|
package/src/widgets.js
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import _ from "underscore";
|
|
4
|
+
|
|
5
|
+
import {Errors, Log} from "./logging/log.js";
|
|
6
|
+
import {PerseusError} from "./perseus-error.js";
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Alignment,
|
|
10
|
+
Tracking,
|
|
11
|
+
Version,
|
|
12
|
+
WidgetExports,
|
|
13
|
+
WidgetInfo,
|
|
14
|
+
WidgetTransform,
|
|
15
|
+
} from "./types.js";
|
|
16
|
+
|
|
17
|
+
const DEFAULT_ALIGNMENT = "block";
|
|
18
|
+
// NOTE(kevinb): "default" is not one in `validAlignments`.
|
|
19
|
+
const DEFAULT_SUPPORTED_ALIGNMENTS = ["default"];
|
|
20
|
+
const DEFAULT_STATIC = false;
|
|
21
|
+
const DEFAULT_TRACKING = "";
|
|
22
|
+
const DEFAULT_LINTABLE = false;
|
|
23
|
+
|
|
24
|
+
type Editor = $FlowFixMe;
|
|
25
|
+
|
|
26
|
+
const widgets: {|[string]: WidgetExports<>|} = {};
|
|
27
|
+
const editors = {};
|
|
28
|
+
|
|
29
|
+
// Widgets must be registered to avoid circular dependencies with the
|
|
30
|
+
// core Editor and Renderer components.
|
|
31
|
+
// TODO(jeremy): The widget name is already embedded in the WidgetExports type
|
|
32
|
+
// so could we drop the `name` parameter here?
|
|
33
|
+
export const registerWidget = (name: string, widget: WidgetExports<>) => {
|
|
34
|
+
widgets[name] = widget;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const registerWidgets = (widgets: $ReadOnlyArray<WidgetExports<>>) => {
|
|
38
|
+
widgets.forEach((widget) => {
|
|
39
|
+
registerWidget(widget.name, widget);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
validateAlignments();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const registerEditors = (editorsToRegister: $ReadOnlyArray<Editor>) => {
|
|
46
|
+
editorsToRegister.forEach((editor) => {
|
|
47
|
+
if (!editor.widgetName) {
|
|
48
|
+
throw new PerseusError(
|
|
49
|
+
`Editor ${editor.displayName} doesn't have a widgetName property`,
|
|
50
|
+
Errors.Internal,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
editors[editor.widgetName] = editor;
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const getWidget = (name: string): ?React.ComponentType<any> => {
|
|
58
|
+
// TODO(alex): Consider referring to these as renderers to avoid
|
|
59
|
+
// overloading "widget"
|
|
60
|
+
if (!_.has(widgets, name)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Allow widgets to specify a widget directly or via a function
|
|
65
|
+
if (widgets[name].getWidget) {
|
|
66
|
+
return widgets[name].getWidget();
|
|
67
|
+
}
|
|
68
|
+
return widgets[name].widget;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const getEditor = (name: string): ?Editor => {
|
|
72
|
+
return _.has(editors, name) ? editors[name] : null;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const getTransform = (name: string): ?WidgetTransform => {
|
|
76
|
+
return _.has(widgets, name) ? widgets[name].transform || _.identity : null;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const getVersion = (name: string): ?Version => {
|
|
80
|
+
const widgetInfo = widgets[name];
|
|
81
|
+
if (widgetInfo) {
|
|
82
|
+
return widgets[name].version || {major: 0, minor: 0};
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const getVersionVector = (): {|[string]: Version|} => {
|
|
88
|
+
const version = {};
|
|
89
|
+
_.each(_.keys(widgets), function (name) {
|
|
90
|
+
version[name] = getVersion(name);
|
|
91
|
+
});
|
|
92
|
+
return version;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const getPublicWidgets = (): $ReadOnlyArray<WidgetExports<>> => {
|
|
96
|
+
// TODO(alex): Update underscore.js so that _.pick can take a function.
|
|
97
|
+
return _.pick(
|
|
98
|
+
widgets,
|
|
99
|
+
_.reject(_.keys(widgets), function (name) {
|
|
100
|
+
return widgets[name].hidden;
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const isAccessible = (widgetInfo: WidgetInfo): boolean => {
|
|
106
|
+
const accessible = widgets[widgetInfo.type].accessible;
|
|
107
|
+
if (typeof accessible === "function") {
|
|
108
|
+
return accessible(widgetInfo.options);
|
|
109
|
+
}
|
|
110
|
+
return !!accessible;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const getAllWidgetTypes = (): $ReadOnlyArray<string> => {
|
|
114
|
+
return _.keys(widgets);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const upgradeWidgetInfoToLatestVersion = (
|
|
118
|
+
oldWidgetInfo: WidgetInfo,
|
|
119
|
+
): WidgetInfo => {
|
|
120
|
+
const type = oldWidgetInfo.type;
|
|
121
|
+
// TODO(LP-10707): Remove unnecessary type checking (`type` is a string)
|
|
122
|
+
if (!_.isString(type)) {
|
|
123
|
+
throw new PerseusError(
|
|
124
|
+
"widget type must be a string, but was: " + type,
|
|
125
|
+
Errors.Internal,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
const widgetExports = widgets[type];
|
|
129
|
+
|
|
130
|
+
if (widgetExports == null) {
|
|
131
|
+
// If we have a widget that isn't registered, we can't upgrade it
|
|
132
|
+
// TODO(aria): Figure out what the best thing to do here would be
|
|
133
|
+
return oldWidgetInfo;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Unversioned widgets (pre-July 2014) are all implicitly 0.0
|
|
137
|
+
const initialVersion = oldWidgetInfo.version || {major: 0, minor: 0};
|
|
138
|
+
const latestVersion = widgetExports.version || {major: 0, minor: 0};
|
|
139
|
+
|
|
140
|
+
// If the widget version is later than what we understand (major
|
|
141
|
+
// version is higher than latest, or major versions are equal and minor
|
|
142
|
+
// version is higher than latest), don't perform any upgrades.
|
|
143
|
+
if (
|
|
144
|
+
initialVersion.major > latestVersion.major ||
|
|
145
|
+
(initialVersion.major === latestVersion.major &&
|
|
146
|
+
initialVersion.minor > latestVersion.minor)
|
|
147
|
+
) {
|
|
148
|
+
return oldWidgetInfo;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// We do a clone here so that it's safe to mutate the input parameter
|
|
152
|
+
// in propUpgrades functions (which I will probably accidentally do at
|
|
153
|
+
// some point, and we would like to not break when that happens).
|
|
154
|
+
let newEditorProps = _.clone(oldWidgetInfo.options) || {};
|
|
155
|
+
|
|
156
|
+
const upgradePropsMap = widgetExports.propUpgrades || {};
|
|
157
|
+
|
|
158
|
+
// Empty props usually mean a newly created widget by the editor,
|
|
159
|
+
// and are always considerered up-to-date.
|
|
160
|
+
// Mostly, we'd rather not run upgrade functions on props that are
|
|
161
|
+
// not complete.
|
|
162
|
+
if (_.keys(newEditorProps).length !== 0) {
|
|
163
|
+
// We loop through all the versions after the current version of
|
|
164
|
+
// the loaded widget, up to and including the latest version of the
|
|
165
|
+
// loaded widget, and run the upgrade function to bring our loaded
|
|
166
|
+
// widget's props up to that version.
|
|
167
|
+
// There is a little subtlety here in that we call
|
|
168
|
+
// upgradePropsMap[1] to upgrade *to* version 1,
|
|
169
|
+
// (not from version 1).
|
|
170
|
+
for (
|
|
171
|
+
let nextVersion = initialVersion.major + 1;
|
|
172
|
+
nextVersion <= latestVersion.major;
|
|
173
|
+
nextVersion++
|
|
174
|
+
) {
|
|
175
|
+
if (upgradePropsMap[String(nextVersion)]) {
|
|
176
|
+
newEditorProps =
|
|
177
|
+
upgradePropsMap[String(nextVersion)](newEditorProps);
|
|
178
|
+
} else {
|
|
179
|
+
// This is a Log.error because it is unlikely to be hit in
|
|
180
|
+
// local testing, and a Log.error is slightly less scary in
|
|
181
|
+
// prod than a `throw new Error`
|
|
182
|
+
Log.error(
|
|
183
|
+
"No upgrade found for widget. Cannot render.",
|
|
184
|
+
Errors.Internal,
|
|
185
|
+
{
|
|
186
|
+
loggedMetadata: {
|
|
187
|
+
type,
|
|
188
|
+
fromMajorVersion: nextVersion - 1,
|
|
189
|
+
toMajorVersion: nextVersion,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
);
|
|
193
|
+
// But try to keep going anyways (yolo!)
|
|
194
|
+
// (Throwing an error here would just break the page
|
|
195
|
+
// silently anyways, so that doesn't seem much better
|
|
196
|
+
// than a halfhearted attempt to continue, however
|
|
197
|
+
// shallow...)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Minor version upgrades (eg. new optional props) don't have
|
|
203
|
+
// transform functions. Instead, we fill in the new props with their
|
|
204
|
+
// defaults.
|
|
205
|
+
const defaultProps = type in editors ? editors[type].defaultProps : {};
|
|
206
|
+
newEditorProps = {
|
|
207
|
+
...defaultProps,
|
|
208
|
+
...newEditorProps,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
let alignment = oldWidgetInfo.alignment;
|
|
212
|
+
|
|
213
|
+
// Widgets that support multiple alignments will "lock in" the
|
|
214
|
+
// alignment to the alignment that would be listed first in the
|
|
215
|
+
// select box. If the widget only supports one alignment, the
|
|
216
|
+
// alignment value will likely just end up as "default".
|
|
217
|
+
if (alignment == null || alignment === "default") {
|
|
218
|
+
alignment = getSupportedAlignments(type)[0];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
let widgetStatic = oldWidgetInfo.static;
|
|
222
|
+
|
|
223
|
+
if (widgetStatic == null) {
|
|
224
|
+
widgetStatic = DEFAULT_STATIC;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return _.extend({}, oldWidgetInfo, {
|
|
228
|
+
// maintain other info, like type
|
|
229
|
+
// After upgrading we guarantee that the version is up-to-date
|
|
230
|
+
version: latestVersion,
|
|
231
|
+
// Default graded to true (so null/undefined becomes true):
|
|
232
|
+
graded: oldWidgetInfo.graded != null ? oldWidgetInfo.graded : true,
|
|
233
|
+
alignment: alignment,
|
|
234
|
+
static: widgetStatic,
|
|
235
|
+
options: newEditorProps,
|
|
236
|
+
});
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export const getRendererPropsForWidgetInfo = (
|
|
240
|
+
widgetInfo: WidgetInfo,
|
|
241
|
+
problemNum?: number,
|
|
242
|
+
): $FlowFixMe => {
|
|
243
|
+
const type = widgetInfo.type;
|
|
244
|
+
const widgetExports = widgets[type];
|
|
245
|
+
if (widgetExports == null) {
|
|
246
|
+
// The widget is not a registered widget
|
|
247
|
+
// It shouldn't matter what we return here, but for consistency
|
|
248
|
+
// we return the untransformed options, as if the widget did
|
|
249
|
+
// not have a transform defined.
|
|
250
|
+
return widgetInfo.options;
|
|
251
|
+
}
|
|
252
|
+
let transform;
|
|
253
|
+
if (widgetInfo.static) {
|
|
254
|
+
// There aren't a lot of real places where we'll have to default to
|
|
255
|
+
// _.identity, but it's theoretically possible if someone changes
|
|
256
|
+
// the JSON manually / we have to back out static support for a
|
|
257
|
+
// widget.
|
|
258
|
+
transform = getStaticTransform(type) || _.identity;
|
|
259
|
+
} else {
|
|
260
|
+
transform = widgetExports.transform || _.identity;
|
|
261
|
+
}
|
|
262
|
+
// widgetInfo.options are the widgetEditor's props:
|
|
263
|
+
return transform(widgetInfo.options, problemNum);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
export const traverseChildWidgets = (
|
|
267
|
+
widgetInfo: WidgetInfo,
|
|
268
|
+
traverseRenderer: $FlowFixMe,
|
|
269
|
+
): WidgetInfo => {
|
|
270
|
+
if (!traverseRenderer) {
|
|
271
|
+
throw new PerseusError(
|
|
272
|
+
"traverseRenderer must be provided, but was not",
|
|
273
|
+
Errors.Internal,
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (!widgetInfo || !widgetInfo.type || !widgets[widgetInfo.type]) {
|
|
278
|
+
return widgetInfo;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const widgetExports = widgets[widgetInfo.type];
|
|
282
|
+
const props = widgetInfo.options;
|
|
283
|
+
|
|
284
|
+
if (widgetExports.traverseChildWidgets && props) {
|
|
285
|
+
const newProps = widgetExports.traverseChildWidgets(
|
|
286
|
+
props,
|
|
287
|
+
traverseRenderer,
|
|
288
|
+
);
|
|
289
|
+
return _.extend({}, widgetInfo, {options: newProps});
|
|
290
|
+
}
|
|
291
|
+
return widgetInfo;
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Handling for the optional alignments for widgets
|
|
296
|
+
* See widget-container.jsx for details on how alignments are implemented.
|
|
297
|
+
*/
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Returns the list of supported alignments for the given (string) widget
|
|
301
|
+
* type. This is used primarily at editing time to display the choices
|
|
302
|
+
* for the user.
|
|
303
|
+
*
|
|
304
|
+
* Supported alignments are given as an array of strings in the exports of
|
|
305
|
+
* a widget's module.
|
|
306
|
+
*/
|
|
307
|
+
export const getSupportedAlignments = (
|
|
308
|
+
type: string,
|
|
309
|
+
): $ReadOnlyArray<Alignment> => {
|
|
310
|
+
const widgetExport = widgets[type];
|
|
311
|
+
return (
|
|
312
|
+
(widgetExport && widgetExport.supportedAlignments) ||
|
|
313
|
+
DEFAULT_SUPPORTED_ALIGNMENTS
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* For the given (string) widget type, determine the default alignment for
|
|
319
|
+
* the widget. This is used at rendering time to go from "default" alignment
|
|
320
|
+
* to the actual alignment displayed on the screen.
|
|
321
|
+
*
|
|
322
|
+
* The default alignment is given either as a string (called
|
|
323
|
+
* `defaultAlignment`) or a function (called `getDefaultAlignment`) on
|
|
324
|
+
* the exports of a widget's module.
|
|
325
|
+
*/
|
|
326
|
+
export const getDefaultAlignment = (type: string): Alignment => {
|
|
327
|
+
const widgetExports = widgets[type];
|
|
328
|
+
let alignment;
|
|
329
|
+
if (!widgetExports) {
|
|
330
|
+
return DEFAULT_ALIGNMENT;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (widgetExports.getDefaultAlignment) {
|
|
334
|
+
alignment = widgetExports.getDefaultAlignment();
|
|
335
|
+
} else {
|
|
336
|
+
alignment = widgetExports.defaultAlignment;
|
|
337
|
+
}
|
|
338
|
+
return alignment || DEFAULT_ALIGNMENT;
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const validAlignments: $ReadOnlyArray<Alignment> = [
|
|
342
|
+
"block",
|
|
343
|
+
"inline-block",
|
|
344
|
+
"inline",
|
|
345
|
+
"float-left",
|
|
346
|
+
"float-right",
|
|
347
|
+
"full-width",
|
|
348
|
+
];
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Used at startup to fail fast if an alignment given by a widget is
|
|
352
|
+
* invalid.
|
|
353
|
+
*/
|
|
354
|
+
// TODO(alex): Change this to run as a testcase (vs. being run at runtime)
|
|
355
|
+
// TODO(LP-10707): I think this can be completely removed because our Flow types
|
|
356
|
+
// enforce this!
|
|
357
|
+
export const validateAlignments = () => {
|
|
358
|
+
_.each(widgets, function (widgetInfo) {
|
|
359
|
+
if (
|
|
360
|
+
widgetInfo.defaultAlignment &&
|
|
361
|
+
!_.contains(validAlignments, widgetInfo.defaultAlignment)
|
|
362
|
+
) {
|
|
363
|
+
throw new PerseusError(
|
|
364
|
+
"Widget '" +
|
|
365
|
+
widgetInfo.displayName +
|
|
366
|
+
"' has an invalid defaultAlignment value: " +
|
|
367
|
+
widgetInfo.defaultAlignment,
|
|
368
|
+
Errors.InvalidInput,
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (widgetInfo.supportedAlignments) {
|
|
373
|
+
const unknownAlignments = _.difference(
|
|
374
|
+
widgetInfo.supportedAlignments,
|
|
375
|
+
validAlignments,
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
if (unknownAlignments.length) {
|
|
379
|
+
throw new PerseusError(
|
|
380
|
+
"Widget '" +
|
|
381
|
+
widgetInfo.displayName +
|
|
382
|
+
"' has an invalid value for supportedAlignments: " +
|
|
383
|
+
unknownAlignments.join(" "),
|
|
384
|
+
Errors.InvalidInput,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Handling for static mode for widgets that support it.
|
|
393
|
+
*/
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Returns true iff the widget supports static mode.
|
|
397
|
+
* A widget implicitly supports static mode if it exports a
|
|
398
|
+
* staticTransform function.
|
|
399
|
+
*/
|
|
400
|
+
export const supportsStaticMode = (type: string): boolean => {
|
|
401
|
+
const widgetInfo = widgets[type];
|
|
402
|
+
return widgetInfo && widgetInfo.staticTransform != null;
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Return the staticTransform function used to convert the editorProps to
|
|
407
|
+
* the rendered widget state.
|
|
408
|
+
*/
|
|
409
|
+
export const getStaticTransform = (type: string): ?WidgetTransform => {
|
|
410
|
+
const widgetInfo = widgets[type];
|
|
411
|
+
return widgetInfo && widgetInfo.staticTransform;
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Returns the tracking option for the widget. The default is "",
|
|
416
|
+
* which means simply to track interactions once. The other available
|
|
417
|
+
* option is "all" which means to track all interactions.
|
|
418
|
+
*/
|
|
419
|
+
export const getTracking = (type: string): Tracking => {
|
|
420
|
+
const widgetExport = widgets[type];
|
|
421
|
+
return (widgetExport && widgetExport.tracking) || DEFAULT_TRACKING;
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Returns true if this widget can include lintable markdown text
|
|
426
|
+
* and supports a highlightLint prop, or false otherwise.
|
|
427
|
+
*/
|
|
428
|
+
export const isLintable = (type: string): boolean => {
|
|
429
|
+
const widgetExports = widgets[type];
|
|
430
|
+
return (widgetExports && widgetExports.isLintable) || DEFAULT_LINTABLE;
|
|
431
|
+
};
|