Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/gorgeous-boats-join.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@khanacademy/perseus": minor
"@khanacademy/perseus-core": minor
"@khanacademy/perseus-editor": minor
---

Add `showAngles` option to polygon locked figure
1 change: 1 addition & 0 deletions __docs__/sample-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export const graphExample: PerseusRenderer = {
[2, -3],
],
showVertices: false,
showAngles: false,
strokeStyle: "solid",
},
],
Expand Down
1 change: 1 addition & 0 deletions packages/perseus-core/src/data-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,7 @@ export type LockedPolygonType = {
points: Coord[];
color: LockedFigureColor;
showVertices: boolean;
showAngles: boolean;
fillStyle: LockedFigureFillType;
strokeStyle: LockedLineStyle;
weight: StrokeWeight;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ const parseLockedPolygonType = object({
points: array(pairOfNumbers),
color: parseLockedFigureColor,
showVertices: boolean,
showAngles: defaulted(boolean, () => false),
fillStyle: parseLockedFigureFillType,
strokeStyle: parseLockedLineStyle,
weight: parseStrokeWeight,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6752,6 +6752,7 @@ exports[`parseAndMigratePerseusItem given interactive-graph-locked-figures-missi
-3,
],
],
"showAngles": false,
"showVertices": false,
"strokeStyle": "solid",
"type": "polygon",
Expand Down Expand Up @@ -7024,6 +7025,7 @@ exports[`parseAndMigratePerseusItem given interactive-graph-locked-figures-missi
-3,
],
],
"showAngles": false,
"showVertices": false,
"strokeStyle": "solid",
"type": "polygon",
Expand Down Expand Up @@ -20239,6 +20241,14 @@ exports[`parseAndMigratePerseusItem given transformer-widget.ts returns the same
}
`;

exports[`parseAndMigratePerseusRenderer given basic-renderer.ts returns the same result as before 1`] = `
{
"content": "Hello from a renderer fixture.",
"images": {},
"widgets": {},
}
`;

exports[`parseAndMigrateUserInputMap given the data from categorizer.ts returns the same result as before 1`] = `
{
"categorizer 1": {
Expand Down Expand Up @@ -20822,11 +20832,3 @@ exports[`parseAndMigrateUserInputMap given the data from table.ts returns the sa
],
}
`;

exports[`parseAndMigratePerseusRenderer given basic-renderer.ts returns the same result as before 1`] = `
{
"content": "Hello from a renderer fixture.",
"images": {},
"widgets": {},
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export default {
],
color: "pink",
showVertices: false,
showAngles: false,
fillStyle: "none",
strokeStyle: "solid",
labels: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,7 @@ describe("generateIGLockedPolygon", () => {
],
color: "grayH",
showVertices: false,
showAngles: false,
fillStyle: "none",
strokeStyle: "solid",
weight: "medium",
Expand All @@ -1194,6 +1195,7 @@ describe("generateIGLockedPolygon", () => {
],
color: "blue",
showVertices: true,
showAngles: true,
fillStyle: "solid",
strokeStyle: "dashed",
weight: "medium",
Expand All @@ -1219,6 +1221,7 @@ describe("generateIGLockedPolygon", () => {
],
color: "blue",
showVertices: true,
showAngles: true,
fillStyle: "solid",
strokeStyle: "dashed",
weight: "medium",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ describe("getDefaultFigureForType", () => {
],
color: "grayH",
showVertices: false,
showAngles: false,
fillStyle: "none",
strokeStyle: "solid",
weight: "medium",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export function getDefaultFigureForType(type: LockedFigureType): LockedFigure {
],
color: DEFAULT_COLOR,
showVertices: false,
showAngles: false,
fillStyle: "none",
strokeStyle: "solid",
weight: "medium",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ class InteractiveGraphQuestionBuilder {
options?: {
color?: LockedFigureColor;
showVertices?: boolean;
showAngles?: boolean;
fillStyle?: LockedFigureFillType;
strokeStyle?: LockedLineStyle;
weight?: StrokeWeight;
Expand All @@ -493,6 +494,7 @@ class InteractiveGraphQuestionBuilder {
points: points,
color: "grayH",
showVertices: false,
showAngles: false,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should pipe in the options.showAngles value here so that we can use the builder in tests/storybook. :)

fillStyle: "none",
strokeStyle: "solid",
weight: options?.weight ?? "medium",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -742,4 +742,25 @@ describe("LockedPolygonSettings", () => {
});
});
});

test("calls onChangeProps when show angle measures switch is toggled", async () => {
// Arrange
const onChangeProps = jest.fn();
render(
<LockedPolygonSettings
{...defaultProps}
onChangeProps={onChangeProps}
/>,
{wrapper: RenderStateRoot},
);

// Act
const toggle = screen.getByRole("switch", {
name: "show angle measures",
});
await userEvent.click(toggle);

// Assert
expect(onChangeProps).toHaveBeenCalledWith({showAngles: true});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const LockedPolygonSettings = (props: Props) => {
points,
color,
showVertices,
showAngles,
fillStyle,
strokeStyle,
weight,
Expand Down Expand Up @@ -256,6 +257,16 @@ const LockedPolygonSettings = (props: Props) => {
style={styles.spaceUnder}
/>

{/* Show angle measures switch */}
<LabeledSwitch
label="show angle measures"
checked={showAngles}
onChange={(newValue: boolean) =>
onChangeProps({showAngles: newValue})
}
style={styles.spaceUnder}
/>

<PerseusEditorAccordion
header={
<BodyText size="medium" weight="bold" tag="span">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,7 @@ describe("InteractiveGraphQuestionBuilder", () => {
],
color: "grayH",
showVertices: false,
showAngles: false,
fillStyle: "none",
strokeStyle: "solid",
weight: "medium",
Expand Down Expand Up @@ -1264,6 +1265,7 @@ describe("InteractiveGraphQuestionBuilder", () => {
],
color: "green",
showVertices: true,
showAngles: false,
fillStyle: "translucent",
strokeStyle: "dashed",
weight: "thin",
Expand Down Expand Up @@ -1306,6 +1308,7 @@ describe("InteractiveGraphQuestionBuilder", () => {
],
color: "grayH",
showVertices: false,
showAngles: false,
fillStyle: "none",
strokeStyle: "solid",
weight: "medium",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ class InteractiveGraphQuestionBuilder {
options?: {
color?: LockedFigureColor;
showVertices?: boolean;
showAngles?: boolean;
fillStyle?: LockedFigureFillType;
strokeStyle?: LockedLineStyle;
weight?: StrokeWeight;
Expand All @@ -522,6 +523,7 @@ class InteractiveGraphQuestionBuilder {
points: points,
color: "grayH",
showVertices: false,
showAngles: false,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should pipe in the options.showAngles value here so that we can use the builder in tests/storybook. :) (For both builders)

fillStyle: "none",
strokeStyle: "solid",
weight: options?.weight ?? "medium",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import {render, screen} from "@testing-library/react";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we were previously just using the global set of tests in locked-function.test.tsx, but I like the coverage and organization here, so I'm all far it. :)

import * as React from "react";

import * as Dependencies from "../../../dependencies";
import {testDependencies} from "../../../testing/test-dependencies";
import {MafsGraph} from "../mafs-graph";
import {getBaseMafsGraphPropsForTests} from "../utils";

import type {NoneGraphState} from "../types";
import type {LockedPolygonType} from "@khanacademy/perseus-core";
import type {vec} from "mafs";

const baseMafsGraphProps = getBaseMafsGraphPropsForTests();
const baseGraphState = {
type: "none",
range: [
[-10, 10],
[-10, 10],
],
hasBeenInteractedWith: false,
snapStep: [1, 1],
} satisfies NoneGraphState;

const baseLockedPolygonProps = {
type: "polygon",
points: [
[0, 2],
[-1, 0],
[1, 0],
],
color: "grayH",
showVertices: false,
showAngles: false,
fillStyle: "none",
strokeStyle: "solid",
weight: "medium",
labels: [],
} satisfies LockedPolygonType;

// Polygon that looks like a chevron, with a concave vertex on the left,
// drawn from the top left.
const concavePolygonClockwise = [
[-7, 5],
[1, 5],
[6, 0],
[1, -5],
[-7, -5],
[-2, 0], // concave vertex
] satisfies vec.Vector2[];

describe("LockedPolygon", () => {
beforeEach(() => {
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
testDependencies,
);
});

it("renders the polygon element", () => {
// Arrange, Act
const {container} = render(
<MafsGraph
{...baseMafsGraphProps}
state={baseGraphState}
lockedFigures={[baseLockedPolygonProps]}
/>,
);

// Assert
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
const lockedPolygon = container.querySelector(".locked-polygon");
expect(lockedPolygon).toBeInTheDocument();
});

it("does not show angle indicators when showAngles is false", () => {
// Arrange, Act
render(
<MafsGraph
{...baseMafsGraphProps}
state={baseGraphState}
lockedFigures={[
{
...baseLockedPolygonProps,
points: concavePolygonClockwise,
showAngles: false,
},
]}
/>,
);

// Assert
const angleIndicators = screen.queryAllByText(/°/);
expect(angleIndicators).toHaveLength(0);
});

it("shows correct angles for concave polygons when points are clockwise", () => {
// Arrange

// Act - render with angles showing
render(
<MafsGraph
{...baseMafsGraphProps}
state={baseGraphState}
lockedFigures={[
{
...baseLockedPolygonProps,
points: concavePolygonClockwise,
showAngles: true,
},
]}
/>,
);

const angleIndicators = screen.getAllByText(/°/);

// Assert
expect(angleIndicators).toHaveLength(concavePolygonClockwise.length);
// Checking angles in render order
expect(angleIndicators[0]).toHaveTextContent("45°");
expect(angleIndicators[1]).toHaveTextContent("135°");
expect(angleIndicators[2]).toHaveTextContent("90°");
expect(angleIndicators[3]).toHaveTextContent("135°");
expect(angleIndicators[4]).toHaveTextContent("45°");
// Concave vertex, greater than 180 degrees
expect(angleIndicators[5]).toHaveTextContent("270°");
});

it("should show correct angles for concave polygons when the points are counter-clockwise", () => {
// Arrange

// Act - render with angles showing
render(
<MafsGraph
{...baseMafsGraphProps}
state={baseGraphState}
lockedFigures={[
{
...baseLockedPolygonProps,
points: [...concavePolygonClockwise].reverse(),
showAngles: true,
},
]}
/>,
);

const angleIndicators = screen.getAllByText(/°/);

// Assert
expect(angleIndicators).toHaveLength(concavePolygonClockwise.length);
// Concave vertex, greater than 180 degrees
expect(angleIndicators[0]).toHaveTextContent("270°");
expect(angleIndicators[1]).toHaveTextContent("45°");
expect(angleIndicators[2]).toHaveTextContent("135°");
expect(angleIndicators[3]).toHaveTextContent("90°");
expect(angleIndicators[4]).toHaveTextContent("135°");
expect(angleIndicators[5]).toHaveTextContent("45°");
});
});
Loading