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 Aria] Use spoken math in Locked Line settings autogen labels #1854

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

nishasy
Copy link
Contributor

@nishasy nishasy commented Nov 14, 2024

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

image

… label, update type on condenseTextNodes params
…labels

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
Copy link
Contributor

github-actions bot commented Nov 14, 2024

npm Snapshot: Published

Good news!! We've packaged up the latest commit from this PR (5aa9a90) and published it to npm. You
can install it using the tag PR1854.

Example:

yarn add @khanacademy/perseus@PR1854

If you are working in Khan Academy's webapp, you can run:

./dev/tools/bump_perseus_version.sh -t PR1854

Copy link
Contributor

github-actions bot commented Nov 14, 2024

Size Change: +72 B (+0.01%)

Total Size: 1.29 MB

Filename Size Change
packages/perseus-editor/dist/es/index.js 699 kB +72 B (+0.01%)
ℹ️ View Unchanged
Filename Size
packages/kas/dist/es/index.js 38.9 kB
packages/keypad-context/dist/es/index.js 760 B
packages/kmath/dist/es/index.js 4.27 kB
packages/math-input/dist/es/index.js 77.8 kB
packages/math-input/dist/es/strings.js 1.79 kB
packages/perseus-core/dist/es/index.js 1.48 kB
packages/perseus-linter/dist/es/index.js 22.2 kB
packages/perseus/dist/es/index.js 420 kB
packages/perseus/dist/es/strings.js 3.54 kB
packages/pure-markdown/dist/es/index.js 3.66 kB
packages/simple-markdown/dist/es/index.js 12.4 kB

compressed-size-action

@nishasy nishasy marked this pull request as ready for review November 14, 2024 01:33
@khan-actions-bot
Copy link
Contributor

khan-actions-bot commented Nov 14, 2024

Gerald

Required Reviewers
  • @Khan/perseus for changes to .changeset/weak-glasses-allow.md, packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.test.tsx, packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-line-settings.tsx

Don't want to be involved in this pull request? Comment #removeme and we won't notify you of further changes.

Copy link
Member

@benchristel benchristel left a comment

Choose a reason for hiding this comment

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

I think there might be a bug when the labels contain incomplete TeX expressions (i.e. an odd number of unescaped dollar signs). I left some comments/questions inline.

let visiblelabel = "";
let point1VisibleLabel = "";
let point2VisibleLabel = "";
Copy link
Member

Choose a reason for hiding this comment

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

Why are these variables called *VisibleLabel if this function is generating an aria label?

...oh, I think I get it. The term "label" is overloaded. We're using "visible label" to contrast with "aria label". If we just called the point labels "labels" that might be confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, it's expressing the the locked (visible) label that's associated with this figure is being added to the aria label.


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]})`,
);
Copy link
Member

Choose a reason for hiding this comment

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

The duplication of the labels.map((l) => l.text).join(", ") logic makes me think there's a missing abstraction here. What exactly that abstraction should be probably depends on how labels are being constructed for other locked figure types. It would be nice if we could represent this logic in one place.

I'm imagining something like:

function describeLockedPoint(point: LockedPointType): string {
  const {coord, labels} = point;
  const visibleLabel = labels && labels.length > 0
    ? ` ${labels.map((l) => l.text).join(", ")}`
    : "";

  return `point${visibleLabel} at (${coord[0]}, ${coord[1]})`
}

but I'm not sure this would be suitable for all locked figures.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alternatively, maybe we could have a function like joinLockedFigureLabels that takes in an array of LockedLabels and then gets used within here. I thought about abstracting away the whole description, but the description does change a bit for every figure, which is why I didn't end up going that way.

Copy link
Member

Choose a reason for hiding this comment

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

I would definitely be for creating a function for this. Especially since we could potentially re-use it in future implementations. Plus we can make calls to joinLockedFigureLabels and add a comment describing the string it's generating. 😊


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]})`,
);
Copy link
Member

Choose a reason for hiding this comment

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

I think there might be a bug here when the labels include dollar signs.

Because we're calling generateSpokenMathDetails on the concatenated string, an unescaped dollar sign in one label might pair with an unescaped dollar sign in another label to create a TeX expression where there shouldn't be one.

For example, if one point had the label $1 and another point had the label $2, the string passed to generateSpokenMathDetails would be:

Line from point $1 at (0, 0) to point $2 at (1, 1)

And that would get interpreted as containing a TeX expression 1 at (0, 0) to point which is not going to speechify nicely (because every letter will be interpreted as a separate variable).

We could fix this by calling generateSpokenMathDetails for each label individually. For the example above, that would mean we'd get TeX syntax errors for both labels due to the unescaped dollar signs.

That makes me wonder: how are we handling TeX syntax errors?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A few points:

  1. I don't think you can use unescaped dollar signs as labels, so I'm not sure how to recreate the case you're referring to.
  2. I can still call generateSpokenMathDetails on each label individually. The reason I didn't do this to begin with is that if it's a math block, it doesn't include the space before, but if it's regular text, it is. So I'd have to add an extra bit of logic to handle this case.
  3. TeX syntax errors show up really ugly in the preview with big red text. We don't handle them specifically.
image

Copy link
Member

Choose a reason for hiding this comment

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

Interesting! Can we file a bug to at some point add an error message into the form for Visible labels? We don't need to inflate the scope of your work but I think this is a small change that would make this error more clear to content creators! ❤️

@nishasy nishasy changed the base branch from spoken-aria-labels to main November 14, 2024 22:30
An error occurred while trying to automatically change base from spoken-aria-labels to main November 14, 2024 22:30
@khan-actions-bot khan-actions-bot requested a review from a team November 14, 2024 22:30
Copy link
Member

@catandthemachines catandthemachines left a comment

Choose a reason for hiding this comment

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

Looking pretty good, would love to see an abstracted function for the label joining and a bug for the syntax errors <3


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]})`,
);
Copy link
Member

Choose a reason for hiding this comment

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

I would definitely be for creating a function for this. Especially since we could potentially re-use it in future implementations. Plus we can make calls to joinLockedFigureLabels and add a comment describing the string it's generating. 😊


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]})`,
);
Copy link
Member

Choose a reason for hiding this comment

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

Interesting! Can we file a bug to at some point add an error message into the form for Visible labels? We don't need to inflate the scope of your work but I think this is a small change that would make this error more clear to content creators! ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants