From a0cc1c90415e900beda8faad630baf5028ce9d99 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 17 Aug 2017 15:26:27 -0700 Subject: [PATCH 1/7] initial port --- addons/viewport/.gitignore | 5 ++ addons/viewport/.storybook/addons.js | 1 + addons/viewport/.storybook/config.js | 2 + addons/viewport/.storybook/stories.js | 7 +++ addons/viewport/manager.js | 2 + addons/viewport/package.json | 27 ++++++++++ addons/viewport/register.js | 3 ++ addons/viewport/src/components/Panel.jsx | 54 +++++++++++++++++++ addons/viewport/src/components/WrapStory.jsx | 21 ++++++++ .../viewport/src/components/viewportInfo.js | 21 ++++++++ addons/viewport/src/index.jsx | 20 +++++++ addons/viewport/src/manager.jsx | 23 ++++++++ 12 files changed, 186 insertions(+) create mode 100644 addons/viewport/.gitignore create mode 100644 addons/viewport/.storybook/addons.js create mode 100644 addons/viewport/.storybook/config.js create mode 100644 addons/viewport/.storybook/stories.js create mode 100644 addons/viewport/manager.js create mode 100644 addons/viewport/package.json create mode 100644 addons/viewport/register.js create mode 100644 addons/viewport/src/components/Panel.jsx create mode 100644 addons/viewport/src/components/WrapStory.jsx create mode 100644 addons/viewport/src/components/viewportInfo.js create mode 100644 addons/viewport/src/index.jsx create mode 100644 addons/viewport/src/manager.jsx diff --git a/addons/viewport/.gitignore b/addons/viewport/.gitignore new file mode 100644 index 000000000000..745197c1619c --- /dev/null +++ b/addons/viewport/.gitignore @@ -0,0 +1,5 @@ +*.sw* +node_modules +dist +jest +jest_0 diff --git a/addons/viewport/.storybook/addons.js b/addons/viewport/.storybook/addons.js new file mode 100644 index 000000000000..339062f226f1 --- /dev/null +++ b/addons/viewport/.storybook/addons.js @@ -0,0 +1 @@ +import '@storybook/addon-viewport/register'; diff --git a/addons/viewport/.storybook/config.js b/addons/viewport/.storybook/config.js new file mode 100644 index 000000000000..29599c59b5d2 --- /dev/null +++ b/addons/viewport/.storybook/config.js @@ -0,0 +1,2 @@ +import * as storybook from '@storybook/react'; +storybook.configure(() => require('./stories'), module); diff --git a/addons/viewport/.storybook/stories.js b/addons/viewport/.storybook/stories.js new file mode 100644 index 000000000000..ad417b1c9d83 --- /dev/null +++ b/addons/viewport/.storybook/stories.js @@ -0,0 +1,7 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { withViewport } from '../src'; + +storiesOf('Viewport Example', module) + .addDecorator(withViewport) + .add('Example', ); diff --git a/addons/viewport/manager.js b/addons/viewport/manager.js new file mode 100644 index 000000000000..68d13f4d0bf1 --- /dev/null +++ b/addons/viewport/manager.js @@ -0,0 +1,2 @@ +const manager = require('./dist/manager'); +manager.init(); diff --git a/addons/viewport/package.json b/addons/viewport/package.json new file mode 100644 index 000000000000..be4f9b3d3930 --- /dev/null +++ b/addons/viewport/package.json @@ -0,0 +1,27 @@ +{ + "name": "@storybook/addon-viewport", + "version": "1.0.0", + "description": "Creates an addon for the storybook action area to change the viewport for mobile screen sizes", + "main": "dist/index.js", + "scripts": { + "build": "babel --source-maps --out-dir dist src", + "watch": "npm run build -- -w", + "prepublish": "npm run build" + }, + "license": "MIT", + "dependencies": { + "prop-types": "^15.5.10" + }, + "peerDependencies": { + "@storybook/addons": "^3.2.0", + "react": "^15.6.1" + }, + "devDependencies": { + "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" + } +} diff --git a/addons/viewport/register.js b/addons/viewport/register.js new file mode 100644 index 000000000000..94af5ec3ba83 --- /dev/null +++ b/addons/viewport/register.js @@ -0,0 +1,3 @@ +// NOTE: loading addons using this file is deprecated! +// please use manager.js and preview.js files instead +require('./manager'); diff --git a/addons/viewport/src/components/Panel.jsx b/addons/viewport/src/components/Panel.jsx new file mode 100644 index 000000000000..6257870b1489 --- /dev/null +++ b/addons/viewport/src/components/Panel.jsx @@ -0,0 +1,54 @@ +import React, { Component } from 'react'; +import { viewports } from './viewportInfo'; + +const storybookIframe = 'storybook-preview-iframe'; +const configuredStyles = { + border: '1px solid #728099', + display: 'flex', + margin: '0 auto', + boxShadow: 'rgba(0,0,0,0.2) 0px 0px 60px 12px', +}; + + +export class Panel extends Component { + static propTypes = { + channel: PropTypes.object.isRequired, + } + + iframe = undefined; + + constructor(props, context) { + super(props, context); + this.state = { viewport: null }; + + this.props.channel.on( + 'addon:viewport:update', + this.changeViewport + ); + } + + componentDidMount() { + this.iframe = document.getElementById(storybookIframe); + } + + setIframeStyles(styles) { + this.iframe.style = styles; + } + + changeViewport = (viewport) => { + const size = viewports[viewport] || viewports.reset; + this.setIframeStyles(viewport.styles); + } + + render() { + return ( +
+ { viewportSizes.map((viewport) => { + + })} +
+ ); + } +} diff --git a/addons/viewport/src/components/WrapStory.jsx b/addons/viewport/src/components/WrapStory.jsx new file mode 100644 index 000000000000..0928e8b11fca --- /dev/null +++ b/addons/viewport/src/components/WrapStory.jsx @@ -0,0 +1,21 @@ +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.isRequired, + storyFn: PropTypes.func.isRequired, + } + + componentDidMount() { + const { channel } = this.props; + channel.emit('addon:viewport:update', 'reset'); + } + + render() { + const { storyFn, context } = this.props; + return storyFn(context); + } +} diff --git a/addons/viewport/src/components/viewportInfo.js b/addons/viewport/src/components/viewportInfo.js new file mode 100644 index 000000000000..0413551c252e --- /dev/null +++ b/addons/viewport/src/components/viewportInfo.js @@ -0,0 +1,21 @@ +export const viewports = { + iphone6: { + name: 'iPhone 6', + styles: { + height: '667px', + width: '375px', + ...configuredStyles, + }, + }, + reset: { + name: 'Reset' + styles: { + width: '100%', + height: '100%', + border: 'none', + display: 'block', + margin: '0', + boxShadow: 'none', + }, + }, +}; diff --git a/addons/viewport/src/index.jsx b/addons/viewport/src/index.jsx new file mode 100644 index 000000000000..927e63c1cdf7 --- /dev/null +++ b/addons/viewport/src/index.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import addons from '@storybook/addons'; + +import { WrapStory } from './components/WrapStory'; + +class ViewportManager { + wrapStory(channel, storyFn, context) { + const props = { context, storyFn, channel }; + return + } +} + +const manager = new ViewportManager(); + +function withViewport(storyFn, context) { + const channel = addons.getChannel(); + return manager.wrapStory(channel, storyFn, context); +} + +export { withViewport }; diff --git a/addons/viewport/src/manager.jsx b/addons/viewport/src/manager.jsx new file mode 100644 index 000000000000..995fb17f66b3 --- /dev/null +++ b/addons/viewport/src/manager.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import addons from '@storybook/addons'; + +import { Panel } from './components/Panel'; + +const ADDON_ID = 'storybook-addon-viewport'; +const PANEL_ID = `${ADDON_ID}/addon-panel`; +const EVENT_ID = `${ADDON_ID}/addon-event`; + +function init() { + addons.register(ADDON_ID, api => { + const channel = addons.getChannel(); + + addons.addPanel(PANEL_ID, { + title: 'Viewport', + render() { + return ; + } + }); + }); +} + +export { init } From b47bc5b3c93697412fbec06698b784e50d22bbe5 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 17 Aug 2017 15:30:13 -0700 Subject: [PATCH 2/7] improve the viewport package.json for integration into the lerna repo for storybook --- addons/viewport/package.json | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/addons/viewport/package.json b/addons/viewport/package.json index be4f9b3d3930..7f07a51d6294 100644 --- a/addons/viewport/package.json +++ b/addons/viewport/package.json @@ -1,12 +1,17 @@ { "name": "@storybook/addon-viewport", "version": "1.0.0", - "description": "Creates an addon for the storybook action area to change the viewport for mobile screen sizes", + "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" + "prepublish": "npm run build", + "storybook": "start-storybook -p 3000", + "deploy-storybook": "storybook-to-ghpages" }, "license": "MIT", "dependencies": { @@ -14,14 +19,16 @@ }, "peerDependencies": { "@storybook/addons": "^3.2.0", - "react": "^15.6.1" + "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" + "babel-preset-react": "^6.24.1", + "react": "^15.6.1" } } From 3eb9cc5dc4f839c88316687d8c632e8ad0afddbd Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 17 Aug 2017 15:51:19 -0700 Subject: [PATCH 3/7] fix most lint errors --- addons/viewport/manager.js | 1 + addons/viewport/src/components/Panel.jsx | 80 ++++++++----------- addons/viewport/src/components/WrapStory.jsx | 26 +++--- .../viewport/src/components/viewportInfo.js | 41 ++++++---- addons/viewport/src/index.jsx | 12 +-- addons/viewport/src/manager.jsx | 19 +++-- 6 files changed, 86 insertions(+), 93 deletions(-) diff --git a/addons/viewport/manager.js b/addons/viewport/manager.js index 68d13f4d0bf1..67d49f75b482 100644 --- a/addons/viewport/manager.js +++ b/addons/viewport/manager.js @@ -1,2 +1,3 @@ const manager = require('./dist/manager'); + manager.init(); diff --git a/addons/viewport/src/components/Panel.jsx b/addons/viewport/src/components/Panel.jsx index 6257870b1489..54150de9c538 100644 --- a/addons/viewport/src/components/Panel.jsx +++ b/addons/viewport/src/components/Panel.jsx @@ -1,54 +1,40 @@ import React, { Component } from 'react'; +import PropTypes from 'prop-types'; import { viewports } from './viewportInfo'; const storybookIframe = 'storybook-preview-iframe'; -const configuredStyles = { - border: '1px solid #728099', - display: 'flex', - margin: '0 auto', - boxShadow: 'rgba(0,0,0,0.2) 0px 0px 60px 12px', -}; - export class Panel extends Component { - static propTypes = { - channel: PropTypes.object.isRequired, - } - - iframe = undefined; - - constructor(props, context) { - super(props, context); - this.state = { viewport: null }; - - this.props.channel.on( - 'addon:viewport:update', - this.changeViewport - ); - } - - componentDidMount() { - this.iframe = document.getElementById(storybookIframe); - } - - setIframeStyles(styles) { - this.iframe.style = styles; - } - - changeViewport = (viewport) => { - const size = viewports[viewport] || viewports.reset; - this.setIframeStyles(viewport.styles); - } - - render() { - return ( -
- { viewportSizes.map((viewport) => { - - })} -
- ); - } + static propTypes = { + channel: PropTypes.object.isRequired, + }; + + constructor(props, context) { + super(props, context); + this.state = { viewport: null }; + + this.props.channel.on('addon:viewport:update', this.changeViewport); + } + + componentDidMount() { + this.iframe = document.getElementById(storybookIframe); + } + + iframe = undefined; + + changeViewport = viewport => { + this.iframe.style = viewport.styles; + }; + + render() { + return ( +
+ {viewports.map(viewport => + + )} +
+ ); + } } diff --git a/addons/viewport/src/components/WrapStory.jsx b/addons/viewport/src/components/WrapStory.jsx index 0928e8b11fca..51d7082eef3e 100644 --- a/addons/viewport/src/components/WrapStory.jsx +++ b/addons/viewport/src/components/WrapStory.jsx @@ -3,19 +3,19 @@ import addons from '@storybook/addons'; import PropTypes from 'prop-types'; export class WrapStory extends Component { - static propTypes = { - channel: PropTypes.object.isRequired, - context: PropTypes.object.isRequired, - storyFn: PropTypes.func.isRequired, - } + static propTypes = { + channel: PropTypes.object.isRequired, + context: PropTypes.object.isRequired, + storyFn: PropTypes.func.isRequired, + }; - componentDidMount() { - const { channel } = this.props; - channel.emit('addon:viewport:update', 'reset'); - } + componentDidMount() { + const { channel } = this.props; + channel.emit('addon:viewport:update', 'reset'); + } - render() { - const { storyFn, context } = this.props; - return storyFn(context); - } + render() { + const { storyFn, context } = this.props; + return storyFn(context); + } } diff --git a/addons/viewport/src/components/viewportInfo.js b/addons/viewport/src/components/viewportInfo.js index 0413551c252e..3b4698c2ddee 100644 --- a/addons/viewport/src/components/viewportInfo.js +++ b/addons/viewport/src/components/viewportInfo.js @@ -1,21 +1,28 @@ +const configuredStyles = { + border: '1px solid #728099', + display: 'flex', + margin: '0 auto', + boxShadow: 'rgba(0,0,0,0.2) 0px 0px 60px 12px', +}; + export const viewports = { - iphone6: { - name: 'iPhone 6', - styles: { - height: '667px', - width: '375px', - ...configuredStyles, - }, + iphone6: { + name: 'iPhone 6', + styles: { + height: '667px', + width: '375px', + ...configuredStyles, }, - reset: { - name: 'Reset' - styles: { - width: '100%', - height: '100%', - border: 'none', - display: 'block', - margin: '0', - boxShadow: 'none', - }, + }, + reset: { + name: 'Reset', + styles: { + width: '100%', + height: '100%', + border: 'none', + display: 'block', + margin: '0', + boxShadow: 'none', }, + }, }; diff --git a/addons/viewport/src/index.jsx b/addons/viewport/src/index.jsx index 927e63c1cdf7..4745fb288a5f 100644 --- a/addons/viewport/src/index.jsx +++ b/addons/viewport/src/index.jsx @@ -4,17 +4,17 @@ import addons from '@storybook/addons'; import { WrapStory } from './components/WrapStory'; class ViewportManager { - wrapStory(channel, storyFn, context) { - const props = { context, storyFn, channel }; - return - } + wrapStory(channel, storyFn, context) { + const props = { context, storyFn, channel }; + return ; + } } const manager = new ViewportManager(); function withViewport(storyFn, context) { - const channel = addons.getChannel(); - return manager.wrapStory(channel, storyFn, context); + const channel = addons.getChannel(); + return manager.wrapStory(channel, storyFn, context); } export { withViewport }; diff --git a/addons/viewport/src/manager.jsx b/addons/viewport/src/manager.jsx index 995fb17f66b3..589ce24808ae 100644 --- a/addons/viewport/src/manager.jsx +++ b/addons/viewport/src/manager.jsx @@ -5,19 +5,18 @@ import { Panel } from './components/Panel'; const ADDON_ID = 'storybook-addon-viewport'; const PANEL_ID = `${ADDON_ID}/addon-panel`; -const EVENT_ID = `${ADDON_ID}/addon-event`; function init() { - addons.register(ADDON_ID, api => { - const channel = addons.getChannel(); + addons.register(ADDON_ID, api => { + const channel = addons.getChannel(); - addons.addPanel(PANEL_ID, { - title: 'Viewport', - render() { - return ; - } - }); + addons.addPanel(PANEL_ID, { + title: 'Viewport', + render() { + return ; + }, }); + }); } -export { init } +export { init }; From c25a42f53d7d424997ad078b8460a35539496e57 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Thu, 17 Aug 2017 18:18:55 -0700 Subject: [PATCH 4/7] Add some features (respnosive, veritcal / horizontal swap), change single button to switch.. unclear if that's good or not though. --- addons/viewport/.storybook/config.js | 1 + addons/viewport/.storybook/stories.js | 10 +- addons/viewport/package.json | 3 +- addons/viewport/src/components/Panel.jsx | 141 ++++++++++++++---- addons/viewport/src/components/WrapStory.jsx | 11 +- .../viewport/src/components/viewportInfo.js | 65 +++++++- addons/viewport/src/index.js | 1 + addons/viewport/src/index.jsx | 20 --- 8 files changed, 183 insertions(+), 69 deletions(-) create mode 100644 addons/viewport/src/index.js delete mode 100644 addons/viewport/src/index.jsx diff --git a/addons/viewport/.storybook/config.js b/addons/viewport/.storybook/config.js index 29599c59b5d2..9c18e1bf514c 100644 --- a/addons/viewport/.storybook/config.js +++ b/addons/viewport/.storybook/config.js @@ -1,2 +1,3 @@ import * as storybook from '@storybook/react'; + storybook.configure(() => require('./stories'), module); diff --git a/addons/viewport/.storybook/stories.js b/addons/viewport/.storybook/stories.js index ad417b1c9d83..d5ea1dea2e4b 100644 --- a/addons/viewport/.storybook/stories.js +++ b/addons/viewport/.storybook/stories.js @@ -1,7 +1,9 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; -import { withViewport } from '../src'; -storiesOf('Viewport Example', module) - .addDecorator(withViewport) - .add('Example', ); +storiesOf('Viewport', module) + .add('Example', () => ( +

+ Change viewport sizes below +

+ )); diff --git a/addons/viewport/package.json b/addons/viewport/package.json index 7f07a51d6294..e9c6961707e9 100644 --- a/addons/viewport/package.json +++ b/addons/viewport/package.json @@ -4,7 +4,7 @@ "description": "Storybook addon to change the viewport size to mobile", "main": "dist/index.js", "keywords": [ - "storybook" + "storybook" ], "scripts": { "build": "babel --source-maps --out-dir dist src", @@ -15,6 +15,7 @@ }, "license": "MIT", "dependencies": { + "@storybook/react": "^3.2.5", "prop-types": "^15.5.10" }, "peerDependencies": { diff --git a/addons/viewport/src/components/Panel.jsx b/addons/viewport/src/components/Panel.jsx index 54150de9c538..397c2f54af86 100644 --- a/addons/viewport/src/components/Panel.jsx +++ b/addons/viewport/src/components/Panel.jsx @@ -1,40 +1,115 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { viewports } from './viewportInfo'; +import { viewports, resetViewport } from './viewportInfo'; const storybookIframe = 'storybook-preview-iframe'; +const defaultViewport = 'default'; export class Panel extends Component { - static propTypes = { - channel: PropTypes.object.isRequired, - }; - - constructor(props, context) { - super(props, context); - this.state = { viewport: null }; - - this.props.channel.on('addon:viewport:update', this.changeViewport); - } - - componentDidMount() { - this.iframe = document.getElementById(storybookIframe); - } - - iframe = undefined; - - changeViewport = viewport => { - this.iframe.style = viewport.styles; - }; - - render() { - return ( -
- {viewports.map(viewport => - - )} -
- ); - } + 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); + } + + 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; + + return ( +
+
+ + + +
+ +
+ +
+ +
+

Responsive

+
+ + px +
+
+ + px +
+
+
+ ); + } } diff --git a/addons/viewport/src/components/WrapStory.jsx b/addons/viewport/src/components/WrapStory.jsx index 51d7082eef3e..2c156b6331dd 100644 --- a/addons/viewport/src/components/WrapStory.jsx +++ b/addons/viewport/src/components/WrapStory.jsx @@ -5,9 +5,14 @@ import PropTypes from 'prop-types'; export class WrapStory extends Component { static propTypes = { channel: PropTypes.object.isRequired, - context: PropTypes.object.isRequired, - storyFn: PropTypes.func.isRequired, - }; + context: PropTypes.object, + storyFn: PropTypes.func, + } + + static defaultProps = { + context: {}, + storyFn: context => context, + } componentDidMount() { const { channel } = this.props; diff --git a/addons/viewport/src/components/viewportInfo.js b/addons/viewport/src/components/viewportInfo.js index 3b4698c2ddee..829eaa4264b3 100644 --- a/addons/viewport/src/components/viewportInfo.js +++ b/addons/viewport/src/components/viewportInfo.js @@ -5,7 +5,27 @@ const configuredStyles = { boxShadow: 'rgba(0,0,0,0.2) 0px 0px 60px 12px', }; +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: { @@ -14,15 +34,44 @@ export const viewports = { ...configuredStyles, }, }, - reset: { - name: 'Reset', + iphone6p: { + name: 'iPhone 6 Plus', styles: { - width: '100%', - height: '100%', - border: 'none', - display: 'block', - margin: '0', - boxShadow: 'none', + 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, }, }, }; diff --git a/addons/viewport/src/index.js b/addons/viewport/src/index.js new file mode 100644 index 000000000000..d3da40ad9193 --- /dev/null +++ b/addons/viewport/src/index.js @@ -0,0 +1 @@ +export { register } from './manager'; diff --git a/addons/viewport/src/index.jsx b/addons/viewport/src/index.jsx deleted file mode 100644 index 4745fb288a5f..000000000000 --- a/addons/viewport/src/index.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import addons from '@storybook/addons'; - -import { WrapStory } from './components/WrapStory'; - -class ViewportManager { - wrapStory(channel, storyFn, context) { - const props = { context, storyFn, channel }; - return ; - } -} - -const manager = new ViewportManager(); - -function withViewport(storyFn, context) { - const channel = addons.getChannel(); - return manager.wrapStory(channel, storyFn, context); -} - -export { withViewport }; From 0ba8c641444732afedab2f45d7b8aae9a5582cba Mon Sep 17 00:00:00 2001 From: hypnos Date: Sun, 20 Aug 2017 20:45:37 +0300 Subject: [PATCH 5/7] Search box: make found options selectable with click --- lib/ui/package.json | 2 +- lib/ui/src/modules/ui/components/search_box.js | 13 ++++++++++--- .../src/modules/ui/components/search_box.test.js | 14 +++++++++++++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/ui/package.json b/lib/ui/package.json index 9c82f46b0a7f..ed7ce92a22f8 100644 --- a/lib/ui/package.json +++ b/lib/ui/package.json @@ -14,7 +14,6 @@ "storybook": "start-storybook -p 9010" }, "dependencies": { - "@storybook/react-fuzzy": "^0.4.0", "@storybook/components": "^3.2.4", "babel-runtime": "^6.23.0", "deep-equal": "^1.0.1", @@ -30,6 +29,7 @@ "podda": "^1.2.2", "prop-types": "^15.5.10", "qs": "^6.4.0", + "react-fuzzy": "^0.4.1", "react-icons": "^2.2.5", "react-inspector": "^2.1.1", "react-komposer": "^2.0.0", diff --git a/lib/ui/src/modules/ui/components/search_box.js b/lib/ui/src/modules/ui/components/search_box.js index 657618b55658..2a50d80769b4 100644 --- a/lib/ui/src/modules/ui/components/search_box.js +++ b/lib/ui/src/modules/ui/components/search_box.js @@ -2,7 +2,7 @@ import { document } from 'global'; import PropTypes from 'prop-types'; import React from 'react'; import ReactModal from 'react-modal'; -import FuzzySearch from '@storybook/react-fuzzy'; +import FuzzySearch from 'react-fuzzy'; import { baseFonts } from '@storybook/components'; @@ -48,11 +48,18 @@ const formatStories = stories => { return formattedStories; }; -const suggestionTemplate = (props, state, styles) => +const suggestionTemplate = (props, state, styles, clickHandler) => state.results.map((val, i) => { const style = state.selectedIndex === i ? styles.selectedResultStyle : styles.resultsStyle; return ( -
+ // eslint-disable-next-line jsx-a11y/interactive-supports-focus +
clickHandler(i)} + > {val.value} {val.type === 'story' ? `in ${val.kind}` : 'Kind'} diff --git a/lib/ui/src/modules/ui/components/search_box.test.js b/lib/ui/src/modules/ui/components/search_box.test.js index 97915daedb91..624725a5148f 100644 --- a/lib/ui/src/modules/ui/components/search_box.test.js +++ b/lib/ui/src/modules/ui/components/search_box.test.js @@ -1,4 +1,4 @@ -import { shallow } from 'enzyme'; +import { shallow, mount } from 'enzyme'; import React from 'react'; import ReactModal from 'react-modal'; import FuzzySearch from '@storybook/react-fuzzy'; @@ -95,5 +95,17 @@ describe('manager.ui.components.search_box', () => { expect(onSelectStory).toHaveBeenCalledWith('b', 'a'); expect(onClose).toHaveBeenCalled(); }); + + test('should handle selecting a story with click', () => { + const onSelectStory = jest.fn(); + const onClose = jest.fn(); + const wrap = mount(); + + const option = wrap.findWhere(el => el.key() === 'a'); + option.simulate('click'); + + expect(onSelectStory).toHaveBeenCalledWith('b', 'a'); + expect(onClose).toHaveBeenCalled(); + }); }); }); From bd176f780ec9e113a4468f01ddd410250164164d Mon Sep 17 00:00:00 2001 From: hypnos Date: Sun, 20 Aug 2017 20:59:54 +0300 Subject: [PATCH 6/7] Fix test --- .../modules/ui/components/search_box.test.js | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/ui/src/modules/ui/components/search_box.test.js b/lib/ui/src/modules/ui/components/search_box.test.js index 624725a5148f..9d54bf57a765 100644 --- a/lib/ui/src/modules/ui/components/search_box.test.js +++ b/lib/ui/src/modules/ui/components/search_box.test.js @@ -1,7 +1,7 @@ -import { shallow, mount } from 'enzyme'; +import { shallow } from 'enzyme'; import React from 'react'; import ReactModal from 'react-modal'; -import FuzzySearch from '@storybook/react-fuzzy'; +import FuzzySearch from 'react-fuzzy'; import SearchBox from './search_box'; @@ -97,14 +97,27 @@ describe('manager.ui.components.search_box', () => { }); test('should handle selecting a story with click', () => { + const stories = [ + { + kind: 'a', + stories: ['b', 'c'], + }, + ]; const onSelectStory = jest.fn(); const onClose = jest.fn(); - const wrap = mount(); + const wrap = shallow( + + ); + + const modal = wrap.find(FuzzySearch).dive(); + modal.find('input').simulate('change', { + target: { value: 'b' }, + }); - const option = wrap.findWhere(el => el.key() === 'a'); + const option = modal.findWhere(el => el.key() === 'b'); option.simulate('click'); - expect(onSelectStory).toHaveBeenCalledWith('b', 'a'); + expect(onSelectStory).toHaveBeenCalledWith('a', 'b'); expect(onClose).toHaveBeenCalled(); }); }); From 0a8cf2aaeea06fa4a2be5021a16765cd8b86ab69 Mon Sep 17 00:00:00 2001 From: Josh Callender Date: Fri, 25 Aug 2017 15:54:01 -0700 Subject: [PATCH 7/7] Update the panels to be a bit more stylish --- addons/viewport/src/components/Panel.jsx | 200 +++++++++--------- .../src/components/RotateViewport.jsx | 31 +++ .../src/components/SelectViewport.jsx | 31 +++ addons/viewport/src/components/WrapStory.jsx | 4 +- addons/viewport/src/components/styles.js | 30 +++ .../viewport/src/components/viewportInfo.js | 19 +- 6 files changed, 209 insertions(+), 106 deletions(-) create mode 100644 addons/viewport/src/components/RotateViewport.jsx create mode 100644 addons/viewport/src/components/SelectViewport.jsx create mode 100644 addons/viewport/src/components/styles.js diff --git a/addons/viewport/src/components/Panel.jsx b/addons/viewport/src/components/Panel.jsx index 397c2f54af86..6ebd8b2a9943 100644 --- a/addons/viewport/src/components/Panel.jsx +++ b/addons/viewport/src/components/Panel.jsx @@ -1,115 +1,125 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { viewports, resetViewport } from './viewportInfo'; +import { viewports, defaultViewport, resetViewport } from './viewportInfo'; + +import { SelectViewport } from './SelectViewport'; +import { RotateViewport } from './RotateViewport'; const storybookIframe = 'storybook-preview-iframe'; -const defaultViewport = 'default'; +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, + static propTypes = { + channel: PropTypes.object.isRequired, + }; + + constructor(props, context) { + super(props, context); + this.state = { + viewport: defaultViewport, + isLandscape: false, }; - constructor(props, context) { - super(props, context); - this.state = { - viewport: defaultViewport, - isLandscape: false, - }; - - this.props.channel.on('addon:viewport:update', this.changeViewport); - } + this.props.channel.on('addon:viewport:update', this.changeViewport); + } - componentDidMount() { - this.iframe = document.getElementById(storybookIframe); - } + componentDidMount() { + this.iframe = document.getElementById(storybookIframe); + } - iframe = undefined; + iframe = undefined; - changeViewport = viewport => { - const { viewport: previousViewport, isLandscape } = this.state; + changeViewport = viewport => { + const { viewport: previousViewport, isLandscape } = this.state; - if (previousViewport !== viewport) { - this.setState({ - viewport, - isLandscape: false, - }, this.updateIframe); - } else { - this.updateIframe(); - } - }; + 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'); + } - toggleLandscape = () => { - const { isLandscape } = this.state; + Object.keys(viewport.styles).forEach(prop => { + this.iframe.style[prop] = viewport.styles[prop]; + }); - // TODO simplify the state management - // ideally we simply dispatch an action to the iframe - this.setState({ - isLandscape: !isLandscape, - }, () => { - this.changeViewport(this.state.viewport); - }); + if (isLandscape) { + this.iframe.style.height = viewport.styles.width; + this.iframe.style.width = viewport.styles.height; } + } - updateIframe() { - const { viewport: viewportKey, isLandscape } = this.state; - const viewport = viewports[viewportKey] || resetViewport; - - if (!this.iframe) { - throw new Error('Cannot find Storybook iframe'); - } + render() { + const { isLandscape, viewport } = this.state; - Object.keys(viewport.styles).forEach(prop => { - this.iframe.style[prop] = viewport.styles[prop]; - }); + const disableDefault = viewport === defaultViewport; + const disabledStyles = disableDefault ? styles.disabled : {}; - if (isLandscape) { - this.iframe.style.height = viewport.styles.width; - this.iframe.style.width = viewport.styles.height; - } - } + const buttonStyles = { + ...styles.button, + ...disabledStyles, + marginTop: 30, + padding: 20, + }; - render() { - const { isLandscape, viewport } = this.state; - - return ( -
-
- - - -
- -
- -
- -
-

Responsive

-
- - px -
-
- - px -
-
-
- ); - } + return ( +
+ this.changeViewport(e.target.value)} + /> + + + + +
+ ); + } } diff --git a/addons/viewport/src/components/RotateViewport.jsx b/addons/viewport/src/components/RotateViewport.jsx new file mode 100644 index 000000000000..725680508272 --- /dev/null +++ b/addons/viewport/src/components/RotateViewport.jsx @@ -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 ( +
+ + + +
+ ); + } +} diff --git a/addons/viewport/src/components/SelectViewport.jsx b/addons/viewport/src/components/SelectViewport.jsx new file mode 100644 index 000000000000..cace789f5a13 --- /dev/null +++ b/addons/viewport/src/components/SelectViewport.jsx @@ -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 ( +
+ + + +
+ ); + } +} diff --git a/addons/viewport/src/components/WrapStory.jsx b/addons/viewport/src/components/WrapStory.jsx index 2c156b6331dd..9b863e6677db 100644 --- a/addons/viewport/src/components/WrapStory.jsx +++ b/addons/viewport/src/components/WrapStory.jsx @@ -7,12 +7,12 @@ export class WrapStory extends Component { channel: PropTypes.object.isRequired, context: PropTypes.object, storyFn: PropTypes.func, - } + }; static defaultProps = { context: {}, storyFn: context => context, - } + }; componentDidMount() { const { channel } = this.props; diff --git a/addons/viewport/src/components/styles.js b/addons/viewport/src/components/styles.js new file mode 100644 index 000000000000..95ee46eb732b --- /dev/null +++ b/addons/viewport/src/components/styles.js @@ -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, +}; diff --git a/addons/viewport/src/components/viewportInfo.js b/addons/viewport/src/components/viewportInfo.js index 829eaa4264b3..7084e56f02c3 100644 --- a/addons/viewport/src/components/viewportInfo.js +++ b/addons/viewport/src/components/viewportInfo.js @@ -5,16 +5,17 @@ const configuredStyles = { 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', - }, + name: 'Reset', + styles: { + width: '100%', + height: '100%', + border: 'none', + display: 'block', + margin: '0', + boxShadow: 'none', + }, }; export const viewports = {