-
Notifications
You must be signed in to change notification settings - Fork 221
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
docs: Add new examples of new AriaLiveRegion component #2672
Merged
alanbsmith
merged 31 commits into
Workday:master
from
williamjstanton:william-aria-live-examples
Apr 12, 2024
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
4692dee
docs: Create markdown file
b025520
docs: Adding FilterListWithLiveStatus example
ddf39a3
docs: Writing storybook markdown file
ded42c0
docs: Fixed import file path
8f814da
docs: Importing AriaLiveRegion component
aca8023
docs: Fixing syntax
8f40435
docs: Formatting the list
676ce50
docs: Adding visible live region example
f3b6998
docs: Adding hidden aria live region example
380b40f
Merge remote-tracking branch 'upstream/master' into william-aria-live…
3a657c3
docs: Adding global header example with live badge
1ae9cef
docs: Refactoring example
cd95aef
docs: Adding live notifications badge
0d37b69
docs: Refactoring away from GlobalHeader example
636c768
docs: Commenting support levels
d431ade
Merge remote-tracking branch 'upstream/master' into william-aria-live…
b9e62a0
docs: Refactoring Assistant icon button
d2e70af
docs: Adding text input with live error
851fa15
Merge remote-tracking branch 'upstream/master' into william-aria-live…
0fad095
docs: Drafting content in markdown file
ba49ceb
docs: Adding hyperlinks to screen readers
e746907
Merge remote-tracking branch 'upstream/master' into william-aria-live…
20d12f9
docs: Adding note under the live inline error example
bfdb49d
docs: Accepting suggestions for tokens
williamjstanton 69c989c
docs: Accepting suggestion tokens in List example
williamjstanton ee355ec
docs: Accepting suggestion for space token in List example
williamjstanton 7e85b33
docs: Adding imports and correcting syntax in filter list
088ced4
docs: Using new token package in hidden live region example
91020c6
docs: Adding new token package to visible live region example
ab8af16
docs: Fixing styles in visible live region example
b346629
Merge branch 'master' into william-aria-live-examples
RayRedGoose File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
76 changes: 76 additions & 0 deletions
76
modules/react/_examples/stories/AriaLiveRegions.stories.mdx
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,76 @@ | ||
import {AriaLiveRegion} from '@workday/canvas-kit-react/common'; | ||
import {FilterListWithLiveStatus} from './examples/common/FilterListWithLiveStatus'; | ||
import {VisibleLiveRegion} from './examples/common/VisibleLiveRegion'; | ||
import {HiddenLiveRegion} from './examples/common/HiddenLiveRegion'; | ||
import {TextInputWithLiveError} from './examples/common/TextInputWithLiveError'; | ||
import {IconButtonsWithLiveBadges} from './examples/common/IconButtonsWithLiveBadges'; | ||
|
||
<Meta title="Examples/AriaLiveRegion" component={AriaLiveRegion} /> | ||
|
||
# ARIA Live Regions | ||
|
||
These examples are provided to demonstrate a variety of different use cases for the `AriaLiveRegion` | ||
component. For the full experience, get started by first turning on your favorite screen reading | ||
software. On Windows, we recommend the open source | ||
[NVDA (Non-Visual Desktop Access)](https://www.nvaccess.org/download/) software, or | ||
[JAWS (Job Access With Speech)](https://support.freedomscientific.com/Downloads/JAWS) if you have | ||
purchased a license. MacOS and iOS include VoiceOver, which can be turned on in your settings. | ||
|
||
Live regions work by designating specific DOM nodes for screen readers to monitor for any content | ||
updates inside the node. When an update occurs, screen readers will announce the update to users in | ||
real time, based on a few rules: | ||
|
||
1. `polite` will “politely” wait for users to finish what they are doing before announcing an update | ||
2. `assertive` will interrupt what users are doing (or reading) by immediately announcing an update | ||
|
||
### CAUTION: Don't get carried away | ||
|
||
Key things to understand about live regions: | ||
|
||
1. A live region update will only be announced once. Users are unable to repeat them or re-examine | ||
them if the announcement was not understood. | ||
2. Users may be able to pause a live region announcement, but they cannot prevent a live region | ||
announcement from occurring. Sending frequent, repetitive, or simply too much information to a | ||
live region can be very disruptive to users. | ||
3. Users cannot act on, or navigate to, a live region. Live regions must only contain plain text. | ||
(No images, links, buttons, or other input.) | ||
4. Support for live regions is limited across platforms, browsers, and screen reader software. Real | ||
time announcements may not be perfectly reliable. | ||
|
||
## Visible Live Regions | ||
|
||
Live regions can be applied to dynamic text on the UI. When the dynamic text is updated, screen | ||
readers can describe the text update in live time as it occurs. In the example below, type text into | ||
the input field and activate the "Send Message" button. Listen and observe the screen reader | ||
automatically announce the text update. | ||
|
||
<ExampleCodeBlock code={VisibleLiveRegion} /> | ||
|
||
## Hidden Live Regions | ||
|
||
Live regions don't need to be visible UI text, they can be used to assist the non-visual listening | ||
experience when moving the keyboard focus to a new element on screen isn't feasible. | ||
|
||
<ExampleCodeBlock code={HiddenLiveRegion} /> | ||
|
||
## Filtering lists with a live status | ||
|
||
In this example, a live region is applied to a short UI text describing the number of items shown in | ||
the list. As you type characters into the input, listen for the screen reader to automatically | ||
describe how many items in the list or shown. | ||
|
||
<ExampleCodeBlock code={FilterListWithLiveStatus} /> | ||
|
||
## Text input with live inline error | ||
|
||
In this example, a live region is applied to the inline error message that will appear below the | ||
text input. Listen for the screen reader to automatically describe the error message as you leave | ||
the input field blank. | ||
|
||
**Note:** Use this example with discretion. Using live regions for automatically announcing form | ||
errors to screen reader users can be a nice experience for simple forms with a very limited number | ||
of error conditions. As forms increase in complexity, live regions on each error message can become | ||
increasingly distracting and disruptive to the experience, especially if users are trying to first | ||
understand the information that is required of them to complete the task. | ||
|
||
<ExampleCodeBlock code={TextInputWithLiveError} /> |
65 changes: 65 additions & 0 deletions
65
modules/react/_examples/stories/examples/common/FilterListWithLiveStatus.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,65 @@ | ||
import React, {useState} from 'react'; | ||
import {TextInput} from '@workday/canvas-kit-preview-react/text-input'; | ||
import {BodyText, Heading} from '@workday/canvas-kit-react/text'; | ||
import {AriaLiveRegion} from '@workday/canvas-kit-react/common'; | ||
import {Flex} from '@workday/canvas-kit-react/layout'; | ||
import {system, base} from '@workday/canvas-tokens-web'; | ||
import {createStyles, px2rem} from '@workday/canvas-kit-styling'; | ||
|
||
const fruits = [ | ||
'Apples', | ||
'Oranges', | ||
'Bananas', | ||
'Lemons', | ||
'Limes', | ||
'Strawberries', | ||
'Raspberries', | ||
'Blackberries', | ||
]; | ||
|
||
const liveRegionStyle = createStyles({ | ||
border: `${px2rem(1)} solid ${base.cantaloupe400}`, | ||
backgroundColor: base.cantaloupe100, | ||
padding: system.space.x2, | ||
}); | ||
|
||
const listStyles = {paddingLeft: '0px'}; | ||
|
||
const listItemStyles = createStyles({ | ||
listStyle: 'none', | ||
paddingLeft: system.space.zero, | ||
}); | ||
|
||
let filteredFruits = fruits; | ||
|
||
export const FilterListWithLiveStatus = () => { | ||
const [filter, setFilter] = useState(''); | ||
function handleFilter(e) { | ||
filteredFruits = fruits.filter(i => i.toUpperCase().indexOf(e.target.value.toUpperCase()) >= 0); | ||
setFilter(e.target.value); | ||
} | ||
|
||
return ( | ||
<> | ||
<Flex gap="1rem"> | ||
<Heading size="small">Fruits</Heading> | ||
<AriaLiveRegion> | ||
<BodyText size="small" cs={liveRegionStyle}> | ||
{`Showing ${filteredFruits.length} of ${fruits.length}`} | ||
</BodyText> | ||
</AriaLiveRegion> | ||
</Flex> | ||
<TextInput orientation="vertical"> | ||
<TextInput.Label>Filter Items:</TextInput.Label> | ||
<TextInput.Field value={filter} onChange={handleFilter} /> | ||
</TextInput> | ||
<ul style={listStyles}> | ||
{filteredFruits.map(i => ( | ||
<BodyText size="small" as="li" cs={listItemStyles} key={i}> | ||
{i} | ||
</BodyText> | ||
))} | ||
</ul> | ||
</> | ||
); | ||
}; |
30 changes: 30 additions & 0 deletions
30
modules/react/_examples/stories/examples/common/HiddenLiveRegion.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,30 @@ | ||
import React, {useState, useRef} from 'react'; | ||
import {AriaLiveRegion, AccessibleHide} from '@workday/canvas-kit-react/common'; | ||
import {PrimaryButton} from '@workday/canvas-kit-react/button'; | ||
import {TextInput} from '@workday/canvas-kit-preview-react/text-input'; | ||
import {Flex} from '@workday/canvas-kit-react/layout'; | ||
import {system} from '@workday/canvas-tokens-web'; | ||
|
||
export const HiddenLiveRegion = () => { | ||
const [message, setMessage] = useState('This is an ARIA Live Region!'); | ||
const inputRef = useRef(); | ||
function handleSendMessage() { | ||
setMessage(inputRef.current.value); | ||
inputRef.current.value = ''; | ||
} | ||
|
||
return ( | ||
<> | ||
<Flex gap={`var(${system.space.x4})`} alignItems="flex-end"> | ||
<TextInput orientation="vertical"> | ||
<TextInput.Label>Type your message:</TextInput.Label> | ||
<TextInput.Field ref={inputRef} /> | ||
</TextInput> | ||
<PrimaryButton onClick={handleSendMessage}>Send Message</PrimaryButton> | ||
</Flex> | ||
<AriaLiveRegion> | ||
<AccessibleHide>{message}</AccessibleHide> | ||
</AriaLiveRegion> | ||
</> | ||
); | ||
}; |
98 changes: 98 additions & 0 deletions
98
modules/react/_examples/stories/examples/common/IconButtonsWithLiveBadges.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,98 @@ | ||
import React, {useState} from 'react'; | ||
import {AccessibleHide, AriaLiveRegion, useUniqueId} from '@workday/canvas-kit-react/common'; | ||
import {notificationsIcon, inboxIcon, assistantIcon} from '@workday/canvas-system-icons-web'; | ||
import {space} from '@workday/canvas-kit-react/tokens'; | ||
import {SecondaryButton, TertiaryButton} from '@workday/canvas-kit-react/button'; | ||
import {Flex} from '@workday/canvas-kit-react/layout'; | ||
import {Tooltip} from '@workday/canvas-kit-react/tooltip'; | ||
import {CountBadge} from '@workday/canvas-kit-react/badge'; | ||
|
||
const MyTasksLiveBadge = ({cnt}) => { | ||
// use tooltip to assign name, | ||
// use AriaLiveRegion inside button, | ||
// assign name to live region referencing the button, | ||
// use BadgeCount inside live region, | ||
// use AccessibleHide to create invisible word "new" after badge | ||
// use aria-describedby on button, referencing live region container to set description | ||
// Safari + VO => not working at all | ||
// JAWS 2024 + Chrome / Edge => works as expected :) | ||
// NVDA + Chrome / Edge => works as expected :) | ||
// Firefox => isn't announcing description on focus, only announces "X New" live (missing button name) | ||
const badgeID = useUniqueId(); | ||
const myTasksID = useUniqueId(); | ||
|
||
return ( | ||
<Tooltip title="My Tasks"> | ||
<TertiaryButton icon={inboxIcon} id={myTasksID} aria-describedby={badgeID}> | ||
<AriaLiveRegion id={badgeID} aria-labelledby={myTasksID}> | ||
<CountBadge count={cnt} /> | ||
<AccessibleHide>New</AccessibleHide> | ||
</AriaLiveRegion> | ||
</TertiaryButton> | ||
</Tooltip> | ||
); | ||
}; | ||
|
||
// use AriaLiveRegion around the button, | ||
// use Tooltip to assign the name of the button, | ||
// make sure Tooltip title string includes count value | ||
// Chrome + VO => Announces name "notifications X new" and innerText 'X' | ||
// Safari + VO => Works as expected :) | ||
// JAWS 2024 => Announces full button name twice (previous state, then new state) | ||
// JAWS 2024 + Firefox => Works as expected :) | ||
// NVDA (All Browsers) => Atomic property isn't working, only announcing number change, announces twice | ||
const NotificationsLiveBadge = ({cnt}) => ( | ||
<AriaLiveRegion> | ||
<Tooltip title={`Notifications ${cnt} new`}> | ||
<TertiaryButton icon={notificationsIcon}> | ||
<CountBadge count={cnt} /> | ||
</TertiaryButton> | ||
</Tooltip> | ||
</AriaLiveRegion> | ||
); | ||
|
||
const AssistantLiveBadge = ({cnt}) => { | ||
// use AriaLiveRegion around the button | ||
// use muted type Tooltip (avoid using aria-label to name button) | ||
// use AccessibleHide inside of button to compose name | ||
// Chrome + VO => announces twice | ||
// Safari + VO => works as expected :) | ||
const lbl = 'Workday Assistant'; | ||
|
||
return ( | ||
<AriaLiveRegion> | ||
<Tooltip title={lbl} type="muted"> | ||
<TertiaryButton icon={assistantIcon}> | ||
<AccessibleHide>{lbl}</AccessibleHide> | ||
<CountBadge count={cnt} /> | ||
<AccessibleHide>New</AccessibleHide> | ||
</TertiaryButton> | ||
</Tooltip> | ||
</AriaLiveRegion> | ||
); | ||
}; | ||
|
||
export const IconButtonsWithLiveBadges = () => { | ||
const [counter, setCounter] = useState(0); | ||
const [notifications, setNotifications] = useState(0); | ||
const [assistant, setAssistant] = useState(0); | ||
|
||
const handleAddTask = () => setCounter(prev => prev + 1); | ||
const handleAddNotification = () => setNotifications(prev => prev + 1); | ||
const handleAssistant = () => setAssistant(prev => prev + 1); | ||
|
||
return ( | ||
<> | ||
<Flex padding={space.s} gap={space.s} as="header"> | ||
<AssistantLiveBadge cnt={assistant} /> | ||
<NotificationsLiveBadge cnt={notifications} /> | ||
<MyTasksLiveBadge cnt={counter} /> | ||
</Flex> | ||
<Flex padding={space.s} gap={space.s} as="main"> | ||
<SecondaryButton onClick={handleAssistant}>Add a Message</SecondaryButton> | ||
<SecondaryButton onClick={handleAddNotification}>Add a Notification</SecondaryButton> | ||
<SecondaryButton onClick={handleAddTask}>Add an item to My Tasks</SecondaryButton> | ||
</Flex> | ||
</> | ||
); | ||
}; |
25 changes: 25 additions & 0 deletions
25
modules/react/_examples/stories/examples/common/TextInputWithLiveError.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,25 @@ | ||
import React, {useState, useRef} from 'react'; | ||
import {TextInput} from '@workday/canvas-kit-preview-react/text-input'; | ||
import {AriaLiveRegion, changeFocus} from '@workday/canvas-kit-react/common'; | ||
import {PrimaryButton} from '@workday/canvas-kit-react/button'; | ||
|
||
export const TextInputWithLiveError = () => { | ||
const errMsg = 'Error: First name is required.'; | ||
const [hasError, setHasError] = useState(false); | ||
const inputRef = useRef(); | ||
const handleBlur = e => setHasError(e.target.value.trim().length === 0); | ||
const handleSubmit = () => hasError && changeFocus(inputRef.current); | ||
|
||
return ( | ||
<> | ||
<TextInput orientation="vertical" hasError={hasError} isRequired={true}> | ||
<TextInput.Label>First Name:</TextInput.Label> | ||
<TextInput.Field onBlur={handleBlur} ref={inputRef} /> | ||
<TextInput.Hint height={'16px'}> | ||
<AriaLiveRegion>{hasError && errMsg}</AriaLiveRegion> | ||
</TextInput.Hint> | ||
</TextInput> | ||
<PrimaryButton onClick={handleSubmit}>Continue</PrimaryButton> | ||
</> | ||
); | ||
}; |
40 changes: 40 additions & 0 deletions
40
modules/react/_examples/stories/examples/common/VisibleLiveRegion.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,40 @@ | ||
import React, {useState, useRef} from 'react'; | ||
import {AriaLiveRegion} from '@workday/canvas-kit-react/common'; | ||
import {PrimaryButton} from '@workday/canvas-kit-react/button'; | ||
import {TextInput} from '@workday/canvas-kit-preview-react/text-input'; | ||
import {Flex} from '@workday/canvas-kit-react/layout'; | ||
import {Text} from '@workday/canvas-kit-react/text'; | ||
import {system, base} from '@workday/canvas-tokens-web'; | ||
import {createStyles, px2rem} from '@workday/canvas-kit-styling'; | ||
|
||
const liveRegionStyle = createStyles({ | ||
border: `${px2rem(1)} solid ${base.cantaloupe400}`, | ||
backgroundColor: base.cantaloupe100, | ||
padding: system.space.x4, | ||
display: 'block', | ||
margin: system.space.x4 + ' 0', | ||
}); | ||
|
||
export const VisibleLiveRegion = () => { | ||
const [message, setMessage] = useState('This is an ARIA Live Region!'); | ||
const inputRef = useRef(); | ||
function handleSendMessage() { | ||
setMessage(inputRef.current.value); | ||
inputRef.current.value = ''; | ||
} | ||
|
||
return ( | ||
<> | ||
<AriaLiveRegion> | ||
<Text cs={liveRegionStyle}>{message}</Text> | ||
</AriaLiveRegion> | ||
<Flex gap={`var(${system.space.x4})`} alignItems="flex-end"> | ||
<TextInput orientation="vertical"> | ||
<TextInput.Label>Type your message:</TextInput.Label> | ||
<TextInput.Field ref={inputRef} /> | ||
</TextInput> | ||
<PrimaryButton onClick={handleSendMessage}>Send Message</PrimaryButton> | ||
</Flex> | ||
</> | ||
); | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 was researching 3 different ways to build an icon button using Badges and live regions. I wasn't completely satisfied with any of my results, if I am honest. I didn't exactly want to throw the research away, but I didn't want to necessarily include the example in Storybook until I had something I was happy with.