@khanacademy/perseus 1.2.0 → 1.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 +11 -0
- package/dist/es/index.css +3 -3
- package/dist/es/index.js +138 -167
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +3 -3
- package/dist/index.js +136 -165
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/styles/widgets/passage.less +7 -3
- package/src/widgets/__stories__/passage-ref.stories.jsx +6 -2
- package/src/widgets/__stories__/passage.stories.jsx +9 -1
- package/src/widgets/__stories__/radio.stories.jsx +11 -0
- package/src/widgets/__testdata__/passage-ref_testdata.js +62 -0
- package/src/widgets/__testdata__/passage_testdata.js +25 -0
- package/src/widgets/__testdata__/radio_testdata.js +52 -0
- package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +18 -18
- package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +36 -36
- package/src/widgets/__tests__/passage_test.jsx +6 -8
- package/src/widgets/__tests__/radio_test.jsx +3 -1
- package/src/widgets/passage.jsx +12 -41
- package/src/widgets/radio/widget.jsx +128 -129
- package/src/widgets/passage/get-line-height-for-node.js +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @khanacademy/perseus
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e783be76: Fix passage fonts because content depends on it being an exact size
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- ec92d308: Fix bug with Passage where line numbers stopped abruptly
|
|
12
|
+
- dd55b5e4: Update gutter line numbers in passage
|
|
13
|
+
|
|
3
14
|
## 1.2.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/dist/es/index.css
CHANGED
|
@@ -3070,11 +3070,11 @@ body.mobile .perseus-matrix .math-output {
|
|
|
3070
3070
|
margin: 0 0 10px;
|
|
3071
3071
|
}
|
|
3072
3072
|
.perseus-widget-passage-container .passage-text div.paragraph {
|
|
3073
|
-
font-family: "Noto Serif", serif;
|
|
3074
3073
|
font-weight: 400;
|
|
3075
|
-
font-size:
|
|
3076
|
-
line-height:
|
|
3074
|
+
font-size: 16px;
|
|
3075
|
+
line-height: 20px;
|
|
3077
3076
|
text-indent: 20px;
|
|
3077
|
+
margin: 0;
|
|
3078
3078
|
}
|
|
3079
3079
|
.perseus-widget-passage-container .passage-text div.paragraph span {
|
|
3080
3080
|
text-indent: 0;
|
package/dist/es/index.js
CHANGED
|
@@ -25344,10 +25344,6 @@ var styles$w = StyleSheet.create({
|
|
|
25344
25344
|
}
|
|
25345
25345
|
});
|
|
25346
25346
|
|
|
25347
|
-
var getLineHeightForNode = (line1, line2) => {
|
|
25348
|
-
return $(line2).offset().top - $(line1).offset().top;
|
|
25349
|
-
};
|
|
25350
|
-
|
|
25351
25347
|
function getInitialParseState() {
|
|
25352
25348
|
return {
|
|
25353
25349
|
currentRef: [],
|
|
@@ -25716,65 +25712,40 @@ var PassageMarkdown = {
|
|
|
25716
25712
|
getInitialParseState
|
|
25717
25713
|
};
|
|
25718
25714
|
|
|
25719
|
-
// A fake paragraph to measure the line height of the passage
|
|
25720
|
-
//
|
|
25721
|
-
// the line height often ends up being a fractional number of pixels close to
|
|
25722
|
-
// 22 pixels.
|
|
25715
|
+
// A fake paragraph to measure the line height of the passage,
|
|
25716
|
+
// so we can adapt to browser zoom
|
|
25723
25717
|
class LineHeightMeasurer extends React.Component {
|
|
25724
25718
|
constructor() {
|
|
25725
25719
|
super(...arguments);
|
|
25726
25720
|
|
|
25727
|
-
_defineProperty(this, "
|
|
25728
|
-
|
|
25729
|
-
_defineProperty(this, "body", void 0);
|
|
25730
|
-
|
|
25731
|
-
_defineProperty(this, "end", void 0);
|
|
25721
|
+
_defineProperty(this, "_line", void 0);
|
|
25732
25722
|
}
|
|
25733
25723
|
|
|
25734
25724
|
measureLineHeight() {
|
|
25735
|
-
if (
|
|
25736
|
-
|
|
25725
|
+
if (!this._line) {
|
|
25726
|
+
return 0;
|
|
25737
25727
|
}
|
|
25738
25728
|
|
|
25739
|
-
return this.
|
|
25740
|
-
}
|
|
25741
|
-
|
|
25742
|
-
forceMeasureLineHeight() {
|
|
25743
|
-
var body = this.body;
|
|
25744
|
-
var end = this.end;
|
|
25745
|
-
|
|
25746
|
-
if (!body || !end) {
|
|
25747
|
-
return;
|
|
25748
|
-
} // Add some text which magically fills an entire line.
|
|
25749
|
-
|
|
25750
|
-
|
|
25751
|
-
body.textContent = " \u0080"; // Now, the line height is the difference between the top of the
|
|
25752
|
-
// second line and the top of the first line.
|
|
25753
|
-
|
|
25754
|
-
this._cachedLineHeight = getLineHeightForNode(body, end); // Clear out the first line so it doesn't overlap the passage.
|
|
25755
|
-
|
|
25756
|
-
body.textContent = "";
|
|
25729
|
+
return this._line.clientHeight;
|
|
25757
25730
|
}
|
|
25758
25731
|
|
|
25759
25732
|
render() {
|
|
25760
25733
|
return /*#__PURE__*/React.createElement("div", {
|
|
25761
|
-
className: css(styles$v.measurer)
|
|
25762
|
-
|
|
25763
|
-
|
|
25734
|
+
className: css(styles$v.measurer),
|
|
25735
|
+
"aria-hidden": "true"
|
|
25736
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
25737
|
+
ref: _ref => this._line = _ref,
|
|
25764
25738
|
className: "paragraph"
|
|
25765
|
-
}
|
|
25766
|
-
ref: _ref2 => this.end = _ref2
|
|
25767
|
-
})));
|
|
25739
|
+
}, "Line Height Measurement"));
|
|
25768
25740
|
}
|
|
25769
25741
|
|
|
25770
25742
|
}
|
|
25771
|
-
|
|
25772
25743
|
var styles$v = StyleSheet.create({
|
|
25773
25744
|
measurer: {
|
|
25774
25745
|
position: "absolute",
|
|
25775
|
-
width: "100%",
|
|
25776
25746
|
top: 0,
|
|
25777
25747
|
left: 0,
|
|
25748
|
+
visibility: "hidden",
|
|
25778
25749
|
// keep from blocking text selection
|
|
25779
25750
|
zIndex: -1
|
|
25780
25751
|
}
|
|
@@ -25821,7 +25792,7 @@ class Passage extends React.Component {
|
|
|
25821
25792
|
// listen for resize events.
|
|
25822
25793
|
|
|
25823
25794
|
|
|
25824
|
-
(_this$_lineHeightMeas = this._lineHeightMeasurerRef) === null || _this$_lineHeightMeas === void 0 ? void 0 : _this$_lineHeightMeas.
|
|
25795
|
+
(_this$_lineHeightMeas = this._lineHeightMeasurerRef) === null || _this$_lineHeightMeas === void 0 ? void 0 : _this$_lineHeightMeas.measureLineHeight();
|
|
25825
25796
|
|
|
25826
25797
|
this._updateState();
|
|
25827
25798
|
}, 500);
|
|
@@ -26115,9 +26086,9 @@ class Passage extends React.Component {
|
|
|
26115
26086
|
onSerializedHighlightsUpdate: this._handleSerializedHighlightsUpdate,
|
|
26116
26087
|
serializedHighlights: this.props.highlights
|
|
26117
26088
|
}, /*#__PURE__*/React.createElement("div", {
|
|
26118
|
-
ref:
|
|
26089
|
+
ref: _ref3 => this._contentRef = _ref3
|
|
26119
26090
|
}, /*#__PURE__*/React.createElement(LineHeightMeasurer, {
|
|
26120
|
-
ref:
|
|
26091
|
+
ref: _ref2 => this._lineHeightMeasurerRef = _ref2
|
|
26121
26092
|
}), PassageMarkdown.output(parsed)));
|
|
26122
26093
|
}
|
|
26123
26094
|
|
|
@@ -28153,6 +28124,129 @@ class Radio$1 extends React.Component {
|
|
|
28153
28124
|
});
|
|
28154
28125
|
}
|
|
28155
28126
|
|
|
28127
|
+
static validate(userInput, rubric) {
|
|
28128
|
+
var numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
28129
|
+
return sum + (selected ? 1 : 0);
|
|
28130
|
+
}, 0);
|
|
28131
|
+
|
|
28132
|
+
if (numSelected === 0) {
|
|
28133
|
+
return {
|
|
28134
|
+
type: "invalid",
|
|
28135
|
+
message: null
|
|
28136
|
+
};
|
|
28137
|
+
}
|
|
28138
|
+
|
|
28139
|
+
if (userInput.numCorrect && userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
28140
|
+
return {
|
|
28141
|
+
type: "invalid",
|
|
28142
|
+
message: i18n._("Please choose the correct number of answers.")
|
|
28143
|
+
}; // If NOTA and some other answer are checked, ...
|
|
28144
|
+
}
|
|
28145
|
+
|
|
28146
|
+
if (userInput.noneOfTheAboveSelected && numSelected > 1) {
|
|
28147
|
+
return {
|
|
28148
|
+
type: "invalid",
|
|
28149
|
+
message: i18n._("'None of the above' may not be selected " + "when other answers are selected.")
|
|
28150
|
+
};
|
|
28151
|
+
}
|
|
28152
|
+
|
|
28153
|
+
var correct = userInput.choicesSelected.every((selected, i) => {
|
|
28154
|
+
var isCorrect;
|
|
28155
|
+
|
|
28156
|
+
if (userInput.noneOfTheAboveIndex === i) {
|
|
28157
|
+
isCorrect = rubric.choices.every((choice, j) => {
|
|
28158
|
+
return i === j || !choice.correct;
|
|
28159
|
+
});
|
|
28160
|
+
} else {
|
|
28161
|
+
isCorrect = !!rubric.choices[i].correct;
|
|
28162
|
+
}
|
|
28163
|
+
|
|
28164
|
+
return isCorrect === selected;
|
|
28165
|
+
});
|
|
28166
|
+
return {
|
|
28167
|
+
type: "points",
|
|
28168
|
+
earned: correct ? 1 : 0,
|
|
28169
|
+
total: 1,
|
|
28170
|
+
message: null
|
|
28171
|
+
};
|
|
28172
|
+
}
|
|
28173
|
+
|
|
28174
|
+
static getUserInputFromProps(props) {
|
|
28175
|
+
// Return checked inputs in the form {choicesSelected: [bool]}. (Dear
|
|
28176
|
+
// future timeline implementers: this used to be {value: i} before
|
|
28177
|
+
// multiple select was added)
|
|
28178
|
+
if (props.choiceStates) {
|
|
28179
|
+
var noneOfTheAboveIndex = null;
|
|
28180
|
+
var noneOfTheAboveSelected = false;
|
|
28181
|
+
var choiceStates = props.choiceStates;
|
|
28182
|
+
var choicesSelected = choiceStates.map(() => false);
|
|
28183
|
+
var countChoices = props.countChoices;
|
|
28184
|
+
var numCorrect = props.numCorrect;
|
|
28185
|
+
|
|
28186
|
+
for (var i = 0; i < choicesSelected.length; i++) {
|
|
28187
|
+
var index = props.choices[i].originalIndex;
|
|
28188
|
+
choicesSelected[index] = choiceStates[i].selected;
|
|
28189
|
+
|
|
28190
|
+
if (props.choices[i].isNoneOfTheAbove) {
|
|
28191
|
+
noneOfTheAboveIndex = index;
|
|
28192
|
+
|
|
28193
|
+
if (choicesSelected[i]) {
|
|
28194
|
+
noneOfTheAboveSelected = true;
|
|
28195
|
+
}
|
|
28196
|
+
}
|
|
28197
|
+
}
|
|
28198
|
+
|
|
28199
|
+
return {
|
|
28200
|
+
countChoices,
|
|
28201
|
+
choicesSelected,
|
|
28202
|
+
numCorrect,
|
|
28203
|
+
noneOfTheAboveIndex,
|
|
28204
|
+
noneOfTheAboveSelected
|
|
28205
|
+
}; // Support legacy choiceState implementation
|
|
28206
|
+
}
|
|
28207
|
+
/* istanbul ignore if - props.values is deprecated */
|
|
28208
|
+
|
|
28209
|
+
|
|
28210
|
+
var {
|
|
28211
|
+
values
|
|
28212
|
+
} = props;
|
|
28213
|
+
|
|
28214
|
+
if (values) {
|
|
28215
|
+
var _noneOfTheAboveIndex = null;
|
|
28216
|
+
var _noneOfTheAboveSelected = false;
|
|
28217
|
+
var _choicesSelected = [...values];
|
|
28218
|
+
var _countChoices = props.countChoices;
|
|
28219
|
+
var _numCorrect = props.numCorrect;
|
|
28220
|
+
var valuesLength = values.length;
|
|
28221
|
+
|
|
28222
|
+
for (var _i = 0; _i < valuesLength; _i++) {
|
|
28223
|
+
var _index = props.choices[_i].originalIndex;
|
|
28224
|
+
_choicesSelected[_index] = values[_i];
|
|
28225
|
+
|
|
28226
|
+
if (props.choices[_i].isNoneOfTheAbove) {
|
|
28227
|
+
_noneOfTheAboveIndex = _index;
|
|
28228
|
+
|
|
28229
|
+
if (_choicesSelected[_i]) {
|
|
28230
|
+
_noneOfTheAboveSelected = true;
|
|
28231
|
+
}
|
|
28232
|
+
}
|
|
28233
|
+
}
|
|
28234
|
+
|
|
28235
|
+
return {
|
|
28236
|
+
choicesSelected: _choicesSelected,
|
|
28237
|
+
noneOfTheAboveIndex: _noneOfTheAboveIndex,
|
|
28238
|
+
noneOfTheAboveSelected: _noneOfTheAboveSelected,
|
|
28239
|
+
countChoices: _countChoices,
|
|
28240
|
+
numCorrect: _numCorrect
|
|
28241
|
+
};
|
|
28242
|
+
} // Nothing checked
|
|
28243
|
+
|
|
28244
|
+
|
|
28245
|
+
return {
|
|
28246
|
+
choicesSelected: props.choices.map(() => false)
|
|
28247
|
+
};
|
|
28248
|
+
}
|
|
28249
|
+
|
|
28156
28250
|
// TODO(LP-10672): I think this might be unused right now. I can't find anywhere
|
|
28157
28251
|
// that we pass a value to `.focus()` and it seems to have been used for
|
|
28158
28252
|
// adding hints when editing.
|
|
@@ -28283,129 +28377,6 @@ class Radio$1 extends React.Component {
|
|
|
28283
28377
|
});
|
|
28284
28378
|
}
|
|
28285
28379
|
|
|
28286
|
-
static validate(userInput, rubric) {
|
|
28287
|
-
var numSelected = userInput.choicesSelected.reduce((sum, selected) => {
|
|
28288
|
-
return sum + (selected ? 1 : 0);
|
|
28289
|
-
}, 0);
|
|
28290
|
-
|
|
28291
|
-
if (numSelected === 0) {
|
|
28292
|
-
return {
|
|
28293
|
-
type: "invalid",
|
|
28294
|
-
message: null
|
|
28295
|
-
};
|
|
28296
|
-
}
|
|
28297
|
-
|
|
28298
|
-
if (userInput.numCorrect && userInput.numCorrect > 1 && numSelected !== userInput.numCorrect) {
|
|
28299
|
-
return {
|
|
28300
|
-
type: "invalid",
|
|
28301
|
-
message: i18n._("Please choose the correct number of answers.")
|
|
28302
|
-
}; // If NOTA and some other answer are checked, ...
|
|
28303
|
-
}
|
|
28304
|
-
|
|
28305
|
-
if (userInput.noneOfTheAboveSelected && numSelected > 1) {
|
|
28306
|
-
return {
|
|
28307
|
-
type: "invalid",
|
|
28308
|
-
message: i18n._("'None of the above' may not be selected " + "when other answers are selected.")
|
|
28309
|
-
};
|
|
28310
|
-
}
|
|
28311
|
-
|
|
28312
|
-
var correct = userInput.choicesSelected.every((selected, i) => {
|
|
28313
|
-
var isCorrect;
|
|
28314
|
-
|
|
28315
|
-
if (userInput.noneOfTheAboveIndex === i) {
|
|
28316
|
-
isCorrect = rubric.choices.every((choice, j) => {
|
|
28317
|
-
return i === j || !choice.correct;
|
|
28318
|
-
});
|
|
28319
|
-
} else {
|
|
28320
|
-
isCorrect = !!rubric.choices[i].correct;
|
|
28321
|
-
}
|
|
28322
|
-
|
|
28323
|
-
return isCorrect === selected;
|
|
28324
|
-
});
|
|
28325
|
-
return {
|
|
28326
|
-
type: "points",
|
|
28327
|
-
earned: correct ? 1 : 0,
|
|
28328
|
-
total: 1,
|
|
28329
|
-
message: null
|
|
28330
|
-
};
|
|
28331
|
-
}
|
|
28332
|
-
|
|
28333
|
-
static getUserInputFromProps(props) {
|
|
28334
|
-
// Return checked inputs in the form {choicesSelected: [bool]}. (Dear
|
|
28335
|
-
// future timeline implementers: this used to be {value: i} before
|
|
28336
|
-
// multiple select was added)
|
|
28337
|
-
if (props.choiceStates) {
|
|
28338
|
-
var noneOfTheAboveIndex = null;
|
|
28339
|
-
var noneOfTheAboveSelected = false;
|
|
28340
|
-
var choiceStates = props.choiceStates;
|
|
28341
|
-
var choicesSelected = choiceStates.map(() => false);
|
|
28342
|
-
var countChoices = props.countChoices;
|
|
28343
|
-
var numCorrect = props.numCorrect;
|
|
28344
|
-
|
|
28345
|
-
for (var i = 0; i < choicesSelected.length; i++) {
|
|
28346
|
-
var index = props.choices[i].originalIndex;
|
|
28347
|
-
choicesSelected[index] = choiceStates[i].selected;
|
|
28348
|
-
|
|
28349
|
-
if (props.choices[i].isNoneOfTheAbove) {
|
|
28350
|
-
noneOfTheAboveIndex = index;
|
|
28351
|
-
|
|
28352
|
-
if (choicesSelected[i]) {
|
|
28353
|
-
noneOfTheAboveSelected = true;
|
|
28354
|
-
}
|
|
28355
|
-
}
|
|
28356
|
-
}
|
|
28357
|
-
|
|
28358
|
-
return {
|
|
28359
|
-
countChoices,
|
|
28360
|
-
choicesSelected,
|
|
28361
|
-
numCorrect,
|
|
28362
|
-
noneOfTheAboveIndex,
|
|
28363
|
-
noneOfTheAboveSelected
|
|
28364
|
-
}; // Support legacy choiceState implementation
|
|
28365
|
-
}
|
|
28366
|
-
/* istanbul ignore if - props.values is deprecated */
|
|
28367
|
-
|
|
28368
|
-
|
|
28369
|
-
var {
|
|
28370
|
-
values
|
|
28371
|
-
} = props;
|
|
28372
|
-
|
|
28373
|
-
if (values) {
|
|
28374
|
-
var _noneOfTheAboveIndex = null;
|
|
28375
|
-
var _noneOfTheAboveSelected = false;
|
|
28376
|
-
var _choicesSelected = [...values];
|
|
28377
|
-
var _countChoices = props.countChoices;
|
|
28378
|
-
var _numCorrect = props.numCorrect;
|
|
28379
|
-
var valuesLength = values.length;
|
|
28380
|
-
|
|
28381
|
-
for (var _i = 0; _i < valuesLength; _i++) {
|
|
28382
|
-
var _index = props.choices[_i].originalIndex;
|
|
28383
|
-
_choicesSelected[_index] = values[_i];
|
|
28384
|
-
|
|
28385
|
-
if (props.choices[_i].isNoneOfTheAbove) {
|
|
28386
|
-
_noneOfTheAboveIndex = _index;
|
|
28387
|
-
|
|
28388
|
-
if (_choicesSelected[_i]) {
|
|
28389
|
-
_noneOfTheAboveSelected = true;
|
|
28390
|
-
}
|
|
28391
|
-
}
|
|
28392
|
-
}
|
|
28393
|
-
|
|
28394
|
-
return {
|
|
28395
|
-
choicesSelected: _choicesSelected,
|
|
28396
|
-
noneOfTheAboveIndex: _noneOfTheAboveIndex,
|
|
28397
|
-
noneOfTheAboveSelected: _noneOfTheAboveSelected,
|
|
28398
|
-
countChoices: _countChoices,
|
|
28399
|
-
numCorrect: _numCorrect
|
|
28400
|
-
};
|
|
28401
|
-
} // Nothing checked
|
|
28402
|
-
|
|
28403
|
-
|
|
28404
|
-
return {
|
|
28405
|
-
choicesSelected: props.choices.map(() => false)
|
|
28406
|
-
};
|
|
28407
|
-
}
|
|
28408
|
-
|
|
28409
28380
|
}
|
|
28410
28381
|
|
|
28411
28382
|
_defineProperty(Radio$1, "defaultProps", {
|