Skip to content

Commit

Permalink
Allow setting custom default viewport per story
Browse files Browse the repository at this point in the history
  • Loading branch information
mshaaban088 committed Mar 9, 2018
1 parent 7cd29fa commit f212451
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 15 deletions.
2 changes: 2 additions & 0 deletions addons/viewport/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ const preview = require('./dist/preview');
exports.configure = preview.configure;
exports.DEFAULT_VIEWPORT = preview.DEFAULT_VIEWPORT;
exports.INITIAL_VIEWPORTS = preview.INITIAL_VIEWPORTS;
exports.withViewport = preview.withViewport;
exports.Viewport = preview.Viewport;
48 changes: 43 additions & 5 deletions addons/viewport/src/manager/components/Panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { resetViewport, viewportsTransformer } from './viewportInfo';
import { SelectViewport } from './SelectViewport';
import { RotateViewport } from './RotateViewport';
import {
SET_STORY_DEFAULT_VIEWPORT_EVENT_ID,
UNSET_STORY_DEFAULT_VIEWPORT_EVENT_ID,
CONFIGURE_VIEWPORT_EVENT_ID,
UPDATE_VIEWPORT_EVENT_ID,
INITIAL_VIEWPORTS,
Expand Down Expand Up @@ -46,6 +48,8 @@ export class Panel extends Component {

channel.on(UPDATE_VIEWPORT_EVENT_ID, this.changeViewport);
channel.on(CONFIGURE_VIEWPORT_EVENT_ID, this.configure);
channel.on(SET_STORY_DEFAULT_VIEWPORT_EVENT_ID, this.setStoryDefaultViewport);
channel.on(UNSET_STORY_DEFAULT_VIEWPORT_EVENT_ID, this.unsetStoryDefaultViewport);

this.unsubscribeFromOnStory = api.onStory(() => {
this.changeViewport(this.state.defaultViewport);
Expand All @@ -61,8 +65,36 @@ export class Panel extends Component {

channel.removeListener(UPDATE_VIEWPORT_EVENT_ID, this.changeViewport);
channel.removeListener(CONFIGURE_VIEWPORT_EVENT_ID, this.configure);
channel.removeListener(SET_STORY_DEFAULT_VIEWPORT_EVENT_ID, this.setStoryDefaultViewport);
channel.removeListener(UNSET_STORY_DEFAULT_VIEWPORT_EVENT_ID, this.unsetStoryDefaultViewport);
}

setStoryDefaultViewport = viewport => {
const { viewports } = this.state;

if (!(viewport in viewports)) {
viewport = Object.keys(viewports)[0];
}

this.setState(
{
storyDefaultViewport: viewport,
viewport,
},
this.updateIframe
);
};

unsetStoryDefaultViewport = () => {
this.setState(
{
storyDefaultViewport: undefined,
viewport: this.state.defaultViewport,
},
this.updateIframe
);
};

configure = ({ viewports = INITIAL_VIEWPORTS, defaultViewport = DEFAULT_VIEWPORT }) => {
if (Object.keys(viewports).length === 0) {
viewports = INITIAL_VIEWPORTS;
Expand Down Expand Up @@ -123,9 +155,15 @@ export class Panel extends Component {
};

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

const disableDefault = viewport === defaultViewport;
const {
isLandscape,
defaultViewport,
storyDefaultViewport = defaultViewport,
viewport,
viewports,
} = this.state;

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

const buttonStyles = {
Expand All @@ -139,7 +177,7 @@ export class Panel extends Component {
<div style={containerStyles}>
<SelectViewport
viewports={viewports}
defaultViewport={defaultViewport}
defaultViewport={storyDefaultViewport}
activeViewport={viewport}
onChange={e => this.changeViewport(e.target.value)}
/>
Expand All @@ -152,7 +190,7 @@ export class Panel extends Component {

<button
style={buttonStyles}
onClick={() => this.changeViewport(defaultViewport)}
onClick={() => this.changeViewport(storyDefaultViewport)}
disabled={disableDefault}
>
Reset Viewport
Expand Down
76 changes: 70 additions & 6 deletions addons/viewport/src/manager/components/tests/Panel.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ describe('Viewport/Panel', () => {
subject.instance().changeViewport
);
});

it('listens on `setStoryDefaultViewport` channel', () => {
expect(props.channel.on).toHaveBeenCalledWith(
'addon:viewport:setStoryDefaultViewport',
subject.instance().setStoryDefaultViewport
);
});

it('listens on `unsetStoryDefaultViewport` channel', () => {
expect(props.channel.on).toHaveBeenCalledWith(
'addon:viewport:unsetStoryDefaultViewport',
subject.instance().unsetStoryDefaultViewport
);
});
});

describe('componentWillUnmount', () => {
Expand All @@ -86,6 +100,20 @@ describe('Viewport/Panel', () => {
subject.instance().configure
);
});

it('removes `setStoryDefaultViewport` channel listener', () => {
expect(props.channel.removeListener).toHaveBeenCalledWith(
'addon:viewport:setStoryDefaultViewport',
subject.instance().setStoryDefaultViewport
);
});

it('removes `unsetStoryDefaultViewport` channel listener', () => {
expect(props.channel.removeListener).toHaveBeenCalledWith(
'addon:viewport:unsetStoryDefaultViewport',
subject.instance().unsetStoryDefaultViewport
);
});
});

describe('configure', () => {
Expand All @@ -97,15 +125,15 @@ describe('Viewport/Panel', () => {
viewports: {
foo: {
styles: {
width: '50px'
}
width: '50px',
},
},
bar: {
styles: {
width: '100px'
}
}
}
width: '100px',
},
},
},
});
});

Expand Down Expand Up @@ -167,6 +195,42 @@ describe('Viewport/Panel', () => {
});
});

describe('setStoryDefaultViewport', () => {
beforeEach(() => {
subject.instance().setState = jest.fn();
subject.instance().updateIframe = jest.fn();
subject.instance().setStoryDefaultViewport(INITIAL_VIEWPORTS[1]);
});

it('sets the state with the new information', () => {
expect(subject.instance().setState).toHaveBeenCalledWith(
{
viewport: INITIAL_VIEWPORTS[1],
storyDefaultViewport: INITIAL_VIEWPORTS[1],
},
subject.instance().updateIframe
);
});
});

describe('unsetStoryDefaultViewport', () => {
beforeEach(() => {
subject.instance().setState = jest.fn();
subject.instance().updateIframe = jest.fn();
subject.instance().unsetStoryDefaultViewport();
});

it('resets the state', () => {
expect(subject.instance().setState).toHaveBeenCalledWith(
{
viewport: DEFAULT_VIEWPORT,
storyDefaultViewport: undefined,
},
subject.instance().updateIframe
);
});
});

describe('toggleLandscape', () => {
beforeEach(() => {
subject.setState({ isLandscape: false });
Expand Down
37 changes: 36 additions & 1 deletion addons/viewport/src/preview/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import addons from '@storybook/addons';
import { CONFIGURE_VIEWPORT_EVENT_ID } from '../shared';
import {
CONFIGURE_VIEWPORT_EVENT_ID,
SET_STORY_DEFAULT_VIEWPORT_EVENT_ID,
UNSET_STORY_DEFAULT_VIEWPORT_EVENT_ID,
} from '../shared';

export { INITIAL_VIEWPORTS, DEFAULT_VIEWPORT } from '../shared';

Expand All @@ -10,3 +16,32 @@ export function configure(configs = {}) {
channel.emit(CONFIGURE_VIEWPORT_EVENT_ID, configs);
}
}

export function withViewport(name) {
return getStory => <Viewport name={name}>{getStory()}</Viewport>;
}

export class Viewport extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
};

constructor(props) {
super(props);

this.channel = addons.getChannel();
}

componentWillMount() {
this.channel.emit(SET_STORY_DEFAULT_VIEWPORT_EVENT_ID, this.props.name);
}

componentWillUnmount() {
this.channel.emit(UNSET_STORY_DEFAULT_VIEWPORT_EVENT_ID);
}

render() {
return this.props.children;
}
}
43 changes: 40 additions & 3 deletions addons/viewport/src/preview/tests/index.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import addons from '@storybook/addons';
import { configure } from '../';
import { shallow } from 'enzyme';
import { configure, Viewport } from '../';

jest.mock('@storybook/addons');

Expand All @@ -13,14 +15,49 @@ describe('Viewport preview', () => {
it('publishes configure event with all passed configurations', () => {
const configs = {
foo: 'bar',
john: 'Doe'
john: 'Doe',
};
configure(configs);

expect(channel.emit).toHaveBeenCalledTimes(1);
expect(channel.emit).toHaveBeenCalledWith('addon:viewport:configure', {
foo: 'bar',
john: 'Doe'
john: 'Doe',
});
});
});

describe('Viewport', () => {
const props = {
name: 'iphone6',
children: '1337',
};

let subject;

beforeEach(() => {
subject = shallow(<Viewport {...props} />);
});

describe('componentWillMount', () => {
it('publishes `set` event with `iphone6`', () => {
expect(channel.emit).toHaveBeenCalledTimes(1);
expect(channel.emit).toHaveBeenCalledWith(
'addon:viewport:setStoryDefaultViewport',
'iphone6'
);
});
});

describe('componentWillUnmount', () => {
beforeEach(() => {
channel.emit.mockReset();
subject.unmount();
});

it('publishes `unset` event', () => {
expect(channel.emit).toHaveBeenCalledTimes(1);
expect(channel.emit).toHaveBeenCalledWith('addon:viewport:unsetStoryDefaultViewport');
});
});
});
Expand Down
2 changes: 2 additions & 0 deletions addons/viewport/src/shared/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ export const ADDON_ID = 'storybook-addon-viewport';
export const PANEL_ID = `${ADDON_ID}/addon-panel`;
export const UPDATE_VIEWPORT_EVENT_ID = 'addon:viewport:update';
export const CONFIGURE_VIEWPORT_EVENT_ID = 'addon:viewport:configure';
export const SET_STORY_DEFAULT_VIEWPORT_EVENT_ID = 'addon:viewport:setStoryDefaultViewport';
export const UNSET_STORY_DEFAULT_VIEWPORT_EVENT_ID = 'addon:viewport:unsetStoryDefaultViewport';
export const INITIAL_VIEWPORTS = {
responsive: {
name: 'Responsive',
Expand Down
1 change: 1 addition & 0 deletions examples/official-storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@storybook/addon-storysource": "^3.4.0-rc.0",
"@storybook/addon-viewport": "^3.4.0-rc.0",
"@storybook/addons": "^3.4.0-rc.0",
"@storybook/components": "^3.4.0-rc.0",
"@storybook/node-logger": "^3.4.0-rc.0",
"@storybook/react": "^3.4.0-rc.0",
"babel-runtime": "^6.26.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Storyshots Addons|Viewport default 1`] = `
<div
style="font-family:-apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", \\"Lucida Grande\\", \\"Arial\\", sans-serif;color:#444;-webkit-font-smoothing:antialiased"
>
I don't have problems being rendered using the default viewport.
</div>
`;

exports[`Storyshots Addons|Viewport.Custom Default (Kindle Fire 2) Inherited 1`] = `
<div
style="font-family:-apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", \\"Lucida Grande\\", \\"Arial\\", sans-serif;color:#444;-webkit-font-smoothing:antialiased"
>
I've inherited
<b>
Kindle Fire 2
</b>
viewport from my parent.
</div>
`;

exports[`Storyshots Addons|Viewport.Custom Default (Kindle Fire 2) Overridden 1`] = `
<div
style="font-family:-apple-system, \\".SFNSText-Regular\\", \\"San Francisco\\", BlinkMacSystemFont, \\"Segoe UI\\", \\"Roboto\\", \\"Oxygen\\", \\"Ubuntu\\", \\"Cantarell\\", \\"Fira Sans\\", \\"Droid Sans\\", \\"Helvetica Neue\\", \\"Lucida Grande\\", \\"Arial\\", sans-serif;color:#444;-webkit-font-smoothing:antialiased"
>
I respect my parents but I should be looking good on
<b>
iPhone 6
</b>
.
</div>
`;
27 changes: 27 additions & 0 deletions examples/official-storybook/stories/addon-viewport.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { baseFonts } from '@storybook/components';

import { withViewport, Viewport } from '@storybook/addon-viewport';

// eslint-disable-next-line react/prop-types
const Panel = ({ children }) => <div style={baseFonts}>{children}</div>;

storiesOf('Addons|Viewport', module).add('default', () => (
<Panel>I don't have problems being rendered using the default viewport.</Panel>
));

storiesOf('Addons|Viewport.Custom Default (Kindle Fire 2)', module)
.addDecorator(withViewport('kindleFire2'))
.add('Inherited', () => (
<Panel>
I've inherited <b>Kindle Fire 2</b> viewport from my parent.
</Panel>
))
.add('Overridden', () => (
<Viewport name="iphone6">
<Panel>
I respect my parents but I should be looking good on <b>iPhone 6</b>.
</Panel>
</Viewport>
));

0 comments on commit f212451

Please sign in to comment.