From 8db2874857349bf084188747dca34d72fc009b1d Mon Sep 17 00:00:00 2001 From: Akshita <110122283+akshita317@users.noreply.github.com> Date: Mon, 12 Oct 2026 21:10:28 +0530 Subject: [PATCH 1/3] fix(ActionBar): Allow icon-only items without children ActionBar.Item declared children as required, so an icon-only item such as failed to type-check, even though the underlying SecondaryButton renders icon-only buttons. Make children optional, document providing an aria-label for icon-only items, and add a spec that renders one and finds it by its accessible name. Fixes #3620 --- modules/react/action-bar/lib/ActionBarItem.tsx | 4 +++- .../react/action-bar/spec/ActionBar.spec.tsx | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/react/action-bar/lib/ActionBarItem.tsx b/modules/react/action-bar/lib/ActionBarItem.tsx index bfc28986d7..5297c88980 100644 --- a/modules/react/action-bar/lib/ActionBarItem.tsx +++ b/modules/react/action-bar/lib/ActionBarItem.tsx @@ -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 * First Action + * * ``` */ - 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 diff --git a/modules/react/action-bar/spec/ActionBar.spec.tsx b/modules/react/action-bar/spec/ActionBar.spec.tsx index 4f8dc6749e..88e44476fb 100644 --- a/modules/react/action-bar/spec/ActionBar.spec.tsx +++ b/modules/react/action-bar/spec/ActionBar.spec.tsx @@ -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( + + + + + + ); + + expect(screen.getByRole('button', {name: 'Delete'})).toBeInTheDocument(); + }); }); From c1db351e42c3c33e6b05bba7700c5a2231bdc4d6 Mon Sep 17 00:00:00 2001 From: Akshita <110122283+akshita317@users.noreply.github.com> Date: Tue, 13 Oct 2026 22:42:55 +0530 Subject: [PATCH 2/3] docs(ActionBar): Add icon-only example and visual test Add an Icon-Only example to the ActionBar Storybook docs, showing icon-only items with an aria-label next to a text action, and an ActionBarIconOnlyStates visual test covering icon-only items with and without a text action, in the default and small sizes. --- .../react/action-bar/stories/ActionBar.mdx | 9 ++++++ .../action-bar/stories/ActionBar.stories.ts | 4 +++ .../action-bar/stories/examples/IconOnly.tsx | 15 +++++++++ .../action-bar/stories/examples/index.ts | 1 + .../visual-testing/testing.stories.tsx | 32 +++++++++++++++++++ 5 files changed, 61 insertions(+) create mode 100644 modules/react/action-bar/stories/examples/IconOnly.tsx diff --git a/modules/react/action-bar/stories/ActionBar.mdx b/modules/react/action-bar/stories/ActionBar.mdx index 242c8a4a49..08326d6c70 100644 --- a/modules/react/action-bar/stories/ActionBar.mdx +++ b/modules/react/action-bar/stories/ActionBar.mdx @@ -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'; @@ -42,6 +43,14 @@ with `ActionBar.Item` such as `icon` or `size`. +### 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. + + + ### Delete Action Example `ActionBar.Item` is a `SecondaryButton` by default but it's posible to change it to another element, diff --git a/modules/react/action-bar/stories/ActionBar.stories.ts b/modules/react/action-bar/stories/ActionBar.stories.ts index 25a3ee10b7..9da6f96d7d 100644 --- a/modules/react/action-bar/stories/ActionBar.stories.ts +++ b/modules/react/action-bar/stories/ActionBar.stories.ts @@ -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'; @@ -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, }; diff --git a/modules/react/action-bar/stories/examples/IconOnly.tsx b/modules/react/action-bar/stories/examples/IconOnly.tsx new file mode 100644 index 0000000000..84eddd2ba9 --- /dev/null +++ b/modules/react/action-bar/stories/examples/IconOnly.tsx @@ -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 ( + + + Save + + + + + ); +}; diff --git a/modules/react/action-bar/stories/examples/index.ts b/modules/react/action-bar/stories/examples/index.ts index 4f37831963..12efa4dfbd 100644 --- a/modules/react/action-bar/stories/examples/index.ts +++ b/modules/react/action-bar/stories/examples/index.ts @@ -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'; diff --git a/modules/react/action-bar/stories/visual-testing/testing.stories.tsx b/modules/react/action-bar/stories/visual-testing/testing.stories.tsx index e16ade025d..d33c4f1e35 100644 --- a/modules/react/action-bar/stories/visual-testing/testing.stories.tsx +++ b/modules/react/action-bar/stories/visual-testing/testing.stories.tsx @@ -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 { @@ -55,6 +56,37 @@ export const ActionBarStates = { ), }; +export const ActionBarIconOnlyStates = { + render: () => ( + + + {({size, withText}) => ( + + + {withText && ( + + Save + + )} + + + + + )} + + + ), +}; + export const ActionBarWithOverflowMenuStates = { render: () => { const [items] = React.useState([ From 99d7df8dec90459590d8b2fa53d3d0424a61b812 Mon Sep 17 00:00:00 2001 From: Akshita <110122283+akshita317@users.noreply.github.com> Date: Wed, 14 Oct 2026 20:59:34 +0530 Subject: [PATCH 3/3] test(ActionBar): Render icon-only visual states from a component Move the ActionBarIconOnlyStates table into an ActionBarIconOnlyTest component and render it from the story, following the convention of keeping JSX out of the CSF story object. --- .../visual-testing/testing.stories.tsx | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/modules/react/action-bar/stories/visual-testing/testing.stories.tsx b/modules/react/action-bar/stories/visual-testing/testing.stories.tsx index d33c4f1e35..1f165125ba 100644 --- a/modules/react/action-bar/stories/visual-testing/testing.stories.tsx +++ b/modules/react/action-bar/stories/visual-testing/testing.stories.tsx @@ -56,35 +56,37 @@ export const ActionBarStates = { ), }; +const ActionBarIconOnlyTest = () => ( + + + {({size, withText}) => ( + + + {withText && ( + + Save + + )} + + + + + )} + + +); + export const ActionBarIconOnlyStates = { - render: () => ( - - - {({size, withText}) => ( - - - {withText && ( - - Save - - )} - - - - - )} - - - ), + render: () => , }; export const ActionBarWithOverflowMenuStates = {