@khanacademy/perseus 12.2.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 +23 -0
- package/dist/article-renderer.d.ts +3 -2
- package/dist/error-boundary.d.ts +1 -0
- package/dist/es/index.css +1 -66
- package/dist/es/index.js +86 -66
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +1 -66
- package/dist/index.js +77 -54
- 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/__tests__/renderer-api.test.tsx +18 -23
- package/src/article-renderer.tsx +15 -13
- 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 +0 -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/dist/index.css
CHANGED
|
@@ -2684,71 +2684,6 @@ body.mobile .categorizer-container td.category input[type="radio"] + span:active
|
|
|
2684
2684
|
#answer_area .perseus-widget-expression .error-tooltip .tooltipContainer > div:first-child {
|
|
2685
2685
|
visibility: hidden !important;
|
|
2686
2686
|
}
|
|
2687
|
-
.perseus-widget-expression-old,
|
|
2688
|
-
.perseus-widget-expression-old > .output,
|
|
2689
|
-
.perseus-widget-expression-old > .output > .tex,
|
|
2690
|
-
.perseus-widget-expression-old > .output > .placeholder {
|
|
2691
|
-
display: block;
|
|
2692
|
-
}
|
|
2693
|
-
.perseus-widget-expression-old input,
|
|
2694
|
-
#answer_area .perseus-widget-expression-old input {
|
|
2695
|
-
direction: ltr;
|
|
2696
|
-
border: 1px solid #a4a4a4;
|
|
2697
|
-
border-radius: 5px;
|
|
2698
|
-
box-sizing: border-box;
|
|
2699
|
-
font-size: 14px;
|
|
2700
|
-
margin-bottom: 5px;
|
|
2701
|
-
max-width: 240px;
|
|
2702
|
-
padding: 6px;
|
|
2703
|
-
width: 100%;
|
|
2704
|
-
}
|
|
2705
|
-
.perseus-widget-expression-old > .output {
|
|
2706
|
-
background: #f2f2f2;
|
|
2707
|
-
border-radius: 5px;
|
|
2708
|
-
padding: 10px;
|
|
2709
|
-
margin: 10px 0;
|
|
2710
|
-
}
|
|
2711
|
-
#answer_area .perseus-widget-expression-old > .output {
|
|
2712
|
-
background: #f7f7f7;
|
|
2713
|
-
}
|
|
2714
|
-
.perseus-widget-expression-old > .output > .tex {
|
|
2715
|
-
overflow-x: auto;
|
|
2716
|
-
padding: 5px;
|
|
2717
|
-
}
|
|
2718
|
-
.perseus-widget-expression-old > .output > .placeholder {
|
|
2719
|
-
position: relative;
|
|
2720
|
-
height: 40px;
|
|
2721
|
-
overflow-y: hidden;
|
|
2722
|
-
}
|
|
2723
|
-
.perseus-widget-expression-old > .output > .placeholder > .error {
|
|
2724
|
-
border-radius: 10px;
|
|
2725
|
-
background: #f7f7f7;
|
|
2726
|
-
border: 1px solid #ddd;
|
|
2727
|
-
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.5);
|
|
2728
|
-
color: #000;
|
|
2729
|
-
display: none;
|
|
2730
|
-
font-weight: normal;
|
|
2731
|
-
min-height: 22px;
|
|
2732
|
-
width: 168px;
|
|
2733
|
-
position: absolute;
|
|
2734
|
-
top: 0px;
|
|
2735
|
-
left: 40px;
|
|
2736
|
-
margin: auto;
|
|
2737
|
-
}
|
|
2738
|
-
.perseus-widget-expression-old > .output > .placeholder > .error > .buddy {
|
|
2739
|
-
background-image: url(/images/perseus/error-buddy.png);
|
|
2740
|
-
background-size: 100%;
|
|
2741
|
-
height: 36px;
|
|
2742
|
-
width: 40px;
|
|
2743
|
-
position: absolute;
|
|
2744
|
-
top: -3px;
|
|
2745
|
-
left: -42px;
|
|
2746
|
-
}
|
|
2747
|
-
.perseus-widget-expression-old > .output > .placeholder > .error > .message {
|
|
2748
|
-
font: 12px inherit;
|
|
2749
|
-
line-height: 1.4em;
|
|
2750
|
-
margin: 0px 4px;
|
|
2751
|
-
}
|
|
2752
2687
|
.perseus-widget-grapher {
|
|
2753
2688
|
box-sizing: content-box;
|
|
2754
2689
|
padding: 25px 25px 0 0;
|
|
@@ -3121,7 +3056,7 @@ body.mobile .perseus-matrix .math-output {
|
|
|
3121
3056
|
}
|
|
3122
3057
|
.framework-perseus.perseus-article .perseus-renderer .perseus-widget-passage-container .perseus-widget-passage div.paragraph,
|
|
3123
3058
|
.perseus-widget-passage-container .perseus-widget-passage div.paragraph {
|
|
3124
|
-
font-family:
|
|
3059
|
+
font-family: KaTeX_Main, Times, "Times New Roman", serif;
|
|
3125
3060
|
}
|
|
3126
3061
|
.framework-perseus.perseus-article .perseus-renderer .perseus-widget-passage-container .perseus-widget-passage .passage-title div.paragraph,
|
|
3127
3062
|
.perseus-widget-passage-container .perseus-widget-passage .passage-title div.paragraph {
|
package/dist/index.js
CHANGED
|
@@ -2007,11 +2007,26 @@ class Expression extends React__namespace.Component {
|
|
|
2007
2007
|
});
|
|
2008
2008
|
}
|
|
2009
2009
|
});
|
|
2010
|
-
_defineProperty(this, "simpleValidate", (
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2010
|
+
_defineProperty(this, "simpleValidate", (_ref, onInputError) => {
|
|
2011
|
+
let {
|
|
2012
|
+
scoring,
|
|
2013
|
+
...rubric
|
|
2014
|
+
} = _ref;
|
|
2015
|
+
const score = Expression.validate(this.getUserInput(), rubric, onInputError || function () {});
|
|
2016
|
+
|
|
2017
|
+
// "scoring" is a flag that indicates when we are checking answers.
|
|
2018
|
+
// otherwise, we may just be checking validity after changes.
|
|
2019
|
+
if (scoring && score.type !== "invalid") {
|
|
2020
|
+
var _this$props$analytics;
|
|
2021
|
+
(_this$props$analytics = this.props.analytics) === null || _this$props$analytics === void 0 ? void 0 : _this$props$analytics.onAnalyticsEvent({
|
|
2022
|
+
type: "perseus:expression-evaluated",
|
|
2023
|
+
payload: {
|
|
2024
|
+
result: score.earned === score.total ? "correct" : "incorrect",
|
|
2025
|
+
virtualKeypadVersion: deriveKeypadVersion(this.props.apiOptions)
|
|
2026
|
+
}
|
|
2027
|
+
});
|
|
2028
|
+
}
|
|
2029
|
+
return score;
|
|
2015
2030
|
});
|
|
2016
2031
|
_defineProperty(this, "getUserInput", () => {
|
|
2017
2032
|
return Expression.getUserInputFromProps(this.props);
|
|
@@ -2036,8 +2051,8 @@ class Expression extends React__namespace.Component {
|
|
|
2036
2051
|
this.props.trackInteraction();
|
|
2037
2052
|
});
|
|
2038
2053
|
_defineProperty(this, "_handleFocus", () => {
|
|
2039
|
-
var _this$props$
|
|
2040
|
-
(_this$props$
|
|
2054
|
+
var _this$props$analytics2;
|
|
2055
|
+
(_this$props$analytics2 = this.props.analytics) === null || _this$props$analytics2 === void 0 ? void 0 : _this$props$analytics2.onAnalyticsEvent({
|
|
2041
2056
|
type: "perseus:expression-focused",
|
|
2042
2057
|
payload: null
|
|
2043
2058
|
});
|
|
@@ -2244,16 +2259,6 @@ class Expression extends React__namespace.Component {
|
|
|
2244
2259
|
// @ts-expect-error - TS2339 - Property 'insert' does not exist on type 'ReactInstance'.
|
|
2245
2260
|
this.refs.input.insert(keyPressed);
|
|
2246
2261
|
}
|
|
2247
|
-
sendExpressionEvaluatedEvent(result) {
|
|
2248
|
-
var _this$props$analytics2;
|
|
2249
|
-
(_this$props$analytics2 = this.props.analytics) === null || _this$props$analytics2 === void 0 ? void 0 : _this$props$analytics2.onAnalyticsEvent({
|
|
2250
|
-
type: "perseus:expression-evaluated",
|
|
2251
|
-
payload: {
|
|
2252
|
-
result,
|
|
2253
|
-
virtualKeypadVersion: deriveKeypadVersion(this.props.apiOptions)
|
|
2254
|
-
}
|
|
2255
|
-
});
|
|
2256
|
-
}
|
|
2257
2262
|
render() {
|
|
2258
2263
|
var _this$props$keypadCon, _this$props$analytics3;
|
|
2259
2264
|
if (this.props.apiOptions.customKeypad) {
|
|
@@ -3739,10 +3744,10 @@ Zoom.prototype.dispose = function () {
|
|
|
3739
3744
|
};
|
|
3740
3745
|
const ZoomService = new ZoomServiceClass();
|
|
3741
3746
|
|
|
3742
|
-
// @baseFontFamily: "
|
|
3743
|
-
const baseFontFamily$1 = "'
|
|
3744
|
-
// @boldFontFamily: "
|
|
3745
|
-
const boldFontFamily = "'
|
|
3747
|
+
// @baseFontFamily: "Lato", sans-serif;
|
|
3748
|
+
const baseFontFamily$1 = "'Lato', sans-serif";
|
|
3749
|
+
// @boldFontFamily: "Lato-Bold", "Lato", sans-serif;
|
|
3750
|
+
const boldFontFamily = "'Lato-Bold', 'Lato', sans-serif";
|
|
3746
3751
|
// @green: #76a005;
|
|
3747
3752
|
const green$1 = "#76A005";
|
|
3748
3753
|
// @kaGreen: #71b307;
|
|
@@ -18763,6 +18768,7 @@ var widgets$1 = /*#__PURE__*/Object.freeze({
|
|
|
18763
18768
|
class WidgetContainer extends React__namespace.Component {
|
|
18764
18769
|
constructor() {
|
|
18765
18770
|
super(...arguments);
|
|
18771
|
+
_defineProperty(this, "widgetRef", /*#__PURE__*/React__namespace.createRef());
|
|
18766
18772
|
_defineProperty(this, "state", {
|
|
18767
18773
|
// TODO(benkomalo): before we're mounted, we don't know how big
|
|
18768
18774
|
// we're going to be, so just default to MEDIUM for now. :/ In the
|
|
@@ -18772,8 +18778,7 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
18772
18778
|
widgetProps: this.props.initialProps
|
|
18773
18779
|
});
|
|
18774
18780
|
_defineProperty(this, "getWidget", () => {
|
|
18775
|
-
|
|
18776
|
-
return this.refs.widget;
|
|
18781
|
+
return this.widgetRef.current;
|
|
18777
18782
|
});
|
|
18778
18783
|
_defineProperty(this, "replaceWidgetProps", newWidgetProps => {
|
|
18779
18784
|
this.setState({
|
|
@@ -18868,14 +18873,30 @@ class WidgetContainer extends React__namespace.Component {
|
|
|
18868
18873
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
18869
18874
|
className: className,
|
|
18870
18875
|
style: isStatic ? staticContainerStyles : {}
|
|
18871
|
-
}, /*#__PURE__*/React__namespace.createElement(
|
|
18872
|
-
|
|
18873
|
-
|
|
18874
|
-
|
|
18875
|
-
,
|
|
18876
|
-
|
|
18877
|
-
|
|
18878
|
-
|
|
18876
|
+
}, /*#__PURE__*/React__namespace.createElement(DependenciesContext.Consumer, null, _ref => {
|
|
18877
|
+
let {
|
|
18878
|
+
analytics
|
|
18879
|
+
} = _ref;
|
|
18880
|
+
return /*#__PURE__*/React__namespace.createElement(ErrorBoundary, {
|
|
18881
|
+
metadata: {
|
|
18882
|
+
widget_type: type,
|
|
18883
|
+
widget_id: this.props.id
|
|
18884
|
+
},
|
|
18885
|
+
onError: error => {
|
|
18886
|
+
analytics.onAnalyticsEvent({
|
|
18887
|
+
type: "perseus:widget-rendering-error",
|
|
18888
|
+
payload: {
|
|
18889
|
+
widgetType: type
|
|
18890
|
+
}
|
|
18891
|
+
});
|
|
18892
|
+
}
|
|
18893
|
+
}, /*#__PURE__*/React__namespace.createElement(WidgetType, _extends({}, this.state.widgetProps, {
|
|
18894
|
+
linterContext: linterContext,
|
|
18895
|
+
containerSizeClass: this.state.sizeClass,
|
|
18896
|
+
ref: this.widgetRef
|
|
18897
|
+
})), isStatic && /*#__PURE__*/React__namespace.createElement("div", {
|
|
18898
|
+
style: staticOverlayStyles
|
|
18899
|
+
}));
|
|
18879
18900
|
}));
|
|
18880
18901
|
}
|
|
18881
18902
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
@@ -18899,6 +18920,7 @@ const rContainsNonWhitespace = /\S/;
|
|
|
18899
18920
|
const rImageURL = /(web\+graphie|https):\/\/[^\s]*/;
|
|
18900
18921
|
const noopOnRender = () => {};
|
|
18901
18922
|
const SHOULD_CLEAR_WIDGETS_PROP_LIST = ["content", "problemNum", "widgets"];
|
|
18923
|
+
const makeContainerId = id => "container:" + id;
|
|
18902
18924
|
|
|
18903
18925
|
// Check if one focus path / id path is a prefix of another
|
|
18904
18926
|
// The focus path null will never be a prefix of any non-null
|
|
@@ -18945,6 +18967,7 @@ class Renderer extends React__namespace.Component {
|
|
|
18945
18967
|
_defineProperty(this, "_isMounted", void 0);
|
|
18946
18968
|
_defineProperty(this, "_isTwoColumn", void 0);
|
|
18947
18969
|
_defineProperty(this, "_translationLinter", void 0);
|
|
18970
|
+
_defineProperty(this, "_widgetContainers", new Map());
|
|
18948
18971
|
_defineProperty(this, "lastRenderedMarkdown", void 0);
|
|
18949
18972
|
_defineProperty(this, "reuseMarkdown", void 0);
|
|
18950
18973
|
_defineProperty(this, "translationIndex", void 0);
|
|
@@ -19018,19 +19041,22 @@ class Renderer extends React__namespace.Component {
|
|
|
19018
19041
|
// By this point we should have no duplicates, which are
|
|
19019
19042
|
// filtered out in this.render(), so we shouldn't have to
|
|
19020
19043
|
// worry about using this widget key and ref:
|
|
19021
|
-
return /*#__PURE__*/React__namespace.createElement(
|
|
19022
|
-
key:
|
|
19023
|
-
|
|
19024
|
-
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19044
|
+
return /*#__PURE__*/React__namespace.createElement(WidgetContainer, {
|
|
19045
|
+
key: makeContainerId(id),
|
|
19046
|
+
id: id,
|
|
19047
|
+
ref: node => {
|
|
19048
|
+
const containerId = makeContainerId(id);
|
|
19049
|
+
if (node != null) {
|
|
19050
|
+
this._widgetContainers.set(containerId, node);
|
|
19051
|
+
} else {
|
|
19052
|
+
this._widgetContainers.delete(containerId);
|
|
19053
|
+
}
|
|
19054
|
+
},
|
|
19029
19055
|
type: type,
|
|
19030
19056
|
initialProps: this.getWidgetProps(id),
|
|
19031
19057
|
shouldHighlight: shouldHighlight,
|
|
19032
19058
|
linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "widget")
|
|
19033
|
-
})
|
|
19059
|
+
});
|
|
19034
19060
|
}
|
|
19035
19061
|
return null;
|
|
19036
19062
|
});
|
|
@@ -19058,7 +19084,6 @@ class Renderer extends React__namespace.Component {
|
|
|
19058
19084
|
}
|
|
19059
19085
|
return {
|
|
19060
19086
|
...widgetProps,
|
|
19061
|
-
ref: id,
|
|
19062
19087
|
widgetId: id,
|
|
19063
19088
|
alignment: widgetInfo && widgetInfo.alignment,
|
|
19064
19089
|
// When determining if a widget is static, we verify that the widget is not an
|
|
@@ -19191,12 +19216,10 @@ class Renderer extends React__namespace.Component {
|
|
|
19191
19216
|
return [...this.findInternalWidgets(filterCriterion), ...this.props.findExternalWidgets(filterCriterion)];
|
|
19192
19217
|
});
|
|
19193
19218
|
_defineProperty(this, "getWidgetInstance", id => {
|
|
19194
|
-
|
|
19195
|
-
const ref = this.refs["container:" + id];
|
|
19219
|
+
const ref = this._widgetContainers.get(makeContainerId(id));
|
|
19196
19220
|
if (!ref) {
|
|
19197
19221
|
return null;
|
|
19198
19222
|
}
|
|
19199
|
-
// @ts-expect-error - TS2339 - Property 'getWidget' does not exist on type 'ReactInstance'.
|
|
19200
19223
|
return ref.getWidget();
|
|
19201
19224
|
});
|
|
19202
19225
|
_defineProperty(this, "_onWidgetFocus", function (id) {
|
|
@@ -19910,7 +19933,10 @@ class Renderer extends React__namespace.Component {
|
|
|
19910
19933
|
const widget = this.getWidgetInstance(id);
|
|
19911
19934
|
// widget can be undefined if it hasn't yet been rendered
|
|
19912
19935
|
if (widget && widget.simpleValidate) {
|
|
19913
|
-
widgetScores[id] = widget.simpleValidate(
|
|
19936
|
+
widgetScores[id] = widget.simpleValidate({
|
|
19937
|
+
...(props === null || props === void 0 ? void 0 : props.options),
|
|
19938
|
+
scoring: true
|
|
19939
|
+
}, onInputError);
|
|
19914
19940
|
}
|
|
19915
19941
|
});
|
|
19916
19942
|
return widgetScores;
|
|
@@ -20061,9 +20087,7 @@ class Renderer extends React__namespace.Component {
|
|
|
20061
20087
|
// they are re-rendered, so even if they've been
|
|
20062
20088
|
// re-rendered we need to call these methods on them.
|
|
20063
20089
|
___default["default"].each(this.widgetIds, id => {
|
|
20064
|
-
|
|
20065
|
-
const container = this.refs["container:" + id];
|
|
20066
|
-
// @ts-expect-error - TS2339 - Property 'replaceWidgetProps' does not exist on type 'ReactInstance'.
|
|
20090
|
+
const container = this._widgetContainers.get(makeContainerId(id));
|
|
20067
20091
|
container && container.replaceWidgetProps(this.getWidgetProps(id));
|
|
20068
20092
|
});
|
|
20069
20093
|
if (this.props.serializedState && !___default["default"].isEqual(this.props.serializedState, this.getSerializedState())) {
|
|
@@ -42366,8 +42390,7 @@ class Transformer extends React__namespace.Component {
|
|
|
42366
42390
|
// @ts-expect-error - TS2345 - Argument of type '{ type: "dilation"; center: any; scale: number; }' is not assignable to parameter of type 'PerseusTransformerTransformation'.
|
|
42367
42391
|
self.doTransform({
|
|
42368
42392
|
type: "dilation",
|
|
42369
|
-
|
|
42370
|
-
// will still be defined here. center: self.dilationCircle.centerPoint.coord,
|
|
42393
|
+
center: self.dilationCircle.centerPoint.coord,
|
|
42371
42394
|
scale: newRadius / oldRadius
|
|
42372
42395
|
});
|
|
42373
42396
|
},
|
|
@@ -44015,7 +44038,7 @@ const init = function (options) {
|
|
|
44015
44038
|
|
|
44016
44039
|
// This file is processed by a Rollup plugin (replace) to inject the production
|
|
44017
44040
|
const libName = "@khanacademy/perseus";
|
|
44018
|
-
const libVersion = "12.
|
|
44041
|
+
const libVersion = "12.3.0";
|
|
44019
44042
|
perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
|
|
44020
44043
|
|
|
44021
44044
|
const apiVersion = {
|
|
@@ -44160,7 +44183,7 @@ class ArticleRenderer extends React__namespace.Component {
|
|
|
44160
44183
|
},
|
|
44161
44184
|
linterContext: PerseusLinter__namespace.pushContextStack(this.props.linterContext, "article"),
|
|
44162
44185
|
legacyPerseusLint: this.props.legacyPerseusLint
|
|
44163
|
-
})));
|
|
44186
|
+
}, this.props.dependencies)));
|
|
44164
44187
|
});
|
|
44165
44188
|
return /*#__PURE__*/React__namespace.createElement("div", {
|
|
44166
44189
|
className: classes
|
|
@@ -45256,7 +45279,7 @@ class ServerItemRenderer extends React__namespace.Component {
|
|
|
45256
45279
|
};
|
|
45257
45280
|
const questionRenderer = /*#__PURE__*/React__namespace.createElement(context$1.Provider, {
|
|
45258
45281
|
value: contextValue
|
|
45259
|
-
}, /*#__PURE__*/React__namespace.createElement(Renderer, {
|
|
45282
|
+
}, /*#__PURE__*/React__namespace.createElement(Renderer, _extends({
|
|
45260
45283
|
keypadElement: this.props.keypadElement,
|
|
45261
45284
|
problemNum: this.props.problemNum,
|
|
45262
45285
|
onInteractWithWidget: this.handleInteractWithWidget,
|
|
@@ -45272,7 +45295,7 @@ class ServerItemRenderer extends React__namespace.Component {
|
|
|
45272
45295
|
content: this.props.item.question.content,
|
|
45273
45296
|
widgets: this.props.item.question.widgets,
|
|
45274
45297
|
images: this.props.item.question.images
|
|
45275
|
-
}));
|
|
45298
|
+
}, this.props.dependencies)));
|
|
45276
45299
|
const hintsRenderer = /*#__PURE__*/React__namespace.createElement(HintsRenderer, {
|
|
45277
45300
|
hints: this.props.item.hints,
|
|
45278
45301
|
hintsVisible: this.props.hintsVisible,
|