Merge branch 'next' into core/options-in-config

This commit is contained in:
Norbert de Langen 2019-11-21 12:44:19 +01:00 committed by GitHub
commit 8be348cc2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
366 changed files with 3216 additions and 2064 deletions

View File

@ -1,3 +1,38 @@
## 5.3.0-beta.3 (November 21, 2019)
### Features
* Addon-docs: Rich props table UI ([#8887](https://github.com/storybookjs/storybook/pull/8887))
* Addon-docs: Improve basic support for Flow props ([#8890](https://github.com/storybookjs/storybook/pull/8890))
* CLI: Avoid id changes after `storiesof-to-csf` migration ([#8856](https://github.com/storybookjs/storybook/pull/8856))
### Bug Fixes
* Addon-docs: Fix props table for sections props ([#8904](https://github.com/storybookjs/storybook/pull/8904))
* Addon-docs: Fix Description block when no component provided ([#8902](https://github.com/storybookjs/storybook/pull/8902))
* Angular: Fix project without `architect.build` option ([#6737](https://github.com/storybookjs/storybook/pull/6737))
### Maintenance
* Addon-docs: Docgen lib maintenance ([#8896](https://github.com/storybookjs/storybook/pull/8896))
* Examples: Fix stories glob in official-storybook ([#8888](https://github.com/storybookjs/storybook/pull/8888))
## 5.3.0-beta.2 (November 19, 2019)
### Features
* Addon-docs: Customizable DocPage doc blocks ([#8855](https://github.com/storybookjs/storybook/pull/8855))
### Bug Fixes
* Addon-docs: Add back Props "exclude" support ([#8868](https://github.com/storybookjs/storybook/pull/8868))
* Addon-docs: Fix MDX component permalinking ([#8872](https://github.com/storybookjs/storybook/pull/8872))
* Addon-docs: Fix regression to @ignore in Props ([#8867](https://github.com/storybookjs/storybook/pull/8867))
### Maintenance
* Addon-docs: Add tests for prop types default value ([#8869](https://github.com/storybookjs/storybook/pull/8869))
## 5.3.0-beta.1 (November 18, 2019)
### Features

View File

@ -7,7 +7,7 @@
- [React Native Async Storage](#react-native-async-storage)
- [Deprecate displayName parameter](#deprecate-displayname-parameter)
- [Unified docs preset](#unified-docs-preset)
- [Simplified hierarchy separators](#simplified-heirarchy-separators)
- [Simplified hierarchy separators](#simplified-hierarchy-separators)
- [From version 5.1.x to 5.2.x](#from-version-51x-to-52x)
- [Source-loader](#source-loader)
- [Default viewports](#default-viewports)
@ -125,15 +125,19 @@ Addon-docs configuration gets simpler in 5.3. In 5.2, each framework had its own
We've deprecated the ability to specify the hierarchy separators (how you control the grouping of story kinds in the sidebar). From Storybook 6.0 we will have a single separator `/`, which cannot be configured.
If you are currently using using custom separators, we encourage you to migrate to using `/` as the sole separator. If you are using `|` or `.` as a separator currently, (we will soon provide) a codemod that can be used to rename all your components.
If you are currently using using custom separators, we encourage you to migrate to using `/` as the sole separator. If you are using `|` or `.` as a separator currently, we provide a codemod, [`upgrade-hierarchy-separators`](https://github.com/storybookjs/storybook/blob/next/lib/codemod/README.md#upgrade-hierarchy-separators), that can be used to rename all your components.
If you were using `|` and wish to keep the "root" behaviour, use the `showRoots: true` option to re-enable roots:
```
yarn sb migrate upgrade-hierarchy-separators --glob="*.stories.js"
```
If you were using `|` and wish to keep the "root" behavior, use the `showRoots: true` option to re-enable roots:
```js
addParameters({ options: { showRoots: true } });
```
NOTE: it is no longer possible to have some stories with roots and others without. If you want to keep the old behaviour, simply add a root called "Others" to all your previously unrooted stories.
NOTE: it is no longer possible to have some stories with roots and others without. If you want to keep the old behavior, simply add a root called "Others" to all your previously unrooted stories.
## From version 5.1.x to 5.2.x

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-a11y",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "a11y addon for storybook",
"keywords": [
"a11y",
@ -33,12 +33,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"axe-core": "^3.3.2",
"core-js": "^3.0.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-actions",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Action Logger addon for storybook",
"keywords": [
"storybook"
@ -28,12 +28,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-api": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-api": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"fast-deep-equal": "^2.0.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-backgrounds",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "A storybook addon to show different backgrounds for your preview",
"keywords": [
"addon",
@ -32,12 +32,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"memoizerific": "^1.11.3",
"react": "^16.8.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-centered",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook decorator to center components",
"keywords": [
"addon",
@ -29,7 +29,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"util-deprecate": "^1.0.2"

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-contexts",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook Addon Contexts",
"keywords": [
"preact",
@ -27,10 +27,10 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"qs": "^6.6.0"

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-cssresources",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "A storybook addon to switch between css resources at runtime for your story",
"keywords": [
"addon",
@ -32,10 +32,10 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"react": "^16.8.3"

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-design-assets",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Design asset preview for storybook",
"keywords": [
"addon",
@ -34,12 +34,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"react": "^16.8.3",

View File

@ -8,6 +8,7 @@ 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)
- [Replacing DocsPage](#replacing-docspage)
- [Story file names](#story-file-names)
@ -54,6 +55,26 @@ storiesOf('Path/to/Badge', module).addParameters({ component: Badge });
If you're coming from the `storiesOf` format, there's [a codemod that adds it for you](https://github.com/storybookjs/storybook/blob/next/lib/codemod/README.md#add-component-parameters).
## Subcomponents parameter
Sometimes it's useful to document multiple components on the same page. For example, suppose your component library contains `List` and `ListItem` components that don't make sense without one another. `DocsPage` has the concept of a "primary" component with the [`component` parameter](#component-parameter), and can also accept one or more "subcomponents":
```js
import { List, ListHeading, ListItem } from './List';
export default {
title: 'Path/to/List',
component: List,
subcomponents: { ListHeading, ListItem },
};
```
Subcomponent prop tables will show up in a tabbed interface along with the primary component, and the tab titles will correspond to the keys of the `subcomponents` object.
<img src="./media/docspage-subcomponents.png" width="100%" />
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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,45 +1,36 @@
# Storybook Docs framework dev guide
Storybook Docs [provides basic support for all non-RN Storybook view layers](../README.md#framework-support) out of the box. However, some frameworks have been docs-optimized, adding features like automatic props table generation and inline story rendering. This document is a dev guide for how to set up a new framework in docs.
Storybook Docs [provides basic support for all non-RN Storybook view layers](../README.md#framework-support) out of the box. However, some frameworks have been docs-optimized, adding features like automatic props table generation and inline story rendering. This document is a dev guide for how to optimize a new framework in docs.
- [Adding a preset](#adding-a-preset)
- [Framework-specific configuration](#framework-specific-configuration)
- [Props tables](#props-tables)
- [Component descriptions](#component-descriptions)
- [Inline story rendering](#inline-story-rendering)
## Adding a preset
## Framework-specific configuration
To get basic support, you need to add a [preset](https://storybook.js.org/docs/presets/introduction). By default this doesn't need to do much.
Your framework might need framework-specific configuration. This could include adding extra webpack loaders or global decorators/story parameters.
Here's a basic preset for `@storybook/html` in `addons/docs/html/preset.js`:
Addon-docs handles this kind of customization by file naming convention. Its [common preset](https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/common/preset.ts) does this by looking for files `../<framework>/{preset,config}.[tj]sx?`, where `<framework>` is the framework identifier, e.g. `vue`, `angular`, `react`, etc.
```js
module.exports = require('../dist/frameworks/common/makePreset').default('html');
For example, consider Storybook Docs for Vue, which needs `vue-docgen-loader` in its webpack config, and also has custom extraction functions for [props tables](#props-tables) and [component descriptions](#component-descriptions).
For webpack configuration, Docs for Vue defines [preset.ts](https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/vue/preset.ts), which follows the [preset](https://storybook.js.org/docs/presets/introduction) file structure:
```
export function webpack(webpackConfig: any = {}, options: any = {}) {
webpackConfig.module.rules.push({
test: /\.vue$/,
loader: 'vue-docgen-loader',
enforce: 'post',
});
return webpackConfig;
}
```
This automatically adds [DocsPage](./docspage.md) for each story, as well as webpack/babel settings for MDX support.
This appends `vue-docgen-loader` to the existing configuration, which at this point will also include modifications made by the common preset.
There is also a little hoop-jumping that will hopefully be unnecessary soon.
`addons/docs/src/frameworks/html/config.js`
```js
import { addParameters } from '@storybook/html';
import { DocsPage, DocsContainer } from '@storybook/addon-docs/blocks';
addParameters({
docs: {
container: DocsContainer,
page: DocsPage,
},
});
```
`addons/docs/html/config.js`
```js
module.exports = require('../dist/frameworks/html/config');
```
For props tables and descriptions, both of which are described in more detail below, it defines a file [config.tsx](https://github.com/storybookjs/storybook/blob/next/addons/docs/src/frameworks/vue/config.tsx).
## Props tables

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-docs",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Superior documentation for your components",
"keywords": [
"addon",
@ -45,13 +45,13 @@
"@mdx-js/loader": "^1.1.0",
"@mdx-js/mdx": "^1.1.0",
"@mdx-js/react": "^1.0.27",
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/postinstall": "5.3.0-beta.1",
"@storybook/router": "5.3.0-beta.1",
"@storybook/source-loader": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/postinstall": "5.3.0-beta.3",
"@storybook/router": "5.3.0-beta.3",
"@storybook/source-loader": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"acorn": "^7.1.0",
"acorn-jsx": "^5.1.0",
"acorn-walk": "^7.0.0",

View File

@ -1,8 +1,8 @@
import React, { FunctionComponent } from 'react';
import React, { FunctionComponent, useContext } from 'react';
import { Description, DescriptionProps as PureDescriptionProps } from '@storybook/components';
import { DocsContext, DocsContextProps } from './DocsContext';
import { Component, CURRENT_SELECTION } from './shared';
import { str } from '../lib/docgen/utils';
import { Component, CURRENT_SELECTION, DescriptionSlot } from './shared';
import { str } from '../lib/docgen';
export enum DescriptionType {
INFO = 'info',
@ -16,9 +16,11 @@ type Notes = string | any;
type Info = string | any;
interface DescriptionProps {
slot?: DescriptionSlot;
of?: '.' | Component;
type?: DescriptionType;
markdown?: string;
children?: string;
}
const getNotes = (notes?: Notes) =>
@ -29,11 +31,11 @@ const getInfo = (info?: Info) => info && (typeof info === 'string' ? info : str(
const noDescription = (component?: Component): string | null => null;
export const getDescriptionProps = (
{ of, type, markdown }: DescriptionProps,
{ of, type, markdown, children }: DescriptionProps,
{ parameters }: DocsContextProps
): PureDescriptionProps => {
if (markdown) {
return { markdown };
if (children || markdown) {
return { markdown: children || markdown };
}
const { component, notes, info, docs } = parameters;
const { extractComponentDescription = noDescription } = docs || {};
@ -59,13 +61,19 @@ ${extractComponentDescription(target) || ''}
}
};
const DescriptionContainer: FunctionComponent<DescriptionProps> = props => (
<DocsContext.Consumer>
{context => {
const { markdown } = getDescriptionProps(props, context);
return markdown && <Description markdown={markdown} />;
}}
</DocsContext.Consumer>
);
const DescriptionContainer: FunctionComponent<DescriptionProps> = props => {
const context = useContext(DocsContext);
const { slot } = props;
let { markdown } = getDescriptionProps(props, context);
if (slot) {
markdown = slot(markdown, context);
}
return markdown ? <Description markdown={markdown} /> : null;
};
// since we are in the docs blocks, assume default description if for primary component story
DescriptionContainer.defaultProps = {
of: '.',
};
export { DescriptionContainer as Description };

View File

@ -1,4 +1,4 @@
import { defaultTitleSlot } from './DocsPage';
import { defaultTitleSlot } from './Title';
describe('defaultTitleSlot', () => {
it('showRoots', () => {

View File

@ -1,153 +1,26 @@
import React, { FunctionComponent } from 'react';
import { parseKind } from '@storybook/router';
import { DocsPage as PureDocsPage, PropsTable, PropsTableProps } from '@storybook/components';
import { H2, H3 } from '@storybook/components/html';
import { DocsContext } from './DocsContext';
import { DocsPageProps } from './shared';
import { Title } from './Title';
import { Subtitle } from './Subtitle';
import { Description } from './Description';
import { Story } from './Story';
import { Preview } from './Preview';
import { Anchor } from './Anchor';
import { getPropsTableProps } from './Props';
export interface SlotContext {
id?: string;
selectedKind?: string;
selectedStory?: string;
parameters?: any;
storyStore?: any;
}
export type StringSlot = (context: SlotContext) => string | void;
export type PropsSlot = (context: SlotContext) => PropsTableProps | void;
export type StorySlot = (stories: StoryData[], context: SlotContext) => DocsStoryProps | void;
export type StoriesSlot = (stories: StoryData[], context: SlotContext) => DocsStoryProps[] | void;
export interface DocsPageProps {
titleSlot: StringSlot;
subtitleSlot: StringSlot;
descriptionSlot: StringSlot;
primarySlot: StorySlot;
propsSlot: PropsSlot;
storiesSlot: StoriesSlot;
}
interface DocsStoryProps {
id: string;
name: string;
expanded?: boolean;
withToolbar?: boolean;
parameters?: any;
}
interface StoryData {
id: string;
kind: string;
name: string;
parameters?: any;
}
export const defaultTitleSlot: StringSlot = ({ selectedKind, parameters }) => {
const {
showRoots,
hierarchyRootSeparator: rootSeparator,
hierarchySeparator: groupSeparator,
} = (parameters && parameters.options) || {
showRoots: undefined,
hierarchyRootSeparator: '|',
hierarchySeparator: /\/|\./,
};
let groups;
if (typeof showRoots !== 'undefined') {
groups = selectedKind.split('/');
} else {
// This covers off all the remaining cases:
// - If the separators were set above, we should use them
// - If they weren't set, we should only should use the old defaults if the kind contains '.' or '|',
// which for this particular splitting is the only case in which it actually matters.
({ groups } = parseKind(selectedKind, { rootSeparator, groupSeparator }));
}
return (groups && groups[groups.length - 1]) || selectedKind;
};
const defaultSubtitleSlot: StringSlot = ({ parameters }) =>
parameters && parameters.componentSubtitle;
const defaultPropsSlot: PropsSlot = context => getPropsTableProps({ of: '.' }, context);
const defaultDescriptionSlot: StringSlot = ({ parameters }) => {
const { component, docs } = parameters;
if (!component) {
return null;
}
const { extractComponentDescription } = docs || {};
return extractComponentDescription && extractComponentDescription(component, parameters);
};
const defaultPrimarySlot: StorySlot = stories => stories && stories[0];
const defaultStoriesSlot: StoriesSlot = stories => {
if (stories && stories.length > 1) {
const [first, ...rest] = stories;
return rest;
}
return null;
};
const StoriesHeading = H2;
const StoryHeading = H3;
const DocsStory: FunctionComponent<DocsStoryProps> = ({
id,
name,
expanded = true,
withToolbar = false,
parameters,
}) => (
<Anchor storyId={id}>
{expanded && <StoryHeading>{name}</StoryHeading>}
{expanded && parameters && parameters.docs && parameters.docs.storyDescription && (
<Description markdown={parameters.docs.storyDescription} />
)}
<Preview withToolbar={withToolbar}>
<Story id={id} />
</Preview>
</Anchor>
);
import { Primary } from './Primary';
import { Props } from './Props';
import { Stories } from './Stories';
export const DocsPage: FunctionComponent<DocsPageProps> = ({
titleSlot = defaultTitleSlot,
subtitleSlot = defaultSubtitleSlot,
descriptionSlot = defaultDescriptionSlot,
primarySlot = defaultPrimarySlot,
propsSlot = defaultPropsSlot,
storiesSlot = defaultStoriesSlot,
titleSlot,
subtitleSlot,
descriptionSlot,
primarySlot,
propsSlot,
storiesSlot,
}) => (
<DocsContext.Consumer>
{context => {
const title = titleSlot(context) || '';
const subtitle = subtitleSlot(context) || '';
const description = descriptionSlot(context) || '';
const propsTableProps = propsSlot(context);
const { selectedKind, storyStore } = context;
const componentStories = storyStore
.getStoriesForKind(selectedKind)
.filter((s: any) => !(s.parameters && s.parameters.docs && s.parameters.docs.disable));
const primary = primarySlot(componentStories, context);
const stories = storiesSlot(componentStories, context);
return (
<PureDocsPage title={title} subtitle={subtitle}>
<Description markdown={description} />
{primary && <DocsStory key={primary.id} {...primary} expanded={false} withToolbar />}
{propsTableProps && <PropsTable {...propsTableProps} />}
{stories && stories.length > 0 && <StoriesHeading>Stories</StoriesHeading>}
{stories &&
stories.map(story => story && <DocsStory key={story.id} {...story} expanded />)}
</PureDocsPage>
);
}}
</DocsContext.Consumer>
<>
<Title slot={titleSlot} />
<Subtitle slot={subtitleSlot} />
<Description slot={descriptionSlot} />
<Primary slot={primarySlot} />
<Props slot={propsSlot} />
<Stories slot={storiesSlot} />
</>
);

View File

@ -0,0 +1,25 @@
import React, { FunctionComponent } from 'react';
import { Subheading } from './Subheading';
import { DocsStoryProps } from './shared';
import { Anchor } from './Anchor';
import { Description } from './Description';
import { Story } from './Story';
import { Preview } from './Preview';
export const DocsStory: FunctionComponent<DocsStoryProps> = ({
id,
name,
expanded = true,
withToolbar = false,
parameters,
}) => (
<Anchor storyId={id}>
{expanded && <Subheading>{name}</Subheading>}
{expanded && parameters && parameters.docs && parameters.docs.storyDescription && (
<Description markdown={parameters.docs.storyDescription} />
)}
<Preview withToolbar={withToolbar}>
<Story id={id} />
</Preview>
</Anchor>
);

View File

@ -0,0 +1,7 @@
import React, { FunctionComponent } from 'react';
import { H2 } from '@storybook/components/html';
interface HeadingProps {
children: JSX.Element | string;
}
export const Heading: FunctionComponent<HeadingProps> = ({ children }) => <H2>{children}</H2>;

View File

@ -0,0 +1,16 @@
import React, { useContext, FunctionComponent } from 'react';
import { DocsContext } from './DocsContext';
import { DocsStory } from './DocsStory';
import { getDocsStories } from './utils';
import { StorySlot } from './shared';
interface PrimaryProps {
slot?: StorySlot;
}
export const Primary: FunctionComponent<PrimaryProps> = ({ slot }) => {
const context = useContext(DocsContext);
const componentStories = getDocsStories(context);
const story = slot ? slot(componentStories, context) : componentStories && componentStories[0];
return story ? <DocsStory {...story} expanded={false} withToolbar /> : null;
};

View File

@ -1,7 +1,18 @@
import React, { FunctionComponent } from 'react';
import { PropsTable, PropsTableError, PropsTableProps } from '@storybook/components';
import React, { FunctionComponent, useContext } from 'react';
import { isNil } from 'lodash';
import {
PropsTable,
PropsTableError,
PropsTableProps,
PropsTableRowsProps,
PropsTableSectionsProps,
PropDef,
TabsState,
} from '@storybook/components';
import { DocsContext, DocsContextProps } from './DocsContext';
import { Component, CURRENT_SELECTION } from './shared';
import { Component, PropsSlot, CURRENT_SELECTION } from './shared';
import { getComponentName } from './utils';
import { PropsExtractor } from '../lib/docgen/types';
import { extractProps as reactExtractProps } from '../frameworks/react/extractProps';
@ -9,7 +20,11 @@ import { extractProps as vueExtractProps } from '../frameworks/vue/extractProps'
interface PropsProps {
exclude?: string[];
of: '.' | Component;
of?: '.' | Component;
components?: {
[label: string]: Component;
};
slot?: PropsSlot;
}
// FIXME: remove in SB6.0 & require config
@ -24,36 +39,109 @@ const inferPropsExtractor = (framework: string): PropsExtractor | null => {
}
};
export const getPropsTableProps = (
{ exclude, of }: PropsProps,
const filterRows = (rows: PropDef[], exclude: string[]) =>
rows && rows.filter((row: PropDef) => !exclude.includes(row.name));
export const getComponentProps = (
component: Component,
{ exclude }: PropsProps,
{ parameters }: DocsContextProps
): PropsTableProps => {
if (!component) {
return null;
}
try {
const params = parameters || {};
const { component, framework = null } = params;
const { framework = null } = params;
const target = of === CURRENT_SELECTION ? component : of;
if (!target) {
throw new Error(PropsTableError.NO_COMPONENT);
}
const { extractProps = inferPropsExtractor(framework) } = params.docs || {};
const { extractProps = inferPropsExtractor(framework) }: { extractProps: PropsExtractor } =
params.docs || {};
if (!extractProps) {
throw new Error(PropsTableError.PROPS_UNSUPPORTED);
}
return extractProps(target, { exclude });
let props = extractProps(component);
if (!isNil(exclude)) {
const { rows } = props as PropsTableRowsProps;
const { sections } = props as PropsTableSectionsProps;
if (rows) {
props = { rows: filterRows(rows, exclude) };
} else if (sections) {
Object.keys(sections).forEach(section => {
sections[section] = filterRows(sections[section], exclude);
});
}
}
return props;
} catch (err) {
return { error: err.message };
}
};
const PropsContainer: FunctionComponent<PropsProps> = props => (
<DocsContext.Consumer>
{context => {
const propsTableProps = getPropsTableProps(props, context);
return <PropsTable {...propsTableProps} />;
}}
</DocsContext.Consumer>
);
export const getComponent = (props: PropsProps = {}, context: DocsContextProps): Component => {
const { of } = props;
const { parameters = {} } = context;
const { component } = parameters;
const target = of === CURRENT_SELECTION ? component : of;
if (!target) {
if (of === CURRENT_SELECTION) {
return null;
}
throw new Error(PropsTableError.NO_COMPONENT);
}
return target;
};
const PropsContainer: FunctionComponent<PropsProps> = props => {
const context = useContext(DocsContext);
const { slot, components } = props;
const {
parameters: { subcomponents },
} = context;
let allComponents = components;
if (!allComponents) {
const main = getComponent(props, context);
const mainLabel = getComponentName(main);
const mainProps = slot ? slot(context, main) : getComponentProps(main, props, context);
if (!subcomponents || typeof subcomponents !== 'object') {
return mainProps && <PropsTable {...mainProps} />;
}
allComponents = { [mainLabel]: main, ...subcomponents };
}
const tabs: { label: string; table: PropsTableProps }[] = [];
Object.entries(allComponents).forEach(([label, component]) => {
tabs.push({
label,
table: slot ? slot(context, component) : getComponentProps(component, props, context),
});
});
return (
<TabsState>
{tabs.map(({ label, table }) => {
if (!table) {
return null;
}
const id = `prop_table_div_${label}`;
return (
<div key={id} id={id} title={label}>
{({ active }: { active: boolean }) =>
active ? <PropsTable key={`prop_table_${label}`} {...table} /> : null
}
</div>
);
})}
</TabsState>
);
};
PropsContainer.defaultProps = {
of: '.',
};
export { PropsContainer as Props };

View File

@ -0,0 +1,33 @@
import React, { useContext, FunctionComponent } from 'react';
import { DocsContext } from './DocsContext';
import { DocsStory } from './DocsStory';
import { Heading } from './Heading';
import { getDocsStories } from './utils';
import { StoriesSlot, DocsStoryProps } from './shared';
interface StoriesProps {
slot?: StoriesSlot;
title?: JSX.Element | string;
}
export const Stories: FunctionComponent<StoriesProps> = ({ slot, title }) => {
const context = useContext(DocsContext);
const componentStories = getDocsStories(context);
const stories: DocsStoryProps[] = slot
? slot(componentStories, context)
: componentStories && componentStories.slice(1);
if (!stories) {
return null;
}
return (
<>
<Heading>{title}</Heading>
{stories.map(story => story && <DocsStory key={story.id} {...story} expanded />)}
</>
);
};
Stories.defaultProps = {
title: 'Stories',
};

View File

@ -0,0 +1,7 @@
import React, { FunctionComponent } from 'react';
import { H3 } from '@storybook/components/html';
interface SubheadingProps {
children: JSX.Element | string;
}
export const Subheading: FunctionComponent<SubheadingProps> = ({ children }) => <H3>{children}</H3>;

View File

@ -0,0 +1,19 @@
import React, { useContext, FunctionComponent } from 'react';
import { Subtitle as PureSubtitle } from '@storybook/components';
import { DocsContext } from './DocsContext';
import { StringSlot } from './shared';
interface SubtitleProps {
slot?: StringSlot;
children?: JSX.Element | string;
}
export const Subtitle: FunctionComponent<SubtitleProps> = ({ slot, children }) => {
const context = useContext(DocsContext);
const { parameters } = context;
let text: JSX.Element | string = children;
if (!text) {
text = slot ? slot(context) : parameters && parameters.componentSubtitle;
}
return text ? <PureSubtitle className="sbdocs-subtitle">{text}</PureSubtitle> : null;
};

View File

@ -0,0 +1,48 @@
import React, { useContext, FunctionComponent } from 'react';
import { parseKind } from '@storybook/router';
import { Title as PureTitle } from '@storybook/components';
import { DocsContext } from './DocsContext';
import { StringSlot } from './shared';
interface TitleProps {
slot?: StringSlot;
children?: JSX.Element | string;
}
export const defaultTitleSlot: StringSlot = ({ selectedKind, parameters }) => {
const {
showRoots,
hierarchyRootSeparator: rootSeparator,
hierarchySeparator: groupSeparator,
} = (parameters && parameters.options) || {
showRoots: undefined,
hierarchyRootSeparator: '|',
hierarchySeparator: /\/|\./,
};
let groups;
if (typeof showRoots !== 'undefined') {
groups = selectedKind.split('/');
} else {
// This covers off all the remaining cases:
// - If the separators were set above, we should use them
// - If they weren't set, we should only should use the old defaults if the kind contains '.' or '|',
// which for this particular splitting is the only case in which it actually matters.
({ groups } = parseKind(selectedKind, { rootSeparator, groupSeparator }));
}
return (groups && groups[groups.length - 1]) || selectedKind;
};
export const Title: FunctionComponent<TitleProps> = ({ slot, children }) => {
const context = useContext(DocsContext);
const { selectedKind, parameters } = context;
let text: JSX.Element | string = children;
if (!text) {
if (slot) {
text = slot(context);
} else {
text = defaultTitleSlot({ selectedKind, parameters });
}
}
return text ? <PureTitle className="sbdocs-title">{text}</PureTitle> : null;
};

View File

@ -5,12 +5,21 @@ export * from './Description';
export * from './DocsContext';
export * from './DocsPage';
export * from './DocsContainer';
export * from './DocsStory';
export * from './Heading';
export * from './Meta';
export * from './Preview';
export * from './Primary';
export * from './Props';
export * from './Source';
export * from './Stories';
export * from './Story';
export * from './Subheading';
export * from './Subtitle';
export * from './Title';
export * from './Wrapper';
export * from './shared';
// helper function for MDX
export const makeStoryFn = (val: any) => (typeof val === 'function' ? val : () => val);

View File

@ -1,2 +1,41 @@
import { PropsTableProps } from '@storybook/components';
export const CURRENT_SELECTION = '.';
export type Component = any;
export interface StoryData {
id?: string;
kind?: string;
name?: string;
parameters?: any;
}
export type DocsStoryProps = StoryData & {
expanded?: boolean;
withToolbar?: boolean;
};
export interface SlotContext {
id?: string;
selectedKind?: string;
selectedStory?: string;
parameters?: any;
storyStore?: any;
}
export type StringSlot = (context: SlotContext) => string;
export type DescriptionSlot = (description: string, context: SlotContext) => string;
export type PropsSlot = (context: SlotContext, component: Component) => PropsTableProps;
export type StorySlot = (stories: StoryData[], context: SlotContext) => DocsStoryProps;
export type StoriesSlot = (stories: StoryData[], context: SlotContext) => DocsStoryProps[];
export interface DocsPageProps {
titleSlot?: StringSlot;
subtitleSlot?: StringSlot;
descriptionSlot?: DescriptionSlot;
primarySlot?: StorySlot;
propsSlot?: PropsSlot;
storiesSlot?: StoriesSlot;
}

View File

@ -0,0 +1,34 @@
/* eslint-disable no-underscore-dangle */
import { DocsContextProps } from './DocsContext';
import { StoryData, Component } from './shared';
export const getDocsStories = (context: DocsContextProps): StoryData[] => {
const { storyStore, selectedKind } = context;
return storyStore
.getStoriesForKind(selectedKind)
.filter((s: any) => !(s.parameters && s.parameters.docs && s.parameters.docs.disable));
};
const titleCase = (str: string): string =>
str
.split('-')
.map(part => part.charAt(0).toUpperCase() + part.slice(1))
.join('');
export const getComponentName = (component: Component): string => {
if (!component) {
return undefined;
}
if (typeof component === 'string') {
if (component.includes('-')) {
return titleCase(component);
}
return component;
}
if (component.__docgenInfo && component.__docgenInfo.displayName) {
return component.__docgenInfo.displayName;
}
return component.name;
};

View File

@ -1 +0,0 @@
export * from '../../lib/docgen';

View File

@ -1,7 +1,7 @@
import PropTypes from 'prop-types';
import { isForwardRef, isMemo } from 'react-is';
import { PropDef } from '@storybook/components';
import { hasDocgen, extractPropsFromDocgen, PropsExtractor, TypeSystem } from '../../lib/docgen';
import { hasDocgen, extractComponentProps, PropsExtractor, TypeSystem } from '../../lib/docgen';
import { Component } from '../../blocks/shared';
import { enhancePropTypesProps } from './propTypes/handleProp';
@ -32,7 +32,7 @@ function getPropDefs(component: Component, section: string): PropDef[] {
}
}
const extractedProps = extractPropsFromDocgen(processedComponent, section);
const extractedProps = extractComponentProps(processedComponent, section);
if (extractedProps.length === 0) {
return [];
}

View File

@ -1,6 +1,6 @@
import { isNil } from 'lodash';
// @ts-ignore
import { PropDefaultValue, PropSummaryValue } from '@storybook/components';
import { PropDefaultValue } from '@storybook/components';
import { inspectValue } from '../inspection/inspectValue';
import { OBJECT_CAPTION, FUNCTION_CAPTION, ELEMENT_CAPTION, ARRAY_CAPTION } from './captions';
import { generateCode } from './generateCode';
@ -11,12 +11,7 @@ import {
InspectionElement,
} from '../inspection/types';
import { isHtmlTag } from './isHtmlTag';
const MAX_SUMMARY_LENGTH = 50;
function isTooLongForSummary(value: string): boolean {
return value.length > MAX_SUMMARY_LENGTH;
}
import { createSummaryValue, isTooLongForDefaultValueSummary } from '../../../lib';
// TODO: Fix this any type.
function getPrettyIdentifier(inferedType: any): string {
@ -32,10 +27,6 @@ function getPrettyIdentifier(inferedType: any): string {
}
}
function createSummaryValue(summary: string, detail?: string): PropSummaryValue {
return { summary, detail };
}
function generateObject({ ast }: InspectionResult): PropDefaultValue {
let prettyCaption = generateCode(ast, true);
@ -45,7 +36,7 @@ function generateObject({ ast }: InspectionResult): PropDefaultValue {
prettyCaption = `${prettyCaption.slice(0, -1)} }`;
}
return !isTooLongForSummary(prettyCaption)
return !isTooLongForDefaultValueSummary(prettyCaption)
? createSummaryValue(prettyCaption)
: createSummaryValue(OBJECT_CAPTION, generateCode(ast));
}
@ -59,13 +50,13 @@ function generateFunc({ inferedType, ast }: InspectionResult): PropDefaultValue
const prettyCaption = generateCode(ast, true);
return !isTooLongForSummary(prettyCaption)
return !isTooLongForDefaultValueSummary(prettyCaption)
? createSummaryValue(prettyCaption)
: createSummaryValue(FUNCTION_CAPTION, generateCode(ast));
}
// All elements are JSX elements.
// JSX elements cannot are not supported by escodegen.
// JSX elements are not supported by escodegen.
function generateElement(
defaultValue: string,
inspectionResult: InspectionResult
@ -84,7 +75,7 @@ function generateElement(
}
}
return !isTooLongForSummary(defaultValue)
return !isTooLongForDefaultValueSummary(defaultValue)
? createSummaryValue(defaultValue)
: createSummaryValue(ELEMENT_CAPTION, defaultValue);
}
@ -92,7 +83,7 @@ function generateElement(
function generateArray({ ast }: InspectionResult): PropDefaultValue {
const prettyCaption = generateCode(ast, true);
return !isTooLongForSummary(prettyCaption)
return !isTooLongForDefaultValueSummary(prettyCaption)
? createSummaryValue(prettyCaption)
: createSummaryValue(ARRAY_CAPTION, generateCode(ast));
}

View File

@ -1,5 +1,6 @@
import { isNil } from 'lodash';
import { PropSummaryValue, PropType } from '@storybook/components';
import { createSummaryValue, isTooLongForTypeSummary } from '../../../lib';
import { ExtractedProp, DocgenPropType } from '../../../lib/docgen';
import { inspectValue } from '../inspection/inspectValue';
import { generateCode } from './generateCode';
@ -15,8 +16,6 @@ import {
import { InspectionType } from '../inspection/types';
import { isHtmlTag } from './isHtmlTag';
const MAX_SUMMARY_LENGTH = 35;
enum PropTypesType {
CUSTOM = 'custom',
ANY = 'any',
@ -90,15 +89,11 @@ function getCaptionFromInspectionType(type: InspectionType): string {
}
}
function isTooLongForSummary(value: string): boolean {
return value.length > MAX_SUMMARY_LENGTH;
}
function generateValuesForObjectAst(ast: any): [string, string] {
let summary = prettyObject(ast, true);
let detail;
if (!isTooLongForSummary(summary)) {
if (!isTooLongForTypeSummary(summary)) {
detail = summary;
} else {
summary = OBJECT_CAPTION;
@ -188,7 +183,7 @@ function generateObjectOf(type: DocgenPropType, extractedProp: ExtractedProp): T
let { summary, detail } = value;
if (name === PropTypesType.SHAPE) {
if (!isTooLongForSummary(detail)) {
if (!isTooLongForTypeSummary(detail)) {
summary = detail;
}
}
@ -350,15 +345,12 @@ export function createType(extractedProp: ExtractedProp): PropType {
case PropTypesType.ARRAYOF: {
const { summary, detail } = generateType(type, extractedProp).value;
return {
summary,
detail: summary !== detail ? detail : undefined,
};
return createSummaryValue(summary, summary !== detail ? detail : undefined);
}
case PropTypesType.FUNC: {
const { detail } = generateType(type, extractedProp).value;
return { summary: detail };
return createSummaryValue(detail);
}
default:
return null;

View File

@ -3,7 +3,7 @@
import { PropDef } from '@storybook/components';
import PropTypes from 'prop-types';
import { Component } from '../../../blocks/shared';
import { extractPropsFromDocgen, DocgenInfo } from '../../../lib/docgen';
import { extractComponentProps, DocgenInfo } from '../../../lib/docgen';
import { enhancePropTypesProp, enhancePropTypesProps } from './handleProp';
const DOCGEN_SECTION = 'props';
@ -43,19 +43,19 @@ function createComponent({ propTypes = {}, defaultProps = {}, docgenInfo = {} })
}
function extractPropDef(component: Component): PropDef {
return enhancePropTypesProp(extractPropsFromDocgen(component, DOCGEN_SECTION)[0]);
return enhancePropTypesProp(extractComponentProps(component, DOCGEN_SECTION)[0]);
}
describe('enhancePropTypesProp', () => {
function createTestComponent(docgenInfo: Partial<DocgenInfo>): Component {
return createComponent({
docgenInfo: {
...createDocgenProp({ name: 'prop', ...docgenInfo }),
},
});
}
describe('type', () => {
function createTestComponent(docgenInfo: Partial<DocgenInfo>): Component {
return createComponent({
docgenInfo: {
...createDocgenProp({ name: 'prop', ...docgenInfo }),
},
});
}
describe('custom', () => {
describe('when raw value is available', () => {
it('should support literal', () => {
@ -82,9 +82,7 @@ describe('enhancePropTypesProp', () => {
const { type } = extractPropDef(component);
const expectedSummary = `{
text: string
}`;
const expectedSummary = '{ text: string }';
expect(type.summary.replace(/\s/g, '')).toBe(expectedSummary.replace(/\s/g, ''));
expect(type.detail).toBeUndefined();
@ -707,10 +705,185 @@ describe('enhancePropTypesProp', () => {
});
});
});
describe('defaultValue', () => {
function createTestComponent(defaultValue: string): Component {
return createComponent({
docgenInfo: {
...createDocgenProp({
name: 'prop',
type: { name: 'custom' },
defaultValue: { value: defaultValue },
}),
},
});
}
it('should support short object', () => {
const component = createTestComponent("{ foo: 'foo', bar: 'bar' }");
const { defaultValue } = extractPropDef(component);
const expectedSummary = "{ foo: 'foo', bar: 'bar' }";
expect(defaultValue.summary.replace(/\s/g, '')).toBe(expectedSummary.replace(/\s/g, ''));
expect(defaultValue.detail).toBeUndefined();
});
it('should support long object', () => {
const component = createTestComponent("{ foo: 'foo', bar: 'bar', another: 'another' }");
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).toBe('object');
const expectedDetail = `{
foo: 'foo',
bar: 'bar',
another: 'another'
}`;
expect(defaultValue.detail.replace(/\s/g, '')).toBe(expectedDetail.replace(/\s/g, ''));
});
it('should support short function', () => {
const component = createTestComponent('() => {}');
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).toBe('() => {}');
expect(defaultValue.detail).toBeUndefined();
});
it('should support long function', () => {
const component = createTestComponent(
'(foo, bar) => {\n const concat = foo + bar;\n const append = concat + " hey!";\n \n return append;\n}'
);
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).toBe('func');
const expectedDetail = `(foo, bar) => {
const concat = foo + bar;
const append = concat + ' hey!';
return append
}`;
expect(defaultValue.detail.replace(/\s/g, '')).toBe(expectedDetail.replace(/\s/g, ''));
});
it('should use the name of function when available and indicate that args are present', () => {
const component = createTestComponent('function concat(a, b) {\n return a + b;\n}');
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).toBe('concat( ... )');
const expectedDetail = `function concat(a, b) {
return a + b
}`;
expect(defaultValue.detail.replace(/\s/g, '')).toBe(expectedDetail.replace(/\s/g, ''));
});
it('should use the name of function when available', () => {
const component = createTestComponent('function hello() {\n return "hello";\n}');
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).toBe('hello()');
const expectedDetail = `function hello() {
return 'hello'
}`;
expect(defaultValue.detail.replace(/\s/g, '')).toBe(expectedDetail.replace(/\s/g, ''));
});
it('should support short element', () => {
const component = createTestComponent('<div>Hey!</div>');
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).toBe('<div>Hey!</div>');
expect(defaultValue.detail).toBeUndefined();
});
it('should support long element', () => {
const component = createTestComponent(
'() => {\n return <div>Inlined FunctionnalComponent!</div>;\n}'
);
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).toBe('element');
const expectedDetail = `() => {
return <div>Inlined FunctionnalComponent!</div>;
}`;
expect(defaultValue.detail.replace(/\s/g, '')).toBe(expectedDetail.replace(/\s/g, ''));
});
it("should use the name of the React component when it's available", () => {
const component = createTestComponent(
'function InlinedFunctionalComponent() {\n return <div>Inlined FunctionnalComponent!</div>;\n}'
);
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).toBe('<InlinedFunctionalComponent />');
const expectedDetail = `function InlinedFunctionalComponent() {
return <div>Inlined FunctionnalComponent!</div>;
}`;
expect(defaultValue.detail.replace(/\s/g, '')).toBe(expectedDetail.replace(/\s/g, ''));
});
it('should not use the name of an HTML element', () => {
const component = createTestComponent('<div>Hey!</div>');
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).not.toBe('<div />');
});
it('should support short array', () => {
const component = createTestComponent('[1]');
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).toBe('[1]');
expect(defaultValue.detail).toBeUndefined();
});
it('should support long array', () => {
const component = createTestComponent(
'[\n {\n thing: {\n id: 2,\n func: () => {},\n arr: [],\n },\n },\n]'
);
const { defaultValue } = extractPropDef(component);
expect(defaultValue.summary).toBe('array');
const expectedDetail = `[{
thing: {
id: 2,
func: () => {
},
arr: []
}
}]`;
expect(defaultValue.detail.replace(/\s/g, '')).toBe(expectedDetail.replace(/\s/g, ''));
});
});
});
describe('enhancePropTypesProps', () => {
it('keep the original definition order', () => {
it('should keep the original definition order', () => {
const component = createComponent({
propTypes: {
foo: PropTypes.string,
@ -741,7 +914,7 @@ describe('enhancePropTypesProps', () => {
});
const props = enhancePropTypesProps(
extractPropsFromDocgen(component, DOCGEN_SECTION),
extractComponentProps(component, DOCGEN_SECTION),
component
);
@ -751,4 +924,32 @@ describe('enhancePropTypesProps', () => {
expect(props[2].name).toBe('bar');
expect(props[3].name).toBe('endWithDefaultValue');
});
it('should not include @ignore props', () => {
const component = createComponent({
propTypes: {
foo: PropTypes.string,
bar: PropTypes.string,
},
docgenInfo: {
...createDocgenProp({
name: 'foo',
type: { name: 'string' },
}),
...createDocgenProp({
name: 'bar',
type: { name: 'string' },
description: '@ignore',
}),
},
});
const props = enhancePropTypesProps(
extractComponentProps(component, DOCGEN_SECTION),
component
);
expect(props.length).toBe(1);
expect(props[0].name).toBe('foo');
});
});

View File

@ -2,7 +2,7 @@ import { PropDef } from '@storybook/components';
import { isNil } from 'lodash';
import { Component } from '../../../blocks/shared';
// react-docgen doesn't returned the props in the order they were defined in the "propTypes" of the component.
// react-docgen doesn't returned the props in the order they were defined in the "propTypes" object of the component.
// This function re-order them by their original definition order.
export function keepOriginalDefinitionOrder(
extractedProps: PropDef[],
@ -12,7 +12,9 @@ export function keepOriginalDefinitionOrder(
const { propTypes } = component;
if (!isNil(propTypes)) {
return Object.keys(propTypes).map(x => extractedProps.find(y => y.name === x));
return Object.keys(propTypes)
.map(x => extractedProps.find(y => y.name === x))
.filter(x => x);
}
return extractedProps;

View File

@ -4,7 +4,7 @@ import toReact from '@egoist/vue-to-react';
import { StoryFn } from '@storybook/addons';
import { addParameters } from '@storybook/client-api';
import { extractProps } from './extractProps';
import { extractComponentDescription } from '../../lib/docgen/utils';
import { extractComponentDescription } from '../../lib/docgen';
addParameters({
docs: {

View File

@ -1,5 +1,5 @@
import { PropDef } from '@storybook/components';
import { PropsExtractor, hasDocgen, extractPropsFromDocgen } from '../../lib/docgen';
import { PropsExtractor, hasDocgen, extractComponentProps } from '../../lib/docgen';
const SECTIONS = ['props', 'events', 'slots'];
@ -9,7 +9,7 @@ export const extractProps: PropsExtractor = component => {
}
const sections: Record<string, PropDef[]> = {};
SECTIONS.forEach(section => {
sections[section] = extractPropsFromDocgen(component, section).map(x => x.propDef);
sections[section] = extractComponentProps(component, section).map(x => x.propDef);
});
return { sections };
};

View File

@ -1,23 +0,0 @@
import { isNil } from 'lodash';
import { PropDefaultValue } from '@storybook/components';
import { DocgenPropDefaultValue } from './types';
const BLACKLIST = ['null', 'undefined'];
function isDefaultValueBlacklisted(value: string) {
return BLACKLIST.some(x => x === value);
}
export function createDefaultValue(defaultValue: DocgenPropDefaultValue): PropDefaultValue {
if (!isNil(defaultValue)) {
const { value } = defaultValue;
if (!isDefaultValueBlacklisted(value)) {
return {
summary: value,
};
}
}
return null;
}

View File

@ -1,8 +1,10 @@
import { isNil } from 'lodash';
import { PropDef } from '@storybook/components';
import { TypeSystem, DocgenInfo, DocgenType } from './types';
import { PropDef, PropDefaultValue } from '@storybook/components';
import { TypeSystem, DocgenInfo, DocgenType, DocgenPropDefaultValue } from './types';
import { JsDocParsingResult } from '../jsdocParser';
import { createDefaultValue } from './createDefaultValue';
import { createSummaryValue } from '../utils';
import { createFlowPropDef } from './flow/createPropDef';
import { isDefaultValueBlacklisted } from './utils/defaultValue';
export type PropDefFactory = (
propName: string,
@ -10,30 +12,36 @@ export type PropDefFactory = (
jsDocParsingResult?: JsDocParsingResult
) => PropDef;
function createDefaultValue(defaultValue: DocgenPropDefaultValue): PropDefaultValue {
if (!isNil(defaultValue)) {
const { value } = defaultValue;
if (!isDefaultValueBlacklisted(value)) {
return createSummaryValue(value);
}
}
return null;
}
function createBasicPropDef(name: string, type: DocgenType, docgenInfo: DocgenInfo): PropDef {
const { description, required, defaultValue } = docgenInfo;
return {
name,
type: { summary: type.name },
type: createSummaryValue(type.name),
required,
description,
defaultValue: createDefaultValue(defaultValue),
};
}
function createPropDef(
name: string,
type: DocgenType,
docgenInfo: DocgenInfo,
jsDocParsingResult: JsDocParsingResult
): PropDef {
const propDef = createBasicPropDef(name, type, docgenInfo);
function applyJsDocResult(propDef: PropDef, jsDocParsingResult: JsDocParsingResult): PropDef {
if (jsDocParsingResult.includesJsDoc) {
const { description, extractedTags } = jsDocParsingResult;
if (!isNil(description)) {
// eslint-disable-next-line no-param-reassign
propDef.description = jsDocParsingResult.description;
}
@ -41,6 +49,7 @@ function createPropDef(
const hasReturns = !isNil(extractedTags.returns) && !isNil(extractedTags.returns.type);
if (hasParams || hasReturns) {
// eslint-disable-next-line no-param-reassign
propDef.jsDocTags = {
params:
hasParams &&
@ -53,36 +62,28 @@ function createPropDef(
return propDef;
}
export const javaScriptFactory: PropDefFactory = (
propName: string,
docgenInfo: DocgenInfo,
jsDocParsingResult?: JsDocParsingResult
) => {
return createPropDef(propName, docgenInfo.type, docgenInfo, jsDocParsingResult);
export const javaScriptFactory: PropDefFactory = (propName, docgenInfo, jsDocParsingResult) => {
const propDef = createBasicPropDef(propName, docgenInfo.type, docgenInfo);
return applyJsDocResult(propDef, jsDocParsingResult);
};
export const tsFactory: PropDefFactory = (
propName: string,
docgenInfo: DocgenInfo,
jsDocParsingResult?: JsDocParsingResult
) => {
return createPropDef(propName, docgenInfo.tsType, docgenInfo, jsDocParsingResult);
export const tsFactory: PropDefFactory = (propName, docgenInfo, jsDocParsingResult) => {
const propDef = createBasicPropDef(propName, docgenInfo.tsType, docgenInfo);
return applyJsDocResult(propDef, jsDocParsingResult);
};
export const flowFactory: PropDefFactory = (
propName: string,
docgenInfo: DocgenInfo,
jsDocParsingResult?: JsDocParsingResult
) => {
return createPropDef(propName, docgenInfo.flowType, docgenInfo, jsDocParsingResult);
export const flowFactory: PropDefFactory = (propName, docgenInfo, jsDocParsingResult) => {
const propDef = createFlowPropDef(propName, docgenInfo);
return applyJsDocResult(propDef, jsDocParsingResult);
};
export const unknownFactory: PropDefFactory = (
propName: string,
docgenInfo: DocgenInfo,
jsDocParsingResult?: JsDocParsingResult
) => {
return createPropDef(propName, { name: 'unknown' }, docgenInfo, jsDocParsingResult);
export const unknownFactory: PropDefFactory = (propName, docgenInfo, jsDocParsingResult) => {
const propDef = createBasicPropDef(propName, { name: 'unknown' }, docgenInfo);
return applyJsDocResult(propDef, jsDocParsingResult);
};
export const getPropDefFactory = (typeSystem: TypeSystem): PropDefFactory => {

View File

@ -1,7 +1,7 @@
/* eslint-disable no-underscore-dangle */
import { Component } from '../../blocks/shared';
import { extractPropsFromDocgen } from './extractDocgenProps';
import { extractComponentProps } from './extractDocgenProps';
const DOCGEN_SECTION = 'props';
const PROP_NAME = 'propName';
@ -14,6 +14,7 @@ interface TypeSystemDef {
const TypeSystems: TypeSystemDef[] = [
{ name: 'javascript', typeProperty: 'type' },
{ name: 'typescript', typeProperty: 'tsType' },
{ name: 'flow', typeProperty: 'flowType' },
];
function createType(typeName: string, others: Record<string, any> = {}): Record<string, string> {
@ -53,103 +54,127 @@ function createComponent(docgenInfo: Record<string, any>): Component {
}
TypeSystems.forEach(x => {
it('should map defaults docgen info properly', () => {
const component = createComponent({
...createStringType(x),
description: 'Hey! Hey!',
defaultValue: {
value: 'Default',
},
describe(`${x.name}`, () => {
it('should map defaults docgen info properly', () => {
const component = createComponent({
...createStringType(x),
description: 'Hey! Hey!',
defaultValue: {
value: 'Default',
},
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
expect(propDef.name).toBe(PROP_NAME);
expect(propDef.type.summary).toBe('string');
expect(propDef.description).toBe('Hey! Hey!');
expect(propDef.required).toBe(false);
expect(propDef.defaultValue.summary).toBe('Default');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should remove JSDoc tags from the description', () => {
const component = createComponent({
...createStringType(x),
description: 'Hey!\n@param event\nreturns {string}',
});
expect(propDef.name).toBe(PROP_NAME);
expect(propDef.type.summary).toBe('string');
expect(propDef.description).toBe('Hey! Hey!');
expect(propDef.required).toBe(false);
expect(propDef.defaultValue.summary).toBe('Default');
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
it('should remove JSDoc tags from the description', () => {
const component = createComponent({
...createStringType(x),
description: 'Hey!\n@param event\nreturns {string}',
expect(propDef.description).toBe('Hey!');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should not remove newline characters of multilines description without JSDoc tags', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick description\nis a\nmulti-lines\ndescription',
});
expect(propDef.description).toBe('Hey!');
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
it('should not remove newline characters of multilines description without JSDoc tags', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick description\nis a\nmulti-lines\ndescription',
expect(propDef.description).toBe('onClick description\nis a\nmulti-lines\ndescription');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should not remove newline characters of multilines description with JSDoc tags', () => {
const component = createComponent({
...createFuncType(x),
description: 'onClick description\nis a\nmulti-lines\ndescription\n@param event',
});
expect(propDef.description).toBe('onClick description\nis a\nmulti-lines\ndescription');
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
it('should not remove newline characters of multilines description with JSDoc tags', () => {
const component = createComponent({
...createFuncType(x),
description: 'onClick description\nis a\nmulti-lines\ndescription\n@param event',
expect(propDef.description).toBe('onClick description\nis a\nmulti-lines\ndescription');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should not remove markdown from description without JSDoc tags', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick *emphasis*, **strong**, `formatted` description.',
});
expect(propDef.description).toBe('onClick description\nis a\nmulti-lines\ndescription');
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
it('should not remove markdown from description without JSDoc tags', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick *emphasis*, **strong**, `formatted` description.',
expect(propDef.description).toBe('onClick *emphasis*, **strong**, `formatted` description.');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should not remove markdown from description with JSDoc tags', () => {
const component = createComponent({
...createFuncType(x),
description: 'onClick *emphasis*, **strong**, `formatted` description.\n@param event',
});
expect(propDef.description).toBe('onClick *emphasis*, **strong**, `formatted` description.');
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
it('should not remove markdown from description with JSDoc tags', () => {
const component = createComponent({
...createFuncType(x),
description: 'onClick *emphasis*, **strong**, `formatted` description.\n@param event',
expect(propDef.description).toBe('onClick *emphasis*, **strong**, `formatted` description.');
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it('should return null when the property is marked with @ignore', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick description\n@ignore',
});
expect(propDef.description).toBe('onClick *emphasis*, **strong**, `formatted` description.');
});
it('should return null when the property is marked with @ignore', () => {
const component = createComponent({
...createStringType(x),
description: 'onClick description\n@ignore',
expect(extractComponentProps(component, DOCGEN_SECTION).length).toBe(0);
});
expect(extractPropsFromDocgen(component, DOCGEN_SECTION).length).toBe(0);
});
it('should provide raw @param tags', () => {
const component = createComponent({
...createFuncType(x),
description:
'onClick description\n@param {SyntheticEvent} event - Original event.\n@param {string} value',
});
it('should provide raw @param tags', () => {
const component = createComponent({
...createFuncType(x),
description:
'onClick description\n@param {SyntheticEvent} event - Original event.\n@param {string} value',
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
expect(propDef.description).toBe('onClick description');
expect(propDef.jsDocTags).toBeDefined();
expect(propDef.jsDocTags.params).toBeDefined();
expect(propDef.jsDocTags.params[0].name).toBe('event');
expect(propDef.jsDocTags.params[0].description).toBe('Original event.');
expect(propDef.jsDocTags.params[1].name).toBe('value');
expect(propDef.jsDocTags.params[1].description).toBeNull();
});
const { propDef } = extractPropsFromDocgen(component, DOCGEN_SECTION)[0];
it("should not return 'null' default value", () => {
const component = createComponent({
...createStringType(x),
defaultValue: { value: 'null' },
});
expect(propDef.description).toBe('onClick description');
expect(propDef.jsDocTags).toBeDefined();
expect(propDef.jsDocTags.params).toBeDefined();
expect(propDef.jsDocTags.params[0].name).toBe('event');
expect(propDef.jsDocTags.params[0].description).toBe('Original event.');
expect(propDef.jsDocTags.params[1].name).toBe('value');
expect(propDef.jsDocTags.params[1].description).toBeNull();
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
expect(propDef.defaultValue).toBeNull();
});
it("should not return 'undefined' default value", () => {
const component = createComponent({
...createStringType(x),
defaultValue: { value: 'undefined' },
});
const { propDef } = extractComponentProps(component, DOCGEN_SECTION)[0];
expect(propDef.defaultValue).toBeNull();
});
});
});

View File

@ -3,7 +3,7 @@ import { PropDef } from '@storybook/components';
import { Component } from '../../blocks/shared';
import { ExtractedJsDoc, parseJsDoc } from '../jsdocParser';
import { DocgenInfo, TypeSystem } from './types';
import { getDocgenSection, isValidDocgenSection } from './utils';
import { getDocgenSection, isValidDocgenSection, getDocgenDescription } from './utils';
import { getPropDefFactory, PropDefFactory } from './createPropDef';
export interface ExtractedProp {
@ -31,7 +31,7 @@ const getTypeSystem = (docgenInfo: DocgenInfo): TypeSystem => {
return TypeSystem.UNKNOWN;
};
export const extractPropsFromDocgen: ExtractProps = (component, section) => {
export const extractComponentProps: ExtractProps = (component, section) => {
const docgenSection = getDocgenSection(component, section);
if (!isValidDocgenSection(docgenSection)) {
@ -75,3 +75,7 @@ function extractProp(
return null;
}
export function extractComponentDescription(component?: Component): string {
return !isNil(component) && getDocgenDescription(component);
}

View File

@ -0,0 +1,22 @@
import { PropDefaultValue } from '@storybook/components';
import { isNil } from 'lodash';
import { DocgenPropDefaultValue, DocgenPropType } from '../types';
import { createSummaryValue, isTooLongForDefaultValueSummary } from '../../utils';
import { isDefaultValueBlacklisted } from '../utils/defaultValue';
export function createDefaultValue(
defaultValue: DocgenPropDefaultValue,
type: DocgenPropType
): PropDefaultValue {
if (!isNil(defaultValue)) {
const { value } = defaultValue;
if (!isDefaultValueBlacklisted(value)) {
return !isTooLongForDefaultValueSummary(value)
? createSummaryValue(value)
: createSummaryValue(type.name, value);
}
}
return null;
}

View File

@ -0,0 +1,242 @@
import { isNil } from 'lodash';
import { createFlowPropDef } from './createPropDef';
import { DocgenInfo, DocgenFlowType } from '../types';
const PROP_NAME = 'propName';
function createDocgenInfo({
type,
defaultValue,
...rest
}: {
type: DocgenFlowType;
defaultValue?: string;
}): DocgenInfo {
return {
flowType: type,
defaultValue: !isNil(defaultValue) ? { value: defaultValue } : undefined,
required: false,
...rest,
};
}
describe('type', () => {
['string', 'number', 'boolean', 'any', 'void', 'Object', 'String', 'MyClass', 'literal'].forEach(
x => {
it(`should support ${x}`, () => {
const docgenInfo = createDocgenInfo({
type: { name: x },
});
const { type } = createFlowPropDef(PROP_NAME, docgenInfo);
expect(type.summary).toBe(x);
expect(type.detail).toBeUndefined();
});
}
);
['Array', 'Class', 'MyClass'].forEach(x => {
it(`should support untyped ${x}`, () => {
const docgenInfo = createDocgenInfo({
type: { name: x },
});
const { type } = createFlowPropDef(PROP_NAME, docgenInfo);
expect(type.summary).toBe(x);
expect(type.detail).toBeUndefined();
});
it(`should support typed ${x}`, () => {
const docgenInfo = createDocgenInfo({
type: {
name: x,
elements: [
{
name: 'any',
},
],
raw: `${x}<any>`,
},
});
const { type } = createFlowPropDef(PROP_NAME, docgenInfo);
expect(type.summary).toBe(`${x}<any>`);
expect(type.detail).toBeUndefined();
});
});
it('should support short object signature', () => {
const docgenInfo = createDocgenInfo({
type: {
name: 'signature',
type: 'object',
raw: '{ foo: string, bar?: mixed }',
signature: {
properties: [
{
key: 'foo',
value: {
name: 'string',
required: true,
},
},
{
key: 'bar',
value: {
name: 'mixed',
required: false,
},
},
],
},
},
});
const { type } = createFlowPropDef(PROP_NAME, docgenInfo);
expect(type.summary).toBe('{ foo: string, bar?: mixed }');
expect(type.detail).toBeUndefined();
});
it('should support long object signature', () => {
const docgenInfo = createDocgenInfo({
type: {
name: 'signature',
type: 'object',
raw: '{ (x: string): void, prop: string }',
signature: {
properties: [
{
key: 'prop',
value: {
name: 'string',
required: true,
},
},
],
constructor: {
name: 'signature',
type: 'function',
raw: '(x: string): void',
signature: {
arguments: [
{
name: 'x',
type: {
name: 'string',
},
},
],
return: {
name: 'void',
},
},
},
},
},
});
const { type } = createFlowPropDef(PROP_NAME, docgenInfo);
expect(type.summary).toBe('object');
expect(type.detail).toBe('{ (x: string): void, prop: string }');
});
it('should support func signature', () => {
const docgenInfo = createDocgenInfo({
type: {
name: 'signature',
type: 'function',
raw: '(x: string) => void',
signature: {
arguments: [
{
name: 'x',
type: {
name: 'string',
},
},
],
return: {
name: 'void',
},
},
},
});
const { type } = createFlowPropDef(PROP_NAME, docgenInfo);
expect(type.summary).toBe('(x: string) => void');
expect(type.detail).toBeUndefined();
});
it('should support tuple', () => {
const docgenInfo = createDocgenInfo({
type: {
name: 'tuple',
raw: '[foo, "value", number]',
elements: [
{
name: 'foo',
},
{
name: 'literal',
value: '"value"',
},
{
name: 'number',
},
],
},
});
const { type } = createFlowPropDef(PROP_NAME, docgenInfo);
expect(type.summary).toBe('[foo, "value", number]');
});
it('should support union', () => {
const docgenInfo = createDocgenInfo({
type: {
name: 'union',
raw: 'number | string',
elements: [
{
name: 'number',
},
{
name: 'string',
},
],
},
});
const { type } = createFlowPropDef(PROP_NAME, docgenInfo);
expect(type.summary).toBe('number | string');
});
it('should support intersection', () => {
const docgenInfo = createDocgenInfo({
type: {
name: 'intersection',
raw: 'number & string',
elements: [
{
name: 'number',
},
{
name: 'string',
},
],
},
});
const { type } = createFlowPropDef(PROP_NAME, docgenInfo);
expect(type.summary).toBe('number & string');
});
});

View File

@ -0,0 +1,15 @@
import { PropDefFactory } from '../createPropDef';
import { createType } from './createType';
import { createDefaultValue } from './createDefaultValue';
export const createFlowPropDef: PropDefFactory = (propName, docgenInfo) => {
const { flowType, description, required, defaultValue } = docgenInfo;
return {
name: propName,
type: createType(flowType),
required,
description,
defaultValue: createDefaultValue(defaultValue, flowType),
};
};

View File

@ -0,0 +1,66 @@
import { PropType } from '@storybook/components';
import { isNil } from 'lodash';
import { DocgenFlowType } from '../types';
import { createSummaryValue, isTooLongForTypeSummary } from '../../utils';
enum FlowTypesType {
UNION = 'union',
SIGNATURE = 'signature',
}
interface DocgenFlowUnionType extends DocgenFlowType {
elements: { name: string; value: string }[];
}
function generateUnion({ name, raw, elements }: DocgenFlowUnionType): PropType {
if (!isNil(raw)) {
return createSummaryValue(raw);
}
if (!isNil(elements)) {
return createSummaryValue(elements.map(x => x.value).join(' | '));
}
return createSummaryValue(name);
}
function generateFuncSignature({ type, raw }: DocgenFlowType): PropType {
if (!isNil(raw)) {
return createSummaryValue(raw);
}
return createSummaryValue(type);
}
function generateObjectSignature({ type, raw }: DocgenFlowType): PropType {
if (!isNil(raw)) {
return !isTooLongForTypeSummary(raw) ? createSummaryValue(raw) : createSummaryValue(type, raw);
}
return createSummaryValue(type);
}
function generateSignature(flowType: DocgenFlowType): PropType {
const { type } = flowType;
return type === 'object' ? generateObjectSignature(flowType) : generateFuncSignature(flowType);
}
function generateDefault({ name, raw }: DocgenFlowType): PropType {
if (!isNil(raw)) {
return !isTooLongForTypeSummary(raw) ? createSummaryValue(raw) : createSummaryValue(name, raw);
}
return createSummaryValue(name);
}
export function createType(type: DocgenFlowType): PropType {
switch (type.name) {
case FlowTypesType.UNION:
return generateUnion(type as DocgenFlowUnionType);
case FlowTypesType.SIGNATURE:
return generateSignature(type);
default:
return generateDefault(type);
}
}

View File

@ -15,8 +15,12 @@ export interface DocgenPropType extends DocgenBaseType {
computed?: boolean;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface DocgenFlowType extends DocgenBaseType {}
export interface DocgenFlowType extends DocgenBaseType {
type?: string;
raw?: string;
signature?: any;
elements?: any[];
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface DocgenTypeScriptType extends DocgenBaseType {}

View File

@ -0,0 +1,5 @@
const BLACKLIST = ['null', 'undefined'];
export function isDefaultValueBlacklisted(value: string): boolean {
return BLACKLIST.some(x => x === value);
}

View File

@ -1,17 +1,8 @@
/* eslint-disable no-underscore-dangle */
import { isNil } from 'lodash';
import { Component } from '../../blocks/shared';
export const str = (obj: any) => {
if (!obj) {
return '';
}
if (typeof obj === 'string') {
return obj as string;
}
throw new Error(`Description: expected string, got: ${JSON.stringify(obj)}`);
};
import { Component } from '../../../blocks/shared';
import { str } from './string';
export function hasDocgen(component: Component): boolean {
return !!component.__docgenInfo;
@ -25,5 +16,6 @@ export function getDocgenSection(component: Component, section: string): any {
return hasDocgen(component) ? component.__docgenInfo[section] : null;
}
export const extractComponentDescription = (component?: Component) =>
component && hasDocgen(component) && str(component.__docgenInfo.description);
export function getDocgenDescription(component: Component): string {
return hasDocgen(component) && str(component.__docgenInfo.description);
}

View File

@ -0,0 +1,3 @@
export * from './defaultValue';
export * from './string';
export * from './docgen';

View File

@ -0,0 +1,9 @@
export const str = (obj: any) => {
if (!obj) {
return '';
}
if (typeof obj === 'string') {
return obj as string;
}
throw new Error(`Description: expected string, got: ${JSON.stringify(obj)}`);
};

View File

@ -0,0 +1 @@
export * from './utils';

View File

@ -0,0 +1,16 @@
import { PropSummaryValue } from '@storybook/components';
export const MAX_TYPE_SUMMARY_LENGTH = 30;
export const MAX_DEFALUT_VALUE_SUMMARY_LENGTH = 50;
export function isTooLongForTypeSummary(value: string): boolean {
return value.length > MAX_TYPE_SUMMARY_LENGTH;
}
export function isTooLongForDefaultValueSummary(value: string): boolean {
return value.length > MAX_DEFALUT_VALUE_SUMMARY_LENGTH;
}
export function createSummaryValue(summary: string, detail?: string): PropSummaryValue {
return { summary, detail };
}

View File

@ -39,7 +39,7 @@ componentNotes.story.parameters = { mdxSource: '<Button>Component notes</Button>
const componentMeta = { title: 'Button', id: 'button-id', includeStories: ['componentNotes'] };
const mdxStoryNameToId = { 'component notes': 'button--component-notes' };
const mdxStoryNameToId = { 'component notes': 'button-id--component-notes' };
componentMeta.parameters = componentMeta.parameters || {};
componentMeta.parameters.docs = {

View File

@ -298,11 +298,11 @@ function extractExports(node, options) {
}
metaExport.includeStories = JSON.stringify(includeStories);
const { title } = metaExport;
const { title, id: componentId } = metaExport;
const mdxStoryNameToId = Object.entries(context.storyNameToKey).reduce(
(acc, [storyName, storyKey]) => {
if (title) {
acc[storyName] = toId(title, storyNameFromExport(storyKey));
acc[storyName] = toId(componentId || title, storyNameFromExport(storyKey));
}
return acc;
},

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-events",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Add events to your Storybook stories.",
"keywords": [
"addon",
@ -31,11 +31,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-api": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-api": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"format-json": "^1.0.3",
"lodash": "^4.17.15",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-google-analytics",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook addon for google analytics",
"keywords": [
"addon",
@ -20,8 +20,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"react-ga": "^2.5.7"

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-graphql",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook addon to display the GraphiQL IDE",
"keywords": [
"addon",
@ -29,8 +29,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"graphiql": "^0.16.0",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-info",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "A Storybook addon to show additional information for your stories.",
"keywords": [
"addon",
@ -28,10 +28,10 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"marksy": "^7.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-jest",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "React storybook addon that show component jest report",
"keywords": [
"addon",
@ -35,11 +35,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"react": "^16.8.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-knobs",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook Addon Prop Editor Component",
"keywords": [
"addon",
@ -29,12 +29,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-api": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-api": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"@types/react-color": "^3.0.1",
"copy-to-clipboard": "^3.0.8",
"core-js": "^3.0.1",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-links",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Story Links addon for storybook",
"keywords": [
"addon",
@ -29,10 +29,10 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/router": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/router": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"prop-types": "^15.7.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-notes",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Write notes for your Storybook stories.",
"keywords": [
"addon",
@ -30,13 +30,13 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/router": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/router": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"markdown-to-jsx": "^6.10.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-ondevice-actions",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Action Logger addon for react-native storybook",
"keywords": [
"storybook"
@ -26,13 +26,13 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"core-js": "^3.0.1",
"fast-deep-equal": "^2.0.1"
},
"devDependencies": {
"@storybook/addon-actions": "5.3.0-beta.1"
"@storybook/addon-actions": "5.3.0-beta.3"
},
"peerDependencies": {
"@storybook/addon-actions": "*",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-ondevice-backgrounds",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "A react-native storybook addon to show different backgrounds for your preview",
"keywords": [
"addon",
@ -31,9 +31,9 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-api": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-api": "5.3.0-beta.3",
"core-js": "^3.0.1",
"prop-types": "^15.7.2"
},

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-ondevice-knobs",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Display storybook story knobs on your deviced.",
"keywords": [
"addon",
@ -28,8 +28,8 @@
},
"dependencies": {
"@emotion/native": "^10.0.14",
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"core-js": "^3.0.1",
"deep-equal": "^1.0.1",
"prop-types": "^15.7.2",
@ -39,7 +39,7 @@
"react-native-switch": "^1.5.0"
},
"peerDependencies": {
"@storybook/addon-knobs": "5.2.0-alpha.34",
"@storybook/addon-knobs": "5.2.0-beta.1",
"react": "*",
"react-native": "*"
},

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-ondevice-notes",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Write notes for your react-native Storybook stories.",
"keywords": [
"addon",
@ -28,11 +28,11 @@
},
"dependencies": {
"@emotion/core": "^10.0.20",
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-api": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-api": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"core-js": "^3.0.1",
"prop-types": "^15.7.2",
"react-native-simple-markdown": "^1.1.0"

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-options",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Options addon for storybook",
"keywords": [
"addon",
@ -29,7 +29,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"core-js": "^3.0.1",
"util-deprecate": "^1.0.2"
},

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-queryparams",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "parameter addon for storybook",
"keywords": [
"addon",
@ -30,12 +30,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"qs": "^6.6.0",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-storyshots",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "StoryShots is a Jest Snapshot Testing Addon for Storybook.",
"keywords": [
"addon",
@ -33,8 +33,8 @@
},
"dependencies": {
"@jest/transform": "^24.9.0",
"@storybook/addons": "5.3.0-beta.1",
"@storybook/client-api": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/client-api": "5.3.0-beta.3",
"@types/glob": "^7.1.1",
"@types/jest": "^24.0.16",
"@types/jest-specific-snapshot": "^0.5.3",
@ -47,8 +47,8 @@
"ts-dedent": "^1.1.0"
},
"devDependencies": {
"@storybook/addon-docs": "5.3.0-beta.1",
"@storybook/react": "5.3.0-beta.1",
"@storybook/addon-docs": "5.3.0-beta.3",
"@storybook/react": "5.3.0-beta.3",
"babel-loader": "^8.0.6",
"enzyme-to-json": "^3.4.1",
"jest-emotion": "^10.0.17",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-storyshots-puppeteer",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Image snapshots addition to StoryShots based on puppeteer",
"keywords": [
"addon",
@ -29,8 +29,8 @@
"prepare": "node ../../../scripts/prepare.js"
},
"dependencies": {
"@storybook/node-logger": "5.3.0-beta.1",
"@storybook/router": "5.3.0-beta.1",
"@storybook/node-logger": "5.3.0-beta.3",
"@storybook/router": "5.3.0-beta.3",
"@types/jest-image-snapshot": "^2.8.0",
"@types/puppeteer-core": "^1.9.0",
"core-js": "^3.0.1",
@ -39,7 +39,7 @@
"regenerator-runtime": "^0.13.3"
},
"peerDependencies": {
"@storybook/addon-storyshots": "5.3.0-alpha.13"
"@storybook/addon-storyshots": "5.3.0-beta.1"
},
"publishConfig": {
"access": "public"

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-storysource",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Stories addon for storybook",
"keywords": [
"addon",
@ -28,11 +28,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/router": "5.3.0-beta.1",
"@storybook/source-loader": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/router": "5.3.0-beta.3",
"@storybook/source-loader": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"estraverse": "^4.2.0",
"loader-utils": "^1.2.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-viewport",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook addon to change the viewport size to mobile",
"keywords": [
"addon",
@ -29,12 +29,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"memoizerific": "^1.11.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/angular",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for Angular: Develop Angular Components in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -33,9 +33,9 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.1",
"@storybook/node-logger": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core": "5.3.0-beta.3",
"@storybook/node-logger": "5.3.0-beta.3",
"core-js": "^3.0.1",
"fork-ts-checker-webpack-plugin": "^3.0.1",
"global": "^4.3.2",

View File

@ -51,12 +51,23 @@ describe('angular-cli_config', () => {
});
});
it('should return null if `architect.build` option are not exists.', () => {
const angularJson = fs.readFileSync(path.resolve(__dirname, 'angular.json'), 'utf8');
const angularJsonWithNoBuildOptions = JSON.parse(stripJsonComments(angularJson));
angularJsonWithNoBuildOptions.projects['angular-cli'].architect.build = undefined;
getLeadingAngularCliProject(angularJsonWithNoBuildOptions);
const config = getAngularCliWebpackConfigOptions('/');
expect(config).toBeNull();
});
it('should return baseConfig if no angular.json was found', () => {
const baseConfig = { test: 'config' };
const projectConfig = getAngularCliWebpackConfigOptions('test-path' as Path);
const config = applyAngularCliWebpackConfig(baseConfig, projectConfig);
expect(projectConfig).toBe(undefined);
expect(projectConfig).toBe(null);
expect(config).toBe(baseConfig);
});
});

View File

@ -57,6 +57,10 @@ export function getAngularCliConfig(dirToSearch: string) {
}
export function getLeadingAngularCliProject(ngCliConfig: any) {
if (!ngCliConfig) {
return null;
}
const { defaultProject } = ngCliConfig;
const { projects } = ngCliConfig;
if (!projects || !Object.keys(projects).length) {
@ -70,19 +74,18 @@ export function getLeadingAngularCliProject(ngCliConfig: any) {
export function getAngularCliWebpackConfigOptions(dirToSearch: Path) {
const angularCliConfig = getAngularCliConfig(dirToSearch);
if (!angularCliConfig) {
return undefined;
const project = getLeadingAngularCliProject(angularCliConfig);
if (!angularCliConfig || !project.architect.build) {
return null;
}
const project = getLeadingAngularCliProject(angularCliConfig);
const { options: projectOptions } = project.architect.build;
const normalizedAssets = normalizeAssetPatterns(
projectOptions.assets,
dirToSearch,
project.sourceRoot
);
const projectRoot = path.resolve(dirToSearch, project.root);
const tsConfigPath = path.resolve(dirToSearch, projectOptions.tsConfig) as Path;
const tsConfig = getTsConfigOptions(tsConfigPath);

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/ember",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for Ember: Develop Ember Component in isolation with Hot Reloading.",
"homepage": "https://github.com/storybookjs/storybook/tree/master/app/ember",
"bugs": {
@ -31,7 +31,7 @@
},
"dependencies": {
"@ember/test-helpers": "^1.5.0",
"@storybook/core": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"regenerator-runtime": "^0.13.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/html",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for HTML: View HTML snippets in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -33,8 +33,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core": "5.3.0-beta.3",
"@types/webpack-env": "^1.13.9",
"core-js": "^3.0.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/marko",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for Marko: Develop Marko Component in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -33,8 +33,8 @@
},
"dependencies": {
"@marko/webpack": "^2.0.0",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/core": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"regenerator-runtime": "^0.13.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/mithril",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for Mithril: Develop Mithril Component in isolation.",
"keywords": [
"storybook"
@ -35,8 +35,8 @@
"dependencies": {
"@babel/core": "^7.6.2",
"@babel/plugin-transform-react-jsx": "^7.3.0",
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core": "5.3.0-beta.3",
"@types/mithril": "^2.0.0",
"core-js": "^3.0.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/polymer",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for Polymer: Develop Polymer components in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -32,7 +32,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/core": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.3",
"@webcomponents/webcomponentsjs": "^1.2.0",
"core-js": "^3.0.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/preact",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for Preact: Develop Preact Component in isolation.",
"keywords": [
"storybook"
@ -34,8 +34,8 @@
},
"dependencies": {
"@babel/plugin-transform-react-jsx": "^7.3.0",
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core": "5.3.0-beta.3",
"@types/webpack-env": "^1.13.9",
"core-js": "^3.0.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/rax",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for Rax: Develop Rax Component in isolation.",
"keywords": [
"rax",
@ -33,7 +33,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/core": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.3",
"babel-preset-rax": "^1.0.0-beta.0",
"core-js": "^3.0.1",
"driver-dom": "^2.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/react-native-server",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "A better way to develop React Native Components for your app",
"keywords": [
"react",
@ -31,12 +31,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/channel-websocket": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/ui": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/channel-websocket": "5.3.0-beta.3",
"@storybook/core": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/ui": "5.3.0-beta.3",
"commander": "^4.0.1",
"core-js": "^3.0.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/react-native",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "A better way to develop React Native Components for your app",
"keywords": [
"react",
@ -31,11 +31,11 @@
"dependencies": {
"@emotion/core": "^10.0.20",
"@emotion/native": "^10.0.14",
"@storybook/addons": "5.3.0-beta.1",
"@storybook/channel-websocket": "5.3.0-beta.1",
"@storybook/channels": "5.3.0-beta.1",
"@storybook/client-api": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/channel-websocket": "5.3.0-beta.3",
"@storybook/channels": "5.3.0-beta.3",
"@storybook/client-api": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"core-js": "^3.0.1",
"emotion-theming": "^10.0.19",
"react-native-swipe-gestures": "^1.0.4"

View File

@ -25,7 +25,7 @@ You can also build a [static version](https://storybook.js.org/basics/exporting-
Here are some featured storybooks that you can reference to see how Storybook works:
- [Demo of React Dates](http://airbnb.io/react-dates/) - [source](https://github.com/airbnb/react-dates)
- [Demo of React Native Web](http://necolas.github.io/react-native-web/storybook/) - [source](https://github.com/necolas/react-native-web)
- [Demo of React Native Web](https://necolas.github.io/react-native-web/docs/) - [source](https://github.com/necolas/react-native-web)
## Create React App

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/react",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for React: Develop React Component in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -36,9 +36,9 @@
"@babel/plugin-transform-react-constant-elements": "^7.2.0",
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.1",
"@storybook/node-logger": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core": "5.3.0-beta.3",
"@storybook/node-logger": "5.3.0-beta.3",
"@svgr/webpack": "^4.0.3",
"@types/webpack-env": "^1.13.7",
"babel-plugin-add-react-displayname": "^0.0.5",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/riot",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for riot.js: View riot snippets in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -33,7 +33,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/core": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"raw-loader": "^3.1.0",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/svelte",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for Svelte: Develop Svelte Component in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -33,8 +33,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"regenerator-runtime": "^0.13.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/vue",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for Vue: Develop Vue Component in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -33,8 +33,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core": "5.3.0-beta.3",
"@types/webpack-env": "^1.13.9",
"core-js": "^3.0.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/web-components",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "Storybook for web-components: View web components snippets in isolation with Hot Reloading.",
"keywords": [
"lit-html",
@ -37,8 +37,8 @@
"dependencies": {
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-syntax-import-meta": "^7.2.0",
"@storybook/addons": "5.3.0-beta.1",
"@storybook/core": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/core": "5.3.0-beta.3",
"@types/webpack-env": "^1.13.9",
"babel-plugin-bundled-import-meta": "^0.3.1",
"core-js": "^3.0.1",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-decorator",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "decorator addon for storybook",
"keywords": [
"addon",
@ -24,8 +24,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/client-api": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/client-api": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.4.0"
},

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-parameter",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "parameter addon for storybook",
"keywords": [
"addon",
@ -24,12 +24,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"react": "^16.8.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-preview-wrapper",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "preview wrapper addon for storybook",
"keywords": [
"addon",
@ -24,7 +24,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"react": "^16.8.3"
},
"publishConfig": {

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-roundtrip",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"description": "roundtrip addon for storybook",
"keywords": [
"addon",
@ -24,13 +24,13 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.3.0-beta.1",
"@storybook/api": "5.3.0-beta.1",
"@storybook/client-api": "5.3.0-beta.1",
"@storybook/client-logger": "5.3.0-beta.1",
"@storybook/components": "5.3.0-beta.1",
"@storybook/core-events": "5.3.0-beta.1",
"@storybook/theming": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/api": "5.3.0-beta.3",
"@storybook/client-api": "5.3.0-beta.3",
"@storybook/client-logger": "5.3.0-beta.3",
"@storybook/components": "5.3.0-beta.3",
"@storybook/core-events": "5.3.0-beta.3",
"@storybook/theming": "5.3.0-beta.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"react": "^16.8.3",

View File

@ -38,9 +38,9 @@
"marked": "^0.7.0",
"polished": "^3.4.2",
"prop-types": "^15.7.2",
"react": "^16.11.0",
"react": "^16.12.0",
"react-document-title": "^2.0.3",
"react-dom": "^16.11.0",
"react-dom": "^16.12.0",
"react-helmet": "^5.2.0",
"react-popper-tooltip": "^2.10.0",
"react-router": "^4.3.1",

View File

@ -6,7 +6,7 @@ title: 'Exporting Storybook as a Static App'
Storybook gives a great developer experience with its dev time features, like instant change updates via Webpack's HMR.
But Storybook is also a tool you can use to showcase your components to others.
Demos of [React Native Web](http://necolas.github.io/react-native-web/storybook/) and [React Dates](http://airbnb.io/react-dates/) are a good example for that.
Demos of [React Native Web](https://necolas.github.io/react-native-web/docs/) and [React Dates](http://airbnb.io/react-dates/) are a good example for that.
For that, Storybook comes with a tool to export your storybook into a static web app. Then you can deploy it to GitHub pages or any static hosting service.

View File

@ -16,6 +16,6 @@ A [Static version](/basics/exporting-storybook) of Storybook can also be built a
Here are some featured Storybooks to see how it works:
- [Demo of React Dates](http://airbnb.io/react-dates/) - [source](https://github.com/airbnb/react-dates)
- [Demo of React Native Web](http://necolas.github.io/react-native-web/storybook/) - [source](https://github.com/necolas/react-native-web)
- [Demo of React Native Web](https://necolas.github.io/react-native-web/docs/) - [source](https://github.com/necolas/react-native-web)
Read the Learn Storybook [tutorial](https://www.learnstorybook.com) for a step by step guide to building an app with Storybook and to see how building components in isolation can supercharge your app development workflow.

View File

@ -69,7 +69,7 @@ necolas:
title: React Native Web
description: Storybook demo for React Native Web.
source: https://github.com/necolas/react-native-web
demo: https://necolas.github.io/react-native-web/storybook/
demo: https://necolas.github.io/react-native-web/docs/
griddle:
thumbnail: griddle.jpg
title: Griddle

View File

@ -82,7 +82,7 @@ storiesOf('Toggle', module)
owner: 'https://avatars3.githubusercontent.com/u/239676?v=3&s=460',
storybook: {
name: 'React Native Web',
link: 'https://necolas.github.io/react-native-web/storybook',
link: 'https://necolas.github.io/react-native-web/docs/',
},
source: 'https://github.com/necolas/react-native-web',
},

View File

@ -1 +1 @@
{"version":"5.3.0-beta.1","info":{"plain":"### Features\n\n* Addon-google-analytics: Add gaOption config ([#8859](https://github.com/storybookjs/storybook/pull/8859))\n\n### Bug Fixes\n\n* Addon-docs: Fix props table props sorting for PropTypes ([#8857](https://github.com/storybookjs/storybook/pull/8857))\n* Fix layout of Preview container ([#8628](https://github.com/storybookjs/storybook/pull/8628))"}}
{"version":"5.3.0-beta.3","info":{"plain":"### Features\n\n* Addon-docs: Rich props table UI ([#8887](https://github.com/storybookjs/storybook/pull/8887))\n* Addon-docs: Improve basic support for Flow props ([#8890](https://github.com/storybookjs/storybook/pull/8890))\n* CLI: Avoid id changes after `storiesof-to-csf` migration ([#8856](https://github.com/storybookjs/storybook/pull/8856))\n\n### Bug Fixes\n\n* Addon-docs: Fix props table for sections props ([#8904](https://github.com/storybookjs/storybook/pull/8904))\n* Addon-docs: Fix Description block when no component provided ([#8902](https://github.com/storybookjs/storybook/pull/8902))\n* Angular: Fix project without `architect.build` option ([#6737](https://github.com/storybookjs/storybook/pull/6737))\n\n### Maintenance\n\n* Addon-docs: Docgen lib maintenance ([#8896](https://github.com/storybookjs/storybook/pull/8896))\n* Examples: Fix stories glob in official-storybook ([#8888](https://github.com/storybookjs/storybook/pull/8888))"}}

View File

@ -9807,25 +9807,15 @@ react-dom@^15.6.0:
object-assign "^4.1.0"
prop-types "^15.5.10"
react-dom@^16.11.0:
version "16.11.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.11.0.tgz#7e7c4a5a85a569d565c2462f5d345da2dd849af5"
integrity sha512-nrRyIUE1e7j8PaXSPtyRKtz+2y9ubW/ghNgqKFHHAHaeP0fpF5uXR+sq8IMRHC+ZUxw7W9NyCDTBtwWxvkb0iA==
react-dom@^16.12.0, react-dom@^16.8.3:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.12.0.tgz#0da4b714b8d13c2038c9396b54a92baea633fe11"
integrity sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.17.0"
react-dom@^16.8.3:
version "16.9.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.9.0.tgz#5e65527a5e26f22ae3701131bcccaee9fb0d3962"
integrity sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
scheduler "^0.15.0"
scheduler "^0.18.0"
react-error-overlay@^3.0.0:
version "3.0.0"
@ -10040,19 +10030,10 @@ react@^15.6.0:
object-assign "^4.1.0"
prop-types "^15.5.10"
react@^16.11.0:
version "16.11.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.11.0.tgz#d294545fe62299ccee83363599bf904e4a07fdbb"
integrity sha512-M5Y8yITaLmU0ynd0r1Yvfq98Rmll6q8AxaEe88c8e7LxO8fZ2cNgmFt0aGAS9wzf1Ao32NKXtCl+/tVVtkxq6g==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
react@^16.8.3:
version "16.9.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.9.0.tgz#40ba2f9af13bc1a38d75dbf2f4359a5185c4f7aa"
integrity sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w==
react@^16.12.0, react@^16.8.3:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83"
integrity sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
@ -10882,18 +10863,10 @@ sc-formatter@^3.0.1:
resolved "https://registry.yarnpkg.com/sc-formatter/-/sc-formatter-3.0.2.tgz#9abdb14e71873ce7157714d3002477bbdb33c4e6"
integrity sha512-9PbqYBpCq+OoEeRQ3QfFIGE6qwjjBcd2j7UjgDlhnZbtSnuGgHdcRklPKYGuYFH82V/dwd+AIpu8XvA1zqTd+A==
scheduler@^0.15.0:
version "0.15.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.15.0.tgz#6bfcf80ff850b280fed4aeecc6513bc0b4f17f8e"
integrity sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
scheduler@^0.17.0:
version "0.17.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.17.0.tgz#7c9c673e4ec781fac853927916d1c426b6f3ddfe"
integrity sha512-7rro8Io3tnCPuY4la/NuI5F2yfESpnfZyT6TtkXnSWVkcu0BCDJ+8gk5ozUaFaxpIyNuWAPXrH0yFcSi28fnDA==
scheduler@^0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4"
integrity sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"

View File

@ -1,6 +1,6 @@
{
"name": "crna-kitchen-sink",
"version": "5.3.0-beta.1",
"version": "5.3.0-beta.3",
"private": true,
"main": "node_modules/expo/AppEntry.js",
"workspaces": {
@ -31,15 +31,15 @@
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-transform-react-jsx-source": "^7.2.0",
"@storybook/addon-actions": "5.3.0-beta.1",
"@storybook/addon-knobs": "5.3.0-beta.1",
"@storybook/addon-links": "5.3.0-beta.1",
"@storybook/addon-ondevice-actions": "5.3.0-beta.1",
"@storybook/addon-ondevice-backgrounds": "5.3.0-beta.1",
"@storybook/addon-ondevice-knobs": "5.3.0-beta.1",
"@storybook/addon-ondevice-notes": "5.3.0-beta.1",
"@storybook/addons": "5.3.0-beta.1",
"@storybook/react-native": "5.3.0-beta.1",
"@storybook/addon-actions": "5.3.0-beta.3",
"@storybook/addon-knobs": "5.3.0-beta.3",
"@storybook/addon-links": "5.3.0-beta.3",
"@storybook/addon-ondevice-actions": "5.3.0-beta.3",
"@storybook/addon-ondevice-backgrounds": "5.3.0-beta.3",
"@storybook/addon-ondevice-knobs": "5.3.0-beta.3",
"@storybook/addon-ondevice-notes": "5.3.0-beta.3",
"@storybook/addons": "5.3.0-beta.3",
"@storybook/react-native": "5.3.0-beta.3",
"babel-loader": "^8.0.4",
"babel-plugin-module-resolver": "^3.2.0",
"babel-preset-expo": "^7.0.0",

Some files were not shown because too many files have changed in this diff Show More