Skip to content
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: Refreshing preview SidePanel storybook examples #3056

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions modules/preview-react/side-panel/stories/SidePanel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ logic or styling for bidirecitonal support.

### Hidden Name

`SidePanel` should always have an accessible label. Often this is the heading element with an `id`
attribute. However, as seen in the example below, you can apply a `hidden` attribute to your label
if you do not want it to be visible.
`SidePanel` must always have an accessible label for both the HTML `<section>` container and the
`ToggleButton`. The `labelProps` component must always be present in the DOM in order for the
`panelProps` and `controlProps` component labels to be assigned properly. A `hidden` attribute can
be applied to the `labelProps` component. In the example below, we are demonstrating the
`AccessibleHide` component that relies on CSS properties to visually hide text for screen readers
only.

<ExampleCodeBlock code={HiddenName} />

Expand All @@ -63,6 +66,28 @@ used (this case is covered in the Examples section).
Sometimes you'll want to control `SidePanel`'s' expand / collapse behavior from outside the
component. In that case, you can use the `controlProps` supplied by the `useSidePanel` hook.

#### Notes about accessibility

The `controlProps` object delivers ARIA attributes to a component under the following assumptions:

1. The control is an icon button that does not already have an accessible name
2. The control appears at (or near) the top of the expandable content in the `SidePanel`

Spreading the `controlProps` onto an external control can introduce serious accessibility issues:

- `aria-labelledby` HTML `id` reference may become invalid when the SidePanel is collapsed, or when
the `labelProps` component isn't present in the DOM.
- `aria-labelledby` will change the name of `controlProps` component to the title of the
`SidePanel`. (This may be undesirable. For example, the "Show Side Panel" button will be
overwritten with the "Tasks Panel" heading.)
- `aria-expanded` won't make sense to screen reader users when the expanded `SidePanel` content
isn't logically following the control.
- `aria-controls` is unsupported by screen readers and will not allow users to navigate to the
controlled content.

In the following example, the `controlProps` click handler function is passed down to the
`SecondaryButton` and a toggle state was added to the button using the `aria-pressed` property.

<ExampleCodeBlock code={ExternalControl} />

### Right Origin
Expand Down
53 changes: 36 additions & 17 deletions modules/preview-react/side-panel/stories/examples/AlwaysOpen.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,54 @@
import * as React from 'react';
import {styled} from '@workday/canvas-kit-react/common';
import {space} from '@workday/canvas-kit-react/tokens';
import {AccentIcon} from '@workday/canvas-kit-react/icon';
import {rocketIcon} from '@workday/canvas-accent-icons-web';
import {SidePanel, useSidePanel} from '@workday/canvas-kit-preview-react/side-panel';
import {Flex} from '@workday/canvas-kit-react/layout';
import {Text} from '@workday/canvas-kit-react/text';
import {Heading, Text} from '@workday/canvas-kit-react/text';
import {base, system} from '@workday/canvas-tokens-web';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';

const StyledAccentIcon = styled(AccentIcon)({
marginRight: space.s,
});
const stylesOverride = {
accentIcon: createStyles({
marginRight: system.space.x4,
}),
pageContainer: createStyles({
gap: system.space.x4,
height: px2rem(320),
}),
panelContainer: createStyles({
alignItems: 'center',
paddingY: system.space.x4,
paddingX: system.space.x4,
}),
panelHeading: createStyles({
color: base.licorice500,
}),
mainContent: createStyles({
alignItems: 'center',
justifyContent: 'center',
flexBasis: 'auto',
flex: 1,
}),
};

export const AlwaysOpen = () => {
const {panelProps, labelProps} = useSidePanel();

return (
<Flex gap="s" height={320}>
<Flex cs={stylesOverride.pageContainer}>
<SidePanel {...panelProps}>
<Flex alignItems="center" paddingY="s" paddingX="s">
<StyledAccentIcon icon={rocketIcon} />
<Text
as="h3"
typeLevel="body.large"
color="licorice500"
fontWeight="bold"
{...labelProps}
>
<Flex cs={stylesOverride.panelContainer}>
<AccentIcon icon={rocketIcon} cs={stylesOverride.accentIcon} />
<Heading size="small" cs={stylesOverride.panelHeading} {...labelProps}>
Tasks Panel
</Text>
</Heading>
</Flex>
</SidePanel>
<Flex as="main" cs={stylesOverride.mainContent}>
<Text as="p" typeLevel="body.large">
This is the main content section.
</Text>
</Flex>
</Flex>
);
};
74 changes: 40 additions & 34 deletions modules/preview-react/side-panel/stories/examples/Basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,60 @@ import {
SidePanelTransitionStates,
} from '@workday/canvas-kit-preview-react/side-panel';
import {Flex} from '@workday/canvas-kit-react/layout';
import {Text} from '@workday/canvas-kit-react/text';
import {Heading, Text} from '@workday/canvas-kit-react/text';
import {CanvasProvider} from '@workday/canvas-kit-react/common';
import {AccentIcon} from '@workday/canvas-kit-react/icon';
import {rocketIcon} from '@workday/canvas-accent-icons-web';
// local helper hook for setting content direction;
import {useDirection} from './useDirection';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
import {system, base} from '@workday/canvas-tokens-web';

const stylesOverride = {
viewPortContainer: createStyles({
height: px2rem(320),
}),
panel: createStyles({
alignItems: 'center',
paddingY: system.space.x4,
paddingX: system.space.x4,
}),
accentIcon: createStyles({
marginInlineEnd: system.space.x4,
}),
mainContent: createStyles({
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
flex: 1,
flexBasis: 'auto',
}),
};

export const Basic = () => {
const {direction, toggleDirection} = useDirection();
const {expanded, panelProps, labelProps, controlProps} = useSidePanel();
const [panelState, setPanelState] = React.useState<SidePanelTransitionStates>(
expanded ? 'expanded' : 'collapsed'
);

const expandedContent = (
<Flex alignItems="center" paddingY="s" paddingX="s">
<Flex marginInlineEnd="s">
<AccentIcon icon={rocketIcon} />
</Flex>
<Text as="h3" typeLevel="body.large" color="licorice500" fontWeight="bold" {...labelProps}>
Tasks Panel
</Text>
</Flex>
);

return (
<CanvasProvider theme={{canvas: {direction}}}>
<Flex height={320}>
<SidePanel {...panelProps} onStateTransition={setPanelState}>
<Flex cs={stylesOverride.viewPortContainer}>
<SidePanel {...panelProps}>
<SidePanel.ToggleButton {...controlProps} />
{panelState === 'expanded' ? (
expandedContent
) : (
<Text hidden {...labelProps}>
<Flex cs={stylesOverride.panel}>
{expanded && (
<Flex cs={stylesOverride.accentIcon}>
<AccentIcon icon={rocketIcon} />
</Flex>
)}
<Heading size="small" {...labelProps} hidden={!expanded ? true : undefined}>
Tasks Panel
</Text>
)}
</Heading>
</Flex>
</SidePanel>
<Flex
as="main"
alignItems="center"
justifyContent="center"
flexDirection="column"
flex={1}
flexBasis="auto"
>
<p>Toggle the content direction</p>
<SecondaryButton onClick={toggleDirection} role="button">
<Flex as="main" cs={stylesOverride.mainContent}>
<Text as="p" typeLevel="body.large">
Toggle the content direction
</Text>
<SecondaryButton onClick={toggleDirection}>
Set to {direction === 'ltr' ? 'Right-to-Left' : 'Left-to-Right'}
</SecondaryButton>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,74 @@ import {
SidePanelTransitionStates,
} from '@workday/canvas-kit-preview-react/side-panel';
import {Flex} from '@workday/canvas-kit-react/layout';
import {Text} from '@workday/canvas-kit-react/text';
import {Heading, Text} from '@workday/canvas-kit-react/text';
import {SecondaryButton} from '@workday/canvas-kit-react/button';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
import {base, system} from '@workday/canvas-tokens-web';

const stylesOverride = {
viewport: createStyles({
height: px2rem(320),
}),
panel: createStyles({
alignItems: 'center',
paddingY: system.space.x4,
paddingX: system.space.x4,
}),
panelHeading: createStyles({
color: base.licorice500,
}),
main: createStyles({
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
flex: 1,
flexBasis: 'auto',
}),
};

/*
* NOTE TO DEV:
* Spreading the `controlProps` onto an external control creates serious accessibility issues.
* - `aria-labelledby` id reference is invalid when the SidePanel is collapsed
* - `aria-labelledby` will change the name of "Toggle Side Panel" button to "Tasks Panel"
* - `aria-expanded` won't make sense to screen reader users when the expanded SidePanel content isn't following the control
* - `aria-controls` is unsupported by screen readers and will not allow users to navigate to the controlled content
*
* SOLUTION:
* - Pass the `controlProps` click handler function down to the external control component.
* - Add a toggle state to Button components with `aria-pressed` for screen readers,
* - OR use a similar toggle input like Checkbox or Switch.
*/
export const ExternalControl = () => {
const {expanded, panelProps, labelProps, controlProps} = useSidePanel({initialExpanded: false});
const [panelState, setPanelState] = React.useState<SidePanelTransitionStates>(
expanded ? 'expanded' : 'collapsed'
);

return (
<Flex height={320}>
<Flex cs={stylesOverride.viewport}>
<SidePanel
as="aside"
{...panelProps}
onExpandedChange={expanded => {
console.log(`expanded prop is: ${expanded ? 'true' : 'false'}`);
}}
onStateTransition={setPanelState}
>
{panelState === 'expanded' && (
<Flex alignItems="center" paddingY="s" paddingX="s">
<Text
as="h3"
typeLevel="body.large"
color="licorice500"
fontWeight="bold"
{...labelProps}
>
<Flex cs={stylesOverride.panel}>
<Heading size="small" cs={stylesOverride.panelHeading} {...labelProps}>
Tasks Panel
</Text>
</Heading>
</Flex>
)}
</SidePanel>
<Flex
as="main"
alignItems="center"
justifyContent="center"
flexDirection="column"
flex={1}
flexBasis="auto"
>
<p>Control the panel externally</p>
<SecondaryButton {...controlProps} role="button">
Toggle Side Panel
<Flex as="main" cs={stylesOverride.main}>
<Text as="p" typeLevel="body.large">
Control the panel externally
</Text>
<SecondaryButton onClick={controlProps.onClick} aria-pressed={expanded}>
Show Side Panel
</SecondaryButton>
</Flex>
</Flex>
Expand Down
29 changes: 24 additions & 5 deletions modules/preview-react/side-panel/stories/examples/HiddenName.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
import * as React from 'react';
import {SidePanel, useSidePanel} from '@workday/canvas-kit-preview-react/side-panel';
import {Flex} from '@workday/canvas-kit-react/layout';
import {AccessibleHide} from '@workday/canvas-kit-react/common';
import {Text} from '@workday/canvas-kit-react/text';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';

const stylesOverride = {
viewport: createStyles({
height: px2rem(320),
}),
main: createStyles({
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
flex: 1,
flexBasis: 'auto',
}),
};

export const HiddenName = () => {
const {panelProps, labelProps, controlProps} = useSidePanel();

return (
<Flex height={320}>
<Flex cs={stylesOverride.viewport}>
<SidePanel
as="aside"
{...panelProps}
onExpandedChange={expanded => {
console.log(`expanded prop is: ${expanded ? 'true' : 'false'}`);
Expand All @@ -17,10 +33,13 @@ export const HiddenName = () => {
}}
>
<SidePanel.ToggleButton {...controlProps} />
<span hidden {...labelProps}>
Side Panel with a hidden label
</span>
<AccessibleHide {...labelProps}>Hidden Title</AccessibleHide>
</SidePanel>
<Flex as="main" cs={stylesOverride.main}>
<Text as="p" typeLevel="body.large">
Side Panel with a hidden title text.
</Text>
</Flex>
</Flex>
);
};
Loading
Loading