@khanacademy/perseus 12.1.0 → 12.3.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/CHANGELOG.md +31 -0
- package/dist/article-renderer.d.ts +5 -2
- package/dist/error-boundary.d.ts +1 -0
- package/dist/es/index.css +4 -66
- package/dist/es/index.js +112 -87
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +4 -66
- package/dist/index.js +103 -75
- package/dist/index.js.map +1 -1
- package/dist/renderer.d.ts +4 -1
- package/dist/styles/constants.d.ts +2 -2
- package/dist/widget-container.d.ts +2 -0
- package/dist/widgets/expression.d.ts +5 -4
- package/package.json +8 -8
- package/src/__stories__/article-renderer.stories.tsx +28 -2
- package/src/__testdata__/article-renderer.testdata.ts +85 -0
- package/src/__tests__/article-renderer.test.tsx +131 -0
- package/src/__tests__/renderer-api.test.tsx +18 -23
- package/src/article-renderer.tsx +73 -58
- package/src/error-boundary.tsx +3 -0
- package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +5 -5
- package/src/renderer.tsx +29 -27
- package/src/server-item-renderer.tsx +1 -0
- package/src/styles/constants.ts +4 -4
- package/src/styles/perseus-renderer.less +1 -10
- package/src/styles/variables.less +2 -2
- package/src/styles/variables.shared.less +2 -2
- package/src/styles/widgets/expression.less +0 -77
- package/src/styles/widgets/passage.less +1 -19
- package/src/widget-container.tsx +32 -10
- package/src/widgets/__stories__/label-image.stories.tsx +30 -0
- package/src/widgets/__testdata__/label-image.testdata.ts +63 -0
- package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +5 -5
- package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +28 -28
- package/src/widgets/__tests__/expression.test.tsx +12 -9
- package/src/widgets/__tests__/renderQuestion.tsx +30 -13
- package/src/widgets/expression.tsx +19 -25
- package/src/widgets/transformer.tsx +1 -2
- package/tsconfig-build.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @khanacademy/perseus
|
|
2
2
|
|
|
3
|
+
## 12.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#780](https://github.com/Khan/perseus/pull/780) [`376eb0e4`](https://github.com/Khan/perseus/commit/376eb0e4aaaa4c7a90fd6107a84bb74d382b077c) Thanks [@jeremywiebe](https://github.com/jeremywiebe)! - Adds an `onError` callback prop to the ErrorBoundary component. Uses this new callback in the WidgetContainer component to report all widget rendering failures through the analytics API.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#759](https://github.com/Khan/perseus/pull/759) [`c431c4b8`](https://github.com/Khan/perseus/commit/c431c4b8147ae0630df2d2b19b0f5a5b5f04d4bf) Thanks [@handeyeco](https://github.com/handeyeco)! - Remove references to unused font families
|
|
12
|
+
|
|
13
|
+
* [#775](https://github.com/Khan/perseus/pull/775) [`cb529549`](https://github.com/Khan/perseus/commit/cb5295497fe74e3f4cc00eef0f99da9f83f58e4d) Thanks [@nedredmond](https://github.com/nedredmond)! - Added flag to `simpleValidate` call from Renderer to ensure we call analytics on MathInputEvaluated only when the answer is submitted
|
|
14
|
+
|
|
15
|
+
- [#781](https://github.com/Khan/perseus/pull/781) [`9b9dfd4f`](https://github.com/Khan/perseus/commit/9b9dfd4f6779d129040d9afcf3205f1863a64c25) Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! - Fix for broken dilation tool in the Transformer Widget
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [[`c431c4b8`](https://github.com/Khan/perseus/commit/c431c4b8147ae0630df2d2b19b0f5a5b5f04d4bf), [`fb84640d`](https://github.com/Khan/perseus/commit/fb84640de911a8e8817829985fe9956e83a7f7d1), [`376eb0e4`](https://github.com/Khan/perseus/commit/376eb0e4aaaa4c7a90fd6107a84bb74d382b077c), [`33cc24c3`](https://github.com/Khan/perseus/commit/33cc24c33f62cd87de7594ba575d311fd465e2e0)]:
|
|
18
|
+
- @khanacademy/math-input@14.2.1
|
|
19
|
+
- @khanacademy/perseus-core@1.2.0
|
|
20
|
+
- @khanacademy/kas@0.3.4
|
|
21
|
+
- @khanacademy/kmath@0.1.5
|
|
22
|
+
- @khanacademy/perseus-linter@0.3.7
|
|
23
|
+
- @khanacademy/pure-markdown@0.2.9
|
|
24
|
+
- @khanacademy/simple-markdown@0.10.3
|
|
25
|
+
|
|
26
|
+
## 12.2.0
|
|
27
|
+
|
|
28
|
+
### Minor Changes
|
|
29
|
+
|
|
30
|
+
- [#773](https://github.com/Khan/perseus/pull/773) [`809823e4`](https://github.com/Khan/perseus/commit/809823e490f65cbcbaa05b548de985035810ba8d) Thanks [@nixterrimus](https://github.com/nixterrimus)! - Fix text overlap in interactive graph widget
|
|
31
|
+
|
|
32
|
+
* [#755](https://github.com/Khan/perseus/pull/755) [`6d9e31a4`](https://github.com/Khan/perseus/commit/6d9e31a4ea4a1ed29bef2d9812d21e5b2fd41af6) Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! - Adding logic to ArticleRenderer so that it can return our currently focused element.
|
|
33
|
+
|
|
3
34
|
## 12.1.0
|
|
4
35
|
|
|
5
36
|
### Minor Changes
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
* composed of multiple (Renderer) sections concatenated together.
|
|
4
4
|
*/
|
|
5
5
|
import * as React from "react";
|
|
6
|
+
import { DependenciesContext } from "./dependencies";
|
|
7
|
+
import Renderer from "./renderer";
|
|
6
8
|
import type { KeypadProps } from "./mixins/provide-keypad";
|
|
7
9
|
import type { PerseusRenderer } from "./perseus-types";
|
|
8
10
|
import type { APIOptions, PerseusDependenciesV2 } from "./types";
|
|
9
11
|
import type { KeypadAPI } from "@khanacademy/math-input";
|
|
10
12
|
import type { KeypadContextRendererInterface } from "@khanacademy/perseus-core";
|
|
11
13
|
import type { LinterContextProps } from "@khanacademy/perseus-linter";
|
|
12
|
-
type Props = {
|
|
14
|
+
type Props = Partial<React.ContextType<typeof DependenciesContext>> & KeypadProps & {
|
|
13
15
|
apiOptions: APIOptions;
|
|
14
16
|
json: PerseusRenderer | ReadonlyArray<PerseusRenderer>;
|
|
15
17
|
/**
|
|
@@ -20,7 +22,7 @@ type Props = {
|
|
|
20
22
|
legacyPerseusLint?: ReadonlyArray<string>;
|
|
21
23
|
keypadElement?: KeypadAPI | null | undefined;
|
|
22
24
|
dependencies: PerseusDependenciesV2;
|
|
23
|
-
}
|
|
25
|
+
};
|
|
24
26
|
type DefaultProps = {
|
|
25
27
|
apiOptions: Props["apiOptions"];
|
|
26
28
|
useNewStyles: Props["useNewStyles"];
|
|
@@ -28,6 +30,7 @@ type DefaultProps = {
|
|
|
28
30
|
};
|
|
29
31
|
declare class ArticleRenderer extends React.Component<Props> implements KeypadContextRendererInterface {
|
|
30
32
|
_currentFocus: any;
|
|
33
|
+
sectionRenderers: Array<Renderer>;
|
|
31
34
|
static defaultProps: DefaultProps;
|
|
32
35
|
constructor(props: Props);
|
|
33
36
|
componentDidMount(): void;
|
package/dist/error-boundary.d.ts
CHANGED
package/dist/es/index.css
CHANGED
|
@@ -2613,6 +2613,7 @@ img.zoom-img {
|
|
|
2613
2613
|
}
|
|
2614
2614
|
/* Widget CSS */
|
|
2615
2615
|
.perseus-graph-padding {
|
|
2616
|
+
box-sizing: content-box;
|
|
2616
2617
|
padding: 25px 25px 0 0;
|
|
2617
2618
|
}
|
|
2618
2619
|
.categorizer-container {
|
|
@@ -2683,72 +2684,8 @@ body.mobile .categorizer-container td.category input[type="radio"] + span:active
|
|
|
2683
2684
|
#answer_area .perseus-widget-expression .error-tooltip .tooltipContainer > div:first-child {
|
|
2684
2685
|
visibility: hidden !important;
|
|
2685
2686
|
}
|
|
2686
|
-
.perseus-widget-expression-old,
|
|
2687
|
-
.perseus-widget-expression-old > .output,
|
|
2688
|
-
.perseus-widget-expression-old > .output > .tex,
|
|
2689
|
-
.perseus-widget-expression-old > .output > .placeholder {
|
|
2690
|
-
display: block;
|
|
2691
|
-
}
|
|
2692
|
-
.perseus-widget-expression-old input,
|
|
2693
|
-
#answer_area .perseus-widget-expression-old input {
|
|
2694
|
-
direction: ltr;
|
|
2695
|
-
border: 1px solid #a4a4a4;
|
|
2696
|
-
border-radius: 5px;
|
|
2697
|
-
box-sizing: border-box;
|
|
2698
|
-
font-size: 14px;
|
|
2699
|
-
margin-bottom: 5px;
|
|
2700
|
-
max-width: 240px;
|
|
2701
|
-
padding: 6px;
|
|
2702
|
-
width: 100%;
|
|
2703
|
-
}
|
|
2704
|
-
.perseus-widget-expression-old > .output {
|
|
2705
|
-
background: #f2f2f2;
|
|
2706
|
-
border-radius: 5px;
|
|
2707
|
-
padding: 10px;
|
|
2708
|
-
margin: 10px 0;
|
|
2709
|
-
}
|
|
2710
|
-
#answer_area .perseus-widget-expression-old > .output {
|
|
2711
|
-
background: #f7f7f7;
|
|
2712
|
-
}
|
|
2713
|
-
.perseus-widget-expression-old > .output > .tex {
|
|
2714
|
-
overflow-x: auto;
|
|
2715
|
-
padding: 5px;
|
|
2716
|
-
}
|
|
2717
|
-
.perseus-widget-expression-old > .output > .placeholder {
|
|
2718
|
-
position: relative;
|
|
2719
|
-
height: 40px;
|
|
2720
|
-
overflow-y: hidden;
|
|
2721
|
-
}
|
|
2722
|
-
.perseus-widget-expression-old > .output > .placeholder > .error {
|
|
2723
|
-
border-radius: 10px;
|
|
2724
|
-
background: #f7f7f7;
|
|
2725
|
-
border: 1px solid #ddd;
|
|
2726
|
-
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5);
|
|
2727
|
-
color: #000;
|
|
2728
|
-
display: none;
|
|
2729
|
-
font-weight: normal;
|
|
2730
|
-
min-height: 22px;
|
|
2731
|
-
width: 168px;
|
|
2732
|
-
position: absolute;
|
|
2733
|
-
top: 0px;
|
|
2734
|
-
left: 40px;
|
|
2735
|
-
margin: auto;
|
|
2736
|
-
}
|
|
2737
|
-
.perseus-widget-expression-old > .output > .placeholder > .error > .buddy {
|
|
2738
|
-
background-image: url(/images/perseus/error-buddy.png);
|
|
2739
|
-
background-size: 100%;
|
|
2740
|
-
height: 36px;
|
|
2741
|
-
width: 40px;
|
|
2742
|
-
position: absolute;
|
|
2743
|
-
top: -3px;
|
|
2744
|
-
left: -42px;
|
|
2745
|
-
}
|
|
2746
|
-
.perseus-widget-expression-old > .output > .placeholder > .error > .message {
|
|
2747
|
-
font: 12px inherit;
|
|
2748
|
-
line-height: 1.4em;
|
|
2749
|
-
margin: 0px 4px;
|
|
2750
|
-
}
|
|
2751
2687
|
.perseus-widget-grapher {
|
|
2688
|
+
box-sizing: content-box;
|
|
2752
2689
|
padding: 25px 25px 0 0;
|
|
2753
2690
|
}
|
|
2754
2691
|
.perseus-widget-grapher > .graphie-container {
|
|
@@ -2836,6 +2773,7 @@ body.mobile .categorizer-container td.category input[type="radio"] + span:active
|
|
|
2836
2773
|
margin-top: 5px;
|
|
2837
2774
|
}
|
|
2838
2775
|
.perseus-widget-interactive-graph {
|
|
2776
|
+
box-sizing: content-box;
|
|
2839
2777
|
padding: 25px 25px 0 0;
|
|
2840
2778
|
}
|
|
2841
2779
|
.perseus-widget-interactive-graph > .graphie-container {
|
|
@@ -3118,7 +3056,7 @@ body.mobile .perseus-matrix .math-output {
|
|
|
3118
3056
|
}
|
|
3119
3057
|
.framework-perseus.perseus-article .perseus-renderer .perseus-widget-passage-container .perseus-widget-passage div.paragraph,
|
|
3120
3058
|
.perseus-widget-passage-container .perseus-widget-passage div.paragraph {
|
|
3121
|
-
font-family:
|
|
3059
|
+
font-family: KaTeX_Main, Times, "Times New Roman", serif;
|
|
3122
3060
|
}
|
|
3123
3061
|
.framework-perseus.perseus-article .perseus-renderer .perseus-widget-passage-container .perseus-widget-passage .passage-title div.paragraph,
|
|
3124
3062
|
.perseus-widget-passage-container .perseus-widget-passage .passage-title div.paragraph {
|
package/dist/es/index.js
CHANGED
|
@@ -49,6 +49,21 @@ function _extends() {
|
|
|
49
49
|
return _extends.apply(this, arguments);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
53
|
+
if (source == null) return {};
|
|
54
|
+
var target = {};
|
|
55
|
+
var sourceKeys = Object.keys(source);
|
|
56
|
+
var key, i;
|
|
57
|
+
|
|
58
|
+
for (i = 0; i < sourceKeys.length; i++) {
|
|
59
|
+
key = sourceKeys[i];
|
|
60
|
+
if (excluded.indexOf(key) >= 0) continue;
|
|
61
|
+
target[key] = source[key];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return target;
|
|
65
|
+
}
|
|
66
|
+
|
|
52
67
|
const customKeyTranslator = _extends({}, keyTranslator, {
|
|
53
68
|
// If there's something in the input that can become part of a
|
|
54
69
|
// fraction, typing "/" puts it in the numerator. If not, typing
|
|
@@ -1870,6 +1885,7 @@ const KhanAnswerTypes = {
|
|
|
1870
1885
|
}
|
|
1871
1886
|
};
|
|
1872
1887
|
|
|
1888
|
+
const _excluded$4 = ["scoring"];
|
|
1873
1889
|
const ERROR_TITLE = i18n._("Oops!");
|
|
1874
1890
|
const ERROR_MESSAGE = i18n._("Sorry, I don't understand that!");
|
|
1875
1891
|
const insertBraces = value => {
|
|
@@ -1947,11 +1963,26 @@ class Expression extends React.Component {
|
|
|
1947
1963
|
});
|
|
1948
1964
|
}
|
|
1949
1965
|
};
|
|
1950
|
-
this.simpleValidate = (
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1966
|
+
this.simpleValidate = (_ref, onInputError) => {
|
|
1967
|
+
let {
|
|
1968
|
+
scoring
|
|
1969
|
+
} = _ref,
|
|
1970
|
+
rubric = _objectWithoutPropertiesLoose(_ref, _excluded$4);
|
|
1971
|
+
const score = Expression.validate(this.getUserInput(), rubric, onInputError || function () {});
|
|
1972
|
+
|
|
1973
|
+
// "scoring" is a flag that indicates when we are checking answers.
|
|
1974
|
+
// otherwise, we may just be checking validity after changes.
|
|
1975
|
+
if (scoring && score.type !== "invalid") {
|
|
1976
|
+
var _this$props$analytics;
|
|
1977
|
+
(_this$props$analytics = this.props.analytics) == null ? void 0 : _this$props$analytics.onAnalyticsEvent({
|
|
1978
|
+
type: "perseus:expression-evaluated",
|
|
1979
|
+
payload: {
|
|
1980
|
+
result: score.earned === score.total ? "correct" : "incorrect",
|
|
1981
|
+
virtualKeypadVersion: deriveKeypadVersion(this.props.apiOptions)
|
|
1982
|
+
}
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
return score;
|
|
1955
1986
|
};
|
|
1956
1987
|
this.getUserInput = () => {
|
|
1957
1988
|
return Expression.getUserInputFromProps(this.props);
|
|
@@ -1973,8 +2004,8 @@ class Expression extends React.Component {
|
|
|
1973
2004
|
this.props.trackInteraction();
|
|
1974
2005
|
};
|
|
1975
2006
|
this._handleFocus = () => {
|
|
1976
|
-
var _this$props$
|
|
1977
|
-
(_this$props$
|
|
2007
|
+
var _this$props$analytics2;
|
|
2008
|
+
(_this$props$analytics2 = this.props.analytics) == null ? void 0 : _this$props$analytics2.onAnalyticsEvent({
|
|
1978
2009
|
type: "perseus:expression-focused",
|
|
1979
2010
|
payload: null
|
|
1980
2011
|
});
|
|
@@ -2182,16 +2213,6 @@ class Expression extends React.Component {
|
|
|
2182
2213
|
// @ts-expect-error - TS2339 - Property 'insert' does not exist on type 'ReactInstance'.
|
|
2183
2214
|
this.refs.input.insert(keyPressed);
|
|
2184
2215
|
}
|
|
2185
|
-
sendExpressionEvaluatedEvent(result) {
|
|
2186
|
-
var _this$props$analytics2;
|
|
2187
|
-
(_this$props$analytics2 = this.props.analytics) == null ? void 0 : _this$props$analytics2.onAnalyticsEvent({
|
|
2188
|
-
type: "perseus:expression-evaluated",
|
|
2189
|
-
payload: {
|
|
2190
|
-
result,
|
|
2191
|
-
virtualKeypadVersion: deriveKeypadVersion(this.props.apiOptions)
|
|
2192
|
-
}
|
|
2193
|
-
});
|
|
2194
|
-
}
|
|
2195
2216
|
render() {
|
|
2196
2217
|
var _this$props$keypadCon, _this$props$analytics3;
|
|
2197
2218
|
if (this.props.apiOptions.customKeypad) {
|
|
@@ -3680,10 +3701,10 @@ Zoom.prototype.dispose = function () {
|
|
|
3680
3701
|
};
|
|
3681
3702
|
const ZoomService = new ZoomServiceClass();
|
|
3682
3703
|
|
|
3683
|
-
// @baseFontFamily: "
|
|
3684
|
-
const baseFontFamily$1 = "'
|
|
3685
|
-
// @boldFontFamily: "
|
|
3686
|
-
const boldFontFamily = "'
|
|
3704
|
+
// @baseFontFamily: "Lato", sans-serif;
|
|
3705
|
+
const baseFontFamily$1 = "'Lato', sans-serif";
|
|
3706
|
+
// @boldFontFamily: "Lato-Bold", "Lato", sans-serif;
|
|
3707
|
+
const boldFontFamily = "'Lato-Bold', 'Lato', sans-serif";
|
|
3687
3708
|
// @green: #76a005;
|
|
3688
3709
|
const green$1 = "#76A005";
|
|
3689
3710
|
// @kaGreen: #71b307;
|
|
@@ -18675,6 +18696,7 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
18675
18696
|
class WidgetContainer extends React.Component {
|
|
18676
18697
|
constructor(...args) {
|
|
18677
18698
|
super(...args);
|
|
18699
|
+
this.widgetRef = /*#__PURE__*/React.createRef();
|
|
18678
18700
|
this.state = {
|
|
18679
18701
|
// TODO(benkomalo): before we're mounted, we don't know how big
|
|
18680
18702
|
// we're going to be, so just default to MEDIUM for now. :/ In the
|
|
@@ -18684,8 +18706,7 @@ class WidgetContainer extends React.Component {
|
|
|
18684
18706
|
widgetProps: this.props.initialProps
|
|
18685
18707
|
};
|
|
18686
18708
|
this.getWidget = () => {
|
|
18687
|
-
|
|
18688
|
-
return this.refs.widget;
|
|
18709
|
+
return this.widgetRef.current;
|
|
18689
18710
|
};
|
|
18690
18711
|
this.replaceWidgetProps = newWidgetProps => {
|
|
18691
18712
|
this.setState({
|
|
@@ -18779,15 +18800,28 @@ class WidgetContainer extends React.Component {
|
|
|
18779
18800
|
return /*#__PURE__*/React.createElement("div", {
|
|
18780
18801
|
className: className,
|
|
18781
18802
|
style: isStatic ? staticContainerStyles : {}
|
|
18803
|
+
}, /*#__PURE__*/React.createElement(DependenciesContext.Consumer, null, ({
|
|
18804
|
+
analytics
|
|
18805
|
+
}) => /*#__PURE__*/React.createElement(ErrorBoundary, {
|
|
18806
|
+
metadata: {
|
|
18807
|
+
widget_type: type,
|
|
18808
|
+
widget_id: this.props.id
|
|
18809
|
+
},
|
|
18810
|
+
onError: error => {
|
|
18811
|
+
analytics.onAnalyticsEvent({
|
|
18812
|
+
type: "perseus:widget-rendering-error",
|
|
18813
|
+
payload: {
|
|
18814
|
+
widgetType: type
|
|
18815
|
+
}
|
|
18816
|
+
});
|
|
18817
|
+
}
|
|
18782
18818
|
}, /*#__PURE__*/React.createElement(WidgetType, _extends({}, this.state.widgetProps, {
|
|
18783
18819
|
linterContext: linterContext,
|
|
18784
|
-
containerSizeClass: this.state.sizeClass
|
|
18785
|
-
|
|
18786
|
-
,
|
|
18787
|
-
ref: "widget"
|
|
18820
|
+
containerSizeClass: this.state.sizeClass,
|
|
18821
|
+
ref: this.widgetRef
|
|
18788
18822
|
})), isStatic && /*#__PURE__*/React.createElement("div", {
|
|
18789
18823
|
style: staticOverlayStyles
|
|
18790
|
-
}));
|
|
18824
|
+
}))));
|
|
18791
18825
|
}
|
|
18792
18826
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
18793
18827
|
if (this.props.type !== nextProps.type) {
|
|
@@ -18810,6 +18844,7 @@ const rContainsNonWhitespace = /\S/;
|
|
|
18810
18844
|
const rImageURL = /(web\+graphie|https):\/\/[^\s]*/;
|
|
18811
18845
|
const noopOnRender = () => {};
|
|
18812
18846
|
const SHOULD_CLEAR_WIDGETS_PROP_LIST = ["content", "problemNum", "widgets"];
|
|
18847
|
+
const makeContainerId = id => "container:" + id;
|
|
18813
18848
|
|
|
18814
18849
|
// Check if one focus path / id path is a prefix of another
|
|
18815
18850
|
// The focus path null will never be a prefix of any non-null
|
|
@@ -18854,6 +18889,7 @@ class Renderer extends React.Component {
|
|
|
18854
18889
|
this._isMounted = void 0;
|
|
18855
18890
|
this._isTwoColumn = void 0;
|
|
18856
18891
|
this._translationLinter = void 0;
|
|
18892
|
+
this._widgetContainers = new Map();
|
|
18857
18893
|
this.lastRenderedMarkdown = void 0;
|
|
18858
18894
|
this.reuseMarkdown = void 0;
|
|
18859
18895
|
this.translationIndex = void 0;
|
|
@@ -18924,19 +18960,22 @@ class Renderer extends React.Component {
|
|
|
18924
18960
|
// By this point we should have no duplicates, which are
|
|
18925
18961
|
// filtered out in this.render(), so we shouldn't have to
|
|
18926
18962
|
// worry about using this widget key and ref:
|
|
18927
|
-
return /*#__PURE__*/React.createElement(
|
|
18928
|
-
key:
|
|
18929
|
-
|
|
18930
|
-
|
|
18931
|
-
|
|
18932
|
-
|
|
18933
|
-
|
|
18934
|
-
|
|
18963
|
+
return /*#__PURE__*/React.createElement(WidgetContainer, {
|
|
18964
|
+
key: makeContainerId(id),
|
|
18965
|
+
id: id,
|
|
18966
|
+
ref: node => {
|
|
18967
|
+
const containerId = makeContainerId(id);
|
|
18968
|
+
if (node != null) {
|
|
18969
|
+
this._widgetContainers.set(containerId, node);
|
|
18970
|
+
} else {
|
|
18971
|
+
this._widgetContainers.delete(containerId);
|
|
18972
|
+
}
|
|
18973
|
+
},
|
|
18935
18974
|
type: type,
|
|
18936
18975
|
initialProps: this.getWidgetProps(id),
|
|
18937
18976
|
shouldHighlight: shouldHighlight,
|
|
18938
18977
|
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "widget")
|
|
18939
|
-
})
|
|
18978
|
+
});
|
|
18940
18979
|
}
|
|
18941
18980
|
return null;
|
|
18942
18981
|
};
|
|
@@ -18963,7 +19002,6 @@ class Renderer extends React.Component {
|
|
|
18963
19002
|
getTracking(widgetInfo && widgetInfo.type));
|
|
18964
19003
|
}
|
|
18965
19004
|
return _extends({}, widgetProps, {
|
|
18966
|
-
ref: id,
|
|
18967
19005
|
widgetId: id,
|
|
18968
19006
|
alignment: widgetInfo && widgetInfo.alignment,
|
|
18969
19007
|
// When determining if a widget is static, we verify that the widget is not an
|
|
@@ -19095,12 +19133,10 @@ class Renderer extends React.Component {
|
|
|
19095
19133
|
return [...this.findInternalWidgets(filterCriterion), ...this.props.findExternalWidgets(filterCriterion)];
|
|
19096
19134
|
};
|
|
19097
19135
|
this.getWidgetInstance = id => {
|
|
19098
|
-
|
|
19099
|
-
const ref = this.refs["container:" + id];
|
|
19136
|
+
const ref = this._widgetContainers.get(makeContainerId(id));
|
|
19100
19137
|
if (!ref) {
|
|
19101
19138
|
return null;
|
|
19102
19139
|
}
|
|
19103
|
-
// @ts-expect-error - TS2339 - Property 'getWidget' does not exist on type 'ReactInstance'.
|
|
19104
19140
|
return ref.getWidget();
|
|
19105
19141
|
};
|
|
19106
19142
|
this._onWidgetFocus = (id, focusPath = []) => {
|
|
@@ -19788,7 +19824,9 @@ class Renderer extends React.Component {
|
|
|
19788
19824
|
const widget = this.getWidgetInstance(id);
|
|
19789
19825
|
// widget can be undefined if it hasn't yet been rendered
|
|
19790
19826
|
if (widget && widget.simpleValidate) {
|
|
19791
|
-
widgetScores[id] = widget.simpleValidate(props == null ? void 0 : props.options,
|
|
19827
|
+
widgetScores[id] = widget.simpleValidate(_extends({}, props == null ? void 0 : props.options, {
|
|
19828
|
+
scoring: true
|
|
19829
|
+
}), onInputError);
|
|
19792
19830
|
}
|
|
19793
19831
|
});
|
|
19794
19832
|
return widgetScores;
|
|
@@ -19938,9 +19976,7 @@ class Renderer extends React.Component {
|
|
|
19938
19976
|
// they are re-rendered, so even if they've been
|
|
19939
19977
|
// re-rendered we need to call these methods on them.
|
|
19940
19978
|
_.each(this.widgetIds, id => {
|
|
19941
|
-
|
|
19942
|
-
const container = this.refs["container:" + id];
|
|
19943
|
-
// @ts-expect-error - TS2339 - Property 'replaceWidgetProps' does not exist on type 'ReactInstance'.
|
|
19979
|
+
const container = this._widgetContainers.get(makeContainerId(id));
|
|
19944
19980
|
container && container.replaceWidgetProps(this.getWidgetProps(id));
|
|
19945
19981
|
});
|
|
19946
19982
|
if (this.props.serializedState && !_.isEqual(this.props.serializedState, this.getSerializedState())) {
|
|
@@ -20858,21 +20894,6 @@ InputWithExamples.defaultProps = {
|
|
|
20858
20894
|
className: ""
|
|
20859
20895
|
};
|
|
20860
20896
|
|
|
20861
|
-
function _objectWithoutPropertiesLoose(source, excluded) {
|
|
20862
|
-
if (source == null) return {};
|
|
20863
|
-
var target = {};
|
|
20864
|
-
var sourceKeys = Object.keys(source);
|
|
20865
|
-
var key, i;
|
|
20866
|
-
|
|
20867
|
-
for (i = 0; i < sourceKeys.length; i++) {
|
|
20868
|
-
key = sourceKeys[i];
|
|
20869
|
-
if (excluded.indexOf(key) >= 0) continue;
|
|
20870
|
-
target[key] = source[key];
|
|
20871
|
-
}
|
|
20872
|
-
|
|
20873
|
-
return target;
|
|
20874
|
-
}
|
|
20875
|
-
|
|
20876
20897
|
const _excluded$3 = ["keypadElement", "onFocus", "value"];
|
|
20877
20898
|
class SimpleKeypadInput extends React.Component {
|
|
20878
20899
|
constructor(...args) {
|
|
@@ -42117,8 +42138,7 @@ class Transformer extends React.Component {
|
|
|
42117
42138
|
// @ts-expect-error - TS2345 - Argument of type '{ type: "dilation"; center: any; scale: number; }' is not assignable to parameter of type 'PerseusTransformerTransformation'.
|
|
42118
42139
|
self.doTransform({
|
|
42119
42140
|
type: "dilation",
|
|
42120
|
-
|
|
42121
|
-
// will still be defined here. center: self.dilationCircle.centerPoint.coord,
|
|
42141
|
+
center: self.dilationCircle.centerPoint.coord,
|
|
42122
42142
|
scale: newRadius / oldRadius
|
|
42123
42143
|
});
|
|
42124
42144
|
},
|
|
@@ -43748,7 +43768,7 @@ const init = function init(options) {
|
|
|
43748
43768
|
|
|
43749
43769
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
43750
43770
|
const libName = "@khanacademy/perseus";
|
|
43751
|
-
const libVersion = "12.
|
|
43771
|
+
const libVersion = "12.3.0";
|
|
43752
43772
|
addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
43753
43773
|
|
|
43754
43774
|
const apiVersion = {
|
|
@@ -43770,6 +43790,7 @@ class ArticleRenderer extends React.Component {
|
|
|
43770
43790
|
constructor(props) {
|
|
43771
43791
|
super(props);
|
|
43772
43792
|
this._currentFocus = void 0;
|
|
43793
|
+
this.sectionRenderers = [];
|
|
43773
43794
|
this._handleFocusChange = (newFocusPath, oldFocusPath) => {
|
|
43774
43795
|
// TODO(charlie): DRY this up--some of this logic is repeated in
|
|
43775
43796
|
// ItemRenderer.
|
|
@@ -43794,17 +43815,17 @@ class ArticleRenderer extends React.Component {
|
|
|
43794
43815
|
// paths, so as to check whether the focused path represents an
|
|
43795
43816
|
// input.
|
|
43796
43817
|
let didFocusInput = false;
|
|
43818
|
+
let focusedInput;
|
|
43797
43819
|
if (this._currentFocus) {
|
|
43798
|
-
const [
|
|
43799
|
-
|
|
43800
|
-
// @ts-expect-error - TS2339 - Property 'getInputPaths' does not exist on type 'ReactInstance'.
|
|
43801
|
-
const inputPaths = this.refs[sectionRef].getInputPaths();
|
|
43820
|
+
const [sectionIndex, ...focusPath] = this._currentFocus;
|
|
43821
|
+
const inputPaths = this.sectionRenderers[sectionIndex].getInputPaths();
|
|
43802
43822
|
didFocusInput = inputPaths.some(inputPath => {
|
|
43803
43823
|
return Util.inputPathsEqual(inputPath, focusPath);
|
|
43804
43824
|
});
|
|
43825
|
+
focusedInput = this.sectionRenderers[sectionIndex].getDOMNodeForPath(focusPath);
|
|
43805
43826
|
}
|
|
43806
43827
|
if (this.props.apiOptions.onFocusChange != null) {
|
|
43807
|
-
this.props.apiOptions.onFocusChange(this._currentFocus, prevFocusPath, didFocusInput ? keypadElement == null ? void 0 : keypadElement.getDOMNode() : null);
|
|
43828
|
+
this.props.apiOptions.onFocusChange(this._currentFocus, prevFocusPath, didFocusInput ? keypadElement == null ? void 0 : keypadElement.getDOMNode() : null, didFocusInput ? focusedInput : null);
|
|
43808
43829
|
}
|
|
43809
43830
|
if (keypadElement && isMobile) {
|
|
43810
43831
|
if (didFocusInput) {
|
|
@@ -43836,10 +43857,8 @@ class ArticleRenderer extends React.Component {
|
|
|
43836
43857
|
};
|
|
43837
43858
|
this.blur = () => {
|
|
43838
43859
|
if (this._currentFocus) {
|
|
43839
|
-
const [
|
|
43840
|
-
|
|
43841
|
-
// @ts-expect-error - TS2339 - Property 'blurPath' does not exist on type 'ReactInstance'.
|
|
43842
|
-
this.refs[sectionRef].blurPath(inputPath);
|
|
43860
|
+
const [sectionIndex, ...inputPath] = this._currentFocus;
|
|
43861
|
+
this.sectionRenderers[sectionIndex].blurPath(inputPath);
|
|
43843
43862
|
}
|
|
43844
43863
|
};
|
|
43845
43864
|
this._sections = () => {
|
|
@@ -43865,33 +43884,39 @@ class ArticleRenderer extends React.Component {
|
|
|
43865
43884
|
});
|
|
43866
43885
|
|
|
43867
43886
|
// TODO(alex): Add mobile api functions and pass them down here
|
|
43868
|
-
|
|
43869
|
-
|
|
43870
|
-
|
|
43871
|
-
|
|
43872
|
-
|
|
43873
|
-
key:
|
|
43887
|
+
// We're using the index as the key here because we don't have a unique
|
|
43888
|
+
// identifier for each section. This should be fine as we never remove
|
|
43889
|
+
// or reorder sections.
|
|
43890
|
+
const sections = this._sections().map((section, sectionIndex) => {
|
|
43891
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
43892
|
+
key: sectionIndex,
|
|
43874
43893
|
className: "clearfix"
|
|
43875
43894
|
}, /*#__PURE__*/React.createElement(Renderer, _extends({}, section, {
|
|
43876
|
-
ref:
|
|
43877
|
-
|
|
43878
|
-
|
|
43895
|
+
ref: elem => {
|
|
43896
|
+
if (elem) {
|
|
43897
|
+
this.sectionRenderers[sectionIndex] = elem;
|
|
43898
|
+
}
|
|
43899
|
+
},
|
|
43900
|
+
key: sectionIndex,
|
|
43901
|
+
key_: sectionIndex,
|
|
43879
43902
|
keypadElement: this.props.keypadElement,
|
|
43880
43903
|
apiOptions: _extends({}, apiOptions, {
|
|
43881
43904
|
onFocusChange: (newFocusPath, oldFocusPath) => {
|
|
43882
|
-
// Prefix the paths with the relevant section,
|
|
43905
|
+
// Prefix the paths with the relevant section index,
|
|
43883
43906
|
// so as to allow us to distinguish between
|
|
43884
43907
|
// equivalently-named inputs across Renderers.
|
|
43885
|
-
this._handleFocusChange(newFocusPath && [
|
|
43908
|
+
this._handleFocusChange(newFocusPath && [sectionIndex].concat(newFocusPath), oldFocusPath && [sectionIndex].concat(oldFocusPath));
|
|
43886
43909
|
}
|
|
43887
43910
|
}),
|
|
43888
43911
|
linterContext: PerseusLinter.pushContextStack(this.props.linterContext, "article"),
|
|
43889
43912
|
legacyPerseusLint: this.props.legacyPerseusLint
|
|
43890
|
-
})))
|
|
43913
|
+
}, this.props.dependencies)));
|
|
43891
43914
|
});
|
|
43892
43915
|
return /*#__PURE__*/React.createElement("div", {
|
|
43893
43916
|
className: classes
|
|
43894
|
-
},
|
|
43917
|
+
}, /*#__PURE__*/React.createElement(DependenciesContext.Provider, {
|
|
43918
|
+
value: this.props.dependencies
|
|
43919
|
+
}, sections));
|
|
43895
43920
|
}
|
|
43896
43921
|
}
|
|
43897
43922
|
ArticleRenderer.defaultProps = {
|
|
@@ -44970,7 +44995,7 @@ class ServerItemRenderer extends React.Component {
|
|
|
44970
44995
|
};
|
|
44971
44996
|
const questionRenderer = /*#__PURE__*/React.createElement(context$1.Provider, {
|
|
44972
44997
|
value: contextValue
|
|
44973
|
-
}, /*#__PURE__*/React.createElement(Renderer, {
|
|
44998
|
+
}, /*#__PURE__*/React.createElement(Renderer, _extends({
|
|
44974
44999
|
keypadElement: this.props.keypadElement,
|
|
44975
45000
|
problemNum: this.props.problemNum,
|
|
44976
45001
|
onInteractWithWidget: this.handleInteractWithWidget,
|
|
@@ -44986,7 +45011,7 @@ class ServerItemRenderer extends React.Component {
|
|
|
44986
45011
|
content: this.props.item.question.content,
|
|
44987
45012
|
widgets: this.props.item.question.widgets,
|
|
44988
45013
|
images: this.props.item.question.images
|
|
44989
|
-
}));
|
|
45014
|
+
}, this.props.dependencies)));
|
|
44990
45015
|
const hintsRenderer = /*#__PURE__*/React.createElement(HintsRenderer, {
|
|
44991
45016
|
hints: this.props.item.hints,
|
|
44992
45017
|
hintsVisible: this.props.hintsVisible,
|