-
Notifications
You must be signed in to change notification settings - Fork 10
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
Announcer: Part 1 #2362
Announcer: Part 1 #2362
Conversation
🦋 Changeset detectedLatest commit: 6844066 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Size Change: +1.99 kB (+2.02%) Total Size: 100 kB
ℹ️ View Unchanged
|
A new build was pushed to Chromatic! 🚀https://5e1bf4b385e3fb0020b7073c-mnnpmtuceh.chromatic.com/ Chromatic results:
|
GeraldRequired Reviewers
Don't want to be involved in this pull request? Comment |
npm Snapshot: Published🎉 Good news!! We've packaged up the latest commit from this PR (7b5f690) and published all packages with changesets to npm. You can install the packages in webapp by running: ./services/static/dev/tools/deploy_wonder_blocks.js --tag="PR2362" Packages can also be installed manually by running: yarn add @khanacademy/wonder-blocks-<package-name>@PR2362 |
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 is looking really good! Giving my initial review with some suggestions on how to structure the files to be more consistent with the rest of the repo and asking some questions to get a bit more context on certain parts. This is great progress 👏
packages/wonder-blocks-announcer/src/components/send-message-button.tsx
Outdated
Show resolved
Hide resolved
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.
Great work so far Marcy! Left some feedback and questions 😄
timeoutDelay: { | ||
control: "number", | ||
type: "number", | ||
description: "(milliseconds)", |
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'm wondering if there's a way for us to use the function docs for the storybook docs! Normally we're able to get the prop docs automatically from setting component
in this block, though this is different since these docs are for functions rather than components!
cc: @jandrade in case you have come across similar things before!
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.
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.
Good ideas on docs! I will plan to tackle this in a future PR.
@jandrade @beaesguerra this is ready for another review! I added quite a few more tests. Yay! In testing the debounce logic more thoroughly, I realized the first iteration was going to be unpredictable and hard to use. So I refactored it a bit. I'm currently debugging in ATs and working with Storybook though, as this might have broken things. 😬 Some code highlights:
My next steps are to test this more in webapp and see if it works as I expected! |
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.
Thanks for this new iteration. I'm adding some extra suggestions around the code (specially around tests). I'll keep digging into it and will integrate with the Combobox component. Thanks for all this progress!!
} from "./util/dom"; | ||
import {alternateIndex, debounce} from "./util/util"; | ||
|
||
const REMOVAL_TIMEOUT_DELAY = 5000; |
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.
question: Is there a specific reason why we have to wait for 5 seconds? do you think this would need to be configured by the consumer at some point?
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.
It's admittedly a bit of a magic number... long enough to let the platform accessibility API read out a message of unknown length before it's removed, and also longer than the default initial timeout. Initially I made this configurable by the user but I removed the option due to added complexity to maintain.
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.
idea: What if we determined the delay dynamically based on the number of words in the announcement multiplied by something like 500ms? That way long messages are accounted for! This might also account for translations that might be longer, though we would be assuming approximately how long each word is read!
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.
It's an interesting thought. I'm hesitant to go that route as screen reader speech rates are configurable, so it's a moving target. Perhaps we could mark that down for a future improvement if it comes up in production?
packages/wonder-blocks-announcer/src/__tests__/Announcer.test.ts
Outdated
Show resolved
Hide resolved
packages/wonder-blocks-announcer/src/__tests__/components/announce-message-button.tsx
Outdated
Show resolved
Hide resolved
packages/wonder-blocks-announcer/src/__tests__/util/util.test.ts
Outdated
Show resolved
Hide resolved
packages/wonder-blocks-announcer/src/__tests__/util/dom.test.ts
Outdated
Show resolved
Hide resolved
packages/wonder-blocks-announcer/src/__tests__/util/dom.test.ts
Outdated
Show resolved
Hide resolved
createDuplicateRegions( | ||
aWrapper, | ||
"assertive", | ||
this.regionFactory.count, | ||
this.dictionary, | ||
); |
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.
thought: One thing that I noticed while playing with Storybook is that if I change the text in the Story and click the "submit" button, sometimes the announcement is not read by Voice Over. I'm not sure where to put this, but saw that sometimes was announced in one of the duplicate regions.
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.
Hmm, that is odd. I noticed initial announcements aren't read if I change the politeness level to Assertive, but not for changing the text. The default Storybook frame also seems to impact reliability of messages, so I tend to test it in a standalone window. I think I'll opt for integration testing to observe these kinds of issues since I've spent so long on it already and the behavior is so inconsistent!
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.
Great progress Marcy! Left some questions and comments 😄
Alright, the debounce refactor is in! (it wasn't actually working when I tested it) I had to change it to allow a configurable debounce wait parameter with the simple @jandrade I also fixed that styling issue in Storybook that you were seeing with the Combobox. I added an |
4957581
to
7eacfef
Compare
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 is looking great, I tested the story out with VoiceOver + Safari, NVDA + Chrome/Firefox and the message is announced as I expected! Thanks for your hard work on this!
I had a few suggestions around stories for other scenarios and documentation (non-blocking). I'm looking forward to seeing the announcer get used in context!
(Will let Juan be the final approver though since he may have feedback/insights on how it was integrating it with combobox!)
timeoutDelay: { | ||
control: "number", | ||
type: "number", | ||
description: "(milliseconds)", |
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.
debounceThreshold, | ||
}: AnnounceMessageProps) => { | ||
return ( | ||
<Button |
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.
(suggestion, no changes necessary) - I was curious about other scenarios that we could add to the story (or another story) so we can test different cases easily:
- A button that triggers an announcement and another button that clears the specific announcement and/or all announcements
- 1 button that triggers a polite message, another button that triggers an assertive message to see the behaviour for different announcement levels
- buttons with different debounceThreshold values to show how that option changes the behaviour
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.
Love these ideas. I will tackle them in a future PR also! Thanks Bea!
@marcysutton I've been testing the latest changes in The issue is that the button is pressed, the message is queued to be announced, but because the Screen.Recording.2024-12-20.at.6.12.57.PM.movI'll try to put a new PR with my current progress. |
50f263b
to
8b044c0
Compare
expect(politeRegion1).toHaveAttribute("aria-live", "polite"); | ||
expect(politeRegion1).toHaveAttribute("id", "wbARegion-polite0"); | ||
expect(politeRegion2).toHaveAttribute("aria-live", "polite"); | ||
expect(politeRegion2).toHaveAttribute("id", "wbARegion-polite1"); | ||
}); |
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.
Question: Can we make these aria-atomic
so that they read the whole message instead of browsers/srs trimming similar starting text? Or is that handled in a different way?
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.
We can definitely try it! I wasn't noticing differences with the current implementation. But I will caveat that I'm also planning to experiment with a queue of messages that get appended as children and removed on a staggered delay. So aria-atomic
might not make sense in that implementation!
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 made a note to look into this in a future PR, since I want to do some more cross-platform testing anyway!
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.
Looks good to me! Thanks for the continuous iterations, and summarizing the follow up work after this PR!
1. Keeps announce from being called too frequently. We can play with the timeout duration. 2. Makes the returned IDREF more reliable in a browser.
1. Keeps announce from being called too frequently. We can play with the timeout duration. 2. Makes the returned IDREF more reliable in a browser.
1. Return first result and debounce subsequent calls within the debounceThreshold 2. Remove removalDelay parameter to simplify API 3. Put debounce utility into a separate file and add tests
Debounce wasn't actually limiting execution in the wait period. Now it does, and the debounce duration is still configurable when calling announceMessage!
I was trying to avoid having to import the Announcer in this test to keep things isolated, but it's so specific to the Announcer that I decided it didn't matter that much. Specifying the Announcer instance for the scope instead of generic thisArg logic simplified things quite a bit as well.
84b08d2
to
6844066
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## feature/announcer #2362 +/- ##
=========================================
=========================================
Continue to review full report in Codecov by Sentry.
|
The initial implementation for a live region component! I'm getting this draft PR up so I can test with the remote URL.
Issue: https://khanacademy.atlassian.net/browse/WB-1768
Outstanding questions/work areas:
Test Plan
Play with the Story with screen readers turned on
1. VoiceOver on OSX
2. VoiceOver on iOS
3. NVDA on Windows
4. JAWS on Windows