Skip to content

Commit b7853bb

Browse files
committed
[fixed] Dropdown focus behavior on click
By default, only focus the first dropdown menu item after keydown.
1 parent 1b2b775 commit b7853bb

File tree

2 files changed

+47
-22
lines changed

2 files changed

+47
-22
lines changed

src/Dropdown.js

+46-21
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ class Dropdown extends React.Component {
4646
}];
4747

4848
this.state = {};
49+
50+
this.lastOpenEventType = null;
51+
this.isKeyboardClick = false;
4952
}
5053

5154
componentDidMount() {
52-
let menu = this.refs.menu;
53-
if (this.props.open && menu.focusNext) {
54-
menu.focusNext();
55-
}
55+
this.focusNextOnOpen();
5656
}
5757

5858
componentWillUpdate(nextProps) {
@@ -65,10 +65,8 @@ class Dropdown extends React.Component {
6565
}
6666

6767
componentDidUpdate(prevProps) {
68-
let menu = this.refs.menu;
69-
70-
if (this.props.open && !prevProps.open && menu.focusNext) {
71-
menu.focusNext();
68+
if (this.props.open && !prevProps.open) {
69+
this.focusNextOnOpen();
7270
}
7371

7472
if (!this.props.open && prevProps.open) {
@@ -105,9 +103,13 @@ class Dropdown extends React.Component {
105103
);
106104
}
107105

108-
toggleOpen() {
106+
toggleOpen(eventType = null) {
109107
let open = !this.props.open;
110108

109+
if (open) {
110+
this.lastOpenEventType = eventType;
111+
}
112+
111113
if (this.props.onToggle) {
112114
this.props.onToggle(open);
113115
}
@@ -118,29 +120,32 @@ class Dropdown extends React.Component {
118120
return;
119121
}
120122

121-
this.toggleOpen();
123+
this.toggleOpen(this.isKeyboardClick ? 'keydown' : 'click');
124+
this.isKeyboardClick = false;
122125
}
123126

124127
handleKeyDown(event) {
125-
let focusNext = () => {
126-
if (this.refs.menu.focusNext) {
127-
this.refs.menu.focusNext();
128-
}
129-
};
128+
if (this.props.disabled) {
129+
return;
130+
}
130131

131132
switch (event.keyCode) {
132133
case keycode.codes.down:
133134
if (!this.props.open) {
134-
this.toggleOpen();
135-
} else {
136-
focusNext();
135+
this.toggleOpen('keydown');
136+
} else if (this.refs.menu.focusNext) {
137+
this.refs.menu.focusNext();
137138
}
138139
event.preventDefault();
139140
break;
140141
case keycode.codes.esc:
141142
case keycode.codes.tab:
142143
this.handleClose(event);
143144
break;
145+
case keycode.codes.space:
146+
case keycode.codes.enter:
147+
this.isKeyboardClick = true;
148+
break;
144149
default:
145150
}
146151
}
@@ -153,6 +158,20 @@ class Dropdown extends React.Component {
153158
this.toggleOpen();
154159
}
155160

161+
focusNextOnOpen() {
162+
const {menu} = this.refs;
163+
if (!menu.focusNext) {
164+
return;
165+
}
166+
167+
if (
168+
this.lastOpenEventType === 'keydown' ||
169+
this.props.alwaysFocusNextOnOpen
170+
) {
171+
menu.focusNext();
172+
}
173+
}
174+
156175
focus() {
157176
let toggle = React.findDOMNode(this.refs[TOGGLE_REF]);
158177

@@ -232,7 +251,8 @@ Dropdown.TOGGLE_ROLE = TOGGLE_ROLE;
232251
Dropdown.MENU_ROLE = MENU_ROLE;
233252

234253
Dropdown.defaultProps = {
235-
componentClass: ButtonGroup
254+
componentClass: ButtonGroup,
255+
alwaysFocusNextOnOpen: false
236256
};
237257

238258
Dropdown.propTypes = {
@@ -270,7 +290,7 @@ Dropdown.propTypes = {
270290
disabled: React.PropTypes.bool,
271291

272292
/**
273-
* Align the menu to the right side of the Dropdown toggle
293+
* Align the menu to the right side of the Dropdown toggle
274294
*/
275295
pullRight: React.PropTypes.bool,
276296

@@ -304,7 +324,12 @@ Dropdown.propTypes = {
304324
* function(Object event, Any eventKey)
305325
* ```
306326
*/
307-
onSelect: React.PropTypes.func
327+
onSelect: React.PropTypes.func,
328+
329+
/**
330+
* Focus first menu item on menu open on all events, not just keydown events.
331+
*/
332+
alwaysFocusNextOnOpen: React.PropTypes.bool
308333
};
309334

310335
Dropdown = uncontrollable(Dropdown, { open: 'onToggle' });

test/DropdownSpec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ describe('Dropdown', () => {
416416
// I am fairly confident that the failure is due to a test specific conflict and not an actual bug.
417417
it('when open and the key "esc" is pressed the menu is closed and focus is returned to the button', () => {
418418
const instance = React.render(
419-
<Dropdown defaultOpen id='test-id'>
419+
<Dropdown defaultOpen alwaysFocusNextOnOpen id='test-id'>
420420
{dropdownChildren}
421421
</Dropdown>
422422
, focusableContainer);

0 commit comments

Comments
 (0)