@khanacademy/perseus-editor 4.1.5 → 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 -1155
  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,187 +0,0 @@
1
- import createReactClass from "create-react-class";
2
- import PropTypes from "prop-types";
3
- import * as React from "react";
4
- import ReactDOM from "react-dom";
5
-
6
- const PT = PropTypes;
7
-
8
- // Takes an array of components to sort
9
- // eslint-disable-next-line react/no-unsafe
10
- const SortableArea = createReactClass({
11
- propTypes: {
12
- className: PT.string,
13
- components: PT.arrayOf(PT.node).isRequired,
14
- onReorder: PT.func.isRequired,
15
- style: PT.any,
16
- verify: PT.func,
17
- },
18
- getDefaultProps: function () {
19
- return {verify: () => true};
20
- },
21
- getInitialState: function () {
22
- return {
23
- // index of the component being dragged
24
- dragging: null,
25
- components: this.props.components,
26
- };
27
- },
28
- // Firefox refuses to drag an element unless you set data on it. Hackily
29
- // add data each time an item is dragged.
30
- componentDidMount: function () {
31
- this._setDragEvents();
32
- },
33
- // eslint-disable-next-line react/no-unsafe
34
- UNSAFE_componentWillReceiveProps: function (nextProps) {
35
- this.setState({components: nextProps.components});
36
- },
37
- componentDidUpdate: function () {
38
- this._setDragEvents();
39
- },
40
- // Alternatively send each handler to each component individually,
41
- // partially applied
42
- onDragStart: function (startIndex) {
43
- this.setState({dragging: startIndex});
44
- },
45
- onDrop: function () {
46
- // tell the parent component
47
- this.setState({dragging: null});
48
- this.props.onReorder(this.state.components);
49
- },
50
- onDragEnter: function (enterIndex) {
51
- // When a label is first dragged it triggers a dragEnter with itself,
52
- // which we don't care about.
53
- if (this.state.dragging === enterIndex) {
54
- return;
55
- }
56
-
57
- const newComponents = this.state.components.slice();
58
-
59
- // splice the tab out of its old position
60
- const removed = newComponents.splice(this.state.dragging, 1);
61
- // ... and into its new position
62
- newComponents.splice(enterIndex, 0, removed[0]);
63
-
64
- const verified = this.props.verify(newComponents);
65
- if (verified) {
66
- this.setState({
67
- dragging: enterIndex,
68
- components: newComponents,
69
- });
70
- }
71
- return verified;
72
- },
73
- _listenEvent: function (e) {
74
- e.dataTransfer.setData("hackhackhack", "because browsers!");
75
- },
76
- _cancelEvent: function (e) {
77
- // prevent the browser from redirecting to 'because browsers!'
78
- e.preventDefault();
79
- },
80
- _setDragEvents: function () {
81
- this._dragItems = this._dragItems || [];
82
- const items =
83
- // @ts-expect-error - TS2531 - Object is possibly 'null'
84
- ReactDOM.findDOMNode(this).querySelectorAll("[draggable=true]");
85
-
86
- const oldItems: Array<HTMLElement> = [];
87
- const newItems: Array<HTMLElement> = [];
88
-
89
- for (let i = 0; i < this._dragItems.length; i++) {
90
- const item = this._dragItems[i];
91
- if (items.indexOf(item) < 0) {
92
- oldItems.push(item);
93
- }
94
- }
95
-
96
- for (let i = 0; i < items.length; i++) {
97
- const item = items[i];
98
- if (this._dragItems.indexOf(item) < 0) {
99
- newItems.push(item);
100
- }
101
- }
102
-
103
- for (let i = 0; i < newItems.length; i++) {
104
- const dragItem = newItems[i];
105
- dragItem.addEventListener("dragstart", this._listenEvent);
106
- dragItem.addEventListener("drop", this._cancelEvent);
107
- }
108
-
109
- for (let i = 0; i < oldItems.length; i++) {
110
- const dragItem = oldItems[i];
111
- dragItem.removeEventListener("dragstart", this._listenEvent);
112
- dragItem.removeEventListener("drop", this._cancelEvent);
113
- }
114
- },
115
- render: function () {
116
- const sortables = this.state.components.map((component, index) => (
117
- <SortableItem
118
- index={index}
119
- component={component}
120
- area={this}
121
- key={component.key}
122
- draggable={component.props.draggable}
123
- dragging={index === this.state.dragging}
124
- />
125
- ));
126
- return (
127
- <ol className={this.props.className} style={this.props.style}>
128
- {sortables}
129
- </ol>
130
- );
131
- },
132
- });
133
-
134
- // An individual sortable item
135
- const SortableItem = createReactClass({
136
- propTypes: {
137
- area: PT.shape({
138
- onDragEnter: PT.func.isRequired,
139
- onDragStart: PT.func.isRequired,
140
- onDrop: PT.func.isRequired,
141
- }),
142
- component: PT.node.isRequired,
143
- dragging: PT.bool.isRequired,
144
- draggable: PT.bool.isRequired,
145
- index: PT.number.isRequired,
146
- },
147
- handleDragStart: function (e) {
148
- e.nativeEvent.dataTransfer.effectAllowed = "move";
149
- this.props.area.onDragStart(this.props.index);
150
- },
151
- handleDrop: function () {
152
- this.props.area.onDrop(this.props.index);
153
- },
154
- handleDragEnter: function (e) {
155
- const verified = this.props.area.onDragEnter(this.props.index);
156
- // Ideally this would change the cursor based on whether this is a
157
- // valid place to drop.
158
- e.nativeEvent.dataTransfer.effectAllowed = verified ? "move" : "none";
159
- },
160
- handleDragOver: function (e) {
161
- // allow a drop by preventing default handling
162
- e.preventDefault();
163
- },
164
- render: function () {
165
- let dragState = "sortable-disabled";
166
- if (this.props.dragging) {
167
- dragState = "sortable-dragging";
168
- } else if (this.props.draggable) {
169
- dragState = "sortable-enabled";
170
- }
171
-
172
- return (
173
- <li
174
- draggable={this.props.draggable}
175
- className={dragState}
176
- onDragStart={this.handleDragStart}
177
- onDrop={this.handleDrop}
178
- onDragEnter={this.handleDragEnter}
179
- onDragOver={this.handleDragOver}
180
- >
181
- {this.props.component}
182
- </li>
183
- );
184
- },
185
- });
186
-
187
- export default SortableArea;
@@ -1,38 +0,0 @@
1
- export function focusWithChromeStickyFocusBugWorkaround(element: Element) {
2
- // NOTE(marksandstrom) Chrome sticky focus bug.
3
- //
4
- // There is a bug in Chrome related to sticky positioning and
5
- // focus: When a child of an element with sticky positioning is
6
- // focused with the focus() method, the scrollable area
7
- // containing that element will scroll to the top (even though
8
- // the element is already visble on the page). For a dropdown
9
- // this means that the area will be scrolled to the top when
10
- // the user interacts with the dropdown (since we actively
11
- // manage dropdown header and option focus for keyboard users).
12
- //
13
- // To work around this bug the `preventScroll` focus option is
14
- // used.
15
- //
16
- // Using `focusOptions` is an experimental browser feature
17
- // "that should not be used in production code". However, since
18
- // Chrome is the only browser we support that exhibits this
19
- // buggy behavior and using `focusOptions` works around the
20
- // bug, it is perfectly reasonable to use the feature in this
21
- // case. We are not relying on the effect of the
22
- // `preventScroll` option otherwise.
23
- //
24
- // focusOptions documentation:
25
- //
26
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus
27
- //
28
- // Note that there could be cases where we want default focus() behavior.
29
- // One that comes to mind is a dropdown with a long list of options that
30
- // might be rendered off screen when opened. With this workaround the
31
- // selected option would not be scrolled into view if the option is near
32
- // the end of the list. We're not aware of any uses of the dropdown where
33
- // this is an issue, however, so there isn't currently a way to out of the
34
- // `preventScroll` behavior.
35
-
36
- // @ts-expect-error - TS2339 - Property 'focus' does not exist on type 'Element'.
37
- element.focus({preventScroll: true});
38
- }
@@ -1,72 +0,0 @@
1
- /**
2
- * A component that displays controls for choosing a viewport size.
3
- * Renders three buttons: "Phone", "Tablet", and "Desktop".
4
- */
5
- import {components, constants, icons} from "@khanacademy/perseus";
6
- import PropTypes from "prop-types";
7
- import * as React from "react";
8
-
9
- import type {DeviceType} from "@khanacademy/perseus";
10
-
11
- const {ButtonGroup, InlineIcon} = components;
12
- const {devices} = constants;
13
- const {iconDesktop, iconMobilePhone, iconTablet} = icons;
14
-
15
- type Props = {
16
- deviceType: DeviceType;
17
- onViewportSizeChanged: (arg1: DeviceType) => unknown;
18
- };
19
-
20
- class ViewportResizer extends React.Component<Props> {
21
- static propTypes = {
22
- // The current device type that is selected.
23
- deviceType: PropTypes.string.isRequired,
24
- // A callback that is passed (width, height) as the dimensions of the
25
- // viewport to resize to.
26
- onViewportSizeChanged: PropTypes.func.isRequired,
27
- };
28
-
29
- handleChange: (value: DeviceType) => void = (value: DeviceType) => {
30
- this.props.onViewportSizeChanged(value);
31
- };
32
-
33
- render(): React.ReactNode {
34
- const phoneButtonContents = (
35
- <span>
36
- <InlineIcon {...iconMobilePhone} /> Phone
37
- </span>
38
- );
39
- const tabletButtonContents = (
40
- <span>
41
- <InlineIcon {...iconTablet} /> Tablet
42
- </span>
43
- );
44
- const desktopButtonContents = (
45
- <span>
46
- <InlineIcon {...iconDesktop} /> Desktop
47
- </span>
48
- );
49
-
50
- // TODO(david): Allow input of custom viewport sizes.
51
- return (
52
- <span className="viewport-resizer">
53
- Viewport:{" "}
54
- <ButtonGroup
55
- value={this.props.deviceType}
56
- allowEmpty={false}
57
- buttons={[
58
- {value: devices.PHONE, content: phoneButtonContents},
59
- {value: devices.TABLET, content: tabletButtonContents},
60
- {
61
- value: devices.DESKTOP,
62
- content: desktopButtonContents,
63
- },
64
- ]}
65
- onChange={this.handleChange}
66
- />
67
- </span>
68
- );
69
- }
70
- }
71
-
72
- export default ViewportResizer;
@@ -1,222 +0,0 @@
1
- /* eslint-disable @khanacademy/ts-no-error-suppressions */
2
- import {
3
- components,
4
- icons,
5
- Widgets,
6
- WIDGET_PROP_DENYLIST,
7
- } from "@khanacademy/perseus";
8
- import * as React from "react";
9
- import _ from "underscore";
10
-
11
- import SectionControlButton from "./section-control-button";
12
-
13
- import type {Alignment, PerseusWidget} from "@khanacademy/perseus";
14
-
15
- const {InlineIcon} = components;
16
- const {iconChevronDown, iconChevronRight, iconTrash} = icons;
17
-
18
- type WidgetEditorProps = {
19
- // Unserialized props
20
- id: string;
21
- onChange: (
22
- widgetInfo: PerseusWidget,
23
- cb?: () => unknown,
24
- silent?: boolean,
25
- ) => unknown;
26
- onRemove: () => unknown;
27
- apiOptions: any;
28
- } & PerseusWidget;
29
-
30
- type WidgetEditorState = {
31
- showWidget: boolean;
32
- widgetInfo: PerseusWidget;
33
- };
34
-
35
- const _upgradeWidgetInfo = (props: WidgetEditorProps): PerseusWidget => {
36
- // We can't call serialize here because this.refs.widget
37
- // doesn't exist before this component is mounted.
38
- const filteredProps = _.omit(props, WIDGET_PROP_DENYLIST);
39
- // @ts-expect-error TS(2345) Type '"categorizer" | undefined' is not assignable to type '"deprecated-standin"'.
40
- return Widgets.upgradeWidgetInfoToLatestVersion(filteredProps);
41
- };
42
-
43
- // This component handles upgading widget editor props via prop
44
- // upgrade transforms. Widget editors will always be rendered
45
- // with all available transforms applied, but the results of those
46
- // transforms will not be propogated upwards until serialization.
47
- // eslint-disable-next-line react/no-unsafe
48
- class WidgetEditor extends React.Component<
49
- WidgetEditorProps,
50
- WidgetEditorState
51
- > {
52
- constructor(props: WidgetEditorProps) {
53
- super(props);
54
- this.state = {
55
- showWidget: false,
56
- widgetInfo: _upgradeWidgetInfo(props),
57
- };
58
- }
59
-
60
- // eslint-disable-next-line react/no-unsafe
61
- UNSAFE_componentWillReceiveProps(nextProps: WidgetEditorProps) {
62
- this.setState({widgetInfo: _upgradeWidgetInfo(nextProps)});
63
- }
64
-
65
- _toggleWidget = (e: React.SyntheticEvent) => {
66
- e.preventDefault();
67
- this.setState({showWidget: !this.state.showWidget});
68
- };
69
-
70
- _handleWidgetChange = (
71
- newProps: WidgetEditorProps,
72
- cb: () => unknown,
73
- silent: boolean,
74
- ) => {
75
- const newWidgetInfo = Object.assign(
76
- {},
77
- this.state.widgetInfo,
78
- ) as PerseusWidget;
79
- newWidgetInfo.options = Object.assign(
80
- // eslint-disable-next-line react/no-string-refs
81
- // @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
82
- this.refs.widget.serialize(),
83
- newProps,
84
- );
85
- this.props.onChange(newWidgetInfo, cb, silent);
86
- };
87
-
88
- _toggleStatic = (e: Event) => {
89
- e.preventDefault();
90
- const newWidgetInfo = Object.assign({}, this.state.widgetInfo, {
91
- static: !this.state.widgetInfo.static,
92
- }) as PerseusWidget;
93
- this.props.onChange(newWidgetInfo);
94
- };
95
-
96
- _handleAlignmentChange = (e: React.SyntheticEvent<HTMLSelectElement>) => {
97
- const newAlignment = e.currentTarget.value as Alignment;
98
- const newWidgetInfo = Object.assign(
99
- {},
100
- this.state.widgetInfo,
101
- ) as PerseusWidget;
102
- newWidgetInfo.alignment = newAlignment;
103
- this.props.onChange(newWidgetInfo);
104
- };
105
-
106
- getSaveWarnings = () => {
107
- // eslint-disable-next-line react/no-string-refs
108
- // @ts-expect-error - TS2339 - Property 'getSaveWarnings' does not exist on type 'ReactInstance'.
109
- const issuesFunc = this.refs.widget.getSaveWarnings;
110
- return issuesFunc ? issuesFunc() : [];
111
- };
112
-
113
- serialize = () => {
114
- // TODO(alex): Make this properly handle the case where we load json
115
- // with a more recent widget version than this instance of Perseus
116
- // knows how to handle.
117
- const widgetInfo = this.state.widgetInfo;
118
- return {
119
- type: widgetInfo.type,
120
- alignment: widgetInfo.alignment,
121
- static: widgetInfo.static,
122
- graded: widgetInfo.graded,
123
- // eslint-disable-next-line react/no-string-refs
124
- // @ts-expect-error - TS2339 - Property 'serialize' does not exist on type 'ReactInstance'.
125
- options: this.refs.widget.serialize(),
126
- version: widgetInfo.version,
127
- };
128
- };
129
-
130
- render(): React.ReactNode {
131
- const widgetInfo = this.state.widgetInfo;
132
-
133
- const Ed = Widgets.getEditor(widgetInfo.type);
134
- let supportedAlignments: ReadonlyArray<Alignment>;
135
- if (this.props.apiOptions.showAlignmentOptions) {
136
- supportedAlignments = Widgets.getSupportedAlignments(
137
- widgetInfo.type,
138
- );
139
- } else {
140
- // NOTE(kevinb): "default" is not one in `validAlignments` in widgets.js.
141
- supportedAlignments = ["default"];
142
- }
143
-
144
- const supportsStaticMode = Widgets.supportsStaticMode(widgetInfo.type);
145
-
146
- return (
147
- <div className="perseus-widget-editor">
148
- <div
149
- className={
150
- "perseus-widget-editor-title " +
151
- (this.state.showWidget ? "open" : "closed")
152
- }
153
- >
154
- {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
155
- <a
156
- className="perseus-widget-editor-title-id"
157
- href="#"
158
- onClick={this._toggleWidget}
159
- >
160
- {this.props.id}
161
- {this.state.showWidget ? (
162
- <InlineIcon {...iconChevronDown} />
163
- ) : (
164
- <InlineIcon {...iconChevronRight} />
165
- )}
166
- </a>
167
-
168
- {supportsStaticMode && (
169
- <input
170
- type="button"
171
- // @ts-expect-error - TS2322 - Type '(e: Event) => void' is not assignable to type 'MouseEventHandler<HTMLInputElement>'.
172
- onClick={this._toggleStatic}
173
- className="simple-button--small"
174
- value={
175
- widgetInfo.static
176
- ? "Unset as static"
177
- : "Set as static"
178
- }
179
- />
180
- )}
181
- {supportedAlignments.length > 1 && (
182
- <select
183
- className="alignment"
184
- value={widgetInfo.alignment}
185
- onChange={this._handleAlignmentChange}
186
- >
187
- {supportedAlignments.map((alignment) => (
188
- <option key={alignment}>{alignment}</option>
189
- ))}
190
- </select>
191
- )}
192
- <SectionControlButton
193
- icon={iconTrash}
194
- onClick={() => {
195
- this.props.onRemove();
196
- }}
197
- title="Remove image widget"
198
- />
199
- </div>
200
- <div
201
- className={
202
- "perseus-widget-editor-content " +
203
- (this.state.showWidget ? "enter" : "leave")
204
- }
205
- >
206
- {Ed && (
207
- <Ed
208
- // eslint-disable-next-line react/no-string-refs
209
- ref="widget"
210
- onChange={this._handleWidgetChange}
211
- static={widgetInfo.static}
212
- apiOptions={this.props.apiOptions}
213
- {...widgetInfo.options}
214
- />
215
- )}
216
- </div>
217
- </div>
218
- );
219
- }
220
- }
221
-
222
- export default WidgetEditor;
@@ -1,48 +0,0 @@
1
- import {Widgets} from "@khanacademy/perseus";
2
- import * as React from "react";
3
- import _ from "underscore";
4
-
5
- type WidgetSelectProps = {
6
- onChange?: (widgetType: string) => unknown;
7
- };
8
-
9
- class WidgetSelect extends React.Component<WidgetSelectProps> {
10
- shouldComponentUpdate() {
11
- return false;
12
- }
13
-
14
- handleChange = (e: React.SyntheticEvent<HTMLSelectElement>) => {
15
- const widgetType = e.currentTarget.value;
16
- if (widgetType === "") {
17
- // TODO(alpert): Not sure if change will trigger here
18
- // but might as well be safe
19
- return;
20
- }
21
- if (this.props.onChange) {
22
- this.props.onChange(widgetType);
23
- }
24
- };
25
-
26
- render(): React.ReactNode {
27
- const widgets = Widgets.getPublicWidgets();
28
- const orderedWidgetNames = _.sortBy(_.keys(widgets), (name) => {
29
- return widgets[name].displayName;
30
- });
31
- const addWidgetString = "Add a widget\u2026";
32
- return (
33
- <select value="" onChange={this.handleChange}>
34
- <option value="">{addWidgetString}</option>
35
- <option disabled>--</option>
36
- {_.map(orderedWidgetNames, (name) => {
37
- return (
38
- <option key={name} value={name}>
39
- {widgets[name].displayName}
40
- </option>
41
- );
42
- })}
43
- </select>
44
- );
45
- }
46
- }
47
-
48
- export default WidgetSelect;
@@ -1,11 +0,0 @@
1
- import * as React from "react";
2
-
3
- type Props = {
4
- children: React.ReactNode;
5
- };
6
-
7
- const Wrapper = ({children}: Props): React.ReactElement => {
8
- return <div className="perseus-diff">{children}</div>;
9
- };
10
-
11
- export default Wrapper;