Skip to content
Extraits de code Groupes Projets
Valider 396b8cdd rédigé par ThibG's avatar ThibG Validation de Eugen Rochko
Parcourir les fichiers

Improve focus handling with dropdown menus (#11511)

- Focus first item when activated via keyboard
- When the dropdown menu closes, give back the focus to
  the actual element which was focused prior to opening the menu
parent ac33f1ae
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
...@@ -45,7 +45,6 @@ class DropdownMenu extends React.PureComponent { ...@@ -45,7 +45,6 @@ class DropdownMenu extends React.PureComponent {
document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('click', this.handleDocumentClick, false);
document.addEventListener('keydown', this.handleKeyDown, false); document.addEventListener('keydown', this.handleKeyDown, false);
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
this.activeElement = document.activeElement;
if (this.focusedItem && this.props.openedViaKeyboard) { if (this.focusedItem && this.props.openedViaKeyboard) {
this.focusedItem.focus(); this.focusedItem.focus();
} }
...@@ -56,9 +55,6 @@ class DropdownMenu extends React.PureComponent { ...@@ -56,9 +55,6 @@ class DropdownMenu extends React.PureComponent {
document.removeEventListener('click', this.handleDocumentClick, false); document.removeEventListener('click', this.handleDocumentClick, false);
document.removeEventListener('keydown', this.handleKeyDown, false); document.removeEventListener('keydown', this.handleKeyDown, false);
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
if (this.activeElement) {
this.activeElement.focus();
}
} }
setRef = c => { setRef = c => {
...@@ -117,7 +113,7 @@ class DropdownMenu extends React.PureComponent { ...@@ -117,7 +113,7 @@ class DropdownMenu extends React.PureComponent {
} }
} }
handleItemKeyUp = e => { handleItemKeyPress = e => {
if (e.key === 'Enter' || e.key === ' ') { if (e.key === 'Enter' || e.key === ' ') {
this.handleClick(e); this.handleClick(e);
} }
...@@ -147,7 +143,7 @@ class DropdownMenu extends React.PureComponent { ...@@ -147,7 +143,7 @@ class DropdownMenu extends React.PureComponent {
return ( return (
<li className='dropdown-menu__item' key={`${text}-${i}`}> <li className='dropdown-menu__item' key={`${text}-${i}`}>
<a href={href} target={target} data-method={method} rel='noopener' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyUp={this.handleItemKeyUp} data-index={i}> <a href={href} target={target} data-method={method} rel='noopener' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
{text} {text}
</a> </a>
</li> </li>
...@@ -214,15 +210,44 @@ export default class Dropdown extends React.PureComponent { ...@@ -214,15 +210,44 @@ export default class Dropdown extends React.PureComponent {
} else { } else {
const { top } = target.getBoundingClientRect(); const { top } = target.getBoundingClientRect();
const placement = top * 2 < innerHeight ? 'bottom' : 'top'; const placement = top * 2 < innerHeight ? 'bottom' : 'top';
this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click'); this.props.onOpen(this.state.id, this.handleItemClick, placement, type !== 'click');
} }
} }
handleClose = () => { handleClose = () => {
if (this.activeElement) {
this.activeElement.focus();
this.activeElement = null;
}
this.props.onClose(this.state.id); this.props.onClose(this.state.id);
} }
handleMouseDown = () => {
if (!this.state.open) {
this.activeElement = document.activeElement;
}
}
handleButtonKeyDown = (e) => {
switch(e.key) {
case ' ':
case 'Enter':
this.handleMouseDown();
break;
}
}
handleKeyPress = (e) => {
switch(e.key) {
case ' ':
case 'Enter':
this.handleClick(e);
e.stopPropagation();
e.preventDefault();
break;
}
}
handleItemClick = e => { handleItemClick = e => {
const i = Number(e.currentTarget.getAttribute('data-index')); const i = Number(e.currentTarget.getAttribute('data-index'));
const { action, to } = this.props.items[i]; const { action, to } = this.props.items[i];
...@@ -266,6 +291,9 @@ export default class Dropdown extends React.PureComponent { ...@@ -266,6 +291,9 @@ export default class Dropdown extends React.PureComponent {
size={size} size={size}
ref={this.setTargetRef} ref={this.setTargetRef}
onClick={this.handleClick} onClick={this.handleClick}
onMouseDown={this.handleMouseDown}
onKeyDown={this.handleButtonKeyDown}
onKeyPress={this.handleKeyPress}
/> />
<Overlay show={open} placement={dropdownPlacement} target={this.findTarget}> <Overlay show={open} placement={dropdownPlacement} target={this.findTarget}>
......
...@@ -14,6 +14,7 @@ export default class IconButton extends React.PureComponent { ...@@ -14,6 +14,7 @@ export default class IconButton extends React.PureComponent {
onClick: PropTypes.func, onClick: PropTypes.func,
onMouseDown: PropTypes.func, onMouseDown: PropTypes.func,
onKeyDown: PropTypes.func, onKeyDown: PropTypes.func,
onKeyPress: PropTypes.func,
size: PropTypes.number, size: PropTypes.number,
active: PropTypes.bool, active: PropTypes.bool,
pressed: PropTypes.bool, pressed: PropTypes.bool,
...@@ -44,6 +45,12 @@ export default class IconButton extends React.PureComponent { ...@@ -44,6 +45,12 @@ export default class IconButton extends React.PureComponent {
} }
} }
handleKeyPress = (e) => {
if (this.props.onKeyPress && !this.props.disabled) {
this.props.onKeyPress(e);
}
}
handleMouseDown = (e) => { handleMouseDown = (e) => {
if (!this.props.disabled && this.props.onMouseDown) { if (!this.props.disabled && this.props.onMouseDown) {
this.props.onMouseDown(e); this.props.onMouseDown(e);
...@@ -100,6 +107,7 @@ export default class IconButton extends React.PureComponent { ...@@ -100,6 +107,7 @@ export default class IconButton extends React.PureComponent {
onClick={this.handleClick} onClick={this.handleClick}
onMouseDown={this.handleMouseDown} onMouseDown={this.handleMouseDown}
onKeyDown={this.handleKeyDown} onKeyDown={this.handleKeyDown}
onKeyPress={this.handleKeyPress}
style={style} style={style}
tabIndex={tabIndex} tabIndex={tabIndex}
disabled={disabled} disabled={disabled}
...@@ -121,6 +129,7 @@ export default class IconButton extends React.PureComponent { ...@@ -121,6 +129,7 @@ export default class IconButton extends React.PureComponent {
onClick={this.handleClick} onClick={this.handleClick}
onMouseDown={this.handleMouseDown} onMouseDown={this.handleMouseDown}
onKeyDown={this.handleKeyDown} onKeyDown={this.handleKeyDown}
onKeyPress={this.handleKeyPress}
style={style} style={style}
tabIndex={tabIndex} tabIndex={tabIndex}
disabled={disabled} disabled={disabled}
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter