@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.
- package/.eslintrc.js +6 -1
- package/CHANGELOG.md +45 -0
- package/dist/es/index.css +15 -8
- package/dist/es/index.js +1214 -1404
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +15 -8
- package/dist/index.js +1217 -1411
- package/dist/index.js.map +1 -1
- package/dist/strings.js +0 -4
- package/package.json +9 -8
- package/src/components/number-input.jsx +2 -2
- package/src/components/simple-keypad-input.jsx +18 -21
- package/src/index.js +1 -1
- package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +426 -135
- package/src/multi-items/__tests__/multi-renderer_test.js +9 -9
- package/src/perseus-api.jsx +10 -36
- package/src/perseus-types.js +18 -145
- package/src/styles/constants.js +0 -2
- package/src/styles/perseus-renderer.less +8 -2
- package/src/styles/variables.less +0 -2
- package/src/styles/widgets/passage.less +9 -5
- package/src/types.js +18 -35
- package/src/util/answer-types.js +2 -3
- package/src/util/math.js +3 -10
- package/src/util.js +0 -6
- package/src/version.js +1 -1
- package/src/widgets/__stories__/numeric-input.stories.jsx +43 -2
- package/src/widgets/__testdata__/base-radio_testdata.js +22 -0
- package/src/widgets/__tests__/__snapshots__/definition_test.js.snap +4 -4
- package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +291 -130
- package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +3293 -1415
- package/src/widgets/__tests__/group_test.jsx +4 -4
- package/src/widgets/__tests__/numeric-input_test.jsx +12 -8
- package/src/widgets/__tests__/passage_test.jsx +81 -0
- package/src/widgets/__tests__/radio/base-radio_test.jsx +291 -41
- package/src/widgets/__tests__/radio/choice-icon_test.jsx +67 -0
- package/src/widgets/__tests__/radio/choice_test.jsx +302 -0
- package/src/widgets/__tests__/radio/focus-ring_test.jsx +23 -0
- package/src/widgets/__tests__/radio/option-status_test.jsx +65 -0
- package/src/widgets/__tests__/radio_test.jsx +39 -15
- package/src/widgets/definition.jsx +1 -1
- package/src/widgets/group.jsx +84 -102
- package/src/widgets/input-number.jsx +6 -1
- package/src/widgets/numeric-input.jsx +87 -80
- package/src/widgets/passage/__tests__/passage-markdown_test.jsx +0 -9
- package/src/widgets/passage/get-line-height-for-node.js +4 -5
- package/src/widgets/passage/passage-markdown.jsx +176 -120
- package/src/widgets/passage.jsx +48 -46
- package/src/widgets/radio/__stories__/base-radio.stories.jsx +164 -17
- package/src/widgets/radio/__stories__/choice-icon.stories.jsx +202 -111
- package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +21 -6
- package/src/widgets/radio/__stories__/choice.stories.jsx +138 -0
- package/src/widgets/radio/__stories__/focus-ring.stories.jsx +29 -23
- package/src/widgets/radio/__stories__/option-status.stories.jsx +93 -99
- package/src/widgets/radio/base-radio.jsx +485 -556
- package/src/widgets/radio/choice-a11y-text.js +62 -0
- package/src/widgets/radio/choice-icon/choice-icon.jsx +87 -0
- package/src/widgets/radio/choice-icon/cross-out-line.jsx +55 -0
- package/src/widgets/radio/choice-icon/library-choice-icon.jsx +214 -0
- package/src/widgets/radio/choice-icon/sat-choice-icon.jsx +120 -0
- package/src/widgets/radio/choice-icon/shared-styles.js +17 -0
- package/src/widgets/radio/choice-none-above.jsx +49 -40
- package/src/widgets/radio/choice.jsx +319 -347
- package/src/widgets/radio/choice.jsx.fixture.js +0 -18
- package/src/widgets/radio/focus-ring.jsx +29 -22
- package/src/widgets/radio/option-status.jsx +58 -36
- package/src/widgets/radio/widget.jsx +181 -136
- package/src/widgets/radio.jsx +16 -17
- package/src/widgets/radio/__stories__/cross-out-button.stories.jsx +0 -64
- package/src/widgets/radio/choice-icon.jsx +0 -469
- package/src/widgets/radio/cross-out-button.jsx +0 -134
|
@@ -38,15 +38,19 @@
|
|
|
38
38
|
line-height: @line-height - 2;
|
|
39
39
|
}
|
|
40
40
|
.passage-title div.paragraph {
|
|
41
|
-
font-
|
|
41
|
+
font-family: "Noto Serif", serif;
|
|
42
42
|
font-weight: 700;
|
|
43
|
+
font-size: 20px;
|
|
44
|
+
line-height: 22px;
|
|
45
|
+
|
|
43
46
|
margin: 0 0 10px;
|
|
44
47
|
}
|
|
45
48
|
.passage-text div.paragraph {
|
|
46
|
-
font-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
font-family: "Noto Serif", serif;
|
|
50
|
+
font-weight: 400;
|
|
51
|
+
font-size: 20px;
|
|
52
|
+
line-height: 24px;
|
|
53
|
+
|
|
50
54
|
span {
|
|
51
55
|
text-indent: 0;
|
|
52
56
|
}
|
package/src/types.js
CHANGED
|
@@ -9,7 +9,7 @@ import * as React from "react";
|
|
|
9
9
|
import type {SerializedHighlightSet} from "./components/highlighting/types.js";
|
|
10
10
|
import type {ILogger} from "./logging/log.js";
|
|
11
11
|
import type {Item} from "./multi-items/item-types.js";
|
|
12
|
-
import type {PerseusWidget} from "./perseus-types.js";
|
|
12
|
+
import type {PerseusWidget, PerseusRadioChoice} from "./perseus-types.js";
|
|
13
13
|
import type {SizeClass} from "./util/sizing-utils.js";
|
|
14
14
|
import type {Result} from "@khanacademy/wonder-blocks-data";
|
|
15
15
|
|
|
@@ -75,6 +75,22 @@ export type Version = {|
|
|
|
75
75
|
|
|
76
76
|
export type EditorMode = "edit" | "preview" | "json";
|
|
77
77
|
|
|
78
|
+
export type ChoiceState = {|
|
|
79
|
+
selected: boolean,
|
|
80
|
+
crossedOut: boolean,
|
|
81
|
+
highlighted: boolean,
|
|
82
|
+
rationaleShown: boolean,
|
|
83
|
+
correctnessShown: boolean,
|
|
84
|
+
previouslyAnswered: boolean,
|
|
85
|
+
readOnly: boolean,
|
|
86
|
+
|};
|
|
87
|
+
|
|
88
|
+
export type RadioChoiceWithMetadata = {|
|
|
89
|
+
...PerseusRadioChoice,
|
|
90
|
+
originalIndex: number,
|
|
91
|
+
correct: boolean,
|
|
92
|
+
|};
|
|
93
|
+
|
|
78
94
|
export type ChangeHandler = (
|
|
79
95
|
{|
|
|
80
96
|
hints?: $ReadOnlyArray<Hint>,
|
|
@@ -97,7 +113,7 @@ export type ChangeHandler = (
|
|
|
97
113
|
value?: $FlowFixMe,
|
|
98
114
|
|
|
99
115
|
// widgets/radio/widget.jsx
|
|
100
|
-
choiceStates?: $ReadOnlyArray
|
|
116
|
+
choiceStates?: $ReadOnlyArray<ChoiceState>,
|
|
101
117
|
|
|
102
118
|
// widgets/numeric-input.jsx
|
|
103
119
|
currentValue?: string,
|
|
@@ -230,39 +246,6 @@ export type APIOptions = $ReadOnly<{|
|
|
|
230
246
|
// values. For now, though, you must either specify all fields
|
|
231
247
|
// of `styling`, or omit the `styling` option entirely.
|
|
232
248
|
styling?: {|
|
|
233
|
-
// Which version of radio widget styles to use in non-SAT
|
|
234
|
-
// contexts.
|
|
235
|
-
//
|
|
236
|
-
// "legacy" was the version of the widget display after XOM but
|
|
237
|
-
// before we started adding MCR styles. It doesn't have support
|
|
238
|
-
// for rationales. It has since been removed.
|
|
239
|
-
//
|
|
240
|
-
// "intermediate" is a design which adds several new additions
|
|
241
|
-
// to the "legacy" styles such as:
|
|
242
|
-
// 1. Using the XOM "desktop" styles (with a visible check icon
|
|
243
|
-
// and lines in between choices) on mobile devices.
|
|
244
|
-
// 2. Designs for rationales
|
|
245
|
-
// 3. Using the single-select styles for multi-select styles
|
|
246
|
-
//
|
|
247
|
-
// "final" is a design which will build off of the
|
|
248
|
-
// "intermediate" designs and adds some improved designs as well
|
|
249
|
-
// as:
|
|
250
|
-
// 1. a/b/c/d/etc. letters inside of the prompt check box
|
|
251
|
-
// 2. New iconography and styles to indicate choice correctness
|
|
252
|
-
//
|
|
253
|
-
// The "legacy" and "intermediate" designs will be A/B tested
|
|
254
|
-
// against each other to ensure that its changes don't cause
|
|
255
|
-
// problems due to the new designs. Once the "intermediate"
|
|
256
|
-
// designs are finished, they will be A/B tested against the
|
|
257
|
-
// "final" designs.
|
|
258
|
-
//
|
|
259
|
-
// If no flag is provided, "legacy" styles will be shown.
|
|
260
|
-
//
|
|
261
|
-
// TODO(emily): Remove this by Aug 1, 2017, at which point all
|
|
262
|
-
// callsites should have been switched to using the "final"
|
|
263
|
-
// designs.
|
|
264
|
-
radioStyleVersion?: "intermediate" | "final",
|
|
265
|
-
|
|
266
249
|
// TODO(Nicole): Remove this after landing wonderblock-ifying
|
|
267
250
|
// the radio widget
|
|
268
251
|
primaryProductColor?: string,
|
package/src/util/answer-types.js
CHANGED
|
@@ -106,7 +106,7 @@ export const errors = {
|
|
|
106
106
|
*
|
|
107
107
|
*/
|
|
108
108
|
|
|
109
|
-
const KhanAnswerTypes
|
|
109
|
+
const KhanAnswerTypes = {
|
|
110
110
|
/*
|
|
111
111
|
* predicate answer type
|
|
112
112
|
*
|
|
@@ -653,8 +653,7 @@ const KhanAnswerTypes: $FlowFixMe = {
|
|
|
653
653
|
correctAnswer: string,
|
|
654
654
|
options: $FlowFixMe,
|
|
655
655
|
): Predicate {
|
|
656
|
-
|
|
657
|
-
const correctFloat = parseFloat($.trim(correctAnswer));
|
|
656
|
+
const correctFloat = parseFloat(correctAnswer);
|
|
658
657
|
|
|
659
658
|
return [
|
|
660
659
|
function (guess, maxError) {
|
package/src/util/math.js
CHANGED
|
@@ -4,14 +4,7 @@ import {number as knumber} from "@khanacademy/kmath";
|
|
|
4
4
|
import $ from "jquery";
|
|
5
5
|
import _ from "underscore";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
| "integer"
|
|
9
|
-
| "mixed"
|
|
10
|
-
| "improper"
|
|
11
|
-
| "proper"
|
|
12
|
-
| "decimal"
|
|
13
|
-
| "percent"
|
|
14
|
-
| "pi";
|
|
7
|
+
import type {MathFormat} from "../perseus-types";
|
|
15
8
|
|
|
16
9
|
const KhanMath = {
|
|
17
10
|
// Simplify formulas before display
|
|
@@ -213,7 +206,7 @@ const KhanMath = {
|
|
|
213
206
|
// Returns the format (string) of a given numeric string
|
|
214
207
|
// Note: purposively more inclusive than answer-types' predicate.forms
|
|
215
208
|
// That is, it is not necessarily true that interpreted input are numeric
|
|
216
|
-
getNumericFormat: function (text: string): ?
|
|
209
|
+
getNumericFormat: function (text: string): ?MathFormat {
|
|
217
210
|
text = $.trim(text);
|
|
218
211
|
text = text.replace(/\u2212/, "-").replace(/([+-])\s+/g, "$1");
|
|
219
212
|
if (text.match(/^[+-]?\d+$/)) {
|
|
@@ -238,7 +231,7 @@ const KhanMath = {
|
|
|
238
231
|
},
|
|
239
232
|
|
|
240
233
|
// Returns a string of the number in a specified format
|
|
241
|
-
toNumericString: function (number: number, format?:
|
|
234
|
+
toNumericString: function (number: number, format?: MathFormat): string {
|
|
242
235
|
if (number == null) {
|
|
243
236
|
return "";
|
|
244
237
|
}
|
package/src/util.js
CHANGED
|
@@ -92,14 +92,8 @@ function inputPathsEqual(
|
|
|
92
92
|
);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
// Keep `rWidgetRule` in sync with `WIDGET_RE`
|
|
96
|
-
// in content_internal/perseus_traversal.py
|
|
97
|
-
// sync-start:widget-id-regex 1271666319 services/content-editing/perseus/translation/content_traversal.go
|
|
98
|
-
// sync-start:widget-id-regex 680240484 services/static/javascript/perseus-merged-editor-package/editor.jsx
|
|
99
|
-
// sync-start:widget-id-regex 1400368396 content_internal/perseus_traversal.py
|
|
100
95
|
const rWidgetRule: RegExp = /^\[\[\u2603 (([a-z-]+) ([0-9]+))\]\]/;
|
|
101
96
|
const rTypeFromWidgetId: RegExp = /^([a-z-]+) ([0-9]+)$/;
|
|
102
|
-
// sync-end:widget-id-regex
|
|
103
97
|
|
|
104
98
|
const rWidgetParts: RegExp = new RegExp(rWidgetRule.source + "$");
|
|
105
99
|
const snowman = "\u2603";
|
package/src/version.js
CHANGED
|
@@ -1,19 +1,60 @@
|
|
|
1
1
|
// @flow
|
|
2
|
+
import {action} from "@storybook/addon-actions";
|
|
2
3
|
import * as React from "react";
|
|
3
4
|
|
|
4
5
|
import {RendererWithDebugUI} from "../../../../../testing/renderer-with-debug-ui.jsx";
|
|
5
6
|
import {question1} from "../__testdata__/numeric-input_testdata.js";
|
|
7
|
+
import {NumericInput} from "../numeric-input.jsx";
|
|
6
8
|
|
|
7
|
-
type StoryArgs = {
|
|
9
|
+
type StoryArgs = {|
|
|
10
|
+
currentValue: string,
|
|
11
|
+
|};
|
|
8
12
|
|
|
9
13
|
type Story = {|
|
|
10
14
|
title: string,
|
|
15
|
+
args: StoryArgs,
|
|
11
16
|
|};
|
|
12
17
|
|
|
18
|
+
function generateProps(overwrite) {
|
|
19
|
+
const base = {
|
|
20
|
+
alignment: "",
|
|
21
|
+
answers: [],
|
|
22
|
+
containerSizeClass: "medium",
|
|
23
|
+
isLastUsedWidget: true,
|
|
24
|
+
currentValue: "",
|
|
25
|
+
problemNum: 0,
|
|
26
|
+
reviewModeRubric: {
|
|
27
|
+
answers: [],
|
|
28
|
+
labelText: "",
|
|
29
|
+
size: "medium",
|
|
30
|
+
coefficient: false,
|
|
31
|
+
static: false,
|
|
32
|
+
},
|
|
33
|
+
static: false,
|
|
34
|
+
widgetId: "widgetId",
|
|
35
|
+
findWidgets: action("findWidgets"),
|
|
36
|
+
onBlur: action("onBlur"),
|
|
37
|
+
onChange: action("onChange"),
|
|
38
|
+
onFocus: action("onFocus"),
|
|
39
|
+
trackInteraction: action("trackInteraction"),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return {...base, ...overwrite};
|
|
43
|
+
}
|
|
44
|
+
|
|
13
45
|
export default ({
|
|
14
46
|
title: "Perseus/Widgets/NumericInput",
|
|
47
|
+
args: {
|
|
48
|
+
currentValue: "8675309",
|
|
49
|
+
},
|
|
15
50
|
}: Story);
|
|
16
51
|
|
|
17
|
-
export const Question1 = (
|
|
52
|
+
export const Question1 = (): React.Node => {
|
|
18
53
|
return <RendererWithDebugUI question={question1} />;
|
|
19
54
|
};
|
|
55
|
+
|
|
56
|
+
export const Interactive = (args: StoryArgs): React.Node => {
|
|
57
|
+
const props = generateProps(args);
|
|
58
|
+
|
|
59
|
+
return <NumericInput {...props} />;
|
|
60
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type {ChoiceType} from "../radio/base-radio.jsx";
|
|
3
|
+
|
|
4
|
+
export function generateChoice(options: $Partial<ChoiceType>): ChoiceType {
|
|
5
|
+
const base = {
|
|
6
|
+
checked: false,
|
|
7
|
+
crossedOut: false,
|
|
8
|
+
content: "",
|
|
9
|
+
rationale: "",
|
|
10
|
+
hasRationale: false,
|
|
11
|
+
showRationale: false,
|
|
12
|
+
showCorrectness: false,
|
|
13
|
+
correct: false,
|
|
14
|
+
isNoneOfTheAbove: false,
|
|
15
|
+
highlighted: false,
|
|
16
|
+
previouslyAnswered: false,
|
|
17
|
+
revealNoneOfTheAbove: false,
|
|
18
|
+
disabled: false,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return {...base, ...options};
|
|
22
|
+
}
|
|
@@ -37,13 +37,13 @@ exports[`Definition widget should have a default snapshot: first render 1`] = `
|
|
|
37
37
|
>
|
|
38
38
|
<button
|
|
39
39
|
aria-disabled="false"
|
|
40
|
-
class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1hl5pu8
|
|
40
|
+
class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1hl5pu8"
|
|
41
41
|
role="button"
|
|
42
42
|
tabindex="0"
|
|
43
43
|
type="button"
|
|
44
44
|
>
|
|
45
45
|
<span
|
|
46
|
-
class="text_f1191h-o_O-
|
|
46
|
+
class="text_f1191h-o_O-LabelLarge_5s82ln-o_O-text_m8a8ki-o_O-textWithFocus_e296pg"
|
|
47
47
|
>
|
|
48
48
|
the Pequots
|
|
49
49
|
</span>
|
|
@@ -95,13 +95,13 @@ exports[`Definition widget should have an open state snapshot: open state 1`] =
|
|
|
95
95
|
>
|
|
96
96
|
<button
|
|
97
97
|
aria-disabled="false"
|
|
98
|
-
class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1hl5pu8
|
|
98
|
+
class="button_vr44p2-o_O-shared_lwskrm-o_O-default_1hl5pu8"
|
|
99
99
|
role="button"
|
|
100
100
|
tabindex="0"
|
|
101
101
|
type="button"
|
|
102
102
|
>
|
|
103
103
|
<span
|
|
104
|
-
class="text_f1191h-o_O-
|
|
104
|
+
class="text_f1191h-o_O-LabelLarge_5s82ln-o_O-text_m8a8ki-o_O-textWithFocus_e296pg-o_O-focus_15mc0l2"
|
|
105
105
|
>
|
|
106
106
|
the Pequots
|
|
107
107
|
</span>
|