From 6282c3e401e676c3070a0b6161f4febc7c809ae6 Mon Sep 17 00:00:00 2001 From: Doron Sharon Date: Fri, 19 Feb 2016 19:06:35 +0200 Subject: [PATCH] Added the ability to decide whether the modal should be closed when clicking the overlay area. This is an important ability since in some cases we don't want the modal to be closed when users are clicking outside. Added tests and README instructions. --- README.md | 16 ++++++++++ lib/components/Modal.js | 6 ++-- lib/components/ModalPortal.js | 10 ++++--- package.json | 5 ++-- specs/Modal.spec.js | 55 +++++++++++++++++++++++++++++++++-- specs/helper.js | 1 + 6 files changed, 83 insertions(+), 10 deletions(-) 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;