From abfe9c42e312c73e9a57de1c35b564132a4fa86a Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Wed, 1 Apr 2026 23:04:04 -0400 Subject: [PATCH 1/4] feat(ActionBar): add data-component attributes for better accessibility --- .../react/src/ActionBar/ActionBar.test.tsx | 62 +++++++++++++++++++ packages/react/src/ActionBar/ActionBar.tsx | 15 ++++- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/packages/react/src/ActionBar/ActionBar.test.tsx b/packages/react/src/ActionBar/ActionBar.test.tsx index f176d5e7add..8737e8f282e 100644 --- a/packages/react/src/ActionBar/ActionBar.test.tsx +++ b/packages/react/src/ActionBar/ActionBar.test.tsx @@ -417,3 +417,65 @@ describe('ActionBar.Menu returnFocusRef', () => { expect(document.activeElement).toEqual(menuButton) }) }) + +describe('ActionBar data-component attributes', () => { + it('renders ActionBar with data-component attribute', () => { + const {container} = render( + + + , + ) + + const actionBar = container.querySelector('[data-component="ActionBar"]') + expect(actionBar).toBeInTheDocument() + }) + + it('renders ActionBar.IconButton with data-component attribute', () => { + render( + + + , + ) + + const iconButton = screen.getByRole('button', {name: 'Bold'}) + expect(iconButton).toHaveAttribute('data-component', 'ActionBar.IconButton') + }) + + it('renders ActionBar.Divider with data-component attribute', () => { + const {container} = render( + + + + + , + ) + + const divider = container.querySelector('[data-component="ActionBar.Divider"]') + expect(divider).toBeInTheDocument() + }) + + it('renders ActionBar.Group with data-component attribute', () => { + const {container} = render( + + + + + + , + ) + + const group = container.querySelector('[data-component="ActionBar.Group"]') + expect(group).toBeInTheDocument() + }) + + it('renders ActionBar.Menu with data-component attribute', () => { + render( + + + , + ) + + const menuButton = screen.getByRole('button', {name: 'More options'}) + expect(menuButton).toHaveAttribute('data-component', 'ActionBar.Menu') + }) +}) diff --git a/packages/react/src/ActionBar/ActionBar.tsx b/packages/react/src/ActionBar/ActionBar.tsx index 2ba3b086b9c..97855148b2a 100644 --- a/packages/react/src/ActionBar/ActionBar.tsx +++ b/packages/react/src/ActionBar/ActionBar.tsx @@ -353,7 +353,7 @@ export const ActionBar: React.FC> = prop return ( -
+
) @@ -532,7 +534,7 @@ export const ActionBarGroup = forwardRef(({children}: React.PropsWithChildren, f return ( -
+
{children}
@@ -571,7 +573,14 @@ export const ActionBarMenu = forwardRef( return ( - + {items.map((item, index) => renderMenuItem(item, index))} From 63812809bd26f45fef285e1c41aa2e9e758eadaf Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Wed, 1 Apr 2026 23:20:12 -0400 Subject: [PATCH 2/4] fix test --- packages/react/src/ActionBar/ActionBar.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react/src/ActionBar/ActionBar.test.tsx b/packages/react/src/ActionBar/ActionBar.test.tsx index 8737e8f282e..47d92c122e4 100644 --- a/packages/react/src/ActionBar/ActionBar.test.tsx +++ b/packages/react/src/ActionBar/ActionBar.test.tsx @@ -441,7 +441,7 @@ describe('ActionBar data-component attributes', () => { expect(iconButton).toHaveAttribute('data-component', 'ActionBar.IconButton') }) - it('renders ActionBar.Divider with data-component attribute', () => { + it('renders ActionBar.VerticalDivider with data-component attribute', () => { const {container} = render( @@ -450,7 +450,7 @@ describe('ActionBar data-component attributes', () => { , ) - const divider = container.querySelector('[data-component="ActionBar.Divider"]') + const divider = container.querySelector('[data-component="ActionBar.VerticalDivider"]') expect(divider).toBeInTheDocument() }) From 32d40626b3923140852c369d9be7a2d8814d08bd Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Tue, 7 Apr 2026 18:05:02 -0400 Subject: [PATCH 3/4] fix(ActionBar): update data-component attributes for IconButton and Menu --- packages/react/src/ActionBar/ActionBar.test.tsx | 4 ++-- packages/react/src/ActionBar/ActionBar.tsx | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/react/src/ActionBar/ActionBar.test.tsx b/packages/react/src/ActionBar/ActionBar.test.tsx index 47d92c122e4..63757e6c48e 100644 --- a/packages/react/src/ActionBar/ActionBar.test.tsx +++ b/packages/react/src/ActionBar/ActionBar.test.tsx @@ -438,7 +438,7 @@ describe('ActionBar data-component attributes', () => { ) const iconButton = screen.getByRole('button', {name: 'Bold'}) - expect(iconButton).toHaveAttribute('data-component', 'ActionBar.IconButton') + expect(iconButton).toHaveAttribute('data-component', 'IconButton') }) it('renders ActionBar.VerticalDivider with data-component attribute', () => { @@ -476,6 +476,6 @@ describe('ActionBar data-component attributes', () => { ) const menuButton = screen.getByRole('button', {name: 'More options'}) - expect(menuButton).toHaveAttribute('data-component', 'ActionBar.Menu') + expect(menuButton).toHaveAttribute('data-component', 'ActionBar.Menu.IconButton') }) }) diff --git a/packages/react/src/ActionBar/ActionBar.tsx b/packages/react/src/ActionBar/ActionBar.tsx index 97855148b2a..e6a8bab6eb5 100644 --- a/packages/react/src/ActionBar/ActionBar.tsx +++ b/packages/react/src/ActionBar/ActionBar.tsx @@ -511,8 +511,6 @@ export const ActionBarIconButton = forwardRef( size={size} onClick={clickHandler} {...props} - // TODO: does this make sense? it'll override IconButton's data-component - data-component="ActionBar.IconButton" variant="invisible" /> ) @@ -578,8 +576,8 @@ export const ActionBarMenu = forwardRef( aria-label={ariaLabel} icon={icon} {...props} - // TODO: does this make sense? it'll override IconButton's data-component - data-component="ActionBar.Menu" + // overriding IconButton's data-component so that the ActionBar's "More Menu" Icon can be targetted specifically + data-component="ActionBar.Menu.IconButton" /> From c7c0da789dae56bc2bdb603d7b7fc24d139fa005 Mon Sep 17 00:00:00 2001 From: Marie Lucca Date: Tue, 7 Apr 2026 22:42:37 -0400 Subject: [PATCH 4/4] Add data-component for ActionList --- .../react/src/ActionList/ActionList.test.tsx | 191 ++++++++++++++++++ packages/react/src/ActionList/Group.tsx | 11 +- packages/react/src/ActionList/Heading.tsx | 1 + packages/react/src/ActionList/Item.tsx | 4 +- packages/react/src/ActionList/LinkItem.tsx | 15 +- packages/react/src/ActionList/List.tsx | 1 + .../react/src/ActionList/TrailingAction.tsx | 6 +- packages/react/src/ActionList/Visuals.tsx | 13 +- 8 files changed, 233 insertions(+), 9 deletions(-) diff --git a/packages/react/src/ActionList/ActionList.test.tsx b/packages/react/src/ActionList/ActionList.test.tsx index 49e44022c7f..2b3c0bc0f07 100644 --- a/packages/react/src/ActionList/ActionList.test.tsx +++ b/packages/react/src/ActionList/ActionList.test.tsx @@ -258,3 +258,194 @@ describe('ActionList', () => { expect(linkElements[2]).toHaveAttribute('data-size', 'medium') // default should be medium }) }) + +describe('ActionList data-component attributes', () => { + it('renders ActionList with data-component attribute', () => { + const {container} = HTMLRender( + + Item + , + ) + + const actionList = container.querySelector('[data-component="ActionList"]') + expect(actionList).toBeInTheDocument() + }) + + it('renders ActionList.Item with data-component attribute', () => { + const {container} = HTMLRender( + + Item + , + ) + + const item = container.querySelector('[data-component="ActionList.Item"]') + expect(item).toBeInTheDocument() + }) + + it('renders ActionList.Item.Label with data-component attribute', () => { + const {container} = HTMLRender( + + Item + , + ) + + const label = container.querySelector('[data-component="ActionList.Item.Label"]') + expect(label).toBeInTheDocument() + }) + + it('renders ActionList.Item--DividerContainer with data-component attribute', () => { + const {container} = HTMLRender( + + Item + , + ) + + const dividerContainer = container.querySelector('[data-component="ActionList.Item--DividerContainer"]') + expect(dividerContainer).toBeInTheDocument() + }) + + it('renders ActionList.Group with data-component attribute', () => { + const {container} = HTMLRender( + + + Group + Item + + , + ) + + const group = container.querySelector('[data-component="ActionList.Group"]') + expect(group).toBeInTheDocument() + }) + + it('renders ActionList.GroupHeading with data-component attribute', () => { + const {container} = HTMLRender( + + + Group + Item + + , + ) + + const headingWrap = container.querySelector('[data-component="ActionList.GroupHeading"]') + expect(headingWrap).toBeInTheDocument() + }) + + it('renders ActionList.GroupHeading with data-component attribute', () => { + const {container} = HTMLRender( + + + Group Heading + Item + + , + ) + + const groupHeading = container.querySelector('[data-component="ActionList.GroupHeading"]') + expect(groupHeading).toBeInTheDocument() + }) + + it('renders ActionList.Divider with data-component attribute', () => { + const {container} = HTMLRender( + + Item 1 + + Item 2 + , + ) + + const divider = container.querySelector('[data-component="ActionList.Divider"]') + expect(divider).toBeInTheDocument() + }) + + it('renders ActionList.Description with data-component attribute', () => { + const {container} = HTMLRender( + + + Item + Description + + , + ) + + const description = container.querySelector('[data-component="ActionList.Description"]') + expect(description).toBeInTheDocument() + }) + + it('renders ActionList.LeadingVisual with data-component attribute', () => { + const {container} = HTMLRender( + + + Icon + Item + + , + ) + + const leadingVisual = container.querySelector('[data-component="ActionList.LeadingVisual"]') + expect(leadingVisual).toBeInTheDocument() + }) + + it('renders ActionList.TrailingVisual with data-component attribute', () => { + const {container} = HTMLRender( + + + Item + Icon + + , + ) + + const trailingVisual = container.querySelector('[data-component="ActionList.TrailingVisual"]') + expect(trailingVisual).toBeInTheDocument() + }) + + it('renders ActionList.Selection with data-component attribute', () => { + const {container} = HTMLRender( + + Item + , + ) + + const selection = container.querySelector('[data-component="ActionList.Selection"]') + expect(selection).toBeInTheDocument() + }) + + it('renders ActionList.Heading with data-component attribute', () => { + const {container} = HTMLRender( + + Heading + Item + , + ) + + const heading = container.querySelector('[data-component="ActionList.Heading"]') + expect(heading).toBeInTheDocument() + }) + + it('renders ActionList.TrailingAction with data-component attribute', () => { + const {container} = HTMLRender( + + + Item + + + , + ) + + const trailingAction = container.querySelector('[data-component="ActionList.TrailingAction"]') + expect(trailingAction).toBeInTheDocument() + }) + + it('renders ActionList.LinkItem with data-component attribute', () => { + const {container} = HTMLRender( + + Link Item + , + ) + + const linkItem = container.querySelector('[data-component="ActionList.LinkItem"]') + expect(linkItem).toBeInTheDocument() + }) +}) diff --git a/packages/react/src/ActionList/Group.tsx b/packages/react/src/ActionList/Group.tsx index 441e79302c9..0d409a83723 100644 --- a/packages/react/src/ActionList/Group.tsx +++ b/packages/react/src/ActionList/Group.tsx @@ -98,7 +98,12 @@ export const Group: FCWithSlotMarker +
  • {title && !slots.groupHeading ? ( // Escape hatch: supports old API in a non breaking way @@ -177,7 +182,7 @@ export const GroupHeading: FCWithSlotMarker
  • ( > {slots.leadingVisual} + {/* TODO: next-major: change to data-component="ActionList.Item.DividerContainer" next major version */} - + {childrenWithoutSlots} {/* Loading message needs to be in here so it is read with the label */} {/* If the item is inactive, we do not simultaneously announce that it is loading */} diff --git a/packages/react/src/ActionList/LinkItem.tsx b/packages/react/src/ActionList/LinkItem.tsx index e1687bbdc22..ebb21051b4b 100644 --- a/packages/react/src/ActionList/LinkItem.tsx +++ b/packages/react/src/ActionList/LinkItem.tsx @@ -48,7 +48,11 @@ const LinkItemComponent = fixedForwardRef( props.onClick && props.onClick(event as React.MouseEvent) } if (inactiveText) { - return {children} + return ( + + {children} + + ) } // Type safety for the polymorphic `as` prop is enforced at the @@ -57,7 +61,14 @@ const LinkItemComponent = fixedForwardRef( // constraint across two polymorphic layers. const InternalLink: React.ElementType = Link return ( - + {children} ) diff --git a/packages/react/src/ActionList/List.tsx b/packages/react/src/ActionList/List.tsx index ee9213fcb50..00ef52462a5 100644 --- a/packages/react/src/ActionList/List.tsx +++ b/packages/react/src/ActionList/List.tsx @@ -75,6 +75,7 @@ const UnwrappedList = ( role={listRole} aria-labelledby={ariaLabelledBy} ref={listRef} + data-component="ActionList" data-dividers={showDividers} data-variant={variant} {...restProps} diff --git a/packages/react/src/ActionList/TrailingAction.tsx b/packages/react/src/ActionList/TrailingAction.tsx index 933c3a6f584..585a0f4aa14 100644 --- a/packages/react/src/ActionList/TrailingAction.tsx +++ b/packages/react/src/ActionList/TrailingAction.tsx @@ -31,7 +31,11 @@ export type ActionListTrailingActionProps = ElementProps & { export const TrailingAction = forwardRef( ({as = 'button', icon, label, href = null, className, style, loading, ...props}, forwardedRef) => { return ( - + {icon ? ( > = ( export type ActionListLeadingVisualProps = VisualProps export const LeadingVisual: FCWithSlotMarker> = ({className, ...props}) => { return ( - + {props.children} ) @@ -26,7 +30,12 @@ export type ActionListTrailingVisualProps = VisualProps export const TrailingVisual: FCWithSlotMarker> = ({className, ...props}) => { const {trailingVisualId} = React.useContext(ItemContext) return ( - + {props.children} )