├── .babelrc
├── .editorconfig
├── .eslintrc
├── .github
├── FUNDING.yml
└── workflows
│ └── ci.yml
├── .gitignore
├── .npmignore
├── LICENSE.md
├── README.md
├── example
├── app.jsx
└── index.html
├── index.d.ts
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
├── Content.jsx
├── Context.jsx
├── Frame.jsx
└── index.js
├── test
├── .eslintrc
├── Content.spec.jsx
├── Context.spec.jsx
└── Frame.spec.jsx
├── wallaby.conf.js
├── webpack.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "latest", "stage-2"],
3 | "plugins": "transform-class-properties"
4 | }
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["airbnb", "prettier"],
3 | "parserOptions": {
4 | "ecmaVersion": 7,
5 | "sourceType": "module",
6 | "ecmaFeatures": {
7 | "impliedStrict": true,
8 | "jsx": true
9 | }
10 | },
11 | "env": {
12 | "browser": true
13 | },
14 | "parser": "babel-eslint",
15 | "rules": {
16 | "react/forbid-prop-types": "off",
17 | "react/prop-types": [0, { "ignore": ["children"]}],
18 | "no-underscore-dangle": "off"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: ryanseddon
4 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | on: [push, pull_request]
2 | jobs:
3 | build:
4 | runs-on: ubuntu-latest
5 | steps:
6 | - uses: actions/checkout@v2
7 | - uses: actions/setup-node@v1
8 | with:
9 | node-version: 14
10 | - run: npm install
11 |
12 | - name: Run headless test
13 | uses: GabrielBB/xvfb-action@v1
14 | with:
15 | run: npm test
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | test/processed
2 | node_modules
3 | lib/
4 | npm-debug.log
5 | test-results.xml
6 | *-debug.log*
7 | dist
8 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test-results.xml
2 | wallaby.conf.js
3 | karma.conf.js
4 | .travis.yml
5 | *-debug.log*
6 | example/
7 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Ryan Seddon
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React <Frame /> component
2 |
3 | [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][depstat-image]][depstat-url]
4 |
5 | This component allows you to encapsulate your entire React application or per component in an iFrame.
6 |
7 | ```bash
8 | npm install --save react-frame-component
9 | ```
10 |
11 | ## How to use:
12 |
13 | ```js
14 | import Frame from 'react-frame-component';
15 | ```
16 |
17 | Go check out the [demo][demo-url].
18 |
19 | ```html
20 | const Header = ({ children }) => (
21 |
22 |
{children}
23 |
24 | );
25 |
26 | ReactDOM.render(Hello, document.body);
27 | ```
28 |
29 | Or you can wrap it at the `render` call.
30 |
31 | ```html
32 | ReactDOM.render(
33 |
34 | Hello
35 | ,
36 | document.body
37 | );
38 | ```
39 |
40 | ##### Props:
41 |
42 | ###### head
43 | `head: PropTypes.node`
44 |
45 | The `head` prop is a dom node that gets inserted before the children of the frame. Note that this is injected into the body of frame (see the blog post for why). This has the benefit of being able to update and works for stylesheets.
46 |
47 | ###### initialContent
48 | `initialContent: PropTypes.string`
49 |
50 | Defaults to `''`
51 |
52 | The `initialContent` props is the initial html injected into frame. It is only injected once, but allows you to insert any html into the frame (e.g. a head tag, script tags, etc). Note that it does *not* update if you change the prop. Also at least one div is required in the body of the html, which we use to render the react dom into.
53 |
54 | ###### mountTarget
55 | `mountTarget: PropTypes.string`
56 |
57 | The `mountTarget` props is a css selector (#target/.target) that specifies where in the `initialContent` of the iframe, children will be mounted.
58 |
59 | ```html
60 |
64 |
65 | ```
66 |
67 | ###### contentDidMount and contentDidUpdate
68 | `contentDidMount: PropTypes.func`
69 | `contentDidUpdate: PropTypes.func`
70 |
71 | `contentDidMount` and `contentDidUpdate` are conceptually equivalent to
72 | `componentDidMount` and `componentDidUpdate`, respectively. The reason these are
73 | needed is because internally we call `ReactDOM.render` which starts a new set of
74 | lifecycle calls. This set of lifecycle calls are sometimes triggered after the
75 | lifecycle of the parent component, so these callbacks provide a hook to know
76 | when the frame contents are mounted and updated.
77 |
78 | ###### ref
79 | `ref: PropTypes.oneOfType([ PropTypes.func, PropTypes.shape({ current: PropTypes.instanceOf(Element) }) ])`
80 |
81 | The `ref` prop provides a way to access inner iframe DOM node. To utilitize this prop use, for example, one of the React's built-in methods to create a ref: [`React.createRef()`](https://reactjs.org/docs/refs-and-the-dom.html#creating-refs) or [`React.useRef()`](https://reactjs.org/docs/hooks-reference.html#useref).
82 |
83 | ```js
84 | const MyComponent = (props) => {
85 | const iframeRef = React.useRef();
86 |
87 | React.useEffect(() => {
88 | // Use iframeRef for:
89 | // - focus managing
90 | // - triggering imperative animations
91 | // - integrating with third-party DOM libraries
92 | iframeRef.current.focus()
93 | }, [])
94 |
95 | return (
96 |
97 |
98 |
99 | );
100 | }
101 | ```
102 |
103 | ##### Accessing the iframe's window and document
104 | The iframe's `window` and `document` may be accessed via the `FrameContextConsumer` or the `useFrame` hook.
105 |
106 | The example with `FrameContextConsumer`:
107 |
108 | ```js
109 | import Frame, { FrameContextConsumer } from 'react-frame-component'
110 |
111 | const MyComponent = (props, context) => (
112 |
113 |
114 | {
115 | // Callback is invoked with iframe's window and document instances
116 | ({document, window}) => {
117 | // Render Children
118 | }
119 | }
120 |
121 |
122 | );
123 |
124 | ```
125 |
126 | The example with `useFrame` hook:
127 |
128 | ```js
129 | import Frame, { useFrame } from 'react-frame-component';
130 |
131 | const InnerComponent = () => {
132 | // Hook returns iframe's window and document instances from Frame context
133 | const { document, window } = useFrame();
134 |
135 | return null;
136 | };
137 |
138 | const OuterComponent = () => (
139 |
140 |
141 |
142 | );
143 | ```
144 |
145 | ## More info
146 |
147 | I wrote a [blog post][blog-url] about building this component.
148 |
149 | ## License
150 |
151 | Copyright 2014, Ryan Seddon.
152 | This content is released under the MIT license http://ryanseddon.mit-license.org
153 |
154 | [npm-url]: https://npmjs.org/package/react-frame-component
155 | [npm-image]: https://badge.fury.io/js/react-frame-component.png
156 |
157 | [travis-url]: http://travis-ci.org/ryanseddon/react-frame-component
158 | [travis-image]: https://secure.travis-ci.org/ryanseddon/react-frame-component.png?branch=master
159 |
160 | [depstat-url]: https://david-dm.org/ryanseddon/react-frame-component
161 | [depstat-image]: https://david-dm.org/ryanseddon/react-frame-component.png
162 |
163 | [demo-url]: http://ryanseddon.github.io/react-frame-component/
164 | [blog-url]: https://medium.com/@ryanseddon/rendering-to-iframes-in-react-d1cb92274f86
165 |
--------------------------------------------------------------------------------
/example/app.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Frame from '../src';
4 |
5 | const styles = {
6 | border: '1px solid',
7 | width: '100%',
8 | height: '100%'
9 | };
10 |
11 | const Header = ({ children }) =>