@khanacademy/perseus-editor 4.1.4 → 4.1.6

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.
Files changed (175) hide show
  1. package/dist/es/index.js +1 -1
  2. package/dist/index.js +1 -1
  3. package/package.json +9 -6
  4. package/.babelrc.js +0 -8
  5. package/.eslintrc.js +0 -12
  6. package/CHANGELOG.md +0 -1146
  7. package/src/README.md +0 -7
  8. package/src/__stories__/editor-page.stories.tsx +0 -65
  9. package/src/__stories__/editor.stories.tsx +0 -134
  10. package/src/__stories__/item-editor.stories.tsx +0 -41
  11. package/src/__stories__/tex-error-view.stories.tsx +0 -26
  12. package/src/__testdata__/input-number.testdata.ts +0 -27
  13. package/src/__tests__/editor.test.tsx +0 -142
  14. package/src/__tests__/hint-editor.test.tsx +0 -83
  15. package/src/__tests__/i18n.test.ts +0 -501
  16. package/src/__tests__/integration.test.ts +0 -5
  17. package/src/__tests__/item-extras-editor.test.tsx +0 -85
  18. package/src/__tests__/traversal.test.ts +0 -377
  19. package/src/all-editors.test.ts +0 -16
  20. package/src/all-editors.ts +0 -83
  21. package/src/all-widgets.ts +0 -7
  22. package/src/article-editor.tsx +0 -438
  23. package/src/components/__stories__/blur-input.stories.tsx +0 -22
  24. package/src/components/__stories__/section-control-button.stories.tsx +0 -26
  25. package/src/components/__tests__/blur-input.test.tsx +0 -67
  26. package/src/components/blur-input.tsx +0 -69
  27. package/src/components/checkbox.tsx +0 -194
  28. package/src/components/device-framer.tsx +0 -90
  29. package/src/components/drag-target.tsx +0 -87
  30. package/src/components/dropdown-option.tsx +0 -311
  31. package/src/components/form-wrapped-text-field.tsx +0 -245
  32. package/src/components/graph-points-count-selector.tsx +0 -31
  33. package/src/components/graph-settings.tsx +0 -627
  34. package/src/components/graph-type-selector.tsx +0 -31
  35. package/src/components/hover-behavior.ts +0 -177
  36. package/src/components/json-editor.tsx +0 -121
  37. package/src/components/link.ts +0 -173
  38. package/src/components/section-control-button.tsx +0 -53
  39. package/src/components/segment-count-selector.tsx +0 -27
  40. package/src/components/simple-button.tsx +0 -85
  41. package/src/components/sortable.tsx +0 -187
  42. package/src/components/util.ts +0 -38
  43. package/src/components/viewport-resizer.tsx +0 -72
  44. package/src/components/widget-editor.tsx +0 -222
  45. package/src/components/widget-select.tsx +0 -48
  46. package/src/diffs/__stories__/perseus-diff-wrapper.tsx +0 -11
  47. package/src/diffs/__stories__/structured-item-diff.stories.tsx +0 -855
  48. package/src/diffs/__stories__/tags-diff.stories.tsx +0 -36
  49. package/src/diffs/__stories__/text-diff.stories.tsx +0 -35
  50. package/src/diffs/__tests__/split-diff.test.ts +0 -33
  51. package/src/diffs/__tests__/string-array-diff.test.ts +0 -73
  52. package/src/diffs/__tests__/structured-item-diff.test.ts +0 -433
  53. package/src/diffs/__tests__/widget-diff-performer.test.ts +0 -43
  54. package/src/diffs/article-diff.tsx +0 -69
  55. package/src/diffs/item-diff.tsx +0 -76
  56. package/src/diffs/renderer-diff.tsx +0 -137
  57. package/src/diffs/split-diff.ts +0 -31
  58. package/src/diffs/string-array-diff.ts +0 -74
  59. package/src/diffs/structured-item-diff.tsx +0 -275
  60. package/src/diffs/tags-diff.tsx +0 -49
  61. package/src/diffs/text-diff.tsx +0 -193
  62. package/src/diffs/widget-diff-performer.ts +0 -69
  63. package/src/diffs/widget-diff.tsx +0 -290
  64. package/src/editor-page.tsx +0 -325
  65. package/src/editor.tsx +0 -1099
  66. package/src/hint-editor.tsx +0 -469
  67. package/src/i18n.ts +0 -140
  68. package/src/iframe-content-renderer.tsx +0 -188
  69. package/src/index.ts +0 -30
  70. package/src/item-editor.tsx +0 -158
  71. package/src/item-extras-editor.tsx +0 -230
  72. package/src/katex-mhchem/index.js +0 -73
  73. package/src/katex-mhchem/parser.js +0 -2659
  74. package/src/multirenderer-editor.tsx +0 -1061
  75. package/src/stateful-article-editor.tsx +0 -173
  76. package/src/stateful-editor-page.tsx +0 -88
  77. package/src/styles/perseus-editor.less +0 -1321
  78. package/src/testing-widgets.ts +0 -11
  79. package/src/tex-error-view.tsx +0 -90
  80. package/src/util/points.test.ts +0 -24
  81. package/src/util/points.ts +0 -13
  82. package/src/util/register-all-widgets-and-editors-for-testing.test.ts +0 -17
  83. package/src/util/register-all-widgets-and-editors-for-testing.ts +0 -17
  84. package/src/version.ts +0 -10
  85. package/src/widgets/__stories__/categorizer-editor.stories.tsx +0 -24
  86. package/src/widgets/__stories__/definition-editor.stories.tsx +0 -18
  87. package/src/widgets/__stories__/dropdown-editor.stories.tsx +0 -18
  88. package/src/widgets/__stories__/explanation-editor.stories.tsx +0 -18
  89. package/src/widgets/__stories__/expression-editor.stories.tsx +0 -122
  90. package/src/widgets/__stories__/image-editor.stories.tsx +0 -72
  91. package/src/widgets/__stories__/input-number-editor.stories.tsx +0 -18
  92. package/src/widgets/__stories__/interaction-editor.stories.tsx +0 -37
  93. package/src/widgets/__stories__/label-image-editor.stories.tsx +0 -99
  94. package/src/widgets/__stories__/matcher-editor.stories.tsx +0 -18
  95. package/src/widgets/__stories__/number-line-editor.stories.tsx +0 -18
  96. package/src/widgets/__stories__/numeric-input-editor.stories.tsx +0 -18
  97. package/src/widgets/__stories__/radio-editor.stories.tsx +0 -116
  98. package/src/widgets/__stories__/sorter-editor.stories.tsx +0 -18
  99. package/src/widgets/__tests__/categorizer-editor.test.tsx +0 -51
  100. package/src/widgets/__tests__/definition-editor.test.tsx +0 -41
  101. package/src/widgets/__tests__/dropdown-editor.test.tsx +0 -57
  102. package/src/widgets/__tests__/explanation-editor.test.tsx +0 -59
  103. package/src/widgets/__tests__/expression-editor.test.tsx +0 -403
  104. package/src/widgets/__tests__/input-number-editor.test.tsx +0 -128
  105. package/src/widgets/__tests__/matcher-editor.test.tsx +0 -47
  106. package/src/widgets/__tests__/number-line-editor.test.tsx +0 -144
  107. package/src/widgets/__tests__/numeric-input-editor.test.tsx +0 -177
  108. package/src/widgets/__tests__/radio-editor.test.tsx +0 -118
  109. package/src/widgets/__tests__/sorter-editor.test.tsx +0 -55
  110. package/src/widgets/categorizer-editor.tsx +0 -104
  111. package/src/widgets/cs-program-editor.tsx +0 -256
  112. package/src/widgets/definition-editor.tsx +0 -77
  113. package/src/widgets/deprecated-standin-editor.tsx +0 -29
  114. package/src/widgets/dropdown-editor.tsx +0 -211
  115. package/src/widgets/example-graphie-widget-editor.tsx +0 -70
  116. package/src/widgets/example-graphie-widget.tsx +0 -172
  117. package/src/widgets/example-widget-editor.tsx +0 -62
  118. package/src/widgets/example-widget.tsx +0 -154
  119. package/src/widgets/explanation-editor.tsx +0 -92
  120. package/src/widgets/expression-editor.tsx +0 -557
  121. package/src/widgets/graded-group-editor.tsx +0 -161
  122. package/src/widgets/graded-group-set-editor.tsx +0 -104
  123. package/src/widgets/grapher-editor.tsx +0 -155
  124. package/src/widgets/group-editor.tsx +0 -83
  125. package/src/widgets/iframe-editor.tsx +0 -202
  126. package/src/widgets/image-editor.tsx +0 -367
  127. package/src/widgets/input-number-editor.tsx +0 -288
  128. package/src/widgets/interaction-editor/constraint-editor.tsx +0 -134
  129. package/src/widgets/interaction-editor/function-editor.tsx +0 -108
  130. package/src/widgets/interaction-editor/index.ts +0 -3
  131. package/src/widgets/interaction-editor/interaction-editor.tsx +0 -749
  132. package/src/widgets/interaction-editor/label-editor.tsx +0 -87
  133. package/src/widgets/interaction-editor/line-editor.tsx +0 -125
  134. package/src/widgets/interaction-editor/mathquill-input.tsx +0 -81
  135. package/src/widgets/interaction-editor/movable-line-editor.tsx +0 -121
  136. package/src/widgets/interaction-editor/movable-point-editor.tsx +0 -89
  137. package/src/widgets/interaction-editor/parametric-editor.tsx +0 -117
  138. package/src/widgets/interaction-editor/point-editor.tsx +0 -72
  139. package/src/widgets/interaction-editor/rectangle-editor.tsx +0 -96
  140. package/src/widgets/interactive-graph-editor.tsx +0 -571
  141. package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -82
  142. package/src/widgets/label-image/__stories__/behavior.stories.tsx +0 -31
  143. package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -75
  144. package/src/widgets/label-image/__stories__/question-markers.stories.tsx +0 -107
  145. package/src/widgets/label-image/__stories__/select-image.stories.tsx +0 -59
  146. package/src/widgets/label-image/answer-choices.tsx +0 -231
  147. package/src/widgets/label-image/behavior.tsx +0 -122
  148. package/src/widgets/label-image/marker.tsx +0 -290
  149. package/src/widgets/label-image/question-markers.tsx +0 -176
  150. package/src/widgets/label-image/select-image.tsx +0 -81
  151. package/src/widgets/label-image-editor.tsx +0 -252
  152. package/src/widgets/lights-puzzle-editor.tsx +0 -278
  153. package/src/widgets/matcher-editor.tsx +0 -149
  154. package/src/widgets/matrix-editor.tsx +0 -156
  155. package/src/widgets/measurer-editor.tsx +0 -253
  156. package/src/widgets/molecule-editor.tsx +0 -64
  157. package/src/widgets/number-line-editor.tsx +0 -512
  158. package/src/widgets/numeric-input-editor.tsx +0 -536
  159. package/src/widgets/orderer-editor.tsx +0 -172
  160. package/src/widgets/passage-editor.tsx +0 -117
  161. package/src/widgets/passage-ref-editor.tsx +0 -80
  162. package/src/widgets/passage-ref-target-editor.tsx +0 -49
  163. package/src/widgets/plotter-editor.tsx +0 -542
  164. package/src/widgets/radio/editor.tsx +0 -398
  165. package/src/widgets/reaction-diagram-editor.tsx +0 -131
  166. package/src/widgets/sequence-editor.tsx +0 -205
  167. package/src/widgets/simple-markdown-tester-editor.tsx +0 -86
  168. package/src/widgets/simple-markdown-tester.tsx +0 -92
  169. package/src/widgets/simulator-editor.tsx +0 -146
  170. package/src/widgets/sorter-editor.tsx +0 -103
  171. package/src/widgets/table-editor.tsx +0 -164
  172. package/src/widgets/unit-editor.tsx +0 -299
  173. package/src/widgets/video-editor.tsx +0 -73
  174. package/tsconfig-build.json +0 -22
  175. 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;
@@ -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;
@@ -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;