Merge pull request #5829 from storybooks/5757-restore-viewport

Restore viewport behaviour
This commit is contained in:
Michael Shilman 2019-03-04 10:41:14 +08:00 committed by Michael Shilman
parent d21308929f
commit 586f7620d6
12 changed files with 124 additions and 331 deletions

View File

@ -163,6 +163,28 @@ storiesOf('Stories', module)
.add('centered', () => 'Hello', { decorators: [centered] });
```
## Addon viewport uses parameters
Similarly, `@storybook/addon-viewport` uses parameters to pass viewport options. If you previously had:
```js
import { configureViewport } from `@storybook/addon-viewport`;
configureViewport(options);
```
You can replace it with:
```js
import { addParameters } from '@storybook/react'; // or others
addParameters({ viewport: options });
```
The `withViewport` decorator is also no longer supported and should be replaced with a parameter based API as above. Also the `onViewportChange` callback is no longer supported.
See the [README](https://github.com/storybooks/storybook/blob/master/addons/viewport/README.md) for the viewport addon for more information.
## From version 4.0.x to 4.1.x
There are are a few migrations you should be aware of in 4.1, including one unintentionally breaking change for advanced addon usage.

View File

@ -28,21 +28,30 @@ import '@storybook/addon-viewport/register';
## Configuration
Import and use the `configureViewport` function in your `config.js` file.
The viewport addon is configured by story parameters with the `viewport` key. To configure globally, import `addParameters` from your app layer in your `config.js` file.
```js
import { configureViewport } from '@storybook/addon-viewport';
import { addParameters } from '@storybook/react';
addParameters({ viewport: options });
```
Options can take a object with the following keys:
### defaultViewport : String
----
---
Setting this property to, let say `iphone6`, will make `iPhone 6` the default device/viewport for all stories. Default is `'responsive'` which fills 100% of the preview area.
### viewports : Object
----
---
A key-value pair of viewport's key and properties (see `Viewport` definition below) for all viewports to be displayed. Default is [`INITIAL_VIEWPORTS`](src/shared/index.js)
#### Viewport Model
```js
{
/**
@ -70,154 +79,81 @@ A key-value pair of viewport's key and properties (see `Viewport` definition bel
}
```
## Decorators
## Configuring per component or story
Sometimes you want to show collection of mobile stories, and you know those stories look horible on desktop (`responsive`), so you think you need to change the default viewport only for those?
Parameters can be configured for a whole set of stories or a single story via the standard parameter API:
Here is the answer, with `withViewport` decorator, you can change the default viewport of single, multiple, or all stories.
`withViewport` accepts either
* A `String`, which represents the default viewport, or
* An `Object`, which looks like
```js
{
name: 'iphone6', // default viewport
onViewportChange({ viewport }) { // called whenever different viewport is selected from the dropdown
import addStories from '@storybook/react';
}
}
addStories('Stories', module)
// To set a default viewport for all the stories for this component
.addParameters({ viewport: { defaultViewport: 'iphone6' }})
.add('story', () => </>, { viewport: 'iphonex' });
```
## Examples
### Basic Usage
Simply import the Storybook Viewport Addon in the `addons.js` file in your `.storybook` directory.
```js
import '@storybook/addon-viewport/register'
```
This will register the Viewport Addon to Storybook and will show up in the action area.
### Use Custom Set of Devices
This will replace all previous devices with `Kindle Fire 2` and `Kindle Fire HD` by simply calling `configureViewport` with the two devices as `viewports` in `config.js` file in your `.storybook` directory.
This will replace all previous devices with `Kindle Fire 2` and `Kindle Fire HD` by simply calling `addParameters` with the two devices as `viewports` in `config.js` file in your `.storybook` directory.
```js
import { configureViewport } from '@storybook/addon-viewport';
import { addParameters } from '@storybook/react';
const newViewports = {
kindleFire2: {
name: 'Kindle Fire 2',
styles: {
width: '600px',
height: '963px'
}
height: '963px',
},
},
kindleFireHD: {
name: 'Kindle Fire HD',
styles: {
width: '533px',
height: '801px'
}
}
height: '801px',
},
},
};
configureViewport({
viewports: newViewports
addParameters({
viewport: { viewports: newViewports },
});
```
### Add New Device
This will add both `Kindle Fire 2` and `Kindle Fire HD` to the list of devices. This is acheived by making use of the exported [`INITIAL_VIEWPORTS`](src/shared/index.js) property, by merging it with the new viewports and pass the result as `viewports` to `configureViewport` function
```js
import { configureViewport, INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
import { addParameters } from '@storybook/react';
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
const newViewports = {
kindleFire2: {
name: 'Kindle Fire 2',
styles: {
width: '600px',
height: '963px'
}
height: '963px',
},
},
kindleFireHD: {
name: 'Kindle Fire HD',
styles: {
width: '533px',
height: '801px'
}
}
height: '801px',
},
},
};
configureViewport({
addParameters({
viewport: {
viewports: {
...INITIAL_VIEWPORTS,
...newViewports
}
});
```
### Change The Default Viewport
This will make `iPhone 6` the default viewport for all stories.
```js
import { configureViewport } from '@storybook/addon-viewport';
configureViewport({
defaultViewport: 'iphone6'
});
```
## withViewport Decorator
Change the default viewport for single/multiple/global stories, or listen to viewport selection changes
```js
import React from 'react';
import { storiesOf, addDecorator } from '@storybook/react';
import { withViewport } from '@storybook/addon-viewport';
// Globablly
addDecorator(withViewport('iphone5'));
// Collection
storiesOf('Decorator with string', module)
.addDecorator(withViewport('iphone6'))
.add('iPhone 6', () => (
<h1>
Do I look good on <b>iPhone 6</b>?
</h1>
));
// Single
storiesOf('Parameterized story', module)
.addDecorator(withViewport())
.add(
'iPad',
() => (
<h1>
Do I look good on <b>iPad</b>?
</h1>
),
{ viewport: 'ipad' }
);
storiesOf('Decorator with object', module)
.addDecorator(
withViewport({
onViewportChange({ viewport }) {
console.log(`Viewport changed: ${viewport.name} (${viewport.type})`); // e.g. Viewport changed: iphone6 (mobile)
...newViewports,
},
})
)
.add('onViewportChange', () => <MobileFirstComponent />);
},
});
```

View File

@ -4,4 +4,3 @@ exports.configureViewport = preview.configureViewport;
exports.DEFAULT_VIEWPORT = preview.DEFAULT_VIEWPORT;
exports.INITIAL_VIEWPORTS = preview.INITIAL_VIEWPORTS;
exports.withViewport = preview.withViewport;
exports.Viewport = preview.Viewport;

View File

@ -1,6 +1,7 @@
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import memoize from 'memoizerific';
import deprecate from 'util-deprecate';
import { Global } from '@storybook/theming';
@ -8,6 +9,7 @@ import { Icons, IconButton, WithTooltip, TooltipLinkList } from '@storybook/comp
import { SET_STORIES } from '@storybook/core-events';
import { PARAM_KEY } from './constants';
import { INITIAL_VIEWPORTS, DEFAULT_VIEWPORT } from './defaults';
const toList = memoize(50)(items =>
items ? Object.entries(items).map(([id, value]) => ({ ...value, id })) : []
@ -26,14 +28,35 @@ const createItem = memoize(1000)((id, name, value, change) => ({
const flip = ({ width, height }) => ({ height: width, width: height });
const deprecatedViewportString = deprecate(
() => 0,
'The viewport parameter must be an object with keys `viewports` and `defaultViewport`'
);
const deprecateOnViewportChange = deprecate(
() => 0,
'The viewport parameter `onViewportChange` is no longer supported'
);
const getState = memoize(10)((props, state, change) => {
const data = props.api.getCurrentStoryData();
const list = toList(data && data.parameters && data.parameters[PARAM_KEY]);
const parameters = data && data.parameters && data.parameters[PARAM_KEY];
if (parameters && typeof parameters !== 'object') {
deprecatedViewportString();
}
const { disable, viewports, defaultViewport, onViewportChange } = parameters || {};
if (onViewportChange) {
deprecateOnViewportChange();
}
const list = disable ? [] : toList(viewports || INITIAL_VIEWPORTS);
const selected =
state.selected === 'responsive' || list.find(i => i.id === state.selected)
? state.selected
: list.find(i => i.default) || 'responsive';
: list.find(i => i.default) || defaultViewport || DEFAULT_VIEWPORT;
const resets =
selected !== 'responsive'

View File

@ -1,5 +1,5 @@
export const ADDON_ID = 'storybook/viewport';
export const PARAM_KEY = 'viewports';
export const PARAM_KEY = 'viewport';
export default {
UPDATE: `${ADDON_ID}/update`,

View File

@ -1,7 +1,7 @@
import deprecate from 'util-deprecate';
export { INITIAL_VIEWPORTS, DEFAULT_VIEWPORT } from '../defaults';
export { default as withViewport, Viewport } from './withViewport';
export { default as withViewport } from './withViewport';
export const configureViewport = deprecate(() => {},
'usage is deprecated, use .addParameters({ viewport }) instead');
'configureViewport is no longer supported, use .addParameters({ viewport }) instead');

View File

@ -3,17 +3,11 @@ import deprecate from 'util-deprecate';
const withViewport = makeDecorator({
name: 'withViewport',
parameterName: 'viewports',
allowDeprecatedUsage: true,
parameterName: 'viewport',
wrapper: deprecate(
(getStory, context) => getStory(context),
'usage is deprecated, use .addParameters({ viewport }) instead'
'withViewport is no longer supported, use .addParameters({ viewport }) instead'
),
});
export default withViewport;
export const Viewport = deprecate(
({ children }) => children,
`<Viewport> usage is deprecated, use .addParameters({ viewport }) instead`
);

View File

@ -2,7 +2,6 @@ import React from 'react';
import { storiesOf, configure, addDecorator, addParameters } from '@storybook/react';
import { Global, ThemeProvider, themes, createGlobal } from '@storybook/theming';
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
import { withCssResources } from '@storybook/addon-cssresources';
import { withA11Y } from '@storybook/addon-a11y';
import { withNotes } from '@storybook/addon-notes';
@ -10,7 +9,6 @@ import { withNotes } from '@storybook/addon-notes';
import 'storybook-chromatic';
import addHeadWarning from './head-warning';
import extraViewports from './extra-viewports.json';
if (process.env.NODE_ENV === 'development') {
if (!process.env.DOTENV_DEVELOPMENT_DISPLAY_WARNING) {
@ -52,10 +50,6 @@ addParameters({
hierarchySeparator: /\/|\./,
hierarchyRootSeparator: '|',
},
viewports: {
...INITIAL_VIEWPORTS,
...extraViewports,
},
backgrounds: [
{ name: 'storybook app', value: themes.normal.background.app, default: true },
{ name: 'light', value: '#eeeeee' },

View File

@ -1,18 +0,0 @@
{
"kindleFire2": {
"name": "Kindle Fire 2",
"styles": {
"width": "600px",
"height": "963px"
},
"type": "tablet"
},
"kindleFireHD": {
"name": "Kindle Fire HD",
"styles": {
"width": "533px",
"height": "801px"
},
"type": "tablet"
}
}

View File

@ -8,6 +8,14 @@ exports[`Storyshots Addons|Viewport default 1`] = `
</div>
`;
exports[`Storyshots Addons|Viewport.Custom Default (Kindle Fire 2) Disabled 1`] = `
<div
class="emotion-0"
>
There should be no viewport selector in the toolbar
</div>
`;
exports[`Storyshots Addons|Viewport.Custom Default (Kindle Fire 2) Inherited 1`] = `
<div
class="emotion-0"

View File

@ -3,10 +3,7 @@ import { storiesOf } from '@storybook/react';
import { styled } from '@storybook/theming';
// import { Viewport, withViewport } from '@storybook/addon-viewport';
// import EventEmitter from 'eventemitter3';
// import Logger from './Logger';
import { INITIAL_VIEWPORTS } from '@storybook/addon-viewport';
const Panel = styled.div();
@ -15,6 +12,20 @@ storiesOf('Addons|Viewport', module).add('default', () => (
));
storiesOf('Addons|Viewport.Custom Default (Kindle Fire 2)', module)
.addParameters({
viewport: {
viewports: {
...INITIAL_VIEWPORTS,
kindleFire2: {
name: 'Kindle Fire 2',
styles: {
width: '600px',
height: '963px',
},
},
},
},
})
.add('Inherited', () => (
<Panel>
I've inherited <b>Kindle Fire 2</b> viewport from my parent.
@ -27,38 +38,8 @@ storiesOf('Addons|Viewport.Custom Default (Kindle Fire 2)', module)
I respect my parents but I should be looking good on <b>iPad</b>.
</Panel>
),
{ viewports: [] }
);
// const emitter = new EventEmitter();
// storiesOf('Addons|Viewport.withViewport', module)
// .addDecorator(
// withViewport({
// onViewportChange({ viewport }) {
// emitter.emit(Logger.LOG_EVENT, {
// name: 'Viewport Changed',
// payload: `${viewport.name} (${viewport.type})`,
// });
// },
// })
// )
// .add('onViewportChange', () => <Logger title="Select device/viewport" emitter={emitter} />);
// storiesOf('Addons|Viewport.deprecated', module)
// .addDecorator(withViewport('kindleFire2'))
// .add(
// 'Overridden via "withViewport" decorator',
// withViewport('iphone6')(() => (
// <Panel>
// I respect my parents but I should be looking good on <b>iPhone 6</b>.
// </Panel>
// ))
// )
// .add('Overridden via "Viewport" component', () => (
// <Viewport name="iphone6p">
// <Panel>
// I respect my parents but I should be looking good on <b>iPhone 6 Plus</b>.
// </Panel>
// </Viewport>
// ));
{ viewport: { defaultViewport: 'ipad' } }
)
.add('Disabled', () => <Panel>There should be no viewport selector in the toolbar</Panel>, {
viewport: { disable: true },
});

View File

@ -2666,152 +2666,6 @@ exports[`Storyshots Core|Parameters passed to story 1`] = `
"restoreScroll": true
}
},
"viewports": {
"iphone5": {
"name": "iPhone 5",
"styles": {
"height": "568px",
"width": "320px"
},
"type": "mobile"
},
"iphone6": {
"name": "iPhone 6",
"styles": {
"height": "667px",
"width": "375px"
},
"type": "mobile"
},
"iphone6p": {
"name": "iPhone 6 Plus",
"styles": {
"height": "736px",
"width": "414px"
},
"type": "mobile"
},
"iphone8p": {
"name": "iPhone 8 Plus",
"styles": {
"height": "736px",
"width": "414px"
},
"type": "mobile"
},
"iphonex": {
"name": "iPhone X",
"styles": {
"height": "812px",
"width": "375px"
},
"type": "mobile"
},
"iphonexr": {
"name": "iPhone XR",
"styles": {
"height": "896px",
"width": "414px"
},
"type": "mobile"
},
"iphonexsmax": {
"name": "iPhone XS Max",
"styles": {
"height": "896px",
"width": "414px"
},
"type": "mobile"
},
"ipad": {
"name": "iPad",
"styles": {
"height": "1024px",
"width": "768px"
},
"type": "tablet"
},
"ipad10p": {
"name": "iPad Pro 10.5-in",
"styles": {
"height": "1112px",
"width": "834px"
},
"type": "tablet"
},
"ipad12p": {
"name": "iPad Pro 12.9-in",
"styles": {
"height": "1366px",
"width": "1024px"
},
"type": "tablet"
},
"galaxys5": {
"name": "Galaxy S5",
"styles": {
"height": "640px",
"width": "360px"
},
"type": "mobile"
},
"galaxys9": {
"name": "Galaxy S9",
"styles": {
"height": "1480px",
"width": "720px"
},
"type": "mobile"
},
"nexus5x": {
"name": "Nexus 5X",
"styles": {
"height": "660px",
"width": "412px"
},
"type": "mobile"
},
"nexus6p": {
"name": "Nexus 6P",
"styles": {
"height": "732px",
"width": "412px"
},
"type": "mobile"
},
"pixel": {
"name": "Pixel",
"styles": {
"height": "960px",
"width": "540px"
},
"type": "mobile"
},
"pixelxl": {
"name": "Pixel XL",
"styles": {
"height": "1280px",
"width": "720px"
},
"type": "mobile"
},
"kindleFire2": {
"name": "Kindle Fire 2",
"styles": {
"width": "600px",
"height": "963px"
},
"type": "tablet"
},
"kindleFireHD": {
"name": "Kindle Fire HD",
"styles": {
"width": "533px",
"height": "801px"
},
"type": "tablet"
}
},
"backgrounds": [
{
"name": "storybook app",