-
Notifications
You must be signed in to change notification settings - Fork 352
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Locked Figure Aria] Locked point aria label editor UI (#1682)
## Summary: - Create a new `locked-figure-aria.tsx` file that can be reused for each locked figure's settings. - Use this `locked-figure-aria.tsx` component within LockedPointSettings. - Write the function to auto-generate the locked point aria label with its coordinates and visible labels. Issue: https://khanacademy.atlassian.net/browse/LEMS-2375 ## Test plan: - `yarn jest packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-point-settings.test.tsx` - `yarn jest packages/perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-figure-aria.test.tsx` Storybook - http://localhost:6006/?path=/story/perseuseditor-widgets-interactive-graph--mafs-with-locked-figure-labels-all-flags - Confirm that the locked point aria label field is already populated with "Point A" (passed in via builder) - Confirm that pressing "Auto-generate" works with no labels, one label, and multiple labels - Confirm that updateing the aria label in the editor also updates the aria label on the point - Check the web inspector to confirm the aria-label text is updated - Use a screen reader to confirm the new label is read out <img width="399" alt="image" src="https://github.com/user-attachments/assets/3fef8c13-e4f9-4c62-b63b-63bf34824508"> Author: nishasy Reviewers: catandthemachines, #perseus, benchristel, anakaren-rojas Required Reviewers: Approved By: catandthemachines Checks: ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ gerald Pull Request URL: #1682
- Loading branch information
Showing
6 changed files
with
389 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@khanacademy/perseus": minor | ||
"@khanacademy/perseus-editor": minor | ||
--- | ||
|
||
[Locked Figure Aria] Locked point aria label editor UI |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
109 changes: 109 additions & 0 deletions
109
...us-editor/src/widgets/interactive-graph-editor/locked-figures/locked-figure-aria.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
import {render, screen} from "@testing-library/react"; | ||
import {userEvent as userEventLib} from "@testing-library/user-event"; | ||
import * as React from "react"; | ||
|
||
import LockedFigureAria from "./locked-figure-aria"; | ||
|
||
import type {UserEvent} from "@testing-library/user-event"; | ||
|
||
describe("LockedFigureAria", () => { | ||
let userEvent: UserEvent; | ||
beforeEach(() => { | ||
userEvent = userEventLib.setup({ | ||
advanceTimers: jest.advanceTimersByTime, | ||
}); | ||
}); | ||
|
||
test("renders", () => { | ||
// Arrange | ||
|
||
// Act | ||
render( | ||
<LockedFigureAria | ||
ariaLabel={undefined} | ||
prePopulatedAriaLabel="Pre-populated aria label" | ||
onChangeProps={() => {}} | ||
/>, | ||
); | ||
|
||
const titleText = screen.getByText("Aria label"); | ||
const descriptionText = screen.getByText( | ||
"The figure is hidden from screen readers if this field is left blank.", | ||
); | ||
const input = screen.getByRole("textbox"); | ||
const autoGenButton = screen.getByRole("button", { | ||
name: "Auto-generate", | ||
}); | ||
|
||
// Assert | ||
expect(titleText).toBeInTheDocument(); | ||
expect(descriptionText).toBeInTheDocument(); | ||
expect(input).toBeInTheDocument(); | ||
expect(input).toHaveValue(""); | ||
expect(autoGenButton).toBeInTheDocument(); | ||
}); | ||
|
||
test("renders with aria label", () => { | ||
// Arrange | ||
|
||
// Act | ||
render( | ||
<LockedFigureAria | ||
ariaLabel="Point at (x, y)" | ||
prePopulatedAriaLabel="Pre-populated aria label" | ||
onChangeProps={() => {}} | ||
/>, | ||
); | ||
|
||
const input = screen.getByRole("textbox"); | ||
|
||
// Assert | ||
expect(input).toHaveValue("Point at (x, y)"); | ||
}); | ||
|
||
test("auto-generate button calls onChange with the prepopulated label", async () => { | ||
// Arrange | ||
const onChangeProps = jest.fn(); | ||
|
||
// Act | ||
render( | ||
<LockedFigureAria | ||
ariaLabel={undefined} | ||
prePopulatedAriaLabel="Pre-populated aria label" | ||
onChangeProps={onChangeProps} | ||
/>, | ||
); | ||
|
||
const autoGenButton = screen.getByRole("button", { | ||
name: "Auto-generate", | ||
}); | ||
|
||
await userEvent.click(autoGenButton); | ||
|
||
// Assert | ||
expect(onChangeProps).toHaveBeenCalledWith({ | ||
ariaLabel: "Pre-populated aria label", | ||
}); | ||
}); | ||
|
||
test("calls onChange with undefined when input is cleared", async () => { | ||
// Arrange | ||
const onChangeProps = jest.fn(); | ||
render( | ||
<LockedFigureAria | ||
ariaLabel="Point at (x, y)" | ||
prePopulatedAriaLabel="Pre-populated aria label" | ||
onChangeProps={onChangeProps} | ||
/>, | ||
); | ||
|
||
// Act | ||
const input = screen.getByRole("textbox"); | ||
await userEvent.clear(input); | ||
|
||
// Assert | ||
expect(onChangeProps).toHaveBeenCalledWith({ | ||
ariaLabel: undefined, | ||
}); | ||
}); | ||
}); |
88 changes: 88 additions & 0 deletions
88
...perseus-editor/src/widgets/interactive-graph-editor/locked-figures/locked-figure-aria.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import {components} from "@khanacademy/perseus"; | ||
import Button from "@khanacademy/wonder-blocks-button"; | ||
import {View} from "@khanacademy/wonder-blocks-core"; | ||
import {LabeledTextField} from "@khanacademy/wonder-blocks-form"; | ||
import {Spring} from "@khanacademy/wonder-blocks-layout"; | ||
import {spacing} from "@khanacademy/wonder-blocks-tokens"; | ||
import {LabelMedium} from "@khanacademy/wonder-blocks-typography"; | ||
import pencilCircle from "@phosphor-icons/core/regular/pencil-circle.svg"; | ||
import {StyleSheet} from "aphrodite"; | ||
import * as React from "react"; | ||
|
||
const {InfoTip} = components; | ||
|
||
type Props = { | ||
ariaLabel: string | undefined; | ||
prePopulatedAriaLabel: string; | ||
onChangeProps: (props: {ariaLabel?: string | undefined}) => void; | ||
}; | ||
|
||
function LockedFigureAria(props: Props) { | ||
const {ariaLabel, prePopulatedAriaLabel, onChangeProps} = props; | ||
|
||
return ( | ||
<View> | ||
<LabeledTextField | ||
label={ | ||
<View style={styles.row}> | ||
<LabelMedium>Aria label</LabelMedium> | ||
<Spring /> | ||
<InfoTip> | ||
Aria label is used by screen readers to describe | ||
content to users who may be visually impaired.{" "} | ||
<br /> | ||
<br /> | ||
Populating this field will make it so that users can | ||
use a screen reader to navigate to this point and | ||
hear the description. | ||
<br /> | ||
<br /> | ||
If you leave this field blank, the point will be | ||
hidden from screen readers. Users will not be able | ||
to navigate to this point using a screen reader. | ||
</InfoTip> | ||
</View> | ||
} | ||
description={`The figure is hidden from screen readers | ||
if this field is left blank.`} | ||
value={ariaLabel ?? ""} | ||
onChange={(newValue) => { | ||
onChangeProps({ | ||
// Save as undefined if the field is empty. | ||
ariaLabel: newValue || undefined, | ||
}); | ||
}} | ||
placeholder="Ex. Point at (x, y)" | ||
style={styles.ariaLabelTextField} | ||
/> | ||
|
||
<Button | ||
kind="tertiary" | ||
startIcon={pencilCircle} | ||
style={styles.button} | ||
onClick={() => { | ||
onChangeProps({ | ||
ariaLabel: prePopulatedAriaLabel, | ||
}); | ||
}} | ||
> | ||
Auto-generate | ||
</Button> | ||
</View> | ||
); | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
row: { | ||
flexDirection: "row", | ||
alignItems: "center", | ||
}, | ||
button: { | ||
alignSelf: "start", | ||
}, | ||
ariaLabelTextField: { | ||
marginTop: spacing.xSmall_8, | ||
}, | ||
}); | ||
|
||
export default LockedFigureAria; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.