Skip to content

Commit 0651aa2

Browse files
authored
Merge pull request #36 from github/keyboard
Fix focus and role problems
2 parents d89b169 + cde6b66 commit 0651aa2

File tree

3 files changed

+101
-29
lines changed

3 files changed

+101
-29
lines changed

examples/index.html

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,60 @@
33
<head>
44
<meta charset="utf-8">
55
<title>details-menu demo</title>
6+
<style>
7+
details-menu {
8+
background: white;
9+
border: 1px solid;
10+
display: block;
11+
padding: 4px;
12+
width: 200px;
13+
}
14+
button, label[tabindex] {
15+
font-family: inherit;
16+
font-size: inherit;
17+
display: block;
18+
background: none;
19+
border: 0;
20+
width: 100%;
21+
text-align: left;
22+
padding: 0;
23+
}
24+
</style>
625
</head>
726
<body>
827
<details>
928
<summary>Best robot: <span data-menu-button>Unknown</span></summary>
1029
<details-menu>
11-
<ul>
12-
<li><button type="button" role="menuitem" data-menu-button-text>Hubot</button></li>
13-
<li><button type="button" role="menuitem" data-menu-button-text>Bender</button></li>
14-
<li><button type="button" role="menuitem" data-menu-button-text>BB-8</button></li>
15-
</ul>
30+
<button type="button" role="menuitem" data-menu-button-text>Hubot</button>
31+
<button type="button" role="menuitem" data-menu-button-text>Bender</button>
32+
<button type="button" role="menuitem" data-menu-button-text>BB-8</button>
1633
</details-menu>
1734
</details>
1835

1936
<details>
2037
<summary>Best robot: <span data-menu-button>Unknown</span></summary>
2138
<details-menu>
22-
<ul>
23-
<li><label tabindex="0" role="menuitemradio" data-menu-button-text><input type="radio" name="robot"> Hubot</label></li>
24-
<li><label tabindex="0" role="menuitemradio" data-menu-button-text><input type="radio" name="robot"> Bender</label></li>
25-
<li><label tabindex="0" role="menuitemradio" data-menu-button-text><input type="radio" name="robot"> BB-8</label></li>
26-
</ul>
39+
<label tabindex="0" role="menuitemradio" data-menu-button-text><input type="radio" name="robot"> Hubot</label>
40+
<label tabindex="0" role="menuitemradio" data-menu-button-text><input type="radio" name="robot"> Bender</label>
41+
<label tabindex="0" role="menuitemradio" data-menu-button-text><input type="radio" name="robot"> BB-8</label>
2742
</details-menu>
2843
</details>
2944

3045
<details>
3146
<summary>Favorite robots</summary>
3247
<details-menu>
33-
<ul>
34-
<li><label tabindex="0" role="menuitemcheckbox"><input type="checkbox" name="robot"> Hubot</label></li>
35-
<li><label tabindex="0" role="menuitemcheckbox"><input type="checkbox" name="robot"> Bender</label></li>
36-
<li><label tabindex="0" role="menuitemcheckbox"><input type="checkbox" name="robot"> BB-8</label></li>
37-
</ul>
48+
<label tabindex="0" role="menuitemcheckbox"><input type="checkbox" name="robot"> Hubot</label>
49+
<label tabindex="0" role="menuitemcheckbox"><input type="checkbox" name="robot"> Bender</label>
50+
<label tabindex="0" role="menuitemcheckbox"><input type="checkbox" name="robot"> BB-8</label>
3851
</details-menu>
3952
</details>
4053

4154
<details>
4255
<summary data-menu-button>Favorite robots</summary>
4356
<details-menu>
44-
<ul>
45-
<li><button type="submit" name="robot" value="Hubot" role="menuitemradio" data-menu-button-text>Hubot</button></li>
46-
<li><button type="submit" name="robot" value="Bender" role="menuitemradio" data-menu-button-text>Bender</button></li>
47-
<li><button type="submit" name="robot" value="BB-8" role="menuitemradio" data-menu-button-text>BB-8</button></li>
48-
</ul>
57+
<button type="submit" name="robot" value="Hubot" role="menuitemradio" data-menu-button-text>Hubot</button>
58+
<button type="submit" name="robot" value="Bender" role="menuitemradio" data-menu-button-text>Bender</button>
59+
<button type="submit" name="robot" value="BB-8" role="menuitemradio" data-menu-button-text>BB-8</button>
4960
</details-menu>
5061
</details>
5162

index.js

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@ class DetailsMenuElement extends HTMLElement {
2626
}
2727

2828
connectedCallback() {
29-
this.setAttribute('role', 'menu')
29+
if (!this.hasAttribute('role')) this.setAttribute('role', 'menu')
3030

3131
const details = this.parentElement
3232
if (!details) return
3333

3434
const summary = details.querySelector('summary')
35-
if (summary) summary.setAttribute('aria-haspopup', 'menu')
35+
if (summary) {
36+
summary.setAttribute('aria-haspopup', 'menu')
37+
if (!summary.hasAttribute('role')) summary.setAttribute('role', 'button')
38+
}
3639

3740
details.addEventListener('click', shouldCommit)
3841
details.addEventListener('change', shouldCommit)
@@ -96,10 +99,9 @@ function focusOnOpen(details: Element) {
9699
const onmousedown = () => (isMouse = true)
97100
const onkeydown = () => (isMouse = false)
98101
const ontoggle = () => {
99-
autofocus(details)
100-
if (details.hasAttribute('open') && !isMouse) {
101-
focusFirstItem(details)
102-
}
102+
if (!details.hasAttribute('open')) return
103+
if (autofocus(details)) return
104+
if (!isMouse) focusFirstItem(details)
103105
}
104106

105107
details.addEventListener('mousedown', onmousedown)
@@ -128,12 +130,14 @@ function closeCurrentMenu(event: Event) {
128130
}
129131
}
130132

131-
function autofocus(details: Element) {
132-
if (!details.hasAttribute('open')) return
133-
133+
function autofocus(details: Element): boolean {
134+
if (!details.hasAttribute('open')) return false
134135
const input = details.querySelector('[autofocus]')
135136
if (input) {
136137
input.focus()
138+
return true
139+
} else {
140+
return false
137141
}
138142
}
139143

test/test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ describe('details-menu element', function() {
3434
document.body.innerHTML = ''
3535
})
3636

37+
it('has default attributes set', function() {
38+
const details = document.querySelector('details')
39+
const summary = details.querySelector('summary')
40+
const menu = details.querySelector('details-menu')
41+
assert.equal(summary.getAttribute('role'), 'button')
42+
assert.equal(menu.getAttribute('role'), 'menu')
43+
})
44+
3745
it('opens and does not focus an item on mouse click', function() {
3846
const details = document.querySelector('details')
3947
const summary = details.querySelector('summary')
@@ -546,6 +554,55 @@ describe('details-menu element', function() {
546554
})
547555
})
548556

557+
describe('with input[autofocus]', function() {
558+
beforeEach(function() {
559+
const container = document.createElement('div')
560+
container.innerHTML = `
561+
<details>
562+
<summary>Menu 1</summary>
563+
<details-menu role="none">
564+
<input autofocus>
565+
<div role="menu">
566+
<button role="menuitem">First item</button>
567+
</div>
568+
</details-menu>
569+
</details>
570+
`
571+
document.body.append(container)
572+
})
573+
574+
afterEach(function() {
575+
document.body.innerHTML = ''
576+
})
577+
578+
it('autofocuses on input on mouse click', function() {
579+
const details = document.querySelector('details')
580+
const summary = details.querySelector('summary')
581+
const menu = details.querySelector('details-menu')
582+
const input = details.querySelector('input')
583+
584+
summary.focus()
585+
details.open = true
586+
summary.dispatchEvent(new MouseEvent('mousedown', {bubbles: true}))
587+
details.dispatchEvent(new CustomEvent('toggle'))
588+
assert.equal(menu.getAttribute('role'), 'none')
589+
assert.equal(input, document.activeElement, 'mouse toggle open leaves summary focused')
590+
})
591+
592+
it('autofocuses on input on keyboard activation', function() {
593+
const details = document.querySelector('details')
594+
const summary = details.querySelector('summary')
595+
const input = details.querySelector('input')
596+
597+
summary.focus()
598+
details.open = true
599+
summary.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter', bubbles: true}))
600+
details.dispatchEvent(new CustomEvent('toggle'))
601+
602+
assert.equal(input, document.activeElement, 'toggle open focuses on [autofocus]')
603+
})
604+
})
605+
549606
describe('closing the menu', function() {
550607
beforeEach(function() {
551608
const container = document.createElement('div')

0 commit comments

Comments
 (0)