@khanacademy/perseus 19.1.4 → 19.1.6
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/dist/components/graphie.d.ts +9 -1
- package/dist/es/index.js +255 -122
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +261 -126
- package/dist/index.js.map +1 -1
- package/dist/interactive2/interactive-util.d.ts +1 -1
- package/dist/interactive2/movable-point-options.d.ts +5 -4
- package/dist/interactive2/movable.d.ts +90 -13
- package/dist/interactive2/types.d.ts +6 -0
- package/dist/util/graphie.d.ts +5 -0
- package/dist/widgets/expression.d.ts +4 -4
- package/package.json +11 -8
- package/.babelrc.js +0 -8
- package/.eslintrc.js +0 -12
- package/CHANGELOG.md +0 -1422
- package/src/__stories__/article-renderer.stories.tsx +0 -100
- package/src/__stories__/hints-renderer.stories.tsx +0 -55
- package/src/__stories__/server-item-renderer.stories.tsx +0 -44
- package/src/__testdata__/article-renderer.testdata.ts +0 -212
- package/src/__testdata__/graphie.testdata.ts +0 -51
- package/src/__testdata__/renderer.testdata.ts +0 -274
- package/src/__testdata__/server-item-renderer.testdata.ts +0 -171
- package/src/__tests__/__snapshots__/perseus-markdown.test.ts.snap +0 -507
- package/src/__tests__/__snapshots__/renderer.test.tsx.snap +0 -525
- package/src/__tests__/__snapshots__/server-item-renderer.test.tsx.snap +0 -285
- package/src/__tests__/a11y.test.ts +0 -303
- package/src/__tests__/article-renderer.test.tsx +0 -130
- package/src/__tests__/error-boundary.test.tsx +0 -76
- package/src/__tests__/extract-perseus-data.test.ts +0 -1027
- package/src/__tests__/init.test.ts +0 -10
- package/src/__tests__/jipt-paragraphs.test.ts +0 -32
- package/src/__tests__/mock-asset-loading-widget.tsx +0 -53
- package/src/__tests__/mock-widget.tsx +0 -33
- package/src/__tests__/perseus-markdown.test.ts +0 -764
- package/src/__tests__/renderability.test.ts +0 -480
- package/src/__tests__/renderer-api.test.tsx +0 -302
- package/src/__tests__/renderer.test.tsx +0 -1816
- package/src/__tests__/server-item-renderer.test.tsx +0 -582
- package/src/__tests__/test-items/image-item.ts +0 -24
- package/src/__tests__/test-items/input-number-1-item.ts +0 -26
- package/src/__tests__/test-items/input-number-2-item.ts +0 -38
- package/src/__tests__/test-items/table-item.ts +0 -29
- package/src/__tests__/tex-wrangler.test.ts +0 -82
- package/src/__tests__/util.test.ts +0 -40
- package/src/__tests__/widget-container.test.tsx +0 -126
- package/src/__tests__/widgets.test.ts +0 -91
- package/src/a11y.ts +0 -45
- package/src/all-widgets.ts +0 -9
- package/src/article-renderer.tsx +0 -237
- package/src/asset-context.ts +0 -30
- package/src/basic-widgets.ts +0 -16
- package/src/components/__stories__/button-group.stories.tsx +0 -55
- package/src/components/__stories__/fixed-to-responsive.stories.tsx +0 -141
- package/src/components/__stories__/graph.stories.tsx +0 -28
- package/src/components/__stories__/graphie.stories.tsx +0 -33
- package/src/components/__stories__/hud.stories.tsx +0 -35
- package/src/components/__stories__/icon.stories.tsx +0 -63
- package/src/components/__stories__/image-loader.stories.tsx +0 -63
- package/src/components/__stories__/info-tip.stories.tsx +0 -31
- package/src/components/__stories__/inline-icon.stories.tsx +0 -40
- package/src/components/__stories__/input-with-examples.stories.tsx +0 -53
- package/src/components/__stories__/lint.stories.tsx +0 -86
- package/src/components/__stories__/math-input.stories.tsx +0 -43
- package/src/components/__stories__/math-output.stories.tsx +0 -23
- package/src/components/__stories__/multi-button-group.stories.tsx +0 -66
- package/src/components/__stories__/number-input.stories.tsx +0 -41
- package/src/components/__stories__/prop-check-box.stories.tsx +0 -65
- package/src/components/__stories__/range-input.stories.tsx +0 -29
- package/src/components/__stories__/simple-keypad-input.stories.tsx +0 -27
- package/src/components/__stories__/sortable.stories.tsx +0 -77
- package/src/components/__stories__/stub-tag-editor.stories.tsx +0 -45
- package/src/components/__stories__/svg-image.stories.tsx +0 -74
- package/src/components/__stories__/tex.stories.tsx +0 -23
- package/src/components/__stories__/text-input.stories.tsx +0 -33
- package/src/components/__stories__/text-list-editor.stories.tsx +0 -32
- package/src/components/__stories__/tooltip.stories.tsx +0 -52
- package/src/components/__stories__/zoomable-tex.stories.tsx +0 -41
- package/src/components/__stories__/zoomable.stories.tsx +0 -49
- package/src/components/__testdata__/sorter.testdata.ts +0 -19
- package/src/components/__tests__/__snapshots__/sortable.test.tsx.snap +0 -163
- package/src/components/__tests__/__snapshots__/sorter.test.tsx.snap +0 -265
- package/src/components/__tests__/math-input.test.tsx +0 -147
- package/src/components/__tests__/number-input.test.tsx +0 -128
- package/src/components/__tests__/sortable.test.tsx +0 -269
- package/src/components/__tests__/sorter.test.tsx +0 -114
- package/src/components/__tests__/zoomable.test.tsx +0 -399
- package/src/components/button-group.tsx +0 -130
- package/src/components/constants.ts +0 -14
- package/src/components/fixed-to-responsive.tsx +0 -169
- package/src/components/graph.tsx +0 -444
- package/src/components/graphie-classes.ts +0 -153
- package/src/components/graphie-movables.ts +0 -338
- package/src/components/graphie.tsx +0 -392
- package/src/components/highlighting/highlightable-content.tsx +0 -240
- package/src/components/highlighting/highlights.ts +0 -207
- package/src/components/highlighting/ranges.ts +0 -304
- package/src/components/highlighting/types.ts +0 -73
- package/src/components/highlighting/ui/highlight-renderer.tsx +0 -172
- package/src/components/highlighting/ui/highlight-set-renderer.tsx +0 -185
- package/src/components/highlighting/ui/highlight-tooltip.tsx +0 -141
- package/src/components/highlighting/ui/highlighting-ui.tsx +0 -143
- package/src/components/highlighting/ui/selection-tracker.tsx +0 -186
- package/src/components/highlighting/ui/types.ts +0 -21
- package/src/components/highlighting/ui/util.ts +0 -165
- package/src/components/highlighting/word-indexer.tsx +0 -152
- package/src/components/hud.tsx +0 -167
- package/src/components/icon.tsx +0 -141
- package/src/components/image-loader.tsx +0 -177
- package/src/components/info-tip/info-tip.tsx +0 -144
- package/src/components/info-tip.tsx +0 -39
- package/src/components/inline-icon.tsx +0 -60
- package/src/components/input-with-examples.tsx +0 -249
- package/src/components/lint.tsx +0 -459
- package/src/components/math-input.tsx +0 -474
- package/src/components/math-output.tsx +0 -134
- package/src/components/multi-button-group.tsx +0 -135
- package/src/components/number-input.tsx +0 -249
- package/src/components/prop-check-box.tsx +0 -79
- package/src/components/range-input.tsx +0 -60
- package/src/components/simple-keypad-input.tsx +0 -88
- package/src/components/sortable.tsx +0 -958
- package/src/components/stub-tag-editor.tsx +0 -53
- package/src/components/svg-image.tsx +0 -779
- package/src/components/tex.tsx +0 -67
- package/src/components/text-input.tsx +0 -129
- package/src/components/text-list-editor.tsx +0 -166
- package/src/components/tooltip.tsx +0 -410
- package/src/components/visibility-observer/__stories__/visibility-observer.stories.tsx +0 -141
- package/src/components/visibility-observer/create-visibility-observer.ts +0 -108
- package/src/components/visibility-observer/get-scroll-parent.ts +0 -46
- package/src/components/visibility-observer/visibility-observer.tsx +0 -124
- package/src/components/zoomable-tex.tsx +0 -78
- package/src/components/zoomable.tsx +0 -304
- package/src/components.ts +0 -15
- package/src/definition-context.tsx +0 -59
- package/src/dependencies.ts +0 -40
- package/src/error-boundary.tsx +0 -70
- package/src/extra-widgets.ts +0 -75
- package/src/hint-renderer.tsx +0 -151
- package/src/hints-renderer.tsx +0 -372
- package/src/icon-paths.ts +0 -119
- package/src/index.ts +0 -171
- package/src/init.ts +0 -56
- package/src/interaction-tracker.ts +0 -62
- package/src/interactive2/__tests__/movable-mock.ts +0 -43
- package/src/interactive2/__tests__/movable-point.test.ts +0 -144
- package/src/interactive2/__tests__/movable.test.ts +0 -157
- package/src/interactive2/interactive-util.test.ts +0 -93
- package/src/interactive2/interactive-util.ts +0 -159
- package/src/interactive2/movable-helper-methods.ts +0 -166
- package/src/interactive2/movable-line-options.ts +0 -419
- package/src/interactive2/movable-line.ts +0 -307
- package/src/interactive2/movable-point-options.ts +0 -212
- package/src/interactive2/movable-point.tsx +0 -585
- package/src/interactive2/movable-polygon-options.ts +0 -389
- package/src/interactive2/movable-polygon.ts +0 -360
- package/src/interactive2/movable.ts +0 -279
- package/src/interactive2/objective_.ts +0 -79
- package/src/interactive2/types.ts +0 -2
- package/src/interactive2/wrapped-defaults.ts +0 -87
- package/src/interactive2/wrapped-ellipse.ts +0 -94
- package/src/interactive2/wrapped-line.ts +0 -116
- package/src/interactive2/wrapped-path.ts +0 -51
- package/src/interactive2.ts +0 -70
- package/src/item-version.ts +0 -10
- package/src/jipt-paragraphs.ts +0 -49
- package/src/jquery.mobile.vmouse.js +0 -545
- package/src/loading-context.ts +0 -21
- package/src/logging/log.test.ts +0 -39
- package/src/logging/log.ts +0 -97
- package/src/mixins/__tests__/widget-prop-denylist.test.ts +0 -21
- package/src/mixins/changeable.ts +0 -133
- package/src/mixins/editor-jsonify.ts +0 -13
- package/src/mixins/widget-jsonify-deprecated.ts +0 -23
- package/src/mixins/widget-prop-denylist.ts +0 -48
- package/src/multi-items/__stories__/multi-renderer.stories.tsx +0 -83
- package/src/multi-items/__testdata__/multi-renderer.testdata.ts +0 -172
- package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +0 -1164
- package/src/multi-items/__tests__/items.test.ts +0 -240
- package/src/multi-items/__tests__/multi-renderer.test.tsx +0 -914
- package/src/multi-items/__tests__/prop-type-builders.test.ts +0 -133
- package/src/multi-items/__tests__/shapes.test.ts +0 -43
- package/src/multi-items/__tests__/trees.test.ts +0 -317
- package/src/multi-items/item-types.ts +0 -56
- package/src/multi-items/items.ts +0 -176
- package/src/multi-items/multi-renderer.tsx +0 -569
- package/src/multi-items/prop-type-builders.ts +0 -106
- package/src/multi-items/shape-types.ts +0 -52
- package/src/multi-items/shapes.ts +0 -52
- package/src/multi-items/tree-types.ts +0 -47
- package/src/multi-items/trees.ts +0 -360
- package/src/multi-items.ts +0 -75
- package/src/perseus-api.tsx +0 -188
- package/src/perseus-env.ts +0 -27
- package/src/perseus-error.ts +0 -17
- package/src/perseus-markdown.tsx +0 -405
- package/src/perseus-types.ts +0 -1366
- package/src/perseus-version.ts +0 -9
- package/src/question-paragraph.tsx +0 -34
- package/src/renderability.ts +0 -109
- package/src/renderer.tsx +0 -2000
- package/src/server-item-renderer.tsx +0 -495
- package/src/sigfigs.ts +0 -490
- package/src/styles/articles.less +0 -749
- package/src/styles/constants.ts +0 -94
- package/src/styles/global-constants.ts +0 -343
- package/src/styles/global-styles.ts +0 -306
- package/src/styles/khan-exercise.css +0 -1468
- package/src/styles/media-queries.ts +0 -47
- package/src/styles/perseus-renderer.less +0 -732
- package/src/styles/shared.ts +0 -130
- package/src/styles/util.less +0 -47
- package/src/styles/variables.boostrap.less +0 -277
- package/src/styles/variables.less +0 -56
- package/src/styles/variables.shared.less +0 -340
- package/src/styles/widgets/categorizer.less +0 -38
- package/src/styles/widgets/dropdown.less +0 -3
- package/src/styles/widgets/expression.less +0 -65
- package/src/styles/widgets/graded-group.less +0 -36
- package/src/styles/widgets/grapher.less +0 -12
- package/src/styles/widgets/group.less +0 -19
- package/src/styles/widgets/image.less +0 -54
- package/src/styles/widgets/interactive-graph.less +0 -64
- package/src/styles/widgets/label-image.less +0 -57
- package/src/styles/widgets/matcher.less +0 -8
- package/src/styles/widgets/matrix.less +0 -126
- package/src/styles/widgets/measurer.less +0 -20
- package/src/styles/widgets/orderer.less +0 -98
- package/src/styles/widgets/passage.less +0 -154
- package/src/styles/widgets/plotter.less +0 -31
- package/src/styles/widgets/radio.less +0 -58
- package/src/styles/widgets/reaction-diagram.less +0 -35
- package/src/styles/widgets/sequence.less +0 -5
- package/src/styles/widgets/sortable.less +0 -113
- package/src/styles/widgets/table.less +0 -57
- package/src/styles/widgets/unit.less +0 -15
- package/src/tex-wrangler.ts +0 -182
- package/src/translation-linter.ts +0 -126
- package/src/traversal.ts +0 -138
- package/src/types.ts +0 -513
- package/src/util/a11y.ts +0 -23
- package/src/util/answer-types.test.ts +0 -114
- package/src/util/answer-types.ts +0 -876
- package/src/util/colors.ts +0 -93
- package/src/util/drawing-transform.test.ts +0 -67
- package/src/util/drawing-transform.ts +0 -102
- package/src/util/extract-perseus-data.ts +0 -539
- package/src/util/fix-passage-refs.ts +0 -121
- package/src/util/geometry.test.ts +0 -75
- package/src/util/geometry.ts +0 -312
- package/src/util/graph-bounds.ts +0 -23
- package/src/util/graph-utils.ts +0 -15
- package/src/util/graphie.test.ts +0 -1277
- package/src/util/graphie.ts +0 -1566
- package/src/util/interactive.ts +0 -4172
- package/src/util/interval.ts +0 -4
- package/src/util/katex-preprocess.ts +0 -14
- package/src/util/math.test.ts +0 -11
- package/src/util/math.ts +0 -294
- package/src/util/mobile-native-utils.test.ts +0 -22
- package/src/util/mobile-native-utils.ts +0 -12
- package/src/util/parse-perseus-json.test.ts +0 -10
- package/src/util/parse-perseus-json.ts +0 -13
- package/src/util/react-render.tsx +0 -27
- package/src/util/register-all-widgets-for-testing.ts +0 -13
- package/src/util/scroll-utils.ts +0 -155
- package/src/util/sizing-utils.ts +0 -46
- package/src/util/tex.ts +0 -73
- package/src/util/url-utils.ts +0 -34
- package/src/util.graphie.test.ts +0 -45
- package/src/util.graphie.ts +0 -59
- package/src/util.ts +0 -966
- package/src/version.ts +0 -10
- package/src/widget-container.tsx +0 -199
- package/src/widgets/__stories__/categorizer.stories.tsx +0 -14
- package/src/widgets/__stories__/cs-program.stories.tsx +0 -14
- package/src/widgets/__stories__/definition.stories.tsx +0 -114
- package/src/widgets/__stories__/dropdown.stories.tsx +0 -14
- package/src/widgets/__stories__/explanation.stories.tsx +0 -18
- package/src/widgets/__stories__/expression.stories.tsx +0 -145
- package/src/widgets/__stories__/graded-group-set.stories.tsx +0 -31
- package/src/widgets/__stories__/graded-group.stories.tsx +0 -31
- package/src/widgets/__stories__/grapher.stories.tsx +0 -46
- package/src/widgets/__stories__/group.stories.tsx +0 -14
- package/src/widgets/__stories__/iframe.stories.tsx +0 -14
- package/src/widgets/__stories__/image.stories.tsx +0 -85
- package/src/widgets/__stories__/input-number.stories.tsx +0 -101
- package/src/widgets/__stories__/interaction.stories.tsx +0 -14
- package/src/widgets/__stories__/interactive-graph.stories.tsx +0 -60
- package/src/widgets/__stories__/label-image.stories.tsx +0 -130
- package/src/widgets/__stories__/matcher.stories.tsx +0 -14
- package/src/widgets/__stories__/matrix.stories.tsx +0 -14
- package/src/widgets/__stories__/number-line.stories.tsx +0 -14
- package/src/widgets/__stories__/numeric-input.stories.tsx +0 -105
- package/src/widgets/__stories__/orderer.stories.tsx +0 -18
- package/src/widgets/__stories__/passage-ref.stories.tsx +0 -33
- package/src/widgets/__stories__/passage.stories.tsx +0 -39
- package/src/widgets/__stories__/plotter.stories.tsx +0 -27
- package/src/widgets/__stories__/radio.stories.tsx +0 -86
- package/src/widgets/__stories__/random-widgets.stories.tsx +0 -59
- package/src/widgets/__stories__/sorter.stories.tsx +0 -14
- package/src/widgets/__stories__/test-keypad-context-wrapper.tsx +0 -66
- package/src/widgets/__stories__/unit.stories.tsx +0 -28
- package/src/widgets/__stories__/video-transcript-link.stories.tsx +0 -23
- package/src/widgets/__stories__/video.stories.tsx +0 -22
- package/src/widgets/__testdata__/base-radio.testdata.ts +0 -21
- package/src/widgets/__testdata__/categorizer.testdata.ts +0 -89
- package/src/widgets/__testdata__/cs-program.testdata.ts +0 -27
- package/src/widgets/__testdata__/dropdown.testdata.ts +0 -71
- package/src/widgets/__testdata__/explanation.testdata.ts +0 -85
- package/src/widgets/__testdata__/expression.testdata.ts +0 -198
- package/src/widgets/__testdata__/graded-group-set.testdata.ts +0 -356
- package/src/widgets/__testdata__/graded-group.testdata.ts +0 -77
- package/src/widgets/__testdata__/grapher.testdata.ts +0 -374
- package/src/widgets/__testdata__/group.testdata.ts +0 -153
- package/src/widgets/__testdata__/iframe.testdata.ts +0 -35
- package/src/widgets/__testdata__/image.testdata.ts +0 -36
- package/src/widgets/__testdata__/input-number.testdata.ts +0 -72
- package/src/widgets/__testdata__/interaction.testdata.ts +0 -262
- package/src/widgets/__testdata__/interactive-graph-random.testdata.ts +0 -269
- package/src/widgets/__testdata__/interactive-graph.testdata.ts +0 -644
- package/src/widgets/__testdata__/label-image.testdata.ts +0 -308
- package/src/widgets/__testdata__/matcher.testdata.ts +0 -33
- package/src/widgets/__testdata__/matrix.testdata.ts +0 -28
- package/src/widgets/__testdata__/number-line.testdata.ts +0 -30
- package/src/widgets/__testdata__/numeric-input.testdata.ts +0 -265
- package/src/widgets/__testdata__/orderer.testdata.ts +0 -123
- package/src/widgets/__testdata__/passage-ref.testdata.ts +0 -101
- package/src/widgets/__testdata__/passage.testdata.ts +0 -95
- package/src/widgets/__testdata__/plotter.testdata.ts +0 -36
- package/src/widgets/__testdata__/radio.testdata.ts +0 -324
- package/src/widgets/__testdata__/randomizers.ts +0 -51
- package/src/widgets/__testdata__/sorter.testdata.ts +0 -19
- package/src/widgets/__testdata__/video.testdata.ts +0 -61
- package/src/widgets/__tests__/__snapshots__/categorizer.test.ts.snap +0 -940
- package/src/widgets/__tests__/__snapshots__/cs-program.test.ts.snap +0 -67
- package/src/widgets/__tests__/__snapshots__/definition.test.ts.snap +0 -97
- package/src/widgets/__tests__/__snapshots__/dropdown.test.ts.snap +0 -133
- package/src/widgets/__tests__/__snapshots__/explanation.test.ts.snap +0 -425
- package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt.test.ts.snap +0 -729
- package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +0 -339
- package/src/widgets/__tests__/__snapshots__/graded-group.test.ts.snap +0 -801
- package/src/widgets/__tests__/__snapshots__/grapher.test.ts.snap +0 -2673
- package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +0 -1211
- package/src/widgets/__tests__/__snapshots__/iframe.test.ts.snap +0 -61
- package/src/widgets/__tests__/__snapshots__/image.test.ts.snap +0 -378
- package/src/widgets/__tests__/__snapshots__/input-number.test.ts.snap +0 -495
- package/src/widgets/__tests__/__snapshots__/interaction.test.ts.snap +0 -292
- package/src/widgets/__tests__/__snapshots__/interactive-graph.test.ts.snap +0 -2889
- package/src/widgets/__tests__/__snapshots__/matcher.test.tsx.snap +0 -883
- package/src/widgets/__tests__/__snapshots__/matrix.test.ts.snap +0 -514
- package/src/widgets/__tests__/__snapshots__/number-line.test.ts.snap +0 -814
- package/src/widgets/__tests__/__snapshots__/numeric-input.test.ts.snap +0 -470
- package/src/widgets/__tests__/__snapshots__/orderer.test.ts.snap +0 -243
- package/src/widgets/__tests__/__snapshots__/passage-ref.test.ts.snap +0 -434
- package/src/widgets/__tests__/__snapshots__/passage.test.tsx.snap +0 -1956
- package/src/widgets/__tests__/__snapshots__/plotter.test.tsx.snap +0 -827
- package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +0 -4237
- package/src/widgets/__tests__/__snapshots__/unit.test.tsx.snap +0 -20
- package/src/widgets/__tests__/__snapshots__/video.test.ts.snap +0 -153
- package/src/widgets/__tests__/categorizer.test.ts +0 -203
- package/src/widgets/__tests__/cs-program.test.ts +0 -43
- package/src/widgets/__tests__/definition.test.ts +0 -136
- package/src/widgets/__tests__/dropdown.test.ts +0 -105
- package/src/widgets/__tests__/explanation.test.ts +0 -198
- package/src/widgets/__tests__/expression-mobile.test.tsx +0 -363
- package/src/widgets/__tests__/expression.test.tsx +0 -484
- package/src/widgets/__tests__/graded-group-set-jipt.test.ts +0 -30
- package/src/widgets/__tests__/graded-group-set.test.ts +0 -288
- package/src/widgets/__tests__/graded-group.test.ts +0 -232
- package/src/widgets/__tests__/grapher.cypress.ts +0 -576
- package/src/widgets/__tests__/grapher.test.ts +0 -33
- package/src/widgets/__tests__/group.test.tsx +0 -510
- package/src/widgets/__tests__/iframe.test.ts +0 -45
- package/src/widgets/__tests__/image.test.ts +0 -62
- package/src/widgets/__tests__/input-number.test.ts +0 -354
- package/src/widgets/__tests__/interaction.test.ts +0 -31
- package/src/widgets/__tests__/interactive-graph.cypress.ts +0 -815
- package/src/widgets/__tests__/interactive-graph.test.ts +0 -106
- package/src/widgets/__tests__/matcher.test.tsx +0 -176
- package/src/widgets/__tests__/matrix.test.ts +0 -82
- package/src/widgets/__tests__/number-line.test.ts +0 -79
- package/src/widgets/__tests__/numeric-input.test.ts +0 -575
- package/src/widgets/__tests__/orderer.test.ts +0 -73
- package/src/widgets/__tests__/passage-ref.test.ts +0 -168
- package/src/widgets/__tests__/passage.test.tsx +0 -224
- package/src/widgets/__tests__/plotter.test.tsx +0 -69
- package/src/widgets/__tests__/radio/base-radio.test.tsx +0 -470
- package/src/widgets/__tests__/radio/choice-icon.test.tsx +0 -90
- package/src/widgets/__tests__/radio/choice.test.tsx +0 -310
- package/src/widgets/__tests__/radio/focus-ring.test.tsx +0 -22
- package/src/widgets/__tests__/radio/option-status.test.tsx +0 -77
- package/src/widgets/__tests__/radio.test.ts +0 -807
- package/src/widgets/__tests__/randomizers.test.ts +0 -53
- package/src/widgets/__tests__/renderQuestion.tsx +0 -101
- package/src/widgets/__tests__/unit.test.tsx +0 -168
- package/src/widgets/__tests__/video.test.ts +0 -69
- package/src/widgets/categorizer.tsx +0 -344
- package/src/widgets/cs-program.tsx +0 -227
- package/src/widgets/definition.tsx +0 -121
- package/src/widgets/deprecated-standin/__stories__/deprecated-standin.stories.tsx +0 -30
- package/src/widgets/deprecated-standin/__tests__/__snapshots__/deprecated-standin.test.ts.snap +0 -70
- package/src/widgets/deprecated-standin/__tests__/deprecated-standin.test.ts +0 -93
- package/src/widgets/deprecated-standin/index.tsx +0 -58
- package/src/widgets/dropdown.tsx +0 -142
- package/src/widgets/explanation.tsx +0 -317
- package/src/widgets/expression.tsx +0 -707
- package/src/widgets/graded-group-answer-bar.tsx +0 -142
- package/src/widgets/graded-group-set.tsx +0 -338
- package/src/widgets/graded-group.tsx +0 -471
- package/src/widgets/grapher/util.tsx +0 -799
- package/src/widgets/grapher.tsx +0 -660
- package/src/widgets/group.tsx +0 -195
- package/src/widgets/iframe.tsx +0 -195
- package/src/widgets/image.tsx +0 -312
- package/src/widgets/input-number.tsx +0 -412
- package/src/widgets/interaction/arrow-picker.tsx +0 -38
- package/src/widgets/interaction/color-picker.tsx +0 -69
- package/src/widgets/interaction/dash-picker.tsx +0 -45
- package/src/widgets/interaction/element-container.tsx +0 -100
- package/src/widgets/interaction.tsx +0 -826
- package/src/widgets/interactive-graph.tsx +0 -2597
- package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -91
- package/src/widgets/label-image/__stories__/answer-pill.stories.tsx +0 -62
- package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -171
- package/src/widgets/label-image/__tests__/answer-pill.test.tsx +0 -75
- package/src/widgets/label-image/__tests__/hide-answers-toggle.test.tsx +0 -39
- package/src/widgets/label-image/answer-choices.tsx +0 -88
- package/src/widgets/label-image/answer-pill.tsx +0 -103
- package/src/widgets/label-image/hide-answers-toggle.tsx +0 -40
- package/src/widgets/label-image/marker.tsx +0 -268
- package/src/widgets/label-image/strings.ts +0 -5
- package/src/widgets/label-image/types.ts +0 -27
- package/src/widgets/label-image.test.ts +0 -928
- package/src/widgets/label-image.tsx +0 -819
- package/src/widgets/lights-puzzle.tsx +0 -359
- package/src/widgets/matcher.tsx +0 -298
- package/src/widgets/matrix.tsx +0 -634
- package/src/widgets/measurer.tsx +0 -222
- package/src/widgets/molecule/__tests__/layout.test.ts +0 -162
- package/src/widgets/molecule/__tests__/parser.test.ts +0 -155
- package/src/widgets/molecule/molecule-drawing.ts +0 -173
- package/src/widgets/molecule/molecule-layout.ts +0 -362
- package/src/widgets/molecule/smiles-parser.ts +0 -296
- package/src/widgets/molecule.tsx +0 -172
- package/src/widgets/number-line.tsx +0 -862
- package/src/widgets/numeric-input.tsx +0 -576
- package/src/widgets/orderer.tsx +0 -786
- package/src/widgets/passage/__tests__/passage-markdown.test.ts +0 -376
- package/src/widgets/passage/passage-markdown.tsx +0 -475
- package/src/widgets/passage-ref-target.tsx +0 -76
- package/src/widgets/passage-ref.tsx +0 -213
- package/src/widgets/passage.tsx +0 -582
- package/src/widgets/plotter.tsx +0 -1194
- package/src/widgets/radio/__stories__/base-radio.stories.tsx +0 -196
- package/src/widgets/radio/__stories__/choice-icon.stories.tsx +0 -166
- package/src/widgets/radio/__stories__/choice-none-above.stories.tsx +0 -40
- package/src/widgets/radio/__stories__/choice.stories.tsx +0 -137
- package/src/widgets/radio/__stories__/focus-ring.stories.tsx +0 -43
- package/src/widgets/radio/__stories__/option-status.stories.tsx +0 -103
- package/src/widgets/radio/base-radio.tsx +0 -511
- package/src/widgets/radio/choice-a11y-text.ts +0 -61
- package/src/widgets/radio/choice-icon/choice-icon.tsx +0 -218
- package/src/widgets/radio/choice-icon/cross-out-line.tsx +0 -51
- package/src/widgets/radio/choice-icon/shared-styles.ts +0 -15
- package/src/widgets/radio/choice-none-above.tsx +0 -56
- package/src/widgets/radio/choice.jsx.fixture.ts +0 -45
- package/src/widgets/radio/choice.tsx +0 -359
- package/src/widgets/radio/focus-ring.tsx +0 -57
- package/src/widgets/radio/option-status.tsx +0 -98
- package/src/widgets/radio/util.test.ts +0 -18
- package/src/widgets/radio/util.ts +0 -31
- package/src/widgets/radio/widget.tsx +0 -520
- package/src/widgets/radio.ts +0 -157
- package/src/widgets/reaction-diagram.tsx +0 -144
- package/src/widgets/sequence.tsx +0 -142
- package/src/widgets/simulator.tsx +0 -829
- package/src/widgets/sorter.tsx +0 -155
- package/src/widgets/table.tsx +0 -393
- package/src/widgets/unit.tsx +0 -374
- package/src/widgets/video-transcript-link.test.tsx +0 -213
- package/src/widgets/video-transcript-link.tsx +0 -84
- package/src/widgets/video.tsx +0 -135
- package/src/widgets.ts +0 -489
- package/src/zoom.ts +0 -555
- package/tsconfig-build.json +0 -26
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,707 +0,0 @@
|
|
|
1
|
-
import * as KAS from "@khanacademy/kas";
|
|
2
|
-
import {KeyArray, KeypadInput, KeypadType} from "@khanacademy/math-input";
|
|
3
|
-
import {linterContextDefault} from "@khanacademy/perseus-linter";
|
|
4
|
-
import {View} from "@khanacademy/wonder-blocks-core";
|
|
5
|
-
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
6
|
-
import Tooltip from "@khanacademy/wonder-blocks-tooltip";
|
|
7
|
-
import classNames from "classnames";
|
|
8
|
-
import * as React from "react";
|
|
9
|
-
import _ from "underscore";
|
|
10
|
-
|
|
11
|
-
import MathInput from "../components/math-input";
|
|
12
|
-
import {useDependencies} from "../dependencies";
|
|
13
|
-
import {Errors as PerseusErrors, Log} from "../logging/log";
|
|
14
|
-
import * as Changeable from "../mixins/changeable";
|
|
15
|
-
import {ApiOptions, ClassNames as ApiClassNames} from "../perseus-api";
|
|
16
|
-
import a11y from "../util/a11y";
|
|
17
|
-
import KhanAnswerTypes from "../util/answer-types";
|
|
18
|
-
|
|
19
|
-
import type {DependenciesContext} from "../dependencies";
|
|
20
|
-
import type {
|
|
21
|
-
PerseusExpressionWidgetOptions,
|
|
22
|
-
PerseusExpressionAnswerForm,
|
|
23
|
-
} from "../perseus-types";
|
|
24
|
-
import type {
|
|
25
|
-
APIOptions,
|
|
26
|
-
PerseusScore,
|
|
27
|
-
WidgetExports,
|
|
28
|
-
WidgetProps,
|
|
29
|
-
} from "../types";
|
|
30
|
-
import type {Keys as Key, KeypadConfiguration} from "@khanacademy/math-input";
|
|
31
|
-
import type {PropsFor} from "@khanacademy/wonder-blocks-core";
|
|
32
|
-
|
|
33
|
-
type InputPath = ReadonlyArray<string>;
|
|
34
|
-
|
|
35
|
-
const ERROR_TITLE = i18n._("Oops!");
|
|
36
|
-
const ERROR_MESSAGE = i18n._("Sorry, I don't understand that!");
|
|
37
|
-
|
|
38
|
-
const anglicizeOperators = (tex: string): string => {
|
|
39
|
-
// sen is used instead of sin in some languages, e.g. Portuguese.
|
|
40
|
-
// To ensure that answers in various languages are graded correctly, we
|
|
41
|
-
// convert operators to their Englishy forms.
|
|
42
|
-
return tex.replace(/\\operatorname{sen}/g, "\\sin ");
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const normalizeTex = (tex: string): string => {
|
|
46
|
-
return anglicizeOperators(tex);
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const deriveKeypadVersion = (apiOptions: APIOptions) => {
|
|
50
|
-
// We can derive which version of the keypad is in use. This is
|
|
51
|
-
// a bit tricky, but this code will be relatively short-lived
|
|
52
|
-
// as we coalesce onto the new, v2 Keypad, at which point we
|
|
53
|
-
// can remove this `virtualKeypadVersion` field entirely.
|
|
54
|
-
return apiOptions.nativeKeypadProxy != null
|
|
55
|
-
? "REACT_NATIVE_KEYPAD"
|
|
56
|
-
: "MATH_INPUT_KEYPAD_V2";
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
type Rubric = PerseusExpressionWidgetOptions;
|
|
60
|
-
|
|
61
|
-
type RenderProps = {
|
|
62
|
-
buttonSets: PerseusExpressionWidgetOptions["buttonSets"];
|
|
63
|
-
buttonsVisible?: PerseusExpressionWidgetOptions["buttonsVisible"];
|
|
64
|
-
functions: PerseusExpressionWidgetOptions["functions"];
|
|
65
|
-
times: PerseusExpressionWidgetOptions["times"];
|
|
66
|
-
keypadConfiguration: ReturnType<typeof keypadConfigurationForProps>;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
type ExternalProps = WidgetProps<RenderProps, Rubric>;
|
|
70
|
-
|
|
71
|
-
export type Props = ExternalProps &
|
|
72
|
-
Partial<React.ContextType<typeof DependenciesContext>> & {
|
|
73
|
-
apiOptions: NonNullable<ExternalProps["apiOptions"]>;
|
|
74
|
-
buttonSets: NonNullable<ExternalProps["buttonSets"]>;
|
|
75
|
-
functions: NonNullable<ExternalProps["functions"]>;
|
|
76
|
-
linterContext: NonNullable<ExternalProps["linterContext"]>;
|
|
77
|
-
onBlur: NonNullable<ExternalProps["onBlur"]>;
|
|
78
|
-
onFocus: NonNullable<ExternalProps["onFocus"]>;
|
|
79
|
-
times: NonNullable<ExternalProps["times"]>;
|
|
80
|
-
value: string;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export type ExpressionState = {
|
|
84
|
-
invalid: boolean;
|
|
85
|
-
showErrorTooltip: boolean;
|
|
86
|
-
showErrorStyle: boolean;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
type DefaultProps = {
|
|
90
|
-
apiOptions: Props["apiOptions"];
|
|
91
|
-
buttonSets: Props["buttonSets"];
|
|
92
|
-
functions: Props["functions"];
|
|
93
|
-
linterContext: Props["linterContext"];
|
|
94
|
-
onBlur: Props["onBlur"];
|
|
95
|
-
onFocus: Props["onFocus"];
|
|
96
|
-
times: Props["times"];
|
|
97
|
-
value: Props["value"];
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
type OnInputErrorFunctionType = (
|
|
101
|
-
arg1?: any,
|
|
102
|
-
arg2?: any,
|
|
103
|
-
arg3?: any,
|
|
104
|
-
) => boolean | null | undefined;
|
|
105
|
-
|
|
106
|
-
// The new, MathQuill input expression widget
|
|
107
|
-
export class Expression extends React.Component<Props, ExpressionState> {
|
|
108
|
-
_isMounted = false;
|
|
109
|
-
|
|
110
|
-
//#region Previously a class extension
|
|
111
|
-
/* Content creators input a list of answers which are matched from top to
|
|
112
|
-
* bottom. The intent is that they can include spcific solutions which should
|
|
113
|
-
* be graded as correct or incorrect (or ungraded!) first, then get more
|
|
114
|
-
* general.
|
|
115
|
-
*
|
|
116
|
-
* We iterate through each answer, trying to match it with the user's input
|
|
117
|
-
* using the following angorithm:
|
|
118
|
-
* - Try to parse the user's input. If it doesn't parse then return "not
|
|
119
|
-
* graded".
|
|
120
|
-
* - For each answer:
|
|
121
|
-
* ~ Try to validate the user's input against the answer. The answer is
|
|
122
|
-
* expected to parse.
|
|
123
|
-
* ~ If the user's input validates (the validator judges it "correct"), we've
|
|
124
|
-
* matched and can stop considering answers.
|
|
125
|
-
* - If there were no matches or the matching answer is considered "ungraded",
|
|
126
|
-
* show the user an error. TODO(joel) - what error?
|
|
127
|
-
* - Otherwise, pass through the resulting points and message.
|
|
128
|
-
*/
|
|
129
|
-
static validate(
|
|
130
|
-
userInput: string,
|
|
131
|
-
rubric: Rubric,
|
|
132
|
-
// @ts-expect-error - TS2322 - Type '() => void' is not assignable to type 'OnInputErrorFunctionType'.
|
|
133
|
-
onInputError: OnInputErrorFunctionType = function () {},
|
|
134
|
-
): PerseusScore {
|
|
135
|
-
const options = _.clone(rubric);
|
|
136
|
-
_.extend(options, {
|
|
137
|
-
decimal_separator: i18n.getDecimalSeparator(),
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
const createValidator = (answer: PerseusExpressionAnswerForm) => {
|
|
141
|
-
// We give options to KAS.parse here because it is parsing the
|
|
142
|
-
// solution answer, not the student answer, and we don't want a
|
|
143
|
-
// solution to work if the student is using a different language
|
|
144
|
-
// (different from the content creation language, ie. English).
|
|
145
|
-
const expression = KAS.parse(answer.value, rubric);
|
|
146
|
-
// An answer may not be parsed if the expression was defined
|
|
147
|
-
// incorrectly. For example if the answer is using a symbol defined
|
|
148
|
-
// in the function variables list for the expression.
|
|
149
|
-
if (!expression.parsed) {
|
|
150
|
-
/* c8 ignore next */
|
|
151
|
-
Log.error(
|
|
152
|
-
"Unable to parse solution answer for expression",
|
|
153
|
-
PerseusErrors.InvalidInput,
|
|
154
|
-
{loggedMetadata: {rubric: JSON.stringify(rubric)}},
|
|
155
|
-
);
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
return KhanAnswerTypes.expression.createValidatorFunctional(
|
|
159
|
-
expression.expr,
|
|
160
|
-
_({}).extend(options, {
|
|
161
|
-
simplify: answer.simplify,
|
|
162
|
-
form: answer.form,
|
|
163
|
-
}),
|
|
164
|
-
);
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
// Find the first answer form that matches the user's input and that
|
|
168
|
-
// is considered correct. Also, track whether the input is
|
|
169
|
-
// considered "empty" for all answer forms, and keep the validation
|
|
170
|
-
// result for the first answer form for which the user's input was
|
|
171
|
-
// considered "ungraded".
|
|
172
|
-
// (Terminology reminder: the answer forms are provided by the
|
|
173
|
-
// assessment items; they are not the user's input. Each one might
|
|
174
|
-
// represent a correct answer, an incorrect one (if the exercise
|
|
175
|
-
// creator has predicted certain common wrong answers and wants to
|
|
176
|
-
// provide guidance via a message), or an ungraded one (same idea,
|
|
177
|
-
// but without giving the user an incorrect mark for the question).
|
|
178
|
-
let matchingAnswerForm;
|
|
179
|
-
let matchMessage;
|
|
180
|
-
let allEmpty = true;
|
|
181
|
-
let firstUngradedResult;
|
|
182
|
-
for (const answerForm of rubric.answerForms || []) {
|
|
183
|
-
const validate = createValidator(answerForm);
|
|
184
|
-
if (!validate) {
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const result = validate(userInput);
|
|
189
|
-
|
|
190
|
-
// Short-circuit as soon as the user's input matches some answer
|
|
191
|
-
// (independently of whether the answer is correct)
|
|
192
|
-
if (result.correct) {
|
|
193
|
-
matchingAnswerForm = answerForm;
|
|
194
|
-
matchMessage = result.message || "";
|
|
195
|
-
break;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
allEmpty = allEmpty && result.empty;
|
|
199
|
-
// If this answer form is correct and the user's input is considered
|
|
200
|
-
// "ungraded" for it, we'll want to keep the evaluation result for
|
|
201
|
-
// later. If the user's input doesn't match any answer forms, we'll
|
|
202
|
-
// show the message from this validation.
|
|
203
|
-
if (
|
|
204
|
-
answerForm.considered === "correct" &&
|
|
205
|
-
result.ungraded &&
|
|
206
|
-
!firstUngradedResult
|
|
207
|
-
) {
|
|
208
|
-
firstUngradedResult = result;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Now check to see if we matched any answer form at all, and if
|
|
213
|
-
// we did, whether it's considered correct, incorrect, or ungraded
|
|
214
|
-
if (!matchingAnswerForm) {
|
|
215
|
-
if (firstUngradedResult) {
|
|
216
|
-
// While we didn't directly match with any answer form, we
|
|
217
|
-
// did at some point get an "ungraded" validation result,
|
|
218
|
-
// which might indicate e.g. a mismatch in variable casing.
|
|
219
|
-
// We'll return "invalid", which will let the user try again
|
|
220
|
-
// with no penalty, and the hopefully helpful validation
|
|
221
|
-
// message.
|
|
222
|
-
return {
|
|
223
|
-
type: "invalid",
|
|
224
|
-
message: firstUngradedResult.message,
|
|
225
|
-
suppressAlmostThere:
|
|
226
|
-
firstUngradedResult.suppressAlmostThere,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
if (allEmpty) {
|
|
230
|
-
// If everything graded as empty, it's invalid.
|
|
231
|
-
return {
|
|
232
|
-
type: "invalid",
|
|
233
|
-
message: null,
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
// We fell through all the possibilities and we're not empty,
|
|
237
|
-
// so the answer is considered incorrect.
|
|
238
|
-
return {
|
|
239
|
-
type: "points",
|
|
240
|
-
earned: 0,
|
|
241
|
-
total: 1,
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
if (matchingAnswerForm.considered === "ungraded") {
|
|
245
|
-
// We matched an ungraded answer form - return "invalid", which
|
|
246
|
-
// will let the user try again with no penalty
|
|
247
|
-
const apiResult = onInputError(
|
|
248
|
-
null, // Reserved for some widget identifier
|
|
249
|
-
userInput,
|
|
250
|
-
matchMessage,
|
|
251
|
-
);
|
|
252
|
-
return {
|
|
253
|
-
type: "invalid",
|
|
254
|
-
message: apiResult === false ? null : matchMessage,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
// We matched a graded answer form, so we can now tell the user
|
|
258
|
-
// whether their input was correct or incorrect, and hand out
|
|
259
|
-
// points accordingly
|
|
260
|
-
// TODO(eater): Seems silly to translate result to this
|
|
261
|
-
// invalid/points thing and immediately translate it back in
|
|
262
|
-
// ItemRenderer.scoreInput()
|
|
263
|
-
return {
|
|
264
|
-
type: "points",
|
|
265
|
-
earned: matchingAnswerForm.considered === "correct" ? 1 : 0,
|
|
266
|
-
total: 1,
|
|
267
|
-
message: matchMessage,
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
static getUserInputFromProps(props: Props): string {
|
|
272
|
-
return normalizeTex(props.value);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
static getOneCorrectAnswerFromRubric(
|
|
276
|
-
rubric: Rubric,
|
|
277
|
-
): string | null | undefined {
|
|
278
|
-
const correctAnswers = (rubric.answerForms || []).filter(
|
|
279
|
-
(answerForm) => answerForm.considered === "correct",
|
|
280
|
-
);
|
|
281
|
-
if (correctAnswers.length === 0) {
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
return correctAnswers[0].value;
|
|
285
|
-
}
|
|
286
|
-
//#endregion
|
|
287
|
-
|
|
288
|
-
static defaultProps: DefaultProps = {
|
|
289
|
-
value: "",
|
|
290
|
-
times: false,
|
|
291
|
-
functions: [],
|
|
292
|
-
buttonSets: ["basic", "trig", "prealgebra", "logarithms"],
|
|
293
|
-
onFocus: () => {},
|
|
294
|
-
onBlur: () => {},
|
|
295
|
-
apiOptions: ApiOptions.defaults,
|
|
296
|
-
linterContext: linterContextDefault,
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
displayName = "Expression";
|
|
300
|
-
|
|
301
|
-
state: ExpressionState = {
|
|
302
|
-
invalid: false,
|
|
303
|
-
showErrorTooltip: false,
|
|
304
|
-
showErrorStyle: false,
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
componentDidMount: () => void = () => {
|
|
308
|
-
document.addEventListener("mousedown", this._handleMouseDown);
|
|
309
|
-
|
|
310
|
-
// TODO(scottgrant): This is a hack to remove the deprecated call to
|
|
311
|
-
// this.isMounted() but is still considered an anti-pattern.
|
|
312
|
-
this._isMounted = true;
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
// Whenever the input value changes, attempt to parse it.
|
|
316
|
-
//
|
|
317
|
-
// Clear any errors if this parse succeeds, show an error within a second
|
|
318
|
-
// if it fails.
|
|
319
|
-
componentDidUpdate: (prevProps: Props) => void = (prevProps) => {
|
|
320
|
-
if (
|
|
321
|
-
!_.isEqual(this.props.value, prevProps.value) ||
|
|
322
|
-
!_.isEqual(this.props.functions, prevProps.functions)
|
|
323
|
-
) {
|
|
324
|
-
this.setState({
|
|
325
|
-
invalid: false,
|
|
326
|
-
showErrorTooltip: false,
|
|
327
|
-
showErrorStyle: false,
|
|
328
|
-
});
|
|
329
|
-
if (!this.parse(this.props.value, this.props).parsed) {
|
|
330
|
-
const apiResult = this.props.apiOptions.onInputError(
|
|
331
|
-
null, // reserved for some widget identifier
|
|
332
|
-
this.props.value,
|
|
333
|
-
ERROR_MESSAGE,
|
|
334
|
-
);
|
|
335
|
-
if (apiResult !== false) {
|
|
336
|
-
this.setState({
|
|
337
|
-
invalid: true,
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
componentWillUnmount: () => void = () => {
|
|
345
|
-
this._isMounted = false;
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
_handleMouseDown = () => {
|
|
349
|
-
if (this._isMounted && this.state.showErrorTooltip) {
|
|
350
|
-
this.setState({
|
|
351
|
-
showErrorTooltip: false,
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
simpleValidate: (
|
|
357
|
-
rubric: Rubric & {scoring?: boolean},
|
|
358
|
-
onInputError: OnInputErrorFunctionType,
|
|
359
|
-
) => PerseusScore = ({scoring, ...rubric}, onInputError) => {
|
|
360
|
-
const score = Expression.validate(
|
|
361
|
-
this.getUserInput(),
|
|
362
|
-
rubric,
|
|
363
|
-
onInputError || function () {},
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
// "scoring" is a flag that indicates when we are checking answers.
|
|
367
|
-
// otherwise, we may just be checking validity after changes.
|
|
368
|
-
if (scoring && score.type !== "invalid") {
|
|
369
|
-
this.props.analytics?.onAnalyticsEvent({
|
|
370
|
-
type: "perseus:expression-evaluated",
|
|
371
|
-
payload: {
|
|
372
|
-
result:
|
|
373
|
-
score.earned === score.total ? "correct" : "incorrect",
|
|
374
|
-
virtualKeypadVersion: deriveKeypadVersion(
|
|
375
|
-
this.props.apiOptions,
|
|
376
|
-
),
|
|
377
|
-
},
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
return score;
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
getUserInput: () => string = () => {
|
|
385
|
-
return Expression.getUserInputFromProps(this.props);
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
change: (...args: any) => any | undefined = (...args: any) => {
|
|
389
|
-
return Changeable.change.apply(this, args);
|
|
390
|
-
};
|
|
391
|
-
|
|
392
|
-
parse: (value: string, props: Props) => any = (
|
|
393
|
-
value: string,
|
|
394
|
-
props: Props,
|
|
395
|
-
) => {
|
|
396
|
-
// TODO(jack): Disable icu for content creators here, or
|
|
397
|
-
// make it so that solution answers with ','s or '.'s work
|
|
398
|
-
const options = _.pick(props || this.props, "functions");
|
|
399
|
-
_.extend(options, {
|
|
400
|
-
decimal_separator: i18n.getDecimalSeparator(),
|
|
401
|
-
});
|
|
402
|
-
return KAS.parse(normalizeTex(value), options);
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
changeAndTrack: (e: any, cb: () => void) => void = (
|
|
406
|
-
e: any,
|
|
407
|
-
cb: () => void,
|
|
408
|
-
) => {
|
|
409
|
-
this.change("value", e, cb);
|
|
410
|
-
this.props.trackInteraction();
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
_handleFocus: () => void = () => {
|
|
414
|
-
this.props.analytics?.onAnalyticsEvent({
|
|
415
|
-
type: "perseus:expression-focused",
|
|
416
|
-
payload: null,
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
/* c8 ignore next */
|
|
420
|
-
this.props.onFocus([]);
|
|
421
|
-
};
|
|
422
|
-
|
|
423
|
-
_handleBlur: () => void = () => {
|
|
424
|
-
/* c8 ignore next */
|
|
425
|
-
this.props.onBlur([]);
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
focus: () => boolean = () => {
|
|
429
|
-
if (this.props.apiOptions.customKeypad) {
|
|
430
|
-
// eslint-disable-next-line react/no-string-refs
|
|
431
|
-
// @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
|
|
432
|
-
this.refs.input.focus();
|
|
433
|
-
} else {
|
|
434
|
-
// The buttons are often on top of text you're trying to read, so
|
|
435
|
-
// don't focus the editor automatically.
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
return true;
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
focusInputPath: (inputPath: InputPath) => void = (inputPath: InputPath) => {
|
|
442
|
-
// eslint-disable-next-line react/no-string-refs
|
|
443
|
-
// @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'ReactInstance'.
|
|
444
|
-
this.refs.input.focus();
|
|
445
|
-
};
|
|
446
|
-
|
|
447
|
-
blurInputPath: (inputPath: InputPath) => void = (inputPath: InputPath) => {
|
|
448
|
-
// eslint-disable-next-line react/no-string-refs
|
|
449
|
-
// @ts-expect-error - TS2339 - Property 'blur' does not exist on type 'ReactInstance'.
|
|
450
|
-
this.refs.input.blur();
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
// HACK(joel)
|
|
454
|
-
insert(keyPressed: Key) {
|
|
455
|
-
// eslint-disable-next-line react/no-string-refs
|
|
456
|
-
// @ts-expect-error - TS2339 - Property 'insert' does not exist on type 'ReactInstance'.
|
|
457
|
-
this.refs.input.insert(keyPressed);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
getInputPaths: () => ReadonlyArray<ReadonlyArray<any>> = () => {
|
|
461
|
-
// The widget itself is an input, so we return a single empty list to
|
|
462
|
-
// indicate this.
|
|
463
|
-
/* c8 ignore next */
|
|
464
|
-
return [[]];
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
// TODO(Nicole): I believe this is going away and won't be needed anymore
|
|
468
|
-
getGrammarTypeForPath: (inputPath: string) => string = (
|
|
469
|
-
inputPath: string,
|
|
470
|
-
) => {
|
|
471
|
-
/* c8 ignore next */
|
|
472
|
-
return "expression";
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
setInputValue: (path: string, newValue: string, cb: () => void) => void = (
|
|
476
|
-
path: string,
|
|
477
|
-
newValue: string,
|
|
478
|
-
cb: () => void,
|
|
479
|
-
) => {
|
|
480
|
-
this.props.onChange(
|
|
481
|
-
{
|
|
482
|
-
value: newValue,
|
|
483
|
-
},
|
|
484
|
-
cb,
|
|
485
|
-
);
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
render():
|
|
489
|
-
| React.ReactNode
|
|
490
|
-
| React.ReactElement<React.ComponentProps<"div">> {
|
|
491
|
-
if (this.props.apiOptions.customKeypad) {
|
|
492
|
-
return (
|
|
493
|
-
<KeypadInput
|
|
494
|
-
// eslint-disable-next-line react/no-string-refs
|
|
495
|
-
ref="input"
|
|
496
|
-
value={this.props.value}
|
|
497
|
-
keypadElement={this.props.keypadElement}
|
|
498
|
-
onChange={this.changeAndTrack}
|
|
499
|
-
onFocus={() => {
|
|
500
|
-
// this.props.keypadElement should always be set
|
|
501
|
-
// when apiOptions.customKeypad is set, but how
|
|
502
|
-
// to convince TypeScript of this?
|
|
503
|
-
this.props.keypadElement?.configure(
|
|
504
|
-
this.props.keypadConfiguration,
|
|
505
|
-
() => {
|
|
506
|
-
if (this._isMounted) {
|
|
507
|
-
this._handleFocus();
|
|
508
|
-
}
|
|
509
|
-
},
|
|
510
|
-
);
|
|
511
|
-
}}
|
|
512
|
-
onBlur={this._handleBlur}
|
|
513
|
-
/>
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
const className = classNames({
|
|
518
|
-
"perseus-widget-expression": true,
|
|
519
|
-
"show-error-tooltip": this.state.showErrorTooltip,
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
return (
|
|
523
|
-
<div
|
|
524
|
-
className={className}
|
|
525
|
-
onBlur={() =>
|
|
526
|
-
this.state.invalid &&
|
|
527
|
-
this.setState({
|
|
528
|
-
showErrorTooltip: true,
|
|
529
|
-
showErrorStyle: true,
|
|
530
|
-
})
|
|
531
|
-
}
|
|
532
|
-
onFocus={() =>
|
|
533
|
-
this.setState({
|
|
534
|
-
showErrorTooltip: false,
|
|
535
|
-
})
|
|
536
|
-
}
|
|
537
|
-
>
|
|
538
|
-
{/**
|
|
539
|
-
* This is a visually hidden container for the error tooltip.
|
|
540
|
-
https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/alert_role#example_3_visually_hidden_alert_container_for_screen_reader_notifications
|
|
541
|
-
*/}
|
|
542
|
-
<View style={a11y.srOnly} role="alert">
|
|
543
|
-
{this.state.showErrorTooltip &&
|
|
544
|
-
ERROR_TITLE + " " + ERROR_MESSAGE}
|
|
545
|
-
</View>
|
|
546
|
-
<Tooltip
|
|
547
|
-
forceAnchorFocusivity={false}
|
|
548
|
-
opened={this.state.showErrorTooltip}
|
|
549
|
-
title={ERROR_TITLE}
|
|
550
|
-
content={ERROR_MESSAGE}
|
|
551
|
-
>
|
|
552
|
-
<MathInput
|
|
553
|
-
// eslint-disable-next-line react/no-string-refs
|
|
554
|
-
ref="input"
|
|
555
|
-
className={ApiClassNames.INTERACTIVE}
|
|
556
|
-
value={this.props.value}
|
|
557
|
-
onChange={this.changeAndTrack}
|
|
558
|
-
convertDotToTimes={this.props.times}
|
|
559
|
-
buttonSets={this.props.buttonSets}
|
|
560
|
-
onFocus={this._handleFocus}
|
|
561
|
-
onBlur={this._handleBlur}
|
|
562
|
-
hasError={this.state.showErrorStyle}
|
|
563
|
-
extraKeys={this.props.keypadConfiguration?.extraKeys}
|
|
564
|
-
analytics={
|
|
565
|
-
this.props.analytics ?? {
|
|
566
|
-
onAnalyticsEvent: async () => {},
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
/>
|
|
570
|
-
</Tooltip>
|
|
571
|
-
</div>
|
|
572
|
-
);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* Determine the keypad configuration parameters for the input, based on the
|
|
578
|
-
* provided properties.
|
|
579
|
-
*
|
|
580
|
-
* There are two configuration parameters to be passed to the keypad:
|
|
581
|
-
* (1) The keypad type. For the Expression widget, we always use the
|
|
582
|
-
* Expression keypad.
|
|
583
|
-
* (2) The extra keys; namely, any variables or constants (like Pi) that need
|
|
584
|
-
* to be included as keys on the keypad. These are scraped from the answer
|
|
585
|
-
* forms.
|
|
586
|
-
*/
|
|
587
|
-
const keypadConfigurationForProps = (
|
|
588
|
-
widgetOptions: PerseusExpressionWidgetOptions,
|
|
589
|
-
): KeypadConfiguration => {
|
|
590
|
-
// Always use the Expression keypad, regardless of the button sets that have
|
|
591
|
-
// been enabled.
|
|
592
|
-
const keypadType = KeypadType.EXPRESSION;
|
|
593
|
-
|
|
594
|
-
// Extract any and all variables and constants from the answer forms.
|
|
595
|
-
const uniqueExtraVariables: Partial<Record<Key, boolean>> = {};
|
|
596
|
-
const uniqueExtraConstants: Partial<Record<Key, boolean>> = {};
|
|
597
|
-
for (const answerForm of widgetOptions.answerForms) {
|
|
598
|
-
const maybeExpr = KAS.parse(answerForm.value, widgetOptions);
|
|
599
|
-
if (maybeExpr.parsed) {
|
|
600
|
-
const expr = maybeExpr.expr;
|
|
601
|
-
|
|
602
|
-
// The keypad expects Greek letters to be capitalized (e.g., it
|
|
603
|
-
// requires `PI` instead of `pi`). Right now, it only supports Pi
|
|
604
|
-
// and Theta, so we special-case.
|
|
605
|
-
const isGreek = (symbol: any) =>
|
|
606
|
-
symbol === "pi" || symbol === "theta";
|
|
607
|
-
const toKey = (symbol: any) =>
|
|
608
|
-
isGreek(symbol) ? symbol.toUpperCase() : symbol;
|
|
609
|
-
const isKey = (key: string): key is Key =>
|
|
610
|
-
KeyArray.includes(key as Key);
|
|
611
|
-
|
|
612
|
-
for (const variable of expr.getVars()) {
|
|
613
|
-
const maybeKey = toKey(variable);
|
|
614
|
-
if (isKey(maybeKey)) {
|
|
615
|
-
uniqueExtraVariables[maybeKey] = true;
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
for (const constant of expr.getConsts()) {
|
|
619
|
-
const maybeKey = toKey(constant);
|
|
620
|
-
if (isKey(maybeKey)) {
|
|
621
|
-
uniqueExtraConstants[maybeKey] = true;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// TODO(charlie): Alert the keypad as to which of these symbols should be
|
|
628
|
-
// treated as functions.
|
|
629
|
-
const extraVariables = Object.keys(
|
|
630
|
-
uniqueExtraVariables,
|
|
631
|
-
).sort() as ReadonlyArray<Key>;
|
|
632
|
-
|
|
633
|
-
const extraConstants = Object.keys(
|
|
634
|
-
uniqueExtraConstants,
|
|
635
|
-
).sort() as ReadonlyArray<Key>;
|
|
636
|
-
|
|
637
|
-
let extraKeys = [...extraVariables, ...extraConstants];
|
|
638
|
-
if (!extraKeys.length) {
|
|
639
|
-
// If there are no extra symbols available, we include Pi anyway, so
|
|
640
|
-
// that the "extra symbols" button doesn't appear empty.
|
|
641
|
-
extraKeys = ["PI"];
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
return {keypadType, extraKeys, times: widgetOptions.times};
|
|
645
|
-
};
|
|
646
|
-
|
|
647
|
-
const propUpgrades = {
|
|
648
|
-
/* c8 ignore next */
|
|
649
|
-
"1": (v0props: any): PerseusExpressionWidgetOptions => ({
|
|
650
|
-
times: v0props.times,
|
|
651
|
-
buttonSets: v0props.buttonSets,
|
|
652
|
-
functions: v0props.functions,
|
|
653
|
-
buttonsVisible: v0props.buttonsVisible,
|
|
654
|
-
|
|
655
|
-
answerForms: [
|
|
656
|
-
{
|
|
657
|
-
considered: "correct",
|
|
658
|
-
form: v0props.form,
|
|
659
|
-
simplify: v0props.simplify,
|
|
660
|
-
value: v0props.value,
|
|
661
|
-
},
|
|
662
|
-
],
|
|
663
|
-
}),
|
|
664
|
-
} as const;
|
|
665
|
-
|
|
666
|
-
const ExpressionWithDependencies = React.forwardRef<
|
|
667
|
-
Expression,
|
|
668
|
-
Omit<PropsFor<typeof Expression>, keyof ReturnType<typeof useDependencies>>
|
|
669
|
-
>((props, ref) => {
|
|
670
|
-
const deps = useDependencies();
|
|
671
|
-
return <Expression ref={ref} analytics={deps.analytics} {...props} />;
|
|
672
|
-
});
|
|
673
|
-
|
|
674
|
-
// HACK: Propogate "static" methods onto our wrapper component.
|
|
675
|
-
// In the future we should adjust client apps to not depend on these static
|
|
676
|
-
// methods and instead adjust Peresus to provide these facilities through
|
|
677
|
-
// instance methods on our Renderers.
|
|
678
|
-
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
679
|
-
ExpressionWithDependencies.validate = Expression.validate;
|
|
680
|
-
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
681
|
-
ExpressionWithDependencies.getUserInputFromProps =
|
|
682
|
-
Expression.getUserInputFromProps;
|
|
683
|
-
// @ts-expect-error - TS2339 - Property 'validate' does not exist on type
|
|
684
|
-
ExpressionWithDependencies.getOneCorrectAnswerFromRubric =
|
|
685
|
-
Expression.getOneCorrectAnswerFromRubric;
|
|
686
|
-
|
|
687
|
-
export default {
|
|
688
|
-
name: "expression",
|
|
689
|
-
displayName: "Expression / Equation",
|
|
690
|
-
defaultAlignment: "inline-block",
|
|
691
|
-
widget: ExpressionWithDependencies,
|
|
692
|
-
transform: (widgetOptions: PerseusExpressionWidgetOptions): RenderProps => {
|
|
693
|
-
const {times, functions, buttonSets, buttonsVisible} = widgetOptions;
|
|
694
|
-
return {
|
|
695
|
-
keypadConfiguration: keypadConfigurationForProps(widgetOptions),
|
|
696
|
-
times,
|
|
697
|
-
functions,
|
|
698
|
-
buttonSets,
|
|
699
|
-
buttonsVisible,
|
|
700
|
-
};
|
|
701
|
-
},
|
|
702
|
-
version: {major: 1, minor: 0},
|
|
703
|
-
propUpgrades: propUpgrades,
|
|
704
|
-
|
|
705
|
-
// For use by the editor
|
|
706
|
-
isLintable: true,
|
|
707
|
-
} as WidgetExports<typeof ExpressionWithDependencies>;
|