react-frame-component
Advanced tools
Comparing version 2.0.2 to 3.0.0
115
lib/Frame.js
@@ -27,2 +27,6 @@ 'use strict'; | ||
var _Content = require('./Content'); | ||
var _Content2 = _interopRequireDefault(_Content); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -36,26 +40,2 @@ | ||
var hasConsole = typeof window !== 'undefined' && window.console; | ||
var noop = function noop() {}; | ||
var swallowInvalidHeadWarning = noop; | ||
var resetWarnings = noop; | ||
if (hasConsole) { | ||
(function () { | ||
var originalError = console.error; // eslint-disable-line no-console | ||
// Rendering a <head> into a body is technically invalid although it | ||
// works. We swallow React's validateDOMNesting warning if that is the | ||
// message to avoid confusion | ||
swallowInvalidHeadWarning = function swallowInvalidHeadWarning() { | ||
console.error = function (msg) { | ||
// eslint-disable-line no-console | ||
if (/<head>/.test(msg)) return; | ||
originalError.call(console, msg); | ||
}; | ||
}; | ||
resetWarnings = function resetWarnings() { | ||
return console.error = originalError; | ||
}; // eslint-disable-line no-console | ||
})(); | ||
} | ||
var Frame = function (_Component) { | ||
@@ -73,2 +53,6 @@ _inherits(Frame, _Component); | ||
_this.handleLoad = function () { | ||
_this.forceUpdate(); | ||
}; | ||
_this._isMounted = false; | ||
@@ -82,18 +66,16 @@ return _this; | ||
this._isMounted = true; | ||
this.renderFrameContents(); | ||
var doc = this.getDoc(); | ||
if (doc && doc.readyState === 'complete') { | ||
this.forceUpdate(); | ||
} else { | ||
this.node.addEventListener('load', this.handleLoad); | ||
} | ||
} | ||
}, { | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate() { | ||
this.renderFrameContents(); | ||
} | ||
}, { | ||
key: 'componentWillUnmount', | ||
value: function componentWillUnmount() { | ||
this._isMounted = false; | ||
var doc = this.getDoc(); | ||
var mountTarget = this.getMountTarget(); | ||
if (doc && mountTarget) { | ||
_reactDom2.default.unmountComponentAtNode(mountTarget); | ||
} | ||
this.node.removeEventListener('load', this.handleLoad); | ||
} | ||
@@ -103,3 +85,3 @@ }, { | ||
value: function getDoc() { | ||
return _reactDom2.default.findDOMNode(this).contentDocument; // eslint-disable-line | ||
return this.node.contentDocument; // eslint-disable-line | ||
} | ||
@@ -119,14 +101,20 @@ }, { | ||
if (!this._isMounted) { | ||
return; | ||
return null; | ||
} | ||
var doc = this.getDoc(); | ||
if (doc && doc.readyState === 'complete') { | ||
if (doc.querySelector('div') === null) { | ||
this._setInitialContent = false; | ||
} | ||
var win = doc.defaultView || doc.parentView; | ||
var initialRender = !this._setInitialContent; | ||
var contents = _react2.default.createElement( | ||
if (doc.querySelector('.frame-content') === null) { | ||
this._setInitialContent = false; | ||
} | ||
var contentDidMount = this.props.contentDidMount; | ||
var contentDidUpdate = this.props.contentDidUpdate; | ||
var win = doc.defaultView || doc.parentView; | ||
var initialRender = !this._setInitialContent; | ||
var contents = _react2.default.createElement( | ||
_Content2.default, | ||
{ contentDidMount: contentDidMount, contentDidUpdate: contentDidUpdate }, | ||
_react2.default.createElement( | ||
_DocumentContext2.default, | ||
@@ -137,26 +125,17 @@ { document: doc, window: win }, | ||
{ className: 'frame-content' }, | ||
this.props.head, | ||
this.props.children | ||
) | ||
); | ||
) | ||
); | ||
if (initialRender) { | ||
doc.open('text/html', 'replace'); | ||
doc.write(this.props.initialContent); | ||
doc.close(); | ||
this._setInitialContent = true; | ||
} | ||
if (initialRender) { | ||
doc.open('text/html', 'replace'); | ||
doc.write(this.props.initialContent); | ||
doc.close(); | ||
this._setInitialContent = true; | ||
} | ||
swallowInvalidHeadWarning(); | ||
var mountTarget = this.getMountTarget(); | ||
// unstable_renderSubtreeIntoContainer allows us to pass this component as | ||
// the parent, which exposes context to any child components. | ||
var callback = initialRender ? this.props.contentDidMount : this.props.contentDidUpdate; | ||
var mountTarget = this.getMountTarget(); | ||
_reactDom2.default.unstable_renderSubtreeIntoContainer(this, contents, mountTarget, callback); | ||
resetWarnings(); | ||
} else { | ||
setTimeout(this.renderFrameContents.bind(this), 0); | ||
} | ||
return [_reactDom2.default.createPortal(this.props.head, this.getDoc().head), _reactDom2.default.createPortal(contents, mountTarget)]; | ||
} | ||
@@ -166,2 +145,4 @@ }, { | ||
value: function render() { | ||
var _this2 = this; | ||
var props = _extends({}, this.props, { | ||
@@ -175,3 +156,9 @@ children: undefined // The iframe isn't ready so we drop children from props here. #12, #17 | ||
delete props.contentDidUpdate; | ||
return _react2.default.createElement('iframe', props); | ||
return _react2.default.createElement( | ||
'iframe', | ||
_extends({}, props, { ref: function ref(node) { | ||
return _this2.node = node; | ||
} }), | ||
this.renderFrameContents() | ||
); | ||
} | ||
@@ -178,0 +165,0 @@ }]); |
{ | ||
"name": "react-frame-component", | ||
"version": "2.0.2", | ||
"version": "3.0.0", | ||
"description": "React component to wrap your application or component in an iFrame for encapsulation purposes", | ||
@@ -62,4 +62,2 @@ "main": "lib/index.js", | ||
"karma-osx-reporter": "^0.2.1", | ||
"karma-phantomjs-launcher": "^1.0.2", | ||
"karma-phantomjs2-launcher": "^0.5.0", | ||
"karma-sourcemap-loader": "^0.3.7", | ||
@@ -66,0 +64,0 @@ "karma-webpack": "^2.0.2", |
@@ -5,22 +5,4 @@ import React, { Component } from 'react'; | ||
import DocumentContext from './DocumentContext'; | ||
import Content from './Content'; | ||
const hasConsole = typeof window !== 'undefined' && window.console; | ||
const noop = () => {}; | ||
let swallowInvalidHeadWarning = noop; | ||
let resetWarnings = noop; | ||
if (hasConsole) { | ||
const originalError = console.error; // eslint-disable-line no-console | ||
// Rendering a <head> into a body is technically invalid although it | ||
// works. We swallow React's validateDOMNesting warning if that is the | ||
// message to avoid confusion | ||
swallowInvalidHeadWarning = () => { | ||
console.error = (msg) => { // eslint-disable-line no-console | ||
if (/<head>/.test(msg)) return; | ||
originalError.call(console, msg); | ||
}; | ||
}; | ||
resetWarnings = () => (console.error = originalError); // eslint-disable-line no-console | ||
} | ||
export default class Frame extends Component { | ||
@@ -61,7 +43,9 @@ // React warns when you render directly into the body since browser extensions | ||
this._isMounted = true; | ||
this.renderFrameContents(); | ||
} | ||
componentDidUpdate() { | ||
this.renderFrameContents(); | ||
const doc = this.getDoc(); | ||
if (doc && doc.readyState === 'complete') { | ||
this.forceUpdate(); | ||
} else { | ||
this.node.addEventListener('load', this.handleLoad); | ||
} | ||
} | ||
@@ -71,11 +55,8 @@ | ||
this._isMounted = false; | ||
const doc = this.getDoc(); | ||
const mountTarget = this.getMountTarget(); | ||
if (doc && mountTarget) { | ||
ReactDOM.unmountComponentAtNode(mountTarget); | ||
} | ||
this.node.removeEventListener('load', this.handleLoad); | ||
} | ||
getDoc() { | ||
return ReactDOM.findDOMNode(this).contentDocument; // eslint-disable-line | ||
return this.node.contentDocument; // eslint-disable-line | ||
} | ||
@@ -91,43 +72,45 @@ | ||
handleLoad = () => { | ||
this.forceUpdate(); | ||
}; | ||
renderFrameContents() { | ||
if (!this._isMounted) { | ||
return; | ||
return null; | ||
} | ||
const doc = this.getDoc(); | ||
if (doc && doc.readyState === 'complete') { | ||
if (doc.querySelector('div') === null) { | ||
this._setInitialContent = false; | ||
} | ||
const win = doc.defaultView || doc.parentView; | ||
const initialRender = !this._setInitialContent; | ||
const contents = ( | ||
if (doc.querySelector('.frame-content') === null) { | ||
this._setInitialContent = false; | ||
} | ||
const contentDidMount = this.props.contentDidMount; | ||
const contentDidUpdate = this.props.contentDidUpdate; | ||
const win = doc.defaultView || doc.parentView; | ||
const initialRender = !this._setInitialContent; | ||
const contents = ( | ||
<Content contentDidMount={contentDidMount} contentDidUpdate={contentDidUpdate}> | ||
<DocumentContext document={doc} window={win}> | ||
<div className="frame-content"> | ||
{this.props.head} | ||
{this.props.children} | ||
</div> | ||
</DocumentContext> | ||
); | ||
</Content> | ||
); | ||
if (initialRender) { | ||
doc.open('text/html', 'replace'); | ||
doc.write(this.props.initialContent); | ||
doc.close(); | ||
this._setInitialContent = true; | ||
} | ||
if (initialRender) { | ||
doc.open('text/html', 'replace'); | ||
doc.write(this.props.initialContent); | ||
doc.close(); | ||
this._setInitialContent = true; | ||
} | ||
swallowInvalidHeadWarning(); | ||
const mountTarget = this.getMountTarget(); | ||
// unstable_renderSubtreeIntoContainer allows us to pass this component as | ||
// the parent, which exposes context to any child components. | ||
const callback = initialRender ? this.props.contentDidMount : this.props.contentDidUpdate; | ||
const mountTarget = this.getMountTarget(); | ||
ReactDOM.unstable_renderSubtreeIntoContainer(this, contents, mountTarget, callback); | ||
resetWarnings(); | ||
} else { | ||
setTimeout(this.renderFrameContents.bind(this), 0); | ||
} | ||
return [ | ||
ReactDOM.createPortal(this.props.head, this.getDoc().head), | ||
ReactDOM.createPortal(contents, mountTarget) | ||
]; | ||
} | ||
@@ -145,4 +128,8 @@ | ||
delete props.contentDidUpdate; | ||
return (<iframe {...props} />); | ||
return ( | ||
<iframe {...props} ref={node => (this.node = node)}> | ||
{this.renderFrameContents()} | ||
</iframe> | ||
); | ||
} | ||
} |
@@ -67,6 +67,6 @@ import React from 'react'; | ||
); | ||
const body = ReactDOM.findDOMNode(frame).contentDocument.body; | ||
const head = ReactDOM.findDOMNode(frame).contentDocument.head; | ||
expect(body.querySelector('link')).to.be.defined; | ||
expect(body.querySelector('link').href).to.contain('styles.css'); | ||
expect(head.querySelector('link')).to.be.defined; | ||
expect(head.querySelector('link').href).to.contain('styles.css'); | ||
}); | ||
@@ -83,6 +83,7 @@ | ||
); | ||
const head = ReactDOM.findDOMNode(frame).contentDocument.head; | ||
const body = ReactDOM.findDOMNode(frame).contentDocument.body; | ||
expect(body.querySelector('script')).to.be.defined; | ||
expect(body.querySelector('script').src).to.contain('foo.js'); | ||
expect(head.querySelector('script')).to.be.defined; | ||
expect(head.querySelector('script').src).to.contain('foo.js'); | ||
expect(frame.props.children).to.be.defined; | ||
@@ -102,6 +103,6 @@ expect(body.querySelectorAll('h1,h2').length).to.equal(2); | ||
/>, div); | ||
const body = ReactDOM.findDOMNode(frame).contentDocument.body; | ||
const head = ReactDOM.findDOMNode(frame).contentDocument.head; | ||
expect(body.querySelectorAll('link').length).to.equal(2); | ||
expect(body.querySelectorAll('script').length).to.equal(1); | ||
expect(head.querySelectorAll('link').length).to.equal(2); | ||
expect(head.querySelectorAll('script').length).to.equal(1); | ||
}); | ||
@@ -251,3 +252,3 @@ | ||
expect(didMount.callCount).to.equal(1); | ||
expect(didUpdate.callCount).to.equal(0); | ||
expect(didUpdate.callCount).to.equal(1); | ||
done(); | ||
@@ -287,3 +288,3 @@ }); | ||
<ul className="container"> | ||
<li key="1"> | ||
<li> | ||
<Frame> | ||
@@ -293,3 +294,3 @@ <p>Text 1</p> | ||
</li> | ||
<li key="2"> | ||
<li> | ||
<Frame> | ||
@@ -309,3 +310,3 @@ <p>Text 2</p> | ||
<ul className="container"> | ||
<li key="2"> | ||
<li> | ||
<Frame> | ||
@@ -315,3 +316,3 @@ <p>Text 2</p> | ||
</li> | ||
<li key="1"> | ||
<li> | ||
<Frame> | ||
@@ -318,0 +319,0 @@ <p>Text 1</p> |
Sorry, the diff of this file is not supported yet
381569
36
20
768