diff --git a/client/src/includes/tabs.js b/client/src/includes/tabs.js index b621caccb8..2631c75c9d 100644 --- a/client/src/includes/tabs.js +++ b/client/src/includes/tabs.js @@ -270,10 +270,10 @@ class Tabs { selectTabByURLHash() { if (window.location.hash) { - const cleanedHash = window.location.hash.replace(/[^\w\-#]/g, ''); + const anchorId = window.location.hash.slice(1); // Support linking straight to a tab, or to an element within a tab. const tabID = document - .querySelector(cleanedHash) + .getElementById(anchorId) ?.closest('[role="tabpanel"]') ?.getAttribute('aria-labelledby'); const tab = document.getElementById(tabID); diff --git a/client/src/includes/tabs.test.js b/client/src/includes/tabs.test.js new file mode 100644 index 0000000000..1644af84a1 --- /dev/null +++ b/client/src/includes/tabs.test.js @@ -0,0 +1,158 @@ +import { validateCreationForm } from './chooserModal'; +import { initTabs } from './tabs'; + +describe('tabs', () => { + beforeEach(() => { + document.body.innerHTML = /* html */ ` +
+
+ + Content + + + + Promote + +
+ +
+ + + +
+
+ `; + }); + + describe('initialize selected tab', () => { + afterEach(() => { + window.location.hash = ''; + }); + + it('should select the first tab by default', async () => { + initTabs(); + await Promise.resolve(); + + const contentTab = document.getElementById('tab-content'); + const promoteTab = document.getElementById('tab-promote'); + const contentTabLabel = document.getElementById('tab-label-content'); + const promoteTabLabel = document.getElementById('tab-label-promote'); + + expect(contentTab.hasAttribute('hidden')).toBe(false); + expect(promoteTab.hasAttribute('hidden')).toBe(true); + + expect(contentTabLabel.getAttribute('aria-selected')).toEqual('true'); + expect(promoteTabLabel.getAttribute('aria-selected')).toEqual('false'); + }); + + it('should select the correct tab where the anchored element lives', async () => { + window.location.hash = '#id_search_description'; + initTabs(); + await Promise.resolve(); + + const contentTab = document.getElementById('tab-content'); + const promoteTab = document.getElementById('tab-promote'); + const contentTabLabel = document.getElementById('tab-label-content'); + const promoteTabLabel = document.getElementById('tab-label-promote'); + + expect(contentTab.hasAttribute('hidden')).toBe(true); + expect(promoteTab.hasAttribute('hidden')).toBe(false); + + expect(contentTabLabel.getAttribute('aria-selected')).toEqual('false'); + expect(promoteTabLabel.getAttribute('aria-selected')).toEqual('true'); + }); + + it('should not throw an error if the URL hash begins with a number', async () => { + window.location.hash = '#123abcd'; + initTabs(); + await Promise.resolve(); + + const contentTab = document.getElementById('tab-content'); + const promoteTab = document.getElementById('tab-promote'); + const contentTabLabel = document.getElementById('tab-label-content'); + const promoteTabLabel = document.getElementById('tab-label-promote'); + + expect(contentTab.hasAttribute('hidden')).toBe(false); + expect(promoteTab.hasAttribute('hidden')).toBe(true); + + expect(contentTabLabel.getAttribute('aria-selected')).toEqual('true'); + expect(promoteTabLabel.getAttribute('aria-selected')).toEqual('false'); + }); + }); + + describe('switching tabs', () => { + it('should allow switching to a different tab by clicking on the label', async () => { + initTabs(); + await Promise.resolve(); + + const contentTab = document.getElementById('tab-content'); + const promoteTab = document.getElementById('tab-promote'); + const contentTabLabel = document.getElementById('tab-label-content'); + const promoteTabLabel = document.getElementById('tab-label-promote'); + + promoteTabLabel.click(); + + expect(contentTab.hasAttribute('hidden')).toBe(true); + expect(promoteTab.hasAttribute('hidden')).toBe(false); + + expect(contentTabLabel.getAttribute('aria-selected')).toEqual('false'); + expect(promoteTabLabel.getAttribute('aria-selected')).toEqual('true'); + }); + }); +});