diff --git a/examples/index.html b/examples/index.html index 0bc36d6..48e7ec3 100644 --- a/examples/index.html +++ b/examples/index.html @@ -3,49 +3,60 @@ details-menu demo +
Best robot: Unknown - + + +
Best robot: Unknown - + + +
Favorite robots - + + +
Favorite robots - + + +
diff --git a/index.js b/index.js index cdc2563..9a550ca 100644 --- a/index.js +++ b/index.js @@ -26,13 +26,16 @@ class DetailsMenuElement extends HTMLElement { } connectedCallback() { - this.setAttribute('role', 'menu') + if (!this.hasAttribute('role')) this.setAttribute('role', 'menu') const details = this.parentElement if (!details) return const summary = details.querySelector('summary') - if (summary) summary.setAttribute('aria-haspopup', 'menu') + if (summary) { + summary.setAttribute('aria-haspopup', 'menu') + if (!summary.hasAttribute('role')) summary.setAttribute('role', 'button') + } details.addEventListener('click', shouldCommit) details.addEventListener('change', shouldCommit) @@ -96,10 +99,9 @@ function focusOnOpen(details: Element) { const onmousedown = () => (isMouse = true) const onkeydown = () => (isMouse = false) const ontoggle = () => { - autofocus(details) - if (details.hasAttribute('open') && !isMouse) { - focusFirstItem(details) - } + if (!details.hasAttribute('open')) return + if (autofocus(details)) return + if (!isMouse) focusFirstItem(details) } details.addEventListener('mousedown', onmousedown) @@ -128,12 +130,14 @@ function closeCurrentMenu(event: Event) { } } -function autofocus(details: Element) { - if (!details.hasAttribute('open')) return - +function autofocus(details: Element): boolean { + if (!details.hasAttribute('open')) return false const input = details.querySelector('[autofocus]') if (input) { input.focus() + return true + } else { + return false } } diff --git a/test/test.js b/test/test.js index 72a019f..bd4d4ab 100644 --- a/test/test.js +++ b/test/test.js @@ -34,6 +34,14 @@ describe('details-menu element', function() { document.body.innerHTML = '' }) + it('has default attributes set', function() { + const details = document.querySelector('details') + const summary = details.querySelector('summary') + const menu = details.querySelector('details-menu') + assert.equal(summary.getAttribute('role'), 'button') + assert.equal(menu.getAttribute('role'), 'menu') + }) + it('opens and does not focus an item on mouse click', function() { const details = document.querySelector('details') const summary = details.querySelector('summary') @@ -546,6 +554,55 @@ describe('details-menu element', function() { }) }) + describe('with input[autofocus]', function() { + beforeEach(function() { + const container = document.createElement('div') + container.innerHTML = ` +
+ Menu 1 + + +
+ +
+
+
+ ` + document.body.append(container) + }) + + afterEach(function() { + document.body.innerHTML = '' + }) + + it('autofocuses on input on mouse click', function() { + const details = document.querySelector('details') + const summary = details.querySelector('summary') + const menu = details.querySelector('details-menu') + const input = details.querySelector('input') + + summary.focus() + details.open = true + summary.dispatchEvent(new MouseEvent('mousedown', {bubbles: true})) + details.dispatchEvent(new CustomEvent('toggle')) + assert.equal(menu.getAttribute('role'), 'none') + assert.equal(input, document.activeElement, 'mouse toggle open leaves summary focused') + }) + + it('autofocuses on input on keyboard activation', function() { + const details = document.querySelector('details') + const summary = details.querySelector('summary') + const input = details.querySelector('input') + + summary.focus() + details.open = true + summary.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter', bubbles: true})) + details.dispatchEvent(new CustomEvent('toggle')) + + assert.equal(input, document.activeElement, 'toggle open focuses on [autofocus]') + }) + }) + describe('closing the menu', function() { beforeEach(function() { const container = document.createElement('div')