@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.
@@ -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 className={css(styles.indicatorContainer)}>
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
- role="button"
45
- aria-label={i18n._("Skip to %(title)s", {
46
- title,
47
- })}
48
- key={title}
49
- className={css(
50
- styles.indicator,
51
- i === this.props.currentGroup &&
52
- styles.selectedIndicator,
53
- )}
54
- tabIndex={0}
55
- onClick={() => this.props.onChangeCurrentGroup(i)}
56
- onKeyDown={(e) => this.handleKeyDown(e, i)}
57
- >
58
- {i === this.props.currentGroup && (
59
- <span className={css(a11y.srOnly)}>
60
- {i18n._("Current")}
61
- </span>
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
- border: "3px solid",
310
+ borderWidth: 2,
272
311
  borderColor: Color.blue,
273
- marginLeft: 5,
274
- cursor: "pointer",
312
+ borderStyle: "solid",
313
+ },
314
+
315
+ indicatorDotFocused: {
316
+ borderWidth: 5,
317
+ borderStyle: "double",
275
318
  },
276
319
 
277
- selectedIndicator: {
320
+ indicatorDotActive: {
278
321
  backgroundColor: Color.blue,
322
+ width: "100%",
323
+ height: "100%",
279
324
  },
280
325
 
281
326
  container: {
@@ -0,0 +1,3 @@
1
+ export const strings = {
2
+ hideAnswersToggleLabel: "Hide answer choices",
3
+ };
@@ -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 {APIOptions, WidgetExports} from "../types";
16
+ import type {PerseusPlotterWidgetOptions} from "../perseus-types";
17
+ import type {WidgetExports, WidgetProps} from "../types";
17
18
 
18
19
  const {deepEq} = Util;
19
20
 
20
- const BAR = "bar";
21
- const LINE = "line";
22
- const PIC = "pic";
23
- const HISTOGRAM = "histogram";
24
- const DOTPLOT = "dotplot";
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: Array<number>;
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: BAR,
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 === BAR;
179
- const isLine = self.props.type === LINE;
180
- const isPic = self.props.type === PIC;
181
- const isHistogram = self.props.type === HISTOGRAM;
182
- const isDotplot = self.props.type === DOTPLOT;
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 && this.props.markings !== "none") {
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 === HISTOGRAM) {
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 === BAR) {
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 === LINE) {
569
+ } else if (self.props.type === "line") {
591
570
  x = self.setupLine(i, startHeight, config);
592
- } else if (self.props.type === PIC) {
571
+ } else if (self.props.type === "pic") {
593
572
  x = self.setupPic(i, config);
594
- } else if (self.props.type === DOTPLOT) {
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 === DOTPLOT && !isMobile) {
580
+ if (self.props.type === "dotplot" && !isMobile) {
602
581
  tickStart = -tickEnd;
603
582
  }
604
583
 
605
- if (self.props.type === DOTPLOT) {
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 = _.clone(self.state.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 = _.clone(self.state.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 = _.clone(self.state.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 = _.clone(self.state.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 = _.clone(this.state.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 === DOTPLOT) {
1108
+ if (self.props.type === "dotplot") {
1130
1109
  const wasShown = y <= prevValues[i];
1131
1110
  const wasJustShown = show && !wasShown;
1132
1111
  if (wasJustShown) {