Skip to content
Merged
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
4 changes: 3 additions & 1 deletion modules/react/action-bar/lib/ActionBarItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import {useActionBarModel} from './useActionBarModel';
export interface ActionBarItemProps {
/**
* The contents of the action item. This will be the accessible name of the action for screen readers.
* Omit it to render an icon-only button, and provide an `aria-label` instead.
*
* ```tsx
* <ActionBar.Item>First Action</ActionBar.Item>
* <ActionBar.Item icon={trashIcon} aria-label="Delete" />
* ```
*/
children: React.ReactNode;
children?: React.ReactNode;
/**
* The identifier of the action. This identifier will be used for correct overflow behavior.
* If this property is not provided, it will default to a string representation
Expand Down
17 changes: 17 additions & 0 deletions modules/react/action-bar/spec/ActionBar.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
import {render} from '@testing-library/react';
import * as React from 'react';

import {trashIcon} from '@workday/canvas-system-icons-web';

import {ActionBar} from '../lib/ActionBar';
import {useActionBarModel} from '../lib/useActionBarModel';

describe('ActionBar.Item', () => {
verifyComponent(ActionBar.Item, {modelFn: useActionBarModel});

it('should render an icon-only item named by its aria-label', () => {
const screen = render(
<ActionBar>
<ActionBar.List>
<ActionBar.Item icon={trashIcon} aria-label="Delete" />
</ActionBar.List>
</ActionBar>
);

expect(screen.getByRole('button', {name: 'Delete'})).toBeInTheDocument();
});
});
9 changes: 9 additions & 0 deletions modules/react/action-bar/stories/ActionBar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Meta} from '@storybook/blocks';
import {ExampleCodeBlock, SymbolDoc} from '@workday/canvas-kit-docs';
import {Basic} from './examples/Basic';
import {Icons} from './examples/Icons';
import {IconOnly} from './examples/IconOnly';
import {DeleteAction} from './examples/DeleteAction';
import {OverflowActionBar} from './examples/OverflowActionBar';
import {OverflowActionBarCustomButtonCount} from './examples/OverflowActionBarCustomButtonCount';
Expand Down Expand Up @@ -42,6 +43,14 @@ with `ActionBar.Item` such as `icon` or `size`.

<ExampleCodeBlock code={Icons} />

### Icon-Only Example

An `ActionBar.Item` can render an icon-only button by passing an `icon` without `children`. Because
there is no visible text, provide an `aria-label` so the action has an accessible name. Wrapping the
item in a [Tooltip](/components/popups/tooltip/) also shows the label to sighted users.

<ExampleCodeBlock code={IconOnly} />

### Delete Action Example

`ActionBar.Item` is a `SecondaryButton` by default but it's posible to change it to another element,
Expand Down
4 changes: 4 additions & 0 deletions modules/react/action-bar/stories/ActionBar.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {ActionBar} from '@workday/canvas-kit-react/action-bar';
import mdxDoc from './ActionBar.mdx';
import {Basic as BasicExample} from './examples/Basic';
import {DeleteAction as DeleteActionExample} from './examples/DeleteAction';
import {IconOnly as IconOnlyExample} from './examples/IconOnly';
import {Icons as IconsExample} from './examples/Icons';
import {OverflowActionBar as OverflowActionBarExample} from './examples/OverflowActionBar';
import {OverflowActionBarCustomButtonCount as OverflowActionBarCustomButtonCountExample} from './examples/OverflowActionBarCustomButtonCount';
Expand All @@ -28,6 +29,9 @@ export const Basic: Story = {
export const Icons: Story = {
render: IconsExample,
};
export const IconOnly: Story = {
render: IconOnlyExample,
};
export const DeleteAction: Story = {
render: DeleteActionExample,
};
Expand Down
15 changes: 15 additions & 0 deletions modules/react/action-bar/stories/examples/IconOnly.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {ActionBar} from '@workday/canvas-kit-react/action-bar';
import {PrimaryButton} from '@workday/canvas-kit-react/button';
import {editIcon, trashIcon} from '@workday/canvas-system-icons-web';

export const IconOnly = () => {
return (
<ActionBar>
<ActionBar.List position="relative" as="section" aria-label="Icon-only Action Bar">
<ActionBar.Item as={PrimaryButton}>Save</ActionBar.Item>
<ActionBar.Item icon={editIcon} aria-label="Edit" />
<ActionBar.Item icon={trashIcon} aria-label="Delete" />
</ActionBar.List>
</ActionBar>
);
};
1 change: 1 addition & 0 deletions modules/react/action-bar/stories/examples/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export {Basic} from './Basic';
export {DeleteAction} from './DeleteAction';
export {IconOnly} from './IconOnly';
export {Icons} from './Icons';
export {OverflowActionBar} from './OverflowActionBar';
export {OverflowActionBarCustomButtonCount} from './OverflowActionBarCustomButtonCount';
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {ActionBar} from '@workday/canvas-kit-react/action-bar';
import {PrimaryButton, SecondaryButton} from '@workday/canvas-kit-react/button';
import {ComponentStatesTable, StaticStates} from '@workday/canvas-kit-react/testing';
import {cssVar, px2rem} from '@workday/canvas-kit-styling';
import {editIcon, trashIcon} from '@workday/canvas-system-icons-web';
import {base} from '@workday/canvas-tokens-web';

export default {
Expand Down Expand Up @@ -55,6 +56,39 @@ export const ActionBarStates = {
),
};

const ActionBarIconOnlyTest = () => (
<StaticStates>
<ComponentStatesTable
rowProps={[
{label: 'Icon-only items', props: {}},
{label: 'Icon-only items (small)', props: {size: 'small'}},
]}
columnProps={[
{label: 'Icons only', props: {withText: false}},
{label: 'With a text action', props: {withText: true}},
]}
>
{({size, withText}) => (
<ActionBar>
<ActionBar.List position="relative">
{withText && (
<ActionBar.Item as={PrimaryButton} size={size}>
Save
</ActionBar.Item>
)}
<ActionBar.Item icon={editIcon} aria-label="Edit" size={size} />
<ActionBar.Item icon={trashIcon} aria-label="Delete" size={size} />
</ActionBar.List>
</ActionBar>
)}
</ComponentStatesTable>
</StaticStates>
);

export const ActionBarIconOnlyStates = {
render: () => <ActionBarIconOnlyTest />,
};

export const ActionBarWithOverflowMenuStates = {
render: () => {
const [items] = React.useState<MyActionItem[]>([
Expand Down
Loading