@khanacademy/perseus 27.0.1 → 27.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.
@@ -27,7 +27,7 @@ type Props = {
27
27
  * Overrides deprecated `buttonSets` prop.
28
28
  */
29
29
  keypadButtonSets?: KeypadButtonSets;
30
- labelText?: string;
30
+ ariaLabel: string;
31
31
  onFocus?: () => void;
32
32
  onBlur?: () => void;
33
33
  hasError?: boolean;
@@ -83,6 +83,7 @@ declare class MathInput extends React.Component<Props, State> {
83
83
  }>;
84
84
  context: React.ContextType<typeof MathInputI18nContext>;
85
85
  inputRef: React.RefObject<InnerMathInput>;
86
+ static defaultProps: Pick<Props, "ariaLabel">;
86
87
  blur(): void;
87
88
  focus(): void;
88
89
  insert(value: any): void;
package/dist/es/index.css CHANGED
@@ -3329,6 +3329,7 @@ body.mobile .framework-perseus table.perseus-widget-table-of-values.non-markdown
3329
3329
  }
3330
3330
  .perseus-widget-container.widget-inline-block {
3331
3331
  display: inline-block;
3332
+ vertical-align: bottom;
3332
3333
  }
3333
3334
  .bibliotron-exercise .perseus-hint-renderer {
3334
3335
  border-left: 4px solid #f6f7f7;
package/dist/es/index.js CHANGED
@@ -5,18 +5,19 @@ import * as PerseusLinter from '@khanacademy/perseus-linter';
5
5
  import { linterContextDefault, Rule, linterContextProps } from '@khanacademy/perseus-linter';
6
6
  import { View, RenderStateRoot, UniqueIDProvider, useLatestRef, useUniqueIdWithMock, Text as Text$2 } from '@khanacademy/wonder-blocks-core';
7
7
  import Tooltip$1 from '@khanacademy/wonder-blocks-tooltip';
8
+ import { LabelSmall, Body, LabelMedium } from '@khanacademy/wonder-blocks-typography';
9
+ import { StyleSheet, css } from 'aphrodite';
8
10
  import classNames from 'classnames';
9
11
  import * as React from 'react';
10
12
  import React__default, { useContext, useState, useEffect, useRef, createContext, forwardRef, useImperativeHandle } from 'react';
13
+ import * as ReactDOM from 'react-dom';
14
+ import ReactDOM__default from 'react-dom';
11
15
  import _, { debounce as debounce$1 } from 'underscore';
12
16
  import Clickable from '@khanacademy/wonder-blocks-clickable';
13
17
  import { Popover, PopoverContentCore, PopoverContent } from '@khanacademy/wonder-blocks-popover';
14
18
  import { color, spacing } from '@khanacademy/wonder-blocks-tokens';
15
- import { StyleSheet, css } from 'aphrodite';
16
19
  import $ from 'jquery';
17
20
  import PropTypes from 'prop-types';
18
- import * as ReactDOM from 'react-dom';
19
- import ReactDOM__default from 'react-dom';
20
21
  import { number, point, vector as vector$1, line } from '@khanacademy/kmath';
21
22
  import { entries, UnreachableCaseError, keys } from '@khanacademy/wonder-stuff-core';
22
23
  import { CircularSpinner } from '@khanacademy/wonder-blocks-progress-spinner';
@@ -32,7 +33,6 @@ import caretUp from '@phosphor-icons/core/regular/caret-up.svg';
32
33
  import debounce$2 from 'lodash.debounce';
33
34
  import { vec, Coordinates, Ellipse, Plot as Plot$3, Line as Line$4, Point as Point$2, Polygon, useTransformContext, Text as Text$1, Mafs } from 'mafs';
34
35
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
35
- import { Body, LabelSmall, LabelMedium } from '@khanacademy/wonder-blocks-typography';
36
36
  import questionIcon from '@phosphor-icons/core/regular/question.svg';
37
37
  import createReactClass from 'create-react-class';
38
38
  import { useDrag } from '@use-gesture/react';
@@ -79,6 +79,7 @@ function _objectWithoutPropertiesLoose(source, excluded) {
79
79
  const mockStrings = {
80
80
  closeKeypad: "close math keypad",
81
81
  openKeypad: "open math keypad",
82
+ mathInputBox: "Math input box",
82
83
  removeHighlight: "Remove highlight",
83
84
  addHighlight: "Add highlight",
84
85
  hintPos: ({
@@ -337,6 +338,7 @@ class InnerMathInput extends React.Component {
337
338
  input == null || input.focus();
338
339
  };
339
340
  this.mathField = () => {
341
+ var _this$__mathField;
340
342
  if (!this.__mathField && this.__mathFieldWrapperRef) {
341
343
  const {
342
344
  locale
@@ -403,6 +405,7 @@ class InnerMathInput extends React.Component {
403
405
  }
404
406
  }));
405
407
  }
408
+ (_this$__mathField = this.__mathField) == null || _this$__mathField.setAriaLabel(this.props.ariaLabel);
406
409
  return this.__mathField;
407
410
  };
408
411
  this.focus = () => {
@@ -473,7 +476,7 @@ class InnerMathInput extends React.Component {
473
476
  className = className + " " + this.props.className;
474
477
  }
475
478
  return /*#__PURE__*/React.createElement(View, {
476
- style: [styles$D.outerWrapper, this.state.focused && styles$D.wrapperFocused, this.props.hasError && styles$D.wrapperError]
479
+ style: [styles$E.outerWrapper, this.state.focused && styles$E.wrapperFocused, this.props.hasError && styles$E.wrapperError]
477
480
  }, /*#__PURE__*/React.createElement("div", {
478
481
  style: {
479
482
  display: "flex",
@@ -495,19 +498,17 @@ class InnerMathInput extends React.Component {
495
498
  }, /*#__PURE__*/React.createElement("span", {
496
499
  className: className,
497
500
  ref: ref => this.__mathFieldWrapperRef = ref,
498
- "aria-label": this.props.labelText,
499
501
  onFocus: () => this.focus(),
500
502
  onBlur: () => this.blur()
501
503
  }), /*#__PURE__*/React.createElement(Popover, {
502
504
  opened: this.state.keypadOpen,
503
505
  onClose: () => this.closeKeypad(),
504
- portal: false,
505
506
  dismissEnabled: true,
506
507
  content: () => {
507
508
  var _this$props$keypadBut, _this$props;
508
509
  return /*#__PURE__*/React.createElement(PopoverContentCore, {
509
510
  closeButtonVisible: true,
510
- style: styles$D.popoverContent
511
+ style: styles$E.popoverContent
511
512
  }, /*#__PURE__*/React.createElement(DesktopKeypad, _extends({
512
513
  onAnalyticsEvent: this.props.analytics.onAnalyticsEvent,
513
514
  extraKeys: this.props.extraKeys,
@@ -563,6 +564,9 @@ class MathInput extends React.Component {
563
564
  }
564
565
  }
565
566
  MathInput.contextType = MathInputI18nContext;
567
+ MathInput.defaultProps = {
568
+ ariaLabel: "Math input"
569
+ };
566
570
  const MathInputIcon = ({
567
571
  hovered,
568
572
  focused,
@@ -580,9 +584,9 @@ const MathInputIcon = ({
580
584
  fillColor = color.offBlack;
581
585
  break;
582
586
  }
583
- const dynamicClass = active || focused ? styles$D.iconActive : styles$D.iconInactive;
587
+ const dynamicClass = active || focused ? styles$E.iconActive : styles$E.iconInactive;
584
588
  return /*#__PURE__*/React.createElement(View, {
585
- style: [styles$D.iconContainer, dynamicClass]
589
+ style: [styles$E.iconContainer, dynamicClass]
586
590
  }, /*#__PURE__*/React.createElement("svg", {
587
591
  xmlns: "http://www.w3.org/2000/svg",
588
592
  width: "16",
@@ -627,7 +631,7 @@ const inputFocused = {
627
631
  borderColor: color.blue,
628
632
  margin: -1
629
633
  };
630
- const styles$D = StyleSheet.create({
634
+ const styles$E = StyleSheet.create({
631
635
  iconContainer: {
632
636
  display: "flex",
633
637
  justifyContent: "center",
@@ -2151,6 +2155,7 @@ const getDecimalSeparator = locale => {
2151
2155
  class Expression extends React.Component {
2152
2156
  constructor(..._args) {
2153
2157
  super(..._args);
2158
+ this._textareaId = `expression_textarea_${Date.now()}`;
2154
2159
  this._isMounted = false;
2155
2160
  this.displayName = "Expression";
2156
2161
  this.state = {
@@ -2164,6 +2169,17 @@ class Expression extends React.Component {
2164
2169
  // TODO(scottgrant): This is a hack to remove the deprecated call to
2165
2170
  // this.isMounted() but is still considered an anti-pattern.
2166
2171
  this._isMounted = true;
2172
+
2173
+ // HACK: imperatively add an ID onto the Mathquill input
2174
+ // (which in mobile is a span; desktop a textarea)
2175
+ // in order to associate a visual label with it
2176
+ if (this.refs.input) {
2177
+ const isMobile = this.props.apiOptions.customKeypad;
2178
+ const container = ReactDOM__default.findDOMNode(this.refs.input);
2179
+ const selector = isMobile ? ".mq-textarea > span" : "textarea";
2180
+ const inputElement = container.querySelector(selector);
2181
+ inputElement == null || inputElement.setAttribute("id", this._textareaId);
2182
+ }
2167
2183
  };
2168
2184
  // Whenever the input value changes, attempt to parse it.
2169
2185
  //
@@ -2458,10 +2474,16 @@ class Expression extends React.Component {
2458
2474
  render() {
2459
2475
  var _this$props$keypadCon, _this$props$analytics3;
2460
2476
  if (this.props.apiOptions.customKeypad) {
2461
- return /*#__PURE__*/React.createElement(KeypadInput
2477
+ return /*#__PURE__*/React.createElement(View, {
2478
+ className: css(styles$D.mobileLabelInputWrapper)
2479
+ }, !!this.props.visibleLabel && /*#__PURE__*/React.createElement(LabelSmall, {
2480
+ htmlFor: this._textareaId,
2481
+ tag: "label"
2482
+ }, this.props.visibleLabel), /*#__PURE__*/React.createElement(KeypadInput
2462
2483
  // eslint-disable-next-line react/no-string-refs
2463
2484
  , {
2464
2485
  ref: "input",
2486
+ ariaLabel: this.props.ariaLabel || this.context.strings.mathInputBox,
2465
2487
  value: this.props.value,
2466
2488
  keypadElement: this.props.keypadElement,
2467
2489
  onChange: this.changeAndTrack,
@@ -2477,7 +2499,7 @@ class Expression extends React.Component {
2477
2499
  });
2478
2500
  },
2479
2501
  onBlur: this._handleBlur
2480
- });
2502
+ }));
2481
2503
  }
2482
2504
  const className = classNames({
2483
2505
  "perseus-widget-expression": true,
@@ -2487,7 +2509,12 @@ class Expression extends React.Component {
2487
2509
  ERROR_MESSAGE,
2488
2510
  ERROR_TITLE
2489
2511
  } = this.context.strings;
2490
- return /*#__PURE__*/React.createElement("div", {
2512
+ return /*#__PURE__*/React.createElement(View, {
2513
+ className: css(styles$D.desktopLabelInputWrapper)
2514
+ }, !!this.props.visibleLabel && /*#__PURE__*/React.createElement(LabelSmall, {
2515
+ htmlFor: this._textareaId,
2516
+ tag: "label"
2517
+ }, this.props.visibleLabel), /*#__PURE__*/React.createElement("div", {
2491
2518
  className: className,
2492
2519
  onBlur: () => this.state.invalid && this.setState({
2493
2520
  showErrorTooltip: true,
@@ -2516,13 +2543,33 @@ class Expression extends React.Component {
2516
2543
  onFocus: this._handleFocus,
2517
2544
  onBlur: this._handleBlur,
2518
2545
  hasError: this.state.showErrorStyle,
2546
+ ariaLabel: this.props.ariaLabel || this.context.strings.mathInputBox,
2519
2547
  extraKeys: (_this$props$keypadCon = this.props.keypadConfiguration) == null ? void 0 : _this$props$keypadCon.extraKeys,
2520
2548
  analytics: (_this$props$analytics3 = this.props.analytics) != null ? _this$props$analytics3 : {
2521
2549
  onAnalyticsEvent: async function () {}
2522
2550
  }
2523
- })));
2551
+ }))));
2524
2552
  }
2525
2553
  }
2554
+ Expression.contextType = PerseusI18nContext;
2555
+ Expression.defaultProps = {
2556
+ value: "",
2557
+ times: false,
2558
+ functions: [],
2559
+ buttonSets: ["basic", "trig", "prealgebra", "logarithms"],
2560
+ onFocus: () => {},
2561
+ onBlur: () => {},
2562
+ apiOptions: ApiOptions.defaults,
2563
+ linterContext: linterContextDefault
2564
+ };
2565
+ const styles$D = StyleSheet.create({
2566
+ mobileLabelInputWrapper: {
2567
+ padding: "15px 4px 0"
2568
+ },
2569
+ desktopLabelInputWrapper: {
2570
+ margin: "5px 5px 0"
2571
+ }
2572
+ });
2526
2573
 
2527
2574
  /**
2528
2575
  * Determine the keypad configuration parameters for the input, based on the
@@ -2535,17 +2582,6 @@ class Expression extends React.Component {
2535
2582
  * to be included as keys on the keypad. These are scraped from the answer
2536
2583
  * forms.
2537
2584
  */
2538
- Expression.contextType = PerseusI18nContext;
2539
- Expression.defaultProps = {
2540
- value: "",
2541
- times: false,
2542
- functions: [],
2543
- buttonSets: ["basic", "trig", "prealgebra", "logarithms"],
2544
- onFocus: () => {},
2545
- onBlur: () => {},
2546
- apiOptions: ApiOptions.defaults,
2547
- linterContext: linterContextDefault
2548
- };
2549
2585
  const keypadConfigurationForProps = widgetOptions => {
2550
2586
  // Always use the Expression keypad, regardless of the button sets that have
2551
2587
  // been enabled.
@@ -2603,6 +2639,8 @@ const propUpgrades$2 = {
2603
2639
  buttonSets: v0props.buttonSets,
2604
2640
  functions: v0props.functions,
2605
2641
  buttonsVisible: v0props.buttonsVisible,
2642
+ visibleLabel: v0props.visibleLabel,
2643
+ ariaLabel: v0props.ariaLabel,
2606
2644
  answerForms: [{
2607
2645
  considered: "correct",
2608
2646
  form: v0props.form,
@@ -2640,14 +2678,18 @@ var Expression$1 = {
2640
2678
  times,
2641
2679
  functions,
2642
2680
  buttonSets,
2643
- buttonsVisible
2681
+ buttonsVisible,
2682
+ visibleLabel,
2683
+ ariaLabel
2644
2684
  } = widgetOptions;
2645
2685
  return {
2646
2686
  keypadConfiguration: keypadConfigurationForProps(widgetOptions),
2647
2687
  times,
2648
2688
  functions,
2649
2689
  buttonSets,
2650
- buttonsVisible
2690
+ buttonsVisible,
2691
+ visibleLabel,
2692
+ ariaLabel
2651
2693
  };
2652
2694
  },
2653
2695
  version: {
@@ -32096,16 +32138,17 @@ function getQuadraticCoords(graph, range, step) {
32096
32138
  return normalizePoints(range, step, defaultCoords, true);
32097
32139
  }
32098
32140
  function getCircleCoords(graph) {
32141
+ var _graph$startCoords3;
32099
32142
  if (graph.center != null && graph.radius != null) {
32100
32143
  return {
32101
32144
  center: graph.center,
32102
32145
  radiusPoint: vec.add(graph.center, [graph.radius, 0])
32103
32146
  };
32104
32147
  }
32105
- if (graph.startCoords) {
32148
+ if ((_graph$startCoords3 = graph.startCoords) != null && _graph$startCoords3.center && graph.startCoords.radius) {
32106
32149
  return {
32107
- center: graph.startCoords,
32108
- radiusPoint: vec.add(graph.startCoords, [2, 0])
32150
+ center: graph.startCoords.center,
32151
+ radiusPoint: vec.add(graph.startCoords.center, [graph.startCoords.radius, 0])
32109
32152
  };
32110
32153
  }
32111
32154
  return {
@@ -44115,7 +44158,7 @@ const init = function init(options) {
44115
44158
 
44116
44159
  // This file is processed by a Rollup plugin (replace) to inject the production
44117
44160
  const libName = "@khanacademy/perseus";
44118
- const libVersion = "27.0.1";
44161
+ const libVersion = "27.1.0";
44119
44162
  addLibraryVersionToPerseusDebug(libName, libVersion);
44120
44163
 
44121
44164
  const apiVersion = {
@@ -47695,5 +47738,5 @@ const EditorJsonify = {
47695
47738
  }
47696
47739
  };
47697
47740
 
47698
- export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getSegmentCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
47741
+ export { ApiOptions, ArrowPicker, ArticleRenderer, BaseRadio, Categorizer$1 as Categorizer, changeable as Changeable, ClassNames, ColorPicker, DashPicker, dependencies as Dependencies, EditorJsonify, ElementContainer, Expression, util as GrapherUtil, Grapher$1 as GrapherWidget, HintRenderer, HintsRenderer, InputNumber$1 as InputNumber, InteractiveGraph$1 as InteractiveGraphWidget, ItemExtras, JiptParagraphs, KhanColors, KhanMath, context as LoadingContext, Log, Matrix$1 as MatrixWidget, MultiItems, NumericInput$1 as NumericInput, PerseusExpressionAnswerFormConsidered, PerseusI18nContext, PerseusI18nContextProvider, PerseusMarkdown, Plotter$1 as PlotterWidget, QUESTION_WIDGETS, Radio, Renderer, ref as ServerItemRenderer, sizingUtils as SizingUtils, Table$1 as TableWidget, Util, denylist as WIDGET_PROP_DENYLIST, WidgetJsonifyDeprecated, widgets$1 as Widgets, addWidget, apiVersion, buildEmptyItemTreeForShape, buildMapper, components, constants, contentHasWidgetType, convertWidgetNameToEnum, displaySigFigs, generateTestCategorizerWidget, generateTestExpressionWidget, generateTestInteractiveGraphWidget, generateTestNumericInputWidget, generateTestPerseusItem, generateTestRadioWidget, genericPerseusItemData, getAnswerFromUserInput, getAnswersFromWidgets, getCircleCoords, getCorrectAnswerForWidgetId, getLineCoords, getLinearSystemCoords, getSegmentCoords, getValidWidgetIds, getWidgetFromWidgetMap, getWidgetIdsFromContent, getWidgetIdsFromContentByType, getWidgetTypeByWidgetId, getWidgetsFromWidgetMap, getWidgetsMapFromItemData, globalConstants, globalStyles, iconPaths as icons, init, injectWidgets, interactiveSizes$1 as interactiveSizes, isItemRenderableByVersion, isWidgetIdInContent, isWrongAnswerSupported, itemDataVersion, itemToTree, ItemVersion as itemVersion, libVersion, lockedFigureColors, lockedFigureFillStyles, mediaQueries, parsePerseusItem, plotterPlotTypes, preprocessTex, registerAllWidgetsForTesting, shapes, shouldHaveIndividualAnswer, traverse, usePerseusI18n, violatingWidgets, allWidgets as widgets };
47699
47742
  //# sourceMappingURL=index.js.map