Skip to content

Commit 81a930b

Browse files
Merge pull request #2 from claydiffrient/master
[changed] Maintain focus inside of tray
2 parents b052164 + 720c1ce commit 81a930b

File tree

3 files changed

+47
-3
lines changed

3 files changed

+47
-3
lines changed

lib/components/Tray.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@ export default React.createClass({
1010
isOpen: React.PropTypes.bool,
1111
onBlur: React.PropTypes.func,
1212
closeTimeoutMS: React.PropTypes.number,
13-
closeOnBlur: React.PropTypes.bool
13+
closeOnBlur: React.PropTypes.bool,
14+
maintainFocus: React.PropTypes.bool
1415
},
1516

1617
getDefaultProps() {
1718
return {
1819
isOpen: false,
1920
closeTimeoutMS: 0,
20-
closeOnBlur: true
21+
closeOnBlur: true,
22+
maintainFocus: false
2123
};
2224
},
2325

lib/components/TrayPortal.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { PropTypes } from 'react';
22
import cx from 'classnames';
33
import focusManager from '../helpers/focusManager';
44
import isLeavingNode from '../helpers/isLeavingNode';
5+
import findTabbable from '../helpers/tabbable';
56

67
const styles = {
78
overlay: {
@@ -55,7 +56,8 @@ export default React.createClass({
5556
onBlur: PropTypes.func,
5657
closeOnBlur: PropTypes.bool,
5758
closeTimeoutMS: PropTypes.number,
58-
children: PropTypes.any
59+
children: PropTypes.any,
60+
maintainFocus: PropTypes.bool
5961
},
6062

6163
getInitialState() {
@@ -108,6 +110,15 @@ export default React.createClass({
108110
this.props.onBlur();
109111
}
110112

113+
// Keep focus inside the tray if maintainFocus is true
114+
if (e.keyCode === 9 && this.props.maintainFocus && isLeavingNode(this.refs.content, e)) {
115+
e.preventDefault();
116+
const tabbable = findTabbable(this.refs.content);
117+
const target = tabbable[e.shiftKey ? tabbable.length - 1 : 0];
118+
target.focus();
119+
return;
120+
}
121+
111122
// Treat tabbing away from content as blur/close if closeOnBlur
112123
if (e.keyCode === 9 && this.props.closeOnBlur && isLeavingNode(this.refs.content, e)) {
113124
e.preventDefault();

lib/components/__tests__/Tray-test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,35 @@ describe('react-tray', function() {
7979
equal(document.querySelectorAll('.ReactTray__Content').length, 1);
8080
}, 0);
8181
});
82+
83+
describe('maintainFocus prop', function() {
84+
this.timeout(0);
85+
beforeEach(function(done) {
86+
const props = {isOpen: true, onBlur: function() {}, closeTimeoutMS: 0, maintainFocus: true};
87+
const children = (
88+
<div>
89+
<a href="#" id="one">One</a>
90+
<a href="#" id="two">Two</a>
91+
<a href="#" id="three">Three</a>
92+
</div>
93+
);
94+
renderTray(props, children, () => done());
95+
});
96+
97+
it('sends focus to the first item if tabbing away from the last element', function() {
98+
const firstItem = document.querySelector('#one');
99+
const lastItem = document.querySelector('#three');
100+
lastItem.focus();
101+
TestUtils.Simulate.keyDown(document.querySelector('.ReactTray__Content'), {keyCode: 9});
102+
equal(document.activeElement, firstItem);
103+
});
104+
105+
it('sends focus to the last item if shift + tabbing from the first item', function() {
106+
const firstItem = document.querySelector('#one');
107+
const lastItem = document.querySelector('#three');
108+
firstItem.focus();
109+
TestUtils.Simulate.keyDown(document.querySelector('.ReactTray__Content'), {keyCode: 9, shiftKey: true});
110+
equal(document.activeElement, lastItem);
111+
});
112+
});
82113
});

0 commit comments

Comments
 (0)