Skip to content

Commit

Permalink
fix(tab-overflow): improve tab navigation experience and support cust…
Browse files Browse the repository at this point in the history
…om aria labels (#4165)

* fix: remove tabnav to taboverflow and allow custom label

* docs: add accessibility disclaimer on taboverflow docs

* fix: syntax updates, clearer default behavior in new labels, revised test

* fix: remove comment

---------

Co-authored-by: davidx <davidx@adobe.com>
  • Loading branch information
blaabaer and davidx authored Mar 14, 2024
1 parent c832eb5 commit 9c9bf95
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 1 deletion.
14 changes: 13 additions & 1 deletion packages/tabs/src/TabsOverflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,13 @@ export class TabsOverflow extends SizedMixin(SpectrumElement) {
}

@property({ type: Boolean, reflect: true })
compact = false;
public compact = false;

@property({ type: String, attribute: 'label-previous' })
public labelPrevious = 'Scroll to previous tabs';

@property({ type: String, attribute: 'label-next' })
public labelNext = 'Scroll to next tabs';

@property({ reflect: true })
public override dir!: 'ltr' | 'rtl';
Expand Down Expand Up @@ -132,6 +138,8 @@ export class TabsOverflow extends SizedMixin(SpectrumElement) {

protected override render(): TemplateResult {
const { canScrollRight, canScrollLeft } = this.overflowState;
const ariaLabelPrevious = this.labelPrevious;
const ariaLabelNext = this.labelNext;
return html`
<div
class=${classMap({
Expand All @@ -145,8 +153,10 @@ export class TabsOverflow extends SizedMixin(SpectrumElement) {
'left-scroll': true,
show: canScrollLeft,
})}
aria-label=${ariaLabelPrevious}
quiet
dir="rtl"
tabindex="-1"
@click=${this._handleScrollClick}
>
<sp-icon-chevron100
Expand All @@ -159,7 +169,9 @@ export class TabsOverflow extends SizedMixin(SpectrumElement) {
'right-scroll': true,
show: canScrollRight,
})}
aria-label=${ariaLabelNext}
quiet
tabindex="-1"
@click=${this._handleScrollClick}
>
<sp-icon-chevron100
Expand Down
4 changes: 4 additions & 0 deletions packages/tabs/tabs-overflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@ To use the `<sp-tabs-overflow>` component, simply wrap it around the `<sp-tabs>`
</sp-tabs-overflow>
</div>
```

### Accessibility

The `<sp-tabs-overflow>` component is not focusable via Keyboard Tab Navigation. The Tabs Overflow buttons only help visually scroll down the list of Tabs. Keyboard users can navigate through all elements inside the Tabs list using arrow keys, and Keyboard users will always initially focus on the very first Tab element, no matter how visually scrolled the Tab group might be. Therefore, the `<sp-tabs-overflow>` component is not useful for Keyboard Tab Navigation, so it is removed as to not be a hinderance.
84 changes: 84 additions & 0 deletions packages/tabs/test/tabs-overflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type OverflowProperties = {
includeTabPanel: boolean;
selected?: number;
autoscroll?: boolean;
labelPrev?: string;
labelNext?: string;
};

const renderTabsOverflow = async ({
Expand Down Expand Up @@ -213,4 +215,86 @@ describe('TabsOverflow', () => {
const firstTabPosition = firstTab.getBoundingClientRect();
expect(firstTabPosition.left).to.be.lessThan(0);
});

it('prev and next buttons have default labels', async () => {
const el = await renderTabsOverflow({
count: 20,
size: ElementSizes.M,
includeTabPanel: true,
});
await elementUpdated(el);

const spTabsOverflows: TabsOverflow = el.querySelector(
'sp-tabs-overflow'
) as TabsOverflow;
const leftButton = spTabsOverflows.shadowRoot.querySelector(
'.left-scroll'
) as ActionButton;
const rightButton = spTabsOverflows.shadowRoot.querySelector(
'.right-scroll'
) as ActionButton;

expect(leftButton?.getAttribute('aria-label')).to.equal(
'Scroll to previous tabs'
);
expect(rightButton?.getAttribute('aria-label')).to.equal(
'Scroll to next tabs'
);
});

it('prev and next buttons labels overwritten via attributes', async () => {
const tabsContainer = await fixture<HTMLDivElement>(
html`
<div class="container" style="width: 200px; height: 150px;">
<sp-tabs-overflow
label-previous="custom label prev"
label-next="custom label next"
>
<sp-tabs size=${ElementSizes.M} selected=${1}>
${repeat(
new Array(20),
(item) => item,
(_item, index) =>
html`
<sp-tab
label=${`Tab Item ${index + 1}`}
value=${index + 1}
></sp-tab>
`
)}
${repeat(
new Array(20),
(item) => item,
(_item, index) =>
html`
<sp-tab-panel value=${index + 1}>
Content for Tab Item ${index + 1}
</sp-tab-panel>
`
)}
</sp-tabs>
</sp-tabs-overflow>
</div>
`
);
await elementUpdated(tabsContainer);
const el = tabsContainer;

const spTabsOverflows: TabsOverflow = el.querySelector(
'sp-tabs-overflow'
) as TabsOverflow;
const leftButton = spTabsOverflows.shadowRoot.querySelector(
'.left-scroll'
) as ActionButton;
const rightButton = spTabsOverflows.shadowRoot.querySelector(
'.right-scroll'
) as ActionButton;

expect(leftButton?.getAttribute('aria-label')).to.equal(
'custom label prev'
);
expect(rightButton?.getAttribute('aria-label')).to.equal(
'custom label next'
);
});
});

0 comments on commit 9c9bf95

Please # to comment.