@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/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/index.js CHANGED
@@ -25037,6 +25037,7 @@ class Explanation extends React__namespace.Component {
25037
25037
  // While the button is not normally included in a block of text, it needs to be able to accommodate such a case.
25038
25038
  const buttonStyleOverrides = {
25039
25039
  height: "auto",
25040
+ lineHeight: "inherit",
25040
25041
  marginLeft: "-2px",
25041
25042
  // transfer space on the left side to the padding
25042
25043
  marginRight: "2px",
@@ -25046,7 +25047,8 @@ class Explanation extends React__namespace.Component {
25046
25047
 
25047
25048
  const labelStyle = {
25048
25049
  fontSize: "18px",
25049
- lineHeight: "20px",
25050
+ lineHeight: "inherit",
25051
+ "text-align": "left",
25050
25052
  // The following property adjusts the large space between the button text and the caret icon.
25051
25053
  // Since we are unable to adjust the styling of the icon (where the extra space exists),
25052
25054
  // we are adjusting it on the text label by using a negative margin.
@@ -28389,14 +28391,6 @@ function changeRange(range) {
28389
28391
  };
28390
28392
  }
28391
28393
 
28392
- const GraphStateContext = /*#__PURE__*/React.createContext({
28393
- state: {},
28394
- dispatch: () => {}
28395
- });
28396
- function useGraphState() {
28397
- return React__default["default"].useContext(GraphStateContext);
28398
- }
28399
-
28400
28394
  /**
28401
28395
  * 44 is touch best practice and AAA compliant for WCAG
28402
28396
  * https://www.w3.org/WAI/WCAG21/Understanding/target-size.html
@@ -28485,9 +28479,6 @@ const getLines = points => points.map((point, i) => {
28485
28479
 
28486
28480
  const defaultStroke = "var(--movable-line-stroke-color)";
28487
28481
  const MovableLine = props => {
28488
- const {
28489
- state
28490
- } = useGraphState();
28491
28482
  const {
28492
28483
  start,
28493
28484
  end,
@@ -28512,12 +28503,13 @@ const MovableLine = props => {
28512
28503
  onMove: newPoint => {
28513
28504
  onMove(mafs.vec.sub(newPoint, midpoint));
28514
28505
  },
28515
- constrain: p => snap(state.snapStep, p)
28506
+ constrain: p => p
28516
28507
  });
28517
28508
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("g", {
28518
28509
  ref: line,
28519
28510
  tabIndex: 0,
28520
28511
  className: "movable-line",
28512
+ "data-test-id": "movable-line",
28521
28513
  style: {
28522
28514
  cursor: dragging ? "grabbing" : "grab"
28523
28515
  }
@@ -28528,6 +28520,16 @@ const MovableLine = props => {
28528
28520
  stroke: "transparent",
28529
28521
  strokeWidth: TARGET_SIZE
28530
28522
  }
28523
+ }), /*#__PURE__*/React__namespace.createElement(SVGLine, {
28524
+ start: startPtPx,
28525
+ end: endPtPx,
28526
+ className: "movable-line-focus-outline",
28527
+ style: {}
28528
+ }), /*#__PURE__*/React__namespace.createElement(SVGLine, {
28529
+ start: startPtPx,
28530
+ end: endPtPx,
28531
+ className: "movable-line-focus-outline-gap",
28532
+ style: {}
28531
28533
  }), /*#__PURE__*/React__namespace.createElement(SVGLine, {
28532
28534
  start: startPtPx,
28533
28535
  end: endPtPx,
@@ -28535,7 +28537,8 @@ const MovableLine = props => {
28535
28537
  stroke,
28536
28538
  strokeWidth: "var(--movable-line-stroke-weight)"
28537
28539
  },
28538
- dragging: dragging
28540
+ dragging: dragging,
28541
+ testId: "movable-line__line"
28539
28542
  })), startExtend && /*#__PURE__*/React__namespace.createElement(mafs.Vector, {
28540
28543
  tail: start,
28541
28544
  tip: startExtend,
@@ -28551,18 +28554,30 @@ function SVGLine(props) {
28551
28554
  start,
28552
28555
  end,
28553
28556
  style,
28554
- dragging
28557
+ dragging,
28558
+ className,
28559
+ testId
28555
28560
  } = props;
28561
+ const draggingClass = dragging ? "movable-dragging" : "";
28556
28562
  return /*#__PURE__*/React__namespace.createElement("line", {
28557
28563
  x1: start[0],
28558
28564
  y1: start[1],
28559
28565
  x2: end[0],
28560
28566
  y2: end[1],
28561
28567
  style: style,
28562
- className: dragging ? "movable-dragging" : undefined
28568
+ className: `${className} ${draggingClass}`,
28569
+ "data-test-id": testId
28563
28570
  });
28564
28571
  }
28565
28572
 
28573
+ const GraphStateContext = /*#__PURE__*/React.createContext({
28574
+ state: {},
28575
+ dispatch: () => {}
28576
+ });
28577
+ function useGraphState() {
28578
+ return React__default["default"].useContext(GraphStateContext);
28579
+ }
28580
+
28566
28581
  // The hitbox size of 48px by 48px is preserved from the legacy interactive
28567
28582
  // graph.
28568
28583
  const hitboxSizePx = 48;
@@ -28594,7 +28609,8 @@ const StyledMovablePoint = props => {
28594
28609
  touchAction: "none",
28595
28610
  outline: "none",
28596
28611
  "--movable-point-color": color
28597
- }
28612
+ },
28613
+ "data-test-id": "movable-point"
28598
28614
  }, /*#__PURE__*/React__namespace.createElement("circle", {
28599
28615
  className: "movable-point-hitbox",
28600
28616
  r: hitboxSizePx / 2,
@@ -28618,7 +28634,8 @@ const StyledMovablePoint = props => {
28618
28634
  cy: y,
28619
28635
  style: {
28620
28636
  fill: color
28621
- }
28637
+ },
28638
+ "data-test-id": "movable-point__center"
28622
28639
  }));
28623
28640
  };
28624
28641
 
@@ -29084,7 +29101,13 @@ const YGridTick = _ref => {
29084
29101
  x2: xPosition + tickSize / 2,
29085
29102
  y2: yPosition,
29086
29103
  style: tickStyle
29087
- }));
29104
+ }), y !== -1 && /*#__PURE__*/React__namespace.createElement("text", {
29105
+ height: 20,
29106
+ width: 50,
29107
+ textAnchor: "end",
29108
+ x: xPosition - 10,
29109
+ y: yPosition + 5
29110
+ }, y.toString()));
29088
29111
  };
29089
29112
  const XGridTick = _ref2 => {
29090
29113
  let {
@@ -29100,13 +29123,23 @@ const XGridTick = _ref2 => {
29100
29123
  x2: xPosition,
29101
29124
  y2: yPosition - tickSize / 2,
29102
29125
  style: tickStyle
29103
- }));
29126
+ }), x !== -1 && /*#__PURE__*/React__namespace.createElement("text", {
29127
+ height: 20,
29128
+ width: 50,
29129
+ textAnchor: "middle",
29130
+ x: xPosition,
29131
+ y: yPosition + 25
29132
+ }, x.toString()));
29104
29133
  };
29105
29134
  function generateTickLocations(tickStep, min, max) {
29106
29135
  const ticks = [];
29136
+
29137
+ // Add ticks in the positive direction
29107
29138
  for (let i = 0 + tickStep; i < max; i += tickStep) {
29108
29139
  ticks.push(i);
29109
29140
  }
29141
+
29142
+ // Add ticks in the negative direction
29110
29143
  for (let i = 0 - tickStep; i > min; i -= tickStep) {
29111
29144
  ticks.push(i);
29112
29145
  }
@@ -29135,24 +29168,6 @@ const AxisTicks = props => {
29135
29168
  }));
29136
29169
  };
29137
29170
 
29138
- /**
29139
- * lineLabelText get the text that should
29140
- * be displayed for a specific tick place
29141
- *
29142
- * @param {number} n the tick number
29143
- * @param {number} tickStep how frequently a label should appear
29144
- * @param {vec.Vector2} range the min/max range on the axis
29145
- */
29146
- const lineLabelText = (n, tickStep, range) => {
29147
- const [min, max] = range;
29148
- const isOnStep = n % tickStep === 0;
29149
- const isNegativeOne = n === -1;
29150
- const isMin = n === min;
29151
- const isMax = n === max;
29152
- const shouldRender = isOnStep && !isNegativeOne && !isMin && !isMax;
29153
- return shouldRender ? `${n}` : "";
29154
- };
29155
-
29156
29171
  /**
29157
29172
  * axisOptions determine axis options for Mafs
29158
29173
  *
@@ -29164,12 +29179,10 @@ const lineLabelText = (n, tickStep, range) => {
29164
29179
  * @param {number} axisIndex which axis we're getting options for
29165
29180
  */
29166
29181
  const axisOptions = (props, axisIndex) => {
29167
- const axisStep = props.tickStep[axisIndex];
29168
- const axisRange = props.range[axisIndex];
29169
29182
  return {
29170
29183
  axis: props.markings === "graph",
29171
29184
  lines: props.gridStep[axisIndex],
29172
- labels: n => lineLabelText(n, axisStep, axisRange)
29185
+ labels: false
29173
29186
  };
29174
29187
  };
29175
29188
  const Grid = props => {
@@ -30416,9 +30429,22 @@ const renderGraph = props => {
30416
30429
  return new wonderStuffCore.UnreachableCaseError(type);
30417
30430
  }
30418
30431
  };
30419
- const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
30420
- const [width, height] = props.box;
30432
+ const StatefulMafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
30421
30433
  const [state, dispatch] = React__namespace.useReducer(interactiveGraphReducer, props, initializeGraphState);
30434
+ React.useImperativeHandle(ref, () => ({
30435
+ getUserInput: () => getGradableGraph(state, props.graph)
30436
+ }));
30437
+ return /*#__PURE__*/React__namespace.createElement(MafsGraph, _extends({
30438
+ state: state,
30439
+ dispatch: dispatch
30440
+ }, props));
30441
+ });
30442
+ const MafsGraph = props => {
30443
+ const {
30444
+ state,
30445
+ dispatch
30446
+ } = props;
30447
+ const [width, height] = props.box;
30422
30448
  const prevState = React.useRef(state);
30423
30449
  React.useEffect(() => {
30424
30450
  if (prevState.current !== state) {
@@ -30433,16 +30459,13 @@ const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
30433
30459
  const [xSnap, ySnap] = props.snapStep;
30434
30460
  React.useEffect(() => {
30435
30461
  dispatch(changeSnapStep([xSnap, ySnap]));
30436
- }, [xSnap, ySnap]);
30462
+ }, [dispatch, xSnap, ySnap]);
30437
30463
 
30438
30464
  // Destructuring first to keep useEffect from making excess calls
30439
30465
  const [[xMinRange, xMaxRange], [yMinRange, yMaxRange]] = props.range;
30440
30466
  React.useEffect(() => {
30441
30467
  dispatch(changeRange([[xMinRange, xMaxRange], [yMinRange, yMaxRange]]));
30442
- }, [xMinRange, xMaxRange, yMinRange, yMaxRange]);
30443
- React__namespace.useImperativeHandle(ref, () => ({
30444
- getUserInput: () => getGradableGraph(state, props.graph)
30445
- }));
30468
+ }, [dispatch, xMinRange, xMaxRange, yMinRange, yMaxRange]);
30446
30469
  return /*#__PURE__*/React__namespace.createElement(GraphStateContext.Provider, {
30447
30470
  value: {
30448
30471
  state,
@@ -30452,7 +30475,9 @@ const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
30452
30475
  style: {
30453
30476
  width,
30454
30477
  height,
30455
- position: "relative"
30478
+ position: "relative",
30479
+ padding: "25px 25px 0 0",
30480
+ boxSizing: "content-box"
30456
30481
  }
30457
30482
  }, /*#__PURE__*/React__namespace.createElement(LegacyGrid, {
30458
30483
  box: props.box,
@@ -30486,7 +30511,7 @@ const MafsGraph = /*#__PURE__*/React__namespace.forwardRef((props, ref) => {
30486
30511
  state,
30487
30512
  dispatch
30488
30513
  })))));
30489
- });
30514
+ };
30490
30515
 
30491
30516
  const {
30492
30517
  DeprecationMixin
@@ -31762,7 +31787,7 @@ class InteractiveGraph extends React__namespace.Component {
31762
31787
  const box = getInteractiveBoxFromSizeClass(this.props.containerSizeClass);
31763
31788
  const gridStep = this.props.gridStep || Util.getGridStep(this.props.range, this.props.step, box[0]);
31764
31789
  const snapStep = this.props.snapStep || Util.snapStepFromGridStep(gridStep);
31765
- return /*#__PURE__*/React__namespace.createElement(MafsGraph, _extends({}, this.props, {
31790
+ return /*#__PURE__*/React__namespace.createElement(StatefulMafsGraph, _extends({}, this.props, {
31766
31791
  ref: this.mafsRef,
31767
31792
  gridStep: gridStep,
31768
31793
  snapStep: snapStep,
@@ -42493,7 +42518,7 @@ const init = function (options) {
42493
42518
 
42494
42519
  // This file is processed by a Rollup plugin (replace) to inject the production
42495
42520
  const libName = "@khanacademy/perseus";
42496
- const libVersion = "21.2.1";
42521
+ const libVersion = "21.3.0";
42497
42522
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
42498
42523
 
42499
42524
  const apiVersion = {