diff --git a/README.md b/README.md index b1aedd6f..7dffd709 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,22 @@ var App = React.createClass({ ReactDOM.render(, appElement); ``` +By default the modal is closed when clicking outside of it (the overlay area). If you want to prevent this behavior you can +pass the 'shouldCloseOnOverlayClick' prop with 'false' value. +```xml + + +

Force Modal

+

Modal cannot be closed when clickig the overlay area

+ +
+``` + # Demos * http://reactjs.github.io/react-modal/ * http://reactjs.github.io/react-modal/bootstrap diff --git a/lib/components/Modal.js b/lib/components/Modal.js index 49f0a86c..07b5dac8 100644 --- a/lib/components/Modal.js +++ b/lib/components/Modal.js @@ -29,14 +29,16 @@ var Modal = module.exports = React.createClass({ appElement: React.PropTypes.instanceOf(SafeHTMLElement), onRequestClose: React.PropTypes.func, closeTimeoutMS: React.PropTypes.number, - ariaHideApp: React.PropTypes.bool + ariaHideApp: React.PropTypes.bool, + shouldCloseOnOverlayClick: React.PropTypes.bool }, getDefaultProps: function () { return { isOpen: false, ariaHideApp: true, - closeTimeoutMS: 0 + closeTimeoutMS: 0, + shouldCloseOnOverlayClick: true }; }, diff --git a/lib/components/ModalPortal.js b/lib/components/ModalPortal.js index b7db99c7..689b2e73 100644 --- a/lib/components/ModalPortal.js +++ b/lib/components/ModalPortal.js @@ -146,10 +146,12 @@ var ModalPortal = module.exports = React.createClass({ }, handleOverlayClick: function() { - if (this.ownerHandlesClose()) - this.requestClose(); - else - this.focusContent(); + if (this.props.shouldCloseOnOverlayClick) { + if (this.ownerHandlesClose()) + this.requestClose(); + else + this.focusContent(); + } }, requestClose: function() { diff --git a/package.json b/package.json index 5549ffa8..3afc47fe 100644 --- a/package.json +++ b/package.json @@ -34,11 +34,12 @@ "karma-mocha": "0.2.0", "karma-safari-launcher": "^0.1.1", "mocha": "2.3.3", + "react": "^0.14.0", "react-addons-test-utils": "^0.14.0", "react-dom": "^0.14.0", - "react": "^0.14.0", "reactify": "^1.1.1", "rf-release": "0.4.0", + "sinon": "^1.17.3", "uglify-js": "2.4.24", "webpack-dev-server": "1.11.0" }, @@ -65,4 +66,4 @@ "react": "global:React", "react-dom": "global:ReactDOM" } -} \ No newline at end of file +} diff --git a/specs/Modal.spec.js b/specs/Modal.spec.js index e0052815..0552004b 100644 --- a/specs/Modal.spec.js +++ b/specs/Modal.spec.js @@ -6,6 +6,7 @@ var Modal = require('../lib/components/Modal'); var Simulate = TestUtils.Simulate; var ariaAppHider = require('../lib/helpers/ariaAppHider'); var button = ReactDOM.button; +var sinon = require('sinon'); describe('Modal', function () { @@ -74,6 +75,7 @@ describe('Modal', function () { equal(props.isOpen, false); equal(props.ariaHideApp, true); equal(props.closeTimeoutMS, 0); + equal(props.shouldCloseOnOverlayClick, true); ReactDOM.unmountComponentAtNode(node); ariaAppHider.resetForTesting(); }); @@ -94,13 +96,13 @@ describe('Modal', function () { it('supports custom className', function() { var modal = renderModal({isOpen: true, className: 'myClass'}); - equal(modal.portal.refs.content.className.contains('myClass'), true); + notEqual(modal.portal.refs.content.className.indexOf('myClass'), -1); unmountModal(); }); it('supports overlayClassName', function () { var modal = renderModal({isOpen: true, overlayClassName: 'myOverlayClass'}); - equal(modal.portal.refs.overlay.className.contains('myOverlayClass'), true); + notEqual(modal.portal.refs.overlay.className.indexOf('myOverlayClass'), -1); unmountModal(); }); @@ -145,6 +147,55 @@ describe('Modal', function () { unmountModal(); }); + describe('should close on overlay click', function() { + afterEach('Unmount modal', function() { + unmountModal(); + }); + + describe('verify props', function() { + it('verify default prop of shouldCloseOnOverlayClick', function () { + var modal = renderModal({isOpen: true}); + equal(modal.props.shouldCloseOnOverlayClick, true); + }); + + it('verify prop of shouldCloseOnOverlayClick', function () { + var modal = renderModal({isOpen: true, shouldCloseOnOverlayClick: false}); + equal(modal.props.shouldCloseOnOverlayClick, false); + }); + }); + + describe('verify clicks', function() { + it('verify overlay click when shouldCloseOnOverlayClick sets to false', function () { + var requestCloseCallback = sinon.spy(); + var modal = renderModal({ + isOpen: true, + shouldCloseOnOverlayClick: false + }); + equal(modal.props.isOpen, true); + var overlay = TestUtils.scryRenderedDOMComponentsWithClass(modal.portal, 'ReactModal__Overlay'); + equal(overlay.length, 1); + Simulate.click(overlay[0]); // click the overlay + ok(!requestCloseCallback.called) + }); + + it('verify overlay click when shouldCloseOnOverlayClick sets to true', function() { + var requestCloseCallback = sinon.spy(); + var modal = renderModal({ + isOpen: true, + shouldCloseOnOverlayClick: true, + onRequestClose: function() { + requestCloseCallback(); + } + }); + equal(modal.props.isOpen, true); + var overlay = TestUtils.scryRenderedDOMComponentsWithClass(modal.portal, 'ReactModal__Overlay'); + equal(overlay.length, 1); + Simulate.click(overlay[0]); // click the overlay + ok(requestCloseCallback.called) + }); + }); + }); + //it('adds --before-close for animations', function() { //var node = document.createElement('div'); diff --git a/specs/helper.js b/specs/helper.js index 62e88bc4..5cc5e88b 100644 --- a/specs/helper.js +++ b/specs/helper.js @@ -5,6 +5,7 @@ var Modal = React.createFactory(require('../lib/components/Modal')); ok = assert.ok; equal = assert.equal; +notEqual = assert.notEqual; strictEqual = assert.strictEqual; throws = assert.throws;