diff --git a/.gitignore b/.gitignore
index 783821d1..27aa61d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
examples/**/*-bundle.js
node_modules/
+.idea/
diff --git a/.npmignore b/.npmignore
index 3c28928f..552bfc2d 100644
--- a/.npmignore
+++ b/.npmignore
@@ -4,3 +4,4 @@ examples
karma.conf.js
script
specs
+.idea/
diff --git a/README.md b/README.md
index 4b30ca9d..3fad0cfa 100644
--- a/README.md
+++ b/README.md
@@ -9,12 +9,47 @@ Accessible modal dialog component for React.JS
isOpen={bool}
onRequestClose={fn}
closeTimeoutMS={n}
->
+ style={customStyle}>
+
Modal Content
Etc.
```
+## Styles
+Styles are passed as an object with 2 keys, 'overlay' and 'content' like so
+```js
+{
+ overlay : {
+ position : 'fixed',
+ top : 0,
+ left : 0,
+ right : 0,
+ bottom : 0,
+ backgroundColor : 'rgba(255, 255, 255, 0.75)'
+ },
+ content : {
+ position : 'absolute',
+ top : '40px',
+ left : '40px',
+ right : '40px',
+ bottom : '40px',
+ border : '1px solid #ccc',
+ background : '#fff',
+ overflow : 'auto',
+ WebkitOverflowScrolling : 'touch',
+ borderRadius : '4px',
+ outline : 'none',
+ padding : '20px'
+
+ }
+}
+```
+
+Styles passed to the modal are merged in with the above defaults and applied to their respective elements.
+At this time, media queries will need to be handled by the consumer.
+
+## Examples
Inside an app:
```js
@@ -23,8 +58,28 @@ var Modal = require('react-modal');
var appElement = document.getElementById('your-app-element');
+/*
+By default the modal is anchored to document.body. All of the following overrides are available.
+
+* element
Modal.setAppElement(appElement);
-Modal.injectCSS();
+
+* query selector - uses the first element found if you pass in a class.
+Modal.setAppElement('#your-app-element');
+
+*/
+
+const customStyles = {
+ content : {
+ top : '50%',
+ left : '50%',
+ right : 'auto',
+ bottom : 'auto',
+ marginRight : '-50%',
+ transform : 'translate(-50%, -50%)'
+ }
+};
+
var App = React.createClass({
@@ -47,7 +102,8 @@ var App = React.createClass({
+ style={customStyles} >
+
Hello
close
I am a modal
diff --git a/examples/basic/app.js b/examples/basic/app.js
index e7da9d0d..9381eafa 100644
--- a/examples/basic/app.js
+++ b/examples/basic/app.js
@@ -3,8 +3,7 @@ var Modal = require('../../lib/index');
var appElement = document.getElementById('example');
-Modal.setAppElement(appElement);
-Modal.injectCSS();
+Modal.setAppElement('#example');
var App = React.createClass({
@@ -37,8 +36,7 @@ var App = React.createClass({
+ onRequestClose={this.handleModalCloseRequest}>
Hello
close
I am a modal
diff --git a/examples/bootstrap/app.js b/examples/bootstrap/app.js
index a20c88de..75dff72b 100644
--- a/examples/bootstrap/app.js
+++ b/examples/bootstrap/app.js
@@ -4,7 +4,6 @@ var Modal = require('../../lib/index');
var appElement = document.getElementById('example');
Modal.setAppElement(appElement);
-Modal.injectCSS();
var App = React.createClass({
diff --git a/lib/components/Modal.js b/lib/components/Modal.js
index b790d5a1..37da231a 100644
--- a/lib/components/Modal.js
+++ b/lib/components/Modal.js
@@ -2,7 +2,6 @@ var React = require('react');
var ExecutionEnvironment = require('react/lib/ExecutionEnvironment');
var ModalPortal = React.createFactory(require('./ModalPortal'));
var ariaAppHider = require('../helpers/ariaAppHider');
-var injectCSS = require('../helpers/injectCSS');
var elementClass = require('element-class');
var SafeHTMLElement = ExecutionEnvironment.canUseDOM ? window.HTMLElement : {};
@@ -10,16 +9,23 @@ var SafeHTMLElement = ExecutionEnvironment.canUseDOM ? window.HTMLElement : {};
var Modal = module.exports = React.createClass({
displayName: 'Modal',
-
statics: {
setAppElement: ariaAppHider.setElement,
- injectCSS: injectCSS
+ injectCSS : function() {
+ "production" !== process.env.NODE_ENV
+ && console.warn('React-Modal: injectCSS has been deprecated ' +
+ 'and no longer has any effect. It will be removed in a later version');
+ }
},
propTypes: {
isOpen: React.PropTypes.bool.isRequired,
- onRequestClose: React.PropTypes.func,
+ style : React.PropTypes.shape({
+ content: React.PropTypes.object,
+ overlay: React.PropTypes.object
+ }),
appElement: React.PropTypes.instanceOf(SafeHTMLElement),
+ onRequestClose: React.PropTypes.func,
closeTimeoutMS: React.PropTypes.number,
ariaHideApp: React.PropTypes.bool
},
diff --git a/lib/components/ModalPortal.js b/lib/components/ModalPortal.js
index fb1644af..06ba9e1b 100644
--- a/lib/components/ModalPortal.js
+++ b/lib/components/ModalPortal.js
@@ -2,7 +2,8 @@ var React = require('react');
var div = React.DOM.div;
var focusManager = require('../helpers/focusManager');
var scopeTab = require('../helpers/scopeTab');
-var cx = require('classnames');
+var Assign = require('lodash.assign');
+
// so that our CSS is statically analyzable
var CLASS_NAMES = {
@@ -18,7 +19,31 @@ var CLASS_NAMES = {
}
};
-var OVERLAY_STYLES = { position: 'fixed', left: 0, right: 0, top: 0, bottom: 0 };
+var defaultStyles = {
+ overlay : {
+ position : 'fixed',
+ top : 0,
+ left : 0,
+ right : 0,
+ bottom : 0,
+ backgroundColor : 'rgba(255, 255, 255, 0.75)'
+ },
+ content : {
+ position : 'absolute',
+ top : '40px',
+ left : '40px',
+ right : '40px',
+ bottom : '40px',
+ border : '1px solid #ccc',
+ background : '#fff',
+ overflow : 'auto',
+ WebkitOverflowScrolling : 'touch',
+ borderRadius : '4px',
+ outline : 'none',
+ padding : '20px'
+
+ }
+};
function stopPropagation(event) {
event.stopPropagation();
@@ -28,6 +53,15 @@ var ModalPortal = module.exports = React.createClass({
displayName: 'ModalPortal',
+ getDefaultProps: function() {
+ return {
+ style: {
+ overlay : {},
+ content : {}
+ }
+ }
+ },
+
getInitialState: function() {
return {
afterOpen: false,
@@ -132,27 +166,27 @@ var ModalPortal = module.exports = React.createClass({
return !this.props.isOpen && !this.state.beforeClose;
},
- buildClassName: function(which) {
+ buildClassName: function(which, additional) {
var className = CLASS_NAMES[which].base;
if (this.state.afterOpen)
className += ' '+CLASS_NAMES[which].afterOpen;
if (this.state.beforeClose)
className += ' '+CLASS_NAMES[which].beforeClose;
- return className;
+ return additional ? className + ' ' + additional : className;
},
render: function() {
return this.shouldBeClosed() ? div() : (
div({
ref: "overlay",
- className: cx(this.buildClassName('overlay'), this.props.overlayClassName),
- style: OVERLAY_STYLES,
+ className: this.buildClassName('overlay', this.props.overlayClassName),
+ style: Assign({}, defaultStyles.overlay, this.props.style.overlay || {}),
onClick: this.handleOverlayClick
},
div({
ref: "content",
- style: this.props.style,
- className: cx(this.buildClassName('content'), this.props.className),
+ style: Assign({}, defaultStyles.content, this.props.style.content || {}),
+ className: this.buildClassName('content', this.props.className),
tabIndex: "-1",
onClick: stopPropagation,
onKeyDown: this.handleKeyDown
diff --git a/lib/helpers/ariaAppHider.js b/lib/helpers/ariaAppHider.js
index 886ab3d1..02ba80eb 100644
--- a/lib/helpers/ariaAppHider.js
+++ b/lib/helpers/ariaAppHider.js
@@ -1,7 +1,11 @@
-var _element = null;
+var _element = document.body;
function setElement(element) {
- _element = element;
+ if (typeof element === 'string') {
+ var el = document.querySelectorAll(element);
+ element = 'length' in el ? el[0] : el;
+ }
+ _element = element || _element;
}
function hide(appElement) {
@@ -27,7 +31,7 @@ function validateElement(appElement) {
}
function resetForTesting() {
- _element = null;
+ _element = document.body;
}
exports.toggle = toggle;
diff --git a/lib/helpers/injectCSS.js b/lib/helpers/injectCSS.js
deleted file mode 100644
index 9defc0e9..00000000
--- a/lib/helpers/injectCSS.js
+++ /dev/null
@@ -1,48 +0,0 @@
-module.exports = function() {
- injectStyle([
- '.ReactModal__Overlay {',
- ' background-color: rgba(255, 255, 255, 0.75);',
- '}',
- '.ReactModal__Content {',
- ' position: absolute;',
- ' top: 40px;',
- ' left: 40px;',
- ' right: 40px;',
- ' bottom: 40px;',
- ' border: 1px solid #ccc;',
- ' background: #fff;',
- ' overflow: auto;',
- ' -webkit-overflow-scrolling: touch;',
- ' border-radius: 4px;',
- ' outline: none;',
- ' padding: 20px;',
- '}',
- '@media (max-width: 768px) {',
- ' .ReactModal__Content {',
- ' top: 10px;',
- ' left: 10px;',
- ' right: 10px;',
- ' bottom: 10px;',
- ' padding: 10px;',
- ' }',
- '}'
- ].join('\n'));
-};
-
-function injectStyle(css) {
- var style = document.getElementById('rackt-style');
- if (!style) {
- style = document.createElement('style');
- style.setAttribute('id', 'rackt-style');
- style.setAttribute("type", "text/css");
- }
-
- if (style.styleSheet) {
- style.styleSheet.cssText = css;
- document.body.appendChild(style);
- } else {
- style.innerHTML = css;
- document.head.appendChild(style);
- }
-}
-
diff --git a/package.json b/package.json
index a8df6610..34b54933 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-modal",
- "version": "0.3.0",
+ "version": "0.4.0",
"description": "Accessible modal dialog component for React.JS",
"main": "./lib/index",
"repository": {
@@ -32,6 +32,7 @@
"karma-cli": "0.1.0",
"karma-firefox-launcher": "0.1.6",
"karma-mocha": "0.2.0",
+ "karma-safari-launcher": "^0.1.1",
"mocha": "2.2.5",
"react": ">=0.13.3",
"reactify": "^1.1.1",
@@ -40,8 +41,8 @@
"webpack-dev-server": "1.10.1"
},
"dependencies": {
- "classnames": "^2.1.3",
- "element-class": "^0.2.0"
+ "element-class": "^0.2.0",
+ "lodash.assign": "^3.2.0"
},
"peerDependencies": {
"react": ">=0.13.3"
@@ -60,4 +61,4 @@
"browserify-shim": {
"react": "global:React"
}
-}
\ No newline at end of file
+}
diff --git a/specs/Modal.spec.js b/specs/Modal.spec.js
index 2678df8a..eeece65e 100644
--- a/specs/Modal.spec.js
+++ b/specs/Modal.spec.js
@@ -23,14 +23,6 @@ describe('Modal', function () {
unmountModal();
});
- it('throws without an appElement', function() {
- var node = document.createElement('div');
- throws(function() {
- React.render(React.createElement(Modal, {isOpen: true}), node);
- });
- React.unmountComponentAtNode(node);
- });
-
it('uses the global appElement', function() {
var app = document.createElement('div');
var node = document.createElement('div');
@@ -100,30 +92,45 @@ describe('Modal', function () {
it('supports custom className', function() {
var modal = renderModal({isOpen: true, className: 'myClass'});
- equal(modal.portal.refs.content.getDOMNode().className.contains('myClass'), true);
+ equal(modal.portal.refs.content.getDOMNode().className.indexOf('myClass') !== -1, true);
unmountModal();
});
it('supports overlayClassName', function () {
var modal = renderModal({isOpen: true, overlayClassName: 'myOverlayClass'});
- equal(modal.portal.refs.overlay.getDOMNode().className.contains('myOverlayClass'), true);
+ equal(modal.portal.refs.overlay.getDOMNode().className.indexOf('myOverlayClass') !== -1, true);
unmountModal();
});
it('supports adding style to the modal contents', function () {
- var modal = renderModal({isOpen: true, style: {width: '20px'}});
+ var modal = renderModal({isOpen: true, style: {content: {width: '20px'}}});
equal(modal.portal.refs.content.getDOMNode().style.width, '20px');
});
+ it('supports overridding style on the modal contents', function() {
+ var modal = renderModal({isOpen: true, style: {content: {position: 'static'}}});
+ equal(modal.portal.refs.content.getDOMNode().style.position, 'static');
+ });
+
+ it('supports adding style on the modal overlay', function() {
+ var modal = renderModal({isOpen: true, style: {overlay: {width: '75px'}}});
+ equal(modal.portal.refs.overlay.getDOMNode().style.width, '75px');
+ });
+
+ it('supports overridding style on the modal overlay', function() {
+ var modal = renderModal({isOpen: true, style: {overlay: {position: 'static'}}});
+ equal(modal.portal.refs.overlay.getDOMNode().style.position, 'static');
+ });
+
it('adds class to body when open', function() {
var modal = renderModal({isOpen: false});
- equal(document.body.className.contains('ReactModal__Body--open'), false);
+ equal(document.body.className.indexOf('ReactModal__Body--open') !== -1, false);
modal.setProps({ isOpen: true});
- equal(document.body.className.contains('ReactModal__Body--open'), true);
+ equal(document.body.className.indexOf('ReactModal__Body--open') !== -1, true);
modal = renderModal({isOpen: false});
- equal(document.body.className.contains('ReactModal__Body--open'), false);
+ equal(document.body.className.indexOf('ReactModal__Body--open') !== -1, false);
unmountModal();
});