diff --git a/packages/perseus-editor/src/components/__tests__/interactive-graph-description.test.tsx b/packages/perseus-editor/src/components/__tests__/interactive-graph-description.test.tsx
new file mode 100644
index 0000000000..5195146d49
--- /dev/null
+++ b/packages/perseus-editor/src/components/__tests__/interactive-graph-description.test.tsx
@@ -0,0 +1,102 @@
+import {Dependencies} from "@khanacademy/perseus";
+import {render, screen} from "@testing-library/react";
+import {userEvent as userEventLib} from "@testing-library/user-event";
+import * as React from "react";
+
+import {testDependencies} from "../../../../../testing/test-dependencies";
+import InteractiveGraphDescription from "../interactive-graph-description";
+
+import type {UserEvent} from "@testing-library/user-event";
+
+import "@testing-library/jest-dom"; // Imports custom matchers
+
+function userEventForFakeTimers() {
+ return userEventLib.setup({
+ advanceTimers: jest.advanceTimersByTime,
+ });
+}
+
+describe("InteractiveGraphSettings", () => {
+ let userEvent: UserEvent;
+ beforeEach(() => {
+ userEvent = userEventForFakeTimers();
+ jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
+ testDependencies,
+ );
+ });
+
+ test("renders", () => {
+ // Arrange
+ render(
+ ,
+ );
+
+ // Act
+ const titleInput = screen.getByRole("textbox", {name: "Title"});
+ const descriptionInput = screen.getByRole("textbox", {
+ name: "Description",
+ });
+
+ // Assert
+ expect(titleInput).toBeInTheDocument();
+ expect(titleInput).toHaveValue("Graph Title");
+ expect(descriptionInput).toBeInTheDocument();
+ expect(descriptionInput).toHaveValue("Graph Description");
+ });
+
+ test("calls onChange when the title is changed", async () => {
+ // Arrange
+ const onChange = jest.fn();
+ render(
+ ,
+ );
+
+ // Act
+ const titleInput = screen.getByRole("textbox", {name: "Title"});
+ await userEvent.clear(titleInput);
+ await userEvent.type(titleInput, "Zot");
+
+ // Assert
+ // Calls are not being accumulated because they're mocked.
+ expect(onChange.mock.calls).toEqual([
+ [{fullGraphAriaLabel: "Z"}],
+ [{fullGraphAriaLabel: "o"}],
+ [{fullGraphAriaLabel: "t"}],
+ ]);
+ });
+
+ test("calls onChange when the description is changed", async () => {
+ // Arrange
+ const onChange = jest.fn();
+ render(
+ ,
+ );
+
+ // Act
+ const descriptionInput = screen.getByRole("textbox", {
+ name: "Description",
+ });
+ await userEvent.clear(descriptionInput);
+ await userEvent.type(descriptionInput, "Zot");
+
+ // Assert
+ // Calls are not being accumulated because they're mocked.
+ expect(onChange.mock.calls).toEqual([
+ [{fullGraphAriaDescription: "Z"}],
+ [{fullGraphAriaDescription: "o"}],
+ [{fullGraphAriaDescription: "t"}],
+ ]);
+ });
+});
diff --git a/packages/perseus-editor/src/components/interactive-graph-description.tsx b/packages/perseus-editor/src/components/interactive-graph-description.tsx
index 257fedbdaa..86700f3b32 100644
--- a/packages/perseus-editor/src/components/interactive-graph-description.tsx
+++ b/packages/perseus-editor/src/components/interactive-graph-description.tsx
@@ -36,7 +36,7 @@ export default function InteractiveGraphDescription(props: Props) {
are used by screen readers to describe content to users
who are visually impaired.
-
+
Title
-
+
Description
{/* TODO: Change this to a WB TextArea */}
{
}),
).toBeNull();
});
+
+ test("shows description section", () => {
+ // Arrange
+
+ // Act
+ render(
+ ,
+ {
+ wrapper: RenderStateRoot,
+ },
+ );
+
+ // Assert
+ expect(
+ screen.getByRole("textbox", {name: "Title"}),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByRole("textbox", {name: "Description"}),
+ ).toBeInTheDocument();
+ });
});
diff --git a/packages/perseus/src/widgets/interactive-graphs/interactive-graph.test.tsx b/packages/perseus/src/widgets/interactive-graphs/interactive-graph.test.tsx
index 1e2c9c90dc..8ff861a1db 100644
--- a/packages/perseus/src/widgets/interactive-graphs/interactive-graph.test.tsx
+++ b/packages/perseus/src/widgets/interactive-graphs/interactive-graph.test.tsx
@@ -19,6 +19,7 @@ import {
angleQuestionWithDefaultCorrect,
circleQuestion,
circleQuestionWithDefaultCorrect,
+ interactiveGraphWithAriaLabel,
linearQuestion,
linearQuestionWithDefaultCorrect,
linearSystemQuestion,
@@ -911,4 +912,47 @@ describe("locked layer", () => {
top: "240px",
});
});
+
+ it("should have an aria-label and description if they are provided", async () => {
+ // Arrange
+ const {container} = renderQuestion(interactiveGraphWithAriaLabel, {
+ flags: {
+ mafs: {
+ segment: true,
+ "interactive-graph-locked-features-labels": true,
+ },
+ },
+ });
+
+ // Act
+ // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
+ const graph = container.querySelector(".mafs-graph");
+
+ // Assert
+ expect(graph).toHaveAttribute("aria-label", "Segment Graph Title");
+ // The aria-describedby attribute is set to the description
+ // element's ID. This ID is unique to the graph instance, so
+ // we can't predict it in this test.
+ expect(graph).toHaveAttribute("aria-describedby");
+ });
+
+ it("should not have an aria-label or description if they are not provided", async () => {
+ // Arrange
+ const {container} = renderQuestion(segmentQuestion, {
+ flags: {
+ mafs: {
+ segment: true,
+ "interactive-graph-locked-features-labels": true,
+ },
+ },
+ });
+
+ // Act
+ // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
+ const graph = container.querySelector(".mafs-graph");
+
+ // Assert
+ expect(graph).not.toHaveAttribute("aria-label");
+ expect(graph).not.toHaveAttribute("aria-describedby");
+ });
});
diff --git a/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx b/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx
index 397fa2969a..e42191b636 100644
--- a/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx
+++ b/packages/perseus/src/widgets/interactive-graphs/mafs-graph.tsx
@@ -108,21 +108,25 @@ export const MafsGraph = (props: MafsGraphProps) => {
}
}}
aria-label={fullGraphAriaLabel}
- aria-describedby={descriptionId}
+ aria-describedby={
+ fullGraphAriaDescription ? descriptionId : undefined
+ }
ref={graphRef}
tabIndex={0}
>
-
- {fullGraphAriaDescription}
-
+ {fullGraphAriaDescription && (
+
+ {fullGraphAriaDescription}
+
+ )}