diff --git a/modules/react/_examples/stories/examples/AriaLiveRegions/IconButtonsWithLiveBadges.tsx b/modules/react/_examples/stories/examples/AriaLiveRegions/IconButtonsWithLiveBadges.tsx deleted file mode 100644 index 4f326d7660..0000000000 --- a/modules/react/_examples/stories/examples/AriaLiveRegions/IconButtonsWithLiveBadges.tsx +++ /dev/null @@ -1,98 +0,0 @@ -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 ( - - - - - New - - - - ); -}; - -// 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}) => ( - - - - - - - -); - -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 ( - - - - {lbl} - - New - - - - ); -}; - -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 ( - <> - - - - - - - Add a Message - Add a Notification - Add an item to My Tasks - - - ); -}; diff --git a/modules/react/badge/stories/CountBadge.stories.mdx b/modules/react/badge/stories/CountBadge.stories.mdx index 7294f2caa2..3684815382 100644 --- a/modules/react/badge/stories/CountBadge.stories.mdx +++ b/modules/react/badge/stories/CountBadge.stories.mdx @@ -43,10 +43,20 @@ messages on their first day back. ### Notification Badge -Notifications are a major use case for `CountBadge`. Remember that any `CountBadge` with a -live-updating value should be announced to screen readers with an `aria-live` region and an -`aria-label` on the button to provide additional context, as in the example below. Please also read -the Accessibility guidance below this example for aditional information. +Notifications are a major use case for `CountBadge`. When the `CountBadge` value is updated in +real-time, screen readers must be supported with an `AriaLiveRegion` that will automatically +describe the change in the number of notifications. If the web app only updates `CountBadge` as part +of another screen update, then this use of `AriaLiveRegion` is unnecessary and not recommended. + +#### Notes on accessibility for the example below + +- `Tooltip` is set on the `SecondaryButton` automatically applying the `aria-label` to the button. +- `aria-describedby` property is conditionally set on the `SecondaryButton` when greater than zero + referencing a unique `id` for the `CountBadge` value . +- `AriaLiveRegion` is used around the `CountBadge`, enabling screen readers to monitor changes in + value. +- `aria-label` string is conditionally set on `AriaLiveRegion` when greater than zero, describing + "New notification" @@ -55,17 +65,6 @@ the Accessibility guidance below this example for aditional information. Count Badge supports custom styling via the `cs` prop. For more information, check our ["How To Customize Styles"](https://workday.github.io/canvas-kit/?path=/docs/styling-how-to-customize-styles--page). -## Accessibility - -A common use case for `CountBadge` is displaying notifications, but there are other situations where -they will have live-updated values. There are several accessibility concerns you'll want to keep in -mind: - -- The button should have an aria-label that updates with the count -- The elements inside the button should have `aria-hidden` -- The live region should be outside the button -- The live region should be visually hidden and only contain text - ## Component API diff --git a/modules/react/badge/stories/examples/NotificationBadge.tsx b/modules/react/badge/stories/examples/NotificationBadge.tsx index 6a6cda7ad8..a5135d1a92 100644 --- a/modules/react/badge/stories/examples/NotificationBadge.tsx +++ b/modules/react/badge/stories/examples/NotificationBadge.tsx @@ -1,10 +1,12 @@ import * as React from 'react'; import {CountBadge} from '@workday/canvas-kit-react/badge'; import {SecondaryButton, TertiaryButton} from '@workday/canvas-kit-react/button'; -import {accessibleHide} from '@workday/canvas-kit-react/common'; +import {AriaLiveRegion, useUniqueId} from '@workday/canvas-kit-react/common'; import {createStyles, cssVar} from '@workday/canvas-kit-styling'; import {notificationsIcon} from '@workday/canvas-system-icons-web'; import {base, system} from '@workday/canvas-tokens-web'; +import {Tooltip} from '@workday/canvas-kit-react/tooltip'; +import {Flex} from '@workday/canvas-kit-react/layout'; function negate(value: string, fallback?: string) { return `calc(${cssVar(value, fallback)} * -1)`; @@ -12,16 +14,13 @@ function negate(value: string, fallback?: string) { const container = createStyles({ boxSizing: 'border-box', - display: 'flex', flexDirection: 'column', gap: system.space.x4, }); const controls = createStyles({ boxSizing: 'border-box', - borderBottom: `solid 1px ${cssVar(base.soap400)}`, - display: 'flex', - gap: system.space.x1, + gap: system.space.x2, padding: system.space.x1, }); @@ -33,40 +32,46 @@ const notificationContainerStyles = createStyles({ const countBadgeStyles = createStyles({ boxSizing: 'border-box', position: 'absolute', - top: negate(system.space.x4), + top: negate(system.space.x1), insetInlineEnd: negate(system.space.x1), }); -const accessibleHideStyles = createStyles(accessibleHide); - +// Testing notes (Aug. 30, 2024): +// Windows 11 +// JAWS 2024 + Chrome / Edge: "New notifications" once, then only the count change "2" +// JAWS 2024 + FF: "New notifications" once, then describes nothing +// NVDA + Chrome / Edge: Consistently describes "{X} New notifications" +// NVDA + FF: Consistently describes count value only "{X}" +// macOS v14.6.1 +// VoiceOver + Chrome / Safari: Consistently describes "New notifications {X}" export function NotificationBadge() { const [count, setCount] = React.useState(4); + const badgeID = useUniqueId(); return ( -
-
+ + setCount(count + 1)}> Add Notification setCount(0)}> Clear -
-
+ + - - {!!count && ( - -
-
+ + ); }