diff --git a/.changeset/fast-points-complain.md b/.changeset/fast-points-complain.md new file mode 100644 index 00000000000..4f641e0fae6 --- /dev/null +++ b/.changeset/fast-points-complain.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/perseus-editor": minor +--- + +Creation of new Vector Graph Editor diff --git a/packages/perseus-editor/src/__testdata__/interactive-graph-question-builder.ts b/packages/perseus-editor/src/__testdata__/interactive-graph-question-builder.ts index 2b4659217d6..950fe426fe9 100644 --- a/packages/perseus-editor/src/__testdata__/interactive-graph-question-builder.ts +++ b/packages/perseus-editor/src/__testdata__/interactive-graph-question-builder.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ // Duplicated from packages/perseus/src/widgets/interactive-graphs/interactive-graph-question-builder.ts // This is to have similar test data for packages/perseus-editor @@ -302,6 +303,14 @@ class InteractiveGraphQuestionBuilder { return this; } + withVector(options?: { + coords?: CollinearTuple; + startCoords?: CollinearTuple; + }): InteractiveGraphQuestionBuilder { + this.interactiveFigureConfig = new VectorGraphConfig(options); + return this; + } + withPolygon( snapTo?: SnapTo, options?: { @@ -842,6 +851,30 @@ class ExponentialGraphConfig implements InteractiveFigureConfig { } } +class VectorGraphConfig implements InteractiveFigureConfig { + private coords?: CollinearTuple; + private startCoords?: CollinearTuple; + + constructor(options?: { + coords?: CollinearTuple; + startCoords?: CollinearTuple; + }) { + this.coords = options?.coords; + this.startCoords = options?.startCoords; + } + + correct(): PerseusGraphType { + return { + type: "vector", + coords: this.coords, + }; + } + + graph(): PerseusGraphType { + return {type: "vector", startCoords: this.startCoords}; + } +} + class PolygonGraphConfig implements InteractiveFigureConfig { private snapTo: SnapTo; private match?: "similar" | "congruent" | "approx"; diff --git a/packages/perseus-editor/src/__testdata__/interactive-graph.testdata.ts b/packages/perseus-editor/src/__testdata__/interactive-graph.testdata.ts index 5bcd723b3b4..4a23d3c0d0c 100644 --- a/packages/perseus-editor/src/__testdata__/interactive-graph.testdata.ts +++ b/packages/perseus-editor/src/__testdata__/interactive-graph.testdata.ts @@ -157,6 +157,9 @@ export const sinusoidMinimalQuestion: PerseusRenderer = export const exponentialMinimalQuestion: PerseusRenderer = interactiveGraphQuestionBuilder().withExponential().build(); +export const vectorMinimalQuestion: PerseusRenderer = + interactiveGraphQuestionBuilder().withVector().build(); + export const segmentWithLockedFigures: PerseusRenderer = interactiveGraphQuestionBuilder() .addLockedPointAt(-7, -7, {labels: [{text: "A"}], ariaLabel: "Point A"}) diff --git a/packages/perseus-editor/src/widgets/__docs__/interactive-graph-editor.stories.tsx b/packages/perseus-editor/src/widgets/__docs__/interactive-graph-editor.stories.tsx index e6ea297b10f..190525b52fa 100644 --- a/packages/perseus-editor/src/widgets/__docs__/interactive-graph-editor.stories.tsx +++ b/packages/perseus-editor/src/widgets/__docs__/interactive-graph-editor.stories.tsx @@ -27,6 +27,7 @@ import { segmentWithLockedFigures, segmentWithStartingCoordsQuestion, exponentialMinimalQuestion, + vectorMinimalQuestion, sinusoidMinimalQuestion, sinusoidWithStartingCoordsAndPiTicksQuestion, unlimitedPolygonWithCorrectAnswerQuestion, @@ -121,6 +122,10 @@ export const InteractiveGraphExponential = (): React.ReactElement => { ); }; +export const InteractiveGraphVector = (): React.ReactElement => { + return ; +}; + export const InteractiveGraphSinusoid = (): React.ReactElement => { return ( diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/components/graph-type-selector.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/components/graph-type-selector.tsx index 610c90fbe84..5fd06c82e8e 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/components/graph-type-selector.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/components/graph-type-selector.tsx @@ -34,6 +34,11 @@ const GraphTypeSelector = (props: GraphTypeSelectorProps) => { "interactive-graph-logarithm", ); + const showVector = isFeatureOn( + {apiOptions: props.apiOptions}, + "interactive-graph-vector", + ); + return ( { + {showVector && } ); diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/components/vector-answer-options.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/components/vector-answer-options.tsx new file mode 100644 index 00000000000..8defeccefdc --- /dev/null +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/components/vector-answer-options.tsx @@ -0,0 +1,53 @@ +import {components} from "@khanacademy/perseus"; +import {OptionItem, SingleSelect} from "@khanacademy/wonder-blocks-dropdown"; +import * as React from "react"; +import invariant from "tiny-invariant"; + +import styles from "../interactive-graph-editor.module.css"; +import LabeledRow from "../locked-figures/labeled-row"; + +import type {Props as EditorProps} from "../interactive-graph-editor"; +import type {PerseusGraphTypeVector} from "@khanacademy/perseus-core"; + +const {InfoTip} = components; + +interface Props { + correct: PerseusGraphTypeVector; + onChange: (props: Partial) => void; +} + +export default function VectorAnswerOptions({correct, onChange}: Props) { + return ( + + { + invariant( + correct.type === "vector", + `Expected graph type to be vector, but got ${correct.type}`, + ); + onChange({ + correct: { + ...correct, + match: newValue as PerseusGraphTypeVector["match"], + }, + }); + }} + // Never uses placeholder, always has value + placeholder="" + className={styles.singleSelectShort} + > + + + + + + Congruency requires only that the vector has the same + direction and magnitude. An exact match implies congruency, + but also requires that the tail and tip are in the same + position on the grid. + + + + ); +} diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/interactive-graph-editor.tsx b/packages/perseus-editor/src/widgets/interactive-graph-editor/interactive-graph-editor.tsx index 81416f25f76..400257df854 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/interactive-graph-editor.tsx +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/interactive-graph-editor.tsx @@ -32,6 +32,7 @@ import InteractiveGraphSettings from "./components/interactive-graph-settings"; import InteractiveGraphSRTree from "./components/interactive-graph-sr-tree"; import PolygonAnswerOptions from "./components/polygon-answer-options"; import SegmentCountSelector from "./components/segment-count-selector"; +import VectorAnswerOptions from "./components/vector-answer-options"; import LabeledRow from "./locked-figures/labeled-row"; import LockedFiguresSection from "./locked-figures/locked-figures-section"; import StartCoordsSettings from "./start-coords/start-coords-settings"; @@ -457,6 +458,12 @@ class InteractiveGraphEditor extends React.Component { onChange={this.props.onChange} /> )} + {this.props.correct?.type === "vector" && ( + + )} {this.props.correct?.type === "segment" && ( { /> ); } + case "vector": { + const vectorCoords = getVectorCoords(props, range, step); + return ( + + ); + } case "tangent": const tangentCoords = getTangentCoords(props, range, step); return ( diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/types.ts b/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/types.ts index 4b933f3aaac..af790b59e30 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/types.ts +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/types.ts @@ -14,7 +14,8 @@ type GraphTypesThatHaveStartCoords = | {type: "sinusoid"} | {type: "exponential"} | {type: "logarithm"} - | {type: "tangent"}; + | {type: "tangent"} + | {type: "vector"}; export type StartCoords = Extract< PerseusGraphType, diff --git a/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/util.ts b/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/util.ts index 24adf65ca69..eb1adeadb0d 100644 --- a/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/util.ts +++ b/packages/perseus-editor/src/widgets/interactive-graph-editor/start-coords/util.ts @@ -13,6 +13,7 @@ import { getSegmentCoords, getSinusoidCoords, getTangentCoords, + getVectorCoords, } from "@khanacademy/perseus"; import {UnreachableCaseError} from "@khanacademy/wonder-stuff-core"; @@ -47,6 +48,12 @@ export function getDefaultGraphStartCoords( range, step, ); + case "vector": + return getVectorCoords( + {...graph, startCoords: undefined}, + range, + step, + ); case "segment": return getSegmentCoords( {...graph, startCoords: undefined},
+ Congruency requires only that the vector has the same + direction and magnitude. An exact match implies congruency, + but also requires that the tail and tip are in the same + position on the grid. +