[Glitch] Improve dropdown menu keyboard navigation
Port a12f1a0baf to glitch-soc
Signed-off-by: Thibaut Girka <thib@sitedethib.com>
			
			
This commit is contained in:
		
							parent
							
								
									6afdb6c2b6
								
							
						
					
					
						commit
						fe1de4e49b
					
				
					 4 changed files with 30 additions and 21 deletions
				
			
		| 
						 | 
				
			
			@ -9,8 +9,9 @@ export function openModal(type, props) {
 | 
			
		|||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export function closeModal() {
 | 
			
		||||
export function closeModal(type) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: MODAL_CLOSE,
 | 
			
		||||
    modalType: type,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,10 @@ class DropdownMenu extends React.PureComponent {
 | 
			
		|||
    document.addEventListener('click', this.handleDocumentClick, false);
 | 
			
		||||
    document.addEventListener('keydown', this.handleKeyDown, false);
 | 
			
		||||
    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
 | 
			
		||||
    if (this.focusedItem && this.props.openedViaKeyboard) this.focusedItem.focus();
 | 
			
		||||
    this.activeElement = document.activeElement;
 | 
			
		||||
    if (this.focusedItem && this.props.openedViaKeyboard) {
 | 
			
		||||
      this.focusedItem.focus();
 | 
			
		||||
    }
 | 
			
		||||
    this.setState({ mounted: true });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +56,9 @@ class DropdownMenu extends React.PureComponent {
 | 
			
		|||
    document.removeEventListener('click', this.handleDocumentClick, false);
 | 
			
		||||
    document.removeEventListener('keydown', this.handleKeyDown, false);
 | 
			
		||||
    document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
 | 
			
		||||
    if (this.activeElement) {
 | 
			
		||||
      this.activeElement.focus();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setRef = c => {
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +87,18 @@ class DropdownMenu extends React.PureComponent {
 | 
			
		|||
        element.focus();
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case 'Tab':
 | 
			
		||||
      if (e.shiftKey) {
 | 
			
		||||
        element = items[index-1] || items[items.length-1];
 | 
			
		||||
      } else {
 | 
			
		||||
        element = items[index+1] || items[0];
 | 
			
		||||
      }
 | 
			
		||||
      if (element) {
 | 
			
		||||
        element.focus();
 | 
			
		||||
        e.preventDefault();
 | 
			
		||||
        e.stopPropagation();
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case 'Home':
 | 
			
		||||
      element = items[0];
 | 
			
		||||
      if (element) {
 | 
			
		||||
| 
						 | 
				
			
			@ -93,11 +111,14 @@ class DropdownMenu extends React.PureComponent {
 | 
			
		|||
        element.focus();
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case 'Escape':
 | 
			
		||||
      this.props.onClose();
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleItemKeyDown = e => {
 | 
			
		||||
    if (e.key === 'Enter') {
 | 
			
		||||
  handleItemKeyUp = e => {
 | 
			
		||||
    if (e.key === 'Enter' || e.key === ' ') {
 | 
			
		||||
      this.handleClick(e);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -126,7 +147,7 @@ class DropdownMenu extends React.PureComponent {
 | 
			
		|||
 | 
			
		||||
    return (
 | 
			
		||||
      <li className='dropdown-menu__item' key={`${text}-${i}`}>
 | 
			
		||||
        <a href={href} target='_blank' rel='noopener' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyDown={this.handleItemKeyDown} data-index={i}>
 | 
			
		||||
        <a href={href} target='_blank' rel='noopener' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyUp={this.handleItemKeyUp} data-index={i}>
 | 
			
		||||
          {text}
 | 
			
		||||
        </a>
 | 
			
		||||
      </li>
 | 
			
		||||
| 
						 | 
				
			
			@ -202,19 +223,6 @@ export default class Dropdown extends React.PureComponent {
 | 
			
		|||
    this.props.onClose(this.state.id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleKeyDown = e => {
 | 
			
		||||
    switch(e.key) {
 | 
			
		||||
    case ' ':
 | 
			
		||||
    case 'Enter':
 | 
			
		||||
      this.handleClick(e);
 | 
			
		||||
      e.preventDefault();
 | 
			
		||||
      break;
 | 
			
		||||
    case 'Escape':
 | 
			
		||||
      this.handleClose();
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleItemClick = (i, e) => {
 | 
			
		||||
    const { action, to } = this.props.items[i];
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +256,7 @@ export default class Dropdown extends React.PureComponent {
 | 
			
		|||
    const open = this.state.id === openDropdownId;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div onKeyDown={this.handleKeyDown}>
 | 
			
		||||
      <div>
 | 
			
		||||
        <IconButton
 | 
			
		||||
          icon={icon}
 | 
			
		||||
          title={ariaLabel}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ const mapDispatchToProps = (dispatch, { status, items }) => ({
 | 
			
		|||
    }) : openDropdownMenu(id, dropdownPlacement, keyboard));
 | 
			
		||||
  },
 | 
			
		||||
  onClose(id) {
 | 
			
		||||
    dispatch(closeModal());
 | 
			
		||||
    dispatch(closeModal('ACTIONS'));
 | 
			
		||||
    dispatch(closeDropdownMenu(id));
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ export default function modal(state = initialState, action) {
 | 
			
		|||
  case MODAL_OPEN:
 | 
			
		||||
    return { modalType: action.modalType, modalProps: action.modalProps };
 | 
			
		||||
  case MODAL_CLOSE:
 | 
			
		||||
    return initialState;
 | 
			
		||||
    return (action.modalType === undefined || action.modalType === state.modalType) ? initialState : state;
 | 
			
		||||
  default:
 | 
			
		||||
    return state;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue