@khanacademy/perseus 21.2.1 → 21.3.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/dist/es/index.css CHANGED
@@ -4291,9 +4291,13 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4291
4291
  --movable-line-stroke-weight-active: 4px;
4292
4292
  }
4293
4293
 
4294
- .MafsView .movable-line:is(:focus-visible, :hover),
4294
+ .MafsView > svg {
4295
+ /* Chrome/Safari bugfix for LEMS-1906 */
4296
+ display: block;
4297
+ }
4298
+
4299
+ .MafsView .movable-line:hover,
4295
4300
  .movable-dragging {
4296
- outline: none;
4297
4301
  --movable-line-stroke-weight: var(--movable-line-stroke-weight-active);
4298
4302
  }
4299
4303
 
@@ -4302,6 +4306,22 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4302
4306
  outline: none;
4303
4307
  }
4304
4308
 
4309
+ .MafsView
4310
+ .movable-line
4311
+ :is(.movable-line-focus-outline, .movable-line-focus-outline-gap) {
4312
+ stroke: transparent;
4313
+ }
4314
+
4315
+ .MafsView .movable-line:focus-visible .movable-line-focus-outline {
4316
+ stroke: var(--mafs-blue);
4317
+ stroke-width: 10px;
4318
+ }
4319
+
4320
+ .MafsView .movable-line:focus-visible .movable-line-focus-outline-gap {
4321
+ stroke: white;
4322
+ stroke-width: 6px;
4323
+ }
4324
+
4305
4325
  .MafsView .movable-point-hitbox {
4306
4326
  fill: transparent;
4307
4327
  }
@@ -4372,3 +4392,14 @@ html body .mq-editable-field .mq-selection .mq-nthroot-container * {
4372
4392
  .MafsView .movable-point:focus-visible .movable-point-focus-outline {
4373
4393
  visibility: visible;
4374
4394
  }
4395
+
4396
+ @font-face {
4397
+ font-family: Mafs-MJXTEX;
4398
+ src: url("https://cdn.kastatic.org/fonts/mathjax/MathJax_Main-Regular.woff")
4399
+ format("woff");
4400
+ }
4401
+
4402
+ .MafsView .axis-ticks text {
4403
+ font-size: 14px;
4404
+ font-family: "Mafs-MJXTEX";
4405
+ }
package/dist/es/index.js CHANGED
@@ -7,7 +7,7 @@ import * as i18n from '@khanacademy/wonder-blocks-i18n';
7
7
  import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
8
8
  import classNames from 'classnames';
9
9
  import * as React from 'react';
10
- import React__default, { useState, useEffect, useRef, createContext } from 'react';
10
+ import React__default, { useState, useEffect, useRef, createContext, useImperativeHandle } from 'react';
11
11
  import _, { debounce as debounce$1 } from 'underscore';
12
12
  import Clickable from '@khanacademy/wonder-blocks-clickable';
13
13
  import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder-blocks-popover';
@@ -24912,6 +24912,7 @@ class Explanation extends React.Component {
24912
24912
  // While the button is not normally included in a block of text, it needs to be able to accommodate such a case.
24913
24913
  const buttonStyleOverrides = {
24914
24914
  height: "auto",
24915
+ lineHeight: "inherit",
24915
24916
  marginLeft: "-2px",
24916
24917
  // transfer space on the left side to the padding
24917
24918
  marginRight: "2px",
@@ -24921,7 +24922,8 @@ class Explanation extends React.Component {
24921
24922
 
24922
24923
  const labelStyle = {
24923
24924
  fontSize: "18px",
24924
- lineHeight: "20px",
24925
+ lineHeight: "inherit",
24926
+ "text-align": "left",
24925
24927
  // The following property adjusts the large space between the button text and the caret icon.
24926
24928
  // Since we are unable to adjust the styling of the icon (where the extra space exists),
24927
24929
  // we are adjusting it on the text label by using a negative margin.
@@ -28237,14 +28239,6 @@ function changeRange(range) {
28237
28239
  };
28238
28240
  }
28239
28241
 
28240
- const GraphStateContext = /*#__PURE__*/createContext({
28241
- state: {},
28242
- dispatch: () => {}
28243
- });
28244
- function useGraphState() {
28245
- return React__default.useContext(GraphStateContext);
28246
- }
28247
-
28248
28242
  /**
28249
28243
  * 44 is touch best practice and AAA compliant for WCAG
28250
28244
  * https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
@@ -28330,9 +28324,6 @@ const getLines = points => points.map((point, i) => {
28330
28324
 
28331
28325
  const defaultStroke = "var(--movable-line-stroke-color)";
28332
28326
  const MovableLine = props => {
28333
- const {
28334
- state
28335
- } = useGraphState();
28336
28327
  const {
28337
28328
  start,
28338
28329
  end,
@@ -28357,12 +28348,13 @@ const MovableLine = props => {
28357
28348
  onMove: newPoint => {
28358
28349
  onMove(vec.sub(newPoint, midpoint));
28359
28350
  },
28360
- constrain: p => snap(state.snapStep, p)
28351
+ constrain: p => p
28361
28352
  });
28362
28353
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("g", {
28363
28354
  ref: line,
28364
28355
  tabIndex: 0,
28365
28356
  className: "movable-line",
28357
+ "data-test-id": "movable-line",
28366
28358
  style: {
28367
28359
  cursor: dragging ? "grabbing" : "grab"
28368
28360
  }
@@ -28373,6 +28365,16 @@ const MovableLine = props => {
28373
28365
  stroke: "transparent",
28374
28366
  strokeWidth: TARGET_SIZE
28375
28367
  }
28368
+ }), /*#__PURE__*/React.createElement(SVGLine, {
28369
+ start: startPtPx,
28370
+ end: endPtPx,
28371
+ className: "movable-line-focus-outline",
28372
+ style: {}
28373
+ }), /*#__PURE__*/React.createElement(SVGLine, {
28374
+ start: startPtPx,
28375
+ end: endPtPx,
28376
+ className: "movable-line-focus-outline-gap",
28377
+ style: {}
28376
28378
  }), /*#__PURE__*/React.createElement(SVGLine, {
28377
28379
  start: startPtPx,
28378
28380
  end: endPtPx,
@@ -28380,7 +28382,8 @@ const MovableLine = props => {
28380
28382
  stroke,
28381
28383
  strokeWidth: "var(--movable-line-stroke-weight)"
28382
28384
  },
28383
- dragging: dragging
28385
+ dragging: dragging,
28386
+ testId: "movable-line__line"
28384
28387
  })), startExtend && /*#__PURE__*/React.createElement(Vector, {
28385
28388
  tail: start,
28386
28389
  tip: startExtend,
@@ -28396,18 +28399,30 @@ function SVGLine(props) {
28396
28399
  start,
28397
28400
  end,
28398
28401
  style,
28399
- dragging
28402
+ dragging,
28403
+ className,
28404
+ testId
28400
28405
  } = props;
28406
+ const draggingClass = dragging ? "movable-dragging" : "";
28401
28407
  return /*#__PURE__*/React.createElement("line", {
28402
28408
  x1: start[0],
28403
28409
  y1: start[1],
28404
28410
  x2: end[0],
28405
28411
  y2: end[1],
28406
28412
  style: style,
28407
- className: dragging ? "movable-dragging" : undefined
28413
+ className: `${className} ${draggingClass}`,
28414
+ "data-test-id": testId
28408
28415
  });
28409
28416
  }
28410
28417
 
28418
+ const GraphStateContext = /*#__PURE__*/createContext({
28419
+ state: {},
28420
+ dispatch: () => {}
28421
+ });
28422
+ function useGraphState() {
28423
+ return React__default.useContext(GraphStateContext);
28424
+ }
28425
+
28411
28426
  // The hitbox size of 48px by 48px is preserved from the legacy interactive
28412
28427
  // graph.
28413
28428
  const hitboxSizePx = 48;
@@ -28439,7 +28454,8 @@ const StyledMovablePoint = props => {
28439
28454
  touchAction: "none",
28440
28455
  outline: "none",
28441
28456
  "--movable-point-color": color$1
28442
- }
28457
+ },
28458
+ "data-test-id": "movable-point"
28443
28459
  }, /*#__PURE__*/React.createElement("circle", {
28444
28460
  className: "movable-point-hitbox",
28445
28461
  r: hitboxSizePx / 2,
@@ -28463,7 +28479,8 @@ const StyledMovablePoint = props => {
28463
28479
  cy: y,
28464
28480
  style: {
28465
28481
  fill: color$1
28466
- }
28482
+ },
28483
+ "data-test-id": "movable-point__center"
28467
28484
  }));
28468
28485
  };
28469
28486
 
@@ -28920,7 +28937,13 @@ const YGridTick = ({
28920
28937
  x2: xPosition + tickSize / 2,
28921
28938
  y2: yPosition,
28922
28939
  style: tickStyle
28923
- }));
28940
+ }), y !== -1 && /*#__PURE__*/React.createElement("text", {
28941
+ height: 20,
28942
+ width: 50,
28943
+ textAnchor: "end",
28944
+ x: xPosition - 10,
28945
+ y: yPosition + 5
28946
+ }, y.toString()));
28924
28947
  };
28925
28948
  const XGridTick = ({
28926
28949
  x
@@ -28935,13 +28958,23 @@ const XGridTick = ({
28935
28958
  x2: xPosition,
28936
28959
  y2: yPosition - tickSize / 2,
28937
28960
  style: tickStyle
28938
- }));
28961
+ }), x !== -1 && /*#__PURE__*/React.createElement("text", {
28962
+ height: 20,
28963
+ width: 50,
28964
+ textAnchor: "middle",
28965
+ x: xPosition,
28966
+ y: yPosition + 25
28967
+ }, x.toString()));
28939
28968
  };
28940
28969
  function generateTickLocations(tickStep, min, max) {
28941
28970
  const ticks = [];
28971
+
28972
+ // Add ticks in the positive direction
28942
28973
  for (let i = 0 + tickStep; i < max; i += tickStep) {
28943
28974
  ticks.push(i);
28944
28975
  }
28976
+
28977
+ // Add ticks in the negative direction
28945
28978
  for (let i = 0 - tickStep; i > min; i -= tickStep) {
28946
28979
  ticks.push(i);
28947
28980
  }
@@ -28970,24 +29003,6 @@ const AxisTicks = props => {
28970
29003
  }));
28971
29004
  };
28972
29005
 
28973
- /**
28974
- * lineLabelText get the text that should
28975
- * be displayed for a specific tick place
28976
- *
28977
- * @param {number} n the tick number
28978
- * @param {number} tickStep how frequently a label should appear
28979
- * @param {vec.Vector2} range the min/max range on the axis
28980
- */
28981
- const lineLabelText = (n, tickStep, range) => {
28982
- const [min, max] = range;
28983
- const isOnStep = n % tickStep === 0;
28984
- const isNegativeOne = n === -1;
28985
- const isMin = n === min;
28986
- const isMax = n === max;
28987
- const shouldRender = isOnStep && !isNegativeOne && !isMin && !isMax;
28988
- return shouldRender ? `${n}` : "";
28989
- };
28990
-
28991
29006
  /**
28992
29007
  * axisOptions determine axis options for Mafs
28993
29008
  *
@@ -28999,12 +29014,10 @@ const lineLabelText = (n, tickStep, range) => {
28999
29014
  * @param {number} axisIndex which axis we're getting options for
29000
29015
  */
29001
29016
  const axisOptions = (props, axisIndex) => {
29002
- const axisStep = props.tickStep[axisIndex];
29003
- const axisRange = props.range[axisIndex];
29004
29017
  return {
29005
29018
  axis: props.markings === "graph",
29006
29019
  lines: props.gridStep[axisIndex],
29007
- labels: n => lineLabelText(n, axisStep, axisRange)
29020
+ labels: false
29008
29021
  };
29009
29022
  };
29010
29023
  const Grid = props => {
@@ -30232,9 +30245,22 @@ const renderGraph = props => {
30232
30245
  return new UnreachableCaseError(type);
30233
30246
  }
30234
30247
  };
30235
- const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30236
- const [width, height] = props.box;
30248
+ const StatefulMafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30237
30249
  const [state, dispatch] = React.useReducer(interactiveGraphReducer, props, initializeGraphState);
30250
+ useImperativeHandle(ref, () => ({
30251
+ getUserInput: () => getGradableGraph(state, props.graph)
30252
+ }));
30253
+ return /*#__PURE__*/React.createElement(MafsGraph, _extends({
30254
+ state: state,
30255
+ dispatch: dispatch
30256
+ }, props));
30257
+ });
30258
+ const MafsGraph = props => {
30259
+ const {
30260
+ state,
30261
+ dispatch
30262
+ } = props;
30263
+ const [width, height] = props.box;
30238
30264
  const prevState = useRef(state);
30239
30265
  useEffect(() => {
30240
30266
  if (prevState.current !== state) {
@@ -30249,16 +30275,13 @@ const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30249
30275
  const [xSnap, ySnap] = props.snapStep;
30250
30276
  useEffect(() => {
30251
30277
  dispatch(changeSnapStep([xSnap, ySnap]));
30252
- }, [xSnap, ySnap]);
30278
+ }, [dispatch, xSnap, ySnap]);
30253
30279
 
30254
30280
  // Destructuring first to keep useEffect from making excess calls
30255
30281
  const [[xMinRange, xMaxRange], [yMinRange, yMaxRange]] = props.range;
30256
30282
  useEffect(() => {
30257
30283
  dispatch(changeRange([[xMinRange, xMaxRange], [yMinRange, yMaxRange]]));
30258
- }, [xMinRange, xMaxRange, yMinRange, yMaxRange]);
30259
- React.useImperativeHandle(ref, () => ({
30260
- getUserInput: () => getGradableGraph(state, props.graph)
30261
- }));
30284
+ }, [dispatch, xMinRange, xMaxRange, yMinRange, yMaxRange]);
30262
30285
  return /*#__PURE__*/React.createElement(GraphStateContext.Provider, {
30263
30286
  value: {
30264
30287
  state,
@@ -30268,7 +30291,9 @@ const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30268
30291
  style: {
30269
30292
  width,
30270
30293
  height,
30271
- position: "relative"
30294
+ position: "relative",
30295
+ padding: "25px 25px 0 0",
30296
+ boxSizing: "content-box"
30272
30297
  }
30273
30298
  }, /*#__PURE__*/React.createElement(LegacyGrid, {
30274
30299
  box: props.box,
@@ -30302,7 +30327,7 @@ const MafsGraph = /*#__PURE__*/React.forwardRef((props, ref) => {
30302
30327
  state,
30303
30328
  dispatch
30304
30329
  })))));
30305
- });
30330
+ };
30306
30331
 
30307
30332
  const {
30308
30333
  DeprecationMixin
@@ -31606,7 +31631,7 @@ class InteractiveGraph extends React.Component {
31606
31631
  const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
31607
31632
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
31608
31633
  const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
31609
- return /*#__PURE__*/React.createElement(MafsGraph, _extends({}, this.props, {
31634
+ return /*#__PURE__*/React.createElement(StatefulMafsGraph, _extends({}, this.props, {
31610
31635
  ref: this.mafsRef,
31611
31636
  gridStep: gridStep,
31612
31637
  snapStep: snapStep,
@@ -42345,7 +42370,7 @@ const init = function init(options) {
42345
42370
 
42346
42371
  // This file is processed by a Rollup plugin (replace) to inject the production
42347
42372
  const libName = "@khanacademy/perseus";
42348
- const libVersion = "21.2.1";
42373
+ const libVersion = "21.3.0";
42349
42374
  addLibraryVersionToPerseusDebug(libName, libVersion);
42350
42375
 
42351
42376
  const apiVersion = {