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

DocsPage: Remove slots for 6.0 #10259

Merged
merged 6 commits into from
Mar 31, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
28 changes: 25 additions & 3 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<h1>Migration</h1>

- [From version 5.3.x to 6.0.x](#from-version-53x-to-60x)
- [DocsPage slots removed](#docspage-slots-removed)
- [React prop tables with Typescript](#react-prop-tables-with-typescript)
- [React.FC interfaces](#reactfc-interfaces)
- [Imported types](#imported-types)
Expand All @@ -11,12 +12,12 @@
- [Removed Legacy Story APIs](#removed-legacy-story-apis)
- [Can no longer add decorators/parameters after stories](#can-no-longer-add-decoratorsparameters-after-stories)
- [Changed Parameter Handling](#changed-parameter-handling)
- [Actions Addon API changes](#actions-addon-api-changes)
- [Actions Addon uses parameters](#actions-addon-uses-parameters)
- [Removed action decorator APIs](#removed-action-decorator-apis)
- [Simplified Render Context](#simplified-render-context)
- [Story Store immutable outside of configuration](#story-store-immutable-outside-of-configuration)
- [Improved story source handling](#improved-story-source-handling)
- [Actions Addon API changes](#actions-addon-api-changes)
- [Actions Addon uses parameters](#actions-addon-uses-parameters)
- [Removed action decorator APIs](#removed-action-decorator-apis)
- [From version 5.2.x to 5.3.x](#from-version-52x-to-53x)
- [To main.js configuration](#to-mainjs-configuration)
- [Using main.js](#using-mainjs)
Expand Down Expand Up @@ -98,6 +99,25 @@

## From version 5.3.x to 6.0.x

### DocsPage slots removed

In SB5.2, we introduced the concept of [DocsPage slots](https://github.com/storybookjs/storybook/blob/0de8575eab73bfd5c5c7ba5fe33e53a49b92db3a/addons/docs/docs/docspage.md#docspage-slots) for customizing the DocsPage.

In 5.3, we introduced `docs.x` story parameters like `docs.prepareForInline` which get filled in by frameworks and can also be overwritten by users, which is a more natural/convenient way to make global customizations.

We also introduced introduced [Custom DocsPage](https://github.com/storybookjs/storybook/blob/next/addons/docs/docs/docspage.md#replacing-docspage), which makes it possible to add/remove/update DocBlocks on the page.

These mechanisms are superior to slots, so we've removed slots in 6.0. For each slot, we provide a migration path here:

| Slot | Slot function | Replacement |
| ----------- | ----------------- | -------------------------------------------- |
| Title | `titleSlot` | Custom DocsPage |
| Subtitle | `subtitleSlot` | Custom DocsPage |
| Description | `descriptionSlot` | `docs.extractComponentDescription` parameter |
| Primary | `primarySlot` | Custom DocsPage |
| Props | `propsSlot` | `docs.extractProps` parameter |
| Stories | `storiesSlot` | Custom DocsPage |

### React prop tables with Typescript

Starting in 6.0 we are changing our recommended setup for extracting prop tables in `addon-docs` for React projects using TypeScript.
Expand All @@ -107,6 +127,7 @@ In earlier versions, we recommended `react-docgen-typescript-loader` (`RDTL`) an
As a consequence we've removed `RDTL` from the presets, which is a breaking change. We made this change because `react-docgen` now supports TypeScript natively, and fewer dependencies simplifies things for everybody.

The Babel-based `react-docgen` version is the default in:

- `@storybook/preset-create-react-app` @ `^2.1.0`
- `@storybook/preset-typescript` @ `^3.0.0`

Expand Down Expand Up @@ -322,6 +343,7 @@ The MDX analog:
<Button />
</Story>
```

### Actions Addon API changes

#### Actions Addon uses parameters
Expand Down
149 changes: 0 additions & 149 deletions addons/docs/docs/docspage.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@ When you install [Storybook Docs](../README.md), `DocsPage` is the zero-config d
- [Motivation](#motivation)
- [Component parameter](#component-parameter)
- [Subcomponents parameter](#subcomponents-parameter)
- [DocsPage slots](#docspage-slots)
- [Slot values](#slot-values)
- [Title](#title)
- [Subtitle](#subtitle)
- [Description](#description)
- [Primary](#primary)
- [Props](#props)
- [Stories](#stories)
- [Slot functions](#slot-functions)
- [Replacing DocsPage](#replacing-docspage)
- [Remixing DocsPage using doc blocks](#remixing-docspage-using-doc-blocks)
- [Story file names](#story-file-names)
Expand Down Expand Up @@ -85,146 +76,6 @@ Subcomponent prop tables will show up in a tabbed interface along with the prima

If you want organize your documentation differently for groups of components, we recommend trying [MDX](./mdx.md) which is completely flexible to support any configuration.

## DocsPage slots

`DocsPage` is organized into a series of "slots" including Title, Subtitle, Description, Props, and Story. Each of these slots pulls information from your project and formats it for the screen.

<center>
<img style="padding: 30px; border: 3px solid #eee;" src="https://raw.githubusercontent.com/storybookjs/storybook/master/addons/docs/docs/media/docspage-slots.png" width="100%" />
</center>

## Slot values

Each of the slots is computed by a built-in function, that can also be overridden using [Slot Function](#slot-functions).

Here is a summary of the slots, where the data comes from by default, and the slot function that can be used to override it:

| Slot | Default source | Slot function | Frameworks |
| ----------- | ----------------------------------- | ----------------- | ---------- |
| Title | component `title` | `titleSlot` | All |
| Subtitle | `componentSubtitle` parameter | `subtitleSlot` | All |
| Description | component `docgen` comment | `descriptionSlot` | React, Vue |
| Primary | storybook stories | `primarySlot` | All |
| Props | component docgen props or propTypes | `propsSlot` | React, Vue |
| Stories | storybook stories | `storiesSlot` | All |

The `storiesSlot` uses the `docs.storyDescription` parameter to show a description for each story, if available.

For more information on frameworks, see ["Framework support"](../README.md#framework-support)

### Title

`Title` is computed from the component's `title`, and matches the component caption in Storybook's navigation.

For example:

```js
export default {
title: 'Path/to/Badge',
};
```

### Subtitle

The `Subtitle` slot is computed from the component's `componentSubtitle` parameter.

For example in [Component Story Format (CSF)](https://medium.com/storybookjs/component-story-format-66f4c32366df):

```js
export default {
...
parameters: {
componentSubtitle: 'Handy status label',
},
};
```

### Description

The `Description` slot is computed from the Component's docgen comments in the component's source.

For example, here's the source for `Badge`:

```js
/**
* Use `Badge` to highlight key info with a predefined status.
*/
export const Badge = ({ status, children }) => { ... }
```

### Primary

The `Primary` slot is computed from the first user-defined story for the component.

For example here are `Badge`'s stories in CSF. The `allBadges` is selected as the primary story because it's first:

```js
// export default { ... }; /* Badge component metadata */
export const allBadges = () => ...
export const positive = () => ...
export const negative = () => ...
```

### Props

The `Props` slot is computed from the component's docgen props, which can be defined in typescript or using `react` PropTypes.

For example, here are the `PropTypes` for the `Badge` component

```js
import PropTypes from 'prop-types';

// ... Badge definition ...

Badge.propTypes = {
status: PropTypes.oneOf(['positive', 'negative', 'neutral', 'error', 'warning']),
};
Badge.defaultProps = {
status: 'neutral',
};
```

### Stories

The `Stories` slot is computed from the user-defined stories for the component, excluding the first.

For example here are `Badge`'s stories in CSF. The `positive` and `negative` stories are selected in that order:

```js
// export default { ... }; /* Badge component metadata */
export const allBadges = () => ...
export const positive = () => ...
export const negative = () => ...
```

## Slot functions

> ⚠️ Slot functions are an experimental feature in Storybook 5.2. The API may change in 5.3 outside of the normal semver rules. Be forewarned!

The value for each slot is computed from a `SlotContext` context, and the function that's used to compute the value can be overridden if you need to customize the page. If you find yourself doing a lot of configuration, or wanting different configurations for different pages, you might be better off using `MDX`. Everything that `DocsPage` gives you can be reconstructed in a few lines of `MDX`.

Here is the `SlotContext` type definition:

```ts
export interface SlotContext {
id?: string;
kind?: string;
name?: string;
parameters?: any;
storyStore?: any;
}
```

And here are the return type signatures for each of the slot functions

| Slot | Function | Inputs | Output |
| -------- | ------------ | ---------------------------- | ------------------ |
| Title | titleSlot | `SlotContext` | `string?` |
| Subtitle | subtitleSlot | `SlotContext` | `string?` |
| Primary | primarySlot | `StoryData[]`, `SlotContext` | `StoryProps?` |
| Props | propsSlot | `SlotContext` | `PropsTableProps?` |
| Stories | storiesSlot | `StoryData[]`, `SlotContext` | `StoryProps[]?` |

## Replacing DocsPage

What if you don't want a `DocsPage` for your storybook, for a specific component, or even for a specific story?
Expand Down
11 changes: 3 additions & 8 deletions addons/docs/src/blocks/Description.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { FunctionComponent, useContext } from 'react';
import { Description, DescriptionProps as PureDescriptionProps } from '@storybook/components';
import { DocsContext, DocsContextProps } from './DocsContext';
import { Component, CURRENT_SELECTION, DescriptionSlot } from './shared';
import { Component, CURRENT_SELECTION } from './shared';
import { str } from '../lib/docgen';

export enum DescriptionType {
Expand All @@ -16,7 +16,6 @@ type Notes = string | any;
type Info = string | any;

interface DescriptionProps {
slot?: DescriptionSlot;
of?: '.' | Component;
type?: DescriptionType;
markdown?: string;
Expand Down Expand Up @@ -61,13 +60,9 @@ ${extractComponentDescription(target) || ''}
}
};

const DescriptionContainer: FunctionComponent<DescriptionProps> = (props) => {
const DescriptionContainer: FunctionComponent<DescriptionProps> = props => {
const context = useContext(DocsContext);
const { slot } = props;
let { markdown } = getDescriptionProps(props, context);
if (slot) {
markdown = slot(markdown, context);
}
const { markdown } = getDescriptionProps(props, context);
return markdown ? <Description markdown={markdown} /> : null;
};

Expand Down
21 changes: 7 additions & 14 deletions addons/docs/src/blocks/DocsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,13 @@ import { Primary } from './Primary';
import { Props } from './Props';
import { Stories } from './Stories';

export const DocsPage: FunctionComponent<DocsPageProps> = ({
titleSlot,
subtitleSlot,
descriptionSlot,
primarySlot,
propsSlot,
storiesSlot,
}) => (
export const DocsPage: FunctionComponent<DocsPageProps> = () => (
<>
<Title slot={titleSlot} />
<Subtitle slot={subtitleSlot} />
<Description slot={descriptionSlot} />
<Primary slot={primarySlot} />
<Props slot={propsSlot} />
<Stories slot={storiesSlot} />
<Title />
<Subtitle />
<Description />
<Primary />
<Props />
<Stories />
</>
);
12 changes: 7 additions & 5 deletions addons/docs/src/blocks/Primary.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import React, { useContext, FunctionComponent } from 'react';
import React, { useContext, FC } from 'react';
import { DocsContext } from './DocsContext';
import { DocsStory } from './DocsStory';
import { getDocsStories } from './utils';
import { StorySlot } from './shared';

interface PrimaryProps {
slot?: StorySlot;
name?: string;
}

export const Primary: FunctionComponent<PrimaryProps> = ({ slot }) => {
export const Primary: FC<PrimaryProps> = ({ name }) => {
const context = useContext(DocsContext);
const componentStories = getDocsStories(context);
const story = slot ? slot(componentStories, context) : componentStories && componentStories[0];
let story;
if (componentStories) {
story = name ? componentStories.find(s => s.name === name) : componentStories[0];
}
return story ? <DocsStory {...story} expanded={false} withToolbar /> : null;
};
13 changes: 6 additions & 7 deletions addons/docs/src/blocks/Props.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
TabsState,
} from '@storybook/components';
import { DocsContext, DocsContextProps } from './DocsContext';
import { Component, PropsSlot, CURRENT_SELECTION } from './shared';
import { Component, CURRENT_SELECTION } from './shared';
import { getComponentName } from './utils';

import { PropsExtractor } from '../lib/docgen/types';
Expand All @@ -23,7 +23,6 @@ interface PropsProps {
components?: {
[label: string]: Component;
};
slot?: PropsSlot;
}

// FIXME: remove in SB6.0 & require config
Expand Down Expand Up @@ -65,7 +64,7 @@ export const getComponentProps = (
if (rows) {
props = { rows: filterRows(rows, exclude) };
} else if (sections) {
Object.keys(sections).forEach((section) => {
Object.keys(sections).forEach(section => {
sections[section] = filterRows(sections[section], exclude);
});
}
Expand All @@ -92,9 +91,9 @@ export const getComponent = (props: PropsProps = {}, context: DocsContextProps):
return target;
};

const PropsContainer: FunctionComponent<PropsProps> = (props) => {
const PropsContainer: FunctionComponent<PropsProps> = props => {
const context = useContext(DocsContext);
const { slot, components } = props;
const { components } = props;
const {
parameters: { subcomponents },
} = context;
Expand All @@ -103,7 +102,7 @@ const PropsContainer: FunctionComponent<PropsProps> = (props) => {
if (!allComponents) {
const main = getComponent(props, context);
const mainLabel = getComponentName(main);
const mainProps = slot ? slot(context, main) : getComponentProps(main, props, context);
const mainProps = getComponentProps(main, props, context);

if (!subcomponents || typeof subcomponents !== 'object') {
return mainProps && <PropsTable {...mainProps} />;
Expand All @@ -116,7 +115,7 @@ const PropsContainer: FunctionComponent<PropsProps> = (props) => {
Object.entries(allComponents).forEach(([label, component]) => {
tabs.push({
label,
table: slot ? slot(context, component) : getComponentProps(component, props, context),
table: getComponentProps(component, props, context),
});
});

Expand Down
Loading