Merge branch 'add-support-for-importing-markdown-in-storybook-react' of https://github.com/storybooks/storybook into add-support-for-importing-markdown-in-storybook-react

This commit is contained in:
Dan Green 2017-11-15 18:55:51 -05:00
commit dafe0313f5
239 changed files with 6091 additions and 2080 deletions

14
.github/stale.yml vendored
View File

@ -4,11 +4,12 @@ daysUntilStale: 45
daysUntilClose: 15
# Issues with these labels will never be considered stale
exemptLabels:
- bug
- 'help wanted'
- todo
- ready
- 'in progress'
- 'do not merge'
- 'needs review'
- 'high priority'
# Label to use when marking an issue as stale
staleLabel: inactive
@ -16,11 +17,12 @@ staleLabel: inactive
markComment: >
Hi everyone! Seems like there hasn't been much going on in this issue lately.
If there are still questions, comments, or bugs, please feel free to continue
the discussion. We do try to do some housekeeping every once in a while so
inactive issues will get closed after 60 days. Thanks!
the discussion. Unfortunately, we don't have time to get to every issue. We
are always open to contributions so please send us a pull request if you would
like to help. Inactive issues will be closed after 60 days. Thanks!
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
Hey there, it's me again! I am going to help our maintainers close this issue
so they can focus on development efforts instead. If the issue mentioned is
Hey there, it's me again! I am going close this issue to help our maintainers
focus on the current development roadmap instead. If the issue mentioned is
still a concern, please open a new ticket and mention this old one. Cheers
and thanks for using Storybook!

View File

@ -0,0 +1 @@
import '../register';

View File

@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
const styles = {
button: {
padding: '12px 6px',
fontSize: '12px',
lineHeight: '16px',
borderRadius: '5px',
},
ok: {
backgroundColor: '#028402',
color: '#ffffff',
},
wrong: {
color: '#ffffff',
backgroundColor: '#4caf50',
}
}
function Button({ label, content, disabled, contrast }) {
return (
<button
style={{
...styles.button,
...styles[contrast],
}}
disabled={disabled}
>
{ content }
</button>
)
}
Button.propTypes = {
label: PropTypes.string,
content: PropTypes.string,
disabled: PropTypes.bool,
contrast: PropTypes.oneOf(['ok', 'wrong'])
};
Button.defaultProps = {
disabled: false,
contrast: 'ok',
};
export default Button;

View File

@ -0,0 +1,34 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { checkA11y } from './../../../src';
import Button from './component';
import Faker from 'faker';
const text = Faker.lorem.words();
storiesOf('<Button />', module)
.addDecorator(checkA11y)
.add('Default', () => (
<Button />
))
.add('Content', () => (
<Button content={text} />
))
.add('Label', () => (
<Button label={text} />
))
.add('Disabled', () => (
<Button
disabled
content={text}
/>
))
.add('Invalid contrast', () => (
<Button
contrast="wrong"
content={Faker.lorem.words()}
/>
));

View File

@ -0,0 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
function Input({ id, value, type, placeholder }) {
return (
<input
id={id}
value={value}
placeholder={placeholder}
type={type}
/>
);
}
Input.propTypes = {
type: PropTypes.oneOf(['text', 'password']),
id: PropTypes.string,
value: PropTypes.string,
placeholder: PropTypes.string,
}
export default Input;

View File

@ -0,0 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
const styles = {
label: {
padding: '0 6px',
},
}
function Label({ id, content }) {
return (
<label
style={styles.label}
htmlFor={id}
>
{ content }
</label>
)
}
Label.propTypes = {
content: PropTypes.string,
id: PropTypes.string,
};
export default Label;

View File

@ -0,0 +1,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import Label from './Label';
import Input from './Input';
function Row({ label, input }) {
return (
<div>
{label}
{input}
</div>
);
}
Row.propTypes = {
label: PropTypes.instanceOf(Label),
input: PropTypes.instanceOf(Input),
}
export default Row;

View File

@ -0,0 +1,9 @@
import Input from './Input';
import Label from './Label';
import Row from './Row';
export {
Input,
Label,
Row,
};

View File

@ -0,0 +1,36 @@
import React from 'react';
import * as Form from './components';
import { storiesOf } from '@storybook/react';
import { checkA11y } from './../../../src';
import Faker from 'faker';
const label = Faker.lorem.word();
const placeholder = Faker.lorem.word();
storiesOf('<Form />', module)
.addDecorator(checkA11y)
.add('Without Label', () => (
<Form.Row
input={<Form.Input />}
/>
))
.add ('With label', () => (
<Form.Row
label={<Form.Label
content={label}
id="1"
/>}
input={<Form.Input id="1" />}
/>
))
.add ('With placeholder', () => (
<Form.Row
input={<Form.Input
id="1"
placeholder={placeholder}
/>}
/>
))

View File

@ -0,0 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
function Image({ src, alt, presentation }) {
return (
<img
src={src}
alt={alt}
role={presentation && 'presentation'}
/>
);
}
Image.propTypes = {
src: PropTypes.string.isRequired,
alt: PropTypes.string,
presentation: PropTypes.bool,
};
export default Image;

View File

@ -0,0 +1,29 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { checkA11y } from './../../../src';
import Image from './component';
import Faker from 'faker';
const image = Faker.image.animals();
const alt = Faker.lorem.words();
storiesOf('<Image />', module)
.addDecorator(checkA11y)
.add('Without alt', () => (
<Image src={image} />
))
.add('With alt', () => (
<Image
src={image}
alt={alt}
/>
))
.add('Presentation', () => (
<Image
presentation
src={image}
/>
));

View File

@ -0,0 +1,24 @@
import React, { cloneElement } from 'react';
import PropTypes from 'prop-types';
const headings = {
1: (<h1 />),
2: (<h2 />),
3: (<h3 />),
4: (<h4 />),
};
function Heading({ level, children }) {
return cloneElement(headings[level], {}, children)
}
Heading.propTypes = {
level: PropTypes.oneOf([1, 2, 3, 4]),
children: PropTypes.any,
};
Heading.defaultProps = {
level: 1,
};
export default Heading;

View File

@ -0,0 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
function Link({ href, content }) {
return (
<a href={href}>
{ content }
</a>
);
}
Link.propTypes = {
href: PropTypes.string,
content: PropTypes.string,
};
export default Link;

View File

@ -0,0 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
function Text({ children }) {
return (
<p>
{children}
</p>
);
}
Text.propTypes = {
children: PropTypes.any,
};
export default Text;

View File

@ -0,0 +1,9 @@
import Heading from './Heading';
import Link from './Link';
import Text from './Text';
export {
Heading,
Link,
Text,
};

View File

@ -0,0 +1,41 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { checkA11y } from './../../../src';
import * as Typography from './components';
import Faker from 'faker';
const href = "javascript:void 0";
storiesOf('<Typography />', module)
.addDecorator(checkA11y)
.add('Correct', () => (
<div>
<Typography.Heading level={1}>
{Faker.lorem.sentence()}
</Typography.Heading>
<Typography.Text>
{Faker.lorem.paragraph()}
</Typography.Text>
<Typography.Link
content={`${Faker.lorem.words(4)}...`}
href={href}
/>
</div>
))
.add('Empty Heading', () => (
<Typography.Heading level={2} />
))
.add('Empty Paragraph', () => (
<Typography.Text />
))
.add('Empty Link', () => (
<Typography.Link href={href} />
))
.add('Link without href', () => (
<Typography.Link content={`${Faker.lorem.words(4)}...`} />
));

View File

@ -0,0 +1,9 @@
import * as storybook from '@storybook/react';
const req = require.context('./components/', true, /stories\.js$/)
const loadStories = () =>
req.keys().forEach(req);
storybook.configure(loadStories, module)

51
addons/a11y/README.md Executable file
View File

@ -0,0 +1,51 @@
# storybook-addon-a11y
This storybook addon can be helpfull to make your UI components more accessibile.
![](docs/screenshot.png)
## Getting started
First, install the addon.
```sh
$ npm install -D @storybook/addon-a11y
```
Add this line to your `addons.js` file (create this file inside your storybook config directory if needed).
```js
import '@storybook/addon-a11y/register';
```
import the `'checkA11y'` decorator to check your stories for violations within your components.
```js
import React from 'react';
import { storiesOf } from '@storybook/react';
import { checkA11y } from '@storybook/addon-a11y';
storiesOf('button', module)
.addDecorator(checkA11y)
.add('Accessible', () => (
<button>
Accessible button
</button>
))
.add('Inaccessible', () => (
<button style={{ backgroundColor: 'red', color: 'darkRed', }}>
Inaccessible button
</button>
));
```
## Roadmap
* Make UI accessibile
* Add color blindness filters ([Example](http://lowvision.support/))
* Show in story where violations are.
* Make it configurable
* Add more example tests
* Add tests
* Make CI integration possible

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

3
addons/a11y/manager.js Executable file
View File

@ -0,0 +1,3 @@
const manager = require('./dist/register');
manager.init();

36
addons/a11y/package.json Normal file
View File

@ -0,0 +1,36 @@
{
"name": "@storybook/addon-a11y",
"version": "3.3.0-alpha.3",
"description": "a11y addon for storybook",
"keywords": [
"a11y",
"accessibility",
"addon",
"storybook",
"valid",
"verify"
],
"homepage": "https://github.com/storybooks/storybook#readme",
"bugs": {
"url": "https://github.com/storybooks/storybook/issues"
},
"license": "MIT",
"main": "dist/index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/storybooks/storybook.git"
},
"scripts": {
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "^3.3.0-alpha.3",
"@storybook/components": "^3.3.0-alpha.3",
"axe-core": "^2.0.7",
"prop-types": "^15.6.0"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
}
}

1
addons/a11y/register.js Executable file
View File

@ -0,0 +1 @@
require('./manager');

View File

@ -0,0 +1,14 @@
import React from 'react';
import WrapStory from './components/WrapStory';
// Run all a11y checks inside
class A11yManager {
wrapStory(channel, storyFn, context) {
const props = { context, storyFn, channel };
return <WrapStory {...props} />;
}
}
export default A11yManager;

View File

@ -0,0 +1,63 @@
import React, { Component } from 'react';
import addons from '@storybook/addons';
import Tabs from './Tabs';
import Report from './Report';
const styles = {
passes: {
color: '#2ecc71',
},
violations: {
color: '#e74c3c',
},
};
class Panel extends Component {
constructor(props, ...args) {
super(props, ...args);
this.state = {
passes: [],
violations: [],
};
this.channel = addons.getChannel();
this.onUpdate = this.onUpdate.bind(this);
}
componentDidMount() {
this.channel.on('addon:a11y:check', this.onUpdate);
}
componentWillUnmount() {
this.channel.removeListener('addon:a11y:check', this.onUpdate);
}
onUpdate({ passes, violations }) {
this.setState({
passes,
violations,
});
}
render() {
const { passes, violations } = this.state;
return (
<Tabs
tabs={[
{
label: <span style={styles.violations}>Violations</span>,
panel: <Report passes={false} items={violations} empty="No a11y violations found." />,
},
{
label: <span style={styles.passes}>Passes</span>,
panel: <Report passes items={passes} empty="No a11y check passed" />,
},
]}
/>
);
}
}
export default Panel;

View File

@ -0,0 +1,59 @@
import React from 'react';
import PropTypes from 'prop-types';
import Rules from './Rules';
const styles = {
element: {
fontWeight: 600,
},
target: {
borderBottom: '1px solid rgb(130, 130, 130)',
width: '100%',
display: 'inline-block',
paddingBottom: '4px',
marginBottom: '4px',
},
};
function Element({ element, passes }) {
const { any, all, none } = element;
const rules = [...any, ...all, ...none];
return (
<li style={styles.element}>
<span style={styles.target}>{element.target[0]}</span>
<Rules rules={rules} passes={passes} />
</li>
);
}
Element.propTypes = {
element: PropTypes.shape({
any: PropTypes.array.isRequired,
all: PropTypes.array.isRequired,
none: PropTypes.array.isRequired,
}).isRequired,
passes: PropTypes.bool.isRequired,
};
/* eslint-disable react/no-array-index-key */
function Elements({ elements, passes }) {
return (
<ol style={styles.element}>
{elements.map((element, index) => <Element passes={passes} element={element} key={index} />)}
</ol>
);
}
Elements.propTypes = {
elements: PropTypes.arrayOf(
PropTypes.shape({
any: PropTypes.array.isRequired,
all: PropTypes.array.isRequired,
none: PropTypes.array.isRequired,
})
).isRequired,
passes: PropTypes.bool.isRequired,
};
export default Elements;

View File

@ -0,0 +1,39 @@
import React from 'react';
import PropTypes from 'prop-types';
const styles = {
info: {
backgroundColor: 'rgb(234, 234, 234)',
padding: '12px',
marginBottom: '10px',
},
help: {
margin: '0 0 12px',
},
helpUrl: {
marginTop: '12px',
textDecoration: 'underline',
color: 'rgb(130, 130, 130)',
display: 'block',
},
};
function Info({ item }) {
return (
<div style={styles.info}>
<p style={styles.help}>{item.help}</p>
<a style={styles.helpUrl} href={item.helpUrl} target="_blank">
More info...
</a>
</div>
);
}
Info.propTypes = {
item: PropTypes.shape({
help: PropTypes.node,
helpUrl: PropTypes.string,
}).isRequired,
};
export default Info;

View File

@ -0,0 +1,63 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Info from './Info';
import Tags from './Tags';
import Elements from './Elements';
const styles = {
item: {
padding: '0 14px',
cursor: 'pointer',
borderBottom: '1px solid rgb(234, 234, 234)',
},
headerBar: {
padding: '12px 0px',
display: 'block',
width: '100%',
border: 0,
background: 'none',
},
};
class Item extends Component {
static propTypes = {
item: PropTypes.shape({
description: PropTypes.string,
nodes: PropTypes.array,
tags: PropTypes.array,
}).isRequired,
passes: PropTypes.bool.isRequired,
};
constructor() {
super();
this.state = {
open: false,
};
}
onToggle = () =>
this.setState(prevState => ({
open: !prevState.open,
}));
render() {
const { item, passes } = this.props;
const { open } = this.state;
return (
<div style={styles.item}>
<button style={styles.headerBar} onClick={() => this.onToggle()}>
{item.description}
</button>
{open && <Info item={item} />}
{open && <Elements elements={item.nodes} passes={passes} />}
{open && <Tags tags={item.tags} />}
</div>
);
}
}
export default Item;

View File

@ -0,0 +1,83 @@
import React from 'react';
import PropTypes from 'prop-types';
const impactColors = {
minor: '#f1c40f',
moderate: '#e67e22',
serious: '#e74c3c',
critical: '#c0392b',
success: '#2ecc71',
};
const styles = {
rules: {
display: 'flex',
flexDirection: 'column',
padding: '4px',
fontWeight: '400',
},
rule: {
display: 'flex',
flexDirection: 'row',
marginBottom: '6px',
},
status: {
height: '16px',
width: '16px',
borderRadius: '8px',
fontSize: '10px',
display: 'inline-flex',
justifyContent: 'center',
alignItems: 'center',
color: '#fff',
textAlign: 'center',
flex: '0 0 16px',
},
message: {
paddingLeft: '6px',
},
};
function Rule({ rule, passes }) {
const color = passes ? impactColors.success : impactColors[rule.impact];
return (
<div style={styles.rule}>
<div
style={{
...styles.status,
backgroundColor: color,
}}
>
{passes ? '✔' : '✘'}
</div>
<span style={styles.message}>{rule.message}</span>
</div>
);
}
Rule.propTypes = {
rule: PropTypes.shape({
message: PropTypes.node,
}).isRequired,
passes: PropTypes.bool.isRequired,
};
/* eslint-disable react/no-array-index-key */
function Rules({ rules, passes }) {
return (
<div style={styles.rules}>
{rules.map((rule, index) => <Rule passes={passes} rule={rule} key={index} />)}
</div>
);
}
Rules.propTypes = {
rules: PropTypes.arrayOf(
PropTypes.shape({
message: PropTypes.node,
})
).isRequired,
passes: PropTypes.bool.isRequired,
};
export default Rules;

View File

@ -0,0 +1,35 @@
import React from 'react';
import PropTypes from 'prop-types';
const styles = {
tags: {
display: 'flex',
flexWrap: 'wrap',
margin: '12px 0',
},
tag: {
margin: '0 6px',
padding: '5px',
border: '1px solid rgb(234, 234, 234)',
borderRadius: '2px',
color: 'rgb(130, 130, 130)',
fontSize: '12px',
},
};
function Tags({ tags }) {
return (
<div style={styles.tags}>
{tags.map(tag => (
<div key={tag} style={styles.tag}>
{tag}
</div>
))}
</div>
);
}
Tags.propTypes = {
tags: PropTypes.arrayOf(PropTypes.node).isRequired,
};
export default Tags;

View File

@ -0,0 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import Item from './Item';
const styles = {
container: {
fontSize: '12px',
},
empty: {
fontSize: '11px',
padding: '20px 12px',
width: '100%',
display: 'block',
textAlign: 'center',
textTransform: 'uppercase',
},
};
function Report({ items, empty, passes }) {
if (items.length) {
return (
<div style={styles.container}>
{items.map(item => <Item passes={passes} item={item} key={item.id} />)}
</div>
);
}
return <span style={styles.empty}>{empty}</span>;
}
Report.propTypes = {
items: PropTypes.arrayOf(
PropTypes.shape({
description: PropTypes.string,
nodes: PropTypes.array,
tags: PropTypes.array,
})
).isRequired,
empty: PropTypes.string.isRequired,
passes: PropTypes.bool.isRequired,
};
export default Report;

View File

@ -0,0 +1,105 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { baseFonts } from '@storybook/components';
const styles = {
container: {
width: '100%',
...baseFonts,
},
tabs: {
borderBottom: '1px solid rgb(234, 234, 234)',
flexWrap: 'wrap',
display: 'flex',
},
tab: {
color: 'rgb(68, 68, 68)',
fontSize: '11px',
textDecoration: 'none',
textTransform: 'uppercase',
padding: '10px 15px',
letterSpacing: '1px',
cursor: 'pointer',
fontWeight: 500,
opacity: 0.7,
border: 'none',
background: 'none',
flex: 1,
},
tabActive: {
opacity: 1,
fontWeight: 600,
},
};
const tabStyle = active => ({
...styles.tab,
...(active ? styles.tabActive : undefined),
});
class Tabs extends Component {
constructor(props) {
super(props);
this.state = {
active: 0,
};
this.onToggle = this.onToggle.bind(this);
this.renderPanel = this.renderPanel.bind(this);
this.renderTabs = this.renderTabs.bind(this);
}
onToggle(index) {
this.setState({
active: index,
});
}
renderPanel() {
const { tabs } = this.props;
const { active } = this.state;
return <div style={styles.panel}>{tabs[active].panel}</div>;
}
renderTabs() {
const { tabs } = this.props;
const { active } = this.state;
/* eslint-disable react/no-array-index-key */
return (
<div style={styles.tabs}>
{tabs.map((tab, index) => (
<button
key={index}
style={tabStyle(active === index)}
onClick={() => this.onToggle(index)}
>
{tab.label}
</button>
))}
</div>
);
}
render() {
return (
<div style={styles.container}>
{this.renderTabs()}
{this.renderPanel()}
</div>
);
}
}
Tabs.propTypes = {
tabs: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.node,
panel: PropTypes.node,
})
).isRequired,
};
export default Tabs;

View File

@ -0,0 +1,37 @@
import { Component } from 'react';
import { findDOMNode } from 'react-dom';
import PropTypes from 'prop-types';
import axe from 'axe-core';
class WrapStory extends Component {
static propTypes = {
context: PropTypes.shape({}),
storyFn: PropTypes.func,
channel: PropTypes.shape({}),
};
static defaultProps = {
context: {},
storyFn: () => {},
channel: {},
};
/* eslint-disable react/no-find-dom-node */
componentDidMount() {
const { channel } = this.props;
const wrapper = findDOMNode(this);
if (wrapper !== null) {
axe.a11yCheck(wrapper, {}, results => {
channel.emit('addon:a11y:check', results);
});
}
}
render() {
const { storyFn, context } = this.props;
return storyFn(context);
}
}
export default WrapStory;

13
addons/a11y/src/index.js Normal file
View File

@ -0,0 +1,13 @@
import addons from '@storybook/addons';
import A11yManager from './A11yManager';
import * as shared from './shared';
const manager = new A11yManager();
function checkA11y(storyFn, context) {
const channel = addons.getChannel();
return manager.wrapStory(channel, storyFn, context);
}
export { checkA11y, shared };

View File

@ -0,0 +1,18 @@
import React from 'react';
import addons from '@storybook/addons';
import Panel from './components/Panel';
import { ADDON_ID, PANEL_ID } from './shared';
function init() {
addons.register(ADDON_ID, () => {
addons.addPanel(PANEL_ID, {
title: 'Accessibility',
render() {
return <Panel />;
},
});
});
}
export { init };

View File

@ -0,0 +1,6 @@
// addons, panels and events get unique names using a prefix
const ADDON_ID = '@storybook/addon-a11y';
const PANEL_ID = `${ADDON_ID}/panel`;
const EVENT_ID = `${ADDON_ID}/event`;
export { ADDON_ID, PANEL_ID, EVENT_ID };

View File

@ -27,12 +27,6 @@
"react-inspector": "^2.2.1",
"uuid": "^3.1.0"
},
"devDependencies": {
"react": "^16.1.0",
"react-dom": "^16.1.0",
"react-test-renderer": "^16.1.0",
"shelljs": "^0.7.8"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"

View File

@ -24,13 +24,9 @@
"babel-runtime": "^6.26.0",
"format-json": "^1.0.3",
"prop-types": "^15.6.0",
"react-textarea-autosize": "^5.2.0",
"react-textarea-autosize": "^5.2.1",
"uuid": "^3.1.0"
},
"devDependencies": {
"react": "^16.1.0",
"react-dom": "^16.1.0"
},
"peerDependencies": {
"react": "*"
}

View File

@ -26,11 +26,6 @@
"graphql": "^0.11.7",
"prop-types": "^15.6.0"
},
"devDependencies": {
"react": "^16.1.0",
"react-dom": "^16.1.0",
"shelljs": "^0.7.8"
},
"peerDependencies": {
"react": "*"
}

View File

@ -24,11 +24,10 @@
"util-deprecate": "^1.0.2"
},
"devDependencies": {
"react": "^16.1.0",
"react-dom": "^16.1.0",
"react-test-renderer": "^16.1.0"
},
"peerDependencies": {
"react": "*"
"react": "*",
"react-dom": "*"
}
}

View File

@ -21,17 +21,15 @@
"global": "^4.3.2",
"insert-css": "^2.0.0",
"lodash.debounce": "^4.0.8",
"moment": "^2.19.1",
"moment": "^2.19.2",
"prop-types": "^15.6.0",
"react-color": "^2.11.4",
"react-datetime": "^2.10.3",
"react-textarea-autosize": "^5.2.0",
"react-datetime": "^2.11.0",
"react-textarea-autosize": "^5.2.1",
"util-deprecate": "^1.0.2"
},
"devDependencies": {
"raw-loader": "^0.5.1",
"react": "^16.1.0",
"react-dom": "^16.1.0",
"style-loader": "^0.19.0",
"vue": "^2.5.3"
},

View File

@ -24,11 +24,6 @@
"prop-types": "^15.6.0",
"util-deprecate": "^1.0.2"
},
"devDependencies": {
"react": "^16.1.0",
"react-addons-test-utils": "^15.5.1",
"react-dom": "^16.1.0"
},
"peerDependencies": {
"react": "*"
},

View File

@ -22,12 +22,6 @@
"dependencies": {
"@storybook/addons": "^3.3.0-alpha.3"
},
"devDependencies": {
"react": "^16.1.0",
"react-dom": "^16.1.0",
"react-test-renderer": "^16.1.0",
"shelljs": "^0.7.8"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"

View File

@ -72,9 +72,6 @@
"ws": "^3.3.1"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"react": "^16.1.0",
"react-dom": "^16.1.0",
"react-native": "^0.50.3"
},
"peerDependencies": {

View File

@ -27,6 +27,8 @@ const isDeviceInPortrait = () => {
const openMenuImage = require('./menu_open.png');
const closeMenuImage = require('./menu_close.png');
const DRAWER_WIDTH = 250;
export default class OnDeviceUI extends Component {
constructor(...args) {
super(...args);
@ -36,7 +38,6 @@ export default class OnDeviceUI extends Component {
isMenuOpen: false,
selectedKind: null,
selectedStory: null,
menuWidth: Dimensions.get('screen').width / 2,
isPortrait: isDeviceInPortrait(),
};
}
@ -58,7 +59,6 @@ export default class OnDeviceUI extends Component {
handleDeviceRotation = () => {
this.setState({
isPortrait: isDeviceInPortrait(),
menuWidth: Dimensions.get('screen').width / 2,
});
};
@ -86,7 +86,7 @@ export default class OnDeviceUI extends Component {
render() {
const { stories, events, url } = this.props;
const { isPortrait, menuAnimation, selectedKind, selectedStory, menuWidth } = this.state;
const { isPortrait, menuAnimation, selectedKind, selectedStory } = this.state;
const iPhoneXStyles = ifIphoneX(
isPortrait
@ -106,7 +106,7 @@ export default class OnDeviceUI extends Component {
{
translateX: menuAnimation.interpolate({
inputRange: [0, 1],
outputRange: [menuWidth * -1, 0],
outputRange: [-DRAWER_WIDTH - 30, 0],
}),
},
],
@ -177,6 +177,7 @@ export default class OnDeviceUI extends Component {
<StoryListView
stories={stories}
events={events}
width={DRAWER_WIDTH}
selectedKind={selectedKind}
selectedStory={selectedStory}
/>

View File

@ -1,7 +1,6 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { ListView, View, Text, TouchableOpacity } from 'react-native';
import { MinMaxView } from 'react-native-compat';
import style from './style';
const SectionHeader = ({ title, selected }) => (
@ -92,25 +91,23 @@ export default class StoryListView extends Component {
render() {
return (
<MinMaxView maxWidth={250}>
<ListView
style={style.list}
renderRow={item => (
<ListItem
title={item.name}
selected={
item.kind === this.props.selectedKind && item.name === this.props.selectedStory
}
onPress={() => this.changeStory(item.kind, item.name)}
/>
)}
renderSectionHeader={(sectionData, sectionName) => (
<SectionHeader title={sectionName} selected={sectionName === this.props.selectedKind} />
)}
dataSource={this.state.dataSource}
stickySectionHeadersEnabled={false}
/>
</MinMaxView>
<ListView
style={[style.list, { width: this.props.width }]}
renderRow={item => (
<ListItem
title={item.name}
selected={
item.kind === this.props.selectedKind && item.name === this.props.selectedStory
}
onPress={() => this.changeStory(item.kind, item.name)}
/>
)}
renderSectionHeader={(sectionData, sectionName) => (
<SectionHeader title={sectionName} selected={sectionName === this.props.selectedKind} />
)}
dataSource={this.state.dataSource}
stickySectionHeadersEnabled={false}
/>
);
}
}
@ -129,6 +126,7 @@ StoryListView.propTypes = {
}).isRequired,
selectedKind: PropTypes.string,
selectedStory: PropTypes.string,
width: PropTypes.number.isRequired,
};
StoryListView.defaultProps = {

View File

@ -78,10 +78,7 @@
"webpack-hot-middleware": "^2.20.0"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"nodemon": "^1.12.1",
"react": "^16.1.0",
"react-dom": "^16.1.0"
"nodemon": "^1.12.1"
},
"peerDependencies": {
"react": ">=15.0.0 || ^16.0.0",

View File

@ -77,7 +77,6 @@
"webpack-hot-middleware": "^2.20.0"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"nodemon": "^1.12.1",
"vue": "^2.5.3",
"vue-loader": "^13.5.0",

View File

@ -1,8 +1,8 @@
const warn = 1;
module.exports = {
settings: {
'import/core-modules': ['config'],
globals: {
graphql: false,
},
rules: {
'import/no-unresolved': warn,

View File

@ -4,10 +4,10 @@
// This file is auto-written and used by Gatsby to require
// files from your pages directory.
module.exports = function (callback) {
var context = require.context('./pages', true, /(coffee|cjsx|ts|tsx|jsx|js|md|rmd|mkdn?|mdwn|mdown|markdown|litcoffee|ipynb|html|json|yaml|toml)$/ // eslint-disable-line
var context = require.context('./src/pages', true, /(coffee|cjsx|ts|tsx|jsx|js|md|rmd|mkdn?|mdwn|mdown|markdown|litcoffee|ipynb|html|json|yaml|toml)$/ // eslint-disable-line
);if (module.hot) {
module.hot.accept(context.id, function () {
context = require.context('./pages', true, /(coffee|cjsx|ts|tsx|jsx|js|md|rmd|mkdn?|mdwn|mdown|markdown|litcoffee|ipynb|html|json|yaml|toml)$/ // eslint-disable-line
context = require.context('./src/pages', true, /(coffee|cjsx|ts|tsx|jsx|js|md|rmd|mkdn?|mdwn|mdown|markdown|litcoffee|ipynb|html|json|yaml|toml)$/ // eslint-disable-line
);return callback(context);
});
}

View File

@ -1,10 +1,14 @@
import { configure } from '@storybook/react';
import React from 'react';
import { configure, addDecorator } from '@storybook/react';
import { MemoryRouter } from 'react-router'
import 'bootstrap/dist/css/bootstrap.css';
import '../css/main.css';
import '../src/css/main.css';
addDecorator(story => <MemoryRouter>{story()}</MemoryRouter>);
function loadStories() {
require('../stories');
require('../src/stories');
}
configure(loadStories, module);

View File

@ -0,0 +1,2 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css"></link>
<script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>

View File

@ -1,119 +0,0 @@
import React from 'react';
import { Link } from 'react-router';
import { UsedByBg } from '../UsedBy/';
import './style.css';
const MainLinks = () => (
<div id="main-links">
<div className="main-links-container">
<div className="try-now">
<h2>Try Now</h2>
<pre>
<code>
npm i -g @storybook/cli<br />
cd my-react-app<br />
getstorybook<br />
</code>
</pre>
</div>
<div
className="row"
id="nav"
style={{
borderRight: '0 none',
position: 'relative',
}}
>
<UsedByBg
color="#a7d0ff"
style={{
transform: 'rotateY(180deg)',
}}
/>
<div className="col-xs-12 read-docs">
<h2 style={{ color: '#6DABF5' }}>Documentation</h2>
<div
className="form-group has-feedback"
style={{ maxWidth: '450px', margin: '20px auto' }}
>
<label className="sr-only control-label" htmlFor="search">
Search storybook documentation
</label>
<input
className="form-control"
type="search"
id="search"
placeholder="type to search"
/>
<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>
<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>
</ul>
</div>
<div className="col-sm-4 read-docs">
<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-webpack-config/">Webpack configurations</Link>
</li>
<li>
<Link to="/configurations/add-custom-head-tags/">Custom scripts & styling</Link>
</li>
<li>
<Link to="/configurations/serving-static-files/">Serving static files</Link>
</li>
</ul>
</div>
<div className="col-sm-4 read-docs">
<Link to="/configurations/default-config/">
<h3>Addons</h3>
</Link>
<ul>
<li>
<Link to="/addons/introduction/">Intro to Addons</Link>
</li>
<li>
<Link to="/addons/using-addons/">Using Addons</Link>
</li>
<li>
<Link to="/addons/addon-gallery/">Addon Gallery</Link>
</li>
<li>
<Link to="/addons/writing-addons/">Writing Addons</Link>
</li>
<li>
<Link to="/addons/api/">Api</Link>
</li>
</ul>
</div>
</div>
</div>
</div>
);
export default MainLinks;

View File

@ -1,39 +0,0 @@
siteTitle = "Storybook"
baseColor = "#e64074"
linkPrefix = "/"
[docSections]
basics = [
"/basics/introduction/",
"/basics/quick-start-guide/",
"/basics/slow-start-guide/",
"/basics/guide-react/",
"/basics/guide-vue/",
"/basics/writing-stories/",
"/basics/exporting-storybook/",
"/basics/faq/",
"/basics/community/",
]
configurations = [
"/configurations/default-config/",
"/configurations/custom-webpack-config/",
"/configurations/custom-babel-config/",
"/configurations/add-custom-head-tags/",
"/configurations/serving-static-files/",
"/configurations/env-vars/",
"/configurations/cli-options/",
]
testing = [
"/testing/react-ui-testing/",
"/testing/structural-testing/",
"/testing/interaction-testing/",
"/testing/css-style-testing/",
"/testing/manual-testing/",
]
addons = [
"/addons/introduction/",
"/addons/using-addons/",
"/addons/addon-gallery/",
"/addons/writing-addons/",
"/addons/api/",
]

70
docs/gatsby-config.js Normal file
View File

@ -0,0 +1,70 @@
module.exports = {
siteMetadata: {
siteTitle: 'Storybook',
baseColor: '#e64074',
linkPrefix: '/',
docSections: {
basics: [
'/basics/introduction/',
'/basics/quick-start-guide/',
'/basics/slow-start-guide/',
'/basics/guide-react/',
'/basics/guide-vue/',
'/basics/writing-stories/',
'/basics/exporting-storybook/',
'/basics/faq/',
'/basics/community/',
],
configurations: [
'/configurations/default-config/',
'/configurations/custom-webpack-config/',
'/configurations/custom-babel-config/',
'/configurations/add-custom-head-tags/',
'/configurations/serving-static-files/',
'/configurations/env-vars/',
'/configurations/cli-options/',
],
testing: [
'/testing/react-ui-testing/',
'/testing/structural-testing/',
'/testing/interaction-testing/',
'/testing/css-style-testing/',
'/testing/manual-testing/',
],
addons: [
'/addons/introduction/',
'/addons/using-addons/',
'/addons/addon-gallery/',
'/addons/writing-addons/',
'/addons/api/',
],
},
},
plugins: [
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'pages',
path: `${__dirname}/src/pages/`,
},
},
{
resolve: 'gatsby-transformer-remark',
options: {
plugins: [
{
resolve: 'gatsby-remark-images',
options: {
maxWidth: 690,
},
},
'gatsby-remark-autolink-headers',
'gatsby-remark-copy-linked-files',
'gatsby-remark-smartypants',
],
},
},
'gatsby-plugin-sharp',
],
};

View File

@ -1,6 +1,7 @@
// From: https://gist.github.com/ivanoats/8d01d9e934fdc17bae9090147f1e799b
const fs = require('fs');
const path = require('path');
const sm = require('sitemap');
function pagesToSitemap(pages) {
@ -21,8 +22,71 @@ function generateSitemap(pages) {
}
module.exports = {
postBuild(pages, callback) {
generateSitemap(pages);
callback();
async onPostBuild({ graphql }) {
const result = await graphql(`
{
allSitePage {
edges {
node {
path
}
}
}
}
`);
generateSitemap(result.data.allSitePage.edges.map(({ node }) => node));
},
onCreateNode({ node, boundActionCreators, getNode }) {
const { createNodeField } = boundActionCreators;
let slug;
if (node.internal.type === 'MarkdownRemark') {
const fileNode = getNode(node.parent);
const parsedFilePath = path.parse(fileNode.relativePath);
if (parsedFilePath.name !== 'index' && parsedFilePath.dir !== '') {
slug = `/${parsedFilePath.dir}/${parsedFilePath.name}/`;
} else if (parsedFilePath.dir === '') {
slug = `/${parsedFilePath.name}/`;
} else {
slug = `/${parsedFilePath.dir}/`;
}
// Add slug as a field on the node.
createNodeField({ node, name: 'slug', value: slug });
}
},
async createPages({ graphql, boundActionCreators }) {
const { createPage } = boundActionCreators;
const template = path.resolve('src/templates/_docstemplate.jsx');
// Query for all markdown "nodes" and for the slug we previously created.
const result = await graphql(
`
{
allMarkdownRemark {
edges {
node {
fields {
slug
}
}
}
}
}
`
);
if (result.errors) {
throw result.errors;
}
// Create pages.
result.data.allMarkdownRemark.edges.forEach(edge => {
createPage({
path: edge.node.fields.slug, // required
component: template,
context: {
slug: edge.node.fields.slug,
},
});
});
},
};

View File

@ -1,60 +0,0 @@
// see also: https://github.com/gatsbyjs/gatsby-starter-kitchen-sink/blob/master/loaders/markdown-loader/index.js
const frontMatter = require('front-matter');
const markdownIt = require('markdown-it');
const hljs = require('highlight.js');
const path = require('path');
const loaderUtils = require('loader-utils');
const logger = console;
const highlight = (str, lang) => {
if (lang !== null && hljs.getLanguage(lang)) {
try {
return hljs.highlight(lang, str).value;
} catch (error) {
logger.error(error);
}
}
try {
return hljs.highlightAuto(str).value;
} catch (error) {
logger.error(error);
}
return '';
};
const md = (linkPrefix, shouldPrefix) =>
markdownIt({
html: true,
linkify: true,
typographer: true,
highlight,
replaceLink: link => {
if (shouldPrefix && path.isAbsolute(link)) {
return linkPrefix + link;
}
return link;
},
})
.use(require('markdown-it-replace-link')) // eslint-disable-line
.use(require('markdown-it-anchor'), { // eslint-disable-line
permalink: true,
permalinkSymbol: '⚓️',
});
module.exports = function markdownLoader(content) {
this.cacheable();
const query = loaderUtils.parseQuery(this.query);
const linkPrefix = query.config.linkPrefix || '';
const { shouldPrefix } = query;
const meta = frontMatter(content);
const body = md(linkPrefix, shouldPrefix).render(meta.body);
const result = Object.assign({}, meta.attributes, {
body,
});
this.value = result;
return `module.exports = ${JSON.stringify(result)}`;
};

View File

@ -14,7 +14,8 @@
"deploy:ci": "gh-pages -t -r https://${GH_TOKEN}@github.com/storybooks/storybook.git -d public -o origin -b gh-pages",
"deploy:manual": "gh-pages -t -r git@github.com:storybooks/storybook.git -d public -o origin -b gh-pages",
"dev": "gatsby develop",
"storybook": "start-storybook -p 9009 -s pages"
"serve": "gatsby serve",
"storybook": "start-storybook -p 9009 -s src/pages"
},
"dependencies": {
"@storybook/addon-actions": "^3.2.15",
@ -29,35 +30,28 @@
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"bootstrap": "^3.3.7",
"chroma-js": "^0.7.8",
"color-pairs-picker": "^1.3.5",
"docsearch.js": "^2.3.3",
"front-matter": "^2.1.2",
"gatsby": "0.12.48",
"gatsby": "^1.9.112",
"gatsby-link": "^1.6.28",
"gatsby-plugin-sharp": "^1.6.21",
"gatsby-remark-autolink-headers": "^1.4.8",
"gatsby-remark-copy-linked-files": "^1.5.21",
"gatsby-remark-images": "^1.5.32",
"gatsby-remark-smartypants": "^1.4.8",
"gatsby-source-filesystem": "^1.5.8",
"gatsby-transformer-remark": "^1.7.21",
"gh-pages": "^1.0.0",
"global": "^4.3.2",
"highlight.js": "^9.12.0",
"loader-utils": "^1.1.0",
"lodash": "^4.17.2",
"markdown-it": "^8.3.1",
"markdown-it-anchor": "^4.0.0",
"markdown-it-replace-link": "^1.0.1",
"marked": "^0.3.6",
"prop-types": "^15.6.0",
"react": "^15.6.1",
"react-document-title": "^2.0.3",
"react-dom": "^15.6.1",
"react-helmet": "^5.0.3",
"react-motion": "^0.5.2",
"react-responsive-grid": "^0.3.3",
"react-router": "^2.6.1",
"react-stack-grid": "^0.5.0",
"react-typography": "^0.16.5",
"sitemap": "^1.12.0",
"typography": "^0.16.6",
"typography-plugin-code": "^0.15.9",
"underscore.string": "^3.2.2",
"webpack": "^1.15.0"
"react-router": "^4.2.0",
"react-stack-grid": "^0.6.0",
"sitemap": "^1.12.0"
},
"private": true
}

View File

@ -1,70 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import capitalize from 'lodash/capitalize';
import Docs from '../components/Docs';
import { config } from '../config.toml';
const categories = [
{
id: 'react-storybook',
title: 'React Storybook',
},
];
const getSections = (catId, options, pages) => {
// FIXME: use catId
const sections = Object.keys(options.docSections);
return sections.map(key => ({
id: key,
heading: capitalize(key),
items: options.docSections[key].map(path => {
const page = pages.find(p => p.path === path);
return page.data;
}),
}));
};
const getSelectedItem = (children, sectionId) => {
const { data } = children.props.route.page;
return {
id: data.id,
section: sectionId,
title: data.title,
content: data.body,
};
};
const parsePath = path => {
const comps = path.split('/');
const [, itemId, sectionId, catId] = comps.reverse();
return { catId, sectionId, itemId };
};
const DocsContainer = props => {
const { pages } = props.route;
const { children } = props;
const { catId, sectionId, itemId } = parsePath(children.props.route.path);
const docProps = {
categories,
selectedCatId: catId,
sections: getSections(catId, config, pages),
selectedItem: getSelectedItem(children, sectionId),
selectedSectionId: sectionId,
selectedItemId: itemId,
};
return <Docs {...docProps} />;
};
DocsContainer.propTypes = {
location: PropTypes.object, // eslint-disable-line
route: PropTypes.object, // eslint-disable-line
children: PropTypes.node.isRequired,
};
DocsContainer.contextTypes = {
router: PropTypes.object.isRequired,
};
export default DocsContainer;

View File

@ -1 +0,0 @@
export { default } from '../_docstemplate';

View File

@ -1 +0,0 @@
export { default } from '../_docstemplate';

View File

@ -1 +0,0 @@
export { default } from '../_docstemplate';

View File

@ -1 +0,0 @@
export { default } from '../_docstemplate';

View File

@ -1,9 +1,14 @@
import PropTypes from 'prop-types';
import React from 'react';
import { browserHistory } from 'react-router';
import { Redirect } from 'react-router';
import './style.css';
class Nav extends React.Component {
constructor(...args) {
super(...args);
this.state = {};
}
handleHeadingChange(event) {
const { sections } = this.props;
const selectedSectionId = event.target.value;
@ -20,7 +25,7 @@ class Nav extends React.Component {
changeRoute(selectedSectionId, selectedItemId) {
const url = `/${selectedSectionId}/${selectedItemId}/`;
browserHistory.push(url);
this.setState({ redirect: url });
}
renderNavOpts(nav) {
@ -46,7 +51,9 @@ class Nav extends React.Component {
const selectedSectionData = sections.find(section => section.id === selectedSectionId);
const navs = selectedSectionData.items;
return (
return this.state.redirect ? (
<Redirect to={this.state.redirect} />
) : (
<div>
<div>
<select value={selectedSectionId} onChange={event => this.handleHeadingChange(event)}>

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import Link from 'gatsby-link';
import './style.css';
const Nav = ({ sections, selectedSectionId, selectedItemId }) => (

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Link } from 'react-router';
import Link from 'gatsby-link';
import slackIcon from './images/slack-icon.png';
import githubIcon from './images/github-icon.png';
import './style.css';
@ -14,12 +14,20 @@ const Footer = () => (
.
</center>
<center>
<Link to="https://now-examples-slackin-nqnzoygycp.now.sh/" target="_blank">
<a
href="https://now-examples-slackin-nqnzoygycp.now.sh/"
target="_blank"
rel="noreferrer noopener"
>
<img src={slackIcon} alt="Storybook Slack" />
</Link>
<Link to="https://github.com/storybooks/storybook" target="_blank">
</a>
<a
href="https://github.com/storybooks/storybook"
target="_blank"
rel="noreferrer noopener"
>
<img src={githubIcon} alt="Storybook GitHub" style={{ padding: '7px' }} />
</Link>
</a>
</center>
</div>
</div>

View File

@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import { Link } from 'react-router';
import Link from 'gatsby-link';
import './style.css';
import storybookLogo from '../../design/homepage/storybook-logo.svg';

View File

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,135 @@
import React from 'react';
import Link from 'gatsby-link';
import { window } from 'global';
import '../../Docs/Nav/style.css';
import { UsedByBg } from '../UsedBy/';
import './style.css';
class MainLinks extends React.Component {
componentDidMount() {
window.docsearch({
apiKey: 'a4f7f972f1d8f99a66e237e7fd2e489f',
indexName: 'storybook-js',
inputSelector: '#search',
debug: false, // Set debug to true if you want to inspect the dropdown
});
}
render() {
return (
<div id="main-links">
<div className="main-links-container">
<div className="try-now">
<h2>Try Now</h2>
<pre>
<code>
npm i -g @storybook/cli<br />
cd my-react-app<br />
getstorybook<br />
</code>
</pre>
</div>
<div
className="row"
id="nav"
style={{
borderRight: '0 none',
position: 'relative',
}}
>
<UsedByBg
color="#a7d0ff"
style={{
transform: 'rotateY(180deg)',
}}
/>
<div className="col-xs-12 read-docs">
<h2 style={{ color: '#6DABF5' }}>Documentation</h2>
<div
className="form-group has-feedback"
style={{ maxWidth: '450px', margin: '20px auto' }}
>
<label className="sr-only control-label" htmlFor="search">
Search storybook documentation
</label>
<input
className="form-control"
type="search"
id="search"
placeholder="type to search"
/>
<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>
<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>
</ul>
</div>
<div className="col-sm-4 read-docs">
<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-webpack-config/">Webpack configurations</Link>
</li>
<li>
<Link to="/configurations/add-custom-head-tags/">Custom scripts & styling</Link>
</li>
<li>
<Link to="/configurations/serving-static-files/">Serving static files</Link>
</li>
</ul>
</div>
<div className="col-sm-4 read-docs">
<Link to="/configurations/default-config/">
<h3>Addons</h3>
</Link>
<ul>
<li>
<Link to="/addons/introduction/">Intro to Addons</Link>
</li>
<li>
<Link to="/addons/using-addons/">Using Addons</Link>
</li>
<li>
<Link to="/addons/addon-gallery/">Addon Gallery</Link>
</li>
<li>
<Link to="/addons/writing-addons/">Writing Addons</Link>
</li>
<li>
<Link to="/addons/api/">Api</Link>
</li>
</ul>
</div>
</div>
</div>
</div>
);
}
}
export default MainLinks;

View File

@ -1,6 +1,6 @@
import PropTypes from 'prop-types';
import React from 'react';
import { Link } from 'react-router';
import Link from 'gatsby-link';
import './style.css';
export const UsedByBg = ({ color, style }) => (

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