@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,367 +0,0 @@
1
- /* eslint-disable jsx-a11y/anchor-is-valid, react/sort-comp */
2
- import {
3
- components,
4
- icons,
5
- Changeable,
6
- EditorJsonify,
7
- Util,
8
- } from "@khanacademy/perseus";
9
- import * as React from "react";
10
- import _ from "underscore";
11
-
12
- import BlurInput from "../components/blur-input";
13
- import Editor from "../editor";
14
-
15
- import type {APIOptions, Range, Size} from "@khanacademy/perseus";
16
-
17
- const {InfoTip, InlineIcon, RangeInput} = components;
18
-
19
- const defaultBoxSize = 400;
20
- const defaultRange = [0, 10] as const;
21
- const defaultBackgroundImage = {
22
- url: null,
23
- width: 0,
24
- height: 0,
25
- } as const;
26
-
27
- // Match any image URL (including "web+graphie" links) that is hosted by KA.
28
- // We're somewhat generous in our AWS URL matching
29
- // ("ka-<something>.s3.amazonaws.com") so that we don't have to update Perseus
30
- // every time we add a new proxied AWS bucket.
31
- const INTERNALLY_HOSTED_DOMAINS =
32
- "(" +
33
- "ka-.*.s3.amazonaws.com|" +
34
- "(fastly|cdn).kastatic.org|" +
35
- "khanacademy.org|" +
36
- "kasandbox.org" +
37
- ")";
38
- const INTERNALLY_HOSTED_URL_RE = new RegExp(
39
- "^(https?|web\\+graphie)://[^/]*" + INTERNALLY_HOSTED_DOMAINS,
40
- );
41
-
42
- /**
43
- * Alignment option for captions, relative to specified coordinates.
44
- */
45
- const captionAlignments = [
46
- "center",
47
- "above",
48
- "above right",
49
- "right",
50
- "below right",
51
- "below",
52
- "below left",
53
- "left",
54
- "above left",
55
- ];
56
-
57
- type Props = Changeable.ChangeableProps & {
58
- apiOptions: APIOptions;
59
-
60
- title: string;
61
- range: [Readonly<Range>, Readonly<Range>];
62
- box: Size;
63
- backgroundImage: any;
64
- labels: ReadonlyArray<string>;
65
- alt: string;
66
- caption: string;
67
- };
68
-
69
- type DefaultProps = {
70
- title: NonNullable<Props["title"]>;
71
- range: NonNullable<Props["range"]>;
72
- box: NonNullable<Props["box"]>;
73
- backgroundImage: NonNullable<Props["backgroundImage"]>;
74
- labels: NonNullable<Props["labels"]>;
75
- alt: NonNullable<Props["alt"]>;
76
- caption: NonNullable<Props["caption"]>;
77
- };
78
-
79
- type State = {
80
- backgroundImageError?: string;
81
- };
82
-
83
- class ImageEditor extends React.Component<Props> {
84
- _isMounted = false;
85
-
86
- static displayName = "ImageEditor";
87
- static widgetName = "image";
88
-
89
- static defaultProps: DefaultProps = {
90
- title: "",
91
- range: [defaultRange, defaultRange],
92
- box: [defaultBoxSize, defaultBoxSize],
93
- backgroundImage: defaultBackgroundImage,
94
- labels: [],
95
- alt: "",
96
- caption: "",
97
- };
98
-
99
- state: State = {
100
- backgroundImageError: "",
101
- };
102
-
103
- componentDidMount() {
104
- // TODO(scottgrant): This is a hack to remove the deprecated call to
105
- // this.isMounted() but is still considered an anti-pattern.
106
- this._isMounted = true;
107
- }
108
-
109
- componentWillUnmount() {
110
- this._isMounted = false;
111
- }
112
-
113
- render() {
114
- const backgroundImage = this.props.backgroundImage;
115
-
116
- const imageSettings = (
117
- <div className="image-settings">
118
- {!Util.isLabeledSVG(backgroundImage.url) && (
119
- <div>
120
- <label>
121
- <div>Preview:</div>
122
- <img
123
- alt="Editor preview of image"
124
- src={backgroundImage.url}
125
- style={{
126
- width: "100%",
127
- }}
128
- />
129
- </label>
130
- </div>
131
- )}
132
- <div>
133
- <label>
134
- <div>Dimensions:</div>
135
- <p>
136
- {backgroundImage.width}x{backgroundImage.height}
137
- </p>
138
- </label>
139
- </div>
140
-
141
- <div>
142
- <label>
143
- <div>
144
- Alt text:
145
- <InfoTip>
146
- This is important for screenreaders. The content
147
- of this alt text will be formatted as markdown
148
- (tables, emphasis, etc. are supported).
149
- </InfoTip>
150
- </div>
151
- <Editor
152
- apiOptions={this.props.apiOptions}
153
- content={this.props.alt}
154
- onChange={(props) => {
155
- if (props.content != null) {
156
- this.change("alt", props.content);
157
- }
158
- }}
159
- widgetEnabled={false}
160
- />
161
- </label>
162
- </div>
163
- <div>
164
- <label>
165
- <div>Caption:</div>
166
- <Editor
167
- apiOptions={this.props.apiOptions}
168
- content={this.props.caption}
169
- onChange={(props) => {
170
- if (props.content != null) {
171
- this.change("caption", props.content);
172
- }
173
- }}
174
- widgetEnabled={false}
175
- />
176
- </label>
177
- </div>
178
- </div>
179
- );
180
-
181
- const backgroundImageErrorText = (
182
- <div className="renderer-widget-error">
183
- {this.state.backgroundImageError}
184
- </div>
185
- );
186
-
187
- return (
188
- <div className="perseus-image-editor">
189
- <label>
190
- Image url:
191
- <InfoTip>Paste an image or graphie image URL.</InfoTip>
192
- {this.state.backgroundImageError &&
193
- backgroundImageErrorText}
194
- <BlurInput
195
- value={backgroundImage.url || ""}
196
- style={{width: 332}}
197
- onChange={(url) => this.onUrlChange(url, false)}
198
- />
199
- </label>
200
-
201
- {backgroundImage.url && imageSettings}
202
- </div>
203
- );
204
- }
205
-
206
- _renderRowForLabel(label, i) {
207
- return (
208
- <tr key={i}>
209
- <td>
210
- <RangeInput
211
- value={label.coordinates}
212
- // eslint-disable-next-line react/jsx-no-bind
213
- onChange={this.onCoordinateChange.bind(this, i)}
214
- />
215
- </td>
216
- <td style={{verticalAlign: "bottom", width: "5px"}}>
217
- <input
218
- type="text"
219
- className="graph-settings-axis-label"
220
- value={label.content}
221
- // eslint-disable-next-line react/jsx-no-bind
222
- onChange={this.onContentChange.bind(this, i)}
223
- />
224
- </td>
225
- <td>
226
- <select
227
- className="perseus-widget-dropdown"
228
- value={label.alignment}
229
- // eslint-disable-next-line react/jsx-no-bind
230
- onChange={this.onAlignmentChange.bind(this, i)}
231
- >
232
- {captionAlignments.map(function (alignment, i) {
233
- return (
234
- <option key={"" + i} value={alignment}>
235
- {alignment}
236
- </option>
237
- );
238
- }, this)}
239
- </select>
240
- </td>
241
- <td>
242
- <a
243
- href="#"
244
- className="simple-button orange delete-label"
245
- title="Remove this label"
246
- // eslint-disable-next-line react/jsx-no-bind
247
- onClick={this.removeLabel.bind(this, i)}
248
- >
249
- <InlineIcon {...icons.iconTrash} />
250
- </a>
251
- </td>
252
- </tr>
253
- );
254
- }
255
-
256
- change(...args) {
257
- return Changeable.change.apply(this, args);
258
- }
259
-
260
- removeLabel(labelIndex, e) {
261
- e.preventDefault();
262
- const labels = [...this.props.labels];
263
- labels.splice(labelIndex, 1);
264
- this.props.onChange({labels: labels});
265
- }
266
-
267
- onCoordinateChange(labelIndex, newCoordinates) {
268
- const labels = this.props.labels.slice();
269
- labels[labelIndex] = _.extend({}, labels[labelIndex], {
270
- coordinates: newCoordinates,
271
- });
272
- this.props.onChange({labels: labels});
273
- }
274
-
275
- onContentChange(labelIndex, e) {
276
- const newContent = e.target.value;
277
- const labels = this.props.labels.slice();
278
- labels[labelIndex] = _.extend({}, labels[labelIndex], {
279
- content: newContent,
280
- });
281
- this.props.onChange({labels: labels});
282
- }
283
-
284
- onAlignmentChange(labelIndex, e) {
285
- const newAlignment = e.target.value;
286
- const labels = this.props.labels.slice();
287
- labels[labelIndex] = _.extend({}, labels[labelIndex], {
288
- alignment: newAlignment,
289
- });
290
- this.props.onChange({labels: labels});
291
- }
292
-
293
- setUrl(url, width, height, silent) {
294
- // Because this calls into WidgetEditor._handleWidgetChange, which
295
- // checks for this widget's ref to serialize it.
296
- //
297
- // Errors if you switch items before the `Image` from `onUrlChange`
298
- // loads.
299
- if (!this._isMounted) {
300
- return;
301
- }
302
-
303
- const image = _.clone(this.props.backgroundImage);
304
- image.url = url;
305
- image.width = width;
306
- image.height = height;
307
- const box = [image.width, image.height];
308
- this.props.onChange(
309
- {
310
- backgroundImage: image,
311
- box: box,
312
- },
313
- null,
314
- silent,
315
- );
316
- }
317
-
318
- // silently load the image when the component mounts
319
- // silently update url and sizes when the image loads
320
- // noisily load the image in response to the author changing it
321
- async onUrlChange(url: string | undefined | null, silent: boolean) {
322
- // Check if we've been passed something that looks like a URL
323
- if (!url) {
324
- this.setUrl(url, 0, 0, silent);
325
- return;
326
- }
327
-
328
- // All article content must be KA-owned!
329
- if (!INTERNALLY_HOSTED_URL_RE.test(url)) {
330
- this.setState({
331
- backgroundImageError:
332
- "Images must be from sites hosted by Khan Academy. " +
333
- "Please input a Khan Academy-owned address, or use the " +
334
- "Add Image tool to rehost an existing image",
335
- });
336
- return;
337
- }
338
-
339
- // Clear previous errors
340
- this.setState({backgroundImageError: ""});
341
-
342
- try {
343
- const size = await Util.getImageSizeModern(url);
344
- this.setUrl(url, size[0], size[1], true);
345
- } catch (error) {
346
- this.setState({
347
- backgroundImageError: `There was an error loading the image URL: ${JSON.stringify(
348
- error,
349
- null,
350
- 2,
351
- )}`,
352
- });
353
- }
354
- }
355
-
356
- onRangeChange(type, newRange) {
357
- const range = this.props.range.slice();
358
- range[type] = newRange;
359
- this.props.onChange({range: range});
360
- }
361
-
362
- serialize() {
363
- return EditorJsonify.serialize.call(this);
364
- }
365
- }
366
-
367
- export default ImageEditor;
@@ -1,288 +0,0 @@
1
- /* eslint-disable react/sort-comp */
2
- import {components, Util} from "@khanacademy/perseus";
3
- import * as React from "react";
4
- import _ from "underscore";
5
-
6
- import BlurInput from "../components/blur-input";
7
-
8
- import type {ParsedValue, InputNumber} from "@khanacademy/perseus";
9
- import type {PropsFor} from "@khanacademy/wonder-blocks-core";
10
-
11
- const {InfoTip} = components;
12
-
13
- const answerTypes = {
14
- number: {
15
- name: "Numbers",
16
- forms: "integer, decimal, proper, improper, mixed",
17
- },
18
- decimal: {
19
- name: "Decimals",
20
- forms: "decimal",
21
- },
22
- integer: {
23
- name: "Integers",
24
- forms: "integer",
25
- },
26
- rational: {
27
- name: "Fractions and mixed numbers",
28
- forms: "integer, proper, improper, mixed",
29
- },
30
- improper: {
31
- name: "Improper numbers (no mixed)",
32
- forms: "integer, proper, improper",
33
- },
34
- mixed: {
35
- name: "Mixed numbers (no improper)",
36
- forms: "integer, proper, mixed",
37
- },
38
- percent: {
39
- name: "Numbers or percents",
40
- forms: "integer, decimal, proper, improper, mixed, percent",
41
- },
42
- pi: {
43
- name: "Numbers with pi",
44
- forms: "pi",
45
- },
46
- } as const;
47
-
48
- type Props = {
49
- value: number;
50
- simplify: PropsFor<typeof InputNumber.widget>["simplify"];
51
- size: PropsFor<typeof InputNumber.widget>["size"];
52
- inexact: PropsFor<typeof InputNumber.widget>["reviewModeRubric"]["inexact"];
53
- maxError: PropsFor<
54
- typeof InputNumber.widget
55
- >["reviewModeRubric"]["maxError"];
56
- answerType: PropsFor<typeof InputNumber.widget>["answerType"];
57
- rightAlign: PropsFor<typeof InputNumber.widget>["rightAlign"];
58
- onChange: (arg1: {
59
- value?: ParsedValue | 0;
60
- simplify?: Props["simplify"];
61
- size?: Props["size"];
62
- inexact?: Props["inexact"];
63
- maxError?: Props["maxError"];
64
- answerType?: Props["answerType"];
65
- rightAlign?: Props["rightAlign"];
66
- }) => void;
67
- };
68
-
69
- type DefaultProps = {
70
- value: Props["value"];
71
- simplify: Props["simplify"];
72
- size: Props["size"];
73
- inexact: Props["inexact"];
74
- maxError: Props["maxError"];
75
- answerType: Props["answerType"];
76
- rightAlign: Props["rightAlign"];
77
- };
78
-
79
- class InputNumberEditor extends React.Component<Props> {
80
- static widgetName = "input-number" as const;
81
-
82
- static defaultProps: DefaultProps = {
83
- value: 0,
84
- simplify: "required",
85
- size: "normal",
86
- inexact: false,
87
- maxError: 0.1,
88
- answerType: "number",
89
- rightAlign: false,
90
- };
91
-
92
- input = React.createRef<BlurInput>();
93
-
94
- handleAnswerChange: (arg1: string) => void = (str) => {
95
- const value = Util.firstNumericalParse(str) || 0;
96
- this.props.onChange({value: value});
97
- };
98
-
99
- render(): React.ReactNode {
100
- const answerTypeOptions = _.map(
101
- answerTypes,
102
- function (v, k) {
103
- return (
104
- <option value={k} key={k}>
105
- {v.name}
106
- </option>
107
- );
108
- },
109
- this,
110
- );
111
-
112
- return (
113
- <div>
114
- <div>
115
- <label>
116
- Correct answer:{" "}
117
- <BlurInput
118
- value={"" + this.props.value}
119
- onChange={this.handleAnswerChange}
120
- ref={this.input}
121
- />
122
- </label>
123
- </div>
124
-
125
- <div>
126
- <label>
127
- Unsimplified answers{" "}
128
- <select
129
- value={this.props.simplify}
130
- onChange={(e) => {
131
- this.props.onChange({
132
- // @ts-expect-error - TS2322 - Type 'string' is not assignable to type '"optional" | "required" | "enforced" | undefined'.
133
- simplify: e.target.value,
134
- });
135
- }}
136
- >
137
- <option value="required">will not be graded</option>
138
- <option value="optional">will be accepted</option>
139
- <option value="enforced">
140
- will be marked wrong
141
- </option>
142
- </select>
143
- </label>
144
- <InfoTip>
145
- <p>
146
- Normally select "will not be graded". This will give
147
- the user a message saying the answer is correct but
148
- not simplified. The user will then have to simplify
149
- it and re-enter, but will not be penalized. (5th
150
- grade and anything after)
151
- </p>
152
- <p>
153
- Select "will be accepted" only if the user is not
154
- expected to know how to simplify fractions yet.
155
- (Anything prior to 5th grade)
156
- </p>
157
- <p>
158
- Select "will be marked wrong" only if we are
159
- specifically assessing the ability to simplify.
160
- </p>
161
- </InfoTip>
162
- </div>
163
-
164
- <div>
165
- <label>
166
- <input
167
- type="checkbox"
168
- checked={this.props.inexact}
169
- onChange={(e) => {
170
- this.props.onChange({
171
- inexact: e.target.checked,
172
- });
173
- }}
174
- />{" "}
175
- Allow inexact answers
176
- </label>
177
-
178
- <label>
179
- <input /* TODO(emily): don't use a hidden checkbox
180
- for alignment */
181
- type="checkbox"
182
- style={{visibility: "hidden"}}
183
- />
184
- Max error:{" "}
185
- <input
186
- type="text"
187
- disabled={!this.props.inexact}
188
- defaultValue={this.props.maxError}
189
- aria-label="Max error"
190
- onBlur={(e) => {
191
- const ans =
192
- "" +
193
- (Util.firstNumericalParse(e.target.value) ||
194
- 0);
195
- e.target.value = ans;
196
- this.props.onChange({maxError: ans});
197
- }}
198
- />
199
- </label>
200
- </div>
201
-
202
- <div>
203
- Answer type:{" "}
204
- <select
205
- value={this.props.answerType}
206
- onChange={(e) => {
207
- // @ts-expect-error - TS2322 - Type 'string' is not assignable to type '"number" | "integer" | "mixed" | "decimal" | "improper" | "percent" | "pi" | "rational" | undefined'.
208
- this.props.onChange({answerType: e.target.value});
209
- }}
210
- aria-label="Answer type"
211
- >
212
- {answerTypeOptions}
213
- </select>
214
- <InfoTip>
215
- <p>
216
- Use the default "Numbers" unless the answer must be
217
- in a specific form (e.g., question is about
218
- converting decimals to fractions).
219
- </p>
220
- </InfoTip>
221
- </div>
222
-
223
- <div>
224
- <label>
225
- Width{" "}
226
- <select
227
- value={this.props.size}
228
- onChange={(e) => {
229
- // @ts-expect-error - TS2322 - Type 'string' is not assignable to type '"small" | "normal" | undefined'.
230
- this.props.onChange({size: e.target.value});
231
- }}
232
- >
233
- <option value="normal">Normal (80px)</option>
234
- <option value="small">Small (40px)</option>
235
- </select>
236
- </label>
237
- <InfoTip>
238
- <p>
239
- Use size "Normal" for all text boxes, unless there
240
- are multiple text boxes in one line and the answer
241
- area is too narrow to fit them.
242
- </p>
243
- </InfoTip>
244
- </div>
245
-
246
- <div>
247
- <label>
248
- <input
249
- type="checkbox"
250
- checked={this.props.rightAlign}
251
- onChange={(e) => {
252
- this.props.onChange({
253
- rightAlign: e.target.checked,
254
- });
255
- }}
256
- />{" "}
257
- Right alignment
258
- </label>
259
- </div>
260
- </div>
261
- );
262
- }
263
-
264
- focus: () => boolean = () => {
265
- this.input.current?.focus();
266
- return true;
267
- };
268
-
269
- serialize: () => {
270
- value: Props["value"];
271
- simplify: Props["simplify"];
272
- size: Props["size"];
273
- inexact: Props["inexact"];
274
- maxError: Props["maxError"];
275
- answerType: Props["answerType"];
276
- rightAlign: Props["rightAlign"];
277
- } = () => ({
278
- value: this.props.value,
279
- simplify: this.props.simplify,
280
- size: this.props.size,
281
- inexact: this.props.inexact,
282
- maxError: this.props.maxError,
283
- answerType: this.props.answerType,
284
- rightAlign: this.props.rightAlign,
285
- });
286
- }
287
-
288
- export default InputNumberEditor;