From: Mark Otto Date: Sat, 27 Jun 2026 03:15:34 +0000 (-0700) Subject: Tab: ignore modifier+arrow shortcuts; docs: menu Escape behavior (#42553) X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a6bf087a9942f5034b1214db424e4bc020761d05;p=thirdparty%2Fbootstrap.git Tab: ignore modifier+arrow shortcuts; docs: menu Escape behavior (#42553) * Tab: don't hijack modifier+arrow keyboard shortcuts _keydown preventDefault'd arrow keys regardless of modifiers, so Alt+Left/Right (browser history nav) and similar shortcuts were swallowed inside a tablist. Ignore the event when alt/ctrl/meta is held. Fixes #38565. * Docs: clarify Escape behavior in menu auto-close section Esc closes the menu while focus is within it (regardless of autoClose) and returns focus to the toggle. Fixes #38035. * Bump bundlewatch size thresholds --- diff --git a/js/src/tab.js b/js/src/tab.js index 70375ba25d..17f051345b 100644 --- a/js/src/tab.js +++ b/js/src/tab.js @@ -155,6 +155,12 @@ class Tab extends BaseComponent { return } + // Don't hijack modifier+arrow shortcuts (e.g. Alt+Left/Right for browser + // history navigation); only the bare keys drive tablist navigation. + if (event.altKey || event.ctrlKey || event.metaKey) { + return + } + event.stopPropagation()// stopPropagation/preventDefault both added to support up/down keys without scrolling the page event.preventDefault() diff --git a/js/tests/unit/tab.spec.js b/js/tests/unit/tab.spec.js index a3cd9b8e4c..d523c0dc34 100644 --- a/js/tests/unit/tab.spec.js +++ b/js/tests/unit/tab.spec.js @@ -547,6 +547,30 @@ describe('Tab', () => { expect(spyPrevent).not.toHaveBeenCalled() }) + it('if arrow key is pressed with a modifier (e.g. Alt+Left), ignore it', () => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const tabEl = fixtureEl.querySelector('.nav-link') + const tab = new Tab(tabEl) + + const keydown = createEvent('keydown') + keydown.key = 'ArrowLeft' + keydown.altKey = true + const spyStop = spyOn(Event.prototype, 'stopPropagation').and.callThrough() + const spyPrevent = spyOn(Event.prototype, 'preventDefault').and.callThrough() + const spyGet = spyOn(tab, '_getChildren') + + tabEl.dispatchEvent(keydown) + + expect(spyGet).not.toHaveBeenCalled() + expect(spyStop).not.toHaveBeenCalled() + expect(spyPrevent).not.toHaveBeenCalled() + }) + it('if keydown event is right/down arrow, handle it', () => { fixtureEl.innerHTML = [ '