From 9c9bf952cf2a1e4a0c5bc35e63e46f5d5bd6afe8 Mon Sep 17 00:00:00 2001 From: David <162190334+blaabaer@users.noreply.github.com> Date: Thu, 14 Mar 2024 06:30:06 -0700 Subject: [PATCH] fix(tab-overflow): improve tab navigation experience and support custom 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 --- packages/tabs/src/TabsOverflow.ts | 14 +++- packages/tabs/tabs-overflow.md | 4 ++ packages/tabs/test/tabs-overflow.test.ts | 84 ++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/packages/tabs/src/TabsOverflow.ts b/packages/tabs/src/TabsOverflow.ts index cf21f1b116..3a9aa22d45 100644 --- a/packages/tabs/src/TabsOverflow.ts +++ b/packages/tabs/src/TabsOverflow.ts @@ -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'; @@ -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`
` component, simply wrap it around the ``
``` + +### Accessibility + +The `` 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 `` component is not useful for Keyboard Tab Navigation, so it is removed as to not be a hinderance. diff --git a/packages/tabs/test/tabs-overflow.test.ts b/packages/tabs/test/tabs-overflow.test.ts index 58fd686153..80c2ac67b6 100644 --- a/packages/tabs/test/tabs-overflow.test.ts +++ b/packages/tabs/test/tabs-overflow.test.ts @@ -34,6 +34,8 @@ type OverflowProperties = { includeTabPanel: boolean; selected?: number; autoscroll?: boolean; + labelPrev?: string; + labelNext?: string; }; const renderTabsOverflow = async ({ @@ -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( + html` +
+ + + ${repeat( + new Array(20), + (item) => item, + (_item, index) => + html` + + ` + )} + ${repeat( + new Array(20), + (item) => item, + (_item, index) => + html` + + Content for Tab Item ${index + 1} + + ` + )} + + +
+ ` + ); + 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' + ); + }); });