Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

View a locked point with mafs #1067

Merged
merged 13 commits into from
Mar 13, 2024
5 changes: 5 additions & 0 deletions .changeset/shaggy-parrots-provide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/perseus": minor
---

Can pass a point into `lockedFigures` into MafsGraph and a point will be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,36 @@ export const Controlled: StoryComponentType = {
return <InteractiveGraphEditor {...state} onChange={dispatch} />;
},
};

/**
* Example of what the InteractiveGraphEditor experience is when using
* a Mafs-based InteractiveGraph.
*/
export const WithMafs: StoryComponentType = {
render: function Render() {
const reducer = (state, newState) => {
return {
...state,
...newState,
};
};

const [state, dispatch] = React.useReducer(reducer, {
apiOptions: {
flags: {
mafs: {
segment: true,
},
},
},
graph: {
type: "segment",
},
correct: {
type: "segment",
},
});

return <InteractiveGraphEditor {...state} onChange={dispatch} />;
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
polygonQuestion,
rayQuestion,
segmentQuestion,
segmentWithLockedPointsQuestion,
sinusoidQuestion,
} from "../__testdata__/interactive-graph.testdata";

Expand Down Expand Up @@ -56,6 +57,21 @@ export const Segment = (args: StoryArgs): React.ReactElement => (
<RendererWithDebugUI question={segmentQuestion} />
);

export const SegmentWithMafsAndLockedPoints = (
args: StoryArgs,
): React.ReactElement => (
<RendererWithDebugUI
apiOptions={{
flags: {
mafs: {
segment: true,
},
},
}}
question={segmentWithLockedPointsQuestion}
/>
);

export const Sinusoid = (args: StoryArgs): React.ReactElement => (
<RendererWithDebugUI question={sinusoidQuestion} />
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {color} from "@khanacademy/wonder-blocks-tokens";

import type {Coord} from "../../interactive2/types";
import type {PerseusRenderer} from "../../perseus-types";

Expand Down Expand Up @@ -480,6 +482,61 @@ export const segmentQuestion: PerseusRenderer = {
},
};

export const segmentWithLockedPointsQuestion: PerseusRenderer = {
content:
"Line segment $\\overline{OG}$ is rotated $180^\\circ$ about the point $(-2,4)$. \n\n**Draw the image of this rotation using the interactive graph.**\n\n*The direction of a rotation by a positive angle is counter-clockwise.* \n\n[[☃ interactive-graph 1]]\n\n",
images: {},
widgets: {
"interactive-graph 1": {
graded: true,
options: {
correct: {
coords: [
[
[-7, -7],
[2, -5],
],
],
type: "segment",
},
graph: {
type: "segment",
},
gridStep: [1, 1],
labels: ["x", "y"],
markings: "graph",
range: [
[-10, 10],
[-10, 10],
],
rulerLabel: "",
rulerTicks: 10,
showProtractor: false,
showRuler: false,
snapStep: [0.5, 0.5],
step: [1, 1],
lockedFigures: [
{
type: "point",
coord: [-7, -7],
style: {stroke: color.red, fill: color.red},
},
{
type: "point",
coord: [2, -5],
style: {stroke: color.red, fill: color.red},
},
],
},
type: "interactive-graph",
version: {
major: 0,
minor: 0,
},
},
},
};

export const segmentQuestionDefaultCorrect: PerseusRenderer = {
content:
"Line segment $\\overline{OG}$ is rotated $180^\\circ$ about the point $(-2,4)$. \n\n**Draw the image of this rotation using the interactive graph.**\n\n*The direction of a rotation by a positive angle is counter-clockwise.* \n\n[[☃ interactive-graph 1]]\n\n",
Expand Down
42 changes: 42 additions & 0 deletions packages/perseus/src/widgets/__tests__/interactive-graph.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {describe, beforeEach, it} from "@jest/globals";
import {color} from "@khanacademy/wonder-blocks-tokens";
import {waitFor} from "@testing-library/react";
import {userEvent as userEventLib} from "@testing-library/user-event";

Expand All @@ -9,6 +10,7 @@ import {ApiOptions} from "../../perseus-api";
import {
questionsAndAnswers,
segmentQuestion,
segmentWithLockedPointsQuestion,
segmentQuestionDefaultCorrect,
} from "../__testdata__/interactive-graph.testdata";

Expand Down Expand Up @@ -178,3 +180,43 @@ describe("segment graph", () => {
});
});
});

describe("locked layer", () => {
const apiOptions = {flags: {mafs: {segment: true}}};
it("should render locked points", async () => {
// Arrange
const {container} = renderQuestion(
segmentWithLockedPointsQuestion,
apiOptions,
);

// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
const points = container.querySelectorAll(
// Filter out the interactive points' circles
"circle:not([class*='movable-point'])",
);
nishasy marked this conversation as resolved.
Show resolved Hide resolved

// Act

// Assert
expect(points).toHaveLength(2);
});

test("should render locked points with styles", async () => {
// Arrange
const {container} = renderQuestion(
segmentWithLockedPointsQuestion,
apiOptions,
);

// Act
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
const points = container.querySelectorAll(
"circle:not([class*='movable-point'])",
);

// Assert
expect(points[0]).toHaveStyle({fill: color.red, stroke: color.red});
expect(points[1]).toHaveStyle({fill: color.red, stroke: color.red});
});
});
2 changes: 1 addition & 1 deletion packages/perseus/src/widgets/interactive-graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ class InteractiveGraph extends React.Component<Props, State> {
this.sinusoid = null;
// eslint-disable-next-line react/no-string-refs
// @ts-expect-error - TS2339 - Property 'reset' does not exist on type 'ReactInstance'.
this.refs.graph.reset();
this.refs.graph?.reset();
};

setupGraphie: () => void = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {UnreachableCaseError} from "@khanacademy/wonder-stuff-core";
import {Point} from "mafs";
import * as React from "react";

import type {LockedFigure} from "../../perseus-types";

type Props = {
lockedFigures: ReadonlyArray<LockedFigure>;
};

const GraphLockedLayer = (props: Props) => {
const {lockedFigures} = props;
return (
<>
{lockedFigures.map((figure, index) => {
switch (figure.type) {
case "point":
const [x, y] = figure.coord;
return (
<Point
key={`${figure.type}-${index}`}
x={x}
y={y}
svgCircleProps={{style: figure.style}}
/>
);
}

/**
* Devlopment-time future-proofing: This `never` should
* fail during type-checking if we add a new locked
* shape type and forget to handle it in any other
* switch case here.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
throw new UnreachableCaseError(figure.type);
})}
</>
);
};

export default GraphLockedLayer;
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {View} from "@khanacademy/wonder-blocks-core";
import {Mafs} from "mafs";
import * as React from "react";

import GraphLockedLayer from "./graph-locked-layer";
import {SegmentGraph} from "./graphs";
import {Grid} from "./grid";
import {interactiveGraphReducer} from "./interactive-graph-reducer";
Expand Down Expand Up @@ -94,7 +95,15 @@ export const MafsGraph = React.forwardRef<
width={width}
height={height}
>
{/* Background layer */}
{!legacyGrid && <Grid {...props} />}

{/* Locked layer */}
{props.lockedFigures && (
<GraphLockedLayer lockedFigures={props.lockedFigures} />
)}

{/* Interactive layer */}
{renderGraph({
state,
dispatch,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
--mafs-bg: transparent;
--mafs-fg: rgb(33, 36, 44); /* WB color.offBlack */

--mafs-line-color: rgb(33, 36, 44, 0.64); /* WB color.offBlack64 */
--grid-line-subdivision-color: rgba(33, 36, 44, 0.16);
--mafs-line-color: rgba(33, 36, 44, 0.16); /* WB color.offBlack16*/

--mafs-blue: #1865f2; /* WB color.blue */
--mafs-red: #d92916; /* WB color.red */
Expand Down
Loading