@khanacademy/perseus 0.3.3 → 0.4.0

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 (60) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/es/index.js +8247 -5609
  3. package/dist/es/index.js.map +1 -1
  4. package/dist/index.js +17900 -15265
  5. package/dist/index.js.map +1 -1
  6. package/dist/strings.js +0 -1
  7. package/package.json +9 -8
  8. package/src/__tests__/__snapshots__/item-renderer_test.jsx.snap +2 -3
  9. package/src/__tests__/__snapshots__/server-item-renderer_test.jsx.snap +2 -3
  10. package/src/__tests__/renderer-api_test.jsx +3 -27
  11. package/src/components/__stories__/svg-image.stories.jsx +11 -0
  12. package/src/components/fixed-to-responsive.jsx +1 -0
  13. package/src/components/highlighting/ui/util.js +2 -2
  14. package/src/components/input-with-examples.jsx +3 -1
  15. package/src/components/svg-image.jsx +10 -7
  16. package/src/components/text-input.jsx +65 -16
  17. package/src/definition-context.js +59 -0
  18. package/src/index.js +5 -1
  19. package/src/interaction-tracker.js +2 -2
  20. package/src/item-renderer.jsx +1 -1
  21. package/src/mixins/provide-keypad.jsx +1 -1
  22. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +438 -523
  23. package/src/multi-items/__tests__/multi-renderer_test.js +9 -9
  24. package/src/renderer.jsx +15 -8
  25. package/src/server-item-renderer.jsx +1 -1
  26. package/src/types.js +3 -4
  27. package/src/util/scroll-utils.js +2 -2
  28. package/src/widgets/__stories__/definition.stories.jsx +44 -0
  29. package/src/widgets/__tests__/__snapshots__/categorizer_test.jsx.snap +76 -8
  30. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +6 -9
  31. package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +2 -3
  32. package/src/widgets/__tests__/__snapshots__/grapher_test.jsx.snap +19 -2
  33. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +338 -390
  34. package/src/widgets/__tests__/__snapshots__/image_test.jsx.snap +38 -4
  35. package/src/widgets/__tests__/__snapshots__/interactive-graph_test.jsx.snap +114 -12
  36. package/src/widgets/__tests__/__snapshots__/matrix_test.jsx.snap +120 -48
  37. package/src/widgets/__tests__/__snapshots__/numeric-input_test.jsx.snap +8 -12
  38. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +3615 -4517
  39. package/src/widgets/__tests__/graded-group-set-jipt_test.jsx +0 -1
  40. package/src/widgets/__tests__/grapher.cypress.jsx +236 -176
  41. package/src/widgets/__tests__/group_test.jsx +4 -4
  42. package/src/widgets/__tests__/interactive-graph.cypress.jsx +371 -291
  43. package/src/widgets/__tests__/radio_test.jsx +73 -114
  44. package/src/widgets/definition.jsx +34 -32
  45. package/src/widgets/interactive-graph.jsx +4 -4
  46. package/src/widgets/label-image/answer-choices.jsx +1 -1
  47. package/src/widgets/label-image.jsx +1 -1
  48. package/src/widgets/molecule/molecule-drawing.jsx +1 -1
  49. package/src/widgets/numeric-input.jsx +2 -15
  50. package/src/widgets/radio/base-radio.jsx +1 -1
  51. package/src/widgets/radio/choice-icon.jsx +11 -10
  52. package/src/widgets/radio/choice.jsx +164 -396
  53. package/src/widgets/radio/cross-out-button.jsx +3 -5
  54. package/src/widgets/radio/option-status.jsx +4 -5
  55. package/src/widgets/radio/__stories__/cross-out-menu.stories.jsx +0 -59
  56. package/src/widgets/radio/__stories__/cross-out-tooltip.stories.jsx +0 -59
  57. package/src/widgets/radio/__stories__/toggleable-radio-button.stories.jsx +0 -28
  58. package/src/widgets/radio/cross-out-menu-wrapper.jsx +0 -492
  59. package/src/widgets/radio/cross-out-tooltip.jsx +0 -129
  60. package/src/widgets/radio/toggleable-radio-button.jsx +0 -133
@@ -1,6 +1,6 @@
1
1
  // @flow
2
2
 
3
- import {screen, fireEvent} from "@testing-library/react";
3
+ import {act, screen, fireEvent} from "@testing-library/react";
4
4
  import userEvent from "@testing-library/user-event";
5
5
  import _ from "underscore";
6
6
  import "@testing-library/jest-dom"; // Imports custom mathers
@@ -24,23 +24,16 @@ import type {
24
24
  } from "../../perseus-types.js";
25
25
  import type {APIOptions} from "../../types.js";
26
26
 
27
- const waitForTooltip = () => {
28
- // We need to wait !!three!! render passes for the NewTooltip to be
29
- // fully rendered.
30
- jest.runOnlyPendingTimers();
31
- jest.runOnlyPendingTimers();
32
- jest.runOnlyPendingTimers();
33
- };
27
+ const selectOption = (index: number) => {
28
+ const options = screen.getAllByRole("checkbox");
34
29
 
35
- const answer = (index: number) => {
36
- const radios = screen.getAllByRole("radio");
37
- // userEvent.click() returns a very hard-to-understand error if passed an
38
30
  // element that is null/undefined (ie. if the index is invalid) so we
39
31
  // manually check and throw here to protect future me, and others :)
40
- if (index > radios.length) {
32
+ if (index > options.length) {
41
33
  throw new Error("Invalid array index for radio");
42
34
  }
43
- userEvent.click(radios[index]);
35
+
36
+ userEvent.click(options[index]);
44
37
  };
45
38
 
46
39
  describe("single-choice question", () => {
@@ -76,7 +69,7 @@ describe("single-choice question", () => {
76
69
  const {container} = renderQuestion(question, apiOptions);
77
70
 
78
71
  // Act
79
- answer(correct);
72
+ selectOption(correct);
80
73
 
81
74
  // Assert
82
75
  expect(container).toMatchSnapshot("correct answer");
@@ -87,7 +80,7 @@ describe("single-choice question", () => {
87
80
  const {container} = renderQuestion(question, apiOptions);
88
81
 
89
82
  // Act
90
- answer(incorrect[0]);
83
+ selectOption(incorrect[0]);
91
84
 
92
85
  // Assert
93
86
  expect(container).toMatchSnapshot("incorrect answer");
@@ -100,8 +93,7 @@ describe("single-choice question", () => {
100
93
  });
101
94
 
102
95
  // Act
103
- answer(correct);
104
-
96
+ selectOption(correct);
105
97
  // Assert
106
98
  expect(renderer).toHaveBeenAnsweredCorrectly();
107
99
  });
@@ -109,7 +101,8 @@ describe("single-choice question", () => {
109
101
  it("should accept the right answer (touch)", () => {
110
102
  // Arrange
111
103
  const {renderer} = renderQuestion(question, apiOptions);
112
- const correctRadio = screen.getAllByRole("radio")[correct];
104
+ const correctRadio =
105
+ screen.getAllByRole("checkbox")[correct];
113
106
 
114
107
  // Act
115
108
  fireEvent.touchStart(correctRadio);
@@ -131,7 +124,7 @@ describe("single-choice question", () => {
131
124
  const {renderer} = renderQuestion(question, apiOptions);
132
125
 
133
126
  // Act
134
- answer(incorrect);
127
+ selectOption(incorrect);
135
128
 
136
129
  // Assert
137
130
  expect(renderer).toHaveBeenAnsweredIncorrectly();
@@ -160,11 +153,11 @@ describe("single-choice question", () => {
160
153
  // Act
161
154
  // Since this is a single-select setup, just select the first
162
155
  // incorrect choice.
163
- answer(incorrect[0]);
156
+ selectOption(incorrect[0]);
164
157
  renderer.deselectIncorrectSelectedChoices();
165
158
 
166
159
  // Assert
167
- screen.getAllByRole("radio").forEach((r) => {
160
+ screen.getAllByRole("checkbox").forEach((r) => {
168
161
  expect(r).not.toBeChecked();
169
162
  });
170
163
  });
@@ -184,12 +177,12 @@ describe("single-choice question", () => {
184
177
  renderQuestion(staticQuestion, apiOptions, {reviewMode});
185
178
 
186
179
  // Act
187
- answer(correct);
180
+ selectOption(correct);
188
181
 
189
182
  // Assert
190
183
  // Everything's read-only so no selections made
191
- screen.getAllByRole("radio").forEach((r) => {
192
- expect(r).toBeDisabled();
184
+ screen.getAllByRole("checkbox").forEach((r) => {
185
+ expect(r).toHaveAttribute("aria-disabled", "true");
193
186
  });
194
187
  });
195
188
  },
@@ -200,10 +193,12 @@ describe("single-choice question", () => {
200
193
  renderQuestion(question, apiOptions);
201
194
 
202
195
  // Act
203
- userEvent.type(screen.getAllByRole("radio")[0], "{arrowdown}");
196
+ userEvent.tab();
197
+ expect(screen.getAllByRole("checkbox")[0]).toHaveFocus();
198
+ userEvent.tab();
204
199
 
205
200
  // Assert
206
- expect(screen.getAllByRole("radio")[1]).toHaveFocus();
201
+ expect(screen.getAllByRole("checkbox")[1]).toHaveFocus();
207
202
  });
208
203
 
209
204
  it("should be able to navigate up by keyboard", () => {
@@ -211,10 +206,14 @@ describe("single-choice question", () => {
211
206
  renderQuestion(question, apiOptions);
212
207
 
213
208
  // Act
214
- userEvent.type(screen.getAllByRole("radio")[0], "{arrowup}");
209
+ userEvent.tab();
210
+ expect(screen.getAllByRole("checkbox")[0]).toHaveFocus();
211
+ userEvent.tab();
212
+ expect(screen.getAllByRole("checkbox")[1]).toHaveFocus();
213
+ userEvent.tab({shift: true});
215
214
 
216
215
  // Assert
217
- expect(screen.getAllByRole("radio")[3]).toHaveFocus();
216
+ expect(screen.getAllByRole("checkbox")[0]).toHaveFocus();
218
217
  });
219
218
 
220
219
  it("should be able to navigate through 'None of the above' choice by keyboard", () => {
@@ -225,11 +224,12 @@ describe("single-choice question", () => {
225
224
  renderQuestion(q, apiOptions);
226
225
 
227
226
  // Act
228
- userEvent.type(screen.getAllByRole("radio")[0], "{arrowup}");
229
- userEvent.type(screen.getAllByRole("radio")[3], "{arrowup}");
227
+ userEvent.tab();
228
+ userEvent.tab();
229
+ userEvent.tab();
230
230
 
231
231
  // Assert
232
- expect(screen.getAllByRole("radio")[2]).toHaveFocus();
232
+ expect(screen.getAllByRole("checkbox")[2]).toHaveFocus();
233
233
  });
234
234
 
235
235
  it.each([
@@ -251,7 +251,7 @@ describe("single-choice question", () => {
251
251
  // We click on the first item, which was the second (index == 1)
252
252
  // item in the original choices. But because of enforced ordering,
253
253
  // it is now at the top of the list (and thus our correct answer).
254
- userEvent.click(screen.getAllByRole("radio")[0]);
254
+ userEvent.click(screen.getAllByRole("checkbox")[0]);
255
255
 
256
256
  // Assert
257
257
  const items = screen.getAllByRole("listitem");
@@ -316,7 +316,7 @@ describe("single-choice question", () => {
316
316
  const {renderer} = renderQuestion(question, apiOptions);
317
317
 
318
318
  // Act
319
- answer(incorrect[0]);
319
+ selectOption(incorrect[0]);
320
320
  renderer.showRationalesForCurrentlySelectedChoices();
321
321
 
322
322
  // Assert
@@ -347,8 +347,9 @@ describe("single-choice question", () => {
347
347
  ).toBeVisible();
348
348
  });
349
349
 
350
- it("should open the cross-out menu when button clicked", () => {
350
+ it("should open the cross-out menu when button clicked", async () => {
351
351
  // Arrange
352
+
352
353
  renderQuestion(question, crossOutApiOptions);
353
354
 
354
355
  // Act
@@ -357,7 +358,9 @@ describe("single-choice question", () => {
357
358
  name: /Open menu for Choice B/,
358
359
  }),
359
360
  );
360
- waitForTooltip();
361
+ await act(async () => {
362
+ await jest.runAllTimers();
363
+ });
361
364
 
362
365
  // Assert
363
366
  expect(
@@ -367,33 +370,39 @@ describe("single-choice question", () => {
367
370
  ).toBeVisible();
368
371
  });
369
372
 
370
- it("should open the cross-out menu when focused and spacebar pressed", () => {
373
+ it("should open the cross-out menu when focused and spacebar pressed", async () => {
371
374
  // Arrange
375
+
372
376
  renderQuestion(question, crossOutApiOptions);
373
377
  userEvent.tab(); // Choice icon
374
378
  userEvent.tab(); // Cross-out menu ellipsis
375
379
 
376
380
  // Act
377
381
  userEvent.keyboard("{space}");
378
- waitForTooltip();
382
+ await act(async () => {
383
+ await jest.runAllTimers();
384
+ });
379
385
 
380
386
  // Assert
381
387
  expect(
382
388
  screen.getByRole("button", {
383
389
  name: /Cross out Choice A/,
384
390
  }),
385
- ).toHaveFocus();
391
+ ).toBeVisible();
386
392
  });
387
393
 
388
- it("should cross-out selection and dismiss button when clicked", () => {
394
+ it("should cross-out selection and dismiss button when clicked", async () => {
389
395
  // Arrange
396
+
390
397
  renderQuestion(question, crossOutApiOptions);
391
398
  userEvent.click(
392
399
  screen.getByRole("button", {
393
400
  name: /Open menu for Choice B/,
394
401
  }),
395
402
  );
396
- waitForTooltip();
403
+ await act(async () => {
404
+ await jest.runAllTimers();
405
+ });
397
406
 
398
407
  // Act
399
408
  userEvent.click(
@@ -401,7 +410,7 @@ describe("single-choice question", () => {
401
410
  name: /Cross out Choice B/,
402
411
  }),
403
412
  );
404
- jest.runOnlyPendingTimers();
413
+ jest.runAllTimers();
405
414
 
406
415
  // Assert
407
416
  expect(
@@ -409,84 +418,34 @@ describe("single-choice question", () => {
409
418
  name: /Cross out Choice B/,
410
419
  }),
411
420
  ).toHaveLength(0);
412
-
413
- // The choice widget maintains a "preview" of what the opposite
414
- // state is for use in the "Cross-out button", but it's lazy in
415
- // updating the preview (only changing it when the button is
416
- // revealed). So once we cross-out a choice, we have two elements
417
- // in the DOM with the `Cross out` text. :shrug:
418
- expect(
419
- screen.queryAllByText("(Choice B, Crossed out)"),
420
- ).toHaveLength(2);
421
421
  });
422
422
 
423
- it("should dismiss cross-out button with {tab} key", () => {
423
+ it("should dismiss cross-out button with {tab} key", async () => {
424
424
  // Arrange
425
- renderQuestion(question, crossOutApiOptions);
426
- userEvent.tab(); // Choice icon
427
- userEvent.tab(); // Cross-out menu ellipsis
428
-
429
- // Open
430
- userEvent.keyboard("{space}");
431
- waitForTooltip();
432
-
433
- expect(
434
- screen.queryAllByRole("button", {
435
- name: /Cross out Choice A/,
436
- }),
437
- ).toHaveLength(1);
438
-
439
- // Act
440
- userEvent.tab();
441
-
442
- // Assert
443
- expect(
444
- screen.queryAllByRole("button", {
445
- name: /Cross out Choice B/,
446
- }),
447
- ).toHaveLength(0);
448
- });
449
425
 
450
- it("should dismiss cross-out button with {Escape} key", () => {
451
- // Arrange
452
426
  renderQuestion(question, crossOutApiOptions);
453
-
454
427
  userEvent.tab(); // Choice icon
455
428
  userEvent.tab(); // Cross-out menu ellipsis
456
- userEvent.keyboard("{space}"); // Open tooltip
457
-
458
- // Global escape handler only set up after a clock tick
459
- jest.runOnlyPendingTimers();
460
429
 
461
430
  // Act
462
- userEvent.keyboard("{esc}");
431
+ userEvent.keyboard("{space}");
432
+ await act(async () => {
433
+ await jest.runAllTimers();
434
+ });
463
435
 
464
- // Assert
465
436
  expect(
466
- screen.queryAllByRole("button", {
467
- name: /Cross out Choice B/,
437
+ screen.getByRole("button", {
438
+ name: /Cross out Choice A/,
468
439
  }),
469
- ).toHaveLength(0);
470
- });
471
-
472
- it("should dismiss cross-out button with click outside tooltip", () => {
473
- // Arrange
474
- renderQuestion(question, crossOutApiOptions);
475
-
476
- userEvent.tab(); // Choice icon
477
- userEvent.tab(); // Cross-out menu ellipsis
478
- userEvent.keyboard("{space}"); // Open tooltip
479
-
480
- // Global escape handler only set up after a clock tick
481
- jest.runOnlyPendingTimers();
440
+ ).toBeVisible();
482
441
 
483
- // Act
484
- userEvent.click((document.body: any));
442
+ userEvent.keyboard("{space}");
443
+ jest.runAllTimers();
485
444
 
486
445
  // Assert
487
446
  expect(
488
447
  screen.queryAllByRole("button", {
489
- name: /Cross out Choice B/,
448
+ name: /Cross out Choice A/,
490
449
  }),
491
450
  ).toHaveLength(0);
492
451
  });
@@ -514,7 +473,7 @@ describe("single-choice question", () => {
514
473
  renderQuestion(question, apiOptions, {reviewMode: true});
515
474
 
516
475
  // Act
517
- answer(correct);
476
+ selectOption(correct);
518
477
 
519
478
  // Assert
520
479
  expect(screen.getAllByText("Correct (selected)")).toHaveLength(1);
@@ -528,7 +487,7 @@ describe("single-choice question", () => {
528
487
  renderQuestion(question, apiOptions, {reviewMode: true});
529
488
 
530
489
  // Act
531
- answer(incorrect[0]);
490
+ selectOption(incorrect[0]);
532
491
 
533
492
  // Assert
534
493
  expect(screen.getAllByText("Incorrect (selected)")).toHaveLength(1);
@@ -554,8 +513,8 @@ describe("multi-choice question", () => {
554
513
  const {renderer} = renderQuestion(question, apiOptions);
555
514
 
556
515
  // Act
557
- const boxes = screen.getAllByRole("checkbox");
558
- correct.forEach((i) => userEvent.click(boxes[i]));
516
+ const options = screen.getAllByRole("checkbox");
517
+ correct.forEach((i) => userEvent.click(options[i]));
559
518
 
560
519
  // Assert
561
520
  expect(renderer).toHaveBeenAnsweredCorrectly();
@@ -566,8 +525,8 @@ describe("multi-choice question", () => {
566
525
  const {container} = renderQuestion(question, apiOptions);
567
526
 
568
527
  // Act
569
- const boxes = screen.getAllByRole("checkbox");
570
- incorrect[0].forEach((i) => userEvent.click(boxes[i]));
528
+ const options = screen.getAllByRole("checkbox");
529
+ incorrect[0].forEach((i) => userEvent.click(options[i]));
571
530
 
572
531
  // Assert
573
532
  expect(container).toMatchSnapshot("invalid state");
@@ -610,8 +569,8 @@ describe("multi-choice question", () => {
610
569
  );
611
570
 
612
571
  // Act
613
- const boxes = screen.getAllByRole("checkbox");
614
- userEvent.click(boxes[3]); // incorrect
572
+ const option = screen.getAllByRole("checkbox");
573
+ userEvent.click(option[3]); // incorrect
615
574
 
616
575
  // Assert
617
576
  expect(renderer).toHaveInvalidInput(
@@ -626,8 +585,8 @@ describe("multi-choice question", () => {
626
585
  const {renderer} = renderQuestion(question, apiOptions);
627
586
 
628
587
  // Act
629
- const boxes = screen.getAllByRole("checkbox");
630
- choices.forEach((i) => userEvent.click(boxes[i]));
588
+ const option = screen.getAllByRole("checkbox");
589
+ choices.forEach((i) => userEvent.click(option[i]));
631
590
 
632
591
  // Assert
633
592
  expect(renderer).toHaveBeenAnsweredIncorrectly();
@@ -641,8 +600,8 @@ describe("multi-choice question", () => {
641
600
  const {renderer} = renderQuestion(question, apiOptions);
642
601
 
643
602
  // Act
644
- const boxes = screen.getAllByRole("checkbox");
645
- choices.forEach((i) => userEvent.click(boxes[i]));
603
+ const option = screen.getAllByRole("checkbox");
604
+ choices.forEach((i) => userEvent.click(option[i]));
646
605
 
647
606
  // Assert
648
607
  expect(renderer).toHaveInvalidInput();
@@ -3,9 +3,9 @@
3
3
  import Button from "@khanacademy/wonder-blocks-button";
4
4
  import Color from "@khanacademy/wonder-blocks-color";
5
5
  import {Popover, PopoverContentCore} from "@khanacademy/wonder-blocks-popover";
6
- import Spacing from "@khanacademy/wonder-blocks-spacing";
7
6
  import * as React from "react";
8
7
 
8
+ import {DefinitionConsumer} from "../definition-context.js";
9
9
  import Renderer from "../renderer.jsx";
10
10
 
11
11
  import type {
@@ -50,37 +50,41 @@ class Definition extends React.Component<DefinitionProps> {
50
50
 
51
51
  render(): React.Node {
52
52
  return (
53
- <Popover
54
- content={
55
- <PopoverContentCore
56
- color="white"
57
- style={styles.tooltipBody}
58
- closeButtonVisible={true}
53
+ <DefinitionConsumer>
54
+ {({activeDefinitionId, setActiveDefinitionId}) => (
55
+ <Popover
56
+ content={
57
+ <PopoverContentCore
58
+ color="white"
59
+ style={styles.tooltipBody}
60
+ closeButtonVisible={true}
61
+ >
62
+ <Renderer
63
+ apiOptions={this.props.apiOptions}
64
+ content={this.props.definition}
65
+ widgets={this.props.widgets}
66
+ />
67
+ </PopoverContentCore>
68
+ }
69
+ opened={activeDefinitionId === this.props.widgetId}
70
+ onClose={() => setActiveDefinitionId(null)}
71
+ placement="top"
59
72
  >
60
- <Renderer
61
- apiOptions={this.props.apiOptions}
62
- content={this.props.definition}
63
- widgets={this.props.widgets}
64
- />
65
- </PopoverContentCore>
66
- }
67
- placement="top"
68
- >
69
- {({open}) => (
70
- <span className="perseus-widget-definition">
71
- <Button
72
- size="small"
73
- kind="tertiary"
74
- onClick={() => {
75
- this.props.trackInteraction();
76
- open();
77
- }}
78
- >
79
- {this.props.togglePrompt}
80
- </Button>
81
- </span>
73
+ <span className="perseus-widget-definition">
74
+ <Button
75
+ size="small"
76
+ kind="tertiary"
77
+ onClick={() => {
78
+ this.props.trackInteraction();
79
+ setActiveDefinitionId(this.props.widgetId);
80
+ }}
81
+ >
82
+ {this.props.togglePrompt}
83
+ </Button>
84
+ </span>
85
+ </Popover>
82
86
  )}
83
- </Popover>
87
+ </DefinitionConsumer>
84
88
  );
85
89
  }
86
90
  }
@@ -88,11 +92,9 @@ class Definition extends React.Component<DefinitionProps> {
88
92
  const styles = {
89
93
  tooltipBody: {
90
94
  color: Color.offBlack,
91
- display: "block",
92
95
  fontSize: 20,
93
96
  fontWeight: 500,
94
97
  lineHeight: "30px",
95
- margin: Spacing.xSmall_8,
96
98
  },
97
99
  };
98
100
 
@@ -392,7 +392,7 @@ class InteractiveGraph extends React.Component<Props, State> {
392
392
  pointA: ?$FlowFixMe;
393
393
  pointB: ?$FlowFixMe;
394
394
  pointC: ?$FlowFixMe;
395
- // eslint-disable-next-line flowtype/no-mutable-array
395
+ // eslint-disable-next-line ft-flow/no-mutable-array
396
396
  points: Array<$FlowFixMe>;
397
397
  polygon: ?$FlowFixMe;
398
398
  shouldResetGraphie: boolean;
@@ -1356,7 +1356,7 @@ class InteractiveGraph extends React.Component<Props, State> {
1356
1356
  return true;
1357
1357
  }
1358
1358
 
1359
- // eslint-disable-next-line flowtype/no-mutable-array
1359
+ // eslint-disable-next-line ft-flow/no-mutable-array
1360
1360
  const coords: Array<Coord> = _.invoke(this.points, "coord");
1361
1361
  coords[i] = coord;
1362
1362
 
@@ -2686,10 +2686,10 @@ class InteractiveGraph extends React.Component<Props, State> {
2686
2686
  rubric.correct.type === "polygon" &&
2687
2687
  state.coords != null
2688
2688
  ) {
2689
- const guess: Array<Coord> = state.coords?.slice(); // eslint-disable-line flowtype/no-mutable-array
2689
+ const guess: Array<Coord> = state.coords?.slice(); // eslint-disable-line ft-flow/no-mutable-array
2690
2690
  // $FlowFixMe[incompatible-type]
2691
2691
  // $FlowFixMe[prop-missing]
2692
- const correct: Array<Coord> = rubric.correct.coords?.slice(); // eslint-disable-line flowtype/no-mutable-array
2692
+ const correct: Array<Coord> = rubric.correct.coords?.slice(); // eslint-disable-line ft-flow/no-mutable-array
2693
2693
 
2694
2694
  let match;
2695
2695
  if (rubric.correct.match === "similar") {
@@ -185,7 +185,7 @@ export default class AnswerChoices extends React.Component<
185
185
  AnswerChoicesState,
186
186
  > {
187
187
  // The rendered answers elements.
188
- // eslint-disable-next-line flowtype/no-mutable-array
188
+ // eslint-disable-next-line ft-flow/no-mutable-array
189
189
  _choices: Array<?AnswerChoice>;
190
190
 
191
191
  constructor(props: AnswerChoicesProps) {
@@ -85,7 +85,7 @@ type LabelImageState = {|
85
85
 
86
86
  class LabelImage extends React.Component<LabelImageProps, LabelImageState> {
87
87
  // The rendered markers on the question image for labeling.
88
- // eslint-disable-next-line flowtype/no-mutable-array
88
+ // eslint-disable-next-line ft-flow/no-mutable-array
89
89
  _markers: Array<?Marker>;
90
90
 
91
91
  // The popup component containing the answer choices.
@@ -159,7 +159,7 @@ function compareElements(item0, item1) {
159
159
  /**
160
160
  * Draw an array of rendering instructions into a 2D canvas drawing context.
161
161
  */
162
- // eslint-disable-next-line flowtype/no-mutable-array
162
+ // eslint-disable-next-line ft-flow/no-mutable-array
163
163
  function draw(ctx: $FlowFixMe, items: Array<$FlowFixMe>) {
164
164
  items.sort(compareElements).forEach(drawItem(ctx));
165
165
  }
@@ -10,7 +10,7 @@ import _ from "underscore";
10
10
  import InputWithExamples from "../components/input-with-examples.jsx";
11
11
  import PossibleAnswers from "../components/possible-answers.jsx";
12
12
  import SimpleKeypadInput from "../components/simple-keypad-input.jsx";
13
- import {ApiOptions, ClassNames as ApiClassNames} from "../perseus-api.jsx";
13
+ import {ApiOptions} from "../perseus-api.jsx";
14
14
  import TexWrangler from "../tex-wrangler.js";
15
15
  import KhanAnswerTypes from "../util/answer-types.js";
16
16
  import KhanMath from "../util/math.js";
@@ -292,18 +292,6 @@ export class NumericInput extends React.Component<Props, State> {
292
292
  return [answerBlurb, !!correct];
293
293
  };
294
294
 
295
- getClasses: (boolean, $FlowFixMe) => $FlowFixMe = (correct, rubric) => {
296
- const classes = {};
297
- classes["perseus-input-size-" + this.props.size] = true;
298
- classes["perseus-input-right-align"] = this.props.rightAlign;
299
- classes[ApiClassNames.CORRECT] =
300
- rubric && correct && this.props.currentValue;
301
- classes[ApiClassNames.INCORRECT] =
302
- rubric && !correct && this.props.currentValue;
303
- classes[ApiClassNames.UNANSWERED] = rubric && !this.props.currentValue;
304
- return classes;
305
- };
306
-
307
295
  // TODO(Nicole, Jeremy): This is maybe never used and should be removed
308
296
  examples: () => $ReadOnlyArray<string> = () => {
309
297
  // if the set of specified forms are empty, allow all forms
@@ -421,8 +409,7 @@ export class NumericInput extends React.Component<Props, State> {
421
409
  const rubric = this.props.reviewModeRubric;
422
410
  const answers = this.getAnswerBlurb(rubric);
423
411
  const answerBlurb = answers[0];
424
- const correct = answers[1];
425
- const classes = this.getClasses(correct, rubric);
412
+ const classes = [];
426
413
 
427
414
  let labelText = this.props.labelText;
428
415
  if (labelText == null || labelText === "") {
@@ -464,7 +464,7 @@ class BaseRadio extends React.Component<$FlowFixMe, $FlowFixMe> {
464
464
  {shouldShowInstructions && (
465
465
  <div className={instructionsClassName}>{instructions}</div>
466
466
  )}
467
- <ul className={className}>
467
+ <ul className={className} style={{listStyle: "none"}}>
468
468
  {this.props.choices.map(function (choice, i) {
469
469
  let Element = Choice;
470
470
  const elementProps = {