mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-06 15:31:16 +08:00
UI: Add viewMode
parameter to control story nav UI (#9090)
UI: Add `viewMode` parameter to control story nav UI
This commit is contained in:
commit
21a45925d7
@ -209,6 +209,31 @@ User writes documentation & stories side-by-side in a single MDX file, and wants
|
||||
</Story>
|
||||
```
|
||||
|
||||
## Controlling a story's view mode
|
||||
|
||||
Storybook's default story navigation behavior is to preserve the existing view mode. In other words, if a user is viewing a story in "docs" mode, and clicks on another story, they will navigate to the other story in "docs" mode. If they are viewing a story in "story" mode (i.e. "canvas" in the UI) they will navigate to another story in "story" mode (with the exception of "docs-only" pages, which are always shown in "docs" mode).
|
||||
|
||||
Based on user feedback, it's also possible to control the view mode for an individual story using the `viewMode` story parameter. In the following example, the nav link will always set the view mode to story:
|
||||
|
||||
```js
|
||||
export const Foo = () => <Component />;
|
||||
Foo.story = {
|
||||
parameters: {
|
||||
// reset the view mode to "story" whenever the user navigates to this story
|
||||
viewMode: 'story',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
This can also be applied globally in `preview.js`:
|
||||
|
||||
```js
|
||||
// always reset the view mode to "docs" whenever the user navigates
|
||||
addParameters({
|
||||
viewMode: 'docs',
|
||||
});
|
||||
```
|
||||
|
||||
## More resources
|
||||
|
||||
Want to learn more? Here are some more articles on Storybook Docs:
|
||||
|
@ -3,6 +3,6 @@ module.exports = {
|
||||
addons: [
|
||||
'@storybook/preset-create-react-app',
|
||||
'@storybook/addon-actions',
|
||||
'@storybook/addon-links'
|
||||
'@storybook/addon-links',
|
||||
],
|
||||
};
|
||||
|
@ -5,6 +5,7 @@ import { ButtonGroup } from '../../components/ButtonGroup';
|
||||
export default {
|
||||
title: 'Addons/Docs/ButtonGroup',
|
||||
component: ButtonGroup,
|
||||
parameters: { viewMode: 'docs' },
|
||||
subcomponents: { DocgenButton },
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,9 @@ import Button, { Type } from '../../components/TsButton';
|
||||
export default {
|
||||
title: 'Addons/Docs/TsButton',
|
||||
component: Button,
|
||||
parameters: {
|
||||
viewMode: 'story',
|
||||
},
|
||||
};
|
||||
|
||||
type Story = () => any;
|
||||
|
@ -39,6 +39,8 @@ interface Group {
|
||||
isComponent: boolean;
|
||||
isRoot: boolean;
|
||||
isLeaf: boolean;
|
||||
// MDX stories are "Group" type
|
||||
parameters?: any;
|
||||
}
|
||||
|
||||
interface StoryInput {
|
||||
@ -286,6 +288,7 @@ Did you create a path that uses the separator char accidentally, such as 'Vue <d
|
||||
isComponent: false,
|
||||
isLeaf: false,
|
||||
isRoot: !!root && index === 0,
|
||||
parameters,
|
||||
};
|
||||
return soFar.concat([result]);
|
||||
}, [] as GroupsList);
|
||||
|
19
lib/ui/src/components/sidebar/SidebarStories.test.ts
Normal file
19
lib/ui/src/components/sidebar/SidebarStories.test.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { viewMode } from './SidebarStories';
|
||||
|
||||
describe('viewMode', () => {
|
||||
it('always links to parameters.viewMode if one is provided', () => {
|
||||
expect(viewMode('foo', true, { viewMode: 'bar' })).toEqual('bar');
|
||||
});
|
||||
it('links to "docs" view mode for docs-only stories', () => {
|
||||
expect(viewMode('foo', true, undefined)).toEqual('docs');
|
||||
});
|
||||
it('links to "story" viewMode there is no viewMode specified or not on a docs page', () => {
|
||||
expect(viewMode(undefined, false, undefined)).toEqual('story');
|
||||
expect(viewMode('settings', false, undefined)).toEqual('story');
|
||||
});
|
||||
it('links to the current viewMode by default', () => {
|
||||
expect(viewMode('foo', false, undefined)).toEqual('foo');
|
||||
expect(viewMode('story', false, undefined)).toEqual('story');
|
||||
expect(viewMode('docs', false, undefined)).toEqual('docs');
|
||||
});
|
||||
});
|
@ -52,11 +52,22 @@ const PlainLink = styled.a(plain);
|
||||
|
||||
const Wrapper = styled.div({});
|
||||
|
||||
const refinedViewMode = (viewMode: string | undefined, isDocsOnly: boolean) => {
|
||||
if (isDocsOnly) {
|
||||
return 'docs';
|
||||
export const viewMode = (
|
||||
currentViewMode: string | undefined,
|
||||
isDocsOnly: boolean,
|
||||
parameters: { viewMode?: string } = {}
|
||||
) => {
|
||||
const { viewMode: paramViewMode } = parameters;
|
||||
switch (true) {
|
||||
case typeof paramViewMode === 'string':
|
||||
return paramViewMode;
|
||||
case isDocsOnly:
|
||||
return 'docs';
|
||||
case currentViewMode === 'settings' || !currentViewMode:
|
||||
return 'story';
|
||||
default:
|
||||
return currentViewMode;
|
||||
}
|
||||
return viewMode === 'settings' || !viewMode ? 'story' : viewMode;
|
||||
};
|
||||
|
||||
const targetId = (childIds?: string[]) =>
|
||||
@ -73,14 +84,17 @@ export const Link = ({
|
||||
onKeyUp,
|
||||
childIds,
|
||||
isExpanded,
|
||||
parameters,
|
||||
}) => {
|
||||
return isLeaf || (isComponent && !isExpanded) ? (
|
||||
<Location>
|
||||
{({ viewMode }) => (
|
||||
{({ viewMode: currentViewMode }) => (
|
||||
<PlainRouterLink
|
||||
title={name}
|
||||
id={prefix + id}
|
||||
to={`/${refinedViewMode(viewMode, isLeaf && isComponent)}/${targetId(childIds) || id}`}
|
||||
to={`/${viewMode(currentViewMode, isLeaf && isComponent, parameters)}/${targetId(
|
||||
childIds
|
||||
) || id}`}
|
||||
onKeyUp={onKeyUp}
|
||||
onClick={onClick}
|
||||
>
|
||||
@ -161,7 +175,6 @@ const SidebarStories: FunctionComponent<StoriesProps> = memo(
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Wrapper className={className}>
|
||||
<TreeState
|
||||
|
Loading…
x
Reference in New Issue
Block a user