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

[Locked Figure Labels] Add/edit/delete locked vector labels #1652

Merged
merged 5 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
6 changes: 6 additions & 0 deletions .changeset/modern-sheep-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@khanacademy/perseus": minor
"@khanacademy/perseus-editor": minor
---

[Locked Figure Labels] Add/edit/delete locked vector labels
6 changes: 6 additions & 0 deletions .changeset/strange-houses-breathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@khanacademy/perseus": minor
"@khanacademy/perseus-editor": minor
---

[Locked Figure Labels] View locked vector labels
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const flags = {
"interactive-graph-locked-features-labels": true,
"locked-point-labels": true,
"locked-line-labels": true,
"locked-vector-labels": true,
},
} satisfies APIOptions["flags"];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ export const MafsWithLockedFiguresCurrent = (): React.ReactElement => {
"interactive-graph-locked-features-labels": false,
"locked-point-labels": false,
"locked-line-labels": false,
"locked-vector-labels": false,
},
},
}}
Expand All @@ -181,6 +182,7 @@ export const MafsWithLockedLabelsFlag = (): React.ReactElement => {
"interactive-graph-locked-features-labels": true,
"locked-point-labels": false,
"locked-line-labels": false,
"locked-vector-labels": false,
},
},
}}
Expand All @@ -189,14 +191,6 @@ export const MafsWithLockedLabelsFlag = (): React.ReactElement => {
);
};

MafsWithLockedLabelsFlag.parameters = {
chromatic: {
// Disabling because this isn't visually testing anything on the
// initial load of the editor page.
disable: true,
},
};

export const MafsWithLockedPointLabelsFlag = (): React.ReactElement => {
return (
<EditorPageWithStorybookPreview
Expand All @@ -207,6 +201,7 @@ export const MafsWithLockedPointLabelsFlag = (): React.ReactElement => {
"interactive-graph-locked-features-labels": true,
"locked-point-labels": true,
"locked-line-labels": false,
"locked-vector-labels": false,
},
},
}}
Expand All @@ -215,14 +210,6 @@ export const MafsWithLockedPointLabelsFlag = (): React.ReactElement => {
);
};

MafsWithLockedPointLabelsFlag.parameters = {
chromatic: {
// Disabling because this isn't visually testing anything on the
// initial load of the editor page.
disable: true,
},
};

export const MafsWithLockedLineLabelsFlag = (): React.ReactElement => {
return (
<EditorPageWithStorybookPreview
Expand All @@ -233,6 +220,7 @@ export const MafsWithLockedLineLabelsFlag = (): React.ReactElement => {
"interactive-graph-locked-features-labels": true,
"locked-point-labels": false,
"locked-line-labels": true,
"locked-vector-labels": false,
},
},
}}
Expand All @@ -241,12 +229,23 @@ export const MafsWithLockedLineLabelsFlag = (): React.ReactElement => {
);
};

MafsWithLockedLineLabelsFlag.parameters = {
chromatic: {
// Disabling because this isn't visually testing anything on the
// initial load of the editor page.
disable: true,
},
export const MafsWithLockedVectorLabelsFlag = (): React.ReactElement => {
return (
<EditorPageWithStorybookPreview
apiOptions={{
flags: {
mafs: {
...flags.mafs,
"interactive-graph-locked-features-labels": true,
"locked-point-labels": false,
"locked-line-labels": false,
"locked-vector-labels": true,
},
},
}}
question={segmentWithLockedFigures}
/>
);
};

export const WithSaveWarnings = (): React.ReactElement => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,30 @@ import {render, screen} from "@testing-library/react";
import {userEvent as userEventLib} from "@testing-library/user-event";
import * as React from "react";

import {flags} from "../../../__stories__/flags-for-api-options";

import LockedVectorSettings from "./locked-vector-settings";
import {getDefaultFigureForType} from "./util";

import type {Props} from "./locked-vector-settings";
import type {UserEvent} from "@testing-library/user-event";

const defaultProps = {
flags: {
...flags,
mafs: {
...flags.mafs,
"locked-line-settings": true,
},
},
...getDefaultFigureForType("vector"),
onChangeProps: () => {},
onMove: () => {},
onRemove: () => {},
} as Props;

const defaultLabel = getDefaultFigureForType("label");

describe("Locked Vector Settings", () => {
let userEvent: UserEvent;
beforeEach(() => {
Expand Down Expand Up @@ -104,7 +115,10 @@ describe("Locked Vector Settings", () => {
await userEvent.click(colorOption);

// Assert
expect(onChangeProps).toHaveBeenCalledWith({color: "green"});
expect(onChangeProps).toHaveBeenCalledWith({
color: "green",
labels: [],
});
});

test("shows an error when the vector length is zero", () => {
Expand Down Expand Up @@ -176,4 +190,184 @@ describe("Locked Vector Settings", () => {
}
});
});

describe("Labels", () => {
test("Updates the label coords when the vector coords change", async () => {
// Arrange
const onChangeProps = jest.fn();
render(
<LockedVectorSettings
{...defaultProps}
points={[
[0, 0],
[2, 2],
]}
labels={[
{
...defaultLabel,
// Offset by 0.5, 0.5 from the line's midpoint
// of [1, 1].
coord: [1.5, 1.5],
},
]}
onChangeProps={onChangeProps}
/>,
{wrapper: RenderStateRoot},
);

// Act
const point1XInput = screen.getAllByLabelText("x coord")[1];
// Change the x coord of the second point to 20
await userEvent.type(point1XInput, "0");

// Assert
expect(onChangeProps).toHaveBeenCalledWith({
points: [
[0, 0],
[20, 2],
],
labels: [
{
...defaultLabel,
coord: [10.5, 1.5],
},
],
});
});

test("Updates the label color when the vector color changes", async () => {
// Arrange
const onChangeProps = jest.fn();
render(
<LockedVectorSettings
{...defaultProps}
color="green"
labels={[
{
...defaultLabel,
color: "green",
},
]}
onChangeProps={onChangeProps}
/>,
{wrapper: RenderStateRoot},
);

// Act
const colorSelect = screen.getByLabelText("color");
await userEvent.click(colorSelect);
const colorOption = screen.getByText("pink");
await userEvent.click(colorOption);

// Assert
expect(onChangeProps).toHaveBeenCalledWith({
color: "pink",
labels: [
{
...defaultLabel,
color: "pink",
},
],
});
});

test("Updates the label when the label text changes", async () => {
// Arrange
const onChangeProps = jest.fn();
render(
<LockedVectorSettings
{...defaultProps}
labels={[
{
...defaultLabel,
text: "label text",
},
]}
onChangeProps={onChangeProps}
/>,
{wrapper: RenderStateRoot},
);

// Act
const labelText = screen.getByLabelText("TeX");
await userEvent.type(labelText, "!");

// Assert
expect(onChangeProps).toHaveBeenCalledWith({
labels: [{...defaultLabel, text: "label text!"}],
});
});

test("Removes label when delete button is clicked", async () => {
// Arrange
const onChangeProps = jest.fn();
render(
<LockedVectorSettings
{...defaultProps}
labels={[
{
...defaultLabel,
text: "label text",
},
]}
onChangeProps={onChangeProps}
/>,
{wrapper: RenderStateRoot},
);

// Act
const deleteButton = screen.getByRole("button", {
name: "Delete locked label",
});
await userEvent.click(deleteButton);

// Assert
expect(onChangeProps).toHaveBeenCalledWith({
labels: [],
});
});

test("Adds a new label when the add label button is clicked", async () => {
// Arrange
const onChangeProps = jest.fn();
render(
<LockedVectorSettings
{...defaultProps}
labels={[
{
...defaultLabel,
text: "label text",
},
]}
onChangeProps={onChangeProps}
/>,
{wrapper: RenderStateRoot},
);

// Act
const addLabelButtons = screen.getAllByRole("button", {
name: "Add visible label",
});
// The last button is the one for the whole line, not for
// the points the define the line.
const addLabelButton = addLabelButtons[addLabelButtons.length - 1];
await userEvent.click(addLabelButton);

// Assert
expect(onChangeProps).toHaveBeenCalledWith({
labels: [
{
...defaultLabel,
text: "label text",
},
{
...defaultLabel,
// Midpoint of line [[0, 0], [2, 2]] is [1, 1].
// Offset 1 down vertically for each preceding label.
coord: [1, 0],
},
],
});
});
});
});
Loading
Loading