Fix and refactor keyboard navigation in dropdown menus (#13528)

Fixes #13527

- Fixes caught keyboard events being needlessly propagated
- Let up/down arrows wrap around like the tab key does
- Refactor common code
pull/13533/head
ThibG 2020-04-21 15:13:26 +02:00 committed by GitHub
parent ff32a25ee3
commit 80182eda62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 45 deletions

View File

@ -68,20 +68,14 @@ class DropdownMenu extends React.PureComponent {
handleKeyDown = e => { handleKeyDown = e => {
const items = Array.from(this.node.getElementsByTagName('a')); const items = Array.from(this.node.getElementsByTagName('a'));
const index = items.indexOf(document.activeElement); const index = items.indexOf(document.activeElement);
let element; let element = null;
switch(e.key) { switch(e.key) {
case 'ArrowDown': case 'ArrowDown':
element = items[index+1]; element = items[index+1] || items[0];
if (element) {
element.focus();
}
break; break;
case 'ArrowUp': case 'ArrowUp':
element = items[index-1]; element = items[index-1] || items[items.length-1];
if (element) {
element.focus();
}
break; break;
case 'Tab': case 'Tab':
if (e.shiftKey) { if (e.shiftKey) {
@ -89,28 +83,23 @@ class DropdownMenu extends React.PureComponent {
} else { } else {
element = items[index+1] || items[0]; element = items[index+1] || items[0];
} }
if (element) {
element.focus();
e.preventDefault();
e.stopPropagation();
}
break; break;
case 'Home': case 'Home':
element = items[0]; element = items[0];
if (element) {
element.focus();
}
break; break;
case 'End': case 'End':
element = items[items.length-1]; element = items[items.length-1];
if (element) {
element.focus();
}
break; break;
case 'Escape': case 'Escape':
this.props.onClose(); this.props.onClose();
break; break;
} }
if (element) {
element.focus();
e.preventDefault();
e.stopPropagation();
}
} }
handleItemKeyPress = e => { handleItemKeyPress = e => {

View File

@ -50,7 +50,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
const index = items.findIndex(item => { const index = items.findIndex(item => {
return (item.value === value); return (item.value === value);
}); });
let element; let element = null;
switch(e.key) { switch(e.key) {
case 'Escape': case 'Escape':
@ -60,18 +60,10 @@ class PrivacyDropdownMenu extends React.PureComponent {
this.handleClick(e); this.handleClick(e);
break; break;
case 'ArrowDown': case 'ArrowDown':
element = this.node.childNodes[index + 1]; element = this.node.childNodes[index + 1] || this.node.firstChild;
if (element) {
element.focus();
this.props.onChange(element.getAttribute('data-index'));
}
break; break;
case 'ArrowUp': case 'ArrowUp':
element = this.node.childNodes[index - 1]; element = this.node.childNodes[index - 1] || this.node.lastChild;
if (element) {
element.focus();
this.props.onChange(element.getAttribute('data-index'));
}
break; break;
case 'Tab': case 'Tab':
if (e.shiftKey) { if (e.shiftKey) {
@ -79,28 +71,21 @@ class PrivacyDropdownMenu extends React.PureComponent {
} else { } else {
element = this.node.childNodes[index + 1] || this.node.firstChild; element = this.node.childNodes[index + 1] || this.node.firstChild;
} }
break;
case 'Home':
element = this.node.firstChild;
break;
case 'End':
element = this.node.lastChild;
break;
}
if (element) { if (element) {
element.focus(); element.focus();
this.props.onChange(element.getAttribute('data-index')); this.props.onChange(element.getAttribute('data-index'));
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
} }
break;
case 'Home':
element = this.node.firstChild;
if (element) {
element.focus();
this.props.onChange(element.getAttribute('data-index'));
}
break;
case 'End':
element = this.node.lastChild;
if (element) {
element.focus();
this.props.onChange(element.getAttribute('data-index'));
}
break;
}
} }
handleClick = e => { handleClick = e => {