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

[SR][SR Tree] Add screen reader tree to interactive graph editor #2062

Merged
merged 16 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 12 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/four-avocados-raise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@khanacademy/perseus": patch
"@khanacademy/perseus-editor": patch
---

[SR][sr tree] Add screen reader tree to interactive graph editor
2 changes: 2 additions & 0 deletions packages/perseus-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"@khanacademy/wonder-blocks-form": "6.0.1",
"@khanacademy/wonder-blocks-icon": "5.0.3",
"@khanacademy/wonder-blocks-icon-button": "6.0.3",
"@khanacademy/wonder-blocks-pill": "3.0.3",
"@khanacademy/wonder-blocks-switch": "3.0.1",
"@khanacademy/wonder-blocks-timing": "6.0.0",
"@khanacademy/wonder-blocks-tokens": "3.0.0",
Expand Down Expand Up @@ -82,6 +83,7 @@
"@khanacademy/wonder-blocks-form": "6.0.1",
"@khanacademy/wonder-blocks-icon": "5.0.3",
"@khanacademy/wonder-blocks-icon-button": "6.0.3",
"@khanacademy/wonder-blocks-pill": "3.0.3",
"@khanacademy/wonder-blocks-switch": "3.0.1",
"@khanacademy/wonder-blocks-timing": "6.0.0",
"@khanacademy/wonder-blocks-tokens": "3.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
import {render} from "@testing-library/react";
import * as React from "react";

import {getAccessibilityAttributes} from "./interactive-graph-sr-tree";

describe("fetchAriaLabels", () => {
test("should return an empty array if the container is null", () => {
// Arrange
const container = undefined;
const expected = [];

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual(expected);
});

test("should return an array of labels", () => {
// Arrange
const {container} = render(
<div>
<div aria-label="label1" />
<div aria-label="label2" />
</div>,
);

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual([
{
role: "div",
attributes: [{name: "label", value: "label1"}],
},
{
role: "div",
attributes: [{name: "label", value: "label2"}],
},
]);
});

test("should return an array with given roles", () => {
// Arrange
const {container} = render(
<div>
<div role="button" aria-label="aria-label1" />
<div role="button" aria-label="aria-label2" />
</div>,
);

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual([
{
role: "button",
attributes: [{name: "label", value: "aria-label1"}],
},
{
role: "button",
attributes: [{name: "label", value: "aria-label2"}],
},
]);
});

test("should return an array with descriptions", () => {
// Arrange
const {container} = render(
<div>
<div aria-describedby="description1">label1</div>
<div aria-describedby="description2">label2</div>
<div id="description1">description1 content</div>
<div id="description2">description2 content</div>
</div>,
);

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual([
{
role: "div",
attributes: [
{name: "description", value: "description1 content"},
],
},
{
role: "div",
attributes: [
{name: "description", value: "description2 content"},
],
},
]);
});

test("should return an array for element with multiple descriptions", () => {
// Arrange
const {container} = render(
<div>
<div aria-describedby="description1 description2">label1</div>
<div id="description1">description1 content</div>
<div id="description2">description2 content</div>
</div>,
);

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual([
{
role: "div",
attributes: [
{name: "description", value: "description1 content"},
{name: "description", value: "description2 content"},
],
},
]);
});

test("should return an array for element with multiple descriptions with multiple spaces", () => {
// Arrange
const {container} = render(
<div>
<div aria-describedby="description1 description2">
label1
</div>
<div id="description1">description1 content</div>
<div id="description2">description2 content</div>
</div>,
);

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual([
{
role: "div",
attributes: [
{name: "description", value: "description1 content"},
{name: "description", value: "description2 content"},
],
},
]);
});

test("should include hidden descriptions", () => {
// Arrange
const {container} = render(
<div>
<div aria-describedby="description1 description2">label1</div>
<div id="description1">description1 content</div>
<div id="description2" style={{display: "hidden"}}>
description2 content
</div>
</div>,
);

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual([
{
role: "div",
attributes: [
{name: "description", value: "description1 content"},
{name: "description", value: "description2 content"},
],
},
]);
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test still passes if I change the code to use innerText instead of textContent, which is super weird. That's not how it behaves in practice. Not sure what I'm doing wrong. 🤔


test("should not include descriptions that are not found", () => {
// Arrange
const {container} = render(
<div>
<div aria-describedby="description1 description2">label1</div>
<div id="description1">description1 content</div>
</div>,
);

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual([
{
role: "div",
attributes: [
{name: "description", value: "description1 content"},
],
},
]);
});

test("should build attributes array with a variety of attributes", () => {
// Arrange
const {container} = render(
<div>
<div aria-label="label-only" />
<div aria-describedby="description-only" />
<div role="img" aria-label="label with role" />
<div
role="button"
aria-label="aria-label1"
aria-describedby="description1"
/>
<div
role="button"
aria-label="aria-label2"
aria-describedby="description2"
/>
<div id="description-only">description-only content </div>
<div id="description1">description1 content</div>
<div id="description2">description2 content</div>
</div>,
);

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual([
{
role: "div",
attributes: [{name: "label", value: "label-only"}],
},
{
role: "div",
attributes: [
{name: "description", value: "description-only content "},
],
},
{
role: "img",
attributes: [{name: "label", value: "label with role"}],
},
{
role: "button",
attributes: [
{name: "label", value: "aria-label1"},
{name: "description", value: "description1 content"},
],
},
{
role: "button",
attributes: [
{name: "label", value: "aria-label2"},
{name: "description", value: "description2 content"},
],
},
]);
});

test("should not add element if descriptions are not found", () => {
// Arrange
const {container} = render(
<div>
<div aria-describedby="description1 description2">label1</div>
</div>,
);

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual([]);
});

test("should not add element if aria attributes are not found", () => {
// Arrange
const {container} = render(
<div>
<div />
</div>,
);

// Act
const result = getAccessibilityAttributes(container);

// Assert
expect(result).toEqual([]);
});
});
Loading
Loading