mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-07 07:21:17 +08:00
Merge pull request #3337 from denzenin/mobile-device-view
Mobile device view: toggling stories panel with ☰ button
This commit is contained in:
commit
b6178a2a96
@ -9,7 +9,9 @@ function renderMarkdown(text, options) {
|
|||||||
const decorator = options => {
|
const decorator = options => {
|
||||||
const channel = addons.getChannel();
|
const channel = addons.getChannel();
|
||||||
return (getStory, context) => {
|
return (getStory, context) => {
|
||||||
const { parameters: { notes } } = context;
|
const {
|
||||||
|
parameters: { notes },
|
||||||
|
} = context;
|
||||||
const storyOptions = notes || options;
|
const storyOptions = notes || options;
|
||||||
|
|
||||||
if (storyOptions) {
|
if (storyOptions) {
|
||||||
|
@ -8,7 +8,6 @@ setOptions({
|
|||||||
name: 'CRA Kitchen Sink',
|
name: 'CRA Kitchen Sink',
|
||||||
url: 'https://github.com/storybooks/storybook/tree/master/examples/cra-kitchen-sink',
|
url: 'https://github.com/storybooks/storybook/tree/master/examples/cra-kitchen-sink',
|
||||||
goFullScreen: false,
|
goFullScreen: false,
|
||||||
showStoriesPanel: true,
|
|
||||||
showAddonsPanel: true,
|
showAddonsPanel: true,
|
||||||
showSearchBox: false,
|
showSearchBox: false,
|
||||||
addonPanelInRight: true,
|
addonPanelInRight: true,
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
import actions from './actions';
|
import actions from './actions';
|
||||||
|
import checkIfMobileDevice from '../ui/libs/is_mobile_device';
|
||||||
|
|
||||||
|
const { userAgent } = global.window.navigator;
|
||||||
|
const isMobileDevice = checkIfMobileDevice(userAgent);
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
actions,
|
actions,
|
||||||
defaultState: {
|
defaultState: {
|
||||||
|
isMobileDevice,
|
||||||
shortcutOptions: {
|
shortcutOptions: {
|
||||||
goFullScreen: false,
|
goFullScreen: false,
|
||||||
showStoriesPanel: true,
|
showStoriesPanel: !isMobileDevice,
|
||||||
showAddonPanel: true,
|
showAddonPanel: true,
|
||||||
showSearchBox: false,
|
showSearchBox: false,
|
||||||
addonPanelInRight: false,
|
addonPanelInRight: false,
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Storyshots ui/stories/Header mobile-view 1`] = `
|
||||||
|
<div
|
||||||
|
style="background:none;margin:10px 0;display:flex"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
style="text-transform:uppercase;font-size:12px;font-weight:bolder;color:rgb(130, 130, 130);border:1px solid rgb(193, 193, 193);text-align:center;border-radius:2px;cursor:pointer;display:inlineBlock;padding:0;margin:0 15px;background-color:inherit;outline:0;width:30px;flex-shrink:0;padding-bottom:2px"
|
||||||
|
>
|
||||||
|
☰
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
href="http://google.com"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
style="text-decoration:none;flex-grow:1;display:flex;align-items:center;justify-content:center;border:none;border-radius:2px"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
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:#828282;-webkit-font-smoothing:antialiased;text-transform:uppercase;letter-spacing:1.5px;font-size:12px;font-weight:bolder;text-align:center;cursor:pointer;padding:5px;margin:0;overflow:hidden"
|
||||||
|
>
|
||||||
|
name
|
||||||
|
</h3>
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
style="text-transform:uppercase;font-size:12px;font-weight:bolder;color:rgb(130, 130, 130);border:1px solid rgb(193, 193, 193);text-align:center;border-radius:2px;cursor:pointer;display:inlineBlock;padding:0;margin:0 15px;background-color:inherit;outline:0;width:30px;flex-shrink:0"
|
||||||
|
>
|
||||||
|
⌘
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Storyshots ui/stories/Header simple 1`] = `
|
||||||
|
<div
|
||||||
|
style="background:#F7F7F7;margin:0 0 10px;display:flex"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="http://google.com"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
style="text-decoration:none;flex-grow:1;display:flex;align-items:center;justify-content:center;border:1px solid rgb(193, 193, 193);border-radius:2px"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<h3
|
||||||
|
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:#828282;-webkit-font-smoothing:antialiased;text-transform:uppercase;letter-spacing:1.5px;font-size:12px;font-weight:bolder;text-align:center;cursor:pointer;padding:5px;margin:0;overflow:hidden"
|
||||||
|
>
|
||||||
|
name
|
||||||
|
</h3>
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
style="text-transform:uppercase;font-size:12px;font-weight:bolder;color:rgb(130, 130, 130);border:1px solid rgb(193, 193, 193);text-align:center;border-radius:2px;cursor:pointer;display:inlineBlock;padding:0;margin:0 0 0 5px;background-color:inherit;outline:0;width:30px;flex-shrink:0"
|
||||||
|
>
|
||||||
|
⌘
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -2,11 +2,11 @@ import PropTypes from 'prop-types';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { baseFonts } from '@storybook/components';
|
import { baseFonts } from '@storybook/components';
|
||||||
|
|
||||||
const wrapperStyle = {
|
const wrapperStyle = isMobileDevice => ({
|
||||||
background: '#F7F7F7',
|
background: isMobileDevice ? 'none' : '#F7F7F7',
|
||||||
marginBottom: 10,
|
margin: isMobileDevice ? '10px 0' : '0 0 10px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
};
|
});
|
||||||
|
|
||||||
const headingStyle = {
|
const headingStyle = {
|
||||||
...baseFonts,
|
...baseFonts,
|
||||||
@ -22,7 +22,7 @@ const headingStyle = {
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
};
|
};
|
||||||
|
|
||||||
const shortcutIconStyle = {
|
const iconStyle = isMobileDevice => ({
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: 'bolder',
|
fontWeight: 'bolder',
|
||||||
@ -33,30 +33,47 @@ const shortcutIconStyle = {
|
|||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'inlineBlock',
|
display: 'inlineBlock',
|
||||||
padding: 0,
|
padding: 0,
|
||||||
margin: '0 0 0 5px',
|
margin: isMobileDevice ? '0 15px' : '0 0 0 5px',
|
||||||
backgroundColor: 'inherit',
|
backgroundColor: 'inherit',
|
||||||
outline: 0,
|
outline: 0,
|
||||||
width: 30,
|
width: 30,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const burgerIconStyle = {
|
||||||
|
...iconStyle(true),
|
||||||
|
paddingBottom: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const linkStyle = {
|
const linkStyle = isMobileDevice => ({
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
border: '1px solid rgb(193, 193, 193)',
|
border: isMobileDevice ? 'none' : '1px solid rgb(193, 193, 193)',
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
};
|
});
|
||||||
|
|
||||||
const Header = ({ openShortcutsHelp, name, url, enableShortcutsHelp }) => (
|
const Header = ({
|
||||||
<div style={wrapperStyle}>
|
openShortcutsHelp,
|
||||||
<a style={linkStyle} href={url} target="_blank" rel="noopener noreferrer">
|
onBurgerButtonClick,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
enableShortcutsHelp,
|
||||||
|
isMobileDevice,
|
||||||
|
}) => (
|
||||||
|
<div style={wrapperStyle(isMobileDevice)}>
|
||||||
|
{isMobileDevice && (
|
||||||
|
<button style={burgerIconStyle} onClick={onBurgerButtonClick}>
|
||||||
|
☰
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<a style={linkStyle(isMobileDevice)} href={url} target="_blank" rel="noopener noreferrer">
|
||||||
<h3 style={headingStyle}>{name}</h3>
|
<h3 style={headingStyle}>{name}</h3>
|
||||||
</a>
|
</a>
|
||||||
{enableShortcutsHelp && (
|
{enableShortcutsHelp && (
|
||||||
<button style={shortcutIconStyle} onClick={openShortcutsHelp}>
|
<button style={iconStyle(isMobileDevice)} onClick={openShortcutsHelp}>
|
||||||
⌘
|
⌘
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
@ -65,15 +82,19 @@ const Header = ({ openShortcutsHelp, name, url, enableShortcutsHelp }) => (
|
|||||||
|
|
||||||
Header.defaultProps = {
|
Header.defaultProps = {
|
||||||
openShortcutsHelp: null,
|
openShortcutsHelp: null,
|
||||||
|
onBurgerButtonClick: null,
|
||||||
enableShortcutsHelp: true,
|
enableShortcutsHelp: true,
|
||||||
name: '',
|
name: '',
|
||||||
url: '',
|
url: '',
|
||||||
|
isMobileDevice: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
Header.propTypes = {
|
Header.propTypes = {
|
||||||
openShortcutsHelp: PropTypes.func,
|
openShortcutsHelp: PropTypes.func,
|
||||||
|
onBurgerButtonClick: PropTypes.func,
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
url: PropTypes.string,
|
url: PropTypes.string,
|
||||||
|
isMobileDevice: PropTypes.bool,
|
||||||
enableShortcutsHelp: PropTypes.bool,
|
enableShortcutsHelp: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
19
lib/ui/src/modules/ui/components/header.stories.js
Normal file
19
lib/ui/src/modules/ui/components/header.stories.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { storiesOf } from '@storybook/react';
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
|
||||||
|
import Header from './header';
|
||||||
|
|
||||||
|
const openShortcutsHelp = action('openShortcutsHelp');
|
||||||
|
storiesOf('ui/stories/Header', module)
|
||||||
|
.add('simple', () => (
|
||||||
|
<Header name="name" url="http://google.com" openShortcutsHelp={openShortcutsHelp} />
|
||||||
|
))
|
||||||
|
.add('mobile-view', () => (
|
||||||
|
<Header
|
||||||
|
name="name"
|
||||||
|
url="http://google.com"
|
||||||
|
openShortcutsHelp={openShortcutsHelp}
|
||||||
|
isMobileDevice
|
||||||
|
/>
|
||||||
|
));
|
@ -130,7 +130,7 @@ class Layout extends React.Component {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.layerSizes = getSavedSizes(defaultSizes);
|
this.layerSizes = getSavedSizes(defaultSizes, props.isMobileDevice);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
previewPanelDimensions: {
|
previewPanelDimensions: {
|
||||||
@ -188,13 +188,15 @@ class Layout extends React.Component {
|
|||||||
goFullScreen,
|
goFullScreen,
|
||||||
showStoriesPanel,
|
showStoriesPanel,
|
||||||
showAddonPanel,
|
showAddonPanel,
|
||||||
addonPanelInRight,
|
|
||||||
addonPanel,
|
addonPanel,
|
||||||
storiesPanel,
|
storiesPanel,
|
||||||
preview,
|
preview,
|
||||||
|
isMobileDevice,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { previewPanelDimensions } = this.state;
|
const { previewPanelDimensions } = this.state;
|
||||||
|
|
||||||
|
const addonPanelInRight = isMobileDevice ? false : this.props.addonPanelInRight;
|
||||||
|
|
||||||
const storiesPanelOnTop = false;
|
const storiesPanelOnTop = false;
|
||||||
|
|
||||||
let previewStyle = normalPreviewStyle;
|
let previewStyle = normalPreviewStyle;
|
||||||
@ -203,7 +205,7 @@ class Layout extends React.Component {
|
|||||||
previewStyle = fullScreenPreviewStyle;
|
previewStyle = fullScreenPreviewStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sizes = getSavedSizes(this.layerSizes);
|
const sizes = getSavedSizes(this.layerSizes, isMobileDevice);
|
||||||
|
|
||||||
const storiesPanelDefaultSize = !storiesPanelOnTop
|
const storiesPanelDefaultSize = !storiesPanelOnTop
|
||||||
? sizes.storiesPanel.left
|
? sizes.storiesPanel.left
|
||||||
@ -219,8 +221,8 @@ class Layout extends React.Component {
|
|||||||
<div style={rootStyle}>
|
<div style={rootStyle}>
|
||||||
<SplitPane
|
<SplitPane
|
||||||
split={storiesSplit}
|
split={storiesSplit}
|
||||||
allowResize={showStoriesPanel}
|
allowResize={isMobileDevice ? false : showStoriesPanel}
|
||||||
minSize={150}
|
minSize={isMobileDevice ? 0 : 150}
|
||||||
maxSize={-400}
|
maxSize={-400}
|
||||||
size={showStoriesPanel ? storiesPanelDefaultSize : 1}
|
size={showStoriesPanel ? storiesPanelDefaultSize : 1}
|
||||||
defaultSize={storiesPanelDefaultSize}
|
defaultSize={storiesPanelDefaultSize}
|
||||||
@ -235,7 +237,7 @@ class Layout extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<SplitPane
|
<SplitPane
|
||||||
split={addonSplit}
|
split={addonSplit}
|
||||||
allowResize={showAddonPanel}
|
allowResize={isMobileDevice ? false : showAddonPanel}
|
||||||
primary="second"
|
primary="second"
|
||||||
minSize={addonPanelInRight ? 200 : 100}
|
minSize={addonPanelInRight ? 200 : 100}
|
||||||
maxSize={-200}
|
maxSize={-200}
|
||||||
@ -285,6 +287,11 @@ Layout.propTypes = {
|
|||||||
preview: PropTypes.func.isRequired,
|
preview: PropTypes.func.isRequired,
|
||||||
addonPanel: PropTypes.func.isRequired,
|
addonPanel: PropTypes.func.isRequired,
|
||||||
addonPanelInRight: PropTypes.bool.isRequired,
|
addonPanelInRight: PropTypes.bool.isRequired,
|
||||||
|
isMobileDevice: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
Layout.defaultProps = {
|
||||||
|
isMobileDevice: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Layout;
|
export default Layout;
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Storyshots ui/stories/Header simple 1`] = `
|
|
||||||
<div
|
|
||||||
style="background:#F7F7F7;margin-bottom:10px;display:flex"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
href="http://google.com"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
style="text-decoration:none;flex-grow:1;display:flex;align-items:center;justify-content:center;border:1px solid rgb(193, 193, 193);border-radius:2px"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<h3
|
|
||||||
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:#828282;-webkit-font-smoothing:antialiased;text-transform:uppercase;letter-spacing:1.5px;font-size:12px;font-weight:bolder;text-align:center;cursor:pointer;padding:5px;margin:0;overflow:hidden"
|
|
||||||
>
|
|
||||||
name
|
|
||||||
</h3>
|
|
||||||
</a>
|
|
||||||
<button
|
|
||||||
style="text-transform:uppercase;font-size:12px;font-weight:bolder;color:rgb(130, 130, 130);border:1px solid rgb(193, 193, 193);text-align:center;border-radius:2px;cursor:pointer;display:inlineBlock;padding:0;margin:0 0 0 5px;background-color:inherit;outline:0;width:30px;flex-shrink:0"
|
|
||||||
>
|
|
||||||
⌘
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
@ -5,7 +5,7 @@ exports[`Storyshots ui/stories/StoriesPanel default 1`] = `
|
|||||||
style="padding:10px 0 10px 10px"
|
style="padding:10px 0 10px 10px"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style="background:#F7F7F7;margin-bottom:10px;display:flex"
|
style="background:#F7F7F7;margin:0 0 10px;display:flex"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href=""
|
href=""
|
||||||
@ -49,7 +49,7 @@ exports[`Storyshots ui/stories/StoriesPanel storiesHierarchies exists but is emp
|
|||||||
style="padding:10px 0 10px 10px"
|
style="padding:10px 0 10px 10px"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style="background:#F7F7F7;margin-bottom:10px;display:flex"
|
style="background:#F7F7F7;margin:0 0 10px;display:flex"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href=""
|
href=""
|
||||||
@ -88,12 +88,37 @@ exports[`Storyshots ui/stories/StoriesPanel storiesHierarchies exists but is emp
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Storyshots ui/stories/StoriesPanel when open on mobile device 1`] = `
|
||||||
|
<div
|
||||||
|
style="padding:10px 0 10px 10px"
|
||||||
|
>
|
||||||
|
<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;border:1px solid #ECECEC;border-radius:2px;position:relative"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style="background:#F7F7F7"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
name="filter-text"
|
||||||
|
placeholder="Filter"
|
||||||
|
style="font-size:12px;color:#828282;padding:5px;display:block;width:100%;box-sizing:border-box;outline:none;border:0;height:26px"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style="height:calc(100vh - 105px);margin-top:10px;overflow:auto"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Storyshots ui/stories/StoriesPanel with storiesHierarchies prop 1`] = `
|
exports[`Storyshots ui/stories/StoriesPanel with storiesHierarchies prop 1`] = `
|
||||||
<div
|
<div
|
||||||
style="padding:10px 0 10px 10px"
|
style="padding:10px 0 10px 10px"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style="background:#F7F7F7;margin-bottom:10px;display:flex"
|
style="background:#F7F7F7;margin:0 0 10px;display:flex"
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href=""
|
href=""
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { storiesOf } from '@storybook/react';
|
|
||||||
import { action } from '@storybook/addon-actions';
|
|
||||||
|
|
||||||
import Header from './header';
|
|
||||||
|
|
||||||
const openShortcutsHelp = action('openShortcutsHelp');
|
|
||||||
storiesOf('ui/stories/Header', module).add('simple', () => (
|
|
||||||
<Header name="name" url="http://google.com" openShortcutsHelp={openShortcutsHelp} />
|
|
||||||
));
|
|
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import pick from 'lodash.pick';
|
import pick from 'lodash.pick';
|
||||||
import Header from './header';
|
import Header from '../header';
|
||||||
import Stories from './stories_tree';
|
import Stories from './stories_tree';
|
||||||
import TextFilter from './text_filter';
|
import TextFilter from './text_filter';
|
||||||
|
|
||||||
@ -52,16 +52,19 @@ class StoriesPanel extends Component {
|
|||||||
storyFilter,
|
storyFilter,
|
||||||
url,
|
url,
|
||||||
shortcutOptions,
|
shortcutOptions,
|
||||||
|
isMobileDevice,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={mainStyle}>
|
<div style={mainStyle}>
|
||||||
|
{!isMobileDevice && (
|
||||||
<Header
|
<Header
|
||||||
name={name}
|
name={name}
|
||||||
url={url}
|
url={url}
|
||||||
openShortcutsHelp={shortcutOptions.enableShortcuts ? openShortcutsHelp : null}
|
openShortcutsHelp={shortcutOptions.enableShortcuts ? openShortcutsHelp : null}
|
||||||
enableShortcutsHelp={shortcutOptions.enableShortcuts}
|
enableShortcutsHelp={shortcutOptions.enableShortcuts}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
<TextFilter
|
<TextFilter
|
||||||
text={storyFilter}
|
text={storyFilter}
|
||||||
onClear={() => onStoryFilter('')}
|
onClear={() => onStoryFilter('')}
|
||||||
@ -78,6 +81,7 @@ StoriesPanel.defaultProps = {
|
|||||||
storyFilter: null,
|
storyFilter: null,
|
||||||
onStoryFilter: () => {},
|
onStoryFilter: () => {},
|
||||||
openShortcutsHelp: null,
|
openShortcutsHelp: null,
|
||||||
|
isMobileDevice: false,
|
||||||
name: '',
|
name: '',
|
||||||
url: '',
|
url: '',
|
||||||
shortcutOptions: {
|
shortcutOptions: {
|
||||||
@ -102,6 +106,7 @@ StoriesPanel.propTypes = {
|
|||||||
onStoryFilter: PropTypes.func,
|
onStoryFilter: PropTypes.func,
|
||||||
|
|
||||||
openShortcutsHelp: PropTypes.func,
|
openShortcutsHelp: PropTypes.func,
|
||||||
|
isMobileDevice: PropTypes.bool,
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
url: PropTypes.string,
|
url: PropTypes.string,
|
||||||
shortcutOptions: PropTypes.shape({
|
shortcutOptions: PropTypes.shape({
|
||||||
|
@ -12,7 +12,7 @@ const decorator = withLifecyle({
|
|||||||
setContext({
|
setContext({
|
||||||
clientStore: {
|
clientStore: {
|
||||||
getAll() {
|
getAll() {
|
||||||
return { shortcutOptions: {} };
|
return { shortcutOptions: {}, uiOptions: {}, actions: { ui: {} } };
|
||||||
},
|
},
|
||||||
subscribe() {},
|
subscribe() {},
|
||||||
},
|
},
|
||||||
@ -51,4 +51,12 @@ storiesOf('ui/stories/StoriesPanel', module)
|
|||||||
openShortcutsHelp={openShortcutsHelp}
|
openShortcutsHelp={openShortcutsHelp}
|
||||||
onStoryFilter={onStoryFilter}
|
onStoryFilter={onStoryFilter}
|
||||||
/>
|
/>
|
||||||
|
))
|
||||||
|
.add('when open on mobile device', () => (
|
||||||
|
<StoriesPanel
|
||||||
|
storiesHierarchies={createHierarchies([])}
|
||||||
|
openShortcutsHelp={openShortcutsHelp}
|
||||||
|
onStoryFilter={onStoryFilter}
|
||||||
|
isMobileDevice
|
||||||
|
/>
|
||||||
));
|
));
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import StoriesPanel from './index';
|
import StoriesPanel from './index';
|
||||||
import Header from './header';
|
import Header from '../header';
|
||||||
import TextFilter from './text_filter';
|
import TextFilter from './text_filter';
|
||||||
import Stories from './stories_tree';
|
import Stories from './stories_tree';
|
||||||
import { createHierarchies } from '../../libs/hierarchy';
|
import { createHierarchies } from '../../libs/hierarchy';
|
||||||
|
@ -20,8 +20,7 @@ describe('manager.ui.components.stories_panel.stories_tree', () => {
|
|||||||
},
|
},
|
||||||
subscribe() {},
|
subscribe() {},
|
||||||
},
|
},
|
||||||
})
|
}));
|
||||||
);
|
|
||||||
|
|
||||||
afterEach(() => setContext(null));
|
afterEach(() => setContext(null));
|
||||||
|
|
||||||
|
37
lib/ui/src/modules/ui/containers/header.js
Executable file
37
lib/ui/src/modules/ui/containers/header.js
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
import pick from 'lodash.pick';
|
||||||
|
import Header from '../components/header';
|
||||||
|
import genPoddaLoader from '../libs/gen_podda_loader';
|
||||||
|
import compose from '../../../compose';
|
||||||
|
|
||||||
|
export const mapper = (state, props, { actions }) => {
|
||||||
|
const currentOptions = pick(
|
||||||
|
state.shortcutOptions,
|
||||||
|
'showStoriesPanel',
|
||||||
|
'enableShortcuts',
|
||||||
|
'addonPanelInRight'
|
||||||
|
);
|
||||||
|
|
||||||
|
const actionMap = actions();
|
||||||
|
const { uiOptions, isMobileDevice } = state;
|
||||||
|
const { name = '', url = '' } = uiOptions;
|
||||||
|
|
||||||
|
const handleBurgerButtonClick = () => {
|
||||||
|
actionMap.shortcuts.setOptions({
|
||||||
|
showStoriesPanel: !currentOptions.showStoriesPanel,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const addonPanelInRight = isMobileDevice ? false : currentOptions.addonPanelInRight;
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
enableShortcutsHelp: currentOptions.enableShortcuts,
|
||||||
|
openShortcutsHelp: actionMap.ui.toggleShortcutsHelp,
|
||||||
|
onBurgerButtonClick: handleBurgerButtonClick,
|
||||||
|
isMobileDevice,
|
||||||
|
addonPanelInRight,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default compose(genPoddaLoader(mapper))(Header);
|
45
lib/ui/src/modules/ui/containers/header.test.js
Executable file
45
lib/ui/src/modules/ui/containers/header.test.js
Executable file
@ -0,0 +1,45 @@
|
|||||||
|
import { mapper } from './header';
|
||||||
|
|
||||||
|
describe('manager.ui.containers.header', () => {
|
||||||
|
describe('mapper', () => {
|
||||||
|
test('should give correct data', () => {
|
||||||
|
const uiOptions = {
|
||||||
|
name: 'foo',
|
||||||
|
url: 'bar',
|
||||||
|
};
|
||||||
|
const shortcutOptions = {
|
||||||
|
showStoriesPanel: 'aa',
|
||||||
|
enableShortcuts: true,
|
||||||
|
addonPanelInRight: true,
|
||||||
|
};
|
||||||
|
const toggleShortcutsHelp = () => 'toggleShortcutsHelp';
|
||||||
|
const props = {};
|
||||||
|
const env = {
|
||||||
|
actions: () => ({
|
||||||
|
ui: {
|
||||||
|
toggleShortcutsHelp,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
const state = {
|
||||||
|
uiOptions,
|
||||||
|
shortcutOptions,
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = mapper(state, props, env);
|
||||||
|
|
||||||
|
expect(result.openShortcutsHelp).toBe(toggleShortcutsHelp);
|
||||||
|
expect(result.enableShortcutsHelp).toEqual(shortcutOptions.enableShortcuts);
|
||||||
|
expect(result.addonPanelInRight).toEqual(true);
|
||||||
|
|
||||||
|
const stateWhenMobileDevice = {
|
||||||
|
uiOptions,
|
||||||
|
shortcutOptions,
|
||||||
|
isMobileDevice: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const resultWhenMobileDevice = mapper(stateWhenMobileDevice, props, env);
|
||||||
|
expect(resultWhenMobileDevice.addonPanelInRight).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -3,7 +3,20 @@ import Layout from '../components/layout';
|
|||||||
import genPoddaLoader from '../libs/gen_podda_loader';
|
import genPoddaLoader from '../libs/gen_podda_loader';
|
||||||
import compose from '../../../compose';
|
import compose from '../../../compose';
|
||||||
|
|
||||||
export const mapper = ({ shortcutOptions }) =>
|
export const mapper = state => {
|
||||||
pick(shortcutOptions, 'showStoriesPanel', 'showAddonPanel', 'goFullScreen', 'addonPanelInRight');
|
const { shortcutOptions, isMobileDevice } = state;
|
||||||
|
const currentOptions = pick(
|
||||||
|
shortcutOptions,
|
||||||
|
'showStoriesPanel',
|
||||||
|
'showAddonPanel',
|
||||||
|
'goFullScreen',
|
||||||
|
'addonPanelInRight'
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...currentOptions,
|
||||||
|
isMobileDevice,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default compose(genPoddaLoader(mapper))(Layout);
|
export default compose(genPoddaLoader(mapper))(Layout);
|
||||||
|
@ -13,14 +13,23 @@ import {
|
|||||||
export const mapper = (state, props, { actions }) => {
|
export const mapper = (state, props, { actions }) => {
|
||||||
const actionMap = actions();
|
const actionMap = actions();
|
||||||
|
|
||||||
const { stories, selectedKind, selectedStory, uiOptions, storyFilter, shortcutOptions } = state;
|
|
||||||
const {
|
const {
|
||||||
name,
|
stories,
|
||||||
url,
|
selectedKind,
|
||||||
|
selectedStory,
|
||||||
|
uiOptions,
|
||||||
|
storyFilter,
|
||||||
|
shortcutOptions,
|
||||||
|
isMobileDevice,
|
||||||
|
} = state;
|
||||||
|
|
||||||
|
const {
|
||||||
sortStoriesByKind,
|
sortStoriesByKind,
|
||||||
hierarchySeparator,
|
hierarchySeparator,
|
||||||
hierarchyRootSeparator,
|
hierarchyRootSeparator,
|
||||||
sidebarAnimations,
|
sidebarAnimations,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
} = uiOptions;
|
} = uiOptions;
|
||||||
|
|
||||||
const preparedStories = prepareStoriesForHierarchy(
|
const preparedStories = prepareStoriesForHierarchy(
|
||||||
@ -52,9 +61,9 @@ export const mapper = (state, props, { actions }) => {
|
|||||||
|
|
||||||
storyFilter,
|
storyFilter,
|
||||||
onStoryFilter: actionMap.ui.setStoryFilter,
|
onStoryFilter: actionMap.ui.setStoryFilter,
|
||||||
|
|
||||||
openShortcutsHelp: actionMap.ui.toggleShortcutsHelp,
|
openShortcutsHelp: actionMap.ui.toggleShortcutsHelp,
|
||||||
sidebarAnimations,
|
sidebarAnimations,
|
||||||
|
isMobileDevice,
|
||||||
name,
|
name,
|
||||||
url,
|
url,
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,6 @@ describe('manager.ui.containers.stories_panel', () => {
|
|||||||
url: 'bar',
|
url: 'bar',
|
||||||
};
|
};
|
||||||
const selectStory = () => 'selectStory';
|
const selectStory = () => 'selectStory';
|
||||||
const toggleShortcutsHelp = () => 'toggleShortcutsHelp';
|
|
||||||
const setStoryFilter = () => 'setStoryFilter';
|
const setStoryFilter = () => 'setStoryFilter';
|
||||||
const props = {};
|
const props = {};
|
||||||
const env = {
|
const env = {
|
||||||
@ -21,7 +20,6 @@ describe('manager.ui.containers.stories_panel', () => {
|
|||||||
selectStory,
|
selectStory,
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
toggleShortcutsHelp,
|
|
||||||
setStoryFilter,
|
setStoryFilter,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -57,7 +55,6 @@ describe('manager.ui.containers.stories_panel', () => {
|
|||||||
expect(result.storyFilter).toBe(null);
|
expect(result.storyFilter).toBe(null);
|
||||||
expect(result.onSelectStory).toBe(selectStory);
|
expect(result.onSelectStory).toBe(selectStory);
|
||||||
expect(result.onStoryFilter).toBe(setStoryFilter);
|
expect(result.onStoryFilter).toBe(setStoryFilter);
|
||||||
expect(result.openShortcutsHelp).toBe(toggleShortcutsHelp);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should filter stories according to the given filter', () => {
|
test('should filter stories according to the given filter', () => {
|
||||||
|
7
lib/ui/src/modules/ui/libs/is_mobile_device.js
Normal file
7
lib/ui/src/modules/ui/libs/is_mobile_device.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export default userAgent => {
|
||||||
|
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
28
lib/ui/src/modules/ui/libs/is_mobile_device.test.js
Normal file
28
lib/ui/src/modules/ui/libs/is_mobile_device.test.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import isMobileDevice from './is_mobile_device';
|
||||||
|
|
||||||
|
describe('manager.ui.libs.is_mobile_device', () => {
|
||||||
|
it('should detect if storybook is open on mobile device', () => {
|
||||||
|
// chrome
|
||||||
|
expect(
|
||||||
|
isMobileDevice(
|
||||||
|
'Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19'
|
||||||
|
)
|
||||||
|
).toEqual(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
isMobileDevice('Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0')
|
||||||
|
).toEqual(true);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
isMobileDevice(
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36'
|
||||||
|
)
|
||||||
|
).toEqual(false);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
isMobileDevice(
|
||||||
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0.3 Safari/604.5.6'
|
||||||
|
)
|
||||||
|
).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
@ -6,11 +6,12 @@ import StoriesPanel from './containers/stories_panel';
|
|||||||
import AddonPanel from './containers/addon_panel';
|
import AddonPanel from './containers/addon_panel';
|
||||||
import ShortcutsHelp from './containers/shortcuts_help';
|
import ShortcutsHelp from './containers/shortcuts_help';
|
||||||
import SearchBox from './containers/search_box';
|
import SearchBox from './containers/search_box';
|
||||||
|
import Header from './containers/header';
|
||||||
|
|
||||||
export default function(injectDeps, { clientStore, provider, domNode }) {
|
export default function(injectDeps, { clientStore, provider, domNode }) {
|
||||||
|
const state = clientStore.getAll();
|
||||||
// generate preview
|
// generate preview
|
||||||
const Preview = () => {
|
const Preview = () => {
|
||||||
const state = clientStore.getAll();
|
|
||||||
const preview = provider.renderPreview(state.selectedKind, state.selectedStory);
|
const preview = provider.renderPreview(state.selectedKind, state.selectedStory);
|
||||||
return preview;
|
return preview;
|
||||||
};
|
};
|
||||||
@ -22,6 +23,7 @@ export default function(injectDeps, { clientStore, provider, domNode }) {
|
|||||||
|
|
||||||
const root = (
|
const root = (
|
||||||
<Container>
|
<Container>
|
||||||
|
{state.isMobileDevice && <Header />}
|
||||||
<Layout
|
<Layout
|
||||||
storiesPanel={() => <StoriesPanel />}
|
storiesPanel={() => <StoriesPanel />}
|
||||||
preview={() => <Preview />}
|
preview={() => <Preview />}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user