@khanacademy/perseus 17.3.1 → 17.3.3
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 +12 -0
- package/dist/es/index.css +12 -10
- package/dist/es/index.js +14 -5
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +12 -10
- package/dist/index.js +14 -5
- package/dist/index.js.map +1 -1
- package/dist/server-item-renderer.d.ts +5 -5
- package/package.json +1 -1
- package/src/__tests__/error-boundary.test.tsx +76 -0
- package/src/error-boundary.tsx +22 -7
- package/src/server-item-renderer.tsx +5 -5
- package/src/styles/articles.less +20 -15
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as PerseusLinter from "@khanacademy/perseus-linter";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* renderer
|
|
5
|
-
*
|
|
3
|
+
* The main item (aka Exercise) renderer in Perseus. This component renders its
|
|
4
|
+
* question renderer and hints renderer using standard React practices (an
|
|
5
|
+
* earlier ItemRenderer component used to use jQuery and ReactDOM.render()ing
|
|
6
|
+
* to render into elements it didn't own.
|
|
6
7
|
*
|
|
7
|
-
* This
|
|
8
|
-
* exercise.
|
|
8
|
+
* This component is compatible with server-rendering of a perseus exercise.
|
|
9
9
|
*/
|
|
10
10
|
import * as React from "react";
|
|
11
11
|
import Renderer from "./renderer";
|
package/package.json
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {screen, render} from "@testing-library/react";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import {testDependencies} from "../../../../testing/test-dependencies";
|
|
5
|
+
import * as Dependencies from "../dependencies";
|
|
6
|
+
import ErrorBoundary from "../error-boundary";
|
|
7
|
+
|
|
8
|
+
import "@testing-library/jest-dom";
|
|
9
|
+
|
|
10
|
+
const ProblematicComponent = () => {
|
|
11
|
+
throw new Error("I can haz error");
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
describe("error boundary", () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
17
|
+
testDependencies,
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should render children", () => {
|
|
22
|
+
render(
|
|
23
|
+
<ErrorBoundary>
|
|
24
|
+
<span>Hello world</span>
|
|
25
|
+
</ErrorBoundary>,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
expect(screen.getByText("Hello world")).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should render error icon when children error", () => {
|
|
32
|
+
render(
|
|
33
|
+
<ErrorBoundary>
|
|
34
|
+
<ProblematicComponent />
|
|
35
|
+
</ErrorBoundary>,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
expect(screen.getByRole("img")).toBeInTheDocument();
|
|
39
|
+
expect(screen.getByTitle("Rendering Error!")).toBeInTheDocument();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should call the onError callback when children error", () => {
|
|
43
|
+
const onErrorCallback = jest.fn();
|
|
44
|
+
|
|
45
|
+
render(
|
|
46
|
+
<ErrorBoundary onError={onErrorCallback}>
|
|
47
|
+
<ProblematicComponent />
|
|
48
|
+
</ErrorBoundary>,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
expect(onErrorCallback).toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("should log the error when children error", () => {
|
|
55
|
+
const errorSpy = jest.spyOn(testDependencies.Log, "error");
|
|
56
|
+
|
|
57
|
+
render(
|
|
58
|
+
<ErrorBoundary>
|
|
59
|
+
<ProblematicComponent />
|
|
60
|
+
</ErrorBoundary>,
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
expect(errorSpy).toHaveBeenCalledWith(
|
|
64
|
+
"Unhandled Perseus error: I can haz error",
|
|
65
|
+
"Internal",
|
|
66
|
+
{
|
|
67
|
+
cause: expect.anything(),
|
|
68
|
+
loggedMetadata: {
|
|
69
|
+
componentStack: `
|
|
70
|
+
in ProblematicComponent
|
|
71
|
+
in ErrorBoundary`,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
package/src/error-boundary.tsx
CHANGED
|
@@ -22,13 +22,27 @@ class ErrorBoundary extends React.Component<Props, State> {
|
|
|
22
22
|
componentDidCatch(error: Error, info: any) {
|
|
23
23
|
this.setState({error: error.toString()});
|
|
24
24
|
this.props.onError?.(error, info);
|
|
25
|
-
Log.error(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
Log.error(
|
|
26
|
+
// NOTE(jeremy): We concatenate the error messsage here. Typical
|
|
27
|
+
// Khan Academy error handling guidance says that you should never
|
|
28
|
+
// "build" the error message that might be sent to our error
|
|
29
|
+
// reporting tool (currently Sentry). However, if we don't
|
|
30
|
+
// differentiate between the different errors that are thrown, they
|
|
31
|
+
// all end up being grouped as a single Sentry event, which is very
|
|
32
|
+
// unhelpful.
|
|
33
|
+
"Unhandled Perseus error: " + error.message,
|
|
34
|
+
Errors.Internal,
|
|
35
|
+
{
|
|
36
|
+
cause: error,
|
|
37
|
+
loggedMetadata: {
|
|
38
|
+
componentStack:
|
|
39
|
+
!!info && "componentStack" in info
|
|
40
|
+
? info.componentStack
|
|
41
|
+
: "componentStack not provided",
|
|
42
|
+
...this.props.metadata,
|
|
43
|
+
},
|
|
30
44
|
},
|
|
31
|
-
|
|
45
|
+
);
|
|
32
46
|
}
|
|
33
47
|
|
|
34
48
|
render(): React.ReactNode {
|
|
@@ -39,7 +53,8 @@ class ErrorBoundary extends React.Component<Props, State> {
|
|
|
39
53
|
// errors into inline elements.
|
|
40
54
|
// TODO(michaelpolyak): Link error icon to "Report a problem".
|
|
41
55
|
return (
|
|
42
|
-
<svg height="16" width="16" viewBox="0 0 16 16">
|
|
56
|
+
<svg height="16" width="16" viewBox="0 0 16 16" role={"img"}>
|
|
57
|
+
<title>Rendering Error!</title>
|
|
43
58
|
<path
|
|
44
59
|
d="m8 16c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm0-3c0.55 0 1-0.45 1-1s-0.45-1-1-1-1 0.45-1 1 0.45 1 1 1zm0-9c-0.55 0-1 0.45-1 1v4c0 0.55.45 1 1 1s1-0.45 1-1v-4c0-0.55-0.45-1-1-1z"
|
|
45
60
|
fill="#d92916"
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import * as PerseusLinter from "@khanacademy/perseus-linter";
|
|
3
3
|
import {StyleSheet, css} from "aphrodite";
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* renderer
|
|
7
|
-
*
|
|
5
|
+
* The main item (aka Exercise) renderer in Perseus. This component renders its
|
|
6
|
+
* question renderer and hints renderer using standard React practices (an
|
|
7
|
+
* earlier ItemRenderer component used to use jQuery and ReactDOM.render()ing
|
|
8
|
+
* to render into elements it didn't own.
|
|
8
9
|
*
|
|
9
|
-
* This
|
|
10
|
-
* exercise.
|
|
10
|
+
* This component is compatible with server-rendering of a perseus exercise.
|
|
11
11
|
*/
|
|
12
12
|
import * as React from "react";
|
|
13
13
|
import _ from "underscore";
|
package/src/styles/articles.less
CHANGED
|
@@ -2,7 +2,24 @@
|
|
|
2
2
|
@import "./variables.less";
|
|
3
3
|
@import "./util.less";
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Common styles for _all_ of Perseus
|
|
6
|
+
.framework-perseus {
|
|
7
|
+
code {
|
|
8
|
+
font-family: Courier, monospace;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
pre {
|
|
12
|
+
background-color: @gray95;
|
|
13
|
+
border-radius: 4px;
|
|
14
|
+
color: @gray17;
|
|
15
|
+
font-size: 18px;
|
|
16
|
+
padding: 16px;
|
|
17
|
+
white-space: pre;
|
|
18
|
+
overflow: auto;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Desktop Articles
|
|
6
23
|
.framework-perseus.perseus-article:not(.perseus-mobile) {
|
|
7
24
|
@textColor: #21242c;
|
|
8
25
|
.body-text() {
|
|
@@ -190,25 +207,12 @@
|
|
|
190
207
|
}
|
|
191
208
|
}
|
|
192
209
|
|
|
193
|
-
code {
|
|
194
|
-
font-family: Courier, monospace;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
210
|
pre {
|
|
198
|
-
background-color: @gray95;
|
|
199
|
-
border-radius: 4px;
|
|
200
|
-
color: @gray17;
|
|
201
|
-
font-size: 18px;
|
|
202
|
-
padding: 16px;
|
|
203
|
-
white-space: pre;
|
|
204
|
-
overflow: auto;
|
|
205
211
|
margin: 0 -16px 32px -16px;
|
|
206
212
|
}
|
|
207
213
|
}
|
|
208
214
|
|
|
209
|
-
//
|
|
210
|
-
// styles down below. However, those styles are limited to mobile devices
|
|
211
|
-
// only, so this block is for non-mobile devices.
|
|
215
|
+
// Desktop Exercises
|
|
212
216
|
.framework-perseus:not(.perseus-article):not(.perseus-mobile) {
|
|
213
217
|
// Mixins
|
|
214
218
|
.text(@fontSize; @lineHeight; @color) {
|
|
@@ -251,6 +255,7 @@
|
|
|
251
255
|
.choice-text(14px, 1.25);
|
|
252
256
|
}
|
|
253
257
|
|
|
258
|
+
// Mobile Articles and Exercises
|
|
254
259
|
.framework-perseus.perseus-mobile {
|
|
255
260
|
.perseus-article {
|
|
256
261
|
.perseus-widget-container {
|