Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

configure tabs: title, visibility, order and disable #9095

Merged
merged 6 commits into from
Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions examples/official-storybook/manager.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
import { addons } from '@storybook/addons';

import addHeadWarning from './head-warning';

addHeadWarning('manager-head-not-loaded', 'Manager head not loaded');

addons.setConfig({
previewTabs: {
canvas: null,
'storybook/docs/panel': null,
'storybookjs/notes/panel': { title: 'Annotations', hidden: true },
graphiql: {
hidden: true,
},
},
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Addons/Docs/docs-only" />
<Meta title="Addons/Docs/docs-only" parameters={{ previewTabs: { 'canvas': { hidden: true }}}} />

# Documentation-only MDX

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Addons/Docs/markdown-docs" />
<Meta title="Addons/Docs/markdown-docs" parameters={{ previewTabs: { 'storybook/docs/panel': { index: -1 }}}}/>

# h1 Heading

Expand Down
5 changes: 5 additions & 0 deletions examples/official-storybook/stories/addon-graphql.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import React from 'react';

export default {
title: 'Addons/GraphQL',
parameters: {
previewTabs: {
graphiql: { hidden: false, title: 'GraphiQL' },
},
},
};

export const GetPikachu = () => <div>hello</div>;
Expand Down
5 changes: 5 additions & 0 deletions examples/official-storybook/stories/addon-notes.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ const giphyMarkdown = `

export default {
title: 'Addons/Notes',
parameters: {
previewTabs: {
'storybookjs/notes/panel': { hidden: false },
},
},
};

export const AddonNotes = () => (
Expand Down
91 changes: 71 additions & 20 deletions lib/ui/src/components/preview/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
import memoize from 'memoizerific';
import copy from 'copy-to-clipboard';

import { styled } from '@storybook/theming';
import { SET_CURRENT_STORY } from '@storybook/core-events';
import { types } from '@storybook/addons';
import addons, { types } from '@storybook/addons';
import merge from '@storybook/api/dist/lib/merge';
import { Icons, IconButton, TabButton, TabBar, Separator } from '@storybook/components';

import { Helmet } from 'react-helmet-async';
Expand Down Expand Up @@ -86,20 +86,24 @@ const defaultWrappers = [
const getTools = memoize(10)(
(getElements, queryParams, panels, api, options, storyId, viewMode, location, path, baseUrl) => {
const tools = getElementList(getElements, types.TOOL, [
panels.filter(p => p.id !== 'canvas').length
panels.filter(p => !p.hidden).length > 1
? {
render: () => (
<Fragment>
<TabBar key="tabs" scroll={false}>
{panels.map((t, index) => {
const to = t.route({ storyId, viewMode, path, location });
const isActive = path === to;
return (
<S.UnstyledLink key={t.id || `l${index}`} to={to}>
<TabButton active={isActive}>{t.title}</TabButton>
</S.UnstyledLink>
);
})}
{panels
.filter(p => !p.hidden)

Choose a reason for hiding this comment

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

nit: could store as const since this code is repeated here. Obviously the performance isn't a major issue but just in case the requirements to hide the tabs change in the future it would avoid bugs since there would only be one place to update.

Copy link
Member

Choose a reason for hiding this comment

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

@liamross I'm merging this. perhaps you'd be able to open a follow-up PR making it just that bit nicer?

Choose a reason for hiding this comment

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

@ndelangen I don't think it's important enough to actually change, was just a nitpick

.map((t, index) => {
const to = t.route({ storyId, viewMode, path, location });
const isActive = path === to;
return (
<S.UnstyledLink key={t.id || `l${index}`} to={to}>
<TabButton disabled={t.disabled} active={isActive}>
{t.title}
</TabButton>
</S.UnstyledLink>
);
})}
</TabBar>
<Separator />
</Fragment>
Expand Down Expand Up @@ -199,15 +203,16 @@ const getDocumentTitle = description => {
};

class Preview extends Component {
shouldComponentUpdate({ storyId, viewMode, options, queryParams }) {
shouldComponentUpdate({ storyId, viewMode, options, queryParams, parameters }) {
const { props } = this;

return (
options.isFullscreen !== props.options.isFullscreen ||
options.isToolshown !== props.options.isToolshown ||
viewMode !== props.viewMode ||
storyId !== props.storyId ||
queryParams !== props.queryParams
queryParams !== props.queryParams ||
parameters !== props.parameters
);
}

Expand All @@ -233,11 +238,11 @@ class Preview extends Component {
options,
description,
baseUrl,
parameters,
} = this.props;

const toolbarHeight = options.isToolshown ? 40 : 0;
const wrappers = getElementList(getElements, types.PREVIEW, defaultWrappers);
const panels = getElementList(getElements, types.TAB, [
let panels = getElementList(getElements, types.TAB, [
{
route: p => `/story/${p.storyId}`,
match: p => p.viewMode && p.viewMode.match(/^(story|docs)$/),
Expand All @@ -264,6 +269,42 @@ class Preview extends Component {
id: 'canvas',
},
]);
const { previewTabs } = addons.getConfig();
const parametersTabs = parameters ? parameters.previewTabs : undefined;
if (previewTabs || parametersTabs) {
// deep merge global and local settings
const tabs = merge(previewTabs, parametersTabs);
const arrTabs = Object.keys(tabs).map((key, index) => ({
index,
...(typeof tabs[key] === 'string' ? { title: tabs[key] } : tabs[key]),
id: key,
}));
panels = panels
.filter(panel => {
const t = arrTabs.find(tab => tab.id === panel.id);
return t === undefined || t.id === 'canvas' || !t.hidden;
})
.map((panel, index) => ({ ...panel, index }))
.sort((p1, p2) => {
const tab_1 = arrTabs.find(tab => tab.id === p1.id);
const index_1 = tab_1 ? tab_1.index : arrTabs.length + p1.index;
const tab_2 = arrTabs.find(tab => tab.id === p2.id);
const index_2 = tab_2 ? tab_2.index : arrTabs.length + p2.index;
return index_1 - index_2;
})
.map(panel => {
const t = arrTabs.find(tab => tab.id === panel.id);
if (t) {
return {
...panel,
title: t.title || panel.title,
disabled: t.disabled,
hidden: t.hidden,
};
}
return panel;
});
}
const { left, right } = getTools(
getElements,
queryParams,
Expand All @@ -285,10 +326,12 @@ class Preview extends Component {
<title>{getDocumentTitle(description)}</title>
</Helmet>
)}
<Toolbar key="toolbar" shown={options.isToolshown} border>
<Fragment key="left">{left}</Fragment>
<Fragment key="right">{right}</Fragment>
</Toolbar>
{(left || right) && (
<Toolbar key="toolbar" shown={options.isToolshown} border>
<Fragment key="left">{left}</Fragment>
<Fragment key="right">{right}</Fragment>
</Toolbar>
)}
<S.FrameWrap key="frame" offset={toolbarHeight}>
{panels.map(p => (
<Fragment key={p.id || p.key}>
Expand Down Expand Up @@ -322,6 +365,13 @@ Preview.propTypes = {
isToolshown: PropTypes.bool,
}).isRequired,
baseUrl: PropTypes.string,
parameters: PropTypes.shape({
previewTabs: PropTypes.shape({
title: PropTypes.string,
hidden: PropTypes.string,
disabled: PropTypes.string,
}),
}),
};

Preview.defaultProps = {
Expand All @@ -331,6 +381,7 @@ Preview.defaultProps = {
description: undefined,
baseUrl: 'iframe.html',
customCanvas: undefined,
parameters: undefined,
};

export { Preview };
41 changes: 22 additions & 19 deletions lib/ui/src/containers/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,20 @@ const getDescription = (storiesHash, storyId) => {
return storyInfo ? addExtraWhiteSpace(`${storyInfo.kind} - ${storyInfo.name}`) : '';
};

const mapper = ({ api, state: { layout, location, customQueryParams, storiesHash, storyId } }) => ({
api,
getElements: api.getElements,
options: layout,
description: getDescription(storiesHash, storyId),
...api.getUrlState(),
queryParams: customQueryParams,
location,
});
const mapper = ({ api, state }) => {
const { layout, location, customQueryParams, storiesHash, storyId } = state;
const { parameters } = storiesHash[storyId] || {};
return {
api,
getElements: api.getElements,
options: layout,
description: getDescription(storiesHash, storyId),
...api.getUrlState(),
queryParams: customQueryParams,
location,
parameters,
};
};

function getBaseUrl() {
try {
Expand All @@ -35,16 +40,14 @@ function getBaseUrl() {

const PreviewConnected = React.memo(props => (
<Consumer filter={mapper}>
{fromState => {
return (
<Preview
{...props}
baseUrl={getBaseUrl()}
{...fromState}
customCanvas={fromState.api.renderPreview}
/>
);
}}
{fromState => (
<Preview
{...props}
baseUrl={getBaseUrl()}
{...fromState}
customCanvas={fromState.api.renderPreview}
/>
)}
</Consumer>
));
PreviewConnected.displayName = 'PreviewConnected';
Expand Down