@khanacademy/perseus 1.1.1 → 1.2.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 (48) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/es/index.css +2 -2
  3. package/dist/es/index.js +14997 -15079
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.css +2 -2
  6. package/dist/index.js +15033 -15113
  7. package/dist/index.js.map +1 -1
  8. package/dist/strings.js +1 -0
  9. package/package.json +7 -7
  10. package/src/__stories__/hints-renderer.stories.jsx +52 -0
  11. package/src/__tests__/__snapshots__/renderer_test.jsx.snap +21 -3
  12. package/src/components/highlighting/ui/highlight-tooltip.jsx +29 -23
  13. package/src/components/highlighting/ui/selection-tracker.jsx +17 -28
  14. package/src/hints-renderer.jsx +13 -4
  15. package/src/mixins/__tests__/widget-prop-blacklist_test.js +23 -0
  16. package/src/mixins/widget-prop-blacklist.js +15 -1
  17. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +30 -10
  18. package/src/styles/articles.less +0 -2
  19. package/src/styles/perseus-renderer.less +1 -1
  20. package/src/styles/widgets/passage.less +1 -0
  21. package/src/types.js +1 -1
  22. package/src/widgets/__tests__/__snapshots__/dropdown_test.jsx.snap +16 -2
  23. package/src/widgets/__tests__/__snapshots__/graded-group-set-jipt_test.jsx.snap +3 -3
  24. package/src/widgets/__tests__/__snapshots__/graded-group-set_test.jsx.snap +7 -1
  25. package/src/widgets/__tests__/__snapshots__/graded-group_test.jsx.snap +2 -2
  26. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +30 -10
  27. package/src/widgets/__tests__/__snapshots__/passage-ref_test.jsx.snap +3 -3
  28. package/src/widgets/__tests__/__snapshots__/passage_test.jsx.snap +6 -6
  29. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +300 -218
  30. package/src/widgets/__tests__/radio/base-radio_test.jsx +90 -6
  31. package/src/widgets/__tests__/radio/choice-icon_test.jsx +74 -0
  32. package/src/widgets/__tests__/radio/choice_test.jsx +39 -30
  33. package/src/widgets/__tests__/radio_test.jsx +228 -22
  34. package/src/widgets/graded-group-set.jsx +8 -1
  35. package/src/widgets/graded-group.jsx +2 -1
  36. package/src/widgets/passage-ref.jsx +21 -5
  37. package/src/widgets/passage.jsx +3 -1
  38. package/src/widgets/radio/__stories__/base-radio.stories.jsx +7 -3
  39. package/src/widgets/radio/base-radio.jsx +3 -1
  40. package/src/widgets/radio/choice-icon/cross-out-line.jsx +3 -2
  41. package/src/widgets/radio/choice-icon/library-choice-icon.jsx +17 -6
  42. package/src/widgets/radio/choice.jsx +2 -1
  43. package/src/components/new-tooltip/README.md +0 -3
  44. package/src/components/new-tooltip/__stories__/new-tooltip.stories.jsx +0 -665
  45. package/src/components/new-tooltip/__tests__/style-constants_test.js +0 -8
  46. package/src/components/new-tooltip/new-tooltip.jsx +0 -155
  47. package/src/components/new-tooltip/style-constants.js +0 -13
  48. package/src/components/new-tooltip/types.js +0 -183
@@ -180,14 +180,19 @@ describe("single-choice question", () => {
180
180
 
181
181
  // Assert
182
182
  // Everything's read-only so no selections made
183
- // Note(TB): The visual buttons are hidden from screen readers
184
- // so they need to be identified as hidden
185
- // The visual button has the aria attributes
183
+ // Note(TB): The visual buttons are hidden from screen
184
+ // readers, so they need to be identified as hidden;
185
+ // the visual button has the aria attributes
186
186
  screen
187
187
  .getAllByRole("button", {hidden: true})
188
188
  .forEach((r) => {
189
189
  expect(r).toHaveAttribute("aria-disabled", "true");
190
190
  });
191
+ // Note(TB): Confirms the screen reader only
192
+ // radio options are also disabled
193
+ screen.getAllByRole("radio").forEach((r) => {
194
+ expect(r).toHaveAttribute("disabled");
195
+ });
191
196
  });
192
197
  },
193
198
  );
@@ -198,15 +203,12 @@ describe("single-choice question", () => {
198
203
 
199
204
  // Act
200
205
  userEvent.tab();
201
- // Note(TB): The visual buttons are hidden from screen readers
202
- // so they need to be identified as hidden;
203
- // cannot access screen reader buttons via tabbing
204
- expect(
205
- screen.getAllByRole("button", {hidden: true})[0],
206
- ).toHaveFocus();
207
206
  userEvent.tab();
208
207
 
209
208
  // Assert
209
+ // Note(TB): The visual buttons are hidden from screen readers
210
+ // so they need to be identified as hidden;
211
+ // cannot access screen reader buttons via tabbing
210
212
  expect(
211
213
  screen.getAllByRole("button", {hidden: true})[1],
212
214
  ).toHaveFocus();
@@ -237,7 +239,19 @@ describe("single-choice question", () => {
237
239
  ).toHaveFocus();
238
240
  });
239
241
 
240
- it("should be able to navigate through 'None of the above' choice by keyboard", () => {
242
+ it("should be able to select an option by keyboard", () => {
243
+ // Arrange
244
+ renderQuestion(question, apiOptions);
245
+
246
+ // Act
247
+ userEvent.tab();
248
+ userEvent.keyboard("{space}");
249
+
250
+ // Assert
251
+ expect(screen.getAllByRole("radio")[0]).toBeChecked();
252
+ });
253
+
254
+ it("should be able to navigate to 'None of the above' choice by keyboard", () => {
241
255
  // Arrange
242
256
  const q = clone(question);
243
257
  ((q.widgets["radio 1"]
@@ -336,7 +350,7 @@ describe("single-choice question", () => {
336
350
  expect(passageRefRadio).toHaveTextContent("lines NaN–NaN");
337
351
  });
338
352
 
339
- it("Should render option statuses (rationales) for selected choices", () => {
353
+ it("should render rationales for selected choices", () => {
340
354
  // Arrange
341
355
  const {renderer} = renderQuestion(question, apiOptions);
342
356
 
@@ -359,9 +373,7 @@ describe("single-choice question", () => {
359
373
  };
360
374
 
361
375
  it("should render cross-out menu button", () => {
362
- // Arrange
363
-
364
- // Act
376
+ // Arrange & Act
365
377
  renderQuestion(question, crossOutApiOptions);
366
378
 
367
379
  // Assert
@@ -374,7 +386,6 @@ describe("single-choice question", () => {
374
386
 
375
387
  it("should open the cross-out menu when button clicked", async () => {
376
388
  // Arrange
377
-
378
389
  renderQuestion(question, crossOutApiOptions);
379
390
 
380
391
  // Act
@@ -397,7 +408,6 @@ describe("single-choice question", () => {
397
408
 
398
409
  it("should open the cross-out menu when focused and spacebar pressed", async () => {
399
410
  // Arrange
400
-
401
411
  renderQuestion(question, crossOutApiOptions);
402
412
  userEvent.tab(); // Choice icon
403
413
  userEvent.tab(); // Cross-out menu ellipsis
@@ -416,7 +426,7 @@ describe("single-choice question", () => {
416
426
  ).toBeVisible();
417
427
  });
418
428
 
419
- it("should cross-out selection and dismiss button when clicked", async () => {
429
+ it("should cross-out selection and dismiss button when clicked", () => {
420
430
  // Arrange
421
431
  renderQuestion(question, crossOutApiOptions);
422
432
  userEvent.click(
@@ -424,9 +434,6 @@ describe("single-choice question", () => {
424
434
  name: /Open menu for Choice B/,
425
435
  }),
426
436
  );
427
- await act(async () => {
428
- await jest.runAllTimers();
429
- });
430
437
 
431
438
  // Act
432
439
  userEvent.click(
@@ -434,7 +441,6 @@ describe("single-choice question", () => {
434
441
  name: /Cross out Choice B/,
435
442
  }),
436
443
  );
437
- jest.runAllTimers();
438
444
 
439
445
  // Assert
440
446
  expect(
@@ -442,11 +448,43 @@ describe("single-choice question", () => {
442
448
  name: /Cross out Choice B/,
443
449
  }),
444
450
  ).toHaveLength(0);
451
+
452
+ expect(
453
+ screen.getByTestId("choice-icon__cross-out-line"),
454
+ ).toBeVisible();
445
455
  });
446
456
 
447
- it("should dismiss cross-out button with {tab} key", async () => {
457
+ it("should remove cross-out line on selection", async () => {
448
458
  // Arrange
459
+ renderQuestion(question, crossOutApiOptions);
460
+ userEvent.click(
461
+ screen.getByRole("button", {
462
+ name: /Open menu for Choice B/,
463
+ }),
464
+ );
465
+
466
+ // Act
467
+ userEvent.click(
468
+ screen.getByRole("button", {
469
+ name: /Cross out Choice B/,
470
+ }),
471
+ );
472
+ jest.runAllTimers();
473
+
474
+ userEvent.click(
475
+ screen.getByRole("radio", {
476
+ name: "(Choice B, Crossed out) -8",
477
+ }),
478
+ );
479
+
480
+ // Assert
481
+ expect(
482
+ screen.queryByTestId("choice-icon__cross-out-line"),
483
+ ).not.toBeInTheDocument();
484
+ });
449
485
 
486
+ it("should dismiss cross-out button with {tab} key", async () => {
487
+ // Arrange
450
488
  renderQuestion(question, crossOutApiOptions);
451
489
  userEvent.tab(); // Choice icon
452
490
  userEvent.tab(); // Cross-out menu ellipsis
@@ -476,6 +514,19 @@ describe("single-choice question", () => {
476
514
  });
477
515
  });
478
516
 
517
+ it("should not display instructions when satStyling true", () => {
518
+ // Arrange
519
+ const apiOptions: APIOptions = {
520
+ satStyling: true,
521
+ };
522
+
523
+ // Act
524
+ renderQuestion(question, apiOptions);
525
+
526
+ // Assert
527
+ expect(screen.queryByText("Choose 1 answer:")).not.toBeInTheDocument();
528
+ });
529
+
479
530
  it("should be invalid when first rendered", () => {
480
531
  // Arrange
481
532
  const apiOptions: APIOptions = {
@@ -516,6 +567,60 @@ describe("single-choice question", () => {
516
567
  // Assert
517
568
  expect(screen.getAllByText("Incorrect (selected)")).toHaveLength(1);
518
569
  });
570
+
571
+ it("should display all rationales when static is true", () => {
572
+ // Arrange
573
+ const staticQuestion = {
574
+ ...question,
575
+ widgets: {
576
+ ...question.widgets,
577
+ "radio 1": {
578
+ ...question.widgets["radio 1"],
579
+ static: true,
580
+ },
581
+ },
582
+ };
583
+
584
+ // Act
585
+ renderQuestion(staticQuestion);
586
+
587
+ // Assert
588
+ // Part of Choice A rationale
589
+ expect(
590
+ screen.getByText(
591
+ /the positive square root when performed on a number, so/,
592
+ ),
593
+ ).toBeVisible();
594
+ // Part of Choice B rationale
595
+ expect(screen.getByText(/, the square root operation/)).toBeVisible();
596
+ // Part of Choice C rationale
597
+ expect(
598
+ screen.getByText(/is the positive square root of/),
599
+ ).toBeVisible();
600
+ // Part of Choice D rationale
601
+ expect(screen.getAllByText(/satisfies the equation./)[3]).toBeVisible();
602
+ });
603
+
604
+ it("should register as correct when none of the above option selected", () => {
605
+ // Arrange
606
+ const q = clone(question);
607
+ const choices = ((q.widgets["radio 1"]
608
+ .options: any): PerseusRadioWidgetOptions).choices;
609
+ choices[2].correct = false;
610
+ choices[3].isNoneOfTheAbove = true;
611
+ choices[3].correct = true;
612
+
613
+ const {renderer} = renderQuestion(q);
614
+
615
+ // Act
616
+ const noneOption = screen.getByRole("radio", {
617
+ name: "(Choice D) None of the above",
618
+ });
619
+ userEvent.click(noneOption);
620
+
621
+ // Assert
622
+ expect(renderer).toHaveBeenAnsweredCorrectly();
623
+ });
519
624
  });
520
625
 
521
626
  describe("multi-choice question", () => {
@@ -544,6 +649,94 @@ describe("multi-choice question", () => {
544
649
  expect(renderer).toHaveBeenAnsweredCorrectly();
545
650
  });
546
651
 
652
+ it("should select multiple options when clicked", () => {
653
+ // Arrange
654
+ const apiOptions: APIOptions = {
655
+ crossOutEnabled: false,
656
+ };
657
+ const radio1Widget = ((question.widgets["radio 1"]: any): RadioWidget);
658
+ const radioOptions = radio1Widget.options;
659
+
660
+ const multipleCorrectChoicesQuestion: PerseusRenderer = {
661
+ ...question,
662
+ widgets: {
663
+ ...question.widgets,
664
+ "radio 1": ({
665
+ ...radio1Widget,
666
+ options: {
667
+ ...radioOptions,
668
+ choices: [
669
+ {content: "$x=-6$", correct: true},
670
+ {content: "$x=4$", correct: true},
671
+ {content: "$x=7$", correct: false},
672
+ {
673
+ content: "There is no such input value.",
674
+ isNoneOfTheAbove: true,
675
+ correct: false,
676
+ },
677
+ ],
678
+ },
679
+ }: RadioWidget),
680
+ },
681
+ };
682
+
683
+ renderQuestion(multipleCorrectChoicesQuestion, apiOptions);
684
+
685
+ // Act
686
+ const options = screen.getAllByRole("checkbox");
687
+ userEvent.click(options[2]);
688
+ userEvent.click(options[3]);
689
+
690
+ // Assert
691
+ expect(options[2]).toBeChecked();
692
+ expect(options[3]).toBeChecked();
693
+ });
694
+
695
+ it("should deselect selected options when clicked", () => {
696
+ // Arrange
697
+ const apiOptions: APIOptions = {
698
+ crossOutEnabled: false,
699
+ };
700
+ const radio1Widget = ((question.widgets["radio 1"]: any): RadioWidget);
701
+ const radioOptions = radio1Widget.options;
702
+
703
+ const multipleCorrectChoicesQuestion: PerseusRenderer = {
704
+ ...question,
705
+ widgets: {
706
+ ...question.widgets,
707
+ "radio 1": ({
708
+ ...radio1Widget,
709
+ options: {
710
+ ...radioOptions,
711
+ choices: [
712
+ {content: "$x=-6$", correct: true},
713
+ {content: "$x=4$", correct: true},
714
+ {content: "$x=7$", correct: false},
715
+ {
716
+ content: "There is no such input value.",
717
+ isNoneOfTheAbove: true,
718
+ correct: false,
719
+ },
720
+ ],
721
+ },
722
+ }: RadioWidget),
723
+ },
724
+ };
725
+
726
+ renderQuestion(multipleCorrectChoicesQuestion, apiOptions);
727
+
728
+ // Act
729
+ const options = screen.getAllByRole("checkbox");
730
+ userEvent.click(options[2]);
731
+ userEvent.click(options[3]);
732
+ userEvent.click(options[2]);
733
+ userEvent.click(options[3]);
734
+
735
+ // Assert
736
+ expect(options[2]).not.toBeChecked();
737
+ expect(options[3]).not.toBeChecked();
738
+ });
739
+
547
740
  it("should snapshot the same when invalid", () => {
548
741
  // Arrange
549
742
  const {container} = renderQuestion(question, apiOptions);
@@ -556,6 +749,19 @@ describe("multi-choice question", () => {
556
749
  expect(container).toMatchSnapshot("invalid state");
557
750
  });
558
751
 
752
+ it("should be invalid when first rendered", () => {
753
+ // Arrange
754
+ const apiOptions: APIOptions = {
755
+ crossOutEnabled: false,
756
+ };
757
+
758
+ // Act
759
+ const {renderer} = renderQuestion(question, apiOptions);
760
+
761
+ // Assert
762
+ expect(renderer).toHaveInvalidInput();
763
+ });
764
+
559
765
  it("should be invalid when incorrect number of choices selected", () => {
560
766
  // Arrange
561
767
  const apiOptions: APIOptions = {
@@ -14,6 +14,7 @@ import {
14
14
  phoneMargin,
15
15
  negativePhoneMargin,
16
16
  } from "../styles/constants.js";
17
+ import a11y from "../util/a11y.js";
17
18
 
18
19
  import GradedGroupWidget from "./graded-group.jsx";
19
20
 
@@ -51,7 +52,13 @@ class Indicators extends React.Component<IndicatorsProps> {
51
52
  styles.selectedIndicator,
52
53
  )}
53
54
  onClick={() => this.props.onChangeCurrentGroup(i)}
54
- />,
55
+ >
56
+ {i === this.props.currentGroup && (
57
+ <span className={css(a11y.srOnly)}>
58
+ {i18n._("Current")}
59
+ </span>
60
+ )}
61
+ </div>,
55
62
  );
56
63
  }
57
64
  return <div className={css(styles.indicatorContainer)}>{items}</div>;
@@ -13,6 +13,7 @@ import * as Changeable from "../mixins/changeable.jsx";
13
13
  import {ApiOptions} from "../perseus-api.jsx";
14
14
  import Renderer from "../renderer.jsx";
15
15
  import {
16
+ gray68,
16
17
  gray76,
17
18
  phoneMargin,
18
19
  negativePhoneMargin,
@@ -433,7 +434,7 @@ const styles = StyleSheet.create({
433
434
 
434
435
  title: {
435
436
  fontSize: 12,
436
- color: gray76,
437
+ color: gray68,
437
438
  textTransform: "uppercase",
438
439
  marginBottom: 11,
439
440
  letterSpacing: 0.8,
@@ -5,20 +5,31 @@ import * as React from "react";
5
5
  import _ from "underscore";
6
6
 
7
7
  import * as Changeable from "../mixins/changeable.jsx";
8
- import WidgetJsonifyDeprecated from "../mixins/widget-jsonify-deprecated.jsx";
8
+ import {removeDenylistProps} from "../mixins/widget-prop-blacklist.js";
9
9
  import PerseusMarkdown from "../perseus-markdown.jsx";
10
10
 
11
+ import PassageWidgetExport from "./passage.jsx";
12
+
11
13
  import type {PerseusPassageRefWidgetOptions} from "../perseus-types.js";
12
14
  import type {
13
15
  ChangeFn,
14
16
  PerseusScore,
15
17
  WidgetExports,
16
18
  WidgetProps,
19
+ LinterContextProps,
17
20
  } from "../types.js";
21
+ import type {Reference} from "./passage.jsx";
18
22
 
19
23
  const EN_DASH = "\u2013";
20
24
 
21
- type UserInput = $FlowFixMe;
25
+ type UserInput = {|
26
+ ...RenderProps,
27
+ static: ?boolean,
28
+ reviewModeRubric: Rubric,
29
+ linterContext: LinterContextProps,
30
+ isLastUsedWidget: boolean,
31
+ alignment: ?string,
32
+ |};
22
33
 
23
34
  type RenderProps = {|
24
35
  passageNumber: PerseusPassageRefWidgetOptions["passageNumber"],
@@ -64,8 +75,10 @@ class PassageRef extends React.Component<Props, State> {
64
75
  );
65
76
  }
66
77
 
78
+ // TODO(TB): getUserInput needs to be updated to only return
79
+ // props input by the user. Currently returns all the widget's props.
67
80
  getUserInput: () => UserInput = () => {
68
- return WidgetJsonifyDeprecated.getUserInput.call(this);
81
+ return removeDenylistProps(this.props);
69
82
  };
70
83
 
71
84
  render(): React.Node {
@@ -144,11 +157,14 @@ class PassageRef extends React.Component<Props, State> {
144
157
  };
145
158
 
146
159
  _updateRange: () => void = () => {
147
- const passage = this.props.findWidgets(
160
+ // Note(TB): findWidgets runs findInternal and findExternal;
161
+ // findExternal runs findInternal for the renderers involved;
162
+ // findInternal returns type $ReadOnlyArray<?Widget>
163
+ const passage: ?PassageWidgetExport.widget = this.props.findWidgets(
148
164
  "passage " + this.props.passageNumber,
149
165
  )[0];
150
166
 
151
- let refInfo = null;
167
+ let refInfo: ?Reference = null;
152
168
  if (passage) {
153
169
  refInfo = passage.getReference(this.props.referenceNumber);
154
170
  }
@@ -84,6 +84,8 @@ const styles = StyleSheet.create({
84
84
  width: "100%",
85
85
  top: 0,
86
86
  left: 0,
87
+ // keep from blocking text selection
88
+ zIndex: -1,
87
89
  },
88
90
  });
89
91
 
@@ -124,7 +126,7 @@ type PassageState = {|
124
126
  |};
125
127
 
126
128
  // Information about a passage reference, used in inter-widgets.
127
- type Reference = {
129
+ export type Reference = {
128
130
  startLine: number,
129
131
  endLine: number,
130
132
  content: ?string,
@@ -68,17 +68,21 @@ export const Interactive = (args: StoryArgs): React.Node => {
68
68
  return <BaseRadio {...overwrittenProps} />;
69
69
  };
70
70
 
71
- export const DefaultSingleSelect = (args: StoryArgs): React.Node => {
71
+ export const SingleSelectWithNothingSelected = (
72
+ args: StoryArgs,
73
+ ): React.Node => {
72
74
  const overwrittenProps = {...defaultProps, multipleSelect: false};
73
75
  return <BaseRadio {...overwrittenProps} />;
74
76
  };
75
77
 
76
- export const DefaultMultipleSelect = (args: StoryArgs): React.Node => {
78
+ export const MultipleSelectWithNothingSelected = (
79
+ args: StoryArgs,
80
+ ): React.Node => {
77
81
  const overwrittenProps = {...defaultProps, multipleSelect: true};
78
82
  return <BaseRadio {...overwrittenProps} />;
79
83
  };
80
84
 
81
- export const DefaultMultipleSelectCountChoices = (
85
+ export const MultipleSelectWithCountChoicesLabel = (
82
86
  args: StoryArgs,
83
87
  ): React.Node => {
84
88
  const overwrittenProps = {
@@ -247,7 +247,9 @@ function BaseRadio(props: Props): React.Node {
247
247
  <fieldset
248
248
  className={`perseus-widget-radio-fieldset ${responsiveClassName}`}
249
249
  >
250
- <legend className="perseus-sr-only">{instructions}</legend>
250
+ {shouldShowInstructions && (
251
+ <legend className="perseus-sr-only">{instructions}</legend>
252
+ )}
251
253
  {shouldShowInstructions && (
252
254
  <div className={instructionsClassName} aria-hidden="true">
253
255
  {instructions}
@@ -26,6 +26,7 @@ function CrossOutLine(props: {color: string, sat?: boolean, ...}): React.Node {
26
26
  height={crossOutLineSize}
27
27
  viewBox={`0 0 ${crossOutLineSize} ${crossOutLineSize}`}
28
28
  className={css(styles.crossOutLine)}
29
+ data-test-id="choice-icon__cross-out-line"
29
30
  >
30
31
  <line
31
32
  // The line stretches from the bottom-left to top-right.
@@ -47,8 +48,8 @@ const styles = StyleSheet.create({
47
48
  crossOutLine: {
48
49
  // Center the icon within the container.
49
50
  position: "absolute",
50
- top: `calc(50% - ${CROSS_OUT_LINE_SIZE / 2}px)`,
51
- left: `calc(50% - ${CROSS_OUT_LINE_SIZE / 2}px)`,
51
+ top: `0px`,
52
+ left: `0px`,
52
53
  },
53
54
  });
54
55
 
@@ -153,11 +153,13 @@ function LibraryChoiceIcon(props: LibraryChoiceIconProps): React.Node {
153
153
  // radio icon
154
154
  data-is-radio-icon={true}
155
155
  >
156
- <ChoiceInner
157
- letter={letter}
158
- showCorrectness={showCorrectness}
159
- correct={correct}
160
- />
156
+ <div className={css(styles.innerWrapper)}>
157
+ <ChoiceInner
158
+ letter={letter}
159
+ showCorrectness={showCorrectness}
160
+ correct={correct}
161
+ />
162
+ </div>
161
163
  </div>
162
164
  </FocusRing>
163
165
  {crossedOut && <CrossOutLine color={dynamicStyles.borderColor} />}
@@ -166,6 +168,15 @@ function LibraryChoiceIcon(props: LibraryChoiceIconProps): React.Node {
166
168
  }
167
169
 
168
170
  const styles = StyleSheet.create({
171
+ innerWrapper: {
172
+ width: LIBRARY_ICON_SIZE,
173
+ height: LIBRARY_ICON_SIZE,
174
+
175
+ // Center the contents of the icon.
176
+ display: "flex",
177
+ alignItems: "center",
178
+ justifyContent: "center",
179
+ },
169
180
  circle: {
170
181
  // Make the circle
171
182
  width: LIBRARY_ICON_SIZE,
@@ -182,7 +193,7 @@ const styles = StyleSheet.create({
182
193
  fontWeight: "bold",
183
194
  fontSize: 12,
184
195
 
185
- // Center the contents of the icon.
196
+ // Center the icon wrapper.
186
197
  display: "flex",
187
198
  alignItems: "center",
188
199
  justifyContent: "center",
@@ -241,6 +241,7 @@ function Choice(props: ChoicePropsWithForwardRef): React.Node {
241
241
  alignContent: "center",
242
242
  paddingTop: Spacing.xSmall_8,
243
243
  paddingBottom: Spacing.xSmall_8,
244
+ paddingLeft: Spacing.xSmall_8,
244
245
  }}
245
246
  >
246
247
  <ChoiceIcon
@@ -412,7 +413,7 @@ const styles = StyleSheet.create({
412
413
  nonSatRationale: {
413
414
  padding: intermediateCheckboxPadding,
414
415
  paddingTop: 0,
415
- marginLeft: 44,
416
+ marginLeft: 54,
416
417
  [mediaQueries.smOrSmaller]: {
417
418
  padding: intermediateCheckboxPaddingPhone,
418
419
  paddingTop: 0,
@@ -1,3 +0,0 @@
1
- # `NewTooltip`: KA tooltip component
2
-
3
- This is a wrapper around [Wonderblock's popover component](https://github.com/Khan/wonder-blocks). It is API compatiable with Perseus and remains for compatiability and legacy reasons. If you are anyone but the Perseus group at Khan Academy please use the wonderblocks component directly.