@khanacademy/perseus 0.4.3 → 1.1.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 (71) hide show
  1. package/.eslintrc.js +6 -1
  2. package/CHANGELOG.md +45 -0
  3. package/dist/es/index.css +15 -8
  4. package/dist/es/index.js +1214 -1404
  5. package/dist/es/index.js.map +1 -1
  6. package/dist/index.css +15 -8
  7. package/dist/index.js +1217 -1411
  8. package/dist/index.js.map +1 -1
  9. package/dist/strings.js +0 -4
  10. package/package.json +9 -8
  11. package/src/components/number-input.jsx +2 -2
  12. package/src/components/simple-keypad-input.jsx +18 -21
  13. package/src/index.js +1 -1
  14. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +426 -135
  15. package/src/multi-items/__tests__/multi-renderer_test.js +9 -9
  16. package/src/perseus-api.jsx +10 -36
  17. package/src/perseus-types.js +18 -145
  18. package/src/styles/constants.js +0 -2
  19. package/src/styles/perseus-renderer.less +8 -2
  20. package/src/styles/variables.less +0 -2
  21. package/src/styles/widgets/passage.less +9 -5
  22. package/src/types.js +18 -35
  23. package/src/util/answer-types.js +2 -3
  24. package/src/util/math.js +3 -10
  25. package/src/util.js +0 -6
  26. package/src/version.js +1 -1
  27. package/src/widgets/__stories__/numeric-input.stories.jsx +43 -2
  28. package/src/widgets/__testdata__/base-radio_testdata.js +22 -0
  29. package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +4 -4
  30. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +291 -130
  31. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +3293 -1415
  32. package/src/widgets/__tests__/group_test.jsx +4 -4
  33. package/src/widgets/__tests__/numeric-input_test.jsx +12 -8
  34. package/src/widgets/__tests__/passage_test.jsx +81 -0
  35. package/src/widgets/__tests__/radio/base-radio_test.jsx +291 -41
  36. package/src/widgets/__tests__/radio/choice-icon_test.jsx +67 -0
  37. package/src/widgets/__tests__/radio/choice_test.jsx +302 -0
  38. package/src/widgets/__tests__/radio/focus-ring_test.jsx +23 -0
  39. package/src/widgets/__tests__/radio/option-status_test.jsx +65 -0
  40. package/src/widgets/__tests__/radio_test.jsx +39 -15
  41. package/src/widgets/definition.jsx +1 -1
  42. package/src/widgets/group.jsx +84 -102
  43. package/src/widgets/input-number.jsx +6 -1
  44. package/src/widgets/numeric-input.jsx +87 -80
  45. package/src/widgets/passage/__tests__/passage-markdown_test.jsx +0 -9
  46. package/src/widgets/passage/get-line-height-for-node.js +4 -5
  47. package/src/widgets/passage/passage-markdown.jsx +176 -120
  48. package/src/widgets/passage.jsx +48 -46
  49. package/src/widgets/radio/__stories__/base-radio.stories.jsx +164 -17
  50. package/src/widgets/radio/__stories__/choice-icon.stories.jsx +202 -111
  51. package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +21 -6
  52. package/src/widgets/radio/__stories__/choice.stories.jsx +138 -0
  53. package/src/widgets/radio/__stories__/focus-ring.stories.jsx +29 -23
  54. package/src/widgets/radio/__stories__/option-status.stories.jsx +93 -99
  55. package/src/widgets/radio/base-radio.jsx +485 -556
  56. package/src/widgets/radio/choice-a11y-text.js +62 -0
  57. package/src/widgets/radio/choice-icon/choice-icon.jsx +87 -0
  58. package/src/widgets/radio/choice-icon/cross-out-line.jsx +55 -0
  59. package/src/widgets/radio/choice-icon/library-choice-icon.jsx +214 -0
  60. package/src/widgets/radio/choice-icon/sat-choice-icon.jsx +120 -0
  61. package/src/widgets/radio/choice-icon/shared-styles.js +17 -0
  62. package/src/widgets/radio/choice-none-above.jsx +49 -40
  63. package/src/widgets/radio/choice.jsx +319 -347
  64. package/src/widgets/radio/choice.jsx.fixture.js +0 -18
  65. package/src/widgets/radio/focus-ring.jsx +29 -22
  66. package/src/widgets/radio/option-status.jsx +58 -36
  67. package/src/widgets/radio/widget.jsx +181 -136
  68. package/src/widgets/radio.jsx +16 -17
  69. package/src/widgets/radio/__stories__/cross-out-button.stories.jsx +0 -64
  70. package/src/widgets/radio/choice-icon.jsx +0 -469
  71. package/src/widgets/radio/cross-out-button.jsx +0 -134
@@ -3,35 +3,25 @@ export const instances: $ReadOnlyArray<mixed> = [
3
3
  ({}: {...}),
4
4
  {
5
5
  content: "Plain Content",
6
- goToPrevChoice: () => {},
7
- goToNextChoice: () => {},
8
6
  },
9
7
  {
10
8
  content: "Correct Content (Review)",
11
9
  correct: true,
12
10
  reviewMode: true,
13
- goToPrevChoice: () => {},
14
- goToNextChoice: () => {},
15
11
  },
16
12
  {
17
13
  content: "Incorrect Content (Review)",
18
14
  correct: false,
19
15
  reviewMode: true,
20
- goToPrevChoice: () => {},
21
- goToNextChoice: () => {},
22
16
  },
23
17
  {
24
18
  content: "Content (Review, correctness not specified)",
25
19
  reviewMode: true,
26
- goToPrevChoice: () => {},
27
- goToNextChoice: () => {},
28
20
  },
29
21
  {
30
22
  content: "Content with Rationale",
31
23
  rationale: "This is some additional information",
32
24
  showRationale: true,
33
- goToPrevChoice: () => {},
34
- goToNextChoice: () => {},
35
25
  },
36
26
  {
37
27
  content: "Correct Content with Rationale (Review)",
@@ -39,8 +29,6 @@ export const instances: $ReadOnlyArray<mixed> = [
39
29
  rationale: "This is some additional information",
40
30
  reviewMode: true,
41
31
  showRationale: true,
42
- goToPrevChoice: () => {},
43
- goToNextChoice: () => {},
44
32
  },
45
33
  {
46
34
  content: "Incorrect Content with Rationale (Review)",
@@ -48,17 +36,11 @@ export const instances: $ReadOnlyArray<mixed> = [
48
36
  rationale: "This is some additional information",
49
37
  reviewMode: true,
50
38
  showRationale: true,
51
- goToPrevChoice: () => {},
52
- goToNextChoice: () => {},
53
39
  },
54
40
  {
55
41
  content: "Content with Rationale (Review, correctness not specified)",
56
42
  rationale: "This is some additional information",
57
43
  reviewMode: true,
58
44
  showRationale: true,
59
- goToPrevChoice: () => {},
60
- goToNextChoice: () => {},
61
45
  },
62
46
  ];
63
-
64
- export const log = ["goToPrevChoice", "goToNextChoice"];
@@ -8,40 +8,47 @@ import * as React from "react";
8
8
 
9
9
  import * as styleConstants from "../../styles/constants.js";
10
10
 
11
- class FocusRing extends React.Component<{
12
- children?: any,
11
+ type Props = {|
12
+ children?: React.Node,
13
13
  // Whether the focus ring is visible. Allows for positioning
14
14
  // the child identically regardless of whether the ring is visible.
15
15
  visible: boolean,
16
16
  // Color of the focus ring
17
17
  color: string,
18
- ...
19
- }> {
20
- static defaultProps: {|color: string, visible: boolean|} = {
21
- visible: true,
22
- color: styleConstants.kaGreen,
23
- };
18
+ // Whether a user can select multiple options or not
19
+ multipleSelect: boolean,
20
+ |};
21
+
22
+ function FocusRing(props: Props): React.Node {
23
+ const {visible, color, children, multipleSelect} = props;
24
24
 
25
- render(): React.Element<"span"> {
26
- const borderColor = this.props.visible
27
- ? this.props.color
28
- : "transparent";
29
- const style = {
30
- borderColor: borderColor,
31
- };
32
- return (
33
- <span className={css(styles.ring)} style={style}>
34
- {this.props.children}
35
- </span>
36
- );
37
- }
25
+ const borderColor = visible ? color : "transparent";
26
+ const borderRadius = multipleSelect ? 5 : "50%";
27
+ const style = {
28
+ borderColor,
29
+ borderRadius,
30
+ };
31
+ return (
32
+ <span
33
+ data-test-id="focus-ring"
34
+ className={css(styles.ring)}
35
+ style={style}
36
+ >
37
+ {children}
38
+ </span>
39
+ );
38
40
  }
39
41
 
42
+ FocusRing.defaultProps = {
43
+ visible: true,
44
+ color: styleConstants.kaGreen,
45
+ multipleSelect: false,
46
+ };
47
+
40
48
  const styles = StyleSheet.create({
41
49
  ring: {
42
50
  margin: "auto",
43
51
  display: "inline-block",
44
- borderRadius: "50%",
45
52
  borderWidth: 2,
46
53
  padding: 2,
47
54
  borderStyle: "solid",
@@ -12,7 +12,7 @@ import * as i18n from "@khanacademy/wonder-blocks-i18n";
12
12
  import {StyleSheet, css} from "aphrodite";
13
13
  import * as React from "react";
14
14
 
15
- class OptionStatus extends React.Component<{
15
+ type OptionStatusProps = {|
16
16
  // Was this option the correct answer?
17
17
  correct: boolean,
18
18
  // Did the user select this option as the answer?
@@ -21,49 +21,71 @@ class OptionStatus extends React.Component<{
21
21
  crossedOut: boolean,
22
22
  // Did the user select this option as the answer earlier?
23
23
  previouslyAnswered: boolean,
24
- ...
25
- }> {
26
- _renderText(): string {
27
- const {checked, correct, crossedOut} = this.props;
28
- if (correct) {
29
- // For correct answers, we surface checked _or_ crossedOut state,
30
- // because any interaction with the correct answer is noteworthy!
31
- if (checked) {
32
- return i18n._("Correct (selected)");
33
- }
34
- if (crossedOut) {
35
- return i18n._("Correct (but you crossed it out)");
36
- }
37
- return i18n._("Correct");
38
- }
39
- // But, for incorrect answers, we only surface checked state,
40
- // because crossing out an incorrect answer is not noteworthy.
24
+ reviewMode: boolean,
25
+ satStyling?: boolean,
26
+ |};
27
+
28
+ function renderText(
29
+ checked: boolean,
30
+ correct: boolean,
31
+ crossedOut: boolean,
32
+ ): string {
33
+ if (correct) {
34
+ // For correct answers, we surface checked _or_ crossedOut state,
35
+ // because any interaction with the correct answer is noteworthy!
41
36
  if (checked) {
42
- return i18n._("Incorrect (selected)");
37
+ return i18n._("Correct (selected)");
38
+ }
39
+ if (crossedOut) {
40
+ return i18n._("Correct (but you crossed it out)");
43
41
  }
44
- return i18n._("Incorrect");
42
+ return i18n._("Correct");
43
+ }
44
+ // But, for incorrect answers, we only surface checked state,
45
+ // because crossing out an incorrect answer is not noteworthy.
46
+ if (checked) {
47
+ return i18n._("Incorrect (selected)");
45
48
  }
49
+ return i18n._("Incorrect");
50
+ }
51
+
52
+ function OptionStatus(props: OptionStatusProps): React.Node {
53
+ const {
54
+ checked,
55
+ correct,
56
+ crossedOut,
57
+ previouslyAnswered,
58
+ reviewMode,
59
+ satStyling,
60
+ } = props;
46
61
 
47
- render(): React.Element<"div"> {
48
- const {checked, correct, previouslyAnswered} = this.props;
62
+ // Option status is exclued for SAT
63
+ if (satStyling) {
64
+ return null;
65
+ }
49
66
 
50
- let textStyle;
51
- if (correct) {
52
- textStyle = styles.correct;
67
+ // Option status is shown only in review mode, or for incorrectly
68
+ // answered items.
69
+ if (!reviewMode && !previouslyAnswered) {
70
+ return null;
71
+ }
72
+
73
+ let textStyle;
74
+ if (correct) {
75
+ textStyle = styles.correct;
76
+ } else {
77
+ if (checked || previouslyAnswered) {
78
+ textStyle = styles.incorrectAnswered;
53
79
  } else {
54
- if (checked || previouslyAnswered) {
55
- textStyle = styles.incorrectAnswered;
56
- } else {
57
- textStyle = styles.incorrect;
58
- }
80
+ textStyle = styles.incorrect;
59
81
  }
60
-
61
- return (
62
- <div className={css(styles.text, textStyle)}>
63
- {this._renderText()}
64
- </div>
65
- );
66
82
  }
83
+
84
+ return (
85
+ <div className={css(styles.text, textStyle)}>
86
+ {renderText(checked, correct, crossedOut)}
87
+ </div>
88
+ );
67
89
  }
68
90
 
69
91
  const styles = StyleSheet.create({