diff --git a/packages/perseus-editor/src/widgets/passage-editor.tsx b/packages/perseus-editor/src/widgets/passage-editor.tsx
index f2d913b34d..c0755a1b32 100644
--- a/packages/perseus-editor/src/widgets/passage-editor.tsx
+++ b/packages/perseus-editor/src/widgets/passage-editor.tsx
@@ -1,11 +1,12 @@
import {components, Changeable, EditorJsonify} from "@khanacademy/perseus";
+import {Checkbox} from "@khanacademy/wonder-blocks-form";
import PropTypes from "prop-types";
import * as React from "react";
import _ from "underscore";
import Editor from "../editor";
-const {InfoTip, PropCheckBox} = components;
+const {InfoTip} = components;
type Props = any;
@@ -68,11 +69,12 @@ class PassageEditor extends React.Component {
return (
-
{
+ this.props.onChange({showLineNumbers: value});
+ }}
/>
diff --git a/packages/perseus-editor/src/widgets/radio/editor.tsx b/packages/perseus-editor/src/widgets/radio/editor.tsx
index f106d66c9b..fc50cefad7 100644
--- a/packages/perseus-editor/src/widgets/radio/editor.tsx
+++ b/packages/perseus-editor/src/widgets/radio/editor.tsx
@@ -6,13 +6,14 @@ import {
BaseRadio,
Changeable,
} from "@khanacademy/perseus";
+import {Checkbox} from "@khanacademy/wonder-blocks-form";
import PropTypes from "prop-types";
import * as React from "react";
import _ from "underscore";
import Editor from "../../editor";
-const {InlineIcon, PropCheckBox} = components;
+const {InlineIcon} = components;
const {iconPlus, iconTrash} = icons;
class ChoiceEditor extends React.Component
{
@@ -150,28 +151,35 @@ class RadioEditor extends React.Component {
-
{
+ this.onMultipleSelectChange({
+ multipleSelect: value,
+ });
+ }}
/>
-
{
+ this.props.onChange({randomize: value});
+ }}
/>
{this.props.multipleSelect && (
-
{
+ this.onCountChoicesChange({
+ countChoices: value,
+ });
+ }}
/>
)}
diff --git a/packages/perseus-editor/src/widgets/sorter-editor.tsx b/packages/perseus-editor/src/widgets/sorter-editor.tsx
index 4aed056341..6840b62243 100644
--- a/packages/perseus-editor/src/widgets/sorter-editor.tsx
+++ b/packages/perseus-editor/src/widgets/sorter-editor.tsx
@@ -1,11 +1,12 @@
/* eslint-disable @khanacademy/ts-no-error-suppressions */
/* eslint-disable react/forbid-prop-types */
import {components} from "@khanacademy/perseus";
+import {Checkbox} from "@khanacademy/wonder-blocks-form";
import PropTypes from "prop-types";
import * as React from "react";
import _ from "underscore";
-const {InfoTip, PropCheckBox, TextListEditor} = components;
+const {InfoTip, TextListEditor} = components;
const HORIZONTAL = "horizontal";
const VERTICAL = "vertical";
@@ -84,10 +85,12 @@ class SorterEditor extends React.Component {
-
{
+ this.props.onChange({padding: value});
+ }}
/>
diff --git a/packages/perseus/package.json b/packages/perseus/package.json
index d71e3435b3..98bd0801cd 100644
--- a/packages/perseus/package.json
+++ b/packages/perseus/package.json
@@ -47,7 +47,8 @@
"@khanacademy/pure-markdown": "^0.3.7",
"@khanacademy/simple-markdown": "^0.13.0",
"@use-gesture/react": "^10.2.27",
- "mafs": "0.19.0"
+ "mafs": "0.19.0",
+ "uuid": "^10.0.0"
},
"devDependencies": {
"@khanacademy/wonder-blocks-banner": "3.0.42",
@@ -61,7 +62,7 @@
"@khanacademy/wonder-blocks-layout": "2.0.32",
"@khanacademy/wonder-blocks-link": "6.1.1",
"@khanacademy/wonder-blocks-pill": "2.2.1",
- "@khanacademy/wonder-blocks-popover": "3.2.9",
+ "@khanacademy/wonder-blocks-popover": "3.2.11",
"@khanacademy/wonder-blocks-progress-spinner": "2.1.1",
"@khanacademy/wonder-blocks-switch": "1.1.16",
"@khanacademy/wonder-blocks-tokens": "1.3.0",
@@ -95,7 +96,7 @@
"@khanacademy/wonder-blocks-layout": "2.0.32",
"@khanacademy/wonder-blocks-link": "6.1.1",
"@khanacademy/wonder-blocks-pill": "2.2.1",
- "@khanacademy/wonder-blocks-popover": "3.2.9",
+ "@khanacademy/wonder-blocks-popover": "3.2.11",
"@khanacademy/wonder-blocks-progress-spinner": "2.1.1",
"@khanacademy/wonder-blocks-switch": "1.1.16",
"@khanacademy/wonder-blocks-tokens": "1.3.0",
diff --git a/packages/perseus/src/__stories__/server-item-renderer.stories.tsx b/packages/perseus/src/__stories__/server-item-renderer.stories.tsx
index 7599ed3894..d3c92a9fee 100644
--- a/packages/perseus/src/__stories__/server-item-renderer.stories.tsx
+++ b/packages/perseus/src/__stories__/server-item-renderer.stories.tsx
@@ -13,11 +13,11 @@ import {
} from "../__testdata__/server-item-renderer.testdata";
import {ServerItemRenderer} from "../server-item-renderer";
-type StoryArgs = Record;
+import type {StoryObj, Meta} from "@storybook/react";
-type Story = {
- title: string;
-};
+type StoryArgs = StoryObj;
+
+type Story = Meta;
export default {
title: "Perseus/Renderers/Server Item Renderer",
diff --git a/packages/perseus/src/components.ts b/packages/perseus/src/components.ts
index 9c529c7159..ca12de1252 100644
--- a/packages/perseus/src/components.ts
+++ b/packages/perseus/src/components.ts
@@ -8,7 +8,6 @@ export {default as InlineIcon} from "./components/inline-icon";
export {default as MathInput} from "./components/math-input";
export {default as MultiButtonGroup} from "./components/multi-button-group";
export {default as NumberInput} from "./components/number-input";
-export {default as PropCheckBox} from "./components/prop-check-box";
export {default as RangeInput} from "./components/range-input";
export {default as SvgImage} from "./components/svg-image";
export {default as TextInput} from "./components/text-input";
diff --git a/packages/perseus/src/components/__stories__/button-group.stories.tsx b/packages/perseus/src/components/__stories__/button-group.stories.tsx
index 39350d209e..6388d80164 100644
--- a/packages/perseus/src/components/__stories__/button-group.stories.tsx
+++ b/packages/perseus/src/components/__stories__/button-group.stories.tsx
@@ -2,11 +2,11 @@ import * as React from "react";
import ButtonGroup from "../button-group";
-type StoryArgs = Record;
+import type {Meta, StoryObj} from "@storybook/react";
-type Story = {
- title: string;
-};
+type StoryArgs = StoryObj;
+
+type Story = Meta;
export default {
title: "Perseus/Components/Button Group",
diff --git a/packages/perseus/src/components/__stories__/fixed-to-responsive.stories.tsx b/packages/perseus/src/components/__stories__/fixed-to-responsive.stories.tsx
index 632826339a..9619fccf15 100644
--- a/packages/perseus/src/components/__stories__/fixed-to-responsive.stories.tsx
+++ b/packages/perseus/src/components/__stories__/fixed-to-responsive.stories.tsx
@@ -3,11 +3,11 @@ import * as React from "react";
import {getDependencies} from "../../dependencies";
import FixedToResponsive from "../fixed-to-responsive";
-type StoryArgs = Record;
+import type {Meta, StoryObj} from "@storybook/react";
-type Story = {
- title: string;
-};
+type StoryArgs = StoryObj;
+
+type Story = Meta;
const svgUrl = "https://www.khanacademy.org/images/ohnoes-concerned.svg";
const imgUrl = "https://www.khanacademy.org/images/hand-tree.new.png";
diff --git a/packages/perseus/src/components/__stories__/graph.stories.tsx b/packages/perseus/src/components/__stories__/graph.stories.tsx
index 92ed37d60f..5215c417ce 100644
--- a/packages/perseus/src/components/__stories__/graph.stories.tsx
+++ b/packages/perseus/src/components/__stories__/graph.stories.tsx
@@ -2,11 +2,11 @@ import * as React from "react";
import Graph from "../graph";
-type StoryArgs = Record;
+import type {StoryObj, Meta} from "@storybook/react";
-type Story = {
- title: string;
-};
+type StoryArgs = StoryObj;
+
+type Story = Meta;
const size = 200;
diff --git a/packages/perseus/src/components/__stories__/graphie.stories.tsx b/packages/perseus/src/components/__stories__/graphie.stories.tsx
index e6c31d8c66..d0ee574c36 100644
--- a/packages/perseus/src/components/__stories__/graphie.stories.tsx
+++ b/packages/perseus/src/components/__stories__/graphie.stories.tsx
@@ -4,11 +4,11 @@ import {ServerItemRendererWithDebugUI} from "../../../../../testing/server-item-
import {itemWithPieChart} from "../../__testdata__/graphie.testdata";
import Graphie from "../graphie";
-type StoryArgs = Record;
+import type {StoryObj, Meta} from "@storybook/react";
-type Story = {
- title: string;
-};
+type StoryArgs = StoryObj;
+
+type Story = Meta;
const size = 200;
diff --git a/packages/perseus/src/components/__stories__/hud.stories.tsx b/packages/perseus/src/components/__stories__/hud.stories.tsx
index aa657e9baf..cc3a402c76 100644
--- a/packages/perseus/src/components/__stories__/hud.stories.tsx
+++ b/packages/perseus/src/components/__stories__/hud.stories.tsx
@@ -2,11 +2,11 @@ import * as React from "react";
import Hud from "../hud";
-type StoryArgs = Record;
+import type {StoryObj, Meta} from "@storybook/react";
-type Story = {
- title: string;
-};
+type StoryArgs = StoryObj;
+
+type Story = Meta;
export default {
title: "Perseus/Components/HUD",
diff --git a/packages/perseus/src/components/__stories__/icon.stories.tsx b/packages/perseus/src/components/__stories__/icon.stories.tsx
index c6f63d2d7e..08cbd36e90 100644
--- a/packages/perseus/src/components/__stories__/icon.stories.tsx
+++ b/packages/perseus/src/components/__stories__/icon.stories.tsx
@@ -3,36 +3,11 @@ import * as React from "react";
import * as IconPaths from "../../icon-paths";
import IconComponent from "../icon";
-type StorybookStoryArgs = {
- options?: ReadonlyArray;
- mapping?: {
- [value: string]: any;
- };
- defaultValue?: string;
- control?: string;
-};
+import type {StoryObj, Meta} from "@storybook/react";
-type StoryArgs = {
- color?: string;
- size?: number;
- title?: string;
- icon?: typeof IconPaths.iconCheck;
-};
+type StoryArgs = StoryObj;
-type SetValueType = {
- [Property in keyof T]: V;
-};
-
-type StoryArgTypes = SetValueType<
- StoryArgs,
- StorybookStoryArgs | null | undefined
->;
-
-type Story = {
- title: string;
- args?: StoryArgs;
- argTypes: StoryArgTypes;
-};
+type Story = Meta;
export default {
title: "Perseus/Components",
diff --git a/packages/perseus/src/components/__stories__/prop-check-box.stories.tsx b/packages/perseus/src/components/__stories__/prop-check-box.stories.tsx
deleted file mode 100644
index 09eed8aa89..0000000000
--- a/packages/perseus/src/components/__stories__/prop-check-box.stories.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import * as React from "react";
-
-import PropCheckBox from "../prop-check-box";
-
-type StoryArgs = Record;
-
-type Story = {
- title: string;
-};
-
-export default {
- title: "Perseus/Components/Prop Check Box",
-} as Story;
-
-export const TestLabelWithCheckedObject = (
- args: StoryArgs,
-): React.ReactElement => {
- return (
- {}}
- labelAlignment="left"
- />
- );
-};
-
-export const TestLabelWithUncheckedObject = (
- args: StoryArgs,
-): React.ReactElement => {
- return (
- {}}
- labelAlignment="left"
- />
- );
-};
-
-export const TestLabelWithCheckedObjectLabelOnTheRight = (
- args: StoryArgs,
-): React.ReactElement => {
- return (
- {}}
- labelAlignment="right"
- />
- );
-};
-
-export const TestLabelWithUncheckedObjectLabelOnTheRight = (
- args: StoryArgs,
-): React.ReactElement => {
- return (
- {}}
- labelAlignment="right"
- />
- );
-};
diff --git a/packages/perseus/src/components/math-input.tsx b/packages/perseus/src/components/math-input.tsx
index f606ef092f..88623e296c 100644
--- a/packages/perseus/src/components/math-input.tsx
+++ b/packages/perseus/src/components/math-input.tsx
@@ -13,12 +13,15 @@ import Clickable from "@khanacademy/wonder-blocks-clickable";
import {View} from "@khanacademy/wonder-blocks-core";
import {Popover, PopoverContentCore} from "@khanacademy/wonder-blocks-popover";
import {color, spacing} from "@khanacademy/wonder-blocks-tokens";
+import {HeadingMedium} from "@khanacademy/wonder-blocks-typography";
import {StyleSheet} from "aphrodite";
import classNames from "classnames";
import $ from "jquery";
import * as React from "react";
import _ from "underscore";
+import {v4 as uuid} from "uuid";
+import a11y from "../util/a11y";
import {debounce} from "../util/debounce";
import {PerseusI18nContext} from "./i18n-context";
@@ -288,6 +291,8 @@ class InnerMathInput extends React.Component {
"mq-math-mode": true,
});
+ const popoverContentUniqueId = uuid().slice(0, 8);
+
if (this.props.className) {
className = className + " " + this.props.className;
}
@@ -330,25 +335,38 @@ class InnerMathInput extends React.Component {
opened={this.state.keypadOpen}
onClose={() => this.closeKeypad()}
dismissEnabled
+ aria-label={this.context.strings.mathInputTitle}
+ aria-describedby={`popover-content-${popoverContentUniqueId}`}
content={() => (
-
-
-
+ <>
+
+ {this.context.strings.mathInputDescription}
+
+
+
+
+ >
)}
>
{this.props.buttonsVisible === "never" ? (
diff --git a/packages/perseus/src/components/prop-check-box.tsx b/packages/perseus/src/components/prop-check-box.tsx
deleted file mode 100644
index 13380aef6d..0000000000
--- a/packages/perseus/src/components/prop-check-box.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-/* eslint-disable @babel/no-invalid-this */
-/* eslint-disable react/sort-comp */
-import {Errors, PerseusError} from "@khanacademy/perseus-core";
-import {Checkbox} from "@khanacademy/wonder-blocks-form";
-import {LabelSmall} from "@khanacademy/wonder-blocks-typography";
-import {css, StyleSheet} from "aphrodite";
-import createReactClass from "create-react-class";
-import PropTypes from "prop-types";
-import * as React from "react";
-import _ from "underscore";
-
-/* A checkbox that syncs its value to props using the
- * renderer's onChange method, and gets the prop name
- * dynamically from its props list
- */
-const PropCheckBox: any = createReactClass({
- displayName: "PropCheckBox",
-
- propTypes: {
- labelAlignment: PropTypes.oneOf(["left", "right"]),
- },
-
- DEFAULT_PROPS: {
- label: null,
- onChange: null,
- labelAlignment: "left",
- },
-
- getDefaultProps: function () {
- return this.DEFAULT_PROPS;
- },
-
- propName: function () {
- const propName = _.find(
- _.keys(this.props),
- function (localPropName) {
- // @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
- return !_.has(this.DEFAULT_PROPS, localPropName);
- },
- this,
- );
-
- if (!propName) {
- throw new PerseusError(
- "Attempted to create a PropCheckBox with no prop!",
- Errors.InvalidInput,
- );
- }
-
- return propName;
- },
-
- _labelAlignLeft: function () {
- return this.props.labelAlignment === "left";
- },
-
- render: function () {
- const propName = this.propName();
- return (
-
- );
- },
-
- toggle: function () {
- const propName = this.propName();
- const changes: Record = {};
- changes[propName] = !this.props[propName];
- this.props.onChange(changes);
- },
-});
-
-export const styles = StyleSheet.create({
- labeledCheckbox: {
- display: "flex",
- flexDirection: "row",
- alignItems: "center",
- },
-});
-
-export default PropCheckBox;
diff --git a/packages/perseus/src/hints-renderer.tsx b/packages/perseus/src/hints-renderer.tsx
index 7e4248d537..8cf5145039 100644
--- a/packages/perseus/src/hints-renderer.tsx
+++ b/packages/perseus/src/hints-renderer.tsx
@@ -20,15 +20,14 @@ import mediaQueries from "./styles/media-queries";
import sharedStyles from "./styles/shared";
import Util from "./util";
+import type {Hint} from "./perseus-types";
import type Renderer from "./renderer";
import type {APIOptionsWithDefaults} from "./types";
import type {PropsFor} from "@khanacademy/wonder-blocks-core";
type Props = PropsFor & {
className?: string;
- // note (mcurtis): I think this should be $ReadOnlyArray,
- // but things spiraled out of control when I tried to change it
- hints: ReadonlyArray;
+ hints: ReadonlyArray;
hintsVisible?: number;
};
diff --git a/packages/perseus/src/index.ts b/packages/perseus/src/index.ts
index 775d13241b..0952b5f37a 100644
--- a/packages/perseus/src/index.ts
+++ b/packages/perseus/src/index.ts
@@ -122,9 +122,12 @@ export {
getCircleCoords,
getLineCoords,
getLinearSystemCoords,
+ getPointCoords,
+ getPolygonCoords,
getSegmentCoords,
getSinusoidCoords,
getQuadraticCoords,
+ getAngleCoords,
} from "./widgets/interactive-graphs/reducer/initialize-graph-state";
/**
@@ -150,7 +153,6 @@ export type {
DomInsertCheckFn,
EditorMode,
FocusPath,
- Hint,
ImageDict,
ImageUploader,
JiptLabelStore,
@@ -165,6 +167,7 @@ export type {
} from "./types";
export type {ParsedValue} from "./util";
export type {
+ Hint,
LockedFigure,
LockedFigureColor,
LockedFigureFillType,
diff --git a/packages/perseus/src/multi-items/multi-renderer.tsx b/packages/perseus/src/multi-items/multi-renderer.tsx
index f20f5f6615..f29e28d655 100644
--- a/packages/perseus/src/multi-items/multi-renderer.tsx
+++ b/packages/perseus/src/multi-items/multi-renderer.tsx
@@ -319,6 +319,14 @@ class MultiRenderer extends React.Component {
),
diff --git a/packages/perseus/src/perseus-types.ts b/packages/perseus/src/perseus-types.ts
index 85d2d94111..e3582b2ca7 100644
--- a/packages/perseus/src/perseus-types.ts
+++ b/packages/perseus/src/perseus-types.ts
@@ -87,7 +87,7 @@ export type PerseusItem = {
// The details of the question being asked to the user.
question: PerseusRenderer;
// A collection of hints to be offered to the user that support answering the question.
- hints: ReadonlyArray;
+ hints: ReadonlyArray;
// Details about the tools the user might need to answer the question
answerArea: PerseusAnswerArea | null | undefined;
// Multi-item should only show up in Test Prep content and it is a variant of a PerseusItem
@@ -116,8 +116,6 @@ export type PerseusRenderer = {
content: string;
// A dictionary of {[widgetName]: Widget} to be referenced from the content field
widgets: PerseusWidgetsMap;
- // Used only for PerseusItem.hints. If true, it replaces the previous hint in the list with the current one. This allows for hints that build upon each other.
- replace?: boolean;
// Used in the PerseusGradedGroup widget. A list of "tags" that are keys that represent other content in the system. Not rendered to the user.
// NOTE: perseus_data.go says this is required even though it isn't necessary.
metadata?: ReadonlyArray;
@@ -127,6 +125,15 @@ export type PerseusRenderer = {
};
};
+export type Hint = PerseusRenderer & {
+ /**
+ * When `true`, causes the previous hint to be replaced with this hint when
+ * displayed. When `false`, the previous hint remains visible when this one
+ * is displayed. This allows for hints that build upon each other.
+ */
+ replace?: boolean;
+};
+
export type PerseusImageDetail = {
// The width of the image
width: number;
@@ -754,14 +761,6 @@ export type LockedFunctionType = {
color: LockedFigureColor;
strokeStyle: "solid" | "dashed";
equation: string; // This is the user-defined equation (as it was typed)
- equationParsed?: {
- // This is the parsed (tokenized) version of the equation.
- // Since the function that is passed to Mafs is executed many times,
- // it would be expensive to have KAS parse the equation each time.
- // This is parsed version is included to aid in performance.
- // KAS doesn't have any types, so making this generic
- [k: string]: any;
- };
directionalAxis: "x" | "y";
domain?: Interval;
};
diff --git a/packages/perseus/src/renderer.tsx b/packages/perseus/src/renderer.tsx
index f84f5bbf04..47458d6b59 100644
--- a/packages/perseus/src/renderer.tsx
+++ b/packages/perseus/src/renderer.tsx
@@ -47,6 +47,7 @@ import type {
PerseusScore,
WidgetProps,
} from "./types";
+import type {KeypadAPI} from "@khanacademy/math-input";
import type {LinterContextProps} from "@khanacademy/perseus-linter";
import "./styles/perseus-renderer.less";
@@ -169,7 +170,7 @@ type Props = Partial> & {
findExternalWidgets: any;
highlightedWidgets?: ReadonlyArray;
images: PerseusRenderer["images"];
- keypadElement?: any; // TODO(kevinb): add proper types,
+ keypadElement?: KeypadAPI | null;
onInteractWithWidget: (id: string) => void;
onRender: (node?: any) => void;
problemNum?: number;
diff --git a/packages/perseus/src/strings.ts b/packages/perseus/src/strings.ts
index e2027b91cf..41c35574ea 100644
--- a/packages/perseus/src/strings.ts
+++ b/packages/perseus/src/strings.ts
@@ -123,6 +123,8 @@ export type PerseusStrings = {
videoTranscript: string;
somethingWrong: string;
videoWrapper: string;
+ mathInputTitle: string;
+ mathInputDescription: string;
};
/**
@@ -286,6 +288,9 @@ export const strings: {
videoTranscript: "See video transcript",
somethingWrong: "Something went wrong.",
videoWrapper: "Khan Academy video wrapper",
+ mathInputTitle: "mathematics keyboard",
+ mathInputDescription:
+ "Use keyboard/mouse to interact with math-based input fields",
};
/**
@@ -433,4 +438,7 @@ export const mockStrings: PerseusStrings = {
videoTranscript: "See video transcript",
somethingWrong: "Something went wrong.",
videoWrapper: "Khan Academy video wrapper",
+ mathInputTitle: "mathematics keyboard",
+ mathInputDescription:
+ "Use keyboard/mouse to interact with math-based input fields",
};
diff --git a/packages/perseus/src/types.ts b/packages/perseus/src/types.ts
index f0b3d386ab..0ec64276b9 100644
--- a/packages/perseus/src/types.ts
+++ b/packages/perseus/src/types.ts
@@ -1,8 +1,8 @@
import type {ILogger} from "./logging/log";
import type {Item} from "./multi-items/item-types";
import type {
+ Hint,
PerseusAnswerArea,
- PerseusRenderer,
PerseusWidget,
PerseusWidgetsMap,
} from "./perseus-types";
@@ -47,10 +47,6 @@ export type PerseusScore =
message?: string | null | undefined;
};
-export type Hint = PerseusRenderer & {
- replace?: boolean;
-};
-
export type Version = {
major: number;
minor: number;
@@ -101,7 +97,7 @@ export type ChangeHandler = (
) => unknown;
export type ImageUploader = (
- file: string,
+ file: File,
callback: (url: string) => unknown,
) => unknown;
@@ -164,9 +160,24 @@ export const InteractiveGraphEditorFlags = [
"start-coords-ui-phase-1",
/**
* Enables the UI for setting the start coordinates of a graph.
- * Includes sinusoid graph.
+ * Includes sinusoid and quadratic graphs.
*/
"start-coords-ui-phase-2",
+ /**
+ * Enables the UI for setting the start coordinates of a graph.
+ * Includes point graph.
+ */
+ "start-coords-ui-point",
+ /**
+ * Enables the UI for setting the start coordinates of a graph.
+ * Includes polygon graph.
+ */
+ "start-coords-ui-polygon",
+ /**
+ * Enables the UI for setting the start coordinates of a graph.
+ * Includes angle graph.
+ */
+ "start-coords-ui-angle",
] as const;
/**
diff --git a/packages/perseus/src/widgets/__testdata__/grapher.testdata.ts b/packages/perseus/src/widgets/__testdata__/grapher.testdata.ts
index ff9e0b8277..9a6ab0e705 100644
--- a/packages/perseus/src/widgets/__testdata__/grapher.testdata.ts
+++ b/packages/perseus/src/widgets/__testdata__/grapher.testdata.ts
@@ -218,7 +218,6 @@ export const quadraticQuestion: PerseusRenderer = {
content:
"In conclusion, the vertex of the parabola is at\n\n$(3,-8)$\n\nand the zeros are\n\n$(5,0)$ and $(1,0)$\n\nIn order to graph, we need the vertex and another point. That other point can be one of the zeros we found, like $(1,0)$:\n\n[[☃ grapher 1]]",
images: {},
- replace: false,
widgets: {
"grapher 1": {
alignment: "default",
@@ -268,7 +267,6 @@ export const sinusoidQuestion: PerseusRenderer = {
content:
"###The answer\n\nWe found that the graph of $y=-4\\cos\\left(x\\right)+3$ has a minimum point at $(0,-1)$ and then intersects its midline at $\\left(\\dfrac{1}{2}\\pi,3\\right)$.\n\n[[☃ grapher 3]]\n ",
images: {},
- replace: false,
widgets: {
"grapher 3": {
alignment: "default",
diff --git a/packages/perseus/src/widgets/__testdata__/interaction.testdata.ts b/packages/perseus/src/widgets/__testdata__/interaction.testdata.ts
index 91c2e187c3..03e8edbacc 100644
--- a/packages/perseus/src/widgets/__testdata__/interaction.testdata.ts
+++ b/packages/perseus/src/widgets/__testdata__/interaction.testdata.ts
@@ -4,7 +4,6 @@ export const question1: PerseusRenderer = {
content:
"Drag the dot all the way to the right.\n\n[[☃ interaction 1]]\n\n\n*Notice that we add a zero to the empty place value.* ",
images: {},
- replace: false,
widgets: {
"interaction 1": {
alignment: "default",
diff --git a/packages/perseus/src/widgets/__testdata__/interactive-graph.testdata.ts b/packages/perseus/src/widgets/__testdata__/interactive-graph.testdata.ts
index b3038d03d0..f0022b5fa6 100644
--- a/packages/perseus/src/widgets/__testdata__/interactive-graph.testdata.ts
+++ b/packages/perseus/src/widgets/__testdata__/interactive-graph.testdata.ts
@@ -32,6 +32,17 @@ export const angleQuestion: PerseusRenderer = interactiveGraphQuestionBuilder()
export const angleQuestionWithDefaultCorrect: PerseusRenderer =
interactiveGraphQuestionBuilder().withAngle().build();
+export const angleWithStartingCoordsQuestion: PerseusRenderer =
+ interactiveGraphQuestionBuilder()
+ .withAngle({
+ startCoords: [
+ [5, 1],
+ [1, 1],
+ [4, 5],
+ ],
+ })
+ .build();
+
export const circleQuestion: PerseusRenderer = interactiveGraphQuestionBuilder()
.withCircle({center: [-2, -4], radius: 2})
.build();
@@ -97,6 +108,16 @@ export const pointQuestion: PerseusRenderer = interactiveGraphQuestionBuilder()
export const pointQuestionWithDefaultCorrect: PerseusRenderer =
interactiveGraphQuestionBuilder().withPoints(1).build();
+export const pointQuestionWithStartingCoords: PerseusRenderer =
+ interactiveGraphQuestionBuilder()
+ .withPoints(2, {
+ startCoords: [
+ [0, 0],
+ [2, 2],
+ ],
+ })
+ .build();
+
export const polygonQuestion: PerseusRenderer =
interactiveGraphQuestionBuilder()
.withContent(
@@ -120,6 +141,18 @@ export const polygonQuestion: PerseusRenderer =
})
.build();
+export const polygonWithStartingCoordsQuestion: PerseusRenderer =
+ interactiveGraphQuestionBuilder()
+ .withPolygon("grid", {
+ startCoords: [
+ [6, 6],
+ [8, 6],
+ [8, 8],
+ [6, 8],
+ ],
+ })
+ .build();
+
export const polygonWithAnglesQuestion: PerseusRenderer =
interactiveGraphQuestionBuilder()
.withContent(
@@ -765,6 +798,9 @@ export const segmentWithLockedFigures: PerseusRenderer =
],
{color: "pink"},
)
+ .addLockedFunction("sin(x)", {
+ color: "red",
+ })
.build();
export const quadraticQuestion: PerseusRenderer =
diff --git a/packages/perseus/src/widgets/__testdata__/video.testdata.ts b/packages/perseus/src/widgets/__testdata__/video.testdata.ts
index 36b2578d42..6e8a33125e 100644
--- a/packages/perseus/src/widgets/__testdata__/video.testdata.ts
+++ b/packages/perseus/src/widgets/__testdata__/video.testdata.ts
@@ -17,7 +17,6 @@ export const question1: PerseusRenderer = {
alignment: "block",
},
},
- replace: false,
};
export const question2: PerseusRenderer = {
@@ -37,7 +36,6 @@ export const question2: PerseusRenderer = {
alignment: "block",
},
},
- replace: false,
};
export const question3: PerseusRenderer = {
@@ -57,5 +55,4 @@ export const question3: PerseusRenderer = {
alignment: "block",
},
},
- replace: false,
};
diff --git a/packages/perseus/src/widgets/__tests__/interactive-graph.test.tsx b/packages/perseus/src/widgets/__tests__/interactive-graph.test.tsx
index d1e9468a67..8c3a2a0eb2 100644
--- a/packages/perseus/src/widgets/__tests__/interactive-graph.test.tsx
+++ b/packages/perseus/src/widgets/__tests__/interactive-graph.test.tsx
@@ -1,5 +1,4 @@
import {describe, beforeEach, it} from "@jest/globals";
-import * as KAS from "@khanacademy/kas";
import {color as wbColor} from "@khanacademy/wonder-blocks-tokens";
import {act, waitFor} from "@testing-library/react";
import {userEvent as userEventLib} from "@testing-library/user-event";
@@ -822,49 +821,6 @@ describe("locked layer", () => {
});
});
- it("parses equation only when needed for a locked function", () => {
- const PARSED = "equation needed parsing";
- const PREPARSED = "equation was pre-parsed";
- // Arrange - equation needs parsing
- const KasParseMock = jest
- .spyOn(KAS, "parse")
- .mockReturnValue({expr: {eval: () => PARSED}});
- const PlotMock = jest.spyOn(Plot, "OfX");
- renderQuestion(segmentWithLockedFunction("x^2"), {
- flags: {
- mafs: {
- segment: true,
- },
- },
- });
- let plotFn = PlotMock.mock.calls[0][0]["y"];
-
- // Assert
- expect(KasParseMock).toHaveBeenCalledTimes(1);
- expect(plotFn(0)).toEqual(PARSED);
-
- // Arrange - equation does NOT need parsing
- KasParseMock.mockClear();
- PlotMock.mockClear();
- renderQuestion(
- segmentWithLockedFunction("x^2", {
- equationParsed: {eval: () => PREPARSED},
- }),
- {
- flags: {
- mafs: {
- segment: true,
- },
- },
- },
- );
- plotFn = PlotMock.mock.calls[0][0]["y"];
-
- // Assert
- expect(KasParseMock).toHaveBeenCalledTimes(0);
- expect(plotFn(0)).toEqual(PREPARSED);
- });
-
it("plots the supplied equation on the axis specified", () => {
// Arrange
const apiOptions = {
@@ -880,7 +836,6 @@ describe("locked layer", () => {
const PlotOfYMock = jest
.spyOn(Plot, "OfY")
.mockReturnValue(OfY
);
- const equationFnMock = jest.fn();
// Act - Render f(x)
renderQuestion(segmentWithLockedFunction("x^2"), apiOptions);
@@ -896,9 +851,6 @@ describe("locked layer", () => {
renderQuestion(
segmentWithLockedFunction("x^2", {
directionalAxis: "y",
- equationParsed: {
- eval: equationFnMock,
- },
}),
apiOptions,
);
@@ -906,8 +858,5 @@ describe("locked layer", () => {
// Assert
expect(PlotOfXMock).toHaveBeenCalledTimes(0);
expect(PlotOfYMock).toHaveBeenCalledTimes(1);
- PlotOfYMock.mock.calls[0][0]["x"](1.21); // Execute the plot function
- expect(equationFnMock).toHaveBeenCalledTimes(1);
- expect(equationFnMock).toHaveBeenCalledWith({y: 1.21});
});
});
diff --git a/packages/perseus/src/widgets/interactive-graphs/interactive-graph-question-builder.test.ts b/packages/perseus/src/widgets/interactive-graphs/interactive-graph-question-builder.test.ts
index cb494b822d..b38a8dc6c4 100644
--- a/packages/perseus/src/widgets/interactive-graphs/interactive-graph-question-builder.test.ts
+++ b/packages/perseus/src/widgets/interactive-graphs/interactive-graph-question-builder.test.ts
@@ -637,7 +637,6 @@ describe("InteractiveGraphQuestionBuilder", () => {
correct: {
type: "point",
numPoints: "unlimited",
- coords: [[0, 0]],
},
}),
);
diff --git a/packages/perseus/src/widgets/interactive-graphs/interactive-graph-question-builder.ts b/packages/perseus/src/widgets/interactive-graphs/interactive-graph-question-builder.ts
index 7db0b1dbf1..dbd381f900 100644
--- a/packages/perseus/src/widgets/interactive-graphs/interactive-graph-question-builder.ts
+++ b/packages/perseus/src/widgets/interactive-graphs/interactive-graph-question-builder.ts
@@ -684,7 +684,7 @@ class PolygonGraphConfig implements InteractiveFigureConfig {
class PointsGraphConfig implements InteractiveFigureConfig {
private numPoints: number | "unlimited";
- private coords: Coord[];
+ private coords?: Coord[];
private startCoords?: Coord[];
constructor(
@@ -695,7 +695,7 @@ class PointsGraphConfig implements InteractiveFigureConfig {
},
) {
this.numPoints = numPoints;
- this.coords = options?.coords ?? [[0, 0]];
+ this.coords = options?.coords;
this.startCoords = options?.startCoords;
}
diff --git a/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-function.tsx b/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-function.tsx
index 10fb49345d..a7bcf04745 100644
--- a/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-function.tsx
+++ b/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-function.tsx
@@ -1,19 +1,37 @@
import * as KAS from "@khanacademy/kas";
import {Plot} from "mafs";
import * as React from "react";
+import {useState, useEffect} from "react";
import {lockedFigureColors} from "../../../perseus-types";
import type {LockedFunctionType} from "../../../perseus-types";
const LockedFunction = (props: LockedFunctionType) => {
+ type Equation = {
+ [k: string]: any;
+ eval: (number) => number;
+ };
+ const [equation, setEquation]: [
+ Equation | undefined,
+ React.Dispatch>,
+ ] = useState();
const {color, strokeStyle, directionalAxis, domain} = props;
const plotProps = {
color: lockedFigureColors[color],
style: strokeStyle,
domain,
};
- const equation = props.equationParsed || KAS.parse(props.equation).expr;
+
+ useEffect(() => {
+ // Parsing the equation in a "useEffect" hook saves about 2ms each frame
+ // when the learner is interacting with the graph (i.e. moving points).
+ setEquation(KAS.parse(props.equation).expr);
+ }, [props.equation]);
+
+ if (typeof equation === "undefined") {
+ return null;
+ }
return (
diff --git a/packages/perseus/src/widgets/interactive-graphs/reducer/initialize-graph-state.ts b/packages/perseus/src/widgets/interactive-graphs/reducer/initialize-graph-state.ts
index 2b594c827e..6768d821b1 100644
--- a/packages/perseus/src/widgets/interactive-graphs/reducer/initialize-graph-state.ts
+++ b/packages/perseus/src/widgets/interactive-graphs/reducer/initialize-graph-state.ts
@@ -110,7 +110,7 @@ export function initializeGraphState(
}
}
-function getPointCoords(
+export function getPointCoords(
graph: PerseusGraphTypePoint,
range: [x: Interval, y: Interval],
step: [x: number, y: number],
@@ -277,7 +277,7 @@ export function getLinearSystemCoords(
);
}
-function getPolygonCoords(
+export function getPolygonCoords(
graph: PerseusGraphTypePolygon,
range: [x: Interval, y: Interval],
step: [x: number, y: number],
@@ -394,7 +394,7 @@ export function getCircleCoords(graph: PerseusGraphTypeCircle): {
};
}
-const getAngleCoords = (params: {
+export const getAngleCoords = (params: {
graph: PerseusGraphTypeAngle;
range: [x: Interval, y: Interval];
step: [x: number, y: number];
@@ -404,6 +404,10 @@ const getAngleCoords = (params: {
return graph.coords;
}
+ if (graph.startCoords) {
+ return graph.startCoords;
+ }
+
const {snapDegrees, angleOffsetDeg} = graph;
const snap = snapDegrees || 1;
let angle = snap;
diff --git a/packages/perseus/src/widgets/interactive-graphs/stateful-mafs-graph.tsx b/packages/perseus/src/widgets/interactive-graphs/stateful-mafs-graph.tsx
index bd6e412c4b..8bf543d9fd 100644
--- a/packages/perseus/src/widgets/interactive-graphs/stateful-mafs-graph.tsx
+++ b/packages/perseus/src/widgets/interactive-graphs/stateful-mafs-graph.tsx
@@ -96,7 +96,11 @@ export const StatefulMafsGraph = React.forwardRef<
);
}, [dispatch, xMinRange, xMaxRange, yMinRange, yMaxRange]);
+ // Update the graph whenever any of the following values changes.
+ // This is necessary to keep the graph previews in sync with the updated
+ // graph settings within the interative graph editor.
const numSegments = graph.type === "segment" ? graph.numSegments : null;
+ const numPoints = graph.type === "point" ? graph.numPoints : null;
const numSides = graph.type === "polygon" ? graph.numSides : null;
const snapTo = graph.type === "polygon" ? graph.snapTo : null;
const showAngles = graph.type === "polygon" ? graph.showAngles : null;
@@ -114,6 +118,7 @@ export const StatefulMafsGraph = React.forwardRef<
}
}, [
graph.type,
+ numPoints,
numSegments,
numSides,
snapTo,
diff --git a/static/fonts/lato/Lato-Black.woff2 b/static/fonts/lato/Lato-Black.woff2
new file mode 100644
index 0000000000..37a5749e40
Binary files /dev/null and b/static/fonts/lato/Lato-Black.woff2 differ
diff --git a/static/fonts/lato/Lato-Bold.woff2 b/static/fonts/lato/Lato-Bold.woff2
new file mode 100644
index 0000000000..c9f92ef3c1
Binary files /dev/null and b/static/fonts/lato/Lato-Bold.woff2 differ
diff --git a/static/fonts/lato/Lato-Italic.woff2 b/static/fonts/lato/Lato-Italic.woff2
new file mode 100644
index 0000000000..19a47c9059
Binary files /dev/null and b/static/fonts/lato/Lato-Italic.woff2 differ
diff --git a/static/fonts/lato/Lato-Regular.woff2 b/static/fonts/lato/Lato-Regular.woff2
new file mode 100644
index 0000000000..70171778ad
Binary files /dev/null and b/static/fonts/lato/Lato-Regular.woff2 differ
diff --git a/static/fonts/lato/LatoLatin-Black.woff2 b/static/fonts/lato/LatoLatin-Black.woff2
new file mode 100644
index 0000000000..4127b4d0b9
Binary files /dev/null and b/static/fonts/lato/LatoLatin-Black.woff2 differ
diff --git a/static/fonts/lato/LatoLatin-Bold.woff2 b/static/fonts/lato/LatoLatin-Bold.woff2
new file mode 100644
index 0000000000..2615c853d5
Binary files /dev/null and b/static/fonts/lato/LatoLatin-Bold.woff2 differ
diff --git a/static/fonts/lato/LatoLatin-Italic.woff2 b/static/fonts/lato/LatoLatin-Italic.woff2
new file mode 100644
index 0000000000..aaa5a35c3d
Binary files /dev/null and b/static/fonts/lato/LatoLatin-Italic.woff2 differ
diff --git a/static/fonts/lato/LatoLatin-Regular.woff2 b/static/fonts/lato/LatoLatin-Regular.woff2
new file mode 100644
index 0000000000..a4d084bfb7
Binary files /dev/null and b/static/fonts/lato/LatoLatin-Regular.woff2 differ
diff --git a/static/fonts/lato/LatoLatinExtended-Black.woff2 b/static/fonts/lato/LatoLatinExtended-Black.woff2
new file mode 100644
index 0000000000..43d862b8f8
Binary files /dev/null and b/static/fonts/lato/LatoLatinExtended-Black.woff2 differ
diff --git a/static/fonts/lato/LatoLatinExtended-Bold.woff2 b/static/fonts/lato/LatoLatinExtended-Bold.woff2
new file mode 100644
index 0000000000..e78612f949
Binary files /dev/null and b/static/fonts/lato/LatoLatinExtended-Bold.woff2 differ
diff --git a/static/fonts/lato/LatoLatinExtended-Italic.woff2 b/static/fonts/lato/LatoLatinExtended-Italic.woff2
new file mode 100644
index 0000000000..58d8579f32
Binary files /dev/null and b/static/fonts/lato/LatoLatinExtended-Italic.woff2 differ
diff --git a/static/fonts/lato/LatoLatinExtended-Regular.woff2 b/static/fonts/lato/LatoLatinExtended-Regular.woff2
new file mode 100644
index 0000000000..39f33b85ee
Binary files /dev/null and b/static/fonts/lato/LatoLatinExtended-Regular.woff2 differ
diff --git a/static/lato.css b/static/lato.css
new file mode 100644
index 0000000000..648ef82d1f
--- /dev/null
+++ b/static/lato.css
@@ -0,0 +1,128 @@
+/* Adapted from https://fonts.googleapis.com/css?family=Lato:400,400i,700,900 */
+
+/* non-latin (cyrillic, hungarian, serbian) */
+@font-face {
+ font-family: "Lato";
+ font-style: italic;
+ font-weight: 400;
+ src:
+ local("Lato Italic"),
+ local("Lato-Italic"),
+ url(fonts/lato/Lato-Italic.woff2) format("woff2");
+ unicode-range: U+0400-04FF, U+0500-052F, U+2DE0-2DFF, U+A640-A69F,
+ U+1D00-1D7F;
+}
+
+@font-face {
+ font-family: "Lato";
+ font-style: normal;
+ font-weight: 400;
+ src:
+ local("Lato Regular"),
+ local("Lato-Regular"),
+ url(fonts/lato/Lato-Regular.woff2) format("woff2");
+ unicode-range: U+0400-04FF, U+0500-052F, U+2DE0-2DFF, U+A640-A69F,
+ U+1D00-1D7F;
+}
+
+@font-face {
+ font-family: "Lato";
+ font-style: normal;
+ font-weight: 700;
+ src:
+ local("Lato Bold"),
+ local("Lato-Bold"),
+ url(fonts/lato/Lato-Bold.woff2) format("woff2");
+ unicode-range: U+0400-04FF, U+0500-052F, U+2DE0-2DFF, U+A640-A69F,
+ U+1D00-1D7F;
+}
+
+@font-face {
+ font-family: "Lato";
+ font-style: normal;
+ font-weight: 900;
+ src:
+ local("Lato Black"),
+ local("Lato-Black"),
+ url(fonts/lato/Lato-Black.woff2) format("woff2");
+ unicode-range: U+0400-04FF, U+0500-052F, U+2DE0-2DFF, U+A640-A69F,
+ U+1D00-1D7F;
+}
+
+/* latin */
+@font-face {
+ font-family: "Lato";
+ font-style: italic;
+ font-weight: 400;
+ src:
+ local("Lato Italic"),
+ local("Lato-Italic"),
+ url(fonts/lato/LatoLatin-Italic.woff2) format("woff2");
+}
+
+@font-face {
+ font-family: "Lato";
+ font-style: normal;
+ font-weight: 400;
+ src:
+ local("Lato Regular"),
+ local("Lato-Regular"),
+ url(fonts/lato/LatoLatin-Regular.woff2) format("woff2");
+}
+
+@font-face {
+ font-family: "Lato";
+ font-style: normal;
+ font-weight: 700;
+ src:
+ local("Lato Bold"),
+ local("Lato-Bold"),
+ url(fonts/lato/LatoLatin-Bold.woff2) format("woff2");
+}
+
+@font-face {
+ font-family: "Lato";
+ font-style: normal;
+ font-weight: 900;
+ src:
+ local("Lato Black"),
+ local("Lato-Black"),
+ url(fonts/lato/LatoLatin-Black.woff2) format("woff2");
+}
+
+/* Latin Extended (e.g. vietnamese) */
+@font-face {
+ font-family: "Lato";
+ font-style: italic;
+ font-weight: 400;
+ src:
+ local("Lato Italic"),
+ local("Lato-Italic"),
+ url(fonts/lato/LatoLatinExtended-Italic.woff2) format("woff2");
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
+ U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+
+@font-face {
+ font-family: "Lato";
+ font-weight: 400;
+ src: url("fonts/lato/LatoLatinExtended-Regular.woff2");
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
+ U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+
+@font-face {
+ font-family: "Lato";
+ font-weight: 700;
+ src: url("fonts/lato/LatoLatinExtended-Bold.woff2");
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
+ U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+
+@font-face {
+ font-family: "Lato";
+ font-weight: 900;
+ src: url("fonts/lato/LatoLatinExtended-Black.woff2");
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
+ U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
diff --git a/yarn.lock b/yarn.lock
index 5399a4ab6b..ec9ff67904 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2765,17 +2765,17 @@
"@khanacademy/wonder-blocks-tokens" "^1.3.0"
"@khanacademy/wonder-blocks-typography" "^2.1.11"
-"@khanacademy/wonder-blocks-popover@3.2.9":
- version "3.2.9"
- resolved "https://registry.yarnpkg.com/@khanacademy/wonder-blocks-popover/-/wonder-blocks-popover-3.2.9.tgz#f82212edd117832b7eab6066d8b6381fc982126e"
- integrity sha512-WjstA4acn/gOfo1CedA90oq5hWZnkUl4Lj2hpMCHYSTflfEhz0mjcFWzWXPAta+rtk6pAEnAKzqWT1oJ5q+Hvg==
+"@khanacademy/wonder-blocks-popover@3.2.11":
+ version "3.2.11"
+ resolved "https://registry.yarnpkg.com/@khanacademy/wonder-blocks-popover/-/wonder-blocks-popover-3.2.11.tgz#75d6353fa8faa8e8b1d2bb202878565655d75ec2"
+ integrity sha512-JxH0UTg1rYhUG2rr/QV8PlJaIzSV8HvlhTiu7NxUhhciU4dWPtdZKURGthdybWb4lCuWxxQEumeoFrPspN6JjQ==
dependencies:
"@babel/runtime" "^7.18.6"
"@khanacademy/wonder-blocks-core" "^6.4.3"
"@khanacademy/wonder-blocks-icon-button" "^5.3.3"
"@khanacademy/wonder-blocks-modal" "^5.1.8"
"@khanacademy/wonder-blocks-tokens" "^1.3.1"
- "@khanacademy/wonder-blocks-tooltip" "^2.3.7"
+ "@khanacademy/wonder-blocks-tooltip" "^2.3.8"
"@khanacademy/wonder-blocks-typography" "^2.1.14"
"@khanacademy/wonder-blocks-progress-spinner@2.1.1", "@khanacademy/wonder-blocks-progress-spinner@^2.1.1":
@@ -2868,10 +2868,10 @@
"@khanacademy/wonder-blocks-tokens" "^1.3.0"
"@khanacademy/wonder-blocks-typography" "^2.1.11"
-"@khanacademy/wonder-blocks-tooltip@^2.3.7":
- version "2.3.7"
- resolved "https://registry.yarnpkg.com/@khanacademy/wonder-blocks-tooltip/-/wonder-blocks-tooltip-2.3.7.tgz#b09d16311b75609eaf8c0df68a43cf1a410fe7cb"
- integrity sha512-bVLM0+cmGoFro0c6hQkJ3naLWnfrqfOXpNJSJl9HLiZZwWuWEg68cRZRP4KFM9b/2E2LM8GLpK95W7tFrhv6iw==
+"@khanacademy/wonder-blocks-tooltip@^2.3.8":
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/@khanacademy/wonder-blocks-tooltip/-/wonder-blocks-tooltip-2.3.8.tgz#8ecb9238a515ac33212e525dc548f80389c50375"
+ integrity sha512-3jsgqjIX2V2zwMlTiTabvAwhRL70TU+xmaIdJWy7M0JiQXeOFXX5mb9/vhcw0Pv/JFWEOBDAvShaKmQptSQpQg==
dependencies:
"@babel/runtime" "^7.18.6"
"@khanacademy/wonder-blocks-core" "^6.4.3"
@@ -15135,7 +15135,7 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"
-"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
+"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -15153,6 +15153,15 @@ string-width@^1.0.1:
is-fullwidth-code-point "^1.0.0"
strip-ansi "^3.0.0"
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
string-width@^5.0.1, string-width@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
@@ -15237,7 +15246,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -15251,6 +15260,13 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1:
dependencies:
ansi-regex "^2.0.0"
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
@@ -16141,6 +16157,11 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
+uuid@^10.0.0:
+ version "10.0.0"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"
+ integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==
+
uuid@^3.3.2, uuid@^3.3.3:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
@@ -16464,7 +16485,7 @@ wordwrap@^1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
@@ -16482,6 +16503,15 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"