Skip to content

Commit ebd96be

Browse files
authored
chore: Add proper textual name, role and state to breadcrumb last item (#3258)
1 parent fbe6454 commit ebd96be

File tree

4 files changed

+41
-20
lines changed

4 files changed

+41
-20
lines changed

Diff for: src/app-layout/__integ__/toolbar-tooltips/app-layout-toolbar-split-panel-trigger-tooltip.test.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,15 @@ describe('refresh-toolbar', () => {
6868
await expect(page.isExisting(tooltipSelector)).resolves.toBe(false);
6969
// Focus split panel trigger
7070
await page.click(wrapper.findToolbar().toSelector());
71-
await page.keys(['Tab', 'Tab']);
71+
await page.keys(['Tab', 'Tab', 'Tab']);
7272
await assertSplitPanelTriggerFocusedWithTooltip(page);
7373
// Focus breadcrumbs
7474
await page.keys(['Shift', 'Tab']);
75+
await page.keys(['Shift', 'Tab']);
7576
await expect(page.isExisting(tooltipSelector)).resolves.toBe(false);
7677
// Navigate back to split panel trigger
77-
await page.keys(['Tab']);
78+
await page.keys(['Tab', 'Tab']);
79+
7880
await assertSplitPanelTriggerFocusedWithTooltip(page);
7981
// Open the split panel
8082
await page.keys('Enter');
@@ -88,7 +90,7 @@ describe('refresh-toolbar', () => {
8890
await expect(page.isExisting(tooltipSelector)).resolves.toBe(false);
8991
// Focus split panel trigger
9092
await page.click(wrapper.findToolbar().toSelector());
91-
await page.keys(['Tab', 'Tab']);
93+
await page.keys(['Tab', 'Tab', 'Tab']);
9294
await assertSplitPanelTriggerFocusedWithTooltip(page);
9395
await page.keys('Escape');
9496
await expect(page.isExisting(tooltipSelector)).resolves.toBe(false);

Diff for: src/breadcrumb-group/__integ__/breadcrumb-group.test.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -163,16 +163,17 @@ describe('BreadcrumbGroup', () => {
163163
await page.keys('Tab');
164164
await page.keys('Tab');
165165
await page.keys('Tab');
166+
await page.keys('Tab');
166167
await expect(page.getActiveElementId()).resolves.toBe('focus-target-short-text');
167168
},
168169
{ width: 1200, height: 800 }
169170
)
170171
);
171172

172-
test(
173-
'Last item is focusable when truncated',
174-
setupTest(
175-
async page => {
173+
test.each([[{ width: 770, height: 800 }], [{ width: 1100, height: 800 }]])(
174+
'Last item should be focusable %s',
175+
viewportSize =>
176+
setupTest(async page => {
176177
await page.click('#focus-target-long-text');
177178
await page.keys('Tab');
178179
await page.keys('Tab');
@@ -182,10 +183,9 @@ describe('BreadcrumbGroup', () => {
182183
await page.keys('Tab');
183184
await expect(page.isTooltipDisplayed()).resolves.toBe(true);
184185
await page.keys('Tab');
186+
185187
await expect(page.isTooltipDisplayed()).resolves.toBe(false);
186188
await expect(page.getActiveElementId()).resolves.toBe('focus-target-short-text');
187-
},
188-
{ width: 950, height: 800 }
189-
)
189+
}, viewportSize)()
190190
);
191191
});

Diff for: src/breadcrumb-group/__tests__/breadcrumb-item.test.tsx

+5-4
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,11 @@ describe('BreadcrumbGroup Item', () => {
112112
lastLink = links[links.length - 1];
113113
});
114114

115-
test('should not be a link', () => {
116-
expect(lastLink.getElement()).not.toHaveAttribute('href');
117-
expect(lastLink.getElement()).not.toHaveAttribute('aria-current');
118-
expect(lastLink.getElement().tagName).toEqual('SPAN');
115+
test('should have proper aria-labels and focusable', () => {
116+
expect(lastLink.getElement()).toHaveAttribute('aria-current', 'page');
117+
expect(lastLink.getElement()).toHaveAttribute('aria-disabled');
118+
expect(lastLink.getElement().getAttribute('role')).toBe('link');
119+
expect(lastLink.getElement().getAttribute('tabindex')).toBe('0');
119120
});
120121

121122
test('should not trigger click event', () => {

Diff for: src/breadcrumb-group/item/item.tsx

+24-6
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ interface BreadcrumbItemWithPopoverProps<T extends BreadcrumbGroupProps.Item> {
1818
isLast: boolean;
1919
anchorAttributes: React.AnchorHTMLAttributes<HTMLAnchorElement>;
2020
children: React.ReactNode;
21+
itemAttributes: React.HTMLAttributes<HTMLElement>;
2122
}
2223

2324
const BreadcrumbItemWithPopover = <T extends BreadcrumbGroupProps.Item>({
2425
item,
2526
isLast,
2627
anchorAttributes,
28+
itemAttributes,
2729
children,
2830
}: BreadcrumbItemWithPopoverProps<T>) => {
2931
const [showPopover, setShowPopover] = useState(false);
@@ -54,7 +56,7 @@ const BreadcrumbItemWithPopover = <T extends BreadcrumbGroupProps.Item>({
5456
}}
5557
onMouseLeave={() => setShowPopover(false)}
5658
anchorAttributes={anchorAttributes}
57-
{...(isLast ? { tabIndex: 0 } : {})}
59+
{...itemAttributes}
5860
>
5961
{children}
6062
</Item>
@@ -68,16 +70,17 @@ type ItemProps = React.HTMLAttributes<HTMLElement> & {
6870
isLast: boolean;
6971
};
7072
const Item = React.forwardRef<HTMLElement, ItemProps>(
71-
({ anchorAttributes, children, isLast, ...itemAttributes }, ref) =>
72-
isLast ? (
73+
({ anchorAttributes, children, isLast, ...itemAttributes }, ref) => {
74+
return isLast ? (
7375
<span ref={ref} className={styles.anchor} {...itemAttributes}>
7476
{children}
7577
</span>
7678
) : (
7779
<a ref={ref as React.Ref<HTMLAnchorElement>} className={styles.anchor} {...itemAttributes} {...anchorAttributes}>
7880
{children}
7981
</a>
80-
)
82+
);
83+
}
8184
);
8285

8386
export function BreadcrumbItem<T extends BreadcrumbGroupProps.Item>({
@@ -101,11 +104,21 @@ export function BreadcrumbItem<T extends BreadcrumbGroupProps.Item>({
101104
const anchorAttributes: React.AnchorHTMLAttributes<HTMLAnchorElement> = {
102105
href: item.href || '#',
103106
onClick: isLast ? preventDefault : onClickHandler,
107+
tabIndex: 0,
104108
};
109+
110+
const itemAttributes: React.AnchorHTMLAttributes<HTMLAnchorElement> = {};
105111
if (isGhost) {
106112
anchorAttributes.tabIndex = -1;
107113
}
108114

115+
if (isLast && !isGhost) {
116+
itemAttributes['aria-current'] = 'page';
117+
itemAttributes['aria-disabled'] = true;
118+
itemAttributes.tabIndex = 0;
119+
itemAttributes.role = 'link';
120+
}
121+
109122
const breadcrumbItem = (
110123
<FunnelBreadcrumbItem
111124
className={styles.text}
@@ -119,11 +132,16 @@ export function BreadcrumbItem<T extends BreadcrumbGroupProps.Item>({
119132
return (
120133
<div className={clsx(!isGhost && styles.breadcrumb, isGhost && styles['ghost-breadcrumb'], isLast && styles.last)}>
121134
{isTruncated && !isGhost ? (
122-
<BreadcrumbItemWithPopover item={item} isLast={isLast} anchorAttributes={anchorAttributes}>
135+
<BreadcrumbItemWithPopover
136+
item={item}
137+
isLast={isLast}
138+
anchorAttributes={anchorAttributes}
139+
itemAttributes={itemAttributes}
140+
>
123141
{breadcrumbItem}
124142
</BreadcrumbItemWithPopover>
125143
) : (
126-
<Item isLast={isLast} anchorAttributes={anchorAttributes}>
144+
<Item isLast={isLast} anchorAttributes={anchorAttributes} {...itemAttributes}>
127145
{breadcrumbItem}
128146
</Item>
129147
)}

0 commit comments

Comments
 (0)