@khanacademy/perseus 12.3.0 → 13.0.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/CHANGELOG.md +38 -0
- package/dist/es/index.css +3 -3
- package/dist/es/index.js +593 -223
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +9698 -9446
- package/dist/index.js.map +1 -1
- package/dist/item-renderer.d.ts +3 -1
- package/dist/perseus-types.d.ts +4 -2
- package/dist/widgets/label-image/strings.d.ts +3 -0
- package/dist/widgets/plotter.d.ts +8 -22
- package/package.json +10 -8
- package/src/__tests__/item-renderer.test.tsx +41 -0
- package/src/__tests__/widget-container.test.tsx +126 -0
- package/src/error-boundary.tsx +1 -0
- package/src/index.ts +5 -1
- package/src/item-renderer.tsx +4 -2
- package/src/perseus-types.ts +11 -2
- package/src/styles/articles.less +3 -1
- package/src/styles/perseus-renderer.less +1 -1
- package/src/widget-container.tsx +2 -1
- package/src/widgets/__tests__/__snapshots__/graded-group-set.test.ts.snap +50 -19
- package/src/widgets/__tests__/plotter.test.tsx +11 -8
- package/src/widgets/graded-group-set.tsx +70 -25
- package/src/widgets/label-image/strings.ts +3 -0
- package/src/widgets/plotter.tsx +29 -50
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @khanacademy/ts-no-error-suppressions */
|
|
2
2
|
import {linterContextDefault} from "@khanacademy/perseus-linter";
|
|
3
|
+
import Clickable from "@khanacademy/wonder-blocks-clickable";
|
|
3
4
|
import Color from "@khanacademy/wonder-blocks-color";
|
|
5
|
+
import {View} from "@khanacademy/wonder-blocks-core";
|
|
4
6
|
import * as i18n from "@khanacademy/wonder-blocks-i18n";
|
|
5
7
|
import {StyleSheet, css} from "aphrodite";
|
|
8
|
+
import classNames from "classnames";
|
|
6
9
|
import * as React from "react";
|
|
7
10
|
|
|
8
11
|
import {getDependencies} from "../dependencies";
|
|
@@ -38,28 +41,43 @@ class Indicators extends React.Component<IndicatorsProps> {
|
|
|
38
41
|
|
|
39
42
|
render(): React.ReactNode {
|
|
40
43
|
return (
|
|
41
|
-
<ul
|
|
44
|
+
<ul
|
|
45
|
+
// reduntantly add class name for use in .less files--
|
|
46
|
+
// the styles object key gets hashed
|
|
47
|
+
className={classNames(
|
|
48
|
+
css(styles.indicatorContainer),
|
|
49
|
+
"indicatorContainer",
|
|
50
|
+
)}
|
|
51
|
+
>
|
|
42
52
|
{this.props.gradedGroups.map(({title}, i) => (
|
|
43
|
-
<li
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
title,
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
<li className={css(styles.indicator)} key={title}>
|
|
54
|
+
<Clickable
|
|
55
|
+
role="button"
|
|
56
|
+
aria-label={i18n._("Skip to %(title)s", {
|
|
57
|
+
title,
|
|
58
|
+
})}
|
|
59
|
+
style={styles.indicatorButton}
|
|
60
|
+
onClick={() => this.props.onChangeCurrentGroup(i)}
|
|
61
|
+
onKeyDown={(e) => this.handleKeyDown(e, i)}
|
|
62
|
+
>
|
|
63
|
+
{({hovered, focused, pressed}) => (
|
|
64
|
+
<View
|
|
65
|
+
style={[
|
|
66
|
+
styles.indicatorDot,
|
|
67
|
+
(hovered || focused || pressed) &&
|
|
68
|
+
styles.indicatorDotFocused,
|
|
69
|
+
]}
|
|
70
|
+
>
|
|
71
|
+
{i === this.props.currentGroup && (
|
|
72
|
+
<View style={styles.indicatorDotActive}>
|
|
73
|
+
<span className={css(a11y.srOnly)}>
|
|
74
|
+
{i18n._("Current")}
|
|
75
|
+
</span>
|
|
76
|
+
</View>
|
|
77
|
+
)}
|
|
78
|
+
</View>
|
|
79
|
+
)}
|
|
80
|
+
</Clickable>
|
|
63
81
|
</li>
|
|
64
82
|
))}
|
|
65
83
|
</ul>
|
|
@@ -262,20 +280,47 @@ const styles = StyleSheet.create({
|
|
|
262
280
|
flexDirection: "row",
|
|
263
281
|
listStyle: "none",
|
|
264
282
|
margin: "unset",
|
|
283
|
+
paddingInlineStart: "unset",
|
|
284
|
+
flexWrap: "wrap",
|
|
265
285
|
},
|
|
266
286
|
|
|
267
287
|
indicator: {
|
|
288
|
+
width: 24,
|
|
289
|
+
height: 24,
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
indicatorButton: {
|
|
293
|
+
width: "100%",
|
|
294
|
+
height: "100%",
|
|
295
|
+
display: "flex",
|
|
296
|
+
flexWrap: "wrap",
|
|
297
|
+
placeContent: "center",
|
|
298
|
+
cursor: "pointer",
|
|
299
|
+
|
|
300
|
+
":focus": {
|
|
301
|
+
outline: "none",
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
indicatorDot: {
|
|
306
|
+
boxSizing: "content-box",
|
|
268
307
|
width: 10,
|
|
269
308
|
height: 10,
|
|
270
309
|
borderRadius: "100%",
|
|
271
|
-
|
|
310
|
+
borderWidth: 2,
|
|
272
311
|
borderColor: Color.blue,
|
|
273
|
-
|
|
274
|
-
|
|
312
|
+
borderStyle: "solid",
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
indicatorDotFocused: {
|
|
316
|
+
borderWidth: 5,
|
|
317
|
+
borderStyle: "double",
|
|
275
318
|
},
|
|
276
319
|
|
|
277
|
-
|
|
320
|
+
indicatorDotActive: {
|
|
278
321
|
backgroundColor: Color.blue,
|
|
322
|
+
width: "100%",
|
|
323
|
+
height: "100%",
|
|
279
324
|
},
|
|
280
325
|
|
|
281
326
|
container: {
|
package/src/widgets/plotter.tsx
CHANGED
|
@@ -13,38 +13,17 @@ import KhanColors from "../util/colors";
|
|
|
13
13
|
import GraphUtils from "../util/graph-utils";
|
|
14
14
|
import KhanMath from "../util/math";
|
|
15
15
|
|
|
16
|
-
import type {
|
|
16
|
+
import type {PerseusPlotterWidgetOptions} from "../perseus-types";
|
|
17
|
+
import type {WidgetExports, WidgetProps} from "../types";
|
|
17
18
|
|
|
18
19
|
const {deepEq} = Util;
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const plotTypes = [BAR, LINE, PIC, HISTOGRAM, DOTPLOT];
|
|
27
|
-
type PlotType = typeof plotTypes[number];
|
|
28
|
-
|
|
29
|
-
type Props = {
|
|
30
|
-
type: PlotType;
|
|
31
|
-
labels: ReadonlyArray<string>;
|
|
32
|
-
categories: ReadonlyArray<string | number>;
|
|
33
|
-
scaleY: number;
|
|
34
|
-
maxY: number;
|
|
35
|
-
snapsPerLine: number;
|
|
36
|
-
picSize: number;
|
|
37
|
-
picBoxHeight: number;
|
|
38
|
-
picUrl: string;
|
|
39
|
-
plotDimensions: ReadonlyArray<number>;
|
|
40
|
-
labelInterval: number;
|
|
41
|
-
|
|
42
|
-
starting: Array<number>;
|
|
43
|
-
static: boolean;
|
|
44
|
-
markings: any;
|
|
45
|
-
onChange: any;
|
|
46
|
-
trackInteraction: () => void;
|
|
47
|
-
apiOptions: APIOptions;
|
|
21
|
+
type Rubric = PerseusPlotterWidgetOptions;
|
|
22
|
+
type RenderProps = PerseusPlotterWidgetOptions;
|
|
23
|
+
|
|
24
|
+
type Props = WidgetProps<RenderProps, Rubric> & {
|
|
25
|
+
labelInterval: NonNullable<PerseusPlotterWidgetOptions["labelInterval"]>;
|
|
26
|
+
picSize: NonNullable<PerseusPlotterWidgetOptions["picSize"]>;
|
|
48
27
|
};
|
|
49
28
|
|
|
50
29
|
type DefaultProps = {
|
|
@@ -62,7 +41,7 @@ type DefaultProps = {
|
|
|
62
41
|
};
|
|
63
42
|
|
|
64
43
|
type State = {
|
|
65
|
-
values:
|
|
44
|
+
values: ReadonlyArray<number>;
|
|
66
45
|
categoryHeights: Record<string, number>;
|
|
67
46
|
};
|
|
68
47
|
|
|
@@ -75,7 +54,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
75
54
|
graphie: any;
|
|
76
55
|
|
|
77
56
|
static defaultProps: DefaultProps = {
|
|
78
|
-
type:
|
|
57
|
+
type: "bar",
|
|
79
58
|
labels: ["", ""],
|
|
80
59
|
categories: [""],
|
|
81
60
|
|
|
@@ -175,11 +154,11 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
175
154
|
self.graphie.pics = [];
|
|
176
155
|
self.graphie.dotTicks = [];
|
|
177
156
|
|
|
178
|
-
const isBar = self.props.type ===
|
|
179
|
-
const isLine = self.props.type ===
|
|
180
|
-
const isPic = self.props.type ===
|
|
181
|
-
const isHistogram = self.props.type ===
|
|
182
|
-
const isDotplot = self.props.type ===
|
|
157
|
+
const isBar = self.props.type === "bar";
|
|
158
|
+
const isLine = self.props.type === "line";
|
|
159
|
+
const isPic = self.props.type === "pic";
|
|
160
|
+
const isHistogram = self.props.type === "histogram";
|
|
161
|
+
const isDotplot = self.props.type === "dotplot";
|
|
183
162
|
|
|
184
163
|
const isTiledPlot = isPic || isDotplot;
|
|
185
164
|
|
|
@@ -462,7 +441,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
462
441
|
};
|
|
463
442
|
|
|
464
443
|
showHairlines: (arg1: any) => void = (point) => {
|
|
465
|
-
if (this.props.apiOptions.isMobile
|
|
444
|
+
if (this.props.apiOptions.isMobile) {
|
|
466
445
|
// Hairlines are already initialized when the graph is loaded, so
|
|
467
446
|
// here we just move them to the updated location and make them
|
|
468
447
|
// visible.
|
|
@@ -546,7 +525,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
546
525
|
// The deferred measurements returned from `labelCategory`.
|
|
547
526
|
const categoryHeightPromises = [];
|
|
548
527
|
|
|
549
|
-
if (self.props.type ===
|
|
528
|
+
if (self.props.type === "histogram") {
|
|
550
529
|
// Histograms with n labels/categories have n - 1 buckets
|
|
551
530
|
_.times(self.props.categories.length - 1, function (i) {
|
|
552
531
|
self.setupBar({
|
|
@@ -580,29 +559,29 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
580
559
|
const startHeight = self.state.values[i];
|
|
581
560
|
let x;
|
|
582
561
|
|
|
583
|
-
if (self.props.type ===
|
|
562
|
+
if (self.props.type === "bar") {
|
|
584
563
|
x = self.setupBar({
|
|
585
564
|
index: i,
|
|
586
565
|
startHeight: startHeight,
|
|
587
566
|
config: config,
|
|
588
567
|
isHistogram: false,
|
|
589
568
|
});
|
|
590
|
-
} else if (self.props.type ===
|
|
569
|
+
} else if (self.props.type === "line") {
|
|
591
570
|
x = self.setupLine(i, startHeight, config);
|
|
592
|
-
} else if (self.props.type ===
|
|
571
|
+
} else if (self.props.type === "pic") {
|
|
593
572
|
x = self.setupPic(i, config);
|
|
594
|
-
} else if (self.props.type ===
|
|
573
|
+
} else if (self.props.type === "dotplot") {
|
|
595
574
|
x = self.setupDotplot(i, config);
|
|
596
575
|
}
|
|
597
576
|
|
|
598
577
|
let tickStart = 0;
|
|
599
578
|
let tickEnd = -6 / c.scale[1];
|
|
600
579
|
|
|
601
|
-
if (self.props.type ===
|
|
580
|
+
if (self.props.type === "dotplot" && !isMobile) {
|
|
602
581
|
tickStart = -tickEnd;
|
|
603
582
|
}
|
|
604
583
|
|
|
605
|
-
if (self.props.type ===
|
|
584
|
+
if (self.props.type === "dotplot") {
|
|
606
585
|
// Dotplot lets you specify to only show labels every 'n'
|
|
607
586
|
// ticks. It also looks nicer if it makes the labelled
|
|
608
587
|
// ticks a bit bigger.
|
|
@@ -804,7 +783,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
804
783
|
onMove: function () {
|
|
805
784
|
const y = config.graph.lines[i].coord()[1];
|
|
806
785
|
|
|
807
|
-
const values =
|
|
786
|
+
const values = [...self.state.values];
|
|
808
787
|
values[i] = y;
|
|
809
788
|
self.setState({values: values});
|
|
810
789
|
self.changeAndTrack({values: values});
|
|
@@ -851,7 +830,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
851
830
|
this.transform();
|
|
852
831
|
}
|
|
853
832
|
|
|
854
|
-
const values =
|
|
833
|
+
const values = [...self.state.values];
|
|
855
834
|
values[i] = y;
|
|
856
835
|
self.setState({values: values});
|
|
857
836
|
self.changeAndTrack({values: values});
|
|
@@ -901,7 +880,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
901
880
|
onMove: function () {
|
|
902
881
|
const y = c.graph.points[i].coord()[1];
|
|
903
882
|
|
|
904
|
-
const values =
|
|
883
|
+
const values = [...self.state.values];
|
|
905
884
|
values[i] = y;
|
|
906
885
|
self.setState({values: values});
|
|
907
886
|
self.changeAndTrack({values: values});
|
|
@@ -941,7 +920,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
941
920
|
});
|
|
942
921
|
c.graph.points[i].onMove = function (x, y) {
|
|
943
922
|
y = Math.min(Math.max(y, 0), c.dimY);
|
|
944
|
-
const values =
|
|
923
|
+
const values = [...self.state.values];
|
|
945
924
|
values[i] = y;
|
|
946
925
|
self.setState({values: values});
|
|
947
926
|
self.changeAndTrack({values: values});
|
|
@@ -1095,7 +1074,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
1095
1074
|
};
|
|
1096
1075
|
|
|
1097
1076
|
setPicHeight: (arg1: number, arg2: number) => void = (i, y) => {
|
|
1098
|
-
const values =
|
|
1077
|
+
const values = [...this.state.values];
|
|
1099
1078
|
values[i] = y;
|
|
1100
1079
|
this.drawPicHeights(values, this.state.values);
|
|
1101
1080
|
this.setState({values: values});
|
|
@@ -1126,7 +1105,7 @@ export class Plotter extends React.Component<Props, State> {
|
|
|
1126
1105
|
const y = (j + 1) * self.props.scaleY;
|
|
1127
1106
|
const show = y <= values[i];
|
|
1128
1107
|
|
|
1129
|
-
if (self.props.type ===
|
|
1108
|
+
if (self.props.type === "dotplot") {
|
|
1130
1109
|
const wasShown = y <= prevValues[i];
|
|
1131
1110
|
const wasJustShown = show && !wasShown;
|
|
1132
1111
|
if (wasJustShown) {
|