Skip to content
2 changes: 1 addition & 1 deletion .storybook/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const routes = {
'/components/popups/modal/': 'components-popups-modal--docs',
'/components/popups/popup/': 'components-popups-popup--docs',
'/components/popups/toast/': 'components-popups-toast--docs',
'/components/popups/tooltip/': 'omponents-popups-tooltip--docs',
'/components/popups/tooltip/': 'components-popups-tooltip--docs',
'/components/text/body-text/': 'components-text-body-text--docs',
'/components/text/heading/': 'components-text-heading--docs',
'/components/text/label-text/': 'components-text-label-text--docs',
Expand Down
6 changes: 3 additions & 3 deletions modules/react/action-bar/stories/ActionBar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ Grouping the actions into an HTML `<section>` element with an `aria-label` strin
This can be useful for helping screen reader users quickly jump down to the actions at the bottom of
a page.

Refer to [Button](?path=/docs/components-buttons--docs#accessibility) and
[Menus](?path=/docs/components-popups-menu--docs#accessibility) for more information about
accessibiliy of these components in the Action Bar.
Refer to [Button](/components/buttons/button/#accessibility) and
[Menus](/components/popups/menu/#accessibility) for more information about accessibiliy of these
components in the Action Bar.

## Component API

Expand Down
13 changes: 6 additions & 7 deletions modules/react/button/stories/button/Button.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,13 @@ Enter and Space keys for keyboard interactions.
[Button Pattern | APG | WAI | W3C](https://www.w3.org/WAI/ARIA/apg/patterns/button/)

- An `aria-label` is only necessary for icon-only buttons in most cases. Using
[Canvas Kit's tooltip component](?path=/docs/components-popups-tooltip--docs) will handle this for
you, and all users will be able to see the label for the button.
[Canvas Kit's tooltip component](/components/popups/tooltip/) will handle this for you, and all
users will be able to see the label for the button.
- When button designs have 2 toggle states, an `aria-pressed={true | false}` property is required
for screen reader support. For example, see Canvas Kit's
[Segmented Control component](?path=/docs/preview-segmented-control--docs).
- When buttons have an attached menu, an `aria-haspopup=true` property is required. Using
[Canvas Kit's Menu component](?path=/docs/components-menus-menu--docs) will handle this for you.
[Segmented Control component](/components/buttons/segmented-control/).
- When buttons have an attached menu, an `aria-haspopup="true"` property is required. Using
[Canvas Kit's Menu component](/components/popups/menu/) will handle this for you.
- The icons used in text buttons are decorative in most cases and include ARIA `role="presentation"`
and `focusable="false"`. In some special cases where an icon does add meaning, you may be required
to change the `role` and add an `aria-label` to the icon for equivalent screen reader support.
Expand All @@ -139,8 +139,7 @@ Enter and Space keys for keyboard interactions.
- Button text content is announced along with the button role (e.g., "Primary, button").
- Icon-only buttons announce the `aria-label` value along with the button role.
- Toggle buttons announce their pressed/unpressed state (e.g., "Activity Stream, toggle button,
pressed" and check out the
[Segmented Control component](?path=/docs/preview-segmented-control--docs)).
pressed" and check out the [Segmented Control component](/components/buttons/segmented-control/)).

### Touch Target Size

Expand Down
9 changes: 7 additions & 2 deletions modules/react/expandable/lib/ExpandableAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@ export const expandableAvatarStencil = createStencil({
// When the component is created, it needs to be a button element to match AvatarProps.
// Once Avatar becomes a `createComponent` we can default the element type to a `div`
// and the types should be properly extracted
// Setting altText prop to a default empty string for decorative purposes
// Setting isDecorative prop to true by default since avatars in expandable headers are typically decorative
export const ExpandableAvatar = createComponent('div')({
displayName: 'Expandable.Avatar',
Component: ({name = '', ...elemProps}: ExpandableAvatarProps, ref, Element) => {
Component: (
{name = '', isDecorative = true, ...elemProps}: ExpandableAvatarProps,
ref,
Element
) => {
return (
<Avatar
as={Element}
name={name}
isDecorative={isDecorative}
ref={ref}
size="extraSmall"
{...mergeStyles(elemProps, expandableAvatarStencil())}
Expand Down
51 changes: 49 additions & 2 deletions modules/react/expandable/stories/Expandable.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ to `Expandable.Icon` depending on whether the `Expandable.Icon` is placed before

<ExampleCodeBlock code={Avatar} />

> **Accessibility Note:** In this situation, the Avatar is decorative and should not be announced to
> screen readers. The `<Expandable.Avatar>` component has `isDecorative` set to `true` by default to
> hide it from screen readers, as avatars in expandable headers are typically decorative when paired
> with adjacent text.

### Right to Left (RTL)

Expandable container has bidirectional support and should function as expected with RTL languages as
Expand All @@ -75,11 +80,53 @@ You can also have direct access to the model if

### Hoisted Model

If you you need direct access to the model, you can hoist it with the `useExpandableModel` hook. In
the example below, we're hoisting the models to expand and collapse all three containers at once.
If you need direct access to the model, you can hoist it with the `useExpandableModel` hook. In the
example below, we're hoisting the models to expand and collapse all three containers at once.

<ExampleCodeBlock code={HoistedModel} />

> **Accessibility Note:** When using multiple Expandable Containers on a page, use the `as` prop to
> render the `<Expandable.Content>` sub-component as an HTML `<section>` element. Then, use
> `aria-labelledby` to reference the unique `id` of the `<Expandable.Title>` element. This practice
> can be useful to screen reader users when multiple Expandable Containers are opened at one time
> for uniquely describing the boundaries of the expandable content.

## Accessibility

Our Expandable component renders a semantic HTML `<button>` element to the DOM, with an optional
parent heading element as defined by the `headingLevel` prop. The `aria-expanded` property is
included on the button to indicate the state of the content to screen readers.

[Accordion Pattern | APG | WAI | W3C](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/)

- Use the `headingLevel` prop to assign an appropriate heading level based on the context of the
page content.
- When using Expandable Container for navigation elements, then we don't recommend using the
`headingLevel` prop. This will render only expandable buttons to the DOM, reserving headings for
organizing content in the main body of the page.
- The `as` prop may also be used on `<Expandable.Content>` to render an HTML `<ul>` element for
displaying a list of items. For example, check out
[Side Panel with Navigation](https://workday.github.io/canvas-kit/?path=/docs/guides-accessibility-examples-side-panel-navigation--docs).

### Navigation

- **Tab key**: Moves focus to the next expandable button or focusable element
- **Shift + Tab**: Moves focus to the previous focusable element
- **Enter or Space**: Toggles the expanded/collapsed state

### Screen Reader Experience

- The expandable button will be announced with its title text followed by the button role
- The current state will be announced as either "collapsed" or "expanded" (For example: "Usage
Guidance, button, collapsed" or "Usage Guidance, button, expanded")
- **State Changes:** When activating the button to expand content, screen readers will announce the
new "expanded" state and vice versa when collapsing content.
- **Content Regions:** Screen reader users can use landmark navigation to jump between sections and
each section will be announced with its associated title (For example: "Usage Guidance, landmark
region")
- **Heading Structure:** Using heading levels with expandable buttons allows screen reader users to
navigate by headings, making the document structure and hierarchy easier to understand.

## Component API

<SymbolDoc name="Expandable" fileName="/react/" />
41 changes: 24 additions & 17 deletions modules/react/expandable/stories/examples/HoistedModel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,27 @@ import React from 'react';
import {Expandable, useExpandableModel} from '@workday/canvas-kit-react/expandable';
import {Flex} from '@workday/canvas-kit-react/layout';
import {SecondaryButton} from '@workday/canvas-kit-react/button';
import {useUniqueId} from '@workday/canvas-kit-react/common';
import {createStyles} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const listStyles = createStyles({
flexDirection: 'column',
gap: system.space.x2,
padding: system.space.zero,
marginX: system.space.x4,
marginY: system.space.zero,
});

export const HoistedModel = () => {
const modelOne = useExpandableModel();
const modelTwo = useExpandableModel();
const modelThree = useExpandableModel();

const idOne = useUniqueId();
const idTwo = useUniqueId();
const idThree = useUniqueId();

const handleExpandAll = () => {
modelOne.events.show();
modelTwo.events.show();
Expand All @@ -22,19 +37,19 @@ export const HoistedModel = () => {
};

return (
<Flex gap="m" flexDirection="column">
<Flex gap="s">
<Flex gap={system.space.x6} flexDirection="column">
<Flex gap={system.space.x4}>
<SecondaryButton onClick={handleExpandAll}>Expand All</SecondaryButton>
<SecondaryButton onClick={handleCollapseAll}>Collapse All</SecondaryButton>
</Flex>
<Flex flexDirection="column">
<Expandable model={modelOne}>
<Expandable.Target headingLevel="h4">
<Expandable.Title>Usage Guidance</Expandable.Title>
<Expandable.Title id={idOne}>Usage Guidance</Expandable.Title>
<Expandable.Icon iconPosition="end" />
</Expandable.Target>

<Expandable.Content>
<Expandable.Content as="section" aria-labelledby={idOne}>
This component highlights the most important details of a section and reveals more when
a user taps or clicks on the header part of the container. Enabling users to hide and
show information ensures the design remains focused and relevant to their expectations.
Expand All @@ -44,20 +59,12 @@ export const HoistedModel = () => {
</Expandable>
<Expandable model={modelTwo}>
<Expandable.Target headingLevel="h4">
<Expandable.Title>Accessibility Guidelines</Expandable.Title>
<Expandable.Title id={idTwo}>Accessibility Guidelines</Expandable.Title>
<Expandable.Icon iconPosition="end" />
</Expandable.Target>

<Expandable.Content>
<Flex
flexDirection="column"
as="ul"
gap="xxs"
maxWidth="60ch"
padding="zero"
marginX="s"
marginY="zero"
>
<Expandable.Content as="section" aria-labelledby={idTwo}>
<Flex as="ul" cs={listStyles}>
<li>
The state of a component being open or closed must be conveyed to assistive
technologies.
Expand Down Expand Up @@ -92,10 +99,10 @@ export const HoistedModel = () => {
</Expandable>
<Expandable model={modelThree}>
<Expandable.Target headingLevel="h4">
<Expandable.Title>Content Guidelines</Expandable.Title>
<Expandable.Title id={idThree}>Content Guidelines</Expandable.Title>
<Expandable.Icon iconPosition="end" />
</Expandable.Target>
<Expandable.Content>
<Expandable.Content as="section" aria-labelledby={idThree}>
Titles should be short and concise, yet long enough to explain what the user would
expect to see when the content is expanded. If titles must be long, make sure it doesn't
wrap more than two lines.
Expand Down
2 changes: 1 addition & 1 deletion modules/react/expandable/stories/examples/LongTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const LongTitle = () => (
<Expandable>
<Expandable.Target headingLevel="h4">
<Expandable.Icon iconPosition="start" />
<Expandable.Avatar name="Avatar" url={testAvatar} />
<Expandable.Avatar name="Avatar" isDecorative url={testAvatar} />
<Expandable.Title>
Our house special supreme pizza includes pepperoni, sausage, bell peppers, mushrooms,
onions, and oregano.
Expand Down
4 changes: 2 additions & 2 deletions modules/react/expandable/stories/examples/RTL.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ export const RTL = () => {
<Expandable>
<Expandable.Target headingLevel="h4">
<Expandable.Icon iconPosition="start" />
<Expandable.Avatar name="Avatar" />
<Expandable.Avatar name="Avatar" isDecorative />
<Expandable.Title>Title</Expandable.Title>
</Expandable.Target>

<Expandable.Content>Content</Expandable.Content>
</Expandable>
<Expandable>
<Expandable.Target headingLevel="h4">
<Expandable.Avatar name="Avatar" />
<Expandable.Avatar name="Avatar" isDecorative />
<Expandable.Title>Title</Expandable.Title>
<Expandable.Icon iconPosition="end" />
</Expandable.Target>
Expand Down
Loading
Loading