@khanacademy/perseus-editor 4.1.5 → 4.1.7
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/dist/es/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +9 -6
- package/.babelrc.js +0 -8
- package/.eslintrc.js +0 -12
- package/CHANGELOG.md +0 -1155
- package/src/README.md +0 -7
- package/src/__stories__/editor-page.stories.tsx +0 -65
- package/src/__stories__/editor.stories.tsx +0 -134
- package/src/__stories__/item-editor.stories.tsx +0 -41
- package/src/__stories__/tex-error-view.stories.tsx +0 -26
- package/src/__testdata__/input-number.testdata.ts +0 -27
- package/src/__tests__/editor.test.tsx +0 -142
- package/src/__tests__/hint-editor.test.tsx +0 -83
- package/src/__tests__/i18n.test.ts +0 -501
- package/src/__tests__/integration.test.ts +0 -5
- package/src/__tests__/item-extras-editor.test.tsx +0 -85
- package/src/__tests__/traversal.test.ts +0 -377
- package/src/all-editors.test.ts +0 -16
- package/src/all-editors.ts +0 -83
- package/src/all-widgets.ts +0 -7
- package/src/article-editor.tsx +0 -438
- package/src/components/__stories__/blur-input.stories.tsx +0 -22
- package/src/components/__stories__/section-control-button.stories.tsx +0 -26
- package/src/components/__tests__/blur-input.test.tsx +0 -67
- package/src/components/blur-input.tsx +0 -69
- package/src/components/checkbox.tsx +0 -194
- package/src/components/device-framer.tsx +0 -90
- package/src/components/drag-target.tsx +0 -87
- package/src/components/dropdown-option.tsx +0 -311
- package/src/components/form-wrapped-text-field.tsx +0 -245
- package/src/components/graph-points-count-selector.tsx +0 -31
- package/src/components/graph-settings.tsx +0 -627
- package/src/components/graph-type-selector.tsx +0 -31
- package/src/components/hover-behavior.ts +0 -177
- package/src/components/json-editor.tsx +0 -121
- package/src/components/link.ts +0 -173
- package/src/components/section-control-button.tsx +0 -53
- package/src/components/segment-count-selector.tsx +0 -27
- package/src/components/simple-button.tsx +0 -85
- package/src/components/sortable.tsx +0 -187
- package/src/components/util.ts +0 -38
- package/src/components/viewport-resizer.tsx +0 -72
- package/src/components/widget-editor.tsx +0 -222
- package/src/components/widget-select.tsx +0 -48
- package/src/diffs/__stories__/perseus-diff-wrapper.tsx +0 -11
- package/src/diffs/__stories__/structured-item-diff.stories.tsx +0 -855
- package/src/diffs/__stories__/tags-diff.stories.tsx +0 -36
- package/src/diffs/__stories__/text-diff.stories.tsx +0 -35
- package/src/diffs/__tests__/split-diff.test.ts +0 -33
- package/src/diffs/__tests__/string-array-diff.test.ts +0 -73
- package/src/diffs/__tests__/structured-item-diff.test.ts +0 -433
- package/src/diffs/__tests__/widget-diff-performer.test.ts +0 -43
- package/src/diffs/article-diff.tsx +0 -69
- package/src/diffs/item-diff.tsx +0 -76
- package/src/diffs/renderer-diff.tsx +0 -137
- package/src/diffs/split-diff.ts +0 -31
- package/src/diffs/string-array-diff.ts +0 -74
- package/src/diffs/structured-item-diff.tsx +0 -275
- package/src/diffs/tags-diff.tsx +0 -49
- package/src/diffs/text-diff.tsx +0 -193
- package/src/diffs/widget-diff-performer.ts +0 -69
- package/src/diffs/widget-diff.tsx +0 -290
- package/src/editor-page.tsx +0 -325
- package/src/editor.tsx +0 -1099
- package/src/hint-editor.tsx +0 -469
- package/src/i18n.ts +0 -140
- package/src/iframe-content-renderer.tsx +0 -188
- package/src/index.ts +0 -30
- package/src/item-editor.tsx +0 -158
- package/src/item-extras-editor.tsx +0 -230
- package/src/katex-mhchem/index.js +0 -73
- package/src/katex-mhchem/parser.js +0 -2659
- package/src/multirenderer-editor.tsx +0 -1061
- package/src/stateful-article-editor.tsx +0 -173
- package/src/stateful-editor-page.tsx +0 -88
- package/src/styles/perseus-editor.less +0 -1321
- package/src/testing-widgets.ts +0 -11
- package/src/tex-error-view.tsx +0 -90
- package/src/util/points.test.ts +0 -24
- package/src/util/points.ts +0 -13
- package/src/util/register-all-widgets-and-editors-for-testing.test.ts +0 -17
- package/src/util/register-all-widgets-and-editors-for-testing.ts +0 -17
- package/src/version.ts +0 -10
- package/src/widgets/__stories__/categorizer-editor.stories.tsx +0 -24
- package/src/widgets/__stories__/definition-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/dropdown-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/explanation-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/expression-editor.stories.tsx +0 -122
- package/src/widgets/__stories__/image-editor.stories.tsx +0 -72
- package/src/widgets/__stories__/input-number-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/interaction-editor.stories.tsx +0 -37
- package/src/widgets/__stories__/label-image-editor.stories.tsx +0 -99
- package/src/widgets/__stories__/matcher-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/number-line-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/numeric-input-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/radio-editor.stories.tsx +0 -116
- package/src/widgets/__stories__/sorter-editor.stories.tsx +0 -18
- package/src/widgets/__tests__/categorizer-editor.test.tsx +0 -51
- package/src/widgets/__tests__/definition-editor.test.tsx +0 -41
- package/src/widgets/__tests__/dropdown-editor.test.tsx +0 -57
- package/src/widgets/__tests__/explanation-editor.test.tsx +0 -59
- package/src/widgets/__tests__/expression-editor.test.tsx +0 -403
- package/src/widgets/__tests__/input-number-editor.test.tsx +0 -128
- package/src/widgets/__tests__/matcher-editor.test.tsx +0 -47
- package/src/widgets/__tests__/number-line-editor.test.tsx +0 -144
- package/src/widgets/__tests__/numeric-input-editor.test.tsx +0 -177
- package/src/widgets/__tests__/radio-editor.test.tsx +0 -118
- package/src/widgets/__tests__/sorter-editor.test.tsx +0 -55
- package/src/widgets/categorizer-editor.tsx +0 -104
- package/src/widgets/cs-program-editor.tsx +0 -256
- package/src/widgets/definition-editor.tsx +0 -77
- package/src/widgets/deprecated-standin-editor.tsx +0 -29
- package/src/widgets/dropdown-editor.tsx +0 -211
- package/src/widgets/example-graphie-widget-editor.tsx +0 -70
- package/src/widgets/example-graphie-widget.tsx +0 -172
- package/src/widgets/example-widget-editor.tsx +0 -62
- package/src/widgets/example-widget.tsx +0 -154
- package/src/widgets/explanation-editor.tsx +0 -92
- package/src/widgets/expression-editor.tsx +0 -557
- package/src/widgets/graded-group-editor.tsx +0 -161
- package/src/widgets/graded-group-set-editor.tsx +0 -104
- package/src/widgets/grapher-editor.tsx +0 -155
- package/src/widgets/group-editor.tsx +0 -83
- package/src/widgets/iframe-editor.tsx +0 -202
- package/src/widgets/image-editor.tsx +0 -367
- package/src/widgets/input-number-editor.tsx +0 -288
- package/src/widgets/interaction-editor/constraint-editor.tsx +0 -134
- package/src/widgets/interaction-editor/function-editor.tsx +0 -108
- package/src/widgets/interaction-editor/index.ts +0 -3
- package/src/widgets/interaction-editor/interaction-editor.tsx +0 -749
- package/src/widgets/interaction-editor/label-editor.tsx +0 -87
- package/src/widgets/interaction-editor/line-editor.tsx +0 -125
- package/src/widgets/interaction-editor/mathquill-input.tsx +0 -81
- package/src/widgets/interaction-editor/movable-line-editor.tsx +0 -121
- package/src/widgets/interaction-editor/movable-point-editor.tsx +0 -89
- package/src/widgets/interaction-editor/parametric-editor.tsx +0 -117
- package/src/widgets/interaction-editor/point-editor.tsx +0 -72
- package/src/widgets/interaction-editor/rectangle-editor.tsx +0 -96
- package/src/widgets/interactive-graph-editor.tsx +0 -571
- package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -82
- package/src/widgets/label-image/__stories__/behavior.stories.tsx +0 -31
- package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -75
- package/src/widgets/label-image/__stories__/question-markers.stories.tsx +0 -107
- package/src/widgets/label-image/__stories__/select-image.stories.tsx +0 -59
- package/src/widgets/label-image/answer-choices.tsx +0 -231
- package/src/widgets/label-image/behavior.tsx +0 -122
- package/src/widgets/label-image/marker.tsx +0 -290
- package/src/widgets/label-image/question-markers.tsx +0 -176
- package/src/widgets/label-image/select-image.tsx +0 -81
- package/src/widgets/label-image-editor.tsx +0 -252
- package/src/widgets/lights-puzzle-editor.tsx +0 -278
- package/src/widgets/matcher-editor.tsx +0 -149
- package/src/widgets/matrix-editor.tsx +0 -156
- package/src/widgets/measurer-editor.tsx +0 -253
- package/src/widgets/molecule-editor.tsx +0 -64
- package/src/widgets/number-line-editor.tsx +0 -512
- package/src/widgets/numeric-input-editor.tsx +0 -536
- package/src/widgets/orderer-editor.tsx +0 -172
- package/src/widgets/passage-editor.tsx +0 -117
- package/src/widgets/passage-ref-editor.tsx +0 -80
- package/src/widgets/passage-ref-target-editor.tsx +0 -49
- package/src/widgets/plotter-editor.tsx +0 -542
- package/src/widgets/radio/editor.tsx +0 -398
- package/src/widgets/reaction-diagram-editor.tsx +0 -131
- package/src/widgets/sequence-editor.tsx +0 -205
- package/src/widgets/simple-markdown-tester-editor.tsx +0 -86
- package/src/widgets/simple-markdown-tester.tsx +0 -92
- package/src/widgets/simulator-editor.tsx +0 -146
- package/src/widgets/sorter-editor.tsx +0 -103
- package/src/widgets/table-editor.tsx +0 -164
- package/src/widgets/unit-editor.tsx +0 -299
- package/src/widgets/video-editor.tsx +0 -73
- package/tsconfig-build.json +0 -22
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/* eslint-disable react/no-unsafe */
|
|
2
|
-
/**
|
|
3
|
-
* A side by side diff view for Perseus articles.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import PropTypes from "prop-types";
|
|
7
|
-
import * as React from "react";
|
|
8
|
-
import _ from "underscore";
|
|
9
|
-
|
|
10
|
-
import RendererDiff from "./renderer-diff";
|
|
11
|
-
|
|
12
|
-
const rendererProps = PropTypes.shape({
|
|
13
|
-
content: PropTypes.string,
|
|
14
|
-
images: PropTypes.objectOf(PropTypes.any),
|
|
15
|
-
widgets: PropTypes.objectOf(PropTypes.any),
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
type Props = any;
|
|
19
|
-
type State = any;
|
|
20
|
-
|
|
21
|
-
class ArticleDiff extends React.Component<Props, State> {
|
|
22
|
-
static propTypes = {
|
|
23
|
-
// TODO(alex): Check whether we still have any Perseus articles whose
|
|
24
|
-
// top-level json is an object, not an array. If not, simplify here.
|
|
25
|
-
after: PropTypes.oneOfType([
|
|
26
|
-
rendererProps,
|
|
27
|
-
PropTypes.arrayOf(rendererProps),
|
|
28
|
-
]).isRequired,
|
|
29
|
-
before: PropTypes.oneOfType([
|
|
30
|
-
rendererProps,
|
|
31
|
-
PropTypes.arrayOf(rendererProps),
|
|
32
|
-
]).isRequired,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
static _stateFromProps: (arg1: Props) => State = (props) => {
|
|
36
|
-
const {before, after} = props;
|
|
37
|
-
return {
|
|
38
|
-
before: Array.isArray(before) ? before : [before],
|
|
39
|
-
after: Array.isArray(after) ? after : [after],
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
state: State = ArticleDiff._stateFromProps(this.props);
|
|
44
|
-
|
|
45
|
-
UNSAFE_componentWillReceiveProps(nextProps: Props) {
|
|
46
|
-
this.setState(ArticleDiff._stateFromProps(nextProps));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
render(): React.ReactNode {
|
|
50
|
-
const {before, after} = this.state;
|
|
51
|
-
|
|
52
|
-
const sectionCount = Math.max(before.length, after.length);
|
|
53
|
-
|
|
54
|
-
const sections = _.times(sectionCount, (n) => (
|
|
55
|
-
<RendererDiff
|
|
56
|
-
before={n < before.length ? before[n] : undefined}
|
|
57
|
-
after={n < after.length ? after[n] : undefined}
|
|
58
|
-
title={`Section ${n + 1}`}
|
|
59
|
-
showAlignmentOptions={true}
|
|
60
|
-
showSeparator={n < sectionCount - 1}
|
|
61
|
-
key={n}
|
|
62
|
-
/>
|
|
63
|
-
));
|
|
64
|
-
|
|
65
|
-
return <div className="framework-perseus">{sections}</div>;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export default ArticleDiff;
|
package/src/diffs/item-diff.tsx
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A side by side diff view for Perseus exercise items
|
|
3
|
-
* in the standard layout.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import PropTypes from "prop-types";
|
|
7
|
-
import * as React from "react";
|
|
8
|
-
import _ from "underscore";
|
|
9
|
-
|
|
10
|
-
import RendererDiff from "./renderer-diff";
|
|
11
|
-
import WidgetDiff from "./widget-diff";
|
|
12
|
-
|
|
13
|
-
const itemProps = PropTypes.shape({
|
|
14
|
-
question: PropTypes.shape({}).isRequired,
|
|
15
|
-
answerArea: PropTypes.shape({}).isRequired,
|
|
16
|
-
hints: PropTypes.arrayOf(PropTypes.any).isRequired,
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
type Props = any;
|
|
20
|
-
|
|
21
|
-
class ItemDiff extends React.Component<Props> {
|
|
22
|
-
static propTypes = {
|
|
23
|
-
after: itemProps.isRequired,
|
|
24
|
-
before: itemProps.isRequired,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
render(): React.ReactNode {
|
|
28
|
-
const {before, after} = this.props;
|
|
29
|
-
|
|
30
|
-
const hintCount = Math.max(before.hints.length, after.hints.length);
|
|
31
|
-
|
|
32
|
-
const question = (
|
|
33
|
-
<RendererDiff
|
|
34
|
-
before={before.question}
|
|
35
|
-
after={after.question}
|
|
36
|
-
title="Question"
|
|
37
|
-
showAlignmentOptions={false}
|
|
38
|
-
showSeparator={true}
|
|
39
|
-
/>
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const extras = (
|
|
43
|
-
<WidgetDiff
|
|
44
|
-
before={before.answerArea}
|
|
45
|
-
after={after.answerArea}
|
|
46
|
-
title="Question extras"
|
|
47
|
-
/>
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
const hints = _.times(hintCount, function (n) {
|
|
51
|
-
return (
|
|
52
|
-
<RendererDiff
|
|
53
|
-
before={
|
|
54
|
-
n < before.hints.length ? before.hints[n] : undefined
|
|
55
|
-
}
|
|
56
|
-
after={n < after.hints.length ? after.hints[n] : undefined}
|
|
57
|
-
title={`Hint ${n + 1}`}
|
|
58
|
-
showAlignmentOptions={false}
|
|
59
|
-
showSeparator={n < hintCount - 1}
|
|
60
|
-
key={n}
|
|
61
|
-
/>
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
return (
|
|
66
|
-
<div className="framework-perseus">
|
|
67
|
-
{question}
|
|
68
|
-
{extras}
|
|
69
|
-
{hints && <div className="diff-separator" />}
|
|
70
|
-
{hints}
|
|
71
|
-
</div>
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export default ItemDiff;
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import {Widgets} from "@khanacademy/perseus";
|
|
2
|
-
/**
|
|
3
|
-
* A side by side diff view for Perseus renderers.
|
|
4
|
-
*/
|
|
5
|
-
import * as React from "react";
|
|
6
|
-
import _ from "underscore";
|
|
7
|
-
|
|
8
|
-
import TextDiff from "./text-diff";
|
|
9
|
-
import WidgetDiff from "./widget-diff";
|
|
10
|
-
|
|
11
|
-
import type {PerseusRenderer} from "@khanacademy/perseus";
|
|
12
|
-
|
|
13
|
-
// In diffs, only show the widgetInfo props that can change
|
|
14
|
-
const filterWidgetInfo = function (widgetInfo, showAlignmentOptions: boolean) {
|
|
15
|
-
const {alignment, graded, options, type} = widgetInfo || {};
|
|
16
|
-
|
|
17
|
-
const filteredWidgetInfo = {options} as const;
|
|
18
|
-
|
|
19
|
-
// Show alignment options iff multiple valid ones exist for this widget
|
|
20
|
-
if (
|
|
21
|
-
showAlignmentOptions &&
|
|
22
|
-
Widgets.getSupportedAlignments(type).length > 1
|
|
23
|
-
) {
|
|
24
|
-
// @ts-expect-error - TS2339 - Property 'alignment' does not exist on type '{ readonly options: any; }'.
|
|
25
|
-
filteredWidgetInfo.alignment = alignment;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (type === "transformer") {
|
|
29
|
-
// @ts-expect-error - TS2339 - Property 'graded' does not exist on type '{ readonly options: any; }'.
|
|
30
|
-
filteredWidgetInfo.graded = graded;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (Widgets.supportsStaticMode(type)) {
|
|
34
|
-
// @ts-expect-error - TS2339 - Property 'static' does not exist on type '{ readonly options: any; }'.
|
|
35
|
-
filteredWidgetInfo.static = widgetInfo?.static ?? undefined;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return filteredWidgetInfo;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// TODO(michaelpolyak): This type is very similar to `PerseusRenderer` type
|
|
42
|
-
// found in `perseus-all-package/perseus-type.js`, consider just using it.
|
|
43
|
-
type RendererProps = {
|
|
44
|
-
content: string;
|
|
45
|
-
// NOTE: images and widgets may not be set for some items hints,
|
|
46
|
-
// specifically in old revisions, which may only be loaded for diffing.
|
|
47
|
-
widgets: PerseusRenderer["widgets"] | null | undefined;
|
|
48
|
-
images: PerseusRenderer["images"] | null | undefined;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
type Props = {
|
|
52
|
-
// The "after" props of the renderer. Will be displayed on the right.
|
|
53
|
-
after: RendererProps;
|
|
54
|
-
// The "before" props of the renderer. Will be displayed on the left.
|
|
55
|
-
before: RendererProps;
|
|
56
|
-
// If true, show widget alignment options in the diff.
|
|
57
|
-
showAlignmentOptions: boolean;
|
|
58
|
-
// If true, render a horizontal rule after this diff.
|
|
59
|
-
showSeparator: boolean;
|
|
60
|
-
// The heading to render above the side by side diff.
|
|
61
|
-
// (In a code review tool this would be the filename.)
|
|
62
|
-
title: string;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
class RendererDiff extends React.Component<Props> {
|
|
66
|
-
static defaultProps: Partial<Omit<Props, "title">> = {
|
|
67
|
-
after: {
|
|
68
|
-
content: "",
|
|
69
|
-
images: {},
|
|
70
|
-
widgets: {},
|
|
71
|
-
},
|
|
72
|
-
before: {
|
|
73
|
-
content: "",
|
|
74
|
-
images: {},
|
|
75
|
-
widgets: {},
|
|
76
|
-
},
|
|
77
|
-
showAlignmentOptions: false,
|
|
78
|
-
showSeparator: false,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
render(): React.ReactNode {
|
|
82
|
-
const {after, before, showAlignmentOptions, showSeparator, title} =
|
|
83
|
-
this.props;
|
|
84
|
-
|
|
85
|
-
let textDiff;
|
|
86
|
-
let widgetsDiff;
|
|
87
|
-
|
|
88
|
-
if (before.content || after.content) {
|
|
89
|
-
textDiff = (
|
|
90
|
-
<TextDiff
|
|
91
|
-
before={before.content}
|
|
92
|
-
after={after.content}
|
|
93
|
-
title={title}
|
|
94
|
-
/>
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const beforeWidgets = Object.keys(before.widgets ?? {}).filter(
|
|
99
|
-
(widget) => before.content.includes(widget),
|
|
100
|
-
);
|
|
101
|
-
const afterWidgets = Object.keys(after.widgets ?? {}).filter((widget) =>
|
|
102
|
-
after.content.includes(widget),
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
if (beforeWidgets.length || afterWidgets.length) {
|
|
106
|
-
const widgets = _.union(beforeWidgets, afterWidgets);
|
|
107
|
-
widgetsDiff = widgets.map((widget) => (
|
|
108
|
-
<WidgetDiff
|
|
109
|
-
before={filterWidgetInfo(
|
|
110
|
-
before.widgets?.[widget],
|
|
111
|
-
showAlignmentOptions,
|
|
112
|
-
)}
|
|
113
|
-
after={filterWidgetInfo(
|
|
114
|
-
after.widgets?.[widget],
|
|
115
|
-
showAlignmentOptions,
|
|
116
|
-
)}
|
|
117
|
-
title={widget}
|
|
118
|
-
type={
|
|
119
|
-
(before.widgets?.[widget] ?? {}).type ||
|
|
120
|
-
(after.widgets?.[widget] ?? {}).type
|
|
121
|
-
}
|
|
122
|
-
key={widget}
|
|
123
|
-
/>
|
|
124
|
-
));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<div>
|
|
129
|
-
{textDiff}
|
|
130
|
-
{widgetsDiff}
|
|
131
|
-
{showSeparator && <div className="diff-separator" />}
|
|
132
|
-
</div>
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export default RendererDiff;
|
package/src/diffs/split-diff.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import _ from "underscore";
|
|
2
|
-
|
|
3
|
-
// Split a word-wise diff generated by jsdiff into multiple lines, for the
|
|
4
|
-
// purpose of breaking up the diffs into lines, so that modified lines can be
|
|
5
|
-
// faintly highlighted
|
|
6
|
-
|
|
7
|
-
const splitDiff = function (diffEntries: any): any {
|
|
8
|
-
const lines = [];
|
|
9
|
-
let currentLine: Array<any> = [];
|
|
10
|
-
_.each(diffEntries, (entry) => {
|
|
11
|
-
const values = entry.value.split("\n");
|
|
12
|
-
_.each(values, (value, i) => {
|
|
13
|
-
const isNewline = i > 0;
|
|
14
|
-
if (isNewline) {
|
|
15
|
-
// @ts-expect-error - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'never'.
|
|
16
|
-
lines.push(currentLine);
|
|
17
|
-
currentLine = [];
|
|
18
|
-
}
|
|
19
|
-
const newEntry = _.extend({}, entry, {value: value});
|
|
20
|
-
currentLine.push(newEntry);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
if (currentLine.length) {
|
|
25
|
-
// @ts-expect-error - TS2345 - Argument of type 'any[]' is not assignable to parameter of type 'never'.
|
|
26
|
-
lines.push(currentLine);
|
|
27
|
-
}
|
|
28
|
-
return lines;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export default splitDiff;
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
2
|
-
import jsdiff from "jsdiff";
|
|
3
|
-
import _ from "underscore";
|
|
4
|
-
|
|
5
|
-
const statusFor = function (chunk: any) {
|
|
6
|
-
if (chunk.added) {
|
|
7
|
-
return "added";
|
|
8
|
-
}
|
|
9
|
-
if (chunk.removed) {
|
|
10
|
-
return "removed";
|
|
11
|
-
}
|
|
12
|
-
return "unchanged";
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
// Turn a chunk (which contains an array of values and a status)
|
|
16
|
-
// into an array of values, each with the same status
|
|
17
|
-
const splitUpChunk = (chunk: any) =>
|
|
18
|
-
_.map(chunk.value, (value) => {
|
|
19
|
-
return {
|
|
20
|
-
value: value,
|
|
21
|
-
status: statusFor(chunk),
|
|
22
|
-
};
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// Apply `fn` to every element in `lst` and then concatenate all the results
|
|
26
|
-
// http://clojuredocs.org/clojure_core/clojure.core/mapcat
|
|
27
|
-
const mapcat = function (lst: any, fn: (chunk?: any) => any) {
|
|
28
|
-
return _.flatten(_.map(lst, fn), true /* only flatten one level */);
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// > ArrayDiff.diff([1,2,3], [2,3,4]);
|
|
32
|
-
// = [{ "value": [1],
|
|
33
|
-
// "removed": true },
|
|
34
|
-
// { "value": [2, 3] },
|
|
35
|
-
// { "value": [4],
|
|
36
|
-
// "added": true }]
|
|
37
|
-
const ArrayDiff = new jsdiff.Diff();
|
|
38
|
-
ArrayDiff.tokenize = (array) => _.map(array, (elem) => [elem]);
|
|
39
|
-
// The default is `+` for string concatenation, which doesn't work for array
|
|
40
|
-
// concatenation.
|
|
41
|
-
ArrayDiff.join = (a, b: any) => a.concat(b);
|
|
42
|
-
// By default jsDiff uses ===
|
|
43
|
-
ArrayDiff.equals = _.isEqual;
|
|
44
|
-
|
|
45
|
-
// Take the output of jsdiff's function (which concatenates adjacent entries)
|
|
46
|
-
// and make it just one entry per chunk
|
|
47
|
-
// > flattenChunks([{ "value": [1],
|
|
48
|
-
// "removed": true },
|
|
49
|
-
// { "value": [2, 3] },
|
|
50
|
-
// { "value": [4],
|
|
51
|
-
// "added": true }])
|
|
52
|
-
// = [{ "value":1, "status":"removed"},
|
|
53
|
-
// { "value":2, "status":"unchanged"},
|
|
54
|
-
// { "value":3, "status":"unchanged"},
|
|
55
|
-
// { "value":4, "status":"added"}]
|
|
56
|
-
const flattenChunks = (chunks: any) => mapcat(chunks, splitUpChunk);
|
|
57
|
-
|
|
58
|
-
// Take two arrays and create a diff for them. The result is two arrays of
|
|
59
|
-
// objects, one for the things that should be included in a 'before', and one
|
|
60
|
-
// for 'after'
|
|
61
|
-
const stringArrayDiff = function (
|
|
62
|
-
a: ReadonlyArray<any>,
|
|
63
|
-
b: ReadonlyArray<any>,
|
|
64
|
-
): any {
|
|
65
|
-
const diffResult = ArrayDiff.diff(a, b);
|
|
66
|
-
const flattened = flattenChunks(diffResult);
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
before: _.filter(flattened, (entry) => entry.status !== "added"),
|
|
70
|
-
after: _.filter(flattened, (entry) => entry.status !== "removed"),
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export default stringArrayDiff;
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
2
|
-
/**
|
|
3
|
-
* A side by side diff view for Perseus exercise items
|
|
4
|
-
* that do not have the standard question layout.
|
|
5
|
-
*/
|
|
6
|
-
import {
|
|
7
|
-
buildEmptyItemTreeForShape,
|
|
8
|
-
buildMapper,
|
|
9
|
-
itemToTree,
|
|
10
|
-
shapes,
|
|
11
|
-
} from "@khanacademy/perseus";
|
|
12
|
-
import * as React from "react";
|
|
13
|
-
|
|
14
|
-
import RendererDiff from "./renderer-diff";
|
|
15
|
-
import TagsDiff from "./tags-diff";
|
|
16
|
-
|
|
17
|
-
import type {Item, Path, Shape} from "@khanacademy/perseus";
|
|
18
|
-
|
|
19
|
-
type ItemList = [unknown, Path];
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Outputs true if path begins with beginPath, false otherwise.
|
|
23
|
-
*/
|
|
24
|
-
function beginsWith(path: Path, beginPath: Path): boolean {
|
|
25
|
-
let matches = true;
|
|
26
|
-
for (let i = 0; i < beginPath.length; i++) {
|
|
27
|
-
if (i >= path.length) {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
if (beginPath[i] !== path[i]) {
|
|
31
|
-
matches = false;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return matches;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Outputs true if beforePath and afterPath are the same.
|
|
39
|
-
*/
|
|
40
|
-
function checkPath(beforePath: Path, afterPath: Path): boolean {
|
|
41
|
-
if (beforePath.length !== afterPath.length) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
for (let i = 0, l = beforePath.length; i < l; i++) {
|
|
45
|
-
if (beforePath[i] !== afterPath[i]) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return true;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Given a path, returns a title. Puts colons after numbers in the path.
|
|
54
|
-
*/
|
|
55
|
-
function getTitle(path: Path): string {
|
|
56
|
-
const title: Array<string> = [];
|
|
57
|
-
for (let i = 0; i < path.length; i++) {
|
|
58
|
-
if (typeof path[i] === "number") {
|
|
59
|
-
// @ts-expect-error - TS2365 - Operator '+' cannot be applied to types 'string | number' and 'number'.
|
|
60
|
-
title.push((path[i] + 1).toString() + ":");
|
|
61
|
-
} else {
|
|
62
|
-
// @ts-expect-error - TS2345 - Argument of type 'string | number' is not assignable to parameter of type 'string'.
|
|
63
|
-
title.push(path[i]);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return title.join(" ");
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
type Tag = {
|
|
70
|
-
idToName: (arg1: string) => string;
|
|
71
|
-
nameToId: (arg1: string) => string;
|
|
72
|
-
names: ReadonlyArray<string>;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
type Props = {
|
|
76
|
-
after: Item;
|
|
77
|
-
before: Item;
|
|
78
|
-
shape: Shape;
|
|
79
|
-
tags: Tag;
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
class StructuredItemDiff extends React.Component<Props> {
|
|
83
|
-
/**
|
|
84
|
-
* Traverses the given shape and adds paths that are present in
|
|
85
|
-
* beforeList and afterList to result. Note that this method assumes
|
|
86
|
-
* the order of elements in beforeList and afterList, which are
|
|
87
|
-
* from buildMapper(), is the same order they appear in in the shape.
|
|
88
|
-
*/
|
|
89
|
-
static generateCompletePathsList(
|
|
90
|
-
beforeList: Array<ItemList>,
|
|
91
|
-
afterList: Array<ItemList>,
|
|
92
|
-
result: Array<Path>,
|
|
93
|
-
shape: Shape,
|
|
94
|
-
path: ReadonlyArray<any>,
|
|
95
|
-
): void {
|
|
96
|
-
if (
|
|
97
|
-
shape.type === "content" ||
|
|
98
|
-
shape.type === "hint" ||
|
|
99
|
-
shape.type === "tags"
|
|
100
|
-
) {
|
|
101
|
-
const beforePath =
|
|
102
|
-
beforeList.length > 0 && checkPath(path, beforeList[0][1]);
|
|
103
|
-
const afterPath =
|
|
104
|
-
afterList.length > 0 && checkPath(path, afterList[0][1]);
|
|
105
|
-
if (beforePath && afterPath) {
|
|
106
|
-
result.push(path);
|
|
107
|
-
beforeList.splice(0, 1);
|
|
108
|
-
afterList.splice(0, 1);
|
|
109
|
-
} else if (beforePath) {
|
|
110
|
-
result.push(path);
|
|
111
|
-
beforeList.splice(0, 1);
|
|
112
|
-
} else if (afterPath) {
|
|
113
|
-
result.push(path);
|
|
114
|
-
afterList.splice(0, 1);
|
|
115
|
-
}
|
|
116
|
-
} else if (shape.type === "array") {
|
|
117
|
-
let index = 0;
|
|
118
|
-
let newPath = path.concat(index);
|
|
119
|
-
|
|
120
|
-
// For array types, the paths will be in the form [<path>, n],
|
|
121
|
-
// where n is an integer > 0 and increments.
|
|
122
|
-
// As long as either beforeList or afterList has a next element that
|
|
123
|
-
// matches [<path>, n], we recurse into that item with the new path.
|
|
124
|
-
while (
|
|
125
|
-
(beforeList.length > 0 &&
|
|
126
|
-
beginsWith(beforeList[0][1], newPath)) ||
|
|
127
|
-
(afterList.length > 0 && beginsWith(afterList[0][1], newPath))
|
|
128
|
-
) {
|
|
129
|
-
StructuredItemDiff.generateCompletePathsList(
|
|
130
|
-
beforeList,
|
|
131
|
-
afterList,
|
|
132
|
-
result,
|
|
133
|
-
shape.elementShape,
|
|
134
|
-
newPath,
|
|
135
|
-
);
|
|
136
|
-
index++;
|
|
137
|
-
newPath = path.concat(index);
|
|
138
|
-
}
|
|
139
|
-
} else if (shape.type === "object") {
|
|
140
|
-
const keys = Object.keys(shape.shape);
|
|
141
|
-
for (let i = 0; i < keys.length; i++) {
|
|
142
|
-
const newPath = path.concat([keys[i]]);
|
|
143
|
-
StructuredItemDiff.generateCompletePathsList(
|
|
144
|
-
beforeList,
|
|
145
|
-
afterList,
|
|
146
|
-
result,
|
|
147
|
-
shape.shape[keys[i]],
|
|
148
|
-
newPath,
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
render(): React.ReactNode {
|
|
155
|
-
const {before, after, shape, tags} = this.props;
|
|
156
|
-
|
|
157
|
-
const beforeList: Array<ItemList> = [];
|
|
158
|
-
const afterList = [];
|
|
159
|
-
|
|
160
|
-
buildMapper()
|
|
161
|
-
.setContentMapper((c, _, p) => beforeList.push([c, p]))
|
|
162
|
-
.setHintMapper((c, _, p) => beforeList.push([c, p]))
|
|
163
|
-
.setTagsMapper((c, _, p) => beforeList.push([c, p]))
|
|
164
|
-
.mapTree(itemToTree(before), shape);
|
|
165
|
-
|
|
166
|
-
buildMapper()
|
|
167
|
-
// @ts-expect-error - TS2322 - Type 'unknown' is not assignable to type 'never'. | TS2322 - Type 'Path' is not assignable to type 'never'.
|
|
168
|
-
.setContentMapper((c, _, p) => afterList.push([c, p]))
|
|
169
|
-
// @ts-expect-error - TS2322 - Type 'unknown' is not assignable to type 'never'. | TS2322 - Type 'Path' is not assignable to type 'never'.
|
|
170
|
-
.setHintMapper((c, _, p) => afterList.push([c, p]))
|
|
171
|
-
// @ts-expect-error - TS2322 - Type 'unknown' is not assignable to type 'never'. | TS2322 - Type 'Path' is not assignable to type 'never'.
|
|
172
|
-
.setTagsMapper((c, _, p) => afterList.push([c, p]))
|
|
173
|
-
.mapTree(itemToTree(after), shape);
|
|
174
|
-
|
|
175
|
-
// These are used in generateCompletePathsList()
|
|
176
|
-
// and are modified in that method.
|
|
177
|
-
const beforeListModified = beforeList.slice();
|
|
178
|
-
const afterListModified = afterList.slice();
|
|
179
|
-
|
|
180
|
-
const allDiffPaths: Array<Path> = [];
|
|
181
|
-
StructuredItemDiff.generateCompletePathsList(
|
|
182
|
-
beforeListModified,
|
|
183
|
-
afterListModified,
|
|
184
|
-
allDiffPaths,
|
|
185
|
-
shape,
|
|
186
|
-
[],
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
const diffCount = allDiffPaths.length;
|
|
190
|
-
|
|
191
|
-
const diffs: React.ReactNode = allDiffPaths.map((path, n) => {
|
|
192
|
-
const isTag = path[path.length - 1] === "tags";
|
|
193
|
-
const currentTitle = getTitle(path);
|
|
194
|
-
|
|
195
|
-
let before = beforeList.find((e) => {
|
|
196
|
-
return checkPath(e[1], path);
|
|
197
|
-
});
|
|
198
|
-
let after = afterList.find((e) => {
|
|
199
|
-
return checkPath(e[1], path);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
if (isTag) {
|
|
203
|
-
if (!before) {
|
|
204
|
-
before = [[], path];
|
|
205
|
-
}
|
|
206
|
-
if (!after) {
|
|
207
|
-
// @ts-expect-error - TS2322 - Type 'Path[]' is not assignable to type 'undefined'.
|
|
208
|
-
after = [[], path];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const beforeTags: Array<string> = [];
|
|
212
|
-
if (Array.isArray(before[0])) {
|
|
213
|
-
before[0].forEach((tagId) => {
|
|
214
|
-
if (typeof tagId === "string") {
|
|
215
|
-
beforeTags.push(tags.idToName(tagId));
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
const afterTags: Array<string> = [];
|
|
220
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
221
|
-
if (Array.isArray(after[0])) {
|
|
222
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
223
|
-
after[0].forEach((tagId) => {
|
|
224
|
-
if (typeof tagId === "string") {
|
|
225
|
-
afterTags.push(tags.idToName(tagId));
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const intersection = beforeTags.filter((tag) =>
|
|
231
|
-
afterTags.includes(tag),
|
|
232
|
-
);
|
|
233
|
-
const beforeOnly = beforeTags.filter(
|
|
234
|
-
(tag) => !afterTags.includes(tag),
|
|
235
|
-
);
|
|
236
|
-
const afterOnly = afterTags.filter(
|
|
237
|
-
(tag) => !beforeTags.includes(tag),
|
|
238
|
-
);
|
|
239
|
-
|
|
240
|
-
return (
|
|
241
|
-
<TagsDiff
|
|
242
|
-
beforeOnly={beforeOnly}
|
|
243
|
-
afterOnly={afterOnly}
|
|
244
|
-
intersection={intersection}
|
|
245
|
-
title={currentTitle}
|
|
246
|
-
showSeparator={n < diffCount - 1}
|
|
247
|
-
key={n}
|
|
248
|
-
/>
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
if (!before) {
|
|
252
|
-
before = [buildEmptyItemTreeForShape(shapes.content), path];
|
|
253
|
-
}
|
|
254
|
-
if (!after) {
|
|
255
|
-
// @ts-expect-error - TS2322 - Type 'any[]' is not assignable to type 'undefined'.
|
|
256
|
-
after = [buildEmptyItemTreeForShape(shapes.content), path];
|
|
257
|
-
}
|
|
258
|
-
return (
|
|
259
|
-
<RendererDiff
|
|
260
|
-
before={before[0] as any}
|
|
261
|
-
// @ts-expect-error - TS2532 - Object is possibly 'undefined'.
|
|
262
|
-
after={after[0] as any}
|
|
263
|
-
title={currentTitle}
|
|
264
|
-
showAlignmentOptions={false}
|
|
265
|
-
showSeparator={n < diffCount - 1}
|
|
266
|
-
key={n}
|
|
267
|
-
/>
|
|
268
|
-
);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
return <div className="framework-perseus">{diffs}</div>;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
export default StructuredItemDiff;
|