mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-02 05:03:44 +08:00
Add tests, docs, example
This commit is contained in:
parent
a8e6d1bcd9
commit
82f241e2c0
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { addons, types } from '@storybook/addons';
|
||||
|
||||
import { ADDON_ID, PANEL_ID } from './constants';
|
||||
import { ADDON_ID, PANEL_ID, PARAM_KEY } from './constants';
|
||||
import { CssResourcePanel } from './css-resource-panel';
|
||||
|
||||
addons.register(ADDON_ID, api => {
|
||||
@ -10,5 +10,6 @@ addons.register(ADDON_ID, api => {
|
||||
type: types.PANEL,
|
||||
title: 'CSS resources',
|
||||
render: ({ active }) => <CssResourcePanel key={PANEL_ID} api={api} active={active} />,
|
||||
paramKey: PARAM_KEY,
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import addons, { types } from '@storybook/addons';
|
||||
|
||||
import { ADDON_ID, PANEL_ID } from './shared';
|
||||
import { ADDON_ID, PANEL_ID, PARAM_KEY } from './shared';
|
||||
|
||||
// TODO: fix eslint in tslint (igor said he fixed it, should ask him)
|
||||
import Panel from './Panel';
|
||||
@ -14,6 +14,7 @@ export default function register(type: types) {
|
||||
route: ({ storyId }) => `/info/${storyId}`, // todo add type
|
||||
match: ({ viewMode }) => viewMode === 'info', // todo add type
|
||||
render: ({ active }) => <Panel api={api} active={active} />,
|
||||
paramKey: PARAM_KEY,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -65,6 +65,29 @@ Then you'll be able to see those notes when you are viewing the story.
|
||||
|
||||

|
||||
|
||||
## Disable the addon
|
||||
|
||||
You can disable an addon panel for a story by adding a `disabled` parameter.
|
||||
|
||||
```js
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import Button from './Button';
|
||||
|
||||
storiesOf('Button', module).add(
|
||||
'with some emoji',
|
||||
() => (
|
||||
<Button onClick={action('clicked')}>
|
||||
<span role="img" aria-label="so cool">
|
||||
😀 😎 👍 💯
|
||||
</span>
|
||||
</Button>
|
||||
),
|
||||
{ notes: { disabled: true } }
|
||||
);
|
||||
```
|
||||
|
||||
## Global Configuration
|
||||
|
||||
Sometimes you might want to configure an addon globally, as in the case of collocating stories with components, or just simply to keep your stories file cleaner. To do that, you can add your decorators to a config file, typically in `.storybook/config.js`. Here's an example of how you might do that.
|
||||
|
@ -110,6 +110,7 @@ addons.register(ADDON_ID, api => {
|
||||
type: types.PANEL,
|
||||
title,
|
||||
render,
|
||||
paramKey: PARAM_KEY,
|
||||
});
|
||||
});
|
||||
```
|
||||
@ -259,6 +260,34 @@ storiesOf('Button', module)
|
||||
});
|
||||
```
|
||||
|
||||
### Disabling an addon panel
|
||||
|
||||
It's possible to disable an addon panel for a particular story.
|
||||
|
||||
To offer that capability, you need to pass the paramKey when you register the panel
|
||||
```js
|
||||
addons.register(ADDON_ID, () => {
|
||||
addons.add(PANEL_ID, {
|
||||
type: types.PANEL,
|
||||
title: 'My Addon',
|
||||
render: () => <div>Addon tab content</div>,
|
||||
paramKey: 'myAddon',
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
While adding a story, you can then pass a `disabled` parameter.
|
||||
|
||||
```js
|
||||
storiesOf('Button', module)
|
||||
.add('with text', () => <Button>Hello Button</Button>, {
|
||||
myAddon: {
|
||||
disabled: true,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Styling your addon
|
||||
|
||||
We use [emotion](https://emotion.sh) for styling, AND we provide a theme which can be set by the user!
|
||||
|
@ -24,7 +24,6 @@ export interface Addon {
|
||||
route?: (routeOptions: RouteOptions) => string;
|
||||
match?: (matchOptions: MatchOptions) => boolean;
|
||||
render: (renderOptions: RenderOptions) => ReactElement<any>;
|
||||
paramKey?: string;
|
||||
}
|
||||
|
||||
export type Loader = (api: API) => void;
|
||||
|
@ -29,6 +29,7 @@ export interface Addon {
|
||||
route?: (routeOptions: RouteOptions) => string;
|
||||
match?: (matchOptions: MatchOptions) => boolean;
|
||||
render: (renderOptions: RenderOptions) => ReactElement<any>;
|
||||
paramKey: string;
|
||||
}
|
||||
export interface Collection {
|
||||
[key: string]: Addon;
|
||||
@ -41,10 +42,15 @@ interface Panels {
|
||||
export interface SubAPI {
|
||||
getElements: (type: Types) => Collection;
|
||||
getPanels: () => Collection;
|
||||
getPanelsForStory: (storyParameters: StoryParameters) => Collection;
|
||||
getSelectedPanel: () => string;
|
||||
setSelectedPanel: (panelName: string) => void;
|
||||
}
|
||||
|
||||
interface StoryParameters {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export function ensurePanel(panels: Panels, selectedPanel?: string, currentPanel?: string) {
|
||||
const keys = Object.keys(panels);
|
||||
|
||||
@ -62,6 +68,23 @@ export default ({ provider, store }: Module) => {
|
||||
const api: SubAPI = {
|
||||
getElements: type => provider.getElements(type),
|
||||
getPanels: () => api.getElements(types.PANEL),
|
||||
getPanelsForStory: (storyParameters: StoryParameters) => {
|
||||
const panels = api.getPanels();
|
||||
if (!panels || !storyParameters) {
|
||||
return panels;
|
||||
}
|
||||
|
||||
const filteredPanels: Collection = {};
|
||||
Object.entries(panels).forEach(([id, panel]) => {
|
||||
const { paramKey } = panel;
|
||||
if (paramKey && storyParameters[paramKey] && storyParameters[paramKey].disabled) {
|
||||
return;
|
||||
}
|
||||
filteredPanels[id] = panel;
|
||||
});
|
||||
|
||||
return filteredPanels;
|
||||
},
|
||||
getSelectedPanel: () => {
|
||||
const { selectedPanel } = store.getState();
|
||||
return ensurePanel(api.getPanels(), selectedPanel, selectedPanel);
|
||||
|
147
lib/api/src/tests/addons.test.js
Normal file
147
lib/api/src/tests/addons.test.js
Normal file
@ -0,0 +1,147 @@
|
||||
import initAddons, { types } from '../modules/addons';
|
||||
|
||||
const PANELS = {
|
||||
a11y: {
|
||||
title: 'Accessibility',
|
||||
paramKey: 'a11y',
|
||||
},
|
||||
actions: {
|
||||
title: 'Actions',
|
||||
paramKey: 'actions',
|
||||
},
|
||||
knobs: {
|
||||
title: 'Knobs',
|
||||
paramKey: 'knobs',
|
||||
},
|
||||
};
|
||||
|
||||
const provider = {
|
||||
getElements(type) {
|
||||
if (type === types.PANEL) {
|
||||
return PANELS;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
const store = {
|
||||
getState: () => ({
|
||||
selectedPanel: '',
|
||||
}),
|
||||
setState: jest.fn(),
|
||||
};
|
||||
|
||||
describe('Addons API', () => {
|
||||
describe('#getElements', () => {
|
||||
it('should return provider elements', () => {
|
||||
// given
|
||||
const { api } = initAddons({ provider, store });
|
||||
|
||||
// when
|
||||
const panels = api.getElements(types.PANEL);
|
||||
|
||||
// then
|
||||
expect(panels).toBe(PANELS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getPanels', () => {
|
||||
it('should return provider panels', () => {
|
||||
// given
|
||||
const { api } = initAddons({ provider, store });
|
||||
|
||||
// when
|
||||
const panels = api.getPanels();
|
||||
|
||||
// then
|
||||
expect(panels).toBe(PANELS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getPanelsForStory', () => {
|
||||
it('should return all panels by default', () => {
|
||||
// given
|
||||
const { api } = initAddons({ provider, store });
|
||||
|
||||
// when
|
||||
const filteredPanels = api.getPanelsForStory();
|
||||
|
||||
// then
|
||||
expect(filteredPanels).toBe(PANELS);
|
||||
});
|
||||
|
||||
it('should filter disabled addons', () => {
|
||||
// given
|
||||
const { api } = initAddons({ provider, store });
|
||||
const storyParameters = {
|
||||
a11y: { disabled: true },
|
||||
};
|
||||
|
||||
// when
|
||||
const filteredPanels = api.getPanelsForStory(storyParameters);
|
||||
|
||||
// then
|
||||
expect(filteredPanels).toEqual({
|
||||
actions: PANELS.actions,
|
||||
knobs: PANELS.knobs,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getSelectedPanel', () => {
|
||||
it('should return provider panels', () => {
|
||||
// given
|
||||
const storeWithSelectedPanel = {
|
||||
getState: () => ({
|
||||
selectedPanel: 'actions',
|
||||
}),
|
||||
setState: jest.fn(),
|
||||
};
|
||||
const { api } = initAddons({ provider, store: storeWithSelectedPanel });
|
||||
|
||||
// when
|
||||
const selectedPanel = api.getSelectedPanel();
|
||||
|
||||
// then
|
||||
expect(selectedPanel).toBe('actions');
|
||||
});
|
||||
|
||||
it('should return first panel when selected is not a panel', () => {
|
||||
// given
|
||||
const storeWithSelectedPanel = {
|
||||
getState: () => ({
|
||||
selectedPanel: 'unknown',
|
||||
}),
|
||||
setState: jest.fn(),
|
||||
};
|
||||
const { api } = initAddons({ provider, store: storeWithSelectedPanel });
|
||||
|
||||
// when
|
||||
const selectedPanel = api.getSelectedPanel();
|
||||
|
||||
// then
|
||||
expect(selectedPanel).toBe('a11y');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setSelectedPanel', () => {
|
||||
it('should set value inn store', () => {
|
||||
// given
|
||||
const setState = jest.fn();
|
||||
const storeWithSelectedPanel = {
|
||||
getState: () => ({
|
||||
selectedPanel: 'actions',
|
||||
}),
|
||||
setState,
|
||||
};
|
||||
const { api } = initAddons({ provider, store: storeWithSelectedPanel });
|
||||
expect(setState).not.toHaveBeenCalled();
|
||||
|
||||
// when
|
||||
api.setSelectedPanel('knobs');
|
||||
|
||||
// then
|
||||
expect(setState).toHaveBeenCalledWith({ selectedPanel: 'knobs' }, { persistence: 'session' });
|
||||
});
|
||||
});
|
||||
});
|
@ -10,25 +10,8 @@ const createPanelActions = memoize(1)(api => ({
|
||||
togglePosition: () => api.togglePanelPosition(),
|
||||
}));
|
||||
|
||||
const filterPanel = (panels, storyParameters) => {
|
||||
if (!panels || !storyParameters) {
|
||||
return panels;
|
||||
}
|
||||
|
||||
const filteredPanels = {};
|
||||
Object.entries(panels).forEach(([id, panel]) => {
|
||||
const { paramKey } = panel;
|
||||
const panelParameters = paramKey && storyParameters[paramKey];
|
||||
if (!panelParameters || !panelParameters.disabled) {
|
||||
filteredPanels[id] = panel;
|
||||
}
|
||||
});
|
||||
|
||||
return filteredPanels;
|
||||
};
|
||||
|
||||
const mapper = ({ state, api }) => ({
|
||||
panels: filterPanel(api.getPanels(), api.getParameters(state.storyId)),
|
||||
panels: api.getPanelsForStory(api.getParameters(state.storyId)),
|
||||
selectedPanel: api.getSelectedPanel(),
|
||||
panelPosition: state.layout.panelPosition,
|
||||
actions: createPanelActions(api),
|
||||
|
22
lib/ui/src/containers/panel.stories.js
Normal file
22
lib/ui/src/containers/panel.stories.js
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
|
||||
storiesOf('UI|Addon Panel', module)
|
||||
.add('default', () => <div>By default all addon panels are rendered</div>)
|
||||
.add(
|
||||
'disable panel',
|
||||
() => (
|
||||
<div>
|
||||
This story disables Actions and Accessibility panels
|
||||
<pre>
|
||||
{`storiesOf('UI|Addon Panel', module)
|
||||
.add(
|
||||
'my story',
|
||||
<MyComponent />,
|
||||
{ a11y: { disable: true }, actions: { disable: true } }
|
||||
);`}
|
||||
</pre>
|
||||
</div>
|
||||
),
|
||||
{ a11y: { disabled: true }, actions: { disabled: true } }
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user