Skip to content

Commit

Permalink
[fixed] Dropdown focus behavior on click
Browse files Browse the repository at this point in the history
By default, only focus the first dropdown menu item after keydown.
  • Loading branch information
taion committed Oct 3, 2015
1 parent 1b2b775 commit b7853bb
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 22 deletions.
67 changes: 46 additions & 21 deletions src/Dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ class Dropdown extends React.Component {
}];

this.state = {};

this.lastOpenEventType = null;
this.isKeyboardClick = false;
}

componentDidMount() {
let menu = this.refs.menu;
if (this.props.open && menu.focusNext) {
menu.focusNext();
}
this.focusNextOnOpen();
}

componentWillUpdate(nextProps) {
Expand All @@ -65,10 +65,8 @@ class Dropdown extends React.Component {
}

componentDidUpdate(prevProps) {
let menu = this.refs.menu;

if (this.props.open && !prevProps.open && menu.focusNext) {
menu.focusNext();
if (this.props.open && !prevProps.open) {
this.focusNextOnOpen();
}

if (!this.props.open && prevProps.open) {
Expand Down Expand Up @@ -105,9 +103,13 @@ class Dropdown extends React.Component {
);
}

toggleOpen() {
toggleOpen(eventType = null) {
let open = !this.props.open;

if (open) {
this.lastOpenEventType = eventType;
}

if (this.props.onToggle) {
this.props.onToggle(open);
}
Expand All @@ -118,29 +120,32 @@ class Dropdown extends React.Component {
return;
}

this.toggleOpen();
this.toggleOpen(this.isKeyboardClick ? 'keydown' : 'click');
this.isKeyboardClick = false;
}

handleKeyDown(event) {
let focusNext = () => {
if (this.refs.menu.focusNext) {
this.refs.menu.focusNext();
}
};
if (this.props.disabled) {
return;
}

switch (event.keyCode) {
case keycode.codes.down:
if (!this.props.open) {
this.toggleOpen();
} else {
focusNext();
this.toggleOpen('keydown');
} else if (this.refs.menu.focusNext) {
this.refs.menu.focusNext();
}
event.preventDefault();
break;
case keycode.codes.esc:
case keycode.codes.tab:
this.handleClose(event);
break;
case keycode.codes.space:
case keycode.codes.enter:
this.isKeyboardClick = true;
break;
default:
}
}
Expand All @@ -153,6 +158,20 @@ class Dropdown extends React.Component {
this.toggleOpen();
}

focusNextOnOpen() {
const {menu} = this.refs;
if (!menu.focusNext) {
return;
}

if (
this.lastOpenEventType === 'keydown' ||
this.props.alwaysFocusNextOnOpen
) {
menu.focusNext();
}
}

focus() {
let toggle = React.findDOMNode(this.refs[TOGGLE_REF]);

Expand Down Expand Up @@ -232,7 +251,8 @@ Dropdown.TOGGLE_ROLE = TOGGLE_ROLE;
Dropdown.MENU_ROLE = MENU_ROLE;

Dropdown.defaultProps = {
componentClass: ButtonGroup
componentClass: ButtonGroup,
alwaysFocusNextOnOpen: false
};

Dropdown.propTypes = {
Expand Down Expand Up @@ -270,7 +290,7 @@ Dropdown.propTypes = {
disabled: React.PropTypes.bool,

/**
* Align the menu to the right side of the Dropdown toggle
* Align the menu to the right side of the Dropdown toggle
*/
pullRight: React.PropTypes.bool,

Expand Down Expand Up @@ -304,7 +324,12 @@ Dropdown.propTypes = {
* function(Object event, Any eventKey)
* ```
*/
onSelect: React.PropTypes.func
onSelect: React.PropTypes.func,

/**
* Focus first menu item on menu open on all events, not just keydown events.
*/
alwaysFocusNextOnOpen: React.PropTypes.bool
};

Dropdown = uncontrollable(Dropdown, { open: 'onToggle' });
Expand Down
2 changes: 1 addition & 1 deletion test/DropdownSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ describe('Dropdown', () => {
// I am fairly confident that the failure is due to a test specific conflict and not an actual bug.
it('when open and the key "esc" is pressed the menu is closed and focus is returned to the button', () => {
const instance = React.render(
<Dropdown defaultOpen id='test-id'>
<Dropdown defaultOpen alwaysFocusNextOnOpen id='test-id'>
{dropdownChildren}
</Dropdown>
, focusableContainer);
Expand Down

0 comments on commit b7853bb

Please sign in to comment.