Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
import React from 'react';
import sinon, { spy, stub } from 'sinon';
import sinonChai from 'sinon-chai';
import { shallow, render } from 'enzyme';
import chai, { expect } from 'chai';
import chaiEnzyme from 'chai-enzyme';
import proxyquire from 'proxyquire';
chai.use(chaiEnzyme());
chai.use(sinonChai);
const WINDOW = '../facades/window';
const UUID = '../util/uuid';
describe('ContentBodyIframe', () => {
let mocks;
let ContentBodyIframe;
beforeEach(() => {
mocks = {
[UUID]: {
default: sinon.stub()
},
[WINDOW]: {
addEventListener: sinon.stub(),
removeEventListener: sinon.stub()
}
};
ContentBodyIframe = proxyquire('../content-body-iframe', mocks).default;
});
describe('render', () => {
let wrapper;
const uuid = 'foobar!';
const id = '123';
const props = {
baseUrl: 'http://www.atlassian.com',
content: {id},
onContentLoaded: sinon.spy()
};
beforeEach(() => {
mocks[UUID].default.returns(uuid);
wrapper = shallow(<ContentBodyIframe {...props} />);
});
it('Should render a iframe', () => {
expect(wrapper).to.have.descendants('iframe');
const iframe = wrapper.find('iframe');
expect(iframe).to.have.prop('src').equal(`${props.baseUrl}/confluence/content-only/viewpage.action?pageId=${id}&iframeId=${uuid}`);
expect(iframe).to.have.prop('onLoad').equal(props.onContentLoaded);
});
it('if `contextPath` prop not passed it is "/confluence"', () => {
expect(wrapper.find('iframe')).to.have.prop('src').equal(`${props.baseUrl}/confluence/content-only/viewpage.action?pageId=${id}&iframeId=${uuid}`);
});
it('use passed `contextPath` for iframe', () => {
wrapper.setProps({contextPath: '/wiki'});
expect(wrapper.find('iframe')).to.have.prop('src').equal(`${props.baseUrl}/wiki/content-only/viewpage.action?pageId=${id}&iframeId=${uuid}`);
wrapper.setProps({contextPath: ''});
expect(wrapper.find('iframe')).to.have.prop('src').equal(`${props.baseUrl}/content-only/viewpage.action?pageId=${id}&iframeId=${uuid}`);
});
});
describe('componentDidMount', () => {
let wrapper;
let instance;
let uuid;
beforeEach(() => {
uuid = 'barfoo';
mocks[UUID].default.returns(uuid);
wrapper = shallow(<ContentBodyIframe baseUrl="" content={{}}/>);
instance = wrapper.instance();
instance.componentDidMount();
});
it('Should setup a message event listener', () => {
expect(mocks[WINDOW].addEventListener).to.have.been.calledWith('message', instance.receiveMessage);
});
describe('receiveMessage handler', () => {
let receiveMessage;
beforeEach(() => {
receiveMessage = mocks[WINDOW].addEventListener.getCall(0).args[1];
});
describe(`When iframeId doesn't match the iframeId of the component`, () => {
it('Should return undefined', () => {
expect(receiveMessage({
data: {
iframeId: 'something',
height: 1234
}
})).to.equal();
});
});
describe('When event iframeId matches the iframeId of the component', () => {
it('Should setState with the height in the message', () => {
const height = 599;
receiveMessage({
data: {
iframeId: uuid,
height
}
});
expect(wrapper).to.have.state('height', `${height}px`);
});
});
});
describe('componentWillUnmount', () => {
beforeEach(() => {
instance.componentWillUnmount();
});
it('Should remove the message event listener', () => {
expect(mocks[WINDOW].removeEventListener).to.have.been.calledWith('message', instance.receiveMessage);
});
});
});
});
import React, { Component, PropTypes } from 'react';
import uuid from '../util/uuid';
import { addEventListener, removeEventListener } from '../facades/window';
const DEFAULT_CONTEXT_PATH = '/confluence';
export default class ContentBodyIframe extends Component {
constructor() {
super();
this.iframeId = uuid();
this.state = {
height: '1000px'
};
}
componentDidMount() {
this.receiveMessage = event => {
const { iframeId } = event.data;
if (!iframeId || iframeId !== this.iframeId) {
return;
}
const { height } = event.data;
this.setState({
height: `${height}px`
});
};
addEventListener('message', this.receiveMessage);
}
componentWillUnmount() {
removeEventListener('message', this.receiveMessage);
}
render() {
const { content, baseUrl, onContentLoaded } = this.props;
let { contextPath } = this.props;
const { height } = this.state;
if (typeof contextPath === 'undefined') {
contextPath = DEFAULT_CONTEXT_PATH;
}
const contentUrl = `${baseUrl}${contextPath}/content-only/viewpage.action?pageId=${content.id}&iframeId=${this.iframeId}`;
return (
<iframe
src={contentUrl}
border="0"
style={{border:0, width: '100%', height}}
onLoad={onContentLoaded}
/>
);
}
}
ContentBodyIframe.displayName = 'ContentBodyIframe';
ContentBodyIframe.defaultProps = {
baseUrl: ''
};
ContentBodyIframe.propTypes = {
/**
* The ID of the content to render.
*/
contentId: PropTypes.string,
/**
* Host of confluence instance for the iframe src attribute
*/
baseUrl: PropTypes.string,
/**
* Confluence instance context path
*/
contextPath: PropTypes.string,
/**
* Callback when iframe finishes loading.
*/
onContentLoaded: PropTypes.func
};
// https://bitbucket.org/atlassian/confluence-react-components/src/master/src/content-body-rest/content-body-rest.js
import React, { Component, PropTypes } from 'react';
import { createElement } from '../facades/document';
export default class ContentBodyRest extends Component {
constructor() {
super();
this._setScriptContainerRef = this._setScriptContainerRef.bind(this);
this._setBodyContainerRef = this._setBodyContainerRef.bind(this);
}
componentDidMount() {
this._updateAnchorTargets();
this._loadJSDependencies();
}
componentDidUpdate() {
this._updateAnchorTargets();
this._loadJSDependencies();
}
_updateAnchorTargets() {
const anchors = this._bodyContainer.querySelectorAll('a');
for (let i = 0, anchor; anchor = anchors[i]; i++) {
anchor.target = '_top';
}
}
_loadJSDependencies() {
const content = this.props.content;
content.jsDependencies.forEach(this._appendScriptToContainer, this);
}
_appendScriptToContainer(uri) {
const scriptElement = createElement('script');
scriptElement.async = true;
scriptElement.src = uri;
this._scriptContainer.appendChild(scriptElement);
}
_setBodyContainerRef(node) {
this._bodyContainer = node;
}
_setScriptContainerRef(node) {
this._scriptContainer = node;
}
render() {
const { body, cssDependencies } = this.props.content;
return (
<div id="content" className="page view">
<div dangerouslySetInnerHTML={{ __html: `${body}${cssDependencies}` }} ref={this._setBodyContainerRef} id="main-content" className="wiki-content"></div>
<div ref={this._setScriptContainerRef}></div>
</div>
);
}
}
ContentBodyRest.displayName = 'ContentBodyRest';
ContentBodyRest.propTypes = {
/**
* The ID of the content to render.
*/
contentId: PropTypes.string
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment