Skip to content
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

Viewport Addon #1740

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions addons/viewport/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.sw*
node_modules
dist
jest
jest_0
1 change: 1 addition & 0 deletions addons/viewport/.storybook/addons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@storybook/addon-viewport/register';
3 changes: 3 additions & 0 deletions addons/viewport/.storybook/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as storybook from '@storybook/react';

storybook.configure(() => require('./stories'), module);
9 changes: 9 additions & 0 deletions addons/viewport/.storybook/stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';
import { storiesOf } from '@storybook/react';

storiesOf('Viewport', module)
.add('Example', () => (
<h1>
Change viewport sizes below
</h1>
));
3 changes: 3 additions & 0 deletions addons/viewport/manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const manager = require('./dist/manager');

manager.init();
35 changes: 35 additions & 0 deletions addons/viewport/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "@storybook/addon-viewport",
"version": "1.0.0",
"description": "Storybook addon to change the viewport size to mobile",
"main": "dist/index.js",
"keywords": [
"storybook"
],
"scripts": {
"build": "babel --source-maps --out-dir dist src",
"watch": "npm run build -- -w",
"prepublish": "npm run build",
"storybook": "start-storybook -p 3000",
"deploy-storybook": "storybook-to-ghpages"
},
"license": "MIT",
"dependencies": {
"@storybook/react": "^3.2.5",
"prop-types": "^15.5.10"
},
"peerDependencies": {
"@storybook/addons": "^3.2.0",
"react": "*"
},
"devDependencies": {
"@storybook/addons": "^3.2.0",
"babel-cli": "^6.24.1",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-es2017": "^6.24.1",
"babel-preset-react": "^6.24.1",
"react": "^15.6.1"
}
}
3 changes: 3 additions & 0 deletions addons/viewport/register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// NOTE: loading addons using this file is deprecated!
// please use manager.js and preview.js files instead
require('./manager');
125 changes: 125 additions & 0 deletions addons/viewport/src/components/Panel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { viewports, defaultViewport, resetViewport } from './viewportInfo';

import { SelectViewport } from './SelectViewport';
import { RotateViewport } from './RotateViewport';

const storybookIframe = 'storybook-preview-iframe';
const containerStyles = {
padding: 15,
width: '100%',
boxSizing: 'border-box',
fontFamily:
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", Arial, sans-serif',
};

import * as styles from './styles';

export class Panel extends Component {
static propTypes = {
channel: PropTypes.object.isRequired,
};

constructor(props, context) {
super(props, context);
this.state = {
viewport: defaultViewport,
isLandscape: false,
};

this.props.channel.on('addon:viewport:update', this.changeViewport);
}

componentDidMount() {
this.iframe = document.getElementById(storybookIframe);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOL 🤣

}

iframe = undefined;

changeViewport = viewport => {
const { viewport: previousViewport, isLandscape } = this.state;

if (previousViewport !== viewport) {
this.setState(
{
viewport,
isLandscape: false,
},
this.updateIframe
);
} else {
this.updateIframe();
}
};

toggleLandscape = () => {
const { isLandscape } = this.state;

// TODO simplify the state management
// ideally we simply dispatch an action to the iframe
this.setState(
{
isLandscape: !isLandscape,
},
() => {
this.changeViewport(this.state.viewport);
}
);
};

updateIframe() {
const { viewport: viewportKey, isLandscape } = this.state;
const viewport = viewports[viewportKey] || resetViewport;

if (!this.iframe) {
throw new Error('Cannot find Storybook iframe');
}

Object.keys(viewport.styles).forEach(prop => {
this.iframe.style[prop] = viewport.styles[prop];
});

if (isLandscape) {
this.iframe.style.height = viewport.styles.width;
this.iframe.style.width = viewport.styles.height;
}
}

render() {
const { isLandscape, viewport } = this.state;

const disableDefault = viewport === defaultViewport;
const disabledStyles = disableDefault ? styles.disabled : {};

const buttonStyles = {
...styles.button,
...disabledStyles,
marginTop: 30,
padding: 20,
};

return (
<div style={containerStyles}>
<SelectViewport
activeViewport={viewport}
onChange={e => this.changeViewport(e.target.value)}
/>

<RotateViewport
onClick={this.toggleLandscape}
disabled={disableDefault}
active={isLandscape}
/>

<button
style={buttonStyles}
onClick={() => this.changeViewport(defaultViewport)}
disabled={disableDefault}
>
Reset Viewport
</button>
</div>
);
}
}
31 changes: 31 additions & 0 deletions addons/viewport/src/components/RotateViewport.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import * as styles from './styles';

export class RotateViewport extends Component {
static propTypes = {
disabled: PropTypes.bool,
onClick: PropTypes.func.isRequired,
active: PropTypes.bool,
};

render() {
const { active, ...props } = this.props;

const disabledStyles = props.disabled ? styles.disabled : {};
const actionStyles = {
...styles.action,
...disabledStyles,
};

return (
<div style={styles.row}>
<label style={styles.label}>Rotate</label>

<button {...props} style={actionStyles}>
{active ? 'Vertical' : 'Landscape'}
</button>
</div>
);
}
}
31 changes: 31 additions & 0 deletions addons/viewport/src/components/SelectViewport.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';

import { viewports, defaultViewport } from './viewportInfo';
import * as styles from './styles';

export class SelectViewport extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
activeViewport: PropTypes.string.isRequired,
};

render() {
const { activeViewport, onChange } = this.props;
return (
<div style={styles.row}>
<label style={styles.label}>Device</label>

<select style={styles.action} value={activeViewport} onChange={onChange}>
<option value={defaultViewport}>Default</option>

{Object.keys(viewports).map(key =>
<option value={key} key={key}>
{viewports[key].name}
</option>
)}
</select>
</div>
);
}
}
26 changes: 26 additions & 0 deletions addons/viewport/src/components/WrapStory.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { Component } from 'react';
import addons from '@storybook/addons';
import PropTypes from 'prop-types';

export class WrapStory extends Component {
static propTypes = {
channel: PropTypes.object.isRequired,
context: PropTypes.object,
storyFn: PropTypes.func,
};

static defaultProps = {
context: {},
storyFn: context => context,
};

componentDidMount() {
const { channel } = this.props;
channel.emit('addon:viewport:update', 'reset');
}

render() {
const { storyFn, context } = this.props;
return storyFn(context);
}
}
30 changes: 30 additions & 0 deletions addons/viewport/src/components/styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export const row = {
width: '100%',
display: 'flex',
marginBottom: 15,
};

export const label = {
width: 80,
marginRight: 15,
};

const actionColor = 'rgb(247, 247, 247)';

export const button = {
color: 'rgb(85, 85, 85)',
width: '100%',
border: `1px solid ${actionColor}`,
backgroundColor: actionColor,
borderRadius: 3,
};

export const disabled = {
opacity: '0.5',
cursor: 'not-allowed',
};

export const action = {
...button,
height: 30,
};
78 changes: 78 additions & 0 deletions addons/viewport/src/components/viewportInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const configuredStyles = {
border: '1px solid #728099',
display: 'flex',
margin: '0 auto',
boxShadow: 'rgba(0,0,0,0.2) 0px 0px 60px 12px',
};

export const defaultViewport = 'default';
export const resetViewport = {
name: 'Reset',
styles: {
width: '100%',
height: '100%',
border: 'none',
display: 'block',
margin: '0',
boxShadow: 'none',
},
};

export const viewports = {
iphone5: {
name: 'iPhone 5',
styles: {
height: '568px',
width: '320px',
...configuredStyles,
},
},
iphone6: {
name: 'iPhone 6',
styles: {
height: '667px',
width: '375px',
...configuredStyles,
},
},
iphone6p: {
name: 'iPhone 6 Plus',
styles: {
height: '736px',
width: '414px',
...configuredStyles,
},
},
ipad: {
name: 'iPad',
styles: {
height: '1024px',
width: '768px',
...configuredStyles,
},
},
galaxys5: {
name: 'Galaxy S5',
styles: {
height: '640px',
width: '360px',
...configuredStyles,
},
},
nexus5x: {
name: 'Nexus 5X',
styles: {
height: '660px',
width: '412px',
...configuredStyles,
},
},
nexus6p: {
name: 'Nexus 6P',
styles: {
height: '732px',
width: '412px',
...configuredStyles,
},
},
};
1 change: 1 addition & 0 deletions addons/viewport/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { register } from './manager';
Loading