Skip to content

Commit b78a004

Browse files
author
Ives van Hoorne
committed
Component progress
1 parent 471a0ab commit b78a004

File tree

13 files changed

+237
-142
lines changed

13 files changed

+237
-142
lines changed

packages/react-sandpack/package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"semantic-release": "semantic-release",
3232
"semantic-release-prepare": "ts-node tools/semantic-release-prepare",
3333
"precommit": "lint-staged",
34-
"storybook": "start-storybook"
34+
"storybook": "start-storybook -p 9001 -c .storybook"
3535
},
3636
"lint-staged": {
3737
"{src,test}/**/*.ts": [
@@ -74,13 +74,18 @@
7474
"mapCoverage": true
7575
},
7676
"dependencies": {
77-
"codesandbox-import-utils": "^1.1.21"
77+
"codesandbox-import-utils": "^1.1.21",
78+
"react-broadcast": "^0.6.2"
79+
},
80+
"peerDependencies": {
81+
"react": "^16.0.0"
7882
},
7983
"devDependencies": {
8084
"@storybook/addon-knobs": "^3.3.14",
8185
"@storybook/react": "^3.3.14",
8286
"@types/jest": "^22.0.0",
8387
"@types/node": "^9.4.6",
88+
"@types/react-broadcast": "^0.6.0",
8489
"awesome-typescript-loader": "^3.5.0",
8590
"colors": "^1.1.2",
8691
"commitizen": "^2.9.6",
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as React from 'react';
2+
3+
import SandpackConsumer from '../SandpackConsumer';
4+
import Preview from '../Preview';
5+
6+
export default class BrowserPreview extends React.PureComponent {
7+
render() {
8+
return (
9+
<div>
10+
This is the navigation
11+
<Preview />
12+
</div>
13+
);
14+
}
15+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import BrowserPreview from './BrowserPreview';
2+
3+
export default BrowserPreview;
Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,47 @@
11
import * as React from 'react';
2-
import { Manager } from 'sandpack';
2+
import SandpackConsumer from '../SandpackConsumer';
33

4-
import { IFileProps } from '../types';
4+
export interface PreviewProps {
5+
browser: HTMLIFrameElement;
6+
}
7+
8+
class Preview extends React.PureComponent<PreviewProps> {
9+
container?: HTMLDivElement;
510

6-
export default class Preview extends React.PureComponent<IFileProps> {
7-
static defaultProps = {
8-
dependencies: {},
9-
entry: '/index.js',
10-
sandboxUrl: 'https://sandbox.codesandbox.io',
11+
setContainerElement = (el: HTMLDivElement) => {
12+
this.container = el;
1113
};
1214

13-
manager?: Manager;
15+
initializeFrame = () => {
16+
const { browser } = this.props;
1417

15-
setupFrame = (el: HTMLIFrameElement) => {
16-
if (el) {
17-
this.manager = new Manager(
18-
el,
19-
{
20-
dependencies: this.props.dependencies,
21-
files: this.props.files,
22-
entry: this.props.entry,
23-
},
24-
{
25-
sandboxUrl: this.props.sandboxUrl,
26-
}
27-
);
28-
}
29-
};
18+
if (browser && this.container) {
19+
browser.style.width = '100%';
20+
browser.style.height = '500px';
21+
browser.style.visibility = 'visible';
22+
browser.style.position = 'relative';
3023

31-
sendCode = () => {
32-
if (this.manager) {
33-
this.manager.sendCode(
34-
this.props.files,
35-
this.props.dependencies,
36-
this.props.entry
37-
);
24+
this.container.appendChild(browser);
3825
}
3926
};
4027

41-
componentDidUpdate(prevProps: IFileProps) {
42-
if (
43-
this.props.files !== prevProps.files ||
44-
this.props.dependencies !== prevProps.dependencies ||
45-
this.props.entry !== prevProps.entry
46-
) {
47-
this.sendCode();
28+
componentDidUpdate(prevProps: PreviewProps) {
29+
if (prevProps.browser !== this.props.browser) {
30+
this.initializeFrame();
4831
}
4932
}
5033

34+
render() {
35+
return <div ref={this.setContainerElement} />;
36+
}
37+
}
38+
39+
export default class ContainerElement extends React.PureComponent {
5140
render() {
5241
return (
53-
<iframe
54-
style={{
55-
width: this.props.width || '100%',
56-
height: this.props.height || '500px',
57-
border: 'none',
58-
}}
59-
src={this.props.sandboxUrl}
60-
ref={this.setupFrame}
61-
/>
42+
<SandpackConsumer>
43+
{state => <Preview browser={state.browserFrame} />}
44+
</SandpackConsumer>
6245
);
6346
}
6447
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import * as React from 'react';
2+
import { Subscriber } from 'react-broadcast';
3+
4+
import { ISandpackContext } from '../types';
5+
6+
export interface Props {
7+
children: (state: ISandpackContext) => React.ReactNode;
8+
}
9+
10+
export default class SandpackConsumer extends React.PureComponent<Props> {
11+
render() {
12+
return <Subscriber channel="sandpack">{this.props.children}</Subscriber>;
13+
}
14+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import SandpackConsumer from './SandpackConsumer';
2+
3+
export default SandpackConsumer;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import * as React from 'react';
2+
import { Broadcast } from 'react-broadcast';
3+
import { Manager } from 'sandpack';
4+
5+
import { IFileProps, IFiles } from '../types';
6+
7+
export interface State {
8+
files: IFiles;
9+
}
10+
11+
export default class SandpackProvider extends React.PureComponent<
12+
IFileProps,
13+
State
14+
> {
15+
manager?: Manager;
16+
iframe?: HTMLIFrameElement;
17+
18+
constructor(props: IFileProps) {
19+
super(props);
20+
21+
this.state = {
22+
files: props.files,
23+
};
24+
}
25+
26+
setupFrame = (el: HTMLIFrameElement) => {
27+
this.manager = new Manager(el, {
28+
files: this.props.files,
29+
entry: this.props.entry,
30+
dependencies: this.props.dependencies,
31+
});
32+
33+
this.iframe = el;
34+
};
35+
36+
updateFiles = (files: IFiles) => {
37+
this.setState({ files });
38+
39+
if (this.manager) {
40+
this.manager.sendCode(files, {}, '/index.js');
41+
}
42+
};
43+
44+
componentDidUpdate(props: IFileProps) {
45+
if (props.files !== this.props.files) {
46+
this.updateFiles(this.props.files);
47+
}
48+
}
49+
50+
render() {
51+
const { children } = this.props;
52+
const { files } = this.state;
53+
return (
54+
<Broadcast
55+
channel="sandpack"
56+
value={{
57+
files,
58+
updateFiles: this.updateFiles,
59+
browserFrame: this.iframe,
60+
}}
61+
>
62+
<div className="sandpack">
63+
{/* We create a hidden iframe, the bundler will live in this. */}
64+
<iframe
65+
ref={this.setupFrame}
66+
style={{
67+
width: 0,
68+
height: 0,
69+
border: 0,
70+
outline: 0,
71+
position: 'absolute',
72+
visibility: 'hidden',
73+
}}
74+
src="http://localhost:3001"
75+
/>
76+
{children}
77+
</div>
78+
</Broadcast>
79+
);
80+
}
81+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import SandpackProvider from './SandpackProvider';
2+
3+
export default SandpackProvider;
Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1-
export interface IFileProps {
2-
files: {
3-
[path: string]: {
4-
code: string;
5-
};
1+
export interface IFiles {
2+
[path: string]: {
3+
code: string;
64
};
5+
}
6+
7+
export interface IFileProps {
8+
files: IFiles;
79
entry: string;
810
dependencies: {
9-
[dep: string]: string;
11+
[depName: string]: string;
1012
};
1113
width?: number | string;
1214
height?: number | string;
1315
sandboxUrl: string;
1416
}
17+
18+
export interface ISandpackContext {
19+
browserFrame: HTMLIFrameElement;
20+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
3+
import { storiesOf, setAddon } from '@storybook/react';
4+
import JSXAddon from 'storybook-addon-jsx';
5+
import { withKnobs, text, object } from '@storybook/addon-knobs/react';
6+
7+
import SandpackProvider from '../src/components/SandpackProvider/index.ts';
8+
import BrowserPreview from '../src/components/BrowserPreview/index.ts';
9+
10+
setAddon(JSXAddon);
11+
12+
const stories = storiesOf('InTheWild', module);
13+
14+
stories.addDecorator(withKnobs);
15+
16+
stories.addWithJSX('with one file', () => (
17+
<SandpackProvider
18+
files={{
19+
'/index.js': {
20+
code: text(
21+
'code',
22+
`document.body.innerHTML = \`<div>$\{require('uuid')()}</div>\``
23+
),
24+
},
25+
}}
26+
dependencies={object('dependencies', {
27+
uuid: 'latest',
28+
})}
29+
>
30+
<BrowserPreview />
31+
</SandpackProvider>
32+
));

0 commit comments

Comments
 (0)