@khanacademy/perseus 0.2.8 → 0.3.2
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 +27 -0
- package/dist/es/index.css +1 -436
- package/dist/es/index.js +165 -212
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +1 -436
- package/dist/index.js +193 -241
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/perseus-markdown_test.jsx +5 -5
- package/src/__tests__/renderer_test.jsx +9 -9
- package/src/article-renderer.jsx +4 -4
- package/src/components/input-with-examples.jsx +4 -4
- package/src/components/lint.jsx +2 -1
- package/src/components/sortable.jsx +6 -6
- package/src/hint-renderer.jsx +3 -3
- package/src/hints-renderer.jsx +3 -3
- package/src/index.js +1 -0
- package/src/item-renderer.jsx +4 -4
- package/src/jquery.mobile.vmouse.js +2 -1
- package/src/perseus-markdown.jsx +2 -1
- package/src/renderer.jsx +31 -32
- package/src/styles/perseus-renderer.less +1 -1
- package/src/{not-gorgon.js → translation-linter.js} +9 -12
- package/src/types.js +3 -1
- package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +30 -12
- package/src/widgets/__tests__/definition_test.js +18 -33
- package/src/widgets/__tests__/grapher.cypress.jsx +2 -2
- package/src/widgets/__tests__/interactive-graph.cypress.jsx +2 -2
- package/src/widgets/definition.jsx +36 -71
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Core Perseus API (includes renderers and widgets)",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "0.2
|
|
6
|
+
"version": "0.3.2",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@khanacademy/kmath": "^0.0.5",
|
|
25
25
|
"@khanacademy/kas": "^0.2.4",
|
|
26
|
-
"@khanacademy/math-input": "^0.5.
|
|
27
|
-
"@khanacademy/perseus-linter": "^0.
|
|
26
|
+
"@khanacademy/math-input": "^0.5.3",
|
|
27
|
+
"@khanacademy/perseus-linter": "^0.2.0",
|
|
28
28
|
"@khanacademy/pure-markdown": "^0.1.0",
|
|
29
29
|
"@khanacademy/simple-markdown": "^0.8.3",
|
|
30
30
|
"mathquill": "git+https://git@github.com/Khan/mathquill.git#a9ae54e057c5c1acc8244a5627acbff29901d992"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
import * as
|
|
2
|
+
import * as PerseusLinter from "@khanacademy/perseus-linter";
|
|
3
3
|
|
|
4
4
|
import PerseusMarkdown from "../perseus-markdown.jsx";
|
|
5
5
|
|
|
@@ -565,14 +565,14 @@ describe("perseus markdown", () => {
|
|
|
565
565
|
"data 4 | $data 5 | data 6\n" +
|
|
566
566
|
"data 7 | data 8 | data 9";
|
|
567
567
|
const context = {
|
|
568
|
-
...
|
|
568
|
+
...PerseusLinter.linterContextDefault,
|
|
569
569
|
content,
|
|
570
570
|
widgets: {},
|
|
571
571
|
};
|
|
572
572
|
|
|
573
573
|
// Act
|
|
574
574
|
const parsedWithLint = parse(content);
|
|
575
|
-
|
|
575
|
+
PerseusLinter.runLinter(parsedWithLint, context, true);
|
|
576
576
|
|
|
577
577
|
// Assert
|
|
578
578
|
expect(parsedWithLint).toMatchSnapshot();
|
|
@@ -650,14 +650,14 @@ describe("perseus markdown", () => {
|
|
|
650
650
|
"data 4 | $data 5 | data 6\n" +
|
|
651
651
|
"data 7 | data 8 | data 9";
|
|
652
652
|
const context = {
|
|
653
|
-
...
|
|
653
|
+
...PerseusLinter.linterContextDefault,
|
|
654
654
|
content,
|
|
655
655
|
widgets: {},
|
|
656
656
|
};
|
|
657
657
|
|
|
658
658
|
// Act
|
|
659
659
|
const parsedWithLint = parse(content);
|
|
660
|
-
|
|
660
|
+
PerseusLinter.runLinter(parsedWithLint, context, true);
|
|
661
661
|
|
|
662
662
|
const output = PerseusMarkdown.basicOutput(parsedWithLint);
|
|
663
663
|
|
|
@@ -31,15 +31,15 @@ import type {
|
|
|
31
31
|
PerseusInputNumberWidgetOptions,
|
|
32
32
|
} from "../perseus-types.js";
|
|
33
33
|
|
|
34
|
-
// NOTE(jeremy): We can't use an automatic mock for
|
|
35
|
-
// it's "instance" methods is created using `debounce` and Jest
|
|
36
|
-
// a mocked instance method for it (I suspect that Jest doesn't
|
|
37
|
-
// symbol as an instance method).
|
|
34
|
+
// NOTE(jeremy): We can't use an automatic mock for the translation linter,
|
|
35
|
+
// because one of it's "instance" methods is created using `debounce` and Jest
|
|
36
|
+
// doesn't provide a mocked instance method for it (I suspect that Jest doesn't
|
|
37
|
+
// see that symbol as an instance method).
|
|
38
38
|
const mockRunLinter = jest.fn();
|
|
39
39
|
const mockApplyLintErrors = jest.fn();
|
|
40
|
-
jest.mock("../
|
|
41
|
-
// We mock the
|
|
42
|
-
// spy/verify calls to instances of
|
|
40
|
+
jest.mock("../translation-linter.js", () => {
|
|
41
|
+
// We mock the TranslationLinter constructor here setting things up so we
|
|
42
|
+
// can spy/verify calls to instances of TranslationLinter
|
|
43
43
|
return function () {
|
|
44
44
|
return {
|
|
45
45
|
runLinter: mockRunLinter,
|
|
@@ -106,7 +106,7 @@ describe("renderer", () => {
|
|
|
106
106
|
});
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
-
describe("linting (
|
|
109
|
+
describe("linting (TranslationLinter)", () => {
|
|
110
110
|
const extraProps = {
|
|
111
111
|
linterContext: {
|
|
112
112
|
contentType: "exercise",
|
|
@@ -165,7 +165,7 @@ describe("renderer", () => {
|
|
|
165
165
|
|
|
166
166
|
// Assert
|
|
167
167
|
expect(renderer.state.jiptContent).toBeNull();
|
|
168
|
-
expect(renderer.state.
|
|
168
|
+
expect(renderer.state.translationLintErrors).toHaveLength(0);
|
|
169
169
|
expect(renderer.state.lastUsedWidgetId).toBeNull();
|
|
170
170
|
|
|
171
171
|
expect(renderer.state.widgetInfo).toStrictEqual(question1.widgets);
|
package/src/article-renderer.jsx
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* composed of multiple (Renderer) sections concatenated together.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import * as
|
|
8
|
+
import * as PerseusLinter from "@khanacademy/perseus-linter";
|
|
9
9
|
import classNames from "classnames";
|
|
10
10
|
import PropTypes from "prop-types";
|
|
11
11
|
import * as React from "react";
|
|
@@ -37,14 +37,14 @@ class ArticleRenderer extends React.Component<$FlowFixMe, $FlowFixMe> {
|
|
|
37
37
|
|
|
38
38
|
// Whether to use the new Bibliotron styles for articles
|
|
39
39
|
useNewStyles: PropTypes.bool,
|
|
40
|
-
linterContext:
|
|
40
|
+
linterContext: PerseusLinter.linterContextProps,
|
|
41
41
|
legacyPerseusLint: PropTypes.arrayOf(PropTypes.string),
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
static defaultProps: $FlowFixMe = {
|
|
45
45
|
apiOptions: {},
|
|
46
46
|
useNewStyles: false,
|
|
47
|
-
linterContext:
|
|
47
|
+
linterContext: PerseusLinter.linterContextDefault,
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
constructor(props: $FlowFixMe) {
|
|
@@ -197,7 +197,7 @@ class ArticleRenderer extends React.Component<$FlowFixMe, $FlowFixMe> {
|
|
|
197
197
|
);
|
|
198
198
|
},
|
|
199
199
|
}}
|
|
200
|
-
linterContext={
|
|
200
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
201
201
|
this.props.linterContext,
|
|
202
202
|
"article",
|
|
203
203
|
)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react/sort-comp */
|
|
2
2
|
// @flow
|
|
3
|
-
import * as
|
|
3
|
+
import * as PerseusLinter from "@khanacademy/perseus-linter";
|
|
4
4
|
import PropTypes from "prop-types";
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import _ from "underscore";
|
|
@@ -37,7 +37,7 @@ class InputWithExamples extends React.Component<$FlowFixMe, $FlowFixMe> {
|
|
|
37
37
|
|
|
38
38
|
// A unique string identifying this InputWithExamples
|
|
39
39
|
id: PropTypes.string.isRequired,
|
|
40
|
-
linterContext:
|
|
40
|
+
linterContext: PerseusLinter.linterContextProps,
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
static defaultProps: $FlowFixMe = {
|
|
@@ -46,7 +46,7 @@ class InputWithExamples extends React.Component<$FlowFixMe, $FlowFixMe> {
|
|
|
46
46
|
onFocus: function () {},
|
|
47
47
|
onBlur: function () {},
|
|
48
48
|
disabled: false,
|
|
49
|
-
linterContext:
|
|
49
|
+
linterContext: PerseusLinter.linterContextDefault,
|
|
50
50
|
};
|
|
51
51
|
|
|
52
52
|
state: $FlowFixMe = {
|
|
@@ -178,7 +178,7 @@ class InputWithExamples extends React.Component<$FlowFixMe, $FlowFixMe> {
|
|
|
178
178
|
<div id={this._getUniqueId()}>
|
|
179
179
|
<Renderer
|
|
180
180
|
content={examplesContent}
|
|
181
|
-
linterContext={
|
|
181
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
182
182
|
this.props.linterContext,
|
|
183
183
|
"input-with-examples",
|
|
184
184
|
)}
|
package/src/components/lint.jsx
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
/**
|
|
3
3
|
* This component renders "lint" nodes in a markdown parse tree. Lint nodes
|
|
4
|
-
* are inserted into the tree by the
|
|
4
|
+
* are inserted into the tree by the Perseus linter (see
|
|
5
|
+
* perseus-linter/src/index).
|
|
5
6
|
*
|
|
6
7
|
* This component serves multiple purposes
|
|
7
8
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @babel/no-invalid-this, react/forbid-prop-types, react/no-unsafe, react/sort-comp */
|
|
2
2
|
// @flow
|
|
3
|
-
import * as
|
|
3
|
+
import * as PerseusLinter from "@khanacademy/perseus-linter";
|
|
4
4
|
import {CircularSpinner} from "@khanacademy/wonder-blocks-progress-spinner";
|
|
5
5
|
import {StyleSheet, css} from "aphrodite";
|
|
6
6
|
import $ from "jquery";
|
|
@@ -95,13 +95,13 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
|
|
|
95
95
|
onMouseUp: PropTypes.func.isRequired,
|
|
96
96
|
onRender: PropTypes.func.isRequired,
|
|
97
97
|
type: PropTypes.oneOf([STATIC, DRAGGING, ANIMATING, DISABLED]),
|
|
98
|
-
linterContext:
|
|
98
|
+
linterContext: PerseusLinter.linterContextProps,
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
static defaultProps = {
|
|
102
102
|
includePadding: true,
|
|
103
103
|
type: STATIC,
|
|
104
|
-
linterContext:
|
|
104
|
+
linterContext: PerseusLinter.linterContextDefault,
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
state: DraggableState = {
|
|
@@ -214,7 +214,7 @@ class Draggable extends React.Component<DraggableProps, DraggableState> {
|
|
|
214
214
|
>
|
|
215
215
|
<Renderer
|
|
216
216
|
content={this.props.content}
|
|
217
|
-
linterContext={
|
|
217
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
218
218
|
this.props.linterContext,
|
|
219
219
|
"draggable",
|
|
220
220
|
)}
|
|
@@ -422,7 +422,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
|
|
|
422
422
|
onMeasure: function () {},
|
|
423
423
|
margin: 5,
|
|
424
424
|
onChange: function () {},
|
|
425
|
-
linterContext:
|
|
425
|
+
linterContext: PerseusLinter.linterContextDefault,
|
|
426
426
|
waitForKatexLoad: true,
|
|
427
427
|
};
|
|
428
428
|
|
|
@@ -618,7 +618,7 @@ class Sortable extends React.Component<SortableProps, SortableState> {
|
|
|
618
618
|
includePadding={this.props.padding}
|
|
619
619
|
margin={isLast && isStatic ? 0 : margin}
|
|
620
620
|
endPosition={item.endPosition}
|
|
621
|
-
linterContext={
|
|
621
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
622
622
|
this.props.linterContext,
|
|
623
623
|
"sortable",
|
|
624
624
|
)}
|
package/src/hint-renderer.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
import * as
|
|
2
|
+
import * as PerseusLinter from "@khanacademy/perseus-linter";
|
|
3
3
|
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
4
4
|
import {StyleSheet, css} from "aphrodite";
|
|
5
5
|
import classnames from "classnames";
|
|
@@ -35,7 +35,7 @@ type DefaultProps = {|
|
|
|
35
35
|
/* Renders just a hint preview */
|
|
36
36
|
class HintRenderer extends React.Component<Props> {
|
|
37
37
|
static defaultProps: DefaultProps = {
|
|
38
|
-
linterContext:
|
|
38
|
+
linterContext: PerseusLinter.linterContextDefault,
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
getSerializedState: () => void = () => {
|
|
@@ -115,7 +115,7 @@ class HintRenderer extends React.Component<Props> {
|
|
|
115
115
|
images={hint.images}
|
|
116
116
|
apiOptions={rendererApiOptions}
|
|
117
117
|
findExternalWidgets={this.props.findExternalWidgets}
|
|
118
|
-
linterContext={
|
|
118
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
119
119
|
this.props.linterContext,
|
|
120
120
|
"hint",
|
|
121
121
|
)}
|
package/src/hints-renderer.jsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
import * as
|
|
2
|
+
import * as PerseusLinter from "@khanacademy/perseus-linter";
|
|
3
3
|
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
4
4
|
import {StyleSheet, css} from "aphrodite";
|
|
5
5
|
import classnames from "classnames";
|
|
@@ -42,7 +42,7 @@ type State = {|
|
|
|
42
42
|
// eslint-disable-next-line react/no-unsafe
|
|
43
43
|
class HintsRenderer extends React.Component<Props, State> {
|
|
44
44
|
static defaultProps: DefaultProps = {
|
|
45
|
-
linterContext:
|
|
45
|
+
linterContext: PerseusLinter.linterContextDefault,
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
// The isFinalHelpPage property determines if the user has requested help
|
|
@@ -180,7 +180,7 @@ class HintsRenderer extends React.Component<Props, State> {
|
|
|
180
180
|
key={"hintRenderer" + i}
|
|
181
181
|
apiOptions={apiOptions}
|
|
182
182
|
findExternalWidgets={this.props.findExternalWidgets}
|
|
183
|
-
linterContext={
|
|
183
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
184
184
|
this.props.linterContext,
|
|
185
185
|
"hints[" + i + "]",
|
|
186
186
|
)}
|
package/src/index.js
CHANGED
package/src/item-renderer.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react/no-unsafe */
|
|
2
2
|
// @flow
|
|
3
|
-
import * as
|
|
3
|
+
import * as PerseusLinter from "@khanacademy/perseus-linter";
|
|
4
4
|
import $ from "jquery";
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import * as ReactDOM from "react-dom";
|
|
@@ -87,7 +87,7 @@ class ItemRenderer extends React.Component<Props, State> {
|
|
|
87
87
|
initialHintsVisible: 0,
|
|
88
88
|
workAreaSelector: "#workarea",
|
|
89
89
|
reviewMode: false,
|
|
90
|
-
linterContext:
|
|
90
|
+
linterContext: PerseusLinter.linterContextDefault,
|
|
91
91
|
};
|
|
92
92
|
|
|
93
93
|
constructor(props: Props) {
|
|
@@ -208,7 +208,7 @@ class ItemRenderer extends React.Component<Props, State> {
|
|
|
208
208
|
questionCompleted={this.state.questionCompleted}
|
|
209
209
|
reviewMode={this.props.reviewMode}
|
|
210
210
|
savedState={this.props.savedState}
|
|
211
|
-
linterContext={
|
|
211
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
212
212
|
this.props.linterContext,
|
|
213
213
|
"question",
|
|
214
214
|
)}
|
|
@@ -226,7 +226,7 @@ class ItemRenderer extends React.Component<Props, State> {
|
|
|
226
226
|
hints={this.props.item.hints}
|
|
227
227
|
hintsVisible={this.state.hintsVisible}
|
|
228
228
|
apiOptions={apiOptions}
|
|
229
|
-
linterContext={
|
|
229
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
230
230
|
this.props.linterContext,
|
|
231
231
|
"hints",
|
|
232
232
|
)}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
//
|
|
22
22
|
// The current version exposes the following virtual events to jQuery bind methods:
|
|
23
23
|
// "vmouseover vmousedown vmousemove vmouseup vclick vmouseout vmousecancel"
|
|
24
|
+
import $ from "jquery";
|
|
24
25
|
|
|
25
26
|
(function ($, window, document, undefined) {
|
|
26
27
|
if (typeof $ === "undefined") {
|
|
@@ -542,4 +543,4 @@
|
|
|
542
543
|
true,
|
|
543
544
|
);
|
|
544
545
|
}
|
|
545
|
-
})(
|
|
546
|
+
})($, window, document);
|
package/src/perseus-markdown.jsx
CHANGED
|
@@ -110,7 +110,8 @@ const rules = {
|
|
|
110
110
|
} else if (node.table.type === "lint") {
|
|
111
111
|
// The `table` in this node is actually a `lint` node,
|
|
112
112
|
// not the expected `table` node. This is due to the linter
|
|
113
|
-
// implementation "reparenting" the `table` node, see
|
|
113
|
+
// implementation "reparenting" the `table` node, see
|
|
114
|
+
// perseus-linter/src/index.js
|
|
114
115
|
if (node.table.content?.type === "table") {
|
|
115
116
|
// And the `content` of this lint node is the `table` node
|
|
116
117
|
// which we actually want to render. Thus, we perform
|
package/src/renderer.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react/no-unsafe */
|
|
2
2
|
// @flow
|
|
3
|
-
import * as
|
|
3
|
+
import * as PerseusLinter from "@khanacademy/perseus-linter";
|
|
4
4
|
import classNames from "classnames";
|
|
5
5
|
import $ from "jquery";
|
|
6
6
|
import * as React from "react";
|
|
@@ -18,11 +18,11 @@ import InteractionTracker from "./interaction-tracker.js";
|
|
|
18
18
|
import Objective from "./interactive2/objective_.js";
|
|
19
19
|
import JiptParagraphs from "./jipt-paragraphs.js";
|
|
20
20
|
import {Errors, Log} from "./logging/log.js";
|
|
21
|
-
import NotGorgon from "./not-gorgon.js"; // The i18n linter
|
|
22
21
|
import {ClassNames as ApiClassNames, ApiOptions} from "./perseus-api.jsx";
|
|
23
22
|
import {PerseusError} from "./perseus-error.js";
|
|
24
23
|
import PerseusMarkdown from "./perseus-markdown.jsx";
|
|
25
24
|
import QuestionParagraph from "./question-paragraph.jsx";
|
|
25
|
+
import TranslationLinter from "./translation-linter.js";
|
|
26
26
|
import Util from "./util.js";
|
|
27
27
|
import preprocessTex from "./util/katex-preprocess.js";
|
|
28
28
|
import WidgetContainer from "./widget-container.jsx";
|
|
@@ -170,7 +170,7 @@ type Props = {|
|
|
|
170
170
|
|};
|
|
171
171
|
|
|
172
172
|
type State = {|
|
|
173
|
-
|
|
173
|
+
translationLintErrors: $ReadOnlyArray<string>,
|
|
174
174
|
widgetInfo: $ReadOnly<{|[id: string]: ?WidgetInfo|}>,
|
|
175
175
|
widgetProps: $ReadOnly<{|[id: string]: ?$FlowFixMe|}>,
|
|
176
176
|
jiptContent: any,
|
|
@@ -209,7 +209,7 @@ class Renderer extends React.Component<Props, State> {
|
|
|
209
209
|
_isTwoColumn: boolean;
|
|
210
210
|
|
|
211
211
|
// The i18n linter.
|
|
212
|
-
|
|
212
|
+
_translationLinter: TranslationLinter;
|
|
213
213
|
|
|
214
214
|
lastRenderedMarkdown: React.Element<PerseusDependencies["KatexProvider"]>;
|
|
215
215
|
reuseMarkdown: boolean;
|
|
@@ -233,20 +233,20 @@ class Renderer extends React.Component<Props, State> {
|
|
|
233
233
|
reviewMode: false,
|
|
234
234
|
serializedState: null,
|
|
235
235
|
onSerializedStateUpdated: () => {},
|
|
236
|
-
linterContext:
|
|
236
|
+
linterContext: PerseusLinter.linterContextDefault,
|
|
237
237
|
};
|
|
238
238
|
|
|
239
239
|
constructor(props: Props, context: Context) {
|
|
240
240
|
super(props, context);
|
|
241
|
-
this.
|
|
241
|
+
this._translationLinter = new TranslationLinter();
|
|
242
242
|
|
|
243
243
|
this.state = {
|
|
244
244
|
jiptContent: null,
|
|
245
245
|
|
|
246
|
-
//
|
|
247
|
-
// This is a list of error strings
|
|
248
|
-
// run.
|
|
249
|
-
|
|
246
|
+
// TranslationLinter is async and currently does not contain a
|
|
247
|
+
// location. This is a list of error strings TranslationLinter
|
|
248
|
+
// detected on its last run.
|
|
249
|
+
translationLintErrors: [],
|
|
250
250
|
|
|
251
251
|
// The ID of the last widget the user interacted with. We'll
|
|
252
252
|
// use this to set the `isLastUsedWidget` flag on the
|
|
@@ -275,9 +275,9 @@ class Renderer extends React.Component<Props, State> {
|
|
|
275
275
|
if (this.props.linterContext.highlightLint) {
|
|
276
276
|
// Get i18n lint errors asynchronously. If there are lint errors,
|
|
277
277
|
// this component will be rerendered.
|
|
278
|
-
this.
|
|
278
|
+
this._translationLinter.runLinter(
|
|
279
279
|
this.props.content,
|
|
280
|
-
this.
|
|
280
|
+
this.handletranslationLintErrors,
|
|
281
281
|
);
|
|
282
282
|
}
|
|
283
283
|
}
|
|
@@ -343,8 +343,8 @@ class Renderer extends React.Component<Props, State> {
|
|
|
343
343
|
!newJipt &&
|
|
344
344
|
oldContent === newContent &&
|
|
345
345
|
_.isEqual(
|
|
346
|
-
this.state.
|
|
347
|
-
nextState.
|
|
346
|
+
this.state.translationLintErrors,
|
|
347
|
+
nextState.translationLintErrors,
|
|
348
348
|
) &&
|
|
349
349
|
// If we are running the linter then we need to know when
|
|
350
350
|
// widgets have changed because we need for force the linter to
|
|
@@ -391,9 +391,9 @@ class Renderer extends React.Component<Props, State> {
|
|
|
391
391
|
if (this.props.linterContext.highlightLint) {
|
|
392
392
|
// Get i18n lint errors asynchronously. If lint errors have changed
|
|
393
393
|
// since the last run, this component will be rerendered.
|
|
394
|
-
this.
|
|
394
|
+
this._translationLinter.runLinter(
|
|
395
395
|
this.props.content,
|
|
396
|
-
this.
|
|
396
|
+
this.handletranslationLintErrors,
|
|
397
397
|
);
|
|
398
398
|
}
|
|
399
399
|
}
|
|
@@ -537,7 +537,7 @@ class Renderer extends React.Component<Props, State> {
|
|
|
537
537
|
type={type}
|
|
538
538
|
initialProps={this.getWidgetProps(id)}
|
|
539
539
|
shouldHighlight={shouldHighlight}
|
|
540
|
-
linterContext={
|
|
540
|
+
linterContext={PerseusLinter.pushContextStack(
|
|
541
541
|
this.props.linterContext,
|
|
542
542
|
"widget",
|
|
543
543
|
)}
|
|
@@ -1780,18 +1780,17 @@ class Renderer extends React.Component<Props, State> {
|
|
|
1780
1780
|
return examples[0];
|
|
1781
1781
|
};
|
|
1782
1782
|
|
|
1783
|
-
//
|
|
1784
|
-
|
|
1785
|
-
lintErrors: $ReadOnlyArray<string
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
}
|
|
1783
|
+
// TranslationLinter callback
|
|
1784
|
+
handletranslationLintErrors: (lintErrors: $ReadOnlyArray<string>) => void =
|
|
1785
|
+
(lintErrors: $ReadOnlyArray<string>) => {
|
|
1786
|
+
if (!this._isMounted) {
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1790
1789
|
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1790
|
+
this.setState({
|
|
1791
|
+
translationLintErrors: lintErrors,
|
|
1792
|
+
});
|
|
1793
|
+
};
|
|
1795
1794
|
|
|
1796
1795
|
render(): React.Node {
|
|
1797
1796
|
const apiOptions = this.getApiOptions();
|
|
@@ -1885,12 +1884,12 @@ class Renderer extends React.Component<Props, State> {
|
|
|
1885
1884
|
...this.props.linterContext,
|
|
1886
1885
|
};
|
|
1887
1886
|
|
|
1888
|
-
|
|
1887
|
+
PerseusLinter.runLinter(parsedMarkdown, context, true);
|
|
1889
1888
|
|
|
1890
|
-
// Apply the lint errors from the last
|
|
1889
|
+
// Apply the lint errors from the last TranslationLinter run.
|
|
1891
1890
|
// TODO(joshuan): Support overlapping dots.
|
|
1892
|
-
this.
|
|
1893
|
-
...this.state.
|
|
1891
|
+
this._translationLinter.applyLintErrors(parsedMarkdown, [
|
|
1892
|
+
...this.state.translationLintErrors,
|
|
1894
1893
|
...(this.props.legacyPerseusLint || []),
|
|
1895
1894
|
]);
|
|
1896
1895
|
}
|
|
@@ -3,14 +3,11 @@ import {Rule} from "@khanacademy/perseus-linter";
|
|
|
3
3
|
import {debounce} from "underscore";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* TranslationLinter asynchronously calls Khan Academy's poentry linter.
|
|
7
7
|
*
|
|
8
8
|
* The poentry linter checks for issues that prevent content from being
|
|
9
|
-
* translated. This linter is KA-specific and so
|
|
10
|
-
* in non-KA environments.
|
|
11
|
-
*
|
|
12
|
-
* NotGorgon is named as such because it is not Gorgon, Perseus' built-in
|
|
13
|
-
* linter.
|
|
9
|
+
* translated. This linter is KA-specific and so TranslationLinter does not do
|
|
10
|
+
* anything in non-KA environments.
|
|
14
11
|
*/
|
|
15
12
|
|
|
16
13
|
// A LintCB is called once the linter has been run.
|
|
@@ -20,7 +17,7 @@ type LintCB = (errors: $ReadOnlyArray<string>) => void;
|
|
|
20
17
|
// last call. The timer is reset if the lint is called during the wait.
|
|
21
18
|
const DEBOUNCE_TIMEOUT = 1000;
|
|
22
19
|
|
|
23
|
-
export default class
|
|
20
|
+
export default class TranslationLinter {
|
|
24
21
|
// To avoid linting when nothing has changed.
|
|
25
22
|
previousContent: ?string = null;
|
|
26
23
|
|
|
@@ -101,17 +98,17 @@ export default class NotGorgon {
|
|
|
101
98
|
|
|
102
99
|
/**
|
|
103
100
|
* Applies an array of errors generated by linters without position
|
|
104
|
-
* information (
|
|
105
|
-
* of a Perseus tree.
|
|
101
|
+
* information (TranslationLinter and the legacy getSaveWarnings()) to the
|
|
102
|
+
* top of a Perseus tree.
|
|
106
103
|
*/
|
|
107
104
|
applyLintErrors(
|
|
108
105
|
parsedMarkdown: any,
|
|
109
|
-
|
|
106
|
+
translationLintErrors: $ReadOnlyArray<string>,
|
|
110
107
|
) {
|
|
111
108
|
// These lint errors do not have position data associated with
|
|
112
109
|
// them, so we just plop them at the top.
|
|
113
|
-
if (
|
|
114
|
-
const errorText =
|
|
110
|
+
if (translationLintErrors.length) {
|
|
111
|
+
const errorText = translationLintErrors.join("\n\n");
|
|
115
112
|
parsedMarkdown.unshift({
|
|
116
113
|
content: {
|
|
117
114
|
type: "text",
|
package/src/types.js
CHANGED
|
@@ -359,6 +359,8 @@ type InitialRequestUrlInterface = {|
|
|
|
359
359
|
protocol: string,
|
|
360
360
|
|};
|
|
361
361
|
|
|
362
|
+
export type VideoKind = "YOUTUBE_ID" | "READABLE_ID";
|
|
363
|
+
|
|
362
364
|
// An object for dependency injection, to allow different clients
|
|
363
365
|
// to provide different methods for logging, translation, network
|
|
364
366
|
// requests, etc.
|
|
@@ -392,7 +394,7 @@ export type PerseusDependencies = {|
|
|
|
392
394
|
// framework.
|
|
393
395
|
useVideo(
|
|
394
396
|
id: string,
|
|
395
|
-
kind:
|
|
397
|
+
kind: VideoKind,
|
|
396
398
|
): Result<{|
|
|
397
399
|
video: ?VideoData,
|
|
398
400
|
|}>,
|
|
@@ -30,15 +30,24 @@ exports[`Definition widget should have a default snapshot: first render 1`] = `
|
|
|
30
30
|
class="perseus-widget-container widget-nohighlight widget-inline"
|
|
31
31
|
>
|
|
32
32
|
<span
|
|
33
|
-
aria-
|
|
33
|
+
aria-controls="uid-popover-0-wb-id"
|
|
34
|
+
aria-expanded="false"
|
|
34
35
|
class="perseus-widget-definition"
|
|
35
|
-
|
|
36
|
+
id="uid-popover-0-wb-id-anchor"
|
|
36
37
|
>
|
|
37
|
-
<
|
|
38
|
-
|
|
38
|
+
<button
|
|
39
|
+
aria-disabled="false"
|
|
40
|
+
class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1hl5pu8-o_O-small_xwkmkn"
|
|
41
|
+
role="button"
|
|
42
|
+
tabindex="0"
|
|
43
|
+
type="button"
|
|
39
44
|
>
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
<span
|
|
46
|
+
class="text_f1191h-o_O-LabelSmall_hl4zs1-o_O-text_m8a8ki-o_O-textWithFocus_e296pg"
|
|
47
|
+
>
|
|
48
|
+
the Pequots
|
|
49
|
+
</span>
|
|
50
|
+
</button>
|
|
42
51
|
</span>
|
|
43
52
|
</div>
|
|
44
53
|
, upon these conditions.
|
|
@@ -79,15 +88,24 @@ exports[`Definition widget should have an open state snapshot: open state 1`] =
|
|
|
79
88
|
class="perseus-widget-container widget-nohighlight widget-inline"
|
|
80
89
|
>
|
|
81
90
|
<span
|
|
82
|
-
aria-
|
|
91
|
+
aria-controls="uid-popover-1-wb-id"
|
|
92
|
+
aria-expanded="true"
|
|
83
93
|
class="perseus-widget-definition"
|
|
84
|
-
|
|
94
|
+
id="uid-popover-1-wb-id-anchor"
|
|
85
95
|
>
|
|
86
|
-
<
|
|
87
|
-
|
|
96
|
+
<button
|
|
97
|
+
aria-disabled="false"
|
|
98
|
+
class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1hl5pu8-o_O-small_xwkmkn"
|
|
99
|
+
role="button"
|
|
100
|
+
tabindex="0"
|
|
101
|
+
type="button"
|
|
88
102
|
>
|
|
89
|
-
|
|
90
|
-
|
|
103
|
+
<span
|
|
104
|
+
class="text_f1191h-o_O-LabelSmall_hl4zs1-o_O-text_m8a8ki-o_O-textWithFocus_e296pg-o_O-focus_15mc0l2"
|
|
105
|
+
>
|
|
106
|
+
the Pequots
|
|
107
|
+
</span>
|
|
108
|
+
</button>
|
|
91
109
|
</span>
|
|
92
110
|
</div>
|
|
93
111
|
, upon these conditions.
|