diff --git a/change/@microsoft-fast-foundation-7d8d0430-523b-473a-af77-e2bcf0bc6e38.json b/change/@microsoft-fast-foundation-7d8d0430-523b-473a-af77-e2bcf0bc6e38.json new file mode 100644 index 00000000000..e6da993e8d0 --- /dev/null +++ b/change/@microsoft-fast-foundation-7d8d0430-523b-473a-af77-e2bcf0bc6e38.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Prevent keyboard navigation to hidden tab", + "packageName": "@microsoft/fast-foundation", + "email": "7282195+m-akinc@users.noreply.github.com", + "dependentChangeType": "prerelease" +} diff --git a/packages/web-components/fast-foundation/src/tabs/tabs.pw.spec.ts b/packages/web-components/fast-foundation/src/tabs/tabs.pw.spec.ts index 4ca4e857224..1ba5cc21fa4 100644 --- a/packages/web-components/fast-foundation/src/tabs/tabs.pw.spec.ts +++ b/packages/web-components/fast-foundation/src/tabs/tabs.pw.spec.ts @@ -386,4 +386,70 @@ test.describe("Tabs", () => { await expect(element).toHaveJSProperty("activeid", secondTabId); }); + + test("should not allow selecting hidden tab using arrow keys", async () => { + test.slow(); + + await root.evaluate(node => { + node.innerHTML = /* html */ ` + + Tab one + + Tab three + Tab panel one + Tab panel two + Tab panel three + + `; + }); + + const firstTab = tabs.nth(0); + + const thirdTab = tabs.nth(2); + + const firstTabId = (await firstTab.getAttribute("id")) ?? ""; + + const thirdTabId = (await thirdTab.getAttribute("id")) ?? ""; + + await element.evaluate((node: FASTTabs, firstTabId) => { + node.activeid = firstTabId; + }, firstTabId); + + await firstTab.press("ArrowRight"); + + await expect(element).toHaveJSProperty("activeid", thirdTabId); + }); + + test("should not allow selecting hidden tab by pressing End", async () => { + test.slow(); + + await root.evaluate(node => { + node.innerHTML = /* html */ ` + + Tab one + Tab two + + Tab panel one + Tab panel two + Tab panel three + + `; + }); + + const firstTab = tabs.nth(0); + + const secondTab = tabs.nth(1); + + const firstTabId = (await firstTab.getAttribute("id")) ?? ""; + + const secondTabId = (await secondTab.getAttribute("id")) ?? ""; + + await element.evaluate((node: FASTTabs, firstTabId) => { + node.activeid = firstTabId; + }, firstTabId); + + await firstTab.press("End"); + + await expect(element).toHaveJSProperty("activeid", secondTabId); + }); }); diff --git a/packages/web-components/fast-foundation/src/tabs/tabs.ts b/packages/web-components/fast-foundation/src/tabs/tabs.ts index 3c7c07cb9fb..26a0a48e3d2 100644 --- a/packages/web-components/fast-foundation/src/tabs/tabs.ts +++ b/packages/web-components/fast-foundation/src/tabs/tabs.ts @@ -130,8 +130,12 @@ export class FASTTabs extends FASTElement { return el.getAttribute("aria-disabled") === "true"; }; + private isHiddenElement = (el: Element): el is HTMLElement => { + return el.hasAttribute("hidden"); + }; + private isFocusableElement = (el: Element): el is HTMLElement => { - return !this.isDisabledElement(el); + return !this.isDisabledElement(el) && !this.isHiddenElement(el); }; private getActiveIndex(): number { @@ -272,7 +276,7 @@ export class FASTTabs extends FASTElement { * This method allows the active index to be adjusted by numerical increments */ public adjust(adjustment: number): void { - const focusableTabs = this.tabs.filter(t => !this.isDisabledElement(t)); + const focusableTabs = this.tabs.filter(t => this.isFocusableElement(t)); const currentActiveTabIndex = focusableTabs.indexOf(this.activetab); const nextTabIndex = limit(