diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 96094a3e6577..7623e790af3e 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -207,6 +207,10 @@ class Dropdown extends BaseComponent { this._element.setAttribute('aria-expanded', 'false') Manipulator.removeDataAttribute(this._menu, 'popper') EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget) + + if (this._menu && this._menu.contains(document.activeElement)) { + this._element.focus() + } } _getConfig(config) { diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js index 63ae4bd102bc..225071760377 100644 --- a/js/tests/unit/dropdown.spec.js +++ b/js/tests/unit/dropdown.spec.js @@ -2097,6 +2097,100 @@ describe('Dropdown', () => { }) }) + it('should focus the dropdown trigger when an item is selected (and the dropdown is hidden)', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]') + const item = fixtureEl.querySelector('.dropdown-item') + + toggle.addEventListener('shown.bs.dropdown', () => { + item.focus() + item.click() + }) + + toggle.addEventListener('hidden.bs.dropdown', () => setTimeout(() => { + expect(document.activeElement).toEqual(toggle) + resolve() + })) + + toggle.click() + }) + }) + + it('should not focus the dropdown trigger when an item is selected and something else is focused in the hidden event', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '', + '' + ].join('') + + const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]') + const item = fixtureEl.querySelector('.dropdown-item') + const focusTarget = fixtureEl.querySelector('.focus-target') + + toggle.addEventListener('shown.bs.dropdown', () => { + item.focus() + item.click() + }) + + toggle.addEventListener('hidden.bs.dropdown', () => { + focusTarget.focus() + setTimeout(() => { + expect(document.activeElement).toEqual(focusTarget) + expect(document.activeElement).not.toEqual(toggle) + resolve() + }) + }) + + toggle.click() + }) + }) + + it('should not throw an error when the dropdown was disposed in the hidden event', () => { + return new Promise(resolve => { + fixtureEl.innerHTML = [ + '' + ].join('') + + const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]') + const item = fixtureEl.querySelector('.dropdown-item') + + toggle.addEventListener('shown.bs.dropdown', () => { + item.click() + }) + + toggle.addEventListener('hidden.bs.dropdown', () => { + const dropdown = Dropdown.getInstance(toggle) + dropdown.dispose() + + setTimeout(() => { + expect(dropdown._menu).toBeNull() + resolve() + }) + }) + + toggle.click() + }) + }) + it('should close dropdown (only) by clicking inside the dropdown menu when it has data-attribute `data-bs-auto-close="inside"`', () => { return new Promise(resolve => { fixtureEl.innerHTML = [