@khanacademy/perseus 36.0.0 → 36.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/dist/index.d.ts CHANGED
@@ -61,7 +61,7 @@ export { default as KhanColors } from "./util/colors";
61
61
  export { default as preprocessTex } from "./util/tex-preprocess";
62
62
  export { registerAllWidgetsForTesting } from "./util/register-all-widgets-for-testing";
63
63
  export * as SizingUtils from "./util/sizing-utils";
64
- export { getAnswersFromWidgets, injectWidgets, isWrongAnswerSupported, shouldHaveIndividualAnswer, isWidgetIdInContent, getValidWidgetIds, getCorrectAnswerForWidgetId, getAnswerFromUserInput, } from "./util/extract-perseus-data";
64
+ export { getAnswersFromWidgets, injectWidgets, isWrongAnswerSupported, shouldHaveIndividualAnswer, isWidgetIdInContent, getValidWidgetIds, getCorrectAnswerForWidgetId, getAnswerFromUserInput, getImagesWithoutAltData, } from "./util/extract-perseus-data";
65
65
  export { parsePerseusItem } from "./util/parse-perseus-json";
66
66
  export { generateTestPerseusItem, genericPerseusItemData, generateTestRadioWidget, generateTestInteractiveGraphWidget, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestNumericInputWidget, } from "./util/test-utils";
67
67
  export { getWidgetTypeByWidgetId, contentHasWidgetType, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetsMapFromItemData, getWidgetFromWidgetMap, getWidgetsFromWidgetMap, } from "./widget-type-utils";
package/dist/index.js CHANGED
@@ -33597,7 +33597,7 @@ function calculateBisectorPoint(point1, point2, vertex, isReflex, allowReflex, a
33597
33597
  return scaledBisector;
33598
33598
  }
33599
33599
 
33600
- const MovablePoint$1 = props => {
33600
+ const MovablePoint$1 = /*#__PURE__*/React__namespace.forwardRef((props, pointRef) => {
33601
33601
  const {
33602
33602
  snapStep
33603
33603
  } = useGraphConfig();
@@ -33620,7 +33620,21 @@ const MovablePoint$1 = props => {
33620
33620
  constrainKeyboardMovement: constrain
33621
33621
  });
33622
33622
  return /*#__PURE__*/React__namespace.createElement(MovablePointView, {
33623
- ref: elementRef,
33623
+ ref: ref => {
33624
+ // Note: This handles the case where pointRef is either a
33625
+ // function or a MutableRefObject. Mostly this is to satisfy
33626
+ // the type system, the function branch seems to be the only
33627
+ // one that occurs
33628
+ if (typeof pointRef === "function") {
33629
+ pointRef(ref);
33630
+ } else if (pointRef !== null) {
33631
+ pointRef.current = ref;
33632
+ }
33633
+
33634
+ // This ref is for the useDraggable and is not passed up the
33635
+ // chain
33636
+ elementRef.current = ref;
33637
+ },
33624
33638
  point: point,
33625
33639
  color: color,
33626
33640
  dragging: dragging,
@@ -33635,7 +33649,7 @@ const MovablePoint$1 = props => {
33635
33649
  },
33636
33650
  cursor: cursor
33637
33651
  });
33638
- };
33652
+ });
33639
33653
 
33640
33654
  const PolygonGraph = props => {
33641
33655
  const [hovered, setHovered] = React__namespace.useState(false);
@@ -34225,6 +34239,13 @@ function UnlimitedPointGraph(props) {
34225
34239
  const height = maxY - minY;
34226
34240
  const [[widthPx, heightPx]] = useTransformDimensionsToPixels([width, height]);
34227
34241
  const [[left, top]] = useTransformVectorsToPixels([minX, maxY]);
34242
+ const itemsRef = React__namespace.useRef([]);
34243
+ React__namespace.useEffect(() => {
34244
+ const focusedIndex = props.graphState.focusedPointIndex;
34245
+ if (focusedIndex != null) {
34246
+ itemsRef.current[focusedIndex]?.focus();
34247
+ }
34248
+ }, [props.graphState.focusedPointIndex, itemsRef]);
34228
34249
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("rect", {
34229
34250
  style: {
34230
34251
  fill: "rgba(0,0,0,0)",
@@ -34245,12 +34266,23 @@ function UnlimitedPointGraph(props) {
34245
34266
  key: i,
34246
34267
  point: point,
34247
34268
  onMove: destination => dispatch(actions.pointGraph.movePoint(i, destination)),
34269
+ ref: ref => {
34270
+ itemsRef.current[i] = ref;
34271
+ },
34248
34272
  onFocusChange: (event, isFocused) => {
34249
34273
  if (isFocused) {
34250
34274
  dispatch(actions.pointGraph.focusPoint(i));
34251
34275
  } else {
34252
34276
  if (event.relatedTarget?.id === REMOVE_BUTTON_ID) {
34253
34277
  return;
34278
+ // This is an optimization: If the next target
34279
+ // is a point then don't blur because it casues
34280
+ // the remove button to get taken off the page
34281
+ // and then put back on The new point will
34282
+ // receive focus and set the correct state in
34283
+ // the reducer
34284
+ } else if (event.relatedTarget?.classList.contains("movable-point")) {
34285
+ return;
34254
34286
  }
34255
34287
  dispatch(actions.pointGraph.blurPoint());
34256
34288
  }
@@ -34425,11 +34457,17 @@ const MafsGraph = props => {
34425
34457
  fullGraphAriaLabel,
34426
34458
  fullGraphAriaDescription
34427
34459
  } = props;
34460
+ const {
34461
+ type
34462
+ } = state;
34428
34463
  const [width, height] = props.box;
34429
34464
  const tickStep = props.step;
34430
34465
  const uniqueId = React__namespace.useId();
34431
34466
  const descriptionId = `interactive-graph-description-${uniqueId}`;
34432
34467
  const graphRef = React__namespace.useRef(null);
34468
+ const {
34469
+ analytics
34470
+ } = useDependencies();
34433
34471
 
34434
34472
  // Set up the SVG attributes for the nested SVGs that help lock
34435
34473
  // the grid and graph elements to the bounds of the graph.
@@ -34449,6 +34487,14 @@ const MafsGraph = props => {
34449
34487
  const {
34450
34488
  strings
34451
34489
  } = usePerseusI18n();
34490
+ wonderBlocksCore.useOnMountEffect(() => {
34491
+ analytics.onAnalyticsEvent({
34492
+ type: "perseus:interactive-graph-widget:rendered",
34493
+ payload: {
34494
+ type
34495
+ }
34496
+ });
34497
+ });
34452
34498
  return /*#__PURE__*/React__namespace.createElement(GraphConfigContext.Provider, {
34453
34499
  value: {
34454
34500
  range: state.range,
@@ -35233,11 +35279,14 @@ function doFocusPoint(state, action) {
35233
35279
  function doBlurPoint(state, action) {
35234
35280
  switch (state.type) {
35235
35281
  case "point":
35236
- return {
35282
+ const nextState = {
35237
35283
  ...state,
35238
- focusedPointIndex: null,
35239
35284
  showRemovePointButton: false
35240
35285
  };
35286
+ if (state.interactionMode === "mouse") {
35287
+ nextState.focusedPointIndex = null;
35288
+ }
35289
+ return nextState;
35241
35290
  default:
35242
35291
  return state;
35243
35292
  }
@@ -35640,17 +35689,24 @@ function doAddPoint(state, action) {
35640
35689
  ...state,
35641
35690
  hasBeenInteractedWith: true,
35642
35691
  coords: [...state.coords, snappedPoint],
35643
- showRemovePointButton: false
35692
+ showRemovePointButton: false,
35693
+ focusedPointIndex: state.coords.length
35644
35694
  };
35645
35695
  }
35646
35696
  function doRemovePoint(state, action) {
35647
35697
  if (state.type !== "point") {
35648
35698
  return state;
35649
35699
  }
35700
+ let nextFocusedPointIndex;
35701
+ if (state.interactionMode === "mouse") {
35702
+ nextFocusedPointIndex = null;
35703
+ } else {
35704
+ nextFocusedPointIndex = state.coords.length > 1 ? state.coords.length - 2 : null;
35705
+ }
35650
35706
  return {
35651
35707
  ...state,
35652
35708
  coords: state.coords.filter((_, i) => i !== action.index),
35653
- focusedPointIndex: null,
35709
+ focusedPointIndex: nextFocusedPointIndex,
35654
35710
  showRemovePointButton: false
35655
35711
  };
35656
35712
  }
@@ -45238,7 +45294,7 @@ const init = function (options) {
45238
45294
 
45239
45295
  // This file is processed by a Rollup plugin (replace) to inject the production
45240
45296
  const libName = "@khanacademy/perseus";
45241
- const libVersion = "36.0.0";
45297
+ const libVersion = "36.1.0";
45242
45298
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45243
45299
 
45244
45300
  const apiVersion = {
@@ -48426,6 +48482,38 @@ function injectWidgets(content, widgets, widgetProps) {
48426
48482
  return context;
48427
48483
  }
48428
48484
 
48485
+ /**
48486
+ * Looks through widgets to identify images that do not have alt text.
48487
+ *
48488
+ * We pass in the fuller perseusRenderer object to allow for the possibility
48489
+ * of expanding to include other img data from content & images in the future.
48490
+ *
48491
+ * @param {PerseusRenderer} perseusRenderer
48492
+ * @returns a stringified list of {widgetId, imageUrl} for imgs that don't have alt text
48493
+ */
48494
+ function getImagesWithoutAltData(perseusRenderer) {
48495
+ if (!perseusRenderer.widgets) {
48496
+ return "";
48497
+ }
48498
+ const imgsWithoutAltData = [];
48499
+ Object.entries(perseusRenderer.widgets).forEach(_ref3 => {
48500
+ let [widgetId, widget] = _ref3;
48501
+ if (!widget.options) {
48502
+ return;
48503
+ }
48504
+
48505
+ // Add to imagesWithoutAltData if img alt is missing and
48506
+ // image has valid img url data.
48507
+ if (widget.type === "image" && !widget.options.alt && widget.options.backgroundImage?.url) {
48508
+ imgsWithoutAltData.push({
48509
+ widgetId,
48510
+ imgUrl: widget.options.backgroundImage.url
48511
+ });
48512
+ }
48513
+ });
48514
+ return JSON.stringify(imgsWithoutAltData);
48515
+ }
48516
+
48429
48517
  /* Widgets that have individual answers */
48430
48518
  const INDIVIDUAL_ANSWER_WIDGETS = ["interactive-graph", "categorizer", "grapher"];
48431
48519
 
@@ -48758,6 +48846,7 @@ exports.getAnswerFromUserInput = getAnswerFromUserInput;
48758
48846
  exports.getAnswersFromWidgets = getAnswersFromWidgets;
48759
48847
  exports.getCircleCoords = getCircleCoords;
48760
48848
  exports.getCorrectAnswerForWidgetId = getCorrectAnswerForWidgetId;
48849
+ exports.getImagesWithoutAltData = getImagesWithoutAltData;
48761
48850
  exports.getLineCoords = getLineCoords;
48762
48851
  exports.getLinearSystemCoords = getLinearSystemCoords;
48763
48852
  exports.getPointCoords = getPointCoords;