Merge branch 'next' into upgrades

# Conflicts:
#	addons/essentials/package.json
#	addons/notes/package.json
#	addons/storyshots/storyshots-core/package.json
#	app/angular/package.json
#	app/html/package.json
#	app/marko/package.json
#	app/preact/package.json
#	app/react/package.json
#	app/vue/package.json
#	app/web-components/package.json
#	examples/riot-kitchen-sink/package.json
#	examples/vue-kitchen-sink/package.json
#	lib/cli/package.json
#	lib/core/package.json
#	yarn.lock
This commit is contained in:
Norbert de Langen 2020-02-03 13:21:06 +01:00
commit 746b2a4f11
No known key found for this signature in database
GPG Key ID: 976651DA156C2825
257 changed files with 2511 additions and 15683 deletions

View File

@ -1,3 +1,31 @@
## 5.3.10 (February 2, 2020)
### Bug Fixes
* Core: Upgrade `min-css-extract-plugin` to fix SASS loading ([#9652](https://github.com/storybookjs/storybook/pull/9652))
* CRA: Fix jsconfig support ([#9324](https://github.com/storybookjs/storybook/pull/9324))
* Web-components: Fix default value for docs prop table ([#9655](https://github.com/storybookjs/storybook/pull/9655))
* Web-components: Fix types to play nicely with lit-element ([#9557](https://github.com/storybookjs/storybook/pull/9557))
* UI: Add support for className prop on Form.Field ([#9665](https://github.com/storybookjs/storybook/pull/9665))
* Addon-storyshots: Remove excess slashes from jest transform warning ([#9616](https://github.com/storybookjs/storybook/pull/9616))
### Maintenance
* Ember: Migrate to new "import { hbs } from 'ember-cli-htmlbars'" ([#9633](https://github.com/storybookjs/storybook/pull/9633))
* Build: Netlify for examples again ([#9585](https://github.com/storybookjs/storybook/pull/9585))
* Publish: Remove docs to reduce package size ([#9612](https://github.com/storybookjs/storybook/pull/9612))
## 6.0.0-alpha.3 (February 2, 2020)
### Bug Fixes
* CRA: Fix jsconfig support ([#9324](https://github.com/storybookjs/storybook/pull/9324))
* UI: Check if docsOnly is set to hide the addon panels ([#9687](https://github.com/storybookjs/storybook/pull/9687))
### Maintenance
* Addon-notes, addon-info: Move to deprecated-addons repo ([#9673](https://github.com/storybookjs/storybook/pull/9673))
## 6.0.0-alpha.2 (January 30, 2020)
### Features

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-a11y",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "a11y addon for storybook",
"keywords": [
"a11y",
@ -32,12 +32,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/client-logger": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/client-logger": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Action Logger addon for storybook",
"keywords": [
"storybook"
@ -27,12 +27,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/client-api": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/client-api": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.3",
"core-js": "^3.0.1",
"fast-deep-equal": "^3.1.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-backgrounds",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "A storybook addon to show different backgrounds for your preview",
"keywords": [
"addon",
@ -31,12 +31,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/client-logger": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/client-logger": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Storybook decorator to center components",
"keywords": [
"addon",
@ -29,7 +29,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"util-deprecate": "^1.0.2"

View File

@ -5,6 +5,17 @@ import parameters from './parameters';
import styles from './styles';
function centered(storyFn: () => ReactNode) {
/* eslint-disable no-undef */
if (window) {
const params = new URL(window.location.href).search;
const isInDocsView = params.includes('viewMode=docs');
if (isInDocsView) {
return storyFn();
}
}
/* eslint-enable no-undef */
return (
<div style={styles.style}>
<div style={styles.innerStyle}>{storyFn()}</div>

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-contexts",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Storybook Addon Contexts",
"keywords": [
"preact",
@ -27,10 +27,10 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "A storybook addon to switch between css resources at runtime for your story",
"keywords": [
"addon",
@ -31,11 +31,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Design asset preview for storybook",
"keywords": [
"addon",
@ -33,12 +33,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/client-logger": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/client-logger": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"react": "^16.8.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-docs",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Superior documentation for your components",
"keywords": [
"addon",
@ -45,14 +45,14 @@
"@mdx-js/loader": "^1.5.1",
"@mdx-js/mdx": "^1.5.1",
"@mdx-js/react": "^1.5.1",
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/csf": "0.0.1",
"@storybook/postinstall": "6.0.0-alpha.2",
"@storybook/source-loader": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/postinstall": "6.0.0-alpha.3",
"@storybook/source-loader": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.3",
"acorn": "^7.1.0",
"acorn-jsx": "^5.1.0",
"acorn-walk": "^7.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-essentials",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Curated addons to bring out the best of Storybook",
"keywords": [
"addon",
@ -27,11 +27,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addon-backgrounds": "6.0.0-alpha.2",
"@storybook/addon-viewport": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/node-logger": "6.0.0-alpha.2",
"@storybook/addon-backgrounds": "6.0.0-alpha.3",
"@storybook/addon-viewport": "6.0.0-alpha.3",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/node-logger": "6.0.0-alpha.3",
"ts-dedent": "^1.1.1"
},
"devDependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-events",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Add events to your Storybook stories.",
"keywords": [
"addon",
@ -30,11 +30,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/client-api": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/client-api": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Storybook addon for google analytics",
"keywords": [
"addon",
@ -20,8 +20,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Storybook addon to display the GraphiQL IDE",
"keywords": [
"addon",
@ -28,8 +28,8 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"graphiql": "^0.17.5",

View File

@ -1,338 +0,0 @@
# Storybook Info Addon
Storybook Info Addon will show additional information for your stories in [Storybook](https://storybook.js.org).
Useful when you want to display usage or other types of documentation alongside your story.
[Framework Support](https://github.com/storybookjs/storybook/blob/master/ADDONS_SUPPORT.md)
![Screenshot](https://raw.githubusercontent.com/storybookjs/storybook/HEAD/addons/info/docs/home-screenshot.png)
## Installation
Install the following npm module:
```sh
npm i -D @storybook/addon-info
```
## Basic usage
Then, add `withInfo` as a decorator to your book of stories.
It is possible to add `info` by default to all or a subsection of stories by using a global or story decorator.
It is important to declare this decorator as **the first decorator**, otherwise it won't work well.
```js
// Globally in your .storybook/preview.js.
import { addDecorator } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
addDecorator(withInfo);
```
or
```js
export default {
title: 'Component',
decorators: [withInfo],
};
```
Then, you can use the `info` parameter to either pass certain options or specific documentation text to your stories.
A complete list of possible configurations can be found [in a later section](#setting-global-options).
This can be done per book of stories:
```js
import Component from './Component';
export default {
title: 'Component',
parameters: {
info: {},
},
};
```
...or for each story individually:
```js
import Component from './Component';
export default {
title: 'Component',
};
export const defaultView = () => <Component />;
defaultView = {
parameters: {
info: { inline: true },
},
};
```
It is also possible to disable the `info` addon entirely.
Depending on the scope at which you want to disable the addon, pass the following parameters object either to an individual story or to an `addParameters` call.
```
info: {
disable: true,
}
```
## Markdown
The `info` addon also supports markdown.
To use markdown as additional textual documentation for your stories, either pass it directly as a String to the `info` parameters, or use the `text` option.
```js
info: {
text: `
description or documentation about my component, supports markdown
~~~js
<Button>Click Here</Button>
~~~
`,
}
```
## Setting Global Options
To configure default options for all usage of the info addon, pass a option object along with the decorator in `.storybook/preview.js`.
```js
import { withInfo } from '@storybook/addon-info';
addDecorator(
withInfo({
header: false,
})
);
```
Configuration parameters can be set at 3 different locations: passed as default options along the `addDecorator` call, passed as an object of parameters to a book of stories to the `addParameters` call, and passed as direct parameters to each individual story.
In order, all of them will be combined together, with a later call overriding the previous set configurations on a per-key basis.
## Options and Defaults
```js
{
/**
* Text to display with storybook component
*/
text?: string;
/**
* Displays info inline vs click button to view
* @default false
*/
inline: boolean,
/**
* Toggles display of header with component name and description
* @default true
*/
header: boolean,
/**
* Displays the source of story Component
* @default true
*/
source: boolean,
/**
* Components used in story
* Displays Prop Tables with these components
* @default []
*/
propTables: Array<React.ComponentType>,
/**
* Exclude Components from being shown in Prop Tables section
* Accepts an array of component classes or functions
* @default []
*/
propTablesExclude: Array<React.ComponentType>,
/**
* Overrides styles of addon. The object should follow this shape:
* https://github.com/storybookjs/storybook/blob/master/addons/info/src/components/Story.js#L19.
* This prop can also accept a function which has the default stylesheet passed as an argument
*/
styles: Object | Function,
/**
* Overrides components used to display markdown
* @default {}
*/
components: { [key: string]: React.ComponentType },
/**
* Max props to display per line in source code
* @default 3
*/
maxPropsIntoLine: number,
/**
* Displays the first 10 characters of the prop name
* @default 3
*/
maxPropObjectKeys: number,
/**
* Displays the first 10 items in the default prop array
* @default 3
*/
maxPropArrayLength: number,
/**
* Displays the first 100 characters in the default prop string
* @default 50
*/
maxPropStringLength: number,
/**
* Override the component used to render the props table
* @default PropTable
*/
TableComponent: React.ComponentType,
/**
* Will exclude any respective properties whose name is included in array
* @default []
*/
excludedPropTypes: Array<string>,
}
```
### Rendering a Custom Table
The `TableComponent` option allows you to define how the prop table should be rendered. Your component will be rendered with the following props.
```js
{
propDefinitions: Array<{
property: string, // The name of the prop
propType: Object | string, // The prop type. TODO: info about what this object is...
required: boolean, // True if the prop is required
description: string, // The description of the prop
defaultValue: any // The default value of the prop
}>
}
```
Example:
```js
// button.js
// @flow
import React from 'react';
const paddingStyles = {
small: '4px 8px',
medium: '8px 16px',
};
const Button = ({
size,
...rest
}: {
/** The size of the button */
size: 'small' | 'medium',
}) => {
const style = {
padding: paddingStyles[size] || '',
};
return <button style={style} {...rest} />;
};
Button.defaultProps = {
size: 'medium',
};
export default Button;
```
```js
// stories.js
import React from 'react';
import { storiesOf } from '@storybook/react';
import Button from './button';
export default {
title: 'Button',
component: Button,
parameters: {
info: TableComponent,
},
};
const Red = props => <span style={{ color: 'red' }} {...props} />;
const TableComponent = ({ propDefinitions }) => {
const props = propDefinitions.map(
({ property, propType, required, description, defaultValue }) => {
return (
<tr key={property}>
<td>
{property}
{required ? <Red>*</Red> : null}
</td>
<td>{propType.name}</td>
<td>{defaultValue}</td>
<td>{description}</td>
</tr>
);
}
);
return (
<table>
<thead>
<tr>
<th>name</th>
<th>type</th>
<th>default</th>
<th>description</th>
</tr>
</thead>
<tbody>{props}</tbody>
</table>
);
};
export const defaultView = () => <Button />;
```
### React Docgen Integration
React Docgen is included as part of the @storybook/react package through the use of `babel-plugin-react-docgen` during babel compile time.
When rendering a story with a React component commented in this supported format, the Addon Info description will render the comments above the component declaration and the prop table will display the prop's comment in the description column.
```js
import React from 'react';
import PropTypes from 'prop-types';
/** Button component description */
const DocgenButton = ({ disabled, label, style, onClick }) => (
<button disabled={disabled} style={style} onClick={onClick}>
{label}
</button>
);
DocgenButton.defaultProps = {
disabled: false,
onClick: () => {},
style: {},
};
DocgenButton.propTypes = {
/** Boolean indicating whether the button should render as disabled */
disabled: PropTypes.bool,
/** button label. */
label: PropTypes.string.isRequired,
/** onClick handler */
onClick: PropTypes.func,
/** component styles */
style: PropTypes.shape,
};
export default DocgenButton;
```
Comments above flow types are also supported. Storybook Info Addon should now render all the correct types for your component if the PropTypes are in the same file as the React component.
## The FAQ
**Components lose their names on static build**
Component names also get minified with other javascript code when building for production.
When creating components, set the `displayName` static property to show the correct component name on static builds.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

View File

@ -1,56 +0,0 @@
{
"name": "@storybook/addon-info",
"version": "6.0.0-alpha.2",
"description": "A Storybook addon to show additional information for your stories.",
"keywords": [
"addon",
"storybook"
],
"homepage": "https://github.com/storybookjs/storybook/tree/master/addons/info",
"bugs": {
"url": "https://github.com/storybookjs/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybookjs/storybook.git",
"directory": "addons/info"
},
"license": "MIT",
"files": [
"dist/**/*",
"README.md",
"*.js",
"*.d.ts"
],
"main": "dist/index.js",
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/client-logger": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"core-js": "^3.0.1",
"global": "^4.3.2",
"marksy": "^8.0.0",
"nested-object-assign": "^1.0.3",
"prop-types": "^15.7.2",
"react": "^16.8.3",
"react-addons-create-fragment": "^15.6.2",
"react-element-to-jsx-string": "^14.0.2",
"react-is": "^16.8.3",
"react-lifecycles-compat": "^3.0.4",
"util-deprecate": "^1.0.2"
},
"devDependencies": {
"react-test-renderer": "^16.8.3"
},
"peerDependencies": {
"react": "*"
},
"publishConfig": {
"access": "public"
},
"gitHead": "4b9d901add9452525135caae98ae5f78dd8da9ff"
}

File diff suppressed because it is too large Load Diff

View File

@ -1,122 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import PropVal from './PropVal';
import PrettyPropType from './types/PrettyPropType';
const Table = props => <table {...props} />;
const Td = props => <td style={{ paddingRight: 10, verticalAlign: 'top' }} {...props} />;
const Tr = props => <tr {...props} />;
const Th = props => <th style={{ textAlign: 'left', verticalAlign: 'top' }} {...props} />;
const Tbody = props => <tbody {...props} />;
const Thead = props => <thead {...props} />;
export const multiLineText = input => {
if (!input) {
return input;
}
const text = String(input);
const arrayOfText = text.split(/\r?\n|\r/g);
const isSingleLine = arrayOfText.length < 2;
return isSingleLine
? text
: arrayOfText.map((lineOfText, i) => (
// eslint-disable-next-line react/no-array-index-key
<span key={`${lineOfText}.${i}`}>
{i > 0 && <br />} {lineOfText}
</span>
));
};
const determineIncludedPropTypes = (propDefinitions, excludedPropTypes) => {
if (excludedPropTypes.length === 0) {
return propDefinitions;
}
return propDefinitions.filter(
propDefinition => !excludedPropTypes.includes(propDefinition.property)
);
};
export default function PropTable(props) {
const {
type,
maxPropObjectKeys,
maxPropArrayLength,
maxPropStringLength,
propDefinitions,
excludedPropTypes,
} = props;
if (!type) {
return null;
}
const includedPropDefinitions = determineIncludedPropTypes(propDefinitions, excludedPropTypes);
if (!includedPropDefinitions.length) {
return <small>No propTypes defined!</small>;
}
const propValProps = {
maxPropObjectKeys,
maxPropArrayLength,
maxPropStringLength,
};
return (
<Table>
<Thead>
<Tr>
<Th>property</Th>
<Th>propType</Th>
<Th>required</Th>
<Th>default</Th>
<Th>description</Th>
</Tr>
</Thead>
<Tbody>
{includedPropDefinitions.map(row => (
<Tr key={row.property}>
<Td>{row.property}</Td>
<Td>
<PrettyPropType propType={row.propType} />
</Td>
<Td>{row.required ? 'yes' : '-'}</Td>
<Td>
{row.defaultValue === undefined ? (
'-'
) : (
<PropVal val={row.defaultValue} {...propValProps} valueStyles={{}} />
)}
</Td>
<Td>{multiLineText(row.description)}</Td>
</Tr>
))}
</Tbody>
</Table>
);
}
PropTable.displayName = 'PropTable';
PropTable.defaultProps = {
type: null,
propDefinitions: [],
excludedPropTypes: [],
};
PropTable.propTypes = {
type: PropTypes.func,
maxPropObjectKeys: PropTypes.number.isRequired,
maxPropArrayLength: PropTypes.number.isRequired,
maxPropStringLength: PropTypes.number.isRequired,
excludedPropTypes: PropTypes.arrayOf(PropTypes.string),
propDefinitions: PropTypes.arrayOf(
PropTypes.shape({
property: PropTypes.string.isRequired,
propType: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
required: PropTypes.bool,
description: PropTypes.string,
defaultValue: PropTypes.any,
})
),
};

View File

@ -1,71 +0,0 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { shallow } from 'enzyme';
import PropTable, { multiLineText } from './PropTable';
describe('PropTable', () => {
describe('multiLineText', () => {
const singleLine = 'Foo bar baz';
const unixMultiLineText = 'foo \n bar \n baz';
const windowsMultiLineText = 'foo \r bar \r baz';
const duplicatedMultiLine = 'foo\nfoo\nfoo';
const propDefinitions = [
{
defaultValue: undefined,
description: '',
propType: { name: 'string' },
property: 'foo',
required: false,
},
];
const FooComponent = () => <div />;
const propTableProps = {
type: FooComponent,
maxPropArrayLength: 5,
maxPropObjectKeys: 5,
maxPropStringLength: 5,
propDefinitions,
};
it('should include all propTypes by default', () => {
const wrapper = shallow(<PropTable {...propTableProps} />);
expect(wrapper).toMatchSnapshot();
});
it('should exclude excluded propTypes', () => {
const props = { ...propTableProps, excludedPropTypes: ['foo'] };
const wrapper = shallow(<PropTable {...props} />);
expect(wrapper).toMatchSnapshot();
});
it('should return a blank string for a null input', () => {
expect(multiLineText(null)).toBe(null);
});
it('should return a blank string for an undefined input', () => {
expect(multiLineText(undefined)).toBe(undefined);
});
it('should cast a number to a string', () => {
expect(multiLineText(1)).toBe('1');
});
it('should return its input for a single line of text', () => {
expect(multiLineText(singleLine)).toBe(singleLine);
});
it('should return an array for unix multiline text', () => {
expect(multiLineText(unixMultiLineText)).toHaveLength(3);
});
it('should return an array for windows multiline text', () => {
expect(multiLineText(windowsMultiLineText)).toHaveLength(3);
});
it('should return an array with unique keys for duplicated multiline text', () => {
const wrappers = multiLineText(duplicatedMultiLine).map(tag => shallow(tag));
const keys = wrappers.map(wrapper => wrapper.key());
const deDup = new Set(keys);
expect(keys).toHaveLength(deDup.size);
});
it('should have 2 br tags for 3 lines of text', () => {
const tree = renderer.create(multiLineText(unixMultiLineText)).toJSON();
expect(tree).toMatchSnapshot();
});
});
});

View File

@ -1,86 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable multiLineText should exclude excluded propTypes 1`] = `
<small>
No propTypes defined!
</small>
`;
exports[`PropTable multiLineText should have 2 br tags for 3 lines of text 1`] = `
Array [
<span>
foo
</span>,
<span>
<br />
bar
</span>,
<span>
<br />
baz
</span>,
]
`;
exports[`PropTable multiLineText should include all propTypes by default 1`] = `
<Table>
<Thead>
<Tr>
<Th>
property
</Th>
<Th>
propType
</Th>
<Th>
required
</Th>
<Th>
default
</Th>
<Th>
description
</Th>
</Tr>
</Thead>
<Tbody>
<Tr
key="foo"
>
<Td
isMonospace={true}
>
foo
</Td>
<Td
isMonospace={true}
>
<PrettyPropType
depth={1}
propType={
Object {
"name": "string",
}
}
/>
</Td>
<Td
isMonospace={false}
>
-
</Td>
<Td
isMonospace={false}
>
-
</Td>
<Td
isMonospace={false}
/>
</Tr>
</Tbody>
</Table>
`;

View File

@ -1,12 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import '../style.css';
const Table = ({ children }) => <table className="info-table">{children}</table>;
Table.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)])
.isRequired,
};
export default Table;

View File

@ -1,25 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import Table from './Table';
describe('PropTable/Table', () => {
it('renders a table html node with one child element', () => {
const wrapper = shallow(
<Table>
<div>foo bar</div>
</Table>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a table html node with multiple children elements', () => {
const wrapper = shallow(
<Table>
<div>foo bar</div>
<div>baz</div>
</Table>
);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -1,11 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
const Tbody = ({ children }) => <tbody>{children}</tbody>;
Tbody.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)])
.isRequired,
};
export default Tbody;

View File

@ -1,25 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import Tbody from './Tbody';
describe('PropTable/Tbody', () => {
it('renders a tbody html node with children', () => {
const wrapper = shallow(
<Tbody>
<div>foo bar</div>
</Tbody>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a tbody html node with multiple children elements', () => {
const wrapper = shallow(
<Tbody>
<div>foo bar</div>
<div>baz</div>
</Tbody>
);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -1,24 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import '../style.css';
const Td = ({ isMonospace, children }) => (
<td className={isMonospace ? 'info-table-monospace' : null}>{children}</td>
);
Td.propTypes = {
children: PropTypes.oneOfType([
PropTypes.node,
PropTypes.element,
PropTypes.arrayOf(PropTypes.node),
PropTypes.arrayOf(PropTypes.element),
]),
isMonospace: PropTypes.bool,
};
Td.defaultProps = {
isMonospace: false,
children: null,
};
export default Td;

View File

@ -1,54 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import Td from './Td';
describe('PropTable/Td', () => {
it('renders a td html node child element', () => {
const wrapper = shallow(
<Td>
<div>foo bar</div>
</Td>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a monospace td html node child element', () => {
const wrapper = shallow(
<Td isMonospace>
<div>foo bar</div>
</Td>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a td html node with multiple children elements', () => {
const wrapper = shallow(
<Td>
<div>foo bar</div>
<div>baz</div>
</Td>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a monospace td html node with multiple children elements', () => {
const wrapper = shallow(
<Td isMonospace>
<div>foo bar</div>
<div>baz</div>
</Td>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a td html node with one child node', () => {
const wrapper = shallow(<Td>foo bar</Td>);
expect(wrapper).toMatchSnapshot();
});
it('renders a monospace td html node with one child node', () => {
const wrapper = shallow(<Td isMonospace>foo bar</Td>);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -1,15 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
const Th = ({ children }) => <th>{children}</th>;
Th.propTypes = {
children: PropTypes.oneOfType([
PropTypes.node,
PropTypes.element,
PropTypes.arrayOf(PropTypes.node),
PropTypes.arrayOf(PropTypes.element),
]).isRequired,
};
export default Th;

View File

@ -1,26 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import Th from './Th';
describe('PropTable/Th', () => {
it('renders a th html node with react element children', () => {
const wrapper = shallow(
<Th>
<div>foo bar</div>
<div>baz</div>
</Th>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a th html node with html node children', () => {
const wrapper = shallow(<Th>foo bar baz</Th>);
expect(wrapper).toMatchSnapshot();
});
it('renders a th html node with one child node', () => {
const wrapper = shallow(<Th>foo bar</Th>);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -1,11 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
const Thead = ({ children }) => <thead>{children}</thead>;
Thead.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)])
.isRequired,
};
export default Thead;

View File

@ -1,25 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import Thead from './Thead';
describe('PropTable/Thead', () => {
it('renders a thead html node with children', () => {
const wrapper = shallow(
<Thead>
<div>foo bar</div>
</Thead>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a thead html node with multiple children elements', () => {
const wrapper = shallow(
<Thead>
<div>foo bar</div>
<div>baz</div>
</Thead>
);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -1,10 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
const Tr = ({ children }) => <tr>{children}</tr>;
Tr.propTypes = {
children: PropTypes.node.isRequired,
};
export default Tr;

View File

@ -1,27 +0,0 @@
import React from 'react';
import { shallow } from 'enzyme';
import Tr from './Tr';
import Td from './Td';
describe('PropTable/Tr', () => {
it('renders a tr html node with react element children', () => {
const wrapper = shallow(
<Tr>
<Td>foo bar</Td>
<Td>baz</Td>
</Tr>
);
expect(wrapper).toMatchSnapshot();
});
it('renders a tr html node with html node children', () => {
const wrapper = shallow(<Tr>foo bar baz</Tr>);
expect(wrapper).toMatchSnapshot();
});
it('renders a tr html node with one child node', () => {
const wrapper = shallow(<Tr>foo bar</Tr>);
expect(wrapper).toMatchSnapshot();
});
});

View File

@ -1,24 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Table renders a table html node with multiple children elements 1`] = `
<table
className="info-table"
>
<div>
foo bar
</div>
<div>
baz
</div>
</table>
`;
exports[`PropTable/Table renders a table html node with one child element 1`] = `
<table
className="info-table"
>
<div>
foo bar
</div>
</table>
`;

View File

@ -1,20 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Tbody renders a tbody html node with children 1`] = `
<tbody>
<div>
foo bar
</div>
</tbody>
`;
exports[`PropTable/Tbody renders a tbody html node with multiple children elements 1`] = `
<tbody>
<div>
foo bar
</div>
<div>
baz
</div>
</tbody>
`;

View File

@ -1,63 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Td renders a monospace td html node child element 1`] = `
<td
className="info-table-monospace"
>
<div>
foo bar
</div>
</td>
`;
exports[`PropTable/Td renders a monospace td html node with multiple children elements 1`] = `
<td
className="info-table-monospace"
>
<div>
foo bar
</div>
<div>
baz
</div>
</td>
`;
exports[`PropTable/Td renders a monospace td html node with one child node 1`] = `
<td
className="info-table-monospace"
>
foo bar
</td>
`;
exports[`PropTable/Td renders a td html node child element 1`] = `
<td
className={null}
>
<div>
foo bar
</div>
</td>
`;
exports[`PropTable/Td renders a td html node with multiple children elements 1`] = `
<td
className={null}
>
<div>
foo bar
</div>
<div>
baz
</div>
</td>
`;
exports[`PropTable/Td renders a td html node with one child node 1`] = `
<td
className={null}
>
foo bar
</td>
`;

View File

@ -1,24 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Th renders a th html node with html node children 1`] = `
<th>
foo bar baz
</th>
`;
exports[`PropTable/Th renders a th html node with one child node 1`] = `
<th>
foo bar
</th>
`;
exports[`PropTable/Th renders a th html node with react element children 1`] = `
<th>
<div>
foo bar
</div>
<div>
baz
</div>
</th>
`;

View File

@ -1,20 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Thead renders a thead html node with children 1`] = `
<thead>
<div>
foo bar
</div>
</thead>
`;
exports[`PropTable/Thead renders a thead html node with multiple children elements 1`] = `
<thead>
<div>
foo bar
</div>
<div>
baz
</div>
</thead>
`;

View File

@ -1,28 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable/Tr renders a tr html node with html node children 1`] = `
<tr>
foo bar baz
</tr>
`;
exports[`PropTable/Tr renders a tr html node with one child node 1`] = `
<tr>
foo bar
</tr>
`;
exports[`PropTable/Tr renders a tr html node with react element children 1`] = `
<tr>
<Td
isMonospace={false}
>
foo bar
</Td>
<Td
isMonospace={false}
>
baz
</Td>
</tr>
`;

View File

@ -1,121 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
import PrettyPropType from '../types/PrettyPropType';
import PropVal from '../PropVal';
import Table from './components/Table';
import Tbody from './components/Tbody';
import Td from './components/Td';
import Th from './components/Th';
import Thead from './components/Thead';
import Tr from './components/Tr';
export const multiLineText = input => {
if (!input) {
return input;
}
const text = String(input);
const arrayOfText = text.split(/\r?\n|\r/g);
const isSingleLine = arrayOfText.length < 2;
return isSingleLine
? text
: arrayOfText.map((lineOfText, i) => (
// eslint-disable-next-line react/no-array-index-key
<span key={`${lineOfText}.${i}`}>
{i > 0 && <br />} {lineOfText}
</span>
));
};
const determineIncludedPropTypes = (propDefinitions, excludedPropTypes) => {
if (excludedPropTypes.length === 0) {
return propDefinitions;
}
return propDefinitions.filter(
propDefinition => !excludedPropTypes.includes(propDefinition.property)
);
};
export default function PropTable(props) {
const {
type,
maxPropObjectKeys,
maxPropArrayLength,
maxPropStringLength,
propDefinitions,
excludedPropTypes,
} = props;
if (!type) {
return null;
}
const includedPropDefinitions = determineIncludedPropTypes(propDefinitions, excludedPropTypes);
if (!includedPropDefinitions.length) {
return <small>No propTypes defined!</small>;
}
const propValProps = {
maxPropObjectKeys,
maxPropArrayLength,
maxPropStringLength,
};
return (
<Table>
<Thead>
<Tr>
<Th>property</Th>
<Th>propType</Th>
<Th>required</Th>
<Th>default</Th>
<Th>description</Th>
</Tr>
</Thead>
<Tbody>
{includedPropDefinitions.map(row => (
<Tr key={row.property}>
<Td isMonospace>{row.property}</Td>
<Td isMonospace>
<PrettyPropType propType={row.propType} />
</Td>
<Td>{row.required ? 'yes' : '-'}</Td>
<Td>
{row.defaultValue === undefined ? (
'-'
) : (
<PropVal val={row.defaultValue} {...propValProps} valueStyles={{}} />
)}
</Td>
<Td>{multiLineText(row.description)}</Td>
</Tr>
))}
</Tbody>
</Table>
);
}
PropTable.displayName = 'PropTable';
PropTable.defaultProps = {
type: null,
propDefinitions: [],
excludedPropTypes: [],
};
PropTable.propTypes = {
type: PropTypes.func,
maxPropObjectKeys: PropTypes.number.isRequired,
maxPropArrayLength: PropTypes.number.isRequired,
maxPropStringLength: PropTypes.number.isRequired,
excludedPropTypes: PropTypes.arrayOf(PropTypes.string),
propDefinitions: PropTypes.arrayOf(
PropTypes.shape({
property: PropTypes.string.isRequired,
propType: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
required: PropTypes.bool,
description: PropTypes.string,
defaultValue: PropTypes.any,
})
),
};

View File

@ -1,78 +0,0 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { shallow } from 'enzyme';
import PropTable, { multiLineText } from './index';
describe('PropTable', () => {
describe('multiLineText', () => {
const singleLine = 'Foo bar baz';
const unixMultiLineText = 'foo \n bar \n baz';
const windowsMultiLineText = 'foo \r bar \r baz';
const duplicatedMultiLine = 'foo\nfoo\nfoo';
const propDefinitions = [
{
defaultValue: undefined,
description: '',
propType: { name: 'string' },
property: 'foo',
required: false,
},
];
const FooComponent = () => <div />;
const propTableProps = {
type: FooComponent,
maxPropArrayLength: 5,
maxPropObjectKeys: 5,
maxPropStringLength: 5,
propDefinitions,
};
it('should include all propTypes by default', () => {
const wrapper = shallow(<PropTable {...propTableProps} />);
expect(wrapper).toMatchSnapshot();
});
it('should exclude excluded propTypes', () => {
const props = { ...propTableProps, excludedPropTypes: ['foo'] };
const wrapper = shallow(<PropTable {...props} />);
expect(wrapper).toMatchSnapshot();
});
it('should return a blank string for a null input', () => {
expect(multiLineText(null)).toBe(null);
});
it('should return a blank string for an undefined input', () => {
expect(multiLineText(undefined)).toBe(undefined);
});
it('should cast a number to a string', () => {
expect(multiLineText(1)).toBe('1');
});
it('should return its input for a single line of text', () => {
expect(multiLineText(singleLine)).toBe(singleLine);
});
it('should return an array for unix multiline text', () => {
expect(multiLineText(unixMultiLineText)).toHaveLength(3);
});
it('should return an array for windows multiline text', () => {
expect(multiLineText(windowsMultiLineText)).toHaveLength(3);
});
it('should return an array with unique keys for duplicated multiline text', () => {
const wrappers = multiLineText(duplicatedMultiLine).map(tag => shallow(tag));
const keys = wrappers.map(wrapper => wrapper.key());
const deDup = new Set(keys);
expect(keys).toHaveLength(deDup.size);
});
it('should have 2 br tags for 3 lines of text', () => {
const tree = renderer.create(multiLineText(unixMultiLineText)).toJSON();
expect(tree).toMatchSnapshot();
});
});
});

View File

@ -1,19 +0,0 @@
.info-table {
width: 100%;
}
.info-table, .info-table td, .info-table th {
border-collapse: collapse;
border: 1px solid #cccccc;
color: #444444;
margin-top: 0.25rem;
padding-right: 0.5rem;
padding: 0.25rem;
text-align: left;
vertical-align: top;
}
.info-table-monospace {
font-family: Menlo, Monaco, "Courier New", monospace;
font-size: 0.88em;
}

View File

@ -1,274 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import createFragment from 'react-addons-create-fragment';
const getValueStyles = (codeColors = {}) => ({
func: {
color: codeColors.func || '#170',
},
attr: {
color: codeColors.attr || '#666',
},
object: {
color: codeColors.object || '#666',
},
array: {
color: codeColors.array || '#666',
},
number: {
color: codeColors.number || '#a11',
},
string: {
color: codeColors.string || '#22a',
wordBreak: 'break-word',
},
bool: {
color: codeColors.bool || '#a11',
},
empty: {
color: '#444',
},
});
function indent(breakIntoNewLines, level, isBlock) {
return (
breakIntoNewLines && (
<span>
<br />
{`${Array(level).join(' ')} `}
{!isBlock && ' '}
</span>
)
);
}
function PreviewArray({
val,
level,
maxPropArrayLength,
maxPropStringLength,
maxPropsIntoLine,
valueStyles,
}) {
const items = {};
const breakIntoNewLines = val.length > maxPropsIntoLine;
val.slice(0, maxPropArrayLength).forEach((item, i) => {
items[`n${i}`] = (
<span>
{indent(breakIntoNewLines, level)}
<PropVal
val={item}
level={level + 1}
valueStyles={valueStyles}
maxPropStringLength={maxPropStringLength}
maxPropsIntoLine={maxPropsIntoLine}
/>
</span>
);
items[`c${i}`] = ',';
});
if (val.length > maxPropArrayLength) {
items.last = <span>{indent(breakIntoNewLines, level)}</span>;
} else {
delete items[`c${val.length - 1}`];
}
return (
<span style={valueStyles.array}>
[{createFragment(items)}
{indent(breakIntoNewLines, level, true)}]
</span>
);
}
PreviewArray.propTypes = {
val: PropTypes.any, // eslint-disable-line
maxPropArrayLength: PropTypes.number.isRequired,
maxPropStringLength: PropTypes.number.isRequired,
maxPropsIntoLine: PropTypes.number.isRequired,
level: PropTypes.number.isRequired,
valueStyles: PropTypes.shape({
func: PropTypes.object,
attr: PropTypes.object,
object: PropTypes.object,
array: PropTypes.object,
number: PropTypes.object,
string: PropTypes.object,
bool: PropTypes.object,
empty: PropTypes.object,
}).isRequired,
};
function PreviewObject({
val,
level,
maxPropObjectKeys,
maxPropStringLength,
maxPropsIntoLine,
valueStyles,
}) {
const names = Object.keys(val);
const items = {};
const breakIntoNewLines = names.length > maxPropsIntoLine;
names.slice(0, maxPropObjectKeys).forEach((name, i) => {
items[`k${i}`] = (
<span>
{indent(breakIntoNewLines, level)}
<span style={valueStyles.attr}>{name}</span>
</span>
);
items[`c${i}`] = ': ';
items[`v${i}`] = (
<PropVal
val={val[name]}
level={level + 1}
valueStyles={valueStyles}
maxPropStringLength={maxPropStringLength}
maxPropsIntoLine={maxPropsIntoLine}
/>
);
items[`m${i}`] = ',';
});
if (names.length > maxPropObjectKeys) {
items.rest = <span>{indent(breakIntoNewLines, level)}</span>;
} else {
delete items[`m${names.length - 1}`];
}
return (
<span style={valueStyles.object}>
{'{'}
{createFragment(items)}
{indent(breakIntoNewLines, level, true)}
{'}'}
</span>
);
}
PreviewObject.propTypes = {
val: PropTypes.any, // eslint-disable-line
maxPropObjectKeys: PropTypes.number.isRequired,
maxPropStringLength: PropTypes.number.isRequired,
maxPropsIntoLine: PropTypes.number.isRequired,
level: PropTypes.number.isRequired,
valueStyles: PropTypes.shape({
func: PropTypes.object,
attr: PropTypes.object,
object: PropTypes.object,
array: PropTypes.object,
number: PropTypes.object,
string: PropTypes.object,
bool: PropTypes.object,
empty: PropTypes.object,
}).isRequired,
};
function PropVal(props) {
const {
level,
maxPropObjectKeys,
maxPropArrayLength,
maxPropStringLength,
maxPropsIntoLine,
theme,
} = props;
let { val } = props;
const { codeColors } = theme || {};
let content = null;
const valueStyles = props.valueStyles || getValueStyles(codeColors);
if (typeof val === 'number') {
content = <span style={valueStyles.number}>{val}</span>;
} else if (typeof val === 'string') {
if (val.length > maxPropStringLength) {
val = `${val.slice(0, maxPropStringLength)}`;
}
if (level > 1) {
val = `'${val}'`;
}
content = <span style={valueStyles.string}>{val}</span>;
} else if (typeof val === 'boolean') {
content = <span style={valueStyles.bool}>{`${val}`}</span>;
} else if (Array.isArray(val)) {
content = (
<PreviewArray
{...{
val,
level,
maxPropArrayLength,
maxPropStringLength,
maxPropsIntoLine,
valueStyles,
}}
/>
);
} else if (typeof val === 'function') {
content = <span style={valueStyles.func}>{val.name || 'anonymous'}</span>;
} else if (!val) {
content = <span style={valueStyles.empty}>{`${val}`}</span>;
} else if (typeof val !== 'object') {
content = <span></span>;
} else if (React.isValidElement(val)) {
content = (
<span style={valueStyles.object}>
{`<${val.type.displayName || val.type.name || val.type} />`}
</span>
);
} else {
content = (
<PreviewObject
{...{
val,
level,
maxPropObjectKeys,
maxPropStringLength,
maxPropsIntoLine,
valueStyles,
}}
/>
);
}
return content;
}
PropVal.defaultProps = {
val: null,
maxPropObjectKeys: 3,
maxPropArrayLength: 3,
maxPropStringLength: 50,
maxPropsIntoLine: 3,
level: 1,
theme: {},
valueStyles: null,
};
PropVal.propTypes = {
val: PropTypes.any, // eslint-disable-line
maxPropObjectKeys: PropTypes.number,
maxPropArrayLength: PropTypes.number,
maxPropStringLength: PropTypes.number,
maxPropsIntoLine: PropTypes.number,
level: PropTypes.number,
theme: PropTypes.shape({
codeColors: PropTypes.object,
}),
valueStyles: PropTypes.shape({
func: PropTypes.object,
attr: PropTypes.object,
object: PropTypes.object,
array: PropTypes.object,
number: PropTypes.object,
string: PropTypes.object,
bool: PropTypes.object,
empty: PropTypes.object,
}),
};
export default PropVal;

View File

@ -1,89 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import PropVal from './PropVal';
import { getType } from '../react-utils';
const stylesheet = {
propStyle: {},
propNameStyle: {},
propValueStyle: {},
};
export default function Props(props) {
const {
maxPropsIntoLine,
maxPropArrayLength,
maxPropObjectKeys,
maxPropStringLength,
node,
singleLine,
} = props;
const nodeProps = node.props;
const { defaultProps } = getType(node.type);
if (!nodeProps || typeof nodeProps !== 'object') {
return <span />;
}
const { propValueStyle, propNameStyle } = stylesheet;
const names = Object.keys(nodeProps).filter(
name =>
name[0] !== '_' &&
name !== 'children' &&
(!defaultProps || nodeProps[name] !== defaultProps[name])
);
const breakIntoNewLines = names.length > maxPropsIntoLine;
const endingSpace = singleLine ? ' ' : '';
const items = [];
names.forEach((name, i) => {
items.push(
<span key={name}>
{breakIntoNewLines ? (
<span>
<br />
&nbsp;&nbsp;
</span>
) : (
' '
)}
<span style={propNameStyle}>{name}</span>
{/* Use implicit true: */}
{(!nodeProps[name] || typeof nodeProps[name] !== 'boolean') && (
<span>
=
<span style={propValueStyle}>
{typeof nodeProps[name] === 'string' ? '"' : '{'}
<PropVal
val={nodeProps[name]}
maxPropObjectKeys={maxPropObjectKeys}
maxPropArrayLength={maxPropArrayLength}
maxPropStringLength={maxPropStringLength}
maxPropsIntoLine={maxPropsIntoLine}
/>
{typeof nodeProps[name] === 'string' ? '"' : '}'}
</span>
</span>
)}
{i === names.length - 1 && (breakIntoNewLines ? <br /> : endingSpace)}
</span>
);
});
return <span>{items}</span>;
}
Props.defaultProps = {
singleLine: false,
};
Props.propTypes = {
node: PropTypes.node.isRequired,
singleLine: PropTypes.bool,
maxPropsIntoLine: PropTypes.number.isRequired,
maxPropObjectKeys: PropTypes.number.isRequired,
maxPropArrayLength: PropTypes.number.isRequired,
maxPropStringLength: PropTypes.number.isRequired,
};

View File

@ -1,442 +0,0 @@
/* eslint no-underscore-dangle: 0 */
import React, { Fragment, Component, createElement } from 'react';
import { isForwardRef } from 'react-is';
import { polyfill } from 'react-lifecycles-compat';
import PropTypes from 'prop-types';
import global from 'global';
import marksy from 'marksy';
import jsxToString from 'react-element-to-jsx-string';
import { Code } from './markdown';
import { getDisplayName, getType } from '../react-utils';
global.STORYBOOK_REACT_CLASSES = global.STORYBOOK_REACT_CLASSES || [];
const { STORYBOOK_REACT_CLASSES } = global;
const stylesheetBase = {
button: {
base: {
fontFamily: 'sans-serif',
fontSize: 12,
display: 'block',
position: 'fixed',
border: 'none',
background: '#027ac5',
color: '#fff',
padding: '5px 15px',
cursor: 'pointer',
},
topRight: {
top: 0,
right: 0,
borderRadius: '0 0 0 5px',
},
},
info: {
position: 'fixed',
background: 'white',
top: 0,
left: 0,
height: '100vh',
width: '100vw',
overflow: 'auto',
zIndex: 99999,
},
children: {
position: 'relative',
zIndex: 0,
},
infoBody: {
fontFamily: 'Helvetica Neue, Helvetica, Segoe UI, Arial, freesans, sans-serif',
color: 'black',
fontWeight: 300,
lineHeight: 1.45,
fontSize: '15px',
padding: '20px 40px 40px',
borderRadius: '2px',
backgroundColor: '#fff',
},
infoContent: {
marginBottom: 0,
},
infoStory: {},
jsxInfoContent: {
borderTop: '1px solid #eee',
margin: '20px 0 0 0',
},
header: {
h1: {
margin: 0,
padding: 0,
fontSize: '35px',
},
h2: {
margin: '0 0 10px 0',
padding: 0,
fontWeight: 400,
fontSize: '22px',
},
h3: {
margin: '0 0 10px 0',
padding: 0,
fontWeight: 400,
fontSize: '18px',
},
body: {
borderBottom: '1px solid #eee',
paddingTop: 10,
marginBottom: 10,
},
},
source: {
h1: {
margin: '20px 0 0 0',
padding: '0 0 5px 0',
fontSize: '25px',
borderBottom: '1px solid #EEE',
},
},
propTableHead: {
margin: '20px 0 0 0',
},
};
class Story extends Component {
constructor(props, ...args) {
super(props, ...args);
this.state = {
open: false,
};
this.marksy = marksy({
createElement,
elements: props.components,
});
}
_renderStory() {
const { stylesheet } = this.state;
const { children } = this.props;
return (
<div id="story-root" style={stylesheet.infoStory}>
{children}
</div>
);
}
_renderInline() {
const { stylesheet } = this.state;
return (
<Fragment>
{this._renderInlineHeader()}
{this._renderStory()}
<div style={stylesheet.infoPage}>
<div style={stylesheet.infoBody}>
{this._getInfoContent()}
{this._getComponentDescription()}
{this._getSourceCode()}
{this._getPropTables()}
</div>
</div>
</Fragment>
);
}
_renderInlineHeader() {
const { stylesheet } = this.state;
const infoHeader = this._getInfoHeader();
return (
infoHeader && (
<div style={stylesheet.infoPage}>
<div style={stylesheet.infoBody}>{infoHeader}</div>
</div>
)
);
}
_renderOverlay() {
const { stylesheet, open } = this.state;
const { children } = this.props;
const buttonStyle = {
...stylesheet.button.base,
...stylesheet.button.topRight,
};
const infoStyle = { ...stylesheet.info };
if (!open) {
infoStyle.display = 'none';
}
const openOverlay = () => {
this.setState({ open: true });
return false;
};
const closeOverlay = () => {
this.setState({ open: false });
return false;
};
return (
<Fragment>
<div style={stylesheet.children}>{children}</div>
<button
type="button"
style={buttonStyle}
onClick={openOverlay}
className="info__show-button"
>
Show Info
</button>
{open ? (
<div style={infoStyle} className="info__overlay">
<button
type="button"
style={buttonStyle}
onClick={closeOverlay}
className="info__close-button"
>
×
</button>
<div style={stylesheet.infoPage}>
<div style={stylesheet.infoBody}>
{this._getInfoHeader()}
{this._getInfoContent()}
{this._getComponentDescription()}
{this._getSourceCode()}
{this._getPropTables()}
</div>
</div>
</div>
) : null}
</Fragment>
);
}
_getInfoHeader() {
const { stylesheet } = this.state;
const { context, showHeader } = this.props;
if (!context || !showHeader) {
return null;
}
return (
<div style={stylesheet.header.body}>
<h1 style={stylesheet.header.h1}>{context.kind}</h1>
<h2 style={stylesheet.header.h2}>{context.name}</h2>
</div>
);
}
_getInfoContent() {
const { info, showInline } = this.props;
const { stylesheet } = this.state;
if (!info) {
return '';
}
if (React.isValidElement(info)) {
return (
<div style={showInline ? stylesheet.jsxInfoContent : stylesheet.infoContent}>{info}</div>
);
}
const lines = info.split('\n');
while (lines[0].trim() === '') {
lines.shift();
}
let padding = 0;
const matches = lines[0].match(/^ */);
if (matches) {
padding = matches[0].length;
}
const source = lines.map(s => s.slice(padding)).join('\n');
return <Fragment>{this.marksy(source).tree}</Fragment>;
}
_getComponentDescription() {
const {
context: { kind, name },
} = this.props;
let retDiv = null;
const validMatches = [kind, name];
if (Object.keys(STORYBOOK_REACT_CLASSES).length) {
Object.keys(STORYBOOK_REACT_CLASSES).forEach(key => {
if (validMatches.includes(STORYBOOK_REACT_CLASSES[key].name)) {
const componentDescription = STORYBOOK_REACT_CLASSES[key].docgenInfo.description;
retDiv = <Fragment>{this.marksy(componentDescription).tree}</Fragment>;
}
});
}
return retDiv;
}
_getSourceCode() {
const { showSource, children } = this.props;
const { stylesheet } = this.state;
if (!showSource) {
return null;
}
return (
<Fragment>
<h1 style={stylesheet.source.h1}>Story Source</h1>
<Code code={jsxToString(children)} language="jsx" format={false} />
</Fragment>
);
}
_getPropTables() {
const {
children,
propTablesExclude,
propTableCompare,
maxPropObjectKeys,
maxPropArrayLength,
maxPropStringLength,
excludedPropTypes,
} = this.props;
let { propTables } = this.props;
const { stylesheet } = this.state;
const types = new Map();
if (!propTables) {
return null;
}
if (!children) {
return null;
}
propTables.forEach(type => {
types.set(type, true);
});
// depth-first traverse and collect types
const extract = innerChildren => {
if (!innerChildren) {
return;
}
if (Array.isArray(innerChildren)) {
innerChildren.forEach(extract);
return;
}
if (innerChildren.props && innerChildren.props.children) {
extract(innerChildren.props.children);
}
if (isForwardRef(innerChildren)) {
try {
// this might fail because of hooks being used
extract(innerChildren.type.render(innerChildren.props));
} catch (e) {
// do nothing
}
}
if (
typeof innerChildren === 'string' ||
typeof innerChildren.type === 'string' ||
(propTables.length > 0 && // if propTables is set and has items in it
!propTables.includes(innerChildren.type)) || // ignore types that are missing from propTables
(Array.isArray(propTablesExclude) && // also ignore excluded types
propTablesExclude.some(Comp => propTableCompare(innerChildren, Comp)))
) {
return;
}
if (innerChildren.type && !types.has(innerChildren.type)) {
types.set(innerChildren.type, true);
}
};
// extract components from children
extract(children);
const array = Array.from(types.keys());
array.sort((a, b) => (getDisplayName(a) > getDisplayName(b) ? 1 : -1));
propTables = array.map((type, i) => (
// eslint-disable-next-line react/no-array-index-key
<div key={`${getDisplayName(type)}_${i}`}>
<h3 style={stylesheet.propTableHead}>"{getDisplayName(type)}" Component</h3>
<this.props.PropTable
type={getType(type)}
maxPropObjectKeys={maxPropObjectKeys}
maxPropArrayLength={maxPropArrayLength}
maxPropStringLength={maxPropStringLength}
excludedPropTypes={excludedPropTypes}
/>
</div>
));
if (!propTables || propTables.length === 0) {
return null;
}
return (
<Fragment>
<h1 style={stylesheet.source.h1}>Prop Types</h1>
{propTables}
</Fragment>
);
}
render() {
const { showInline } = this.props;
return showInline ? this._renderInline() : this._renderOverlay();
}
}
Story.getDerivedStateFromProps = ({ styles }) => ({ stylesheet: styles(stylesheetBase) });
Story.displayName = 'Story';
Story.propTypes = {
context: PropTypes.shape({
kind: PropTypes.string,
name: PropTypes.string,
}),
info: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
propTables: PropTypes.arrayOf(PropTypes.func),
propTablesExclude: PropTypes.arrayOf(PropTypes.func),
propTableCompare: PropTypes.func.isRequired,
showInline: PropTypes.bool,
showHeader: PropTypes.bool,
showSource: PropTypes.bool,
// eslint-disable-next-line react/no-unused-prop-types
styles: PropTypes.func.isRequired,
children: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
components: PropTypes.shape({}),
maxPropObjectKeys: PropTypes.number.isRequired,
maxPropArrayLength: PropTypes.number.isRequired,
maxPropStringLength: PropTypes.number.isRequired,
excludedPropTypes: PropTypes.arrayOf(PropTypes.string),
};
Story.defaultProps = {
context: null,
info: '',
children: null,
propTables: null,
propTablesExclude: [],
showInline: false,
showHeader: true,
showSource: true,
components: {},
excludedPropTypes: [],
};
polyfill(Story);
export default Story;

View File

@ -1,76 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PropTable multiLineText should exclude excluded propTypes 1`] = `
<small>
No propTypes defined!
</small>
`;
exports[`PropTable multiLineText should have 2 br tags for 3 lines of text 1`] = `
Array [
<span>
foo
</span>,
<span>
<br />
bar
</span>,
<span>
<br />
baz
</span>,
]
`;
exports[`PropTable multiLineText should include all propTypes by default 1`] = `
<Table>
<Thead>
<Tr>
<Th>
property
</Th>
<Th>
propType
</Th>
<Th>
required
</Th>
<Th>
default
</Th>
<Th>
description
</Th>
</Tr>
</Thead>
<Tbody>
<Tr
key="foo"
>
<Td>
foo
</Td>
<Td>
<PrettyPropType
depth={1}
propType={
Object {
"name": "string",
}
}
/>
</Td>
<Td>
-
</Td>
<Td>
-
</Td>
<Td />
</Tr>
</Tbody>
</Table>
`;

View File

@ -1,98 +0,0 @@
/* eslint-disable no-underscore-dangle,react/forbid-foreign-prop-types */
import PropTypes from 'prop-types';
import React from 'react';
const PropTypesMap = new Map();
Object.keys(PropTypes).forEach(typeName => {
const type = PropTypes[typeName];
PropTypesMap.set(type, typeName);
PropTypesMap.set(type.isRequired, typeName);
});
const isNotEmpty = obj => obj && obj.props && Object.keys(obj.props).length > 0;
const hasDocgen = type => isNotEmpty(type.__docgenInfo);
const propsFromDocgen = type => {
const props = {};
const docgenInfoProps = type.__docgenInfo.props;
Object.keys(docgenInfoProps).forEach(property => {
const docgenInfoProp = docgenInfoProps[property];
const defaultValueDesc = docgenInfoProp.defaultValue || {};
const propType = docgenInfoProp.flowType || docgenInfoProp.type || 'other';
props[property] = {
property,
propType,
required: docgenInfoProp.required,
description: docgenInfoProp.description,
defaultValue: defaultValueDesc.value,
};
});
return props;
};
const propsFromPropTypes = type => {
const props = {};
if (type.propTypes) {
Object.keys(type.propTypes).forEach(property => {
const typeInfo = type.propTypes[property];
const required = typeInfo.isRequired === undefined;
const docgenInfo =
type.__docgenInfo && type.__docgenInfo.props && type.__docgenInfo.props[property];
const description = docgenInfo ? docgenInfo.description : null;
let propType = PropTypesMap.get(typeInfo) || 'other';
if (propType === 'other') {
if (docgenInfo && docgenInfo.type) {
propType = docgenInfo.type.name;
}
}
props[property] = { property, propType, required, description };
});
}
if (type.defaultProps) {
Object.keys(type.defaultProps).forEach(property => {
const value = type.defaultProps[property];
if (value === undefined) {
return;
}
if (!props[property]) {
props[property] = { property };
}
props[property].defaultValue = value;
});
}
return props;
};
export default function makeTableComponent(Component) {
const TableComponent = props => {
const { type } = props;
if (!type) {
return null;
}
const propDefinitionsMap = hasDocgen(type) ? propsFromDocgen(type) : propsFromPropTypes(type);
const propDefinitions = Object.values(propDefinitionsMap);
return <Component propDefinitions={propDefinitions} {...props} />;
};
TableComponent.propTypes = {
type: PropTypes.func.isRequired,
};
return TableComponent;
}

View File

@ -1,33 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { SyntaxHighlighter } from '@storybook/components';
import { ThemeProvider, convert } from '@storybook/theming';
const Code = ({ code, language = 'plaintext', ...rest }) => (
<ThemeProvider theme={convert()}>
<SyntaxHighlighter bordered copyable format={false} language={language} {...rest}>
{code}
</SyntaxHighlighter>
</ThemeProvider>
);
Code.propTypes = {
language: PropTypes.string.isRequired,
code: PropTypes.string.isRequired,
};
export { Code };
export function Blockquote({ children }) {
const style = {
fontSize: '1.88em',
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
borderLeft: '8px solid #fafafa',
padding: '1rem',
};
return <blockquote style={style}>{children}</blockquote>;
}
Blockquote.propTypes = { children: PropTypes.node };
Blockquote.defaultProps = { children: null };
export { default as Pre } from './pre/pre';

View File

@ -1,115 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
const defaultProps = {
children: null,
id: null,
};
const propTypes = {
children: PropTypes.node,
id: PropTypes.string,
};
export function H1({ id, children }) {
const styles = {
borderBottom: '1px solid #eee',
fontWeight: 600,
margin: 0,
padding: 0,
fontSize: '40px',
};
return (
<h1 id={id} style={styles}>
{children}
</h1>
);
}
H1.defaultProps = defaultProps;
H1.propTypes = propTypes;
export function H2({ id, children }) {
const styles = {
fontWeight: 600,
margin: 0,
padding: 0,
fontSize: '30px',
};
return (
<h2 id={id} style={styles}>
{children}
</h2>
);
}
H2.defaultProps = defaultProps;
H2.propTypes = propTypes;
export function H3({ id, children }) {
const styles = {
fontWeight: 600,
margin: 0,
padding: 0,
fontSize: '22px',
textTransform: 'uppercase',
};
return (
<h3 id={id} style={styles}>
{children}
</h3>
);
}
H3.defaultProps = defaultProps;
H3.propTypes = propTypes;
export function H4({ id, children }) {
const styles = {
fontWeight: 600,
margin: 0,
padding: 0,
fontSize: '20px',
};
return (
<h4 id={id} style={styles}>
{children}
</h4>
);
}
H4.defaultProps = defaultProps;
H4.propTypes = propTypes;
export function H5({ id, children }) {
const styles = {
fontWeight: 600,
margin: 0,
padding: 0,
fontSize: '18px',
};
return (
<h5 id={id} style={styles}>
{children}
</h5>
);
}
H5.defaultProps = defaultProps;
H5.propTypes = propTypes;
export function H6({ id, children }) {
const styles = {
fontWeight: 400,
margin: 0,
padding: 0,
fontSize: '18px',
};
return (
<h6 id={id} style={styles}>
{children}
</h6>
);
}
H6.defaultProps = defaultProps;
H6.propTypes = propTypes;

View File

@ -1,3 +0,0 @@
export { H1, H2, H3, H4, H5, H6 } from './htags';
export { Code, Pre } from './code';
export { P, A, LI, UL } from './text';

View File

@ -1,13 +0,0 @@
/* eslint-disable no-undef */
export default function copy(str) {
const tmp = document.createElement('TEXTAREA');
const focus = document.activeElement;
tmp.value = str;
document.body.appendChild(tmp);
tmp.select();
document.execCommand('copy');
document.body.removeChild(tmp);
focus.focus();
}

View File

@ -1,42 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
function CopyButton({ onClick, toggled }) {
const toggleText = 'Copied!';
const text = 'Copy';
return (
<button
type="button"
onClick={onClick}
style={{
backgroundColor: 'rgb(255, 255, 255)',
cursor: 'pointer',
fontSize: '13px',
alignSelf: 'flex-start',
flexShrink: '0',
overflow: 'hidden',
borderWidth: 1,
borderStyle: 'solid',
borderColor: 'rgb(238, 238, 238)',
borderImage: 'initial',
borderRadius: 3,
padding: '3px 10px',
}}
>
{toggled ? toggleText : text}
</button>
);
}
CopyButton.propTypes = {
onClick: PropTypes.func,
toggled: PropTypes.bool,
};
CopyButton.defaultProps = {
onClick: () => {},
toggled: false,
};
export default CopyButton;

View File

@ -1,76 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import CopyButton from './copyButton';
import copy from './copy';
const TOGGLE_TIMEOUT = 1800;
class Pre extends React.Component {
state = {
copied: false,
};
setRef = elem => {
this.pre = elem;
};
handleClick = () => {
const text = this.pre && this.pre.innerText;
if (!text) {
return;
}
copy(text);
this.setState({ copied: true });
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.setState({ copied: false });
}, TOGGLE_TIMEOUT);
};
render() {
const { theme, children } = this.props;
const { pre } = theme;
const { copied } = this.state;
return (
<pre
style={{
...{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
fontSize: '.88em',
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
backgroundColor: '#fafafa',
padding: '.5rem',
lineHeight: 1.5,
overflowX: 'scroll',
},
...pre,
}}
>
<div ref={this.setRef}>{children}</div>
<CopyButton onClick={this.handleClick} toggled={copied} />
</pre>
);
}
}
Pre.propTypes = {
children: PropTypes.node,
theme: PropTypes.shape({
pre: PropTypes.object,
}),
};
Pre.defaultProps = {
children: null,
theme: {},
};
export default Pre;

View File

@ -1,40 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
const defaultProps = { children: null };
const propTypes = { children: PropTypes.node };
export function P({ children }) {
return <p>{children}</p>;
}
P.defaultProps = defaultProps;
P.propTypes = propTypes;
export function LI({ children }) {
return <li>{children}</li>;
}
LI.defaultProps = defaultProps;
LI.propTypes = propTypes;
export function UL({ children }) {
return <ul>{children}</ul>;
}
UL.defaultProps = defaultProps;
UL.propTypes = propTypes;
export function A({ href, children }) {
const style = {
color: '#3498db',
};
return (
<a href={href} target="_blank" rel="noopener noreferrer" style={style}>
{children}
</a>
);
}
A.defaultProps = defaultProps;
A.propTypes = { children: PropTypes.node, href: PropTypes.string.isRequired };

View File

@ -1,21 +0,0 @@
/* eslint-disable import/no-cycle */
import React from 'react';
import PrettyPropType from './PrettyPropType';
import { TypeInfo, getPropTypes } from './proptypes';
const ArrayOf = ({ propType }) => (
<span>
<span>[</span>
<span>
<PrettyPropType propType={getPropTypes(propType)} />
</span>
<span>]</span>
</span>
);
ArrayOf.propTypes = {
propType: TypeInfo.isRequired,
};
export default ArrayOf;

View File

@ -1,14 +0,0 @@
import React from 'react';
import { TypeInfo, getPropTypes } from './proptypes';
const Enum = ({ propType }) => (
<span>
{getPropTypes(propType)
.map(({ value }) => value)
.join(' | ')}
</span>
);
Enum.propTypes = {
propType: TypeInfo.isRequired,
};

View File

@ -1,10 +0,0 @@
import React from 'react';
import { TypeInfo, getPropTypes } from './proptypes';
const InstanceOf = ({ propType }) => <span>{getPropTypes(propType)}</span>;
InstanceOf.propTypes = {
propType: TypeInfo.isRequired,
};
export default InstanceOf;

View File

@ -1,10 +0,0 @@
import React from 'react';
import { TypeInfo } from './proptypes';
const Literal = ({ propType }) => <span>{propType.value}</span>;
Literal.propTypes = {
propType: TypeInfo.isRequired,
};
export default Literal;

View File

@ -1,19 +0,0 @@
/* eslint-disable import/no-cycle */
import React from 'react';
import PrettyPropType from './PrettyPropType';
import { TypeInfo, getPropTypes } from './proptypes';
const ObjectOf = ({ propType }) => (
<span>
{'{[<key>]: '}
<PrettyPropType propType={getPropTypes(propType)} />
{'}'}
</span>
);
ObjectOf.propTypes = {
propType: TypeInfo.isRequired,
};
export default ObjectOf;

View File

@ -1,15 +0,0 @@
import React from 'react';
import { TypeInfo, getPropTypes } from './proptypes';
const joinValues = propTypes => propTypes.map(({ value }) => value).join(' | ');
const OneOf = ({ propType }) => {
const propTypes = getPropTypes(propType);
return <span>{`oneOf ${Array.isArray(propTypes) ? joinValues(propTypes) : propTypes}`}</span>;
};
OneOf.propTypes = {
propType: TypeInfo.isRequired,
};
export default OneOf;

View File

@ -1,26 +0,0 @@
/* eslint-disable import/no-cycle */
import React from 'react';
import PrettyPropType from './PrettyPropType';
import { TypeInfo, getPropTypes } from './proptypes';
const OneOfType = ({ propType }) => {
const propTypes = getPropTypes(propType);
return (
<span>
{propTypes
.map((value, i) => {
const key = `${value.name}${value.value ? `-${value.value}` : ''}`;
return [
<PrettyPropType key={key} propType={value} />,
i < propTypes.length - 1 ? <span key={`${key}-separator`}> | </span> : null,
];
})
.reduce((acc, tuple) => acc.concat(tuple), [])}
</span>
);
};
OneOfType.propTypes = {
propType: TypeInfo.isRequired,
};
export default OneOfType;

View File

@ -1,56 +0,0 @@
/* eslint-disable import/no-cycle */
import PropTypes from 'prop-types';
import React from 'react';
import Shape from './Shape';
import OneOfType from './OneOfType';
import ArrayOf from './ArrayOf';
import ObjectOf from './ObjectOf';
import OneOf from './OneOf';
import InstanceOf from './InstanceOf';
import Signature from './Signature';
import Literal from './Literal';
import { TypeInfo } from './proptypes';
// propType -> Component map - these are a bit more complex prop types to display
const propTypeComponentMap = new Map([
['shape', Shape],
['union', OneOfType],
['arrayOf', ArrayOf],
['objectOf', ObjectOf],
// Might be overkill to have below proptypes as separate components *shrug*
['literal', Literal],
['enum', OneOf],
['instanceOf', InstanceOf],
['signature', Signature],
]);
const PrettyPropType = props => {
const { propType, depth } = props;
if (!propType) {
return <span>unknown</span>;
}
if (propTypeComponentMap.has(propType.name)) {
const Component = propTypeComponentMap.get(propType.name);
return <Component propType={propType} depth={depth} />;
}
// Otherwise, propType does not have a dedicated component, display proptype name by default
return <span>{propType.name || propType}</span>;
};
PrettyPropType.displayName = 'PrettyPropType';
PrettyPropType.defaultProps = {
propType: null,
depth: 1,
};
PrettyPropType.propTypes = {
propType: TypeInfo,
depth: PropTypes.number,
};
export default PrettyPropType;

View File

@ -1,31 +0,0 @@
import PropTypes from 'prop-types';
import React from 'react';
const styles = {
hasProperty: {
whiteSpace: 'nowrap',
},
};
const PropertyLabel = ({ property, required }) => {
if (!property) return null;
return (
<span style={styles.hasProperty}>
{property}
{required ? '' : '?'}:&nbsp;
</span>
);
};
PropertyLabel.propTypes = {
property: PropTypes.string,
required: PropTypes.bool,
};
PropertyLabel.defaultProps = {
property: '',
required: false,
};
export default PropertyLabel;

View File

@ -1,76 +0,0 @@
/* eslint-disable import/no-cycle */
import PropTypes from 'prop-types';
import React from 'react';
import PrettyPropType from './PrettyPropType';
import PropertyLabel from './PropertyLabel';
import { TypeInfo, getPropTypes } from './proptypes';
const MARGIN_SIZE = 15;
const HighlightButton = props => (
<button
type="button"
{...props}
style={{
display: 'inline-block',
background: 'none',
border: '0 none',
color: 'gray',
cursor: 'pointer',
}}
/>
);
class Shape extends React.Component {
constructor(props) {
super(props);
this.state = {
minimized: false,
};
}
handleToggle = () => {
const { minimized } = this.state;
this.setState({
minimized: !minimized,
});
};
render() {
const { propType, depth } = this.props;
const { minimized } = this.state;
const propTypes = getPropTypes(propType);
return (
<span>
<HighlightButton onClick={this.handleToggle}>{'{'}</HighlightButton>
<HighlightButton onClick={this.handleToggle}>...</HighlightButton>
{!minimized &&
Object.keys(propTypes).map(childProperty => (
<div key={childProperty} style={{ marginLeft: depth * MARGIN_SIZE }}>
<PropertyLabel
property={childProperty}
required={propTypes[childProperty].required}
/>
<PrettyPropType depth={depth + 1} propType={propTypes[childProperty]} />,
</div>
))}
<HighlightButton onClick={this.handleToggle}>{'}'}</HighlightButton>
</span>
);
}
}
Shape.propTypes = {
propType: TypeInfo,
depth: PropTypes.number.isRequired,
};
Shape.defaultProps = {
propType: null,
};
export default Shape;

View File

@ -1,10 +0,0 @@
import React from 'react';
import { TypeInfo } from './proptypes';
const Signature = ({ propType }) => <span>{propType.raw}</span>;
Signature.propTypes = {
propType: TypeInfo.isRequired,
};
export default Signature;

View File

@ -1,12 +0,0 @@
import PropTypes, { oneOfType } from 'prop-types';
export const TypeInfo = oneOfType([
PropTypes.shape({
name: PropTypes.string,
value: PropTypes.any,
}),
PropTypes.string,
]);
export const getPropTypes = propType =>
typeof propType === 'string' ? propType : propType.value || propType.elements;

View File

@ -1,114 +0,0 @@
import React from 'react';
import nestedObjectAssign from 'nested-object-assign';
import deprecate from 'util-deprecate';
import { makeDecorator } from '@storybook/addons';
import { logger } from '@storybook/client-logger';
import Story from './components/Story';
import PropTable from './components/PropTable/index';
import makeTableComponent from './components/makeTableComponent';
import { H1, H2, H3, H4, H5, H6, Code, P, UL, A, LI } from './components/markdown';
const defaultOptions = {
inline: false,
header: true,
source: true,
propTables: [],
propTableCompare: (element, Component) =>
// https://github.com/gaearon/react-hot-loader#checking-element-types
typeof reactHotLoaderGlobal === 'undefined'
? element.type === Component
: // eslint-disable-next-line no-undef
reactHotLoaderGlobal.areComponentsEqual(element.type, Component),
TableComponent: PropTable,
maxPropsIntoLine: 3,
maxPropObjectKeys: 3,
maxPropArrayLength: 3,
maxPropStringLength: 50,
};
const defaultComponents = {
h1: H1,
h2: H2,
h3: H3,
h4: H4,
h5: H5,
h6: H6,
code: Code,
p: P,
a: A,
li: LI,
ul: UL,
};
let hasWarned = false;
function addInfo(storyFn, context, infoOptions) {
const options = {
...defaultOptions,
...infoOptions,
};
// props.propTables can only be either an array of components or null
// propTables option is allowed to be set to 'false' (a boolean)
// if the option is false, replace it with null to avoid react warnings
if (!options.propTables) {
options.propTables = null;
}
const components = { ...defaultComponents };
if (options && options.components) {
Object.assign(components, options.components);
}
if (options && options.marksyConf) {
if (!hasWarned) {
logger.warn('@storybook/addon-info: "marksyConf" option has been renamed to "components"');
hasWarned = true;
}
Object.assign(components, options.marksyConf);
}
const props = {
info: options.text,
context,
showInline: Boolean(options.inline),
showHeader: Boolean(options.header),
showSource: Boolean(options.source),
styles:
typeof options.styles === 'function'
? options.styles
: s => nestedObjectAssign({}, s, options.styles),
propTables: options.propTables,
propTablesExclude: options.propTablesExclude,
propTableCompare: options.propTableCompare,
PropTable: makeTableComponent(options.TableComponent),
components,
maxPropObjectKeys: options.maxPropObjectKeys,
maxPropArrayLength: options.maxPropArrayLength,
maxPropsIntoLine: options.maxPropsIntoLine,
maxPropStringLength: options.maxPropStringLength,
excludedPropTypes: options.excludedPropTypes,
};
return <Story {...props}>{storyFn(context)}</Story>;
}
export const withInfo = makeDecorator({
name: 'withInfo',
parameterName: 'info',
allowDeprecatedUsage: true,
wrapper: (getStory, context, { options, parameters }) => {
const storyOptions = parameters || options;
const infoOptions = typeof storyOptions === 'string' ? { text: storyOptions } : storyOptions;
const mergedOptions =
typeof infoOptions === 'string' ? infoOptions : { ...options, ...infoOptions };
return addInfo(getStory, context, mergedOptions);
},
});
export { Story };
export function setDefaults(newDefaults) {
return deprecate(
() => Object.assign(defaultOptions, newDefaults),
'setDefaults is deprecated. Instead, you can pass options into withInfo(options) directly, or use the info parameter.'
)();
}

View File

@ -1,120 +0,0 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
/* eslint-disable react/prop-types */
import React from 'react';
import { mount } from 'enzyme';
import { withInfo, setDefaults } from '.';
import externalMdDocs from '../README.md';
const TestComponent = ({ func, obj, array, number, string, bool, empty }) => (
<div>
<h1>{String(func)}</h1>
<h2>{String(obj)}</h2>
<h3>{String(array)}</h3>
<h4>{String(number)}</h4>
<h5>{String(string)}</h5>
<h6>{String(bool)}</h6>
<p>{String(empty)}</p>
<a href="#">test</a>
<code>storiesOf</code>
<ul>
<li>1</li>
<li>2</li>
</ul>
</div>
);
const reactClassPath = 'some/path/TestComponent.jsx';
const storybookReactClassMock = {
name: 'TestComponent',
path: reactClassPath,
docgenInfo: {
description: `
# Awesome test component description
## with markdown support
**bold** *cursive*
\`\`\`js
a;
\`\`\``,
name: 'TestComponent',
},
};
const testOptions = { propTables: false };
const testMarkdown = `# Test story
## with markdown info
containing **bold**, *cursive* text, \`code\` and [a link](https://github.com)`;
describe('addon Info', () => {
const createStoryFn = Component => ({ name }) => (
<div>
It's a {name} story:
<Component
func={x => x + 1}
obj={{ a: 'a', b: 'b' }}
array={[1, 2, 3]}
number={7}
string="seven"
bool
/>
</div>
);
const storyFn = createStoryFn(TestComponent);
it('should render <Info /> and markdown', () => {
const Info = withInfo(testMarkdown)(storyFn);
expect(mount(<Info />)).toMatchSnapshot();
});
it('should render <Info /> and external markdown', () => {
const Info = withInfo(externalMdDocs)(storyFn);
expect(mount(<Info />)).toMatchSnapshot();
});
it('should render with text options', () => {
const Info = withInfo({ text: 'some text here' })(storyFn);
mount(<Info />);
});
it('should render with missed info', () => {
setDefaults(testOptions);
const Info = withInfo()(storyFn);
mount(<Info />);
});
it('should render <Info /> for memoized component', () => {
const MemoizedTestComponent = React.memo(TestComponent);
const Info = withInfo()(createStoryFn(MemoizedTestComponent));
expect(mount(<Info />)).toMatchSnapshot();
});
it('should render component description if story kind matches component', () => {
const previousReactClassesValue = global.STORYBOOK_REACT_CLASSES[reactClassPath];
Object.assign(global.STORYBOOK_REACT_CLASSES, { [reactClassPath]: storybookReactClassMock });
const Info = () =>
withInfo({ inline: true, propTables: false })(storyFn, {
kind: 'TestComponent',
name: 'Basic test',
});
expect(mount(<Info />)).toMatchSnapshot();
Object.assign(global.STORYBOOK_REACT_CLASSES, { [reactClassPath]: previousReactClassesValue });
});
it('should render component description if story name matches component', () => {
const previousReactClassesValue = global.STORYBOOK_REACT_CLASSES[reactClassPath];
Object.assign(global.STORYBOOK_REACT_CLASSES, { [reactClassPath]: storybookReactClassMock });
const Info = () =>
withInfo({ inline: true, propTables: false })(storyFn, {
kind: 'Test Components',
name: 'TestComponent',
});
expect(mount(<Info />)).toMatchSnapshot();
Object.assign(global.STORYBOOK_REACT_CLASSES, { [reactClassPath]: previousReactClassesValue });
});
});

View File

@ -1,14 +0,0 @@
import { isMemo } from 'react-is';
export function getType(typeOrMemo) {
return isMemo(typeOrMemo) ? typeOrMemo.type : typeOrMemo;
}
export function getDisplayName(typeOrMemo) {
if (typeof typeOrMemo === 'string') {
return typeOrMemo;
}
const type = getType(typeOrMemo);
return type.displayName || type.name || 'Unknown';
}

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-jest",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "React storybook addon that show component jest report",
"keywords": [
"addon",
@ -34,11 +34,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Storybook Addon Prop Editor Component",
"keywords": [
"addon",
@ -28,12 +28,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/client-api": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/client-api": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Story Links addon for storybook",
"keywords": [
"addon",
@ -28,11 +28,11 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/client-logger": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/client-logger": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/csf": "0.0.1",
"@storybook/router": "6.0.0-alpha.2",
"@storybook/router": "6.0.0-alpha.3",
"@types/qs": "^6.9.0",
"core-js": "^3.0.1",
"global": "^4.3.2",

View File

@ -1,106 +0,0 @@
# Storybook Addon Notes
Storybook Addon Notes allows you to write notes (text or HTML) for your stories in [Storybook](https://storybook.js.org).
[Framework Support](https://github.com/storybookjs/storybook/blob/master/ADDONS_SUPPORT.md)
![Storybook Addon Notes Demo](docs/demo.png)
## Getting Started
**NOTE: Documentation on `next` branch is for alpha version, stable release is on [master](https://github.com/storybookjs/storybook/tree/master/addons/)**
```sh
yarn add -D @storybook/addon-notes
```
within `.storybook/main.js`:
```js
module.exports = {
addons: ['@storybook/addon-notes/register']
}
```
Alternatively register the notes addon into a panel. Choose only one, not both.
```js
module.exports = {
addons: ['@storybook/addon-notes/register-panel']
}
```
Now, you can use the `notes` parameter to add a note to each story.
```js
import Component from './Component';
export default {
title: 'Component',
parameters: {
notes: 'some documentation here',
},
};
```
### Upgrading to CSF Format
Add `notes` to the `parameters` object:
```js
export default {
parameters: {
notes: 'My notes',
}
}
```
## Using Markdown
Using Markdown in your notes is supported, Storybook will load Markdown as raw by default.
```js
import Component from './Component';
import markdown from './someMarkdownText.md';
export default {
title: 'Component',
};
export const withMarkdown = () => <Component />;
withmarkdown.story = {
parameters: {
notes: { markdown },
}
};
```
## Giphy
When using Markdown, you can also embed gifs from Giphy into your Markdown. Currently, the value `cheese` of the query prop is used to search and return the first result returned by Giphy.
```md
# Title
<Giphy query='cheese' />
```
## Multiple Notes Sections
If you need to display different notes for different consumers of your storybook (e.g design, developers), you can configure multiple notes pages. The following will render a tab with unique notes for both `Introduction` and `Design`.
```js
import { storiesOf } from '@storybook/react';
import Component from './Component';
import intro from './intro.md';
import design from './design.md';
export default {
title: 'Component',
parameters: {
notes: { Introduction: intro, 'Design Notes': design },
},
};
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

View File

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

View File

@ -1 +0,0 @@
require('./dist/register.js').default('panel');

View File

@ -1 +0,0 @@
require('./dist/register.js').default('tab');

View File

@ -1,45 +0,0 @@
import React from 'react';
import { shallow, mount } from 'enzyme';
import { Link } from '@reach/router';
import { SyntaxHighlighter as SyntaxHighlighterBase } from '@storybook/components';
import { SyntaxHighlighter, NotesLink } from './Panel';
describe('NotesPanel', () => {
describe('SyntaxHighlighter component', () => {
it('should return code if className is undefined', () => {
const wrapper = shallow(<SyntaxHighlighter>some text</SyntaxHighlighter>);
const code = wrapper.find('code');
expect(code.exists()).toBeTruthy();
expect(code.text()).toBe('some text');
});
it('should return SyntaxHighlighterBase if there is a className prop', () => {
const wrapper = shallow(
<SyntaxHighlighter className="lang-jsx">some text</SyntaxHighlighter>
);
const syntaxHighlighterBase = wrapper.find(SyntaxHighlighterBase);
expect(syntaxHighlighterBase.exists()).toBeTruthy();
expect(syntaxHighlighterBase.prop('language')).toBe('jsx');
});
});
describe('NotesLink component', () => {
it('should render storybook links with @storybook/router Link', () => {
const component = mount(
<NotesLink href="/story/addon-notes" title="title">
Storybook Link
</NotesLink>
);
expect(component.find(Link).prop('to')).toBe('/?path=/story/addon-notes');
expect(component.find(Link).prop('title')).toBe('title');
});
it('should render absolute links as <a>', () => {
const component = mount(
<NotesLink href="https://example.com" title="title">
Storybook Link
</NotesLink>
);
expect(component.find('a').prop('href')).toBe('https://example.com');
expect(component.find('a').prop('title')).toBe('title');
});
});
});

View File

@ -1,218 +0,0 @@
import React, { ReactElement, Fragment, ReactNode } from 'react';
import { types } from '@storybook/addons';
import { API, Consumer, Combo } from '@storybook/api';
import { Link as RouterLink } from '@storybook/router';
import { styled } from '@storybook/theming';
import {
SyntaxHighlighter as SyntaxHighlighterBase,
Placeholder,
DocumentWrapper,
Link,
TabWrapper,
TabsState,
} from '@storybook/components';
import Markdown from 'markdown-to-jsx';
import Giphy from './giphy';
import { formatter } from './formatter';
import { PARAM_KEY, Parameters } from './shared';
const Panel = styled.div<{}>(({ theme }) => ({
padding: '3rem 40px',
boxSizing: 'border-box',
width: '100%',
maxWidth: 980,
margin: '0 auto',
...(theme.addonNotesTheme || {}),
}));
interface Props {
active: boolean;
api: API;
}
function read(param: Parameters | undefined): Record<string, string> | string | undefined {
if (!param) {
return undefined;
}
if (typeof param === 'string') {
return param;
}
if ('disable' in param) {
return undefined;
}
if ('text' in param) {
return param.text;
}
if ('markdown' in param) {
return param.markdown;
}
if (typeof param === 'object') {
return param;
}
return undefined;
}
interface SyntaxHighlighterProps {
className?: string;
children: ReactElement;
[key: string]: any;
}
export const SyntaxHighlighter = ({ className, children, ...props }: SyntaxHighlighterProps) => {
// markdown-to-jsx does not add className to inline code
if (typeof className !== 'string') {
return <code>{children}</code>;
}
// className: "lang-jsx"
const language = className.split('-');
return (
<SyntaxHighlighterBase
language={language[1] || 'plaintext'}
bordered
format={false}
copyable
{...props}
>
{children}
</SyntaxHighlighterBase>
);
};
interface NotesLinkProps {
href: string;
children: ReactElement;
}
export const NotesLink = ({ href, children, ...props }: NotesLinkProps) => {
/* https://github.com/sindresorhus/is-absolute-url/blob/master/index.js */
const isAbsoluteUrl = /^[a-z][a-z0-9+.-]*:/.test(href);
const isAnchorUrl = /^#.*/.test(href);
if (isAbsoluteUrl || isAnchorUrl) {
return (
<a href={href} {...props}>
{children}
</a>
);
}
return (
<RouterLink to={href} {...props}>
{children}
</RouterLink>
);
};
// use our SyntaxHighlighter component in place of a <code> element when
// converting markdown to react elements
const defaultOptions = {
overrides: {
code: SyntaxHighlighter,
a: NotesLink,
Giphy: {
component: Giphy,
},
},
};
interface Overrides {
overrides: {
[type: string]: ReactNode;
};
}
type Options = typeof defaultOptions & Overrides;
const mapper = ({
state,
api,
}: Combo): { value?: string | Record<string, string>; options: Options } => {
const extraElements = Object.entries(api.getElements(types.NOTES_ELEMENT)).reduce(
(acc, [k, v]) => ({ ...acc, [k]: v.render }),
{}
);
const options = {
...defaultOptions,
overrides: { ...defaultOptions.overrides, ...extraElements },
};
const story = state.storiesHash[state.storyId];
const value = read(story ? api.getParameters(story.id, PARAM_KEY) : undefined);
return { options, value };
};
const NotesPanel = ({ active }: Props) => {
if (!active) {
return null;
}
return (
<Consumer filter={mapper}>
{({ options, value }: { options: Options; value?: string | Record<string, string> }) => {
if (!value) {
return (
<Placeholder>
<Fragment>No notes yet</Fragment>
<Fragment>
Learn how to&nbsp;
<Link
href="https://github.com/storybookjs/storybook/tree/master/addons/notes"
target="_blank"
withArrow
secondary
cancel={false}
>
document components in Markdown
</Link>
</Fragment>
</Placeholder>
);
}
if (typeof value === 'string' || Object.keys(value).length === 1) {
const md = typeof value === 'object' ? Object.values(value)[0] : value;
return (
<Panel className="addon-notes-container">
<DocumentWrapper>
<Markdown options={options}>{formatter(md)}</Markdown>
</DocumentWrapper>
</Panel>
);
}
const groups: { title: string; render: (props: { active: boolean }) => void }[] = [];
Object.entries(value).forEach(([title, docs]) => {
groups.push({
title,
render: ({ active: isActive }) => (
<TabWrapper key={title} active={isActive}>
<Panel>
<DocumentWrapper>
<Markdown options={options}>{formatter(docs)}</Markdown>
</DocumentWrapper>
</Panel>
</TabWrapper>
),
});
});
return (
<div className="addon-notes-container">
<TabsState>
{groups.map(group => (
<div id={group.title} key={group.title} title={group.title}>
{group.render}
</div>
))}
</TabsState>
</div>
);
}}
</Consumer>
);
};
export default NotesPanel;

View File

@ -1,26 +0,0 @@
import memoize from 'memoizerific';
export const formatter = memoize(2)((code: string) => {
// code provided to the component is often coming from template literals, which preserve whitespace.
// sometimes the first line doesn't have padding, but the second does.
// we split the code-string into lines, then if we find padding on line 0 or 1,
// we assume that padding is bad, and remove that much padding on all following lines
return code
.split(/\n/)
.reduce(
(acc, i, index) => {
const match = i.match(/^((:?\s|\t)+)/);
const padding = match ? match[1] : '';
if (acc.firstIndent === '' && padding && index < 3) {
return { result: `${acc.result}\n${i.replace(padding, '')}`, firstIndent: padding };
}
return {
result: `${acc.result}\n${i.replace(acc.firstIndent, '').replace(/\s*$/, '')}`,
firstIndent: acc.firstIndent,
};
},
{ firstIndent: '', result: '' }
)
.result.trim();
});

View File

@ -1,37 +0,0 @@
import { fetch } from 'global';
import React, { Component } from 'react';
import { logger } from '@storybook/client-logger';
interface Props {
query: string;
}
interface State {
src: string | null;
}
export default class Giphy extends Component<Props, State> {
state: State = {
src: null,
};
componentDidMount() {
const { query } = this.props;
// TODO: replace this api_key, and make it configurable
// note: I have requested a production api_key:
// it's pending: bluXZc8ZAre19mvTtVi900CdsJhbVTEK
fetch(`http://api.giphy.com/v1/gifs/search?limit=1&api_key=dc6zaTOxFJmzC&q=${query}`)
.then((response: { ok: any; json: () => void }) => response.ok && response.json())
.then((data: { data: { images: { original: { url: string } } }[] }) => {
this.setState({
src: data.data[0].images.original.url,
});
})
.catch((e: any) => logger.error(e));
}
render() {
const { src } = this.state;
// TODO: we should have a nice looking <Img /> component
return src ? <img src={src} alt="" /> : null;
}
}

View File

@ -1,40 +0,0 @@
import { makeDecorator, StoryContext, StoryGetter, WrapperSettings } from '@storybook/addons';
import deprecate from 'util-deprecate';
// todo resolve any after @storybook/addons and @storybook/channels are migrated to TypeScript
export const withNotes = makeDecorator({
name: 'withNotes',
parameterName: 'notes',
skipIfNoParametersOrOptions: true,
allowDeprecatedUsage: true,
wrapper: deprecate(
(getStory: StoryGetter, context: StoryContext, { options, parameters }: WrapperSettings) => {
const storyOptions = parameters || options;
const { text, markdown } =
typeof storyOptions === 'string'
? {
text: storyOptions,
markdown: undefined,
}
: storyOptions;
if (!text && !markdown) {
throw new Error(
`Parameter 'notes' must must be a string or an object with 'text' or 'markdown' properties`
);
}
return getStory(context);
},
'withNotes is deprecated'
),
});
export const withMarkdownNotes = deprecate((text: string, options: any) => {},
'withMarkdownNotes is deprecated');
if (module && module.hot && module.hot.decline) {
module.hot.decline();
}

View File

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

View File

@ -1,20 +0,0 @@
import * as React from 'react';
import addons, { types } from '@storybook/addons';
import { ADDON_ID, PANEL_ID, PARAM_KEY } from './shared';
// TODO: fix eslint in tslint (igor said he fixed it, should ask him)
import Panel from './Panel';
export default function register(type: types) {
addons.register(ADDON_ID, api => {
addons.add(PANEL_ID, {
type,
title: 'Notes',
route: ({ storyId }) => `/info/${storyId}`, // todo add type
match: ({ viewMode }) => viewMode === 'info', // todo add type
render: ({ active, key }) => <Panel api={api} active={active} key={key} />,
paramKey: PARAM_KEY,
});
});
}

View File

@ -1,21 +0,0 @@
export const ADDON_ID = 'storybookjs/notes';
export const PANEL_ID = `${ADDON_ID}/panel`;
export const PARAM_KEY = `notes`;
interface TextParameter {
text: string;
}
interface MarkdownParameter {
markdown: string;
}
interface DisabledParameter {
disable: boolean;
}
type TabsParameter = Record<string, string>;
export type Parameters =
| string
| TextParameter
| MarkdownParameter
| DisabledParameter
| TabsParameter;

View File

@ -1,7 +0,0 @@
// There are no types for markdown-to-jsx
declare module 'markdown-to-jsx' {
const Markdown: any;
export default Markdown;
}
declare module 'global';

View File

@ -1,13 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "./src",
"types": ["webpack-env"]
},
"include": [
"src/**/*"
],
"exclude": [
"src/__tests__/**/*"
]
}

View File

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

View File

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

View File

@ -381,7 +381,7 @@ NOTICE that When using the `asyncJest: true` option, you also must specify a `te
This is a really powerful technique to write stories of Relay components because it integrates data fetching with component rendering. So instead of passing data props manually, we can let Relay do the job for us as it does in our application.
Whenever you change you're data requirements by adding (and rendering) or (accidentally) deleting fields in your graphql query fragments, you'll get a different snapshot and thus an error in the StoryShot test.
Whenever you change your data requirements by adding (and rendering) or (accidentally) deleting fields in your graphql query fragments, you'll get a different snapshot and thus an error in the StoryShot test.
## Options
@ -677,3 +677,22 @@ initStoryshots({
### `asyncJest`
Enables Jest `done()` callback in the StoryShots tests for async testing. See [StoryShots for async rendered components](#storyshots-for-async-rendered-components) for more info.
## Story Parameters
### `disable`
Some stories are difficult or impossible to snapshot, such as those covering components that use external DOM-modifying libraries, and those that deliberately throw errors. It is possible to skip stories like these by giving them a parameter of `storyshots: {disable: true}`. There is also a shorthand for this, `storyshots: false`.
```js
export const Exception = () => {
throw new Error('storyFn threw an error! WHOOPS');
};
Exception.story = {
name: 'story throws exception',
parameters: {
storyshots: { disable: true },
},
};
```

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-storyshots",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "StoryShots is a Jest Snapshot Testing Addon for Storybook.",
"keywords": [
"addon",
@ -32,9 +32,9 @@
},
"dependencies": {
"@jest/transform": "^25.1.0",
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/client-api": "6.0.0-alpha.2",
"@storybook/core": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/client-api": "6.0.0-alpha.3",
"@storybook/core": "6.0.0-alpha.3",
"@types/glob": "^7.1.1",
"@types/jest": "^25.1.1",
"@types/jest-specific-snapshot": "^0.5.3",
@ -49,8 +49,8 @@
"ts-dedent": "^1.1.1"
},
"devDependencies": {
"@storybook/addon-docs": "6.0.0-alpha.2",
"@storybook/react": "6.0.0-alpha.2",
"@storybook/addon-docs": "6.0.0-alpha.3",
"@storybook/react": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Image snapshots addition to StoryShots based on puppeteer",
"keywords": [
"addon",
@ -29,7 +29,7 @@
},
"dependencies": {
"@storybook/csf": "0.0.1",
"@storybook/node-logger": "6.0.0-alpha.2",
"@storybook/node-logger": "6.0.0-alpha.3",
"@types/jest-image-snapshot": "^2.8.0",
"@wordpress/jest-puppeteer-axe": "^1.5.0",
"core-js": "^3.0.1",
@ -41,7 +41,7 @@
"@types/puppeteer": "^2.0.0"
},
"peerDependencies": {
"@storybook/addon-storyshots": "6.0.0-alpha.2",
"@storybook/addon-storyshots": "6.0.0-alpha.3",
"puppeteer": "^1.12.2 || ^2.0.0"
},
"peerDependenciesMeta": {

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/addon-storysource",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Stories addon for storybook",
"keywords": [
"addon",
@ -28,13 +28,13 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/client-logger": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/router": "6.0.0-alpha.2",
"@storybook/source-loader": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/client-logger": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/router": "6.0.0-alpha.3",
"@storybook/source-loader": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Storybook addon to change the viewport size to mobile",
"keywords": [
"addon",
@ -28,12 +28,12 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/api": "6.0.0-alpha.2",
"@storybook/client-logger": "6.0.0-alpha.2",
"@storybook/components": "6.0.0-alpha.2",
"@storybook/core-events": "6.0.0-alpha.2",
"@storybook/theming": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/api": "6.0.0-alpha.3",
"@storybook/client-logger": "6.0.0-alpha.3",
"@storybook/components": "6.0.0-alpha.3",
"@storybook/core-events": "6.0.0-alpha.3",
"@storybook/theming": "6.0.0-alpha.3",
"core-js": "^3.0.1",
"global": "^4.3.2",
"memoizerific": "^1.11.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/angular",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.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": "6.0.0-alpha.2",
"@storybook/core": "6.0.0-alpha.2",
"@storybook/node-logger": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/core": "6.0.0-alpha.3",
"@storybook/node-logger": "6.0.0-alpha.3",
"@types/webpack-env": "^1.15.1",
"core-js": "^3.0.1",
"fork-ts-checker-webpack-plugin": "^4.0.3",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/ember",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.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.7.0",
"@storybook/core": "6.0.0-alpha.2",
"@storybook/core": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.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": "6.0.0-alpha.2",
"@storybook/core": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/core": "6.0.0-alpha.3",
"@types/webpack-env": "^1.15.1",
"core-js": "^3.0.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/marionette",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Storybook for Marionette: Develop Marionette.js component in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -25,7 +25,7 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/core": "6.0.0-alpha.2",
"@storybook/core": "6.0.0-alpha.3",
"common-tags": "^1.8.0",
"core-js": "^3.0.1",
"global": "^4.3.2",

View File

@ -1,6 +1,6 @@
{
"name": "@storybook/marko",
"version": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Storybook for Marko: Develop Marko Component in isolation with Hot Reloading.",
"keywords": [
"storybook"
@ -33,8 +33,8 @@
},
"dependencies": {
"@marko/webpack": "^2.1.0",
"@storybook/client-logger": "6.0.0-alpha.2",
"@storybook/core": "6.0.0-alpha.2",
"@storybook/client-logger": "6.0.0-alpha.3",
"@storybook/core": "6.0.0-alpha.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": "6.0.0-alpha.2",
"version": "6.0.0-alpha.3",
"description": "Storybook for Mithril: Develop Mithril Component in isolation.",
"keywords": [
"storybook"
@ -35,8 +35,8 @@
"dependencies": {
"@babel/core": "^7.8.4",
"@babel/plugin-transform-react-jsx": "^7.3.0",
"@storybook/addons": "6.0.0-alpha.2",
"@storybook/core": "6.0.0-alpha.2",
"@storybook/addons": "6.0.0-alpha.3",
"@storybook/core": "6.0.0-alpha.3",
"@types/mithril": "^2.0.0",
"@types/webpack-env": "^1.15.1",
"core-js": "^3.0.1",

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