mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-20 05:02:37 +08:00
Merge branch 'release/3.2' into update-global-hook
This commit is contained in:
commit
45ada3515e
@ -53,6 +53,7 @@ module.exports = {
|
||||
],
|
||||
'import/prefer-default-export': ignore,
|
||||
'react/jsx-wrap-multilines': ignore,
|
||||
'react/jsx-indent': ignore,
|
||||
'react/jsx-indent-props': ignore,
|
||||
'react/jsx-closing-bracket-location': ignore,
|
||||
'react/jsx-uses-react': error,
|
||||
|
19
CHANGELOG.md
19
CHANGELOG.md
@ -1,3 +1,22 @@
|
||||
# 3.1.8
|
||||
|
||||
2017-July-06
|
||||
|
||||
#### Documentation
|
||||
|
||||
- Updated addon knob readme. [#1406](https://github.com/storybooks/storybook/pull/1406)
|
||||
- Add a FAQ entry for shared config with next [#1390](https://github.com/storybooks/storybook/pull/1390)
|
||||
- Documented webpack customization example for typescript [#1386](https://github.com/storybooks/storybook/pull/1386)
|
||||
|
||||
#### Maintenance
|
||||
|
||||
- Removed empty array, since webpack 2 doesn't support them anymore. [#1381](https://github.com/storybooks/storybook/pull/1381)
|
||||
|
||||
#### Dependency Upgrades
|
||||
|
||||
- Support webpack 3.0.0 [#1410](https://github.com/storybooks/storybook/pull/1410)
|
||||
- Update react inspector to fix #1385 [#1408](https://github.com/storybooks/storybook/pull/1408)
|
||||
|
||||
# 3.1.7
|
||||
|
||||
2017-June-28
|
||||
|
@ -124,5 +124,5 @@ If you **are** using these addons, migrating is simple:
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { link } from '@storybook/addon-links';
|
||||
import { linkTo } from '@storybook/addon-links';
|
||||
```
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-actions",
|
||||
"version": "3.1.6",
|
||||
"version": "3.2.0-alpha.7",
|
||||
"description": "Action Logger addon for storybook",
|
||||
"keywords": [
|
||||
"storybook"
|
||||
|
@ -9,7 +9,11 @@ class ActionLogger extends Component {
|
||||
}
|
||||
|
||||
renderAction(action) {
|
||||
const counter = <div style={style.counter}>{action.count}</div>;
|
||||
const counter = (
|
||||
<div style={style.counter}>
|
||||
{action.count}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div key={action.id} style={style.action}>
|
||||
<div style={style.countwrap}>
|
||||
@ -29,8 +33,12 @@ class ActionLogger extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
<pre style={style.actions}>{this.getActionData()}</pre>
|
||||
<button style={style.button} onClick={this.props.onClear}>CLEAR</button>
|
||||
<pre style={style.actions}>
|
||||
{this.getActionData()}
|
||||
</pre>
|
||||
<button style={style.button} onClick={this.props.onClear}>
|
||||
CLEAR
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export function action(name) {
|
||||
// the same.
|
||||
//
|
||||
// Ref: https://bocoup.com/weblog/whats-in-a-function-name
|
||||
const fnName = name ? name.replace(/\W+/g, '_') : 'action';
|
||||
const fnName = name && typeof name === 'string' ? name.replace(/\W+/g, '_') : 'action';
|
||||
// eslint-disable-next-line no-eval
|
||||
const named = eval(`(function ${fnName}() { return handler.apply(this, arguments) })`);
|
||||
return named;
|
||||
|
@ -9,8 +9,19 @@ const style = {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
overflow: 'auto',
|
||||
};
|
||||
|
||||
const innerStyle = {
|
||||
margin: 'auto',
|
||||
};
|
||||
|
||||
export default function(storyFn) {
|
||||
return <div style={style}>{storyFn()}</div>;
|
||||
return (
|
||||
<div style={style}>
|
||||
<div style={innerStyle}>
|
||||
{storyFn()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -59,10 +59,16 @@ export default class CommentItem extends Component {
|
||||
</div>
|
||||
<div className="comment-content" style={style.commentContent}>
|
||||
<div style={style.commentHead}>
|
||||
<span style={style.commentUser}>{comment.user.name}</span>
|
||||
<span style={style.commentTime}>{time}</span>
|
||||
<span style={style.commentUser}>
|
||||
{comment.user.name}
|
||||
</span>
|
||||
<span style={style.commentTime}>
|
||||
{time}
|
||||
</span>
|
||||
</div>
|
||||
<span style={style.commentText}>{body}</span>
|
||||
<span style={style.commentText}>
|
||||
{body}
|
||||
</span>
|
||||
{showDelete ? this.renderDelete() : null}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,7 +18,9 @@ export default function CommentsPanel(props) {
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
<div style={style.message}>
|
||||
<a style={style.button} href={appsUrl}>Create an app for this repo on Storybook Hub</a>
|
||||
<a style={style.button} href={appsUrl}>
|
||||
Create an app for this repo on Storybook Hub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -136,7 +136,9 @@ export default class Item extends Component {
|
||||
|
||||
return (
|
||||
<div style={styles.item}>
|
||||
<label htmlFor={`addon-event-${name}`} style={styles.label}>{title}</label>
|
||||
<label htmlFor={`addon-event-${name}`} style={styles.label}>
|
||||
{title}
|
||||
</label>
|
||||
<button
|
||||
style={styles.button}
|
||||
onClick={this.onEmitClick}
|
||||
|
@ -19,7 +19,8 @@
|
||||
"global": "^4.3.2",
|
||||
"marksy": "^2.0.0",
|
||||
"prop-types": "^15.5.8",
|
||||
"react-addons-create-fragment": "^15.5.3"
|
||||
"react-addons-create-fragment": "^15.5.3",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"git-url-parse": "^6.2.2",
|
||||
|
@ -66,7 +66,9 @@ export default function Node(props) {
|
||||
if (!name) {
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<span style={tagStyle}>{text}</span>
|
||||
<span style={tagStyle}>
|
||||
{text}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -75,7 +77,9 @@ export default function Node(props) {
|
||||
if (!children) {
|
||||
return (
|
||||
<div style={containerStyle}>
|
||||
<span style={tagStyle}><{name}</span>
|
||||
<span style={tagStyle}>
|
||||
<{name}
|
||||
</span>
|
||||
<Props
|
||||
node={node}
|
||||
singleLine
|
||||
@ -96,7 +100,9 @@ export default function Node(props) {
|
||||
return (
|
||||
<div>
|
||||
<div style={containerStyleCopy}>
|
||||
<span style={tagStyle}><{name}</span>
|
||||
<span style={tagStyle}>
|
||||
<{name}
|
||||
</span>
|
||||
<Props
|
||||
node={node}
|
||||
maxPropsIntoLine={maxPropsIntoLine}
|
||||
@ -117,7 +123,9 @@ export default function Node(props) {
|
||||
/>
|
||||
)}
|
||||
<div style={containerStyleCopy}>
|
||||
<span style={tagStyle}></{name}></span>
|
||||
<span style={tagStyle}>
|
||||
</{name}>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -34,11 +34,10 @@ export default function PropTable(props) {
|
||||
Object.keys(type.propTypes).forEach(property => {
|
||||
const typeInfo = type.propTypes[property];
|
||||
const required = typeInfo.isRequired === undefined ? 'yes' : 'no';
|
||||
const description = type.__docgenInfo &&
|
||||
type.__docgenInfo.props &&
|
||||
type.__docgenInfo.props[property]
|
||||
? type.__docgenInfo.props[property].description
|
||||
: null;
|
||||
const description =
|
||||
type.__docgenInfo && type.__docgenInfo.props && type.__docgenInfo.props[property]
|
||||
? type.__docgenInfo.props[property].description
|
||||
: null;
|
||||
let propType = PropTypesMap.get(typeInfo) || 'other';
|
||||
|
||||
if (propType === 'other') {
|
||||
@ -100,15 +99,23 @@ export default function PropTable(props) {
|
||||
<tbody>
|
||||
{array.map(row =>
|
||||
<tr key={row.property}>
|
||||
<td>{row.property}</td>
|
||||
<td>{row.propType}</td>
|
||||
<td>{row.required}</td>
|
||||
<td>
|
||||
{row.property}
|
||||
</td>
|
||||
<td>
|
||||
{row.propType}
|
||||
</td>
|
||||
<td>
|
||||
{row.required}
|
||||
</td>
|
||||
<td>
|
||||
{row.defaultValue === undefined
|
||||
? '-'
|
||||
: <PropVal val={row.defaultValue} {...propValProps} />}
|
||||
</td>
|
||||
<td>{row.description}</td>
|
||||
<td>
|
||||
{row.description}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
|
@ -59,7 +59,11 @@ function previewObject(val, maxPropObjectKeys) {
|
||||
const names = Object.keys(val);
|
||||
const items = {};
|
||||
names.slice(0, maxPropObjectKeys).forEach((name, i) => {
|
||||
items[`k${i}`] = <span style={valueStyles.attr}>{name}</span>;
|
||||
items[`k${i}`] = (
|
||||
<span style={valueStyles.attr}>
|
||||
{name}
|
||||
</span>
|
||||
);
|
||||
items[`c${i}`] = ': ';
|
||||
items[`v${i}`] = <PropVal val={val[name]} />;
|
||||
items[`m${i}`] = ', ';
|
||||
@ -71,7 +75,9 @@ function previewObject(val, maxPropObjectKeys) {
|
||||
}
|
||||
return (
|
||||
<span style={valueStyles.object}>
|
||||
{'{'}{createFragment(items)}{'}'}
|
||||
{'{'}
|
||||
{createFragment(items)}
|
||||
{'}'}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -83,19 +89,31 @@ export default function PropVal(props) {
|
||||
let content = null;
|
||||
|
||||
if (typeof val === 'number') {
|
||||
content = <span style={valueStyles.number}>{val}</span>;
|
||||
content = (
|
||||
<span style={valueStyles.number}>
|
||||
{val}
|
||||
</span>
|
||||
);
|
||||
} else if (typeof val === 'string') {
|
||||
if (val.length > maxPropStringLength) {
|
||||
val = `${val.slice(0, maxPropStringLength)}…`;
|
||||
}
|
||||
content = <span style={valueStyles.string}>"{val}"</span>;
|
||||
content = (
|
||||
<span style={valueStyles.string}>
|
||||
"{val}"
|
||||
</span>
|
||||
);
|
||||
braceWrap = false;
|
||||
} else if (typeof val === 'boolean') {
|
||||
content = <span style={valueStyles.bool}>{`${val}`}</span>;
|
||||
} else if (Array.isArray(val)) {
|
||||
content = previewArray(val, maxPropArrayLength);
|
||||
} else if (typeof val === 'function') {
|
||||
content = <span style={valueStyles.func}>{val.name ? `${val.name}()` : 'anonymous()'}</span>;
|
||||
content = (
|
||||
<span style={valueStyles.func}>
|
||||
{val.name ? `${val.name}()` : 'anonymous()'}
|
||||
</span>
|
||||
);
|
||||
} else if (!val) {
|
||||
content = <span style={valueStyles.empty}>{`${val}`}</span>;
|
||||
} else if (typeof val !== 'object') {
|
||||
@ -112,12 +130,23 @@ export default function PropVal(props) {
|
||||
|
||||
if (!braceWrap) return content;
|
||||
|
||||
return <span>{content}</span>;
|
||||
return (
|
||||
<span>
|
||||
{content}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
PropVal.propTypes = {
|
||||
val: PropTypes.any.isRequired, // eslint-disable-line
|
||||
maxPropObjectKeys: PropTypes.number.isRequired,
|
||||
maxPropArrayLength: PropTypes.number.isRequired,
|
||||
maxPropStringLength: PropTypes.number.isRequired,
|
||||
PropVal.defaultProps = {
|
||||
val: null,
|
||||
maxPropObjectKeys: 3,
|
||||
maxPropArrayLength: 3,
|
||||
maxPropStringLength: 50,
|
||||
};
|
||||
|
||||
PropVal.propTypes = {
|
||||
val: PropTypes.any, // eslint-disable-line
|
||||
maxPropObjectKeys: PropTypes.number,
|
||||
maxPropArrayLength: PropTypes.number,
|
||||
maxPropStringLength: PropTypes.number,
|
||||
};
|
||||
|
@ -32,8 +32,14 @@ export default function Props(props) {
|
||||
names.forEach((name, i) => {
|
||||
items.push(
|
||||
<span key={name}>
|
||||
{breakIntoNewLines ? <span><br /> </span> : ' '}
|
||||
<span style={propNameStyle}>{name}</span>
|
||||
{breakIntoNewLines
|
||||
? <span>
|
||||
<br />
|
||||
</span>
|
||||
: ' '}
|
||||
<span style={propNameStyle}>
|
||||
{name}
|
||||
</span>
|
||||
{/* Use implicit true: */}
|
||||
{(!nodeProps[name] || typeof nodeProps[name] !== 'boolean') &&
|
||||
<span>
|
||||
@ -53,7 +59,11 @@ export default function Props(props) {
|
||||
);
|
||||
});
|
||||
|
||||
return <span>{items}</span>;
|
||||
return (
|
||||
<span>
|
||||
{items}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
Props.defaultProps = {
|
||||
|
@ -180,9 +180,13 @@ export default class Story extends React.Component {
|
||||
<div style={this.state.stylesheet.children}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
<a style={linkStyle} onClick={openOverlay} role="button" tabIndex="0">Show Info</a>
|
||||
<a style={linkStyle} onClick={openOverlay} role="button" tabIndex="0">
|
||||
Show Info
|
||||
</a>
|
||||
<div style={infoStyle}>
|
||||
<a style={linkStyle} onClick={closeOverlay} role="button" tabIndex="0">×</a>
|
||||
<a style={linkStyle} onClick={closeOverlay} role="button" tabIndex="0">
|
||||
×
|
||||
</a>
|
||||
<div style={this.state.stylesheet.infoPage}>
|
||||
<div style={this.state.stylesheet.infoBody}>
|
||||
{this._getInfoHeader()}
|
||||
@ -204,8 +208,12 @@ export default class Story extends React.Component {
|
||||
|
||||
return (
|
||||
<div style={this.state.stylesheet.header.body}>
|
||||
<h1 style={this.state.stylesheet.header.h1}>{this.props.context.kind}</h1>
|
||||
<h2 style={this.state.stylesheet.header.h2}>{this.props.context.story}</h2>
|
||||
<h1 style={this.state.stylesheet.header.h1}>
|
||||
{this.props.context.kind}
|
||||
</h1>
|
||||
<h2 style={this.state.stylesheet.header.h2}>
|
||||
{this.props.context.story}
|
||||
</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -61,7 +61,11 @@ export function Pre(props) {
|
||||
lineHeight: 1.5,
|
||||
overflowX: 'scroll',
|
||||
};
|
||||
return <pre style={style}>{props.children}</pre>;
|
||||
return (
|
||||
<pre style={style}>
|
||||
{props.children}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
|
||||
Pre.propTypes = { children: PropTypes.node };
|
||||
@ -74,7 +78,11 @@ export function Blockquote(props) {
|
||||
borderLeft: '8px solid #fafafa',
|
||||
padding: '1rem',
|
||||
};
|
||||
return <blockquote style={style}>{props.children}</blockquote>;
|
||||
return (
|
||||
<blockquote style={style}>
|
||||
{props.children}
|
||||
</blockquote>
|
||||
);
|
||||
}
|
||||
|
||||
Blockquote.propTypes = { children: PropTypes.node };
|
||||
|
@ -20,7 +20,11 @@ export function H1(props) {
|
||||
padding: 0,
|
||||
fontSize: '40px',
|
||||
};
|
||||
return <h1 id={props.id} style={styles}>{props.children}</h1>;
|
||||
return (
|
||||
<h1 id={props.id} style={styles}>
|
||||
{props.children}
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
|
||||
H1.defaultProps = defaultProps;
|
||||
@ -34,7 +38,11 @@ export function H2(props) {
|
||||
padding: 0,
|
||||
fontSize: '30px',
|
||||
};
|
||||
return <h2 id={props.id} style={styles}>{props.children}</h2>;
|
||||
return (
|
||||
<h2 id={props.id} style={styles}>
|
||||
{props.children}
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
|
||||
H2.defaultProps = defaultProps;
|
||||
@ -49,7 +57,11 @@ export function H3(props) {
|
||||
fontSize: '22px',
|
||||
textTransform: 'uppercase',
|
||||
};
|
||||
return <h3 id={props.id} style={styles}>{props.children}</h3>;
|
||||
return (
|
||||
<h3 id={props.id} style={styles}>
|
||||
{props.children}
|
||||
</h3>
|
||||
);
|
||||
}
|
||||
|
||||
H3.defaultProps = defaultProps;
|
||||
@ -63,7 +75,11 @@ export function H4(props) {
|
||||
padding: 0,
|
||||
fontSize: '20px',
|
||||
};
|
||||
return <h4 id={props.id} style={styles}>{props.children}</h4>;
|
||||
return (
|
||||
<h4 id={props.id} style={styles}>
|
||||
{props.children}
|
||||
</h4>
|
||||
);
|
||||
}
|
||||
|
||||
H4.defaultProps = defaultProps;
|
||||
@ -77,7 +93,11 @@ export function H5(props) {
|
||||
padding: 0,
|
||||
fontSize: '18px',
|
||||
};
|
||||
return <h5 id={props.id} style={styles}>{props.children}</h5>;
|
||||
return (
|
||||
<h5 id={props.id} style={styles}>
|
||||
{props.children}
|
||||
</h5>
|
||||
);
|
||||
}
|
||||
|
||||
H5.defaultProps = defaultProps;
|
||||
@ -91,7 +111,11 @@ export function H6(props) {
|
||||
padding: 0,
|
||||
fontSize: '18px',
|
||||
};
|
||||
return <h6 id={props.id} style={styles}>{props.children}</h6>;
|
||||
return (
|
||||
<h6 id={props.id} style={styles}>
|
||||
{props.children}
|
||||
</h6>
|
||||
);
|
||||
}
|
||||
|
||||
H6.defaultProps = defaultProps;
|
||||
|
@ -10,7 +10,11 @@ export function P(props) {
|
||||
...baseFonts,
|
||||
fontSize: '15px',
|
||||
};
|
||||
return <p style={style}>{props.children}</p>;
|
||||
return (
|
||||
<p style={style}>
|
||||
{props.children}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
P.defaultProps = defaultProps;
|
||||
@ -21,7 +25,11 @@ export function LI(props) {
|
||||
...baseFonts,
|
||||
fontSize: '15px',
|
||||
};
|
||||
return <li style={style}>{props.children}</li>;
|
||||
return (
|
||||
<li style={style}>
|
||||
{props.children}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
LI.defaultProps = defaultProps;
|
||||
@ -32,7 +40,11 @@ export function UL(props) {
|
||||
...baseFonts,
|
||||
fontSize: '15px',
|
||||
};
|
||||
return <ul style={style}>{props.children}</ul>;
|
||||
return (
|
||||
<ul style={style}>
|
||||
{props.children}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
UL.defaultProps = defaultProps;
|
||||
|
@ -1,7 +1,12 @@
|
||||
import React from 'react';
|
||||
import deprecate from 'util-deprecate';
|
||||
import _Story from './components/Story';
|
||||
import { H1, H2, H3, H4, H5, H6, Code, P, UL, A, LI } from './components/markdown';
|
||||
|
||||
function addonCompose(addonFn) {
|
||||
return storyFn => context => addonFn(storyFn, context);
|
||||
}
|
||||
|
||||
export const Story = _Story;
|
||||
|
||||
const defaultOptions = {
|
||||
@ -29,59 +34,62 @@ const defaultMarksyConf = {
|
||||
ul: UL,
|
||||
};
|
||||
|
||||
export default {
|
||||
addWithInfo(storyName, info, storyFn, _options) {
|
||||
if (typeof storyFn !== 'function') {
|
||||
if (typeof info === 'function') {
|
||||
export function addInfo(storyFn, context, info, _options) {
|
||||
if (typeof storyFn !== 'function') {
|
||||
if (typeof info === 'function') {
|
||||
_options = storyFn; // eslint-disable-line
|
||||
storyFn = info; // eslint-disable-line
|
||||
info = ''; // eslint-disable-line
|
||||
} else {
|
||||
throw new Error('No story defining function has been specified');
|
||||
}
|
||||
} else {
|
||||
throw new Error('No story defining function has been specified');
|
||||
}
|
||||
}
|
||||
|
||||
const options = {
|
||||
...defaultOptions,
|
||||
..._options,
|
||||
};
|
||||
const options = {
|
||||
...defaultOptions,
|
||||
..._options,
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
// 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 marksyConf = { ...defaultMarksyConf };
|
||||
if (options && options.marksyConf) {
|
||||
Object.assign(marksyConf, options.marksyConf);
|
||||
}
|
||||
const marksyConf = { ...defaultMarksyConf };
|
||||
if (options && options.marksyConf) {
|
||||
Object.assign(marksyConf, options.marksyConf);
|
||||
}
|
||||
const props = {
|
||||
info,
|
||||
context,
|
||||
showInline: Boolean(options.inline),
|
||||
showHeader: Boolean(options.header),
|
||||
showSource: Boolean(options.source),
|
||||
propTables: options.propTables,
|
||||
propTablesExclude: options.propTablesExclude,
|
||||
styles: typeof options.styles === 'function' ? options.styles : s => s,
|
||||
marksyConf,
|
||||
maxPropObjectKeys: options.maxPropObjectKeys,
|
||||
maxPropArrayLength: options.maxPropArrayLength,
|
||||
maxPropsIntoLine: options.maxPropsIntoLine,
|
||||
maxPropStringLength: options.maxPropStringLength,
|
||||
};
|
||||
return (
|
||||
<Story {...props}>
|
||||
{storyFn(context)}
|
||||
</Story>
|
||||
);
|
||||
}
|
||||
|
||||
return this.add(storyName, context => {
|
||||
const props = {
|
||||
info,
|
||||
context,
|
||||
showInline: Boolean(options.inline),
|
||||
showHeader: Boolean(options.header),
|
||||
showSource: Boolean(options.source),
|
||||
propTables: options.propTables,
|
||||
propTablesExclude: options.propTablesExclude,
|
||||
styles: typeof options.styles === 'function' ? options.styles : s => s,
|
||||
marksyConf,
|
||||
maxPropObjectKeys: options.maxPropObjectKeys,
|
||||
maxPropArrayLength: options.maxPropArrayLength,
|
||||
maxPropsIntoLine: options.maxPropsIntoLine,
|
||||
maxPropStringLength: options.maxPropStringLength,
|
||||
};
|
||||
export const withInfo = (info, _options) =>
|
||||
addonCompose((storyFn, context) => addInfo(storyFn, context, info, _options));
|
||||
|
||||
return (
|
||||
<Story {...props}>
|
||||
{storyFn(context)}
|
||||
</Story>
|
||||
);
|
||||
});
|
||||
},
|
||||
export default {
|
||||
addWithInfo: deprecate(function addWithInfo(storyName, info, storyFn, _options) {
|
||||
return this.add(storyName, withInfo(info, _options)(storyFn));
|
||||
}, '@storybook/addon-info .addWithInfo() addon is deprecated, use withInfo() from the same package instead. \nSee https://github.com/storybooks/storybook/tree/master/addons/info'),
|
||||
};
|
||||
|
||||
export function setDefaults(newDefaults) {
|
||||
|
59
addons/info/src/index.test.js
Normal file
59
addons/info/src/index.test.js
Normal file
@ -0,0 +1,59 @@
|
||||
/* global document */
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import AddonInfo, { withInfo, setDefaults, addInfo } from './';
|
||||
|
||||
/* eslint-disable */
|
||||
const TestComponent = ({ func, obj, array, number, string, bool, empty }) =>
|
||||
<div>
|
||||
<h1>{func}</h1>
|
||||
<h2>{obj.toString()}</h2>
|
||||
<h3>{array}</h3>
|
||||
<h4>{number}</h4>
|
||||
<h5>{string}</h5>
|
||||
<h6>{bool}</h6>
|
||||
<p>{empty}</p>
|
||||
<a href="#">test</a>
|
||||
<code>storiesOf</code>
|
||||
<ui>
|
||||
<li>1</li>
|
||||
<li>2</li>
|
||||
</ui>
|
||||
</div>;
|
||||
/* eslint-enable */
|
||||
|
||||
const testContext = { kind: 'addon_info', story: 'jest_test' };
|
||||
const testOptions = { propTables: false };
|
||||
|
||||
describe('addon Info', () => {
|
||||
const story = context =>
|
||||
<div>
|
||||
It's a {context.story} story:
|
||||
<TestComponent
|
||||
func={x => x + 1}
|
||||
obj={{ a: 'a', b: 'b' }}
|
||||
array={[1, 2, 3]}
|
||||
number={7}
|
||||
string={'seven'}
|
||||
bool
|
||||
/>
|
||||
</div>;
|
||||
const api = {
|
||||
add: (name, fn) => fn(testContext),
|
||||
};
|
||||
it('should render <Info /> and markdown', () => {
|
||||
const Info = withInfo(
|
||||
'# Test story \n## with markdown info \ncontaing **bold**, *cursive* text and `code`'
|
||||
)(story);
|
||||
ReactDOM.render(<Info />, document.createElement('div'));
|
||||
});
|
||||
it('should render with missed info', () => {
|
||||
setDefaults(testOptions);
|
||||
addInfo(null, testContext, story, testOptions);
|
||||
});
|
||||
it('should show deprecation warning', () => {
|
||||
const addWithInfo = AddonInfo.addWithInfo.bind(api);
|
||||
addWithInfo('jest', story);
|
||||
});
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-knobs",
|
||||
"version": "3.2.0-alpha.5",
|
||||
"version": "3.2.0-alpha.7",
|
||||
"description": "Storybook Addon Prop Editor Component",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
|
@ -147,7 +147,9 @@ export default class Panel extends React.Component {
|
||||
<div style={styles.panel}>
|
||||
<PropForm knobs={knobsArray} onFieldChange={this.handleChange} />
|
||||
</div>
|
||||
<button style={styles.resetButton} onClick={this.reset}>RESET</button>
|
||||
<button style={styles.resetButton} onClick={this.reset}>
|
||||
RESET
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -24,7 +24,11 @@ class SelectType extends React.Component {
|
||||
value: key,
|
||||
};
|
||||
|
||||
return <option {...opts}>{val}</option>;
|
||||
return (
|
||||
<option {...opts}>
|
||||
{val}
|
||||
</option>
|
||||
);
|
||||
}
|
||||
_options(values) {
|
||||
let data = [];
|
||||
|
@ -5,10 +5,10 @@ import { WithNotes as ReactWithNotes } from './react';
|
||||
export const addonNotes = ({ notes }) => {
|
||||
const channel = addons.getChannel();
|
||||
|
||||
return getStory => () => {
|
||||
return getStory => (context) => {
|
||||
// send the notes to the channel before the story is rendered
|
||||
channel.emit('storybook/notes/add_notes', notes);
|
||||
return getStory();
|
||||
return getStory(context);
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -44,6 +44,7 @@ setOptions({
|
||||
showSearchBox: false,
|
||||
downPanelInRight: false,
|
||||
sortStoriesByKind: false,
|
||||
hierarchySeparator: /\//,
|
||||
});
|
||||
|
||||
storybook.configure(() => require('./stories'), module);
|
||||
|
@ -68,15 +68,15 @@ initStoryshots({
|
||||
});
|
||||
```
|
||||
|
||||
### `suit`
|
||||
### `suite`
|
||||
|
||||
By default, Storyshots groups stories inside a Jest test suit called "Storyshots". You could change it like this:
|
||||
By default, Storyshots groups stories inside a Jest test suite called "Storyshots". You could change it like this:
|
||||
|
||||
```js
|
||||
import initStoryshots from '@storybook/addon-storyshots';
|
||||
|
||||
initStoryshots({
|
||||
suit: 'MyStoryshots'
|
||||
suite: 'MyStoryshots'
|
||||
});
|
||||
```
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/addon-storyshots",
|
||||
"version": "3.2.0-alpha.5",
|
||||
"version": "3.2.0-alpha.7",
|
||||
"description": "StoryShots is a Jest Snapshot Testing Addon for Storybook.",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
@ -22,7 +22,7 @@
|
||||
"devDependencies": {
|
||||
"@storybook/addons": "^3.1.6",
|
||||
"@storybook/channels": "^3.1.6",
|
||||
"@storybook/react": "^3.2.0-alpha.5",
|
||||
"@storybook/react": "^3.2.0-alpha.7",
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
@ -33,7 +33,7 @@
|
||||
"peerDependencies": {
|
||||
"@storybook/addons": "^3.1.6",
|
||||
"@storybook/channels": "^3.1.6",
|
||||
"@storybook/react": "^3.2.0-alpha.5",
|
||||
"@storybook/react": "^3.2.0-alpha.7",
|
||||
"babel-core": "^6.24.1",
|
||||
"react": "*",
|
||||
"react-test-renderer": "*"
|
||||
|
@ -56,7 +56,8 @@ export default function testStorySnapshots(options = {}) {
|
||||
throw new Error('testStorySnapshots is intended only to be used inside jest');
|
||||
}
|
||||
|
||||
const suit = options.suit || 'Storyshots';
|
||||
// NOTE: keep `suit` typo for backwards compatibility
|
||||
const suite = options.suite || options.suit || 'Storyshots';
|
||||
const stories = storybook.getStorybook();
|
||||
|
||||
// Added not to break existing storyshots configs (can be removed in a future major release)
|
||||
@ -72,7 +73,7 @@ export default function testStorySnapshots(options = {}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
describe(suit, () => {
|
||||
describe(suite, () => {
|
||||
describe(group.kind, () => {
|
||||
// eslint-disable-next-line
|
||||
for (const story of group.stories) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/react-native",
|
||||
"version": "3.2.0-alpha.5",
|
||||
"version": "3.2.0-alpha.7",
|
||||
"description": "A better way to develop React Native Components for your app",
|
||||
"keywords": [
|
||||
"react",
|
||||
@ -24,11 +24,11 @@
|
||||
"prepublish": "node ../../scripts/prepublish.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addon-actions": "^3.1.6",
|
||||
"@storybook/addon-actions": "^3.2.0-alpha.7",
|
||||
"@storybook/addon-links": "^3.2.0-alpha.5",
|
||||
"@storybook/addons": "^3.1.6",
|
||||
"@storybook/channel-websocket": "^3.1.6",
|
||||
"@storybook/ui": "^3.2.0-alpha.5",
|
||||
"@storybook/ui": "^3.2.0-alpha.7",
|
||||
"autoprefixer": "^7.0.1",
|
||||
"babel-core": "^6.24.1",
|
||||
"babel-loader": "^7.0.0",
|
||||
|
@ -28,27 +28,19 @@ const styles = {
|
||||
const PreviewHelp = () =>
|
||||
<div style={styles.main}>
|
||||
<h1>Welcome to storybook</h1>
|
||||
<p>This is a UI component dev environment for your app.</p>
|
||||
<p>
|
||||
This is a UI component dev environment for your app.
|
||||
We've added some basic stories inside the <span style={styles.code}>
|
||||
storybook/stories
|
||||
</span>{' '}
|
||||
directory. A story is a single state of one or more UI components. You can have as many
|
||||
stories as you want. Basically a story is like a visual test case.
|
||||
</p>
|
||||
<p>
|
||||
We've added some basic stories inside the
|
||||
{' '}
|
||||
<span style={styles.code}>storybook/stories</span>
|
||||
{' '}
|
||||
directory.
|
||||
{' '}
|
||||
A story is a single state of one or more UI components. You can have as many stories as you
|
||||
want. Basically a story is like a visual test case.
|
||||
</p>
|
||||
<p>
|
||||
To see your Storybook stories on the device, you should start your mobile app for the
|
||||
{' '}
|
||||
<span style={styles.code}><platform></span>
|
||||
{' '}
|
||||
of your choice (typically ios or android). (Note that due to an implementation detail, your
|
||||
stories will only show up in the left-pane after your device has connected to this storybook
|
||||
server.)
|
||||
To see your Storybook stories on the device, you should start your mobile app for the{' '}
|
||||
<span style={styles.code}><platform></span> of your choice (typically ios or android).
|
||||
(Note that due to an implementation detail, your stories will only show up in the left-pane
|
||||
after your device has connected to this storybook server.)
|
||||
</p>
|
||||
<p>
|
||||
For <span style={styles.code}>create-react-native-app</span> apps:
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/react",
|
||||
"version": "3.2.0-alpha.5",
|
||||
"version": "3.2.0-alpha.7",
|
||||
"description": "Storybook for React: Develop React Component in isolation with Hot Reloading.",
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/apps/react",
|
||||
"bugs": {
|
||||
@ -22,11 +22,11 @@
|
||||
"prepublish": "node ../../scripts/prepublish.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addon-actions": "^3.1.6",
|
||||
"@storybook/addon-actions": "^3.2.0-alpha.7",
|
||||
"@storybook/addon-links": "^3.2.0-alpha.5",
|
||||
"@storybook/addons": "^3.1.6",
|
||||
"@storybook/channel-postmessage": "^3.1.6",
|
||||
"@storybook/ui": "^3.2.0-alpha.5",
|
||||
"@storybook/ui": "^3.2.0-alpha.7",
|
||||
"airbnb-js-shims": "^1.1.1",
|
||||
"autoprefixer": "^7.1.1",
|
||||
"babel-core": "^6.24.1",
|
||||
|
@ -32,7 +32,9 @@ const codeStyle = {
|
||||
|
||||
const ErrorDisplay = ({ error }) =>
|
||||
<div style={mainStyle}>
|
||||
<div style={headingStyle}>{error.message}</div>
|
||||
<div style={headingStyle}>
|
||||
{error.message}
|
||||
</div>
|
||||
<pre style={codeStyle}>
|
||||
<code>
|
||||
{error.stack}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/vue",
|
||||
"version": "3.2.0-alpha.6",
|
||||
"version": "3.2.0-alpha.7",
|
||||
"description": "Storybook for Vue: Develop Vue Component in isolation with Hot Reloading.",
|
||||
"homepage": "https://github.com/storybooks/storybook/tree/master/apps/vue",
|
||||
"bugs": {
|
||||
|
@ -8,7 +8,9 @@ import './style.css';
|
||||
const DocsContent = ({ title, content, editUrl }) =>
|
||||
<div id="docs-content">
|
||||
<div className="content">
|
||||
<h2 className="title">{title}</h2>
|
||||
<h2 className="title">
|
||||
{title}
|
||||
</h2>
|
||||
<p>
|
||||
<a className="edit-link" href={editUrl} target="_blank" rel="noopener noreferrer">
|
||||
Edit this page
|
||||
|
@ -24,11 +24,19 @@ class Nav extends React.Component {
|
||||
}
|
||||
|
||||
renderNavOpts(nav) {
|
||||
return <option value={nav.id} key={nav.id}>{nav.title}</option>;
|
||||
return (
|
||||
<option value={nav.id} key={nav.id}>
|
||||
{nav.title}
|
||||
</option>
|
||||
);
|
||||
}
|
||||
|
||||
renderHeadingOpts(section) {
|
||||
return <option value={section.id} key={section.id}>{section.heading}</option>;
|
||||
return (
|
||||
<option value={section.id} key={section.id}>
|
||||
{section.heading}
|
||||
</option>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
const { sections, selectedSection, selectedItem } = this.props;
|
||||
|
@ -7,18 +7,21 @@ const Nav = ({ sections, selectedSectionId, selectedItemId }) =>
|
||||
<div id="nav">
|
||||
{sections.map(section =>
|
||||
<div key={section.id}>
|
||||
<h3>{section.heading}</h3>
|
||||
<h3>
|
||||
{section.heading}
|
||||
</h3>
|
||||
<ul>
|
||||
{section.items.map(item => {
|
||||
const cssClass = section.id === selectedSectionId && item.id === selectedItemId
|
||||
? 'selected'
|
||||
: '';
|
||||
const cssClass =
|
||||
section.id === selectedSectionId && item.id === selectedItemId ? 'selected' : '';
|
||||
|
||||
const url = `/${section.id}/${item.id}/`;
|
||||
|
||||
return (
|
||||
<li key={item.id}>
|
||||
<Link className={cssClass} to={url}>{item.title}</Link>
|
||||
<Link className={cssClass} to={url}>
|
||||
{item.title}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
|
@ -10,11 +10,7 @@ const Footer = () =>
|
||||
<div className="row logos">
|
||||
<div className="col-xs-12">
|
||||
<center>
|
||||
Maintained by the
|
||||
{' '}
|
||||
<Link to="/basics/community/">
|
||||
Storybook Community
|
||||
</Link>
|
||||
Maintained by the <Link to="/basics/community/">Storybook Community</Link>
|
||||
.
|
||||
</center>
|
||||
<center>
|
||||
|
@ -14,11 +14,23 @@ const GridItem = ({ title, description, source, demo, thumbnail }) =>
|
||||
<div className="overlay" />
|
||||
</div>
|
||||
<div className="text">
|
||||
<h2>{title}</h2>
|
||||
<p className="desc">{description}</p>
|
||||
<h2>
|
||||
{title}
|
||||
</h2>
|
||||
<p className="desc">
|
||||
{description}
|
||||
</p>
|
||||
<div className="button-row">
|
||||
{demo ? <a href={demo} {...linkProps}>Demo</a> : null}
|
||||
{source ? <a href={source} {...linkProps}>Source</a> : null}
|
||||
{demo
|
||||
? <a href={demo} {...linkProps}>
|
||||
Demo
|
||||
</a>
|
||||
: null}
|
||||
{source
|
||||
? <a href={source} {...linkProps}>
|
||||
Source
|
||||
</a>
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
|
@ -17,7 +17,9 @@ const Item = ({ storybook, owner, source }) =>
|
||||
{storybook.name}
|
||||
</a>
|
||||
</p>
|
||||
<a href={source} target="_blank" rel="noopener noreferrer">source</a>
|
||||
<a href={source} target="_blank" rel="noopener noreferrer">
|
||||
source
|
||||
</a>
|
||||
</div>
|
||||
</div>;
|
||||
Item.propTypes = {
|
||||
|
@ -46,23 +46,37 @@ const MainLinks = () =>
|
||||
id="search"
|
||||
placeholder="type to search"
|
||||
/>
|
||||
<span className="form-control-feedback" aria-hidden="true">🔍</span>
|
||||
<span className="form-control-feedback" aria-hidden="true">
|
||||
🔍
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-sm-4 read-docs">
|
||||
<Link to="/basics/introduction/"><h3>Basics</h3></Link>
|
||||
<Link to="/basics/introduction/">
|
||||
<h3>Basics</h3>
|
||||
</Link>
|
||||
<ul>
|
||||
<li><Link to="/basics/quick-start-guide/">Quick setup</Link></li>
|
||||
<li><Link to="/basics/slow-start-guide/">Adding to existing project</Link></li>
|
||||
<li><Link to="/basics/writing-stories/">Writing stories</Link></li>
|
||||
<li>
|
||||
<Link to="/basics/quick-start-guide/">Quick setup</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/basics/slow-start-guide/">Adding to existing project</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/basics/writing-stories/">Writing stories</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="col-sm-4 read-docs">
|
||||
<Link to="/configurations/default-config/"><h3>Configuration</h3></Link>
|
||||
<Link to="/configurations/default-config/">
|
||||
<h3>Configuration</h3>
|
||||
</Link>
|
||||
<ul>
|
||||
<li><Link to="/configurations/custom-babel-config/">Babel configurations</Link></li>
|
||||
<li>
|
||||
<Link to="/configurations/custom-babel-config/">Babel configurations</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/configurations/custom-webpack-config/">Webpack configurations</Link>
|
||||
</li>
|
||||
@ -76,9 +90,13 @@ const MainLinks = () =>
|
||||
</div>
|
||||
|
||||
<div className="col-sm-4 read-docs">
|
||||
<Link to="/configurations/default-config/"><h3>Addons</h3></Link>
|
||||
<Link to="/configurations/default-config/">
|
||||
<h3>Addons</h3>
|
||||
</Link>
|
||||
<ul>
|
||||
<li><Link to="/addons/introduction/">Intro to Addons</Link></li>
|
||||
<li>
|
||||
<Link to="/addons/introduction/">Intro to Addons</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/addons/using-addons/">Using Addons</Link>
|
||||
</li>
|
||||
|
@ -6,17 +6,14 @@ const Platform = () =>
|
||||
<div className="col-md-12">
|
||||
<h3 className="built-for">Built for</h3>
|
||||
<p className="platforms">
|
||||
|
||||
<a
|
||||
href="https://github.com/storybooks/storybook/tree/master/app/react"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
React
|
||||
</a>
|
||||
{' '}
|
||||
&
|
||||
{' '}
|
||||
</a>{' '}
|
||||
&{' '}
|
||||
<a
|
||||
href="https://github.com/storybooks/storybook/tree/master/app/react-native"
|
||||
target="_blank"
|
||||
|
@ -67,9 +67,9 @@ const UsedBy = ({ users }) =>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Link to="/examples/" className="used-by-more-examples">
|
||||
See more examples…
|
||||
</Link>
|
||||
<div className="used-by-more-examples">
|
||||
<Link to="/examples/">See more examples…</Link>
|
||||
</div>
|
||||
</div>;
|
||||
UsedBy.propTypes = {
|
||||
users: PropTypes.array, // eslint-disable-line
|
||||
|
@ -63,5 +63,13 @@
|
||||
.used-by-more-examples {
|
||||
text-align: center;
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
margin-top: 50px;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.used-by-more-examples a {
|
||||
background-color: #e4004f;
|
||||
color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
@ -41,7 +41,9 @@ const HTML = props => {
|
||||
<meta charSet="utf-8" />
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{title}</title>
|
||||
<title>
|
||||
{title}
|
||||
</title>
|
||||
<link rel="icon" href={favicon} type="image/x-icon" />
|
||||
{css}
|
||||
</head>
|
||||
|
@ -33,8 +33,8 @@
|
||||
"color-pairs-picker": "^1.3.5",
|
||||
"docsearch.js": "^2.3.3",
|
||||
"front-matter": "^2.1.2",
|
||||
"gatsby": "^0.12.45",
|
||||
"gh-pages": "^0.12.0",
|
||||
"gatsby": "0.12.48",
|
||||
"gh-pages": "^1.0.0",
|
||||
"global": "^4.3.2",
|
||||
"highlight.js": "^9.12.0",
|
||||
"loader-utils": "^1.1.0",
|
||||
|
@ -65,6 +65,8 @@ configure(loadStories, module);
|
||||
|
||||
Here we use Webpack's [require.context](https://webpack.github.io/docs/context.html#require-context) to load modules dynamically. Have a look at the relevant Webpack [docs](https://webpack.github.io/docs/context.html#require-context) to learn more about how to use require.context.
|
||||
|
||||
The **React Native** packager resolves all the imports at build-time, so it's not possible to load modules dynamically. If you don't want to import all your stories manually you can use [react-native-storybook-loader](https://github.com/elderfo/react-native-storybook-loader) to automatically create the import statements for all of your stories.
|
||||
|
||||
## Using Decorators
|
||||
|
||||
A decorator is a way to wrap a story with a common set of component(s). Let's say you want to center all your stories. Here is how we can do this with a decorator:
|
||||
|
@ -25,16 +25,23 @@ algolia:
|
||||
description: Lightning-fast, hyper-configurable search.
|
||||
source: https://github.com/algolia/react-instantsearch/
|
||||
demo: https://community.algolia.com/react-instantsearch/storybook/
|
||||
rebass:
|
||||
thumbnail: ./thumbnails/rebass.png
|
||||
title: Rebass
|
||||
description: Functional React UI component library
|
||||
demo: http://jxnblk.com/rebass/stories/
|
||||
source: https://github.com/jxnblk/rebass
|
||||
site: http://jxnblk.com/rebass
|
||||
coursera:
|
||||
thumbnail: ./thumbnails/coursera-ui.png
|
||||
title: Coursera
|
||||
description: Coursera UI component library
|
||||
demo: https://building.coursera.org/ui/
|
||||
demo: https://webedx-spark.github.io/coursera-ui/
|
||||
artsy:
|
||||
thumbnail: ./thumbnails/artsy.png
|
||||
title: Artsy Force
|
||||
description: Artsy's "Force" component library
|
||||
demo: https://artsy.github.io/reaction-force/
|
||||
demo: https://artsy.github.io/reaction/
|
||||
source: https://github.com/artsy/reaction-force
|
||||
site: https://artsy.net
|
||||
necolas:
|
||||
|
BIN
docs/pages/examples/thumbnails/rebass.png
Normal file
BIN
docs/pages/examples/thumbnails/rebass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 110 KiB |
@ -34,31 +34,28 @@ Then, install StoryShots into your app with:
|
||||
npm i -D @storybook/addon-storyshots
|
||||
```
|
||||
|
||||
Then, add the following NPM script into your package.json:
|
||||
Then, assuming you are using Jest for testing, you can create a test file `storyshots.test.js` that contains the following:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"test-storybook": "storyshots"
|
||||
}
|
||||
}
|
||||
```js
|
||||
import initStoryshots from '@storybook/addon-storyshots';
|
||||
|
||||
initStoryshots({ /* configuration options */ });
|
||||
```
|
||||
|
||||
Now you can run the above command with:
|
||||
Now you can snapshot test all of your stories with:
|
||||
|
||||
```sh
|
||||
npm run test-storybook
|
||||
npm test
|
||||
```
|
||||
|
||||
This will save the initial set of snapshots inside your Storybook config directory.
|
||||
|
||||

|
||||
|
||||
After you complete any changes, you can run the above NPM script again and find our structural changes.
|
||||
After you complete any changes, you can run the test again and find all structural changes.
|
||||
|
||||

|
||||
|
||||
* * *
|
||||
|
||||
StoryShots also comes with a few important [productive features](https://github.com/storybooks/storybook/tree/master/addons/storyshots#key-features) that can be customized.
|
||||
Have a look at the StoryShots [repo](https://github.com/storybooks/storybook/tree/master/addons/storyshots) for more information.
|
||||
StoryShots also comes with a variety of customization options. Have a look at the StoryShots [repo](https://github.com/storybooks/storybook/tree/master/addons/storyshots) for more information.
|
||||
|
@ -10,7 +10,9 @@ const Markdown = ({ route }) => {
|
||||
return (
|
||||
<DocumentTitle title={`${post.title} | ${config.siteTitle}`}>
|
||||
<div className="markdown">
|
||||
<h1>{post.title}</h1>
|
||||
<h1>
|
||||
{post.title}
|
||||
</h1>
|
||||
<p>
|
||||
<a className="edit-link" href={editUrl} target="_blank" rel="noopener noreferrer">
|
||||
Edit this page
|
||||
|
@ -19,17 +19,17 @@
|
||||
"uuid": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "^3.0.0",
|
||||
"@storybook/addon-actions": "3.2.0-alpha.7",
|
||||
"@storybook/addon-centered": "^3.0.0",
|
||||
"@storybook/addon-events": "^3.0.0",
|
||||
"@storybook/addon-knobs": "3.2.0-alpha.5",
|
||||
"@storybook/addon-knobs": "3.2.0-alpha.7",
|
||||
"@storybook/addon-info": "^3.0.0",
|
||||
"@storybook/addon-links": "3.2.0-alpha.5",
|
||||
"@storybook/addon-notes": "3.2.0-alpha.5",
|
||||
"@storybook/addon-options": "3.2.0-alpha.5",
|
||||
"@storybook/addon-storyshots": "3.2.0-alpha.5",
|
||||
"@storybook/addon-storyshots": "3.2.0-alpha.7",
|
||||
"@storybook/addons": "^3.0.0",
|
||||
"@storybook/react": "3.2.0-alpha.5",
|
||||
"@storybook/react": "3.2.0-alpha.7",
|
||||
"react-scripts": "1.0.1"
|
||||
},
|
||||
"private": true
|
||||
|
@ -60,8 +60,12 @@ export default class Logger extends Component {
|
||||
<dl>
|
||||
{events.map(({ id, name, payload }) =>
|
||||
<div style={styles.item} key={id}>
|
||||
<dt><b>Event name:</b> {name}</dt>
|
||||
<dd><b>Event payload:</b> {json.plain(payload)}</dd>
|
||||
<dt>
|
||||
<b>Event name:</b> {name}
|
||||
</dt>
|
||||
<dd>
|
||||
<b>Event payload:</b> {json.plain(payload)}
|
||||
</dd>
|
||||
</div>
|
||||
)}
|
||||
</dl>
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
object,
|
||||
} from '@storybook/addon-knobs';
|
||||
import centered from '@storybook/addon-centered';
|
||||
import { withInfo } from '@storybook/addon-info';
|
||||
|
||||
import { Button, Welcome } from '@storybook/react/demo';
|
||||
|
||||
@ -37,6 +38,22 @@ const emit = emiter.emit.bind(emiter);
|
||||
|
||||
storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);
|
||||
|
||||
const InfoButton = () =>
|
||||
<span
|
||||
style={{
|
||||
fontFamily: 'sans-serif',
|
||||
fontSize: 12,
|
||||
textDecoration: 'none',
|
||||
background: 'rgb(34, 136, 204)',
|
||||
color: 'rgb(255, 255, 255)',
|
||||
padding: '5px 15px',
|
||||
margin: 10,
|
||||
borderRadius: '0px 0px 0px 5px',
|
||||
}}
|
||||
>
|
||||
{' '}Show Info{' '}
|
||||
</span>;
|
||||
|
||||
storiesOf('Button', module)
|
||||
.addDecorator(withKnobs)
|
||||
.add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
|
||||
@ -75,21 +92,56 @@ storiesOf('Button', module)
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<p>{intro}</p>
|
||||
<p>My birthday is: {new Date(birthday).toLocaleDateString()}</p>
|
||||
<p>My wallet contains: ${dollars.toFixed(2)}</p>
|
||||
<p>
|
||||
{intro}
|
||||
</p>
|
||||
<p>
|
||||
My birthday is: {new Date(birthday).toLocaleDateString()}
|
||||
</p>
|
||||
<p>
|
||||
My wallet contains: ${dollars.toFixed(2)}
|
||||
</p>
|
||||
<p>In my backpack, I have:</p>
|
||||
<ul>
|
||||
{items.map(item => <li key={item}>{item}</li>)}
|
||||
{items.map(item =>
|
||||
<li key={item}>
|
||||
{item}
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<p>{salutation}</p>
|
||||
<p>
|
||||
{salutation}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
.addWithInfo(
|
||||
'with some info',
|
||||
'Use the [info addon](https://github.com/storybooks/storybook/tree/master/addons/info) with its painful API.',
|
||||
() => <Button>click the "?" in top right for info</Button>
|
||||
context =>
|
||||
<div>
|
||||
click the <InfoButton /> label in top right for info about "{context.story}"
|
||||
</div>
|
||||
)
|
||||
.add(
|
||||
'with new info',
|
||||
withInfo(
|
||||
'Use the [info addon](https://github.com/storybooks/storybook/tree/master/addons/info) with its new painless API.'
|
||||
)(context =>
|
||||
<div>
|
||||
click the <InfoButton /> label in top right for info about "{context.story}"
|
||||
</div>
|
||||
)
|
||||
)
|
||||
.add(
|
||||
'addons composition',
|
||||
withInfo('see Notes panel for composition info')(
|
||||
addonNotes({ notes: 'Composition: Info(Notes())' })(context =>
|
||||
<div>
|
||||
click the <InfoButton /> label in top right for info about "{context.story}"
|
||||
</div>
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
storiesOf('App', module).add('full app', () => <App />);
|
||||
@ -177,7 +229,11 @@ storiesOf('Addon Knobs deprecated Decorator', module)
|
||||
const age = number('Age', 120);
|
||||
|
||||
const content = `I am ${name} and I'm ${age} years old.`;
|
||||
return <div>{content}</div>;
|
||||
return (
|
||||
<div>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
storiesOf('Addon Knobs', module).add(
|
||||
@ -187,14 +243,26 @@ storiesOf('Addon Knobs', module).add(
|
||||
const age = number('Age', 89);
|
||||
|
||||
const content = `I am ${name} and I'm ${age} years old.`;
|
||||
return <div>{content}</div>;
|
||||
return (
|
||||
<div>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
storiesOf('component.base.Link')
|
||||
.addDecorator(withKnobs)
|
||||
.add('first', () => <a>{text('firstLink', 'first link')}</a>)
|
||||
.add('second', () => <a>{text('secondLink', 'second link')}</a>);
|
||||
.add('first', () =>
|
||||
<a>
|
||||
{text('firstLink', 'first link')}
|
||||
</a>
|
||||
)
|
||||
.add('second', () =>
|
||||
<a>
|
||||
{text('secondLink', 'second link')}
|
||||
</a>
|
||||
);
|
||||
|
||||
storiesOf('component.base.Span')
|
||||
.add('first', () => <span>first span</span>)
|
||||
@ -205,8 +273,20 @@ storiesOf('component.common.Div')
|
||||
.add('second', () => <div>second div</div>);
|
||||
|
||||
storiesOf('component.common.Table')
|
||||
.add('first', () => <table><tr><td>first table</td></tr></table>)
|
||||
.add('second', () => <table><tr><td>first table</td></tr></table>);
|
||||
.add('first', () =>
|
||||
<table>
|
||||
<tr>
|
||||
<td>first table</td>
|
||||
</tr>
|
||||
</table>
|
||||
)
|
||||
.add('second', () =>
|
||||
<table>
|
||||
<tr>
|
||||
<td>first table</td>
|
||||
</tr>
|
||||
</table>
|
||||
);
|
||||
|
||||
storiesOf('component.Button')
|
||||
.add('first', () => <button>first button</button>)
|
||||
@ -216,7 +296,11 @@ storiesOf('component.Button')
|
||||
|
||||
storiesOf('Cells¯\\_(ツ)_/¯Molecules.Atoms/simple', module)
|
||||
.addDecorator(withKnobs)
|
||||
.add('with text', () => <Button>{text('buttonText', 'Hello Button')}</Button>)
|
||||
.add('with text', () =>
|
||||
<Button>
|
||||
{text('buttonText', 'Hello Button')}
|
||||
</Button>
|
||||
)
|
||||
.add('with some emoji', () => <Button>😀 😎 👍 💯</Button>);
|
||||
|
||||
storiesOf('Cells/Molecules/Atoms.more', module)
|
||||
@ -230,4 +314,3 @@ storiesOf('Cells/Molecules', module)
|
||||
storiesOf('Cells.Molecules.Atoms', module)
|
||||
.add('with text2', () => <Button>Hello Button</Button>)
|
||||
.add('with some emoji2', () => <Button>😀 😎 👍 💯</Button>);
|
||||
|
||||
|
@ -12,7 +12,11 @@ import Welcome from './Welcome';
|
||||
storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);
|
||||
|
||||
storiesOf('Button', module)
|
||||
.addDecorator(getStory => <CenterView>{getStory()}</CenterView>)
|
||||
.addDecorator(getStory =>
|
||||
<CenterView>
|
||||
{getStory()}
|
||||
</CenterView>
|
||||
)
|
||||
.add('with text', () =>
|
||||
<Button onPress={action('clicked-text')}>
|
||||
<Text>Hello Button</Text>
|
||||
|
@ -13,7 +13,8 @@
|
||||
"test-cra",
|
||||
"react-native-vanilla",
|
||||
"vue-example",
|
||||
"@storybook/components"
|
||||
"@storybook/components",
|
||||
"@storybook/vue"
|
||||
]
|
||||
}
|
||||
},
|
||||
@ -24,5 +25,5 @@
|
||||
"examples/*"
|
||||
],
|
||||
"concurrency": 1,
|
||||
"version": "3.2.0-alpha.5"
|
||||
"version": "3.2.0-alpha.7"
|
||||
}
|
||||
|
@ -14,7 +14,11 @@ import Welcome from './Welcome';
|
||||
storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);
|
||||
|
||||
storiesOf('Button', module)
|
||||
.addDecorator(getStory => <CenterView>{getStory()}</CenterView>)
|
||||
.addDecorator(getStory =>
|
||||
<CenterView>
|
||||
{getStory()}
|
||||
</CenterView>
|
||||
)
|
||||
.add('with text', () =>
|
||||
<Button onPress={action('clicked-text')}>
|
||||
<Text>Hello Button</Text>
|
||||
|
@ -12,7 +12,11 @@ import Welcome from './Welcome';
|
||||
storiesOf('Welcome', module).add('to Storybook', () => <Welcome showApp={linkTo('Button')} />);
|
||||
|
||||
storiesOf('Button', module)
|
||||
.addDecorator(getStory => <CenterView>{getStory()}</CenterView>)
|
||||
.addDecorator(getStory =>
|
||||
<CenterView>
|
||||
{getStory()}
|
||||
</CenterView>
|
||||
)
|
||||
.add('with text', () =>
|
||||
<Button onPress={action('clicked-text')}>
|
||||
<Text>Hello Button</Text>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@storybook/ui",
|
||||
"version": "3.2.0-alpha.5",
|
||||
"version": "3.2.0-alpha.7",
|
||||
"description": "Core Storybook UI",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
|
@ -40,16 +40,16 @@ class DownPanel extends Component {
|
||||
if (name === this.props.selectedPanel) {
|
||||
Object.assign(panelStyle, { flex: 1, display: 'flex' });
|
||||
}
|
||||
return <div key={name} style={panelStyle} role="tabpanel">{panel.render()}</div>;
|
||||
return (
|
||||
<div key={name} style={panelStyle} role="tabpanel">
|
||||
{panel.render()}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
renderEmpty() {
|
||||
return (
|
||||
<div style={style.empty}>
|
||||
no panels available
|
||||
</div>
|
||||
);
|
||||
return <div style={style.empty}>no panels available</div>;
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -58,8 +58,12 @@ class DownPanel extends Component {
|
||||
}
|
||||
return (
|
||||
<div style={style.wrapper}>
|
||||
<div style={style.tabbar} role="tablist">{this.renderTabs()}</div>
|
||||
<div style={style.content}>{this.renderPanels()}</div>
|
||||
<div style={style.tabbar} role="tablist">
|
||||
{this.renderTabs()}
|
||||
</div>
|
||||
<div style={style.content}>
|
||||
{this.renderPanels()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -204,7 +204,9 @@ class Layout extends React.Component {
|
||||
showLeftPanel,
|
||||
() =>
|
||||
<div style={leftPanelStyle(leftPanelOnTop)}>
|
||||
<div style={{ flexGrow: 1, height: '100%' }}>{leftPanel()}</div>
|
||||
<div style={{ flexGrow: 1, height: '100%', width: '100%' }}>
|
||||
{leftPanel()}
|
||||
</div>
|
||||
<USplit shift={5} split={storiesSplit} />
|
||||
</div>,
|
||||
() => <span />
|
||||
|
@ -50,9 +50,13 @@ const linkStyle = {
|
||||
|
||||
const Header = ({ openShortcutsHelp, name, url }) =>
|
||||
<div style={wrapperStyle}>
|
||||
<button style={shortcutIconStyle} onClick={openShortcutsHelp}>⌘</button>
|
||||
<button style={shortcutIconStyle} onClick={openShortcutsHelp}>
|
||||
⌘
|
||||
</button>
|
||||
<a style={linkStyle} href={url} target="_blank" rel="noopener noreferrer">
|
||||
<h3 style={headingStyle}>{name}</h3>
|
||||
<h3 style={headingStyle}>
|
||||
{name}
|
||||
</h3>
|
||||
</a>
|
||||
</div>;
|
||||
|
||||
|
@ -8,7 +8,7 @@ import TextFilter from './text_filter';
|
||||
const scrollStyle = {
|
||||
height: 'calc(100vh - 105px)',
|
||||
marginTop: 10,
|
||||
overflowY: 'auto',
|
||||
overflow: 'auto',
|
||||
};
|
||||
|
||||
const mainStyle = {
|
||||
|
@ -3,10 +3,11 @@ import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import deepEqual from 'deep-equal';
|
||||
import treeNodeTypes from './tree_node_type';
|
||||
import treeDecorators from './tree_decorators';
|
||||
import createTreeDecorators from './tree_decorators';
|
||||
import treeStyle from './tree_style';
|
||||
|
||||
const namespaceSeparator = '@';
|
||||
const keyCodeEnter = 13;
|
||||
|
||||
function createNodeKey({ namespaces, type }) {
|
||||
return [...namespaces, [type]].join(namespaceSeparator);
|
||||
@ -39,12 +40,14 @@ class Stories extends React.Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.onToggle = this.onToggle.bind(this);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
|
||||
const { selectedHierarchy } = this.props;
|
||||
|
||||
this.state = {
|
||||
nodes: getSelectedNodes(selectedHierarchy),
|
||||
};
|
||||
this.treeDecorators = createTreeDecorators(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
@ -82,6 +85,12 @@ class Stories extends React.Component {
|
||||
}));
|
||||
}
|
||||
|
||||
onKeyDown(event, node) {
|
||||
if (event.keyCode === keyCodeEnter) {
|
||||
this.onToggle(node, !node.toggled);
|
||||
}
|
||||
}
|
||||
|
||||
fireOnKind(kind) {
|
||||
const { onSelectStory } = this.props;
|
||||
if (onSelectStory) onSelectStory(kind, null);
|
||||
@ -144,7 +153,7 @@ class Stories extends React.Component {
|
||||
style={treeStyle}
|
||||
data={data}
|
||||
onToggle={this.onToggle}
|
||||
decorators={treeDecorators}
|
||||
decorators={this.treeDecorators}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -313,5 +313,46 @@ describe('manager.ui.components.left_panel.stories', () => {
|
||||
|
||||
expect(onSelectStory).toHaveBeenCalledWith('another.space.20', 'b2');
|
||||
});
|
||||
|
||||
test('should call the onSelectStory prop when a story is selected with enter key', () => {
|
||||
const data = createHierarchy(
|
||||
[
|
||||
{ kind: 'some.name.item1', stories: ['a1', 'a2'] },
|
||||
{ kind: 'another.space.20', stories: ['b1', 'b2'] },
|
||||
],
|
||||
'\\.'
|
||||
);
|
||||
|
||||
const onSelectStory = jest.fn();
|
||||
const wrap = mount(
|
||||
<Stories
|
||||
storiesHierarchy={data}
|
||||
selectedKind="some.name.item1"
|
||||
selectedStory="a2"
|
||||
selectedHierarchy={['some', 'name', 'item1']}
|
||||
onSelectStory={onSelectStory}
|
||||
/>
|
||||
);
|
||||
|
||||
wrap
|
||||
.find('a')
|
||||
.filterWhere(el => el.text() === 'another')
|
||||
.last()
|
||||
.simulate('keyDown', { keyCode: 13 });
|
||||
|
||||
wrap
|
||||
.find('a')
|
||||
.filterWhere(el => el.text() === 'space')
|
||||
.last()
|
||||
.simulate('keyDown', { keyCode: 13 });
|
||||
|
||||
wrap
|
||||
.find('a')
|
||||
.filterWhere(el => el.text() === '20')
|
||||
.last()
|
||||
.simulate('keyDown', { keyCode: 13 });
|
||||
|
||||
expect(onSelectStory).toHaveBeenCalledWith('another.space.20', null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -34,41 +34,61 @@ ContainerDecorator.propTypes = {
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
function HeaderDecorator(props) {
|
||||
const { style, node } = props;
|
||||
function createHeaderDecoratorScope(parent) {
|
||||
class HeaderDecorator extends React.Component {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
this.onKeyDown = this.onKeyDown.bind(this);
|
||||
}
|
||||
|
||||
const newStyleTitle = {
|
||||
...style.title,
|
||||
};
|
||||
onKeyDown(event) {
|
||||
const { onKeyDown } = parent;
|
||||
const { node } = this.props;
|
||||
|
||||
const Icon = iconsMap[node.type];
|
||||
onKeyDown(event, node);
|
||||
}
|
||||
|
||||
if (!node.children || !node.children.length) {
|
||||
newStyleTitle.fontSize = '13px';
|
||||
render() {
|
||||
const { style, node } = this.props;
|
||||
|
||||
const newStyleTitle = {
|
||||
...style.title,
|
||||
};
|
||||
|
||||
const Icon = iconsMap[node.type];
|
||||
|
||||
if (!node.children || !node.children.length) {
|
||||
newStyleTitle.fontSize = '13px';
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={style.base} role="menuitem" tabIndex="0" onKeyDown={this.onKeyDown}>
|
||||
{Icon && <Icon color={iconsColor} />}
|
||||
<a style={newStyleTitle}>
|
||||
{node.name}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={style.base}>
|
||||
{Icon && <Icon color={iconsColor} />}
|
||||
<a style={newStyleTitle}>
|
||||
{node.name}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
HeaderDecorator.propTypes = {
|
||||
style: PropTypes.shape({
|
||||
title: PropTypes.object.isRequired,
|
||||
base: PropTypes.object.isRequired,
|
||||
}).isRequired,
|
||||
node: PropTypes.shape({
|
||||
name: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
return HeaderDecorator;
|
||||
}
|
||||
|
||||
HeaderDecorator.propTypes = {
|
||||
style: PropTypes.shape({
|
||||
title: PropTypes.object,
|
||||
base: PropTypes.object,
|
||||
}).isRequired,
|
||||
node: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default {
|
||||
...decorators,
|
||||
Header: HeaderDecorator,
|
||||
Container: ContainerDecorator,
|
||||
};
|
||||
export default function(parent) {
|
||||
return {
|
||||
...decorators,
|
||||
Header: createHeaderDecoratorScope(parent),
|
||||
Container: ContainerDecorator,
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { baseFonts } from '../../theme';
|
||||
|
||||
const toggleWidth = '24px';
|
||||
|
||||
export default {
|
||||
tree: {
|
||||
base: {
|
||||
@ -8,6 +10,7 @@ export default {
|
||||
padding: 0,
|
||||
fontFamily: baseFonts.fontFamily,
|
||||
fontSize: '15px',
|
||||
minWidth: '200px',
|
||||
},
|
||||
node: {
|
||||
base: {
|
||||
@ -30,7 +33,7 @@ export default {
|
||||
verticalAlign: 'top',
|
||||
marginLeft: '-5px',
|
||||
height: '24px',
|
||||
width: '24px',
|
||||
width: toggleWidth,
|
||||
},
|
||||
wrapper: {
|
||||
position: 'absolute',
|
||||
@ -49,6 +52,7 @@ export default {
|
||||
base: {
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
maxWidth: `calc(100% - ${toggleWidth})`,
|
||||
},
|
||||
connector: {
|
||||
width: '2px',
|
||||
|
@ -62,19 +62,31 @@ export function getShortcuts(platform) {
|
||||
export const Keys = ({ shortcutKeys }) => {
|
||||
// if we have only one key combination for a shortcut
|
||||
if (shortcutKeys.length === 1) {
|
||||
return <span><b style={commandStyle}>{shortcutKeys[0]}</b></span>;
|
||||
return (
|
||||
<span>
|
||||
<b style={commandStyle}>
|
||||
{shortcutKeys[0]}
|
||||
</b>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
// if we have multiple key combinations for a shortcut
|
||||
const keys = shortcutKeys.map((key, index, arr) =>
|
||||
<span key={key}>
|
||||
<b style={commandStyle}>{key}</b>
|
||||
<b style={commandStyle}>
|
||||
{key}
|
||||
</b>
|
||||
{/* add / & space if it is not a last key combination */}
|
||||
{arr.length - 1 !== index ? <span>/ </span> : ''}
|
||||
</span>
|
||||
);
|
||||
|
||||
return <span>{keys}</span>;
|
||||
return (
|
||||
<span>
|
||||
{keys}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
Keys.propTypes = {
|
||||
|
@ -8,9 +8,14 @@ export const mapper = (state, props, { actions }) => {
|
||||
const actionMap = actions();
|
||||
const { stories, selectedKind, selectedStory, uiOptions, storyFilter } = state;
|
||||
const { name, url, sortStoriesByKind, hierarchySeparator } = uiOptions;
|
||||
const filteredStores = filters.storyFilter(stories, storyFilter, selectedKind, sortStoriesByKind);
|
||||
const filteredStories = filters.storyFilter(
|
||||
stories,
|
||||
storyFilter,
|
||||
selectedKind,
|
||||
sortStoriesByKind
|
||||
);
|
||||
|
||||
const storiesHierarchy = createHierarchy(filteredStores, hierarchySeparator);
|
||||
const storiesHierarchy = createHierarchy(filteredStories, hierarchySeparator);
|
||||
const selectedHierarchy = resolveStoryHierarchy(selectedKind, hierarchySeparator);
|
||||
|
||||
const data = {
|
||||
|
@ -11,11 +11,27 @@ export function storyFilter(stories, filter, selectedKind, sortStoriesByKind) {
|
||||
if (!stories) return null;
|
||||
const sorted = sort(stories, sortStoriesByKind);
|
||||
if (!filter) return sorted;
|
||||
|
||||
return sorted.filter(kindInfo => {
|
||||
if (kindInfo.kind === selectedKind) return true;
|
||||
return sorted.reduce((acc, kindInfo) => {
|
||||
// Don't filter out currently selected filter
|
||||
if (kindInfo.kind === selectedKind) return acc.concat(kindInfo);
|
||||
const needle = filter.toLocaleLowerCase();
|
||||
const hstack = kindInfo.kind.toLocaleLowerCase();
|
||||
return fuzzysearch(needle, hstack);
|
||||
});
|
||||
|
||||
// If a match is found in the story hierachy structure return kindInfo
|
||||
if (fuzzysearch(needle, hstack)) return acc.concat(kindInfo);
|
||||
|
||||
// Now search at individual story level and filter results
|
||||
const matchedStories = kindInfo.stories.filter(story => {
|
||||
const storyHstack = story.toLocaleLowerCase();
|
||||
return fuzzysearch(needle, storyHstack);
|
||||
});
|
||||
|
||||
if (matchedStories.length)
|
||||
return acc.concat({
|
||||
kind: kindInfo.kind,
|
||||
stories: matchedStories,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
@ -54,5 +54,45 @@ describe('manager.ui.libs.filters', () => {
|
||||
|
||||
expect(res).toEqual([stories[1], stories[2], stories[0]]);
|
||||
});
|
||||
|
||||
test('should filter on story level', () => {
|
||||
const stories = [
|
||||
{ kind: 'aa', stories: ['bb'] },
|
||||
{ kind: 'cc', stories: ['dd'] },
|
||||
{ kind: 'ee', stories: ['ff'] },
|
||||
];
|
||||
const selectedKind = 'aa';
|
||||
const res = storyFilter(stories, 'ff', selectedKind);
|
||||
|
||||
expect(res).toEqual([stories[0], stories[2]]);
|
||||
});
|
||||
|
||||
test('should filter out unmatched stories at lowest level', () => {
|
||||
const stories = [
|
||||
{ kind: 'aa', stories: ['bb'] },
|
||||
{ kind: 'cc', stories: ['dd'] },
|
||||
{ kind: 'ee', stories: ['ff', 'gg'] },
|
||||
];
|
||||
const selectedKind = 'aa';
|
||||
const res = storyFilter(stories, 'ff', selectedKind);
|
||||
|
||||
expect(res).toEqual([stories[0], { kind: 'ee', stories: ['ff'] }]);
|
||||
});
|
||||
|
||||
test('should be case insensitive at tree level', () => {
|
||||
const stories = [{ kind: 'aA', stories: ['bb'] }, { kind: 'cc', stories: ['dd'] }];
|
||||
const selectedKind = 'aA';
|
||||
const res = storyFilter(stories, 'aa', selectedKind);
|
||||
|
||||
expect(res).toEqual([stories[0]]);
|
||||
});
|
||||
|
||||
test('should be case insensitive at story level', () => {
|
||||
const stories = [{ kind: 'aa', stories: ['bb'] }, { kind: 'cc', stories: ['dd', 'eE'] }];
|
||||
const selectedKind = 'aa';
|
||||
const res = storyFilter(stories, 'ee', selectedKind);
|
||||
|
||||
expect(res).toEqual([stories[0], { kind: 'cc', stories: ['eE'] }]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
14
package.json
14
package.json
@ -39,9 +39,9 @@
|
||||
"babel-preset-env": "^1.5.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-0": "^6.24.1",
|
||||
"chalk": "^1.1.3",
|
||||
"chalk": "^2.0.1",
|
||||
"codecov": "^2.2.0",
|
||||
"danger": "^0.21.0",
|
||||
"danger": "^1.0.0",
|
||||
"enzyme": "^2.8.2",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-config-airbnb": "^15.0.1",
|
||||
@ -54,16 +54,14 @@
|
||||
"eslint-plugin-react": "^7.0.1",
|
||||
"gh-pages": "^1.0.0",
|
||||
"github-release-from-changelog": "^1.2.1",
|
||||
"husky": "^0.13.4",
|
||||
"husky": "^0.14.3",
|
||||
"jest": "^20.0.4",
|
||||
"jest-enzyme": "^3.2.0",
|
||||
"lerna": "2.0.0-rc.5",
|
||||
"lerna": "2.0.0",
|
||||
"lint-staged": "^4.0.0",
|
||||
"markdown-it-anchor": "^4.0.0",
|
||||
"markdownlint-cli": "^0.3.1",
|
||||
"nodemon": "^1.11.0",
|
||||
"npmc": "^5.0.3-canary.12",
|
||||
"prettier": "^1.3.1",
|
||||
"npmc": "^5.1.0-canary.2",
|
||||
"prettier": "^1.5.2",
|
||||
"react": "^15.5.4",
|
||||
"react-dom": "^15.5.4",
|
||||
"react-test-renderer": "^15.5.4",
|
||||
|
Loading…
x
Reference in New Issue
Block a user