From f67eedf8439591531273ad3165d513cdeed0206a Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Sun, 15 Dec 2019 14:24:56 +0000
Subject: [PATCH] Fix keyboard handling including scroll into view, add aria
roles
---
src/components/views/elements/Dropdown.js | 95 ++++++++++++++++-------
1 file changed, 68 insertions(+), 27 deletions(-)
diff --git a/src/components/views/elements/Dropdown.js b/src/components/views/elements/Dropdown.js
index 4c5e14b3ba..65be56f1f5 100644
--- a/src/components/views/elements/Dropdown.js
+++ b/src/components/views/elements/Dropdown.js
@@ -20,6 +20,7 @@ import PropTypes from 'prop-types';
import classnames from 'classnames';
import AccessibleButton from './AccessibleButton';
import { _t } from '../../../languageHandler';
+import {Key} from "../../../Keyboard";
class MenuOption extends React.Component {
constructor(props) {
@@ -48,9 +49,13 @@ class MenuOption extends React.Component {
mx_Dropdown_option_highlight: this.props.highlighted,
});
- return
{ this.props.children }
;
@@ -66,6 +71,7 @@ MenuOption.propTypes = {
dropdownKey: PropTypes.string,
onClick: PropTypes.func.isRequired,
onMouseEnter: PropTypes.func.isRequired,
+ inputRef: PropTypes.any,
};
/*
@@ -177,32 +183,43 @@ export default class Dropdown extends React.Component {
}
_onInputKeyPress(e) {
- // This needs to be on the keypress event because otherwise
- // it can't cancel the form submission
- if (e.key == 'Enter') {
- this.setState({
- expanded: false,
- });
- this.props.onOptionChange(this.state.highlightedOption);
+ // This needs to be on the keypress event because otherwise it can't cancel the form submission
+ if (e.key === Key.ENTER) {
e.preventDefault();
}
}
_onInputKeyUp(e) {
- // These keys don't generate keypress events and so needs to
- // be on keyup
- if (e.key == 'Escape') {
- this.setState({
- expanded: false,
- });
- } else if (e.key == 'ArrowDown') {
- this.setState({
- highlightedOption: this._nextOption(this.state.highlightedOption),
- });
- } else if (e.key == 'ArrowUp') {
- this.setState({
- highlightedOption: this._prevOption(this.state.highlightedOption),
- });
+ // These keys don't generate keypress events and so needs to be on keyup
+ switch (e.key) {
+ case Key.ENTER:
+ this.props.onOptionChange(this.state.highlightedOption);
+ // fallthrough
+ case Key.ESCAPE:
+ this.setState({
+ expanded: false,
+ });
+ break;
+ case Key.ARROW_DOWN:
+ this.setState({
+ highlightedOption: this._nextOption(this.state.highlightedOption),
+ });
+ break;
+ case Key.ARROW_UP:
+ this.setState({
+ highlightedOption: this._prevOption(this.state.highlightedOption),
+ });
+ break;
+ case Key.HOME:
+ this.setState({
+ highlightedOption: this._firstOption(),
+ });
+ break;
+ case Key.END:
+ this.setState({
+ highlightedOption: this._lastOption(),
+ });
+ break;
}
}
@@ -250,13 +267,36 @@ export default class Dropdown extends React.Component {
return keys[(index - 1) % keys.length];
}
+ _firstOption() {
+ const keys = Object.keys(this.childrenByKey);
+ return keys[0];
+ }
+
+ _lastOption() {
+ const keys = Object.keys(this.childrenByKey);
+ return keys[keys.length - 1];
+ }
+
+ _scrollIntoView(node) {
+ if (node) {
+ node.scrollIntoView({
+ block: "nearest",
+ behavior: "auto",
+ });
+ }
+ }
+
_getMenuOptions() {
const options = React.Children.map(this.props.children, (child) => {
+ const highlighted = this.state.highlightedOption === child.key;
return (
-
{ child }
@@ -280,13 +320,14 @@ export default class Dropdown extends React.Component {
if (this.state.expanded) {
if (this.props.searchEnabled) {
currentValue = ;
}
- menu =
+ menu =
{ this._getMenuOptions() }
;
}
@@ -313,7 +354,7 @@ export default class Dropdown extends React.Component {
return
{ currentValue }
-
+
{ menu }
;