-
Notifications
You must be signed in to change notification settings - Fork 350
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] Linear System - add screen reader support for Linear System interactive graph #2030
base: main
Are you sure you want to change the base?
Conversation
The [SRUX doc](https://khanacademy.atlassian.net/wiki/spaces/LC/pages/3460366348/Linear) still needs a label for the grab handle, but I tried my best in the meantime. - Add a label and describedby for the grab handle. - Add aria-live states for the different interactive elements so they don't override each other. Issue: https://khanacademy.atlassian.net/browse/LEMS-1726 Test plan: `yarn jest packages/perseus/src/widgets/interactive-graphs/graphs/linear.test.tsx` Storybook - http://localhost:6006/iframe.html?id=perseuseditor-widgets-interactive-graph--interactive-graph-linear&viewMode=story - Try all the different slopes and intercepts - Move different elements and confirm that the updated aria-label is what is read out, and none of the other elements override the currently focused one.
…inear System interactive graph
npm Snapshot: PublishedGood news!! We've packaged up the latest commit from this PR (9ac9782) and published it to npm. You Example: yarn add @khanacademy/perseus@PR2030 If you are working in Khan Academy's webapp, you can run: ./dev/tools/bump_perseus_version.sh -t PR2030 |
Size Change: +618 B (+0.05%) Total Size: 1.28 MB
ℹ️ View Unchanged
|
The parent pull-request (#2025) has been merged into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The experience is working for the individual points and lines, but I think we need to circle back and make sure it's working at the all up graph level.
return ( | ||
<> | ||
<g | ||
aria-label={strings.srLinearSystemGraph} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This aria-label is not getting read out when navigating to the whole graph. This is because the whole graph descriptions needs to happen at the mafs-graph.tsx level.
packages/perseus/src/strings.ts
Outdated
@@ -478,6 +516,13 @@ export const strings: { | |||
srAngleGraphAriaLabel: "An angle on a coordinate plane.", | |||
srAngleGraphAriaDescription: | |||
"The angle measure is %(angleMeasure)s degrees with a vertex at %(vertexX)s comma %(vertexY)s, a point on the starting side at %(startingSideX)s comma %(startingSideY)s and a point on the ending side at %(endingSideX)s comma %(endingSideY)s", | |||
srLinearSystemGraph: "Two lines on a coordinate plane.", | |||
srLinearSystemPoints: | |||
"Line %(lineSequence)s has two points, point 1 at %(point1X)s comma %(point1Y)s and point 2 at %(point2X)s comma %(point2Y)s.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
%(lineSequence)s
made me think this is a placeholder for a description of a sequence of lines. Translators might find it confusing as well. I might prefer lineSequenceNumber
or maybe just lineNumber
.
"Line %(lineSequence)s has two points, point 1 at %(point1X)s comma %(point1Y)s and point 2 at %(point2X)s comma %(point2Y)s.", | |
"Line %(lineSequenceNumber)s has two points, point 1 at %(point1X)s comma %(point1Y)s and point 2 at %(point2X)s comma %(point2Y)s.", |
expect(linearSystemGraph).toBeInTheDocument(); | ||
expect(linearSystemGraph).toHaveAttribute( | ||
"aria-describedby", | ||
":r1:-line1-points :r1:-line1-intercept :r1:-line1-slope :r1:-line2-points :r1:-line2-intercept :r1:-line2-slope", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish we could assert on the description text instead of the IDs of the description elements. That way we'd be testing what the user actually sees. If React Testing Library doesn't have any matchers for this (I didn't find any with a cursory search) maybe we should write our own. Then we could do something like:
expect(linearSystemGraph).toHaveAriaDescription("line 1 through 0 comma 0 and 5 comma 5 ...")
Our custom matchers for Jest are defined in config/test/custom-matchers.ts
.
test.each` | ||
case | coords | slopeDescription | ||
${"positive slope"} | ${[[1, 1], [3, 3]]} | ${`Its slope increases from left to right.`} | ||
${"negative slope"} | ${[[3, 3], [1, 6]]} | ${`Its slope decreases from left to right.`} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should get a content creator to weigh in on this wording. Strictly speaking, I don't think the slope increases or decreases; the slope is constant and it's the y-coordinate that increases or decreases.
Maybe "Its slope is positive/negative/zero/undefined" would be a good way to phrase this? In any case, I'd defer to Charlie or Nick.
pointsDescriptionId: id + `-line${i + 1}-points`, | ||
interceptDescriptionId: id + `-line${i + 1}-intercept`, | ||
slopeDescriptionId: id + `-line${i + 1}-slope`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: as long as we're using a template string, we could include id
in it too:
pointsDescriptionId: id + `-line${i + 1}-points`, | |
interceptDescriptionId: id + `-line${i + 1}-intercept`, | |
slopeDescriptionId: id + `-line${i + 1}-slope`, | |
pointsDescriptionId: `${id}-line${i + 1}-points`, | |
interceptDescriptionId: `${id}-line${i + 1}-intercept`, | |
slopeDescriptionId: `${id}-line${i + 1}-slope`, |
<g | ||
key={slopeDescriptionId} | ||
id={slopeDescriptionId} | ||
style={{display: "hidden"}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of display: hidden
here? I don't think hidden
is a valid value for display
(MDN docs) and either display: none
or visibility: hidden
will hide the content from screenreaders.
I think we probably want to apply an srOnly
set of styles here.
}); | ||
} | ||
const slopeString = getSlopeStringForLine(line, strings); | ||
const interceptString = getInterceptStringForLine(line, strings, locale); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 for refactoring this!
|
||
export function getSlopeStringForLine(line: PairOfPoints, strings): string { | ||
const slope = (line[1][1] - line[0][1]) / (line[1][0] - line[0][0]); | ||
if (slope === Infinity || slope === -Infinity) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, you could use Number.isFinite
here:
if (slope === Infinity || slope === -Infinity) { | |
if (!Number.isFinite(slope)) { |
That will also cause srLinearGraphSlopeVertical
to be returned if the slope is NaN
. (A slope of NaN
shouldn't be possible, since we prevent the two control points of the line from having the same coordinates, so this is more paranoia than anything.)
const hasXIntercept = xIntercept !== Infinity && xIntercept !== -Infinity; | ||
const hasYIntercept = yIntercept !== Infinity && yIntercept !== -Infinity; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Number.isFinite
would be a nice simplification here, too. Actually, I think there's a bug that isFinite would fix, because NaN
is a possibility here. E.g. if line[0][1]
is 0 and the slope is also 0, the xIntercept
will be NaN
(since the line lies on the x-axis). Might be worth proving that with a test.
const hasXIntercept = xIntercept !== Infinity && xIntercept !== -Infinity; | |
const hasYIntercept = yIntercept !== Infinity && yIntercept !== -Infinity; | |
const hasXIntercept = Number.isFinite(xIntercept); | |
const hasYIntercept = Number.isFinite(yIntercept); |
Summary:
Add the aria label and descriptions for the full graph and the
interactive elements in the Linear System graph, based on the
SRUX doc.
Issue: https://khanacademy.atlassian.net/browse/LEMS-1727
Test plan:
yarn jest packages/perseus/src/widgets/interactive-graphs/graphs/linear-system.test.tsx
Storybook
View Storybook publish in the checks below to try it yourself.
Screen.Recording.2024-12-18.at.2.04.53.PM.mov