Skip to content

Commit

Permalink
[Locked Figure Aria] Use spoken math in Locked Line settings autogen …
Browse files Browse the repository at this point in the history
…labels (#1854)

## Summary:
When auto-generating the aria labels for locked figures, we want it to use
words as if they were spoken rather than math expressions that might be
read incorrectly by the screen reader.

- Use the `generateSpokenMathDetails` utility within LockedLineSettings,
  following the pattern used in LockedPointSettings for this.
- Update the locked line autogen function to include the labels
  on the locked points that define the locked line.

Issue: https://khanacademy.atlassian.net/browse/LEMS-2548

## Test plan:
- `yarn jest packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.test.tsx`

Storybook
- Go to http://localhost:6006/iframe.html?args=&id=perseuseditor-widgets-interactive-graph--mafs-with-locked-figure-labels-all-flags&viewMode=story
- Open the locked line settings
- Change the visible label to hvae a mix of TeX (with `$...$`) and non-TeX
- Press the "Auto-generate" button
- Confirm that the input changes to include spoken math words for the TeX
- Also try this with no labels, multiple labels, and the labels changed
  for the locked points that define the line

<img width="930" alt="image" src="https://github.com/user-attachments/assets/7182c93d-5494-46f3-83d7-0dbadad019bd">

Author: nishasy

Reviewers: benchristel, nishasy, catandthemachines, #perseus, anakaren-rojas

Required Reviewers:

Approved By: catandthemachines

Checks: ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ gerald

Pull Request URL: #1854
  • Loading branch information
nishasy authored Nov 15, 2024
1 parent c303009 commit ef0ad98
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 8 deletions.
6 changes: 6 additions & 0 deletions .changeset/weak-glasses-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@khanacademy/perseus": patch
"@khanacademy/perseus-editor": patch
---

[Locked Figure Aria] Add math descriptions to locked line aria labels
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ const defaultProps = {

const defaultLabel = getDefaultFigureForType("label");

// Mock the async function generateSpokenMathDetails
jest.mock("./util", () => ({
...jest.requireActual("./util"),
generateSpokenMathDetails: (input) => {
return Promise.resolve(`Spoken math details for ${input}`);
},
}));

describe("LockedLineSettings", () => {
let userEvent: UserEvent;
beforeEach(() => {
Expand Down Expand Up @@ -615,7 +623,7 @@ describe("LockedLineSettings", () => {
// Assert
expect(onChangeProps).toHaveBeenCalledWith({
ariaLabel:
"Segment from (0, 0) to (2, 2). Appearance solid gray.",
"Spoken math details for Segment from point at (0, 0) to point at (2, 2). Appearance solid gray.",
});
});

Expand All @@ -640,7 +648,8 @@ describe("LockedLineSettings", () => {

// Assert
expect(onChangeProps).toHaveBeenCalledWith({
ariaLabel: "Line from (0, 0) to (2, 2). Appearance solid gray.",
ariaLabel:
"Spoken math details for Line from point at (0, 0) to point at (2, 2). Appearance solid gray.",
});
});

Expand Down Expand Up @@ -671,7 +680,7 @@ describe("LockedLineSettings", () => {
// Assert
expect(onChangeProps).toHaveBeenCalledWith({
ariaLabel:
"Line A from (0, 0) to (2, 2). Appearance solid gray.",
"Spoken math details for Line A from point at (0, 0) to point at (2, 2). Appearance solid gray.",
});
});

Expand Down Expand Up @@ -706,7 +715,99 @@ describe("LockedLineSettings", () => {
// Assert
expect(onChangeProps).toHaveBeenCalledWith({
ariaLabel:
"Line A, B from (0, 0) to (2, 2). Appearance solid gray.",
"Spoken math details for Line A, B from point at (0, 0) to point at (2, 2). Appearance solid gray.",
});
});

test("aria label auto-generates (one label, including points)", async () => {
// Arrange
const onChangeProps = jest.fn();
render(
<LockedLineSettings
{...defaultProps}
ariaLabel={undefined}
onChangeProps={onChangeProps}
labels={[
{
...defaultLabel,
text: "A",
},
]}
points={[
{
...defaultProps.points[0],
labels: [{...defaultLabel, text: "C"}],
},
{
...defaultProps.points[1],
labels: [{...defaultLabel, text: "D"}],
},
]}
/>,
{wrapper: RenderStateRoot},
);

// Act
const autoGenButton = screen.getByRole("button", {
name: "Auto-generate",
});
await userEvent.click(autoGenButton);

// Assert
expect(onChangeProps).toHaveBeenCalledWith({
ariaLabel:
"Spoken math details for Line A from point C at (0, 0) to point D at (2, 2). Appearance solid gray.",
});
});

test("aria label auto-generates (multiple labels, including points)", async () => {
// Arrange
const onChangeProps = jest.fn();
render(
<LockedLineSettings
{...defaultProps}
ariaLabel={undefined}
onChangeProps={onChangeProps}
labels={[
{
...defaultLabel,
text: "A",
},
{
...defaultLabel,
text: "B",
},
]}
points={[
{
...defaultProps.points[0],
labels: [
{...defaultLabel, text: "C"},
{...defaultLabel, text: "C2"},
],
},
{
...defaultProps.points[1],
labels: [
{...defaultLabel, text: "D"},
{...defaultLabel, text: "D2"},
],
},
]}
/>,
{wrapper: RenderStateRoot},
);

// Act
const autoGenButton = screen.getByRole("button", {
name: "Auto-generate",
});
await userEvent.click(autoGenButton);

// Assert
expect(onChangeProps).toHaveBeenCalledWith({
ariaLabel:
"Spoken math details for Line A, B from point C, C2 at (0, 0) to point D, D2 at (2, 2). Appearance solid gray.",
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import LockedLabelSettings from "./locked-label-settings";
import LockedPointSettings from "./locked-point-settings";
import {
generateLockedFigureAppearanceDescription,
generateSpokenMathDetails,
getDefaultFigureForType,
} from "./util";

Expand Down Expand Up @@ -73,13 +74,34 @@ const LockedLineSettings = (props: Props) => {

// Check if the line has length 0.
const isInvalid = kvector.equal(point1.coord, point2.coord);

function getPrepopulatedAriaLabel() {
/**
* Generate a prepopulated aria label for the line, with the math
* details converted into spoken words.
*/
async function getPrepopulatedAriaLabel() {
let visiblelabel = "";
let point1VisibleLabel = "";
let point2VisibleLabel = "";

if (labels && labels.length > 0) {
visiblelabel += ` ${labels.map((l) => l.text).join(", ")}`;
}
let str = `${capitalizeKind}${visiblelabel} from (${point1.coord[0]}, ${point1.coord[1]}) to (${point2.coord[0]}, ${point2.coord[1]})`;

if (point1.labels && point1.labels.length > 0) {
point1VisibleLabel += ` ${point1.labels
.map((l) => l.text)
.join(", ")}`;
}

if (point2.labels && point2.labels.length > 0) {
point2VisibleLabel += ` ${point2.labels
.map((l) => l.text)
.join(", ")}`;
}

let str = await generateSpokenMathDetails(
`${capitalizeKind}${visiblelabel} from point${point1VisibleLabel} at (${point1.coord[0]}, ${point1.coord[1]}) to point${point2VisibleLabel} at (${point2.coord[0]}, ${point2.coord[1]})`,
);
const lineAppearance = generateLockedFigureAppearanceDescription(
lineColor,
lineStyle,
Expand Down Expand Up @@ -270,7 +292,7 @@ const LockedLineSettings = (props: Props) => {

<LockedFigureAria
ariaLabel={ariaLabel}
prePopulatedAriaLabel={getPrepopulatedAriaLabel()}
getPrepopulatedAriaLabel={getPrepopulatedAriaLabel}
onChangeProps={(newProps) => {
onChangeProps(newProps);
}}
Expand Down

0 comments on commit ef0ad98

Please sign in to comment.