Skip to content

Commit da1d0bc

Browse files
committed
[fixed] focus returns to the toggle by default onClose
1 parent 6c554d5 commit da1d0bc

File tree

1 file changed

+34
-18
lines changed

1 file changed

+34
-18
lines changed

src/Dropdown.js

+34-18
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ import ButtonGroup from './ButtonGroup';
66
import DropdownToggle from './DropdownToggle';
77
import DropdownMenu from './DropdownMenu';
88
import CustomPropTypes from './utils/CustomPropTypes';
9+
import ValidComponentChildren from './utils/ValidComponentChildren';
910
import createChainedFunction from './utils/createChainedFunction';
1011
import find from 'lodash/collection/find';
1112
import omit from 'lodash/object/omit';
1213

14+
import activeElement from 'dom-helpers/activeElement';
15+
import contains from 'dom-helpers/query/contains';
16+
1317
const TOGGLE_REF = 'toggle-btn';
1418

1519
export const TOGGLE_ROLE = DropdownToggle.defaultProps.bsRole;
@@ -52,11 +56,30 @@ class Dropdown extends React.Component {
5256
}
5357
}
5458

55-
componentDidUpdate(prevProps, prevState) {
59+
componentWillUpdate(nextProps) {
60+
if (!nextProps.open && this.props.open) {
61+
this._focusInDropdown = contains(
62+
React.findDOMNode(this.refs.menu),
63+
activeElement(document)
64+
);
65+
}
66+
}
67+
68+
componentDidUpdate(prevProps) {
5669
let menu = this.refs.menu;
70+
5771
if (this.props.open && !prevProps.open && menu.focusNext) {
5872
menu.focusNext();
5973
}
74+
75+
if (!this.props.open && prevProps.open) {
76+
// if focus hasn't already moved from the menu lets return it
77+
// to the toggle
78+
if (this._focusInDropdown) {
79+
this._focusInDropdown = false;
80+
this.focus();
81+
}
82+
}
6083
}
6184

6285
render() {
@@ -74,6 +97,7 @@ class Dropdown extends React.Component {
7497
return (
7598
<Component
7699
{...props}
100+
tabIndex='-1'
77101
className={classNames(this.props.className, rootClasses)}
78102
>
79103
{ children }
@@ -84,7 +108,7 @@ class Dropdown extends React.Component {
84108
toggleOpen() {
85109
let open = !this.props.open;
86110

87-
if (this.props.onToggle){
111+
if (this.props.onToggle) {
88112
this.props.onToggle(open);
89113
}
90114
}
@@ -115,9 +139,7 @@ class Dropdown extends React.Component {
115139
break;
116140
case keycode.codes.esc:
117141
case keycode.codes.tab:
118-
if (this.props.open) {
119-
this.handleClose(event);
120-
}
142+
this.handleClose(event);
121143
break;
122144
default:
123145
}
@@ -128,19 +150,13 @@ class Dropdown extends React.Component {
128150
return;
129151
}
130152

131-
// we need to let the current event finish before closing the menu.
132-
// otherwise the menu may close, shifting focus to document.body, before focus has moved
133-
// to the next focusable input
134-
if (event && event.keyCode === keycode.codes.tab){
135-
setTimeout(this.toggleOpen);
136-
} else {
137-
this.toggleOpen();
138-
}
153+
this.toggleOpen();
154+
}
139155

140-
if (event && event.type === 'keydown' && event.keyCode === keycode.codes.esc) {
141-
let toggle = React.findDOMNode(this.refs[TOGGLE_REF]);
142-
event.preventDefault();
143-
event.stopPropagation();
156+
focus(){
157+
let toggle = React.findDOMNode(this.refs[TOGGLE_REF]);
158+
159+
if (toggle && toggle.focus) {
144160
toggle.focus();
145161
}
146162
}
@@ -149,7 +165,7 @@ class Dropdown extends React.Component {
149165
let open = !!this.props.open;
150166
let seen = {};
151167

152-
return React.Children.map(this.props.children, child => {
168+
return ValidComponentChildren.map(this.props.children, child => {
153169
let extractor = find(this.childExtractors, x => x.matches(child));
154170

155171
if (extractor) {

0 commit comments

Comments
 (0)