Skip to content

Commit

Permalink
Merge pull request react-bootstrap#690 from jportela/modal-focus
Browse files Browse the repository at this point in the history
Modals are focused on opening, for improved acessibility
  • Loading branch information
AlexKVal committed May 24, 2015
2 parents ed469cf + 5313abe commit 0fda76a
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ const Modal = React.createClass({
domUtils.ownerDocument(this).body;
container.className += container.className.length ? ' modal-open' : 'modal-open';

this.focusModalContent();

if (this.props.backdrop) {
this.iosClickHack();
}
Expand All @@ -142,6 +144,8 @@ const Modal = React.createClass({
let container = (this.props.container && React.findDOMNode(this.props.container)) ||
domUtils.ownerDocument(this).body;
container.className = container.className.replace(/ ?modal-open/, '');

this.restoreLastFocus();
},

handleBackdropClick(e) {
Expand All @@ -156,6 +160,19 @@ const Modal = React.createClass({
if (this.props.keyboard && e.keyCode === 27) {
this.props.onRequestHide();
}
},

focusModalContent () {
this.lastFocus = document.activeElement;
let modalContent = React.findDOMNode(this.refs.modal);
modalContent.focus();
},

restoreLastFocus () {
if (this.lastFocus) {
this.lastFocus.focus();
this.lastFocus = null;
}
}
});

Expand Down
61 changes: 61 additions & 0 deletions test/ModalSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,65 @@ describe('Modal', function () {
assert.match(dialog.props.className, /\btestCss\b/);
});

describe('Focused state', function () {
let focusableContainer = null;
beforeEach(function () {
focusableContainer = document.createElement('div');
focusableContainer.tabIndex = 0;
document.body.appendChild(focusableContainer);
focusableContainer.focus();
});

afterEach(function () {
React.unmountComponentAtNode(focusableContainer);
document.body.removeChild(focusableContainer);
});

it('Should focus on the Modal when it is opened', function (done) {
document.activeElement.should.equal(focusableContainer);

let doneOp = function () {
// focus should be back on the previous element when modal closed
setTimeout(function () {
document.activeElement.should.equal(focusableContainer);
done();
}, 0);
};

let Container = React.createClass({
getInitialState() {
return {modalOpen: true};
},
handleCloseModal() {
this.setState({modalOpen: false});
doneOp();
},
render() {
if (this.state.modalOpen) {
return (
<Modal onRequestHide={this.handleCloseModal} container={this}>
<strong>Message</strong>
</Modal>
);
} else {
return <span/>;
}
}
});

let instance = React.render(<Container />, focusableContainer);

setTimeout(function () {
// modal should be focused when opened
let modal = instance.getDOMNode().getElementsByClassName('modal')[0];
document.activeElement.should.equal(modal);

// close the modal
let backdrop = instance.getDOMNode().getElementsByClassName('modal-backdrop')[0];
ReactTestUtils.Simulate.click(backdrop);
}, 0);
});

});

});

0 comments on commit 0fda76a

Please sign in to comment.