-
Notifications
You must be signed in to change notification settings - Fork 260
removes react-iframe-comm dependency #2861
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bde60c4
1e133b5
64410b8
c2c0ec5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| import React from 'react'; | ||
| import { mount } from 'enzyme'; | ||
| import { IframeComm } from '../../../src/lib/IFrameComm'; | ||
|
|
||
| /** | ||
| * | ||
| * @param props | ||
| * @returns {*} | ||
| */ | ||
| function createWrapper(props) { | ||
| return mount( | ||
| <IframeComm | ||
| handleReady={() => {}} | ||
| handleReceiveMessage={() => {}} | ||
| postMessageData="this is a test" | ||
| {...props} | ||
| />, | ||
| ); | ||
| } | ||
|
|
||
| let receivedMessage = {}; | ||
| const mockIFrameContents = { | ||
| contentWindow: { | ||
| postMessage: (postMessageData) => { | ||
| receivedMessage = postMessageData; | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| describe('IFrameComm', () => { | ||
| let wrapper; | ||
| it('renders properly', () => { | ||
| const handleReady = jest.fn(); | ||
| const handleReceiveMessage = jest.fn(); | ||
| wrapper = createWrapper({ handleReady, handleReceiveMessage }); | ||
| expect(wrapper.find(IframeComm).length).toBe(1); | ||
| wrapper.instance().setIFrameElement(mockIFrameContents); | ||
| wrapper.instance().onLoad(); | ||
| expect(handleReady).toHaveBeenCalled(); | ||
| expect(receivedMessage === 'this is a test').toBeTruthy(); | ||
| wrapper.instance().onReceiveMessage(); | ||
| expect(handleReceiveMessage).toHaveBeenCalled(); | ||
| }); | ||
| it('updates props', () => { | ||
| wrapper = createWrapper(); | ||
| wrapper.instance().setIFrameElement(mockIFrameContents); | ||
| wrapper.instance().onLoad(); | ||
| wrapper.setProps({ postMessageData: 'this is another test' }); | ||
| wrapper.instance().setIFrameElement(mockIFrameContents); | ||
| wrapper.instance().onLoad(); | ||
| expect(receivedMessage === 'this is another test').toBeTruthy(); | ||
| }); | ||
| it('returns data with no serialization option', () => { | ||
| wrapper = createWrapper({ serializeMessage: false }); | ||
| wrapper.instance().setIFrameElement(mockIFrameContents); | ||
| wrapper.instance().onLoad(); | ||
| expect(receivedMessage === 'this is a test').toBeTruthy(); | ||
| }); | ||
| it('returns data with when payload is an object', () => { | ||
| wrapper = createWrapper({ postMessageData: { object: 'this is an object' } }); | ||
| wrapper.instance().setIFrameElement(mockIFrameContents); | ||
| wrapper.instance().onLoad(); | ||
| expect(receivedMessage === '{"object":"this is an object"}').toBeTruthy(); | ||
| }); | ||
| it('ummounts', () => { | ||
| wrapper = createWrapper(); | ||
| const componentWillUnmount = jest.spyOn(wrapper.instance(), 'componentWillUnmount'); | ||
| wrapper.unmount(); | ||
| expect(componentWillUnmount).toHaveBeenCalled(); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,225 @@ | ||
| /** | ||
| MIT License | ||
| Copyright (c) 2017 Petar Bojinov | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. | ||
| */ | ||
|
|
||
| import React, { Component } from 'react'; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is code copied from |
||
| import PropTypes from 'prop-types'; | ||
|
|
||
| /** | ||
| * | ||
| */ | ||
| export class IframeComm extends Component { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the reason for this living in
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lib is usually where compiled code goes so storing source in this directory is odd anyway, but in this case, since it is a "third party library component" (note also that it is a |
||
| /** | ||
| * | ||
| */ | ||
| constructor() { | ||
| super(); | ||
| this.onReceiveMessage = this.onReceiveMessage.bind(this); | ||
| this.onLoad = this.onLoad.bind(this); | ||
| this.sendMessage = this.sendMessage.bind(this); | ||
| this.setIFrameElement = (el) => { | ||
| this.frame = el; | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| */ | ||
| componentDidMount() { | ||
| window.addEventListener('message', this.onReceiveMessage); | ||
| this.frame.addEventListener('load', this.onLoad); | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @param prevProps | ||
| * @param prevState | ||
| */ | ||
| componentDidUpdate(prevProps, prevState) { | ||
| const { postMessageData } = this.props; | ||
| if (prevProps.postMessageData !== postMessageData) { | ||
| // send a message if postMessageData changed | ||
| this.sendMessage(postMessageData); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| */ | ||
| componentWillUnmount() { | ||
| window.removeEventListener('message', this.onReceiveMessage, false); | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @param event | ||
| */ | ||
| onReceiveMessage(event) { | ||
| const { handleReceiveMessage } = this.props; | ||
| if (handleReceiveMessage) { | ||
| handleReceiveMessage(event); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| */ | ||
| onLoad() { | ||
| const { handleReady } = this.props; | ||
| if (handleReady) { | ||
| handleReady(); | ||
| } | ||
| // TODO: Look into doing a syn-ack TCP-like handshake | ||
| // to make sure iFrame is ready to REALLY accept messages, not just loaded. | ||
| // send initial props when iframe loads | ||
| const { postMessageData } = this.props; | ||
| this.sendMessage(postMessageData); | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @param data | ||
| * @returns {string|*} | ||
| */ | ||
| serializePostMessageData(data) { | ||
| // Rely on the browser's built-in structured clone algorithm for serialization of the | ||
| // message as described in | ||
| // https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage | ||
| const { serializeMessage } = this.props; | ||
| if (!serializeMessage) { | ||
| return data; | ||
| } | ||
|
|
||
| // To be on the safe side we can also ignore the browser's built-in serialization feature | ||
| // and serialize the data manually. | ||
| if (typeof data === 'object') { | ||
| return JSON.stringify(data); | ||
| } if (typeof data === 'string') { | ||
| return data; | ||
| } | ||
| return `${data}`; | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @param postMessageData | ||
| */ | ||
| sendMessage(postMessageData) { | ||
| // Using postMessage data from props will result in a subtle but deadly bug, | ||
| // where old data from props is being sent instead of new postMessageData. | ||
| // This is because data sent from componentWillReceiveProps is not yet | ||
| // in props but only in nextProps. | ||
| const { targetOrigin } = this.props; | ||
| const serializedData = this.serializePostMessageData(postMessageData); | ||
| this.frame.contentWindow.postMessage(serializedData, targetOrigin); | ||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @returns {*} | ||
| */ | ||
| render() { | ||
| const { attributes } = this.props; | ||
| // define some sensible defaults for our iframe attributes | ||
| const defaultAttributes = { | ||
| allowFullScreen: false, | ||
| frameBorder: 0, | ||
| }; | ||
| // then merge in the user's attributes with our defaults | ||
| const mergedAttributes = Object.assign( | ||
| {}, | ||
| defaultAttributes, | ||
| attributes, | ||
| ); | ||
| return ( | ||
| <iframe //eslint-disable-line | ||
| ref={this.setIFrameElement} | ||
| {...mergedAttributes} | ||
| /> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| IframeComm.defaultProps = { | ||
| attributes: {}, | ||
| handleReady: {}, | ||
| handleReceiveMessage: {}, | ||
| serializeMessage: true, | ||
| targetOrigin: '*', | ||
| }; | ||
|
|
||
| IframeComm.propTypes = { | ||
| /* | ||
| Iframe Attributes | ||
| https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#Attributes | ||
| React Supported Attributes | ||
| https://facebook.github.io/react/docs/dom-elements.html#all-supported-html-attributes | ||
| Note: attributes are camelCase, not all lowercase as usually defined. | ||
| */ | ||
| attributes: PropTypes.shape({ | ||
| allowFullScreen: PropTypes.oneOfType([ | ||
| PropTypes.string, | ||
| PropTypes.bool, | ||
| ]), | ||
| frameBorder: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | ||
| height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | ||
| name: PropTypes.string, | ||
| sandbox: PropTypes.string, | ||
| scrolling: PropTypes.string, | ||
| // https://www.html5rocks.com/en/tutorials/security/sandboxed-iframes/ | ||
| src: PropTypes.string.isRequired, | ||
| srcDoc: PropTypes.string, | ||
| width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | ||
| }), | ||
| /* | ||
| Callback function called when iframe loads. We're simply listening | ||
| to the iframe's `window.onload`. To ensure communication code in your iframe | ||
| is totally loaded, you can implement a syn-ack TCP-like handshake using | ||
| `postMessageData` and `handleReceiveMessage`. | ||
| */ | ||
| handleReady: PropTypes.func, | ||
| // Callback function called when iFrame sends the parent window a message. | ||
| handleReceiveMessage: PropTypes.func, | ||
|
|
||
| /* | ||
| You can pass it anything you want, we'll serialize to a string | ||
| preferablly use a simple string message or an object. | ||
| If you use an object, you need to follow the same naming convention | ||
| in the iframe so you can parse it accordingly. | ||
| */ | ||
| postMessageData: PropTypes.any.isRequired, //eslint-disable-line | ||
|
|
||
| /* | ||
| Enable use of the browser's built-in structured clone algorithm for serialization | ||
| by settings this to `false`. | ||
| Default is `true`, using our built in logic for serializing everything to a string. | ||
| */ | ||
| serializeMessage: PropTypes.bool, | ||
|
|
||
| /* | ||
| Always provide a specific targetOrigin, not *, if you know where the other window's | ||
| document should be located. Failing to provide a specific target discloses the data | ||
| you send to any interested malicious site. | ||
| */ | ||
| targetOrigin: PropTypes.string, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export * from './IFrameComm'; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this a new pattern that is needed? |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this needed for the replacement of the iframe component? Were the tests failing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no, nothing to do with the component. these puppeteer tests consistently fail for me locally and also sometimes on CI. increasing the timeout solves it.