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
14
.github/stale.yml
vendored
@ -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!
|
||||
|
1
addons/a11y/.storybook/addons.js
Executable file
@ -0,0 +1 @@
|
||||
import '../register';
|
47
addons/a11y/.storybook/components/Button/component.js
Normal 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;
|
34
addons/a11y/.storybook/components/Button/stories.js
Normal 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()}
|
||||
/>
|
||||
));
|
22
addons/a11y/.storybook/components/Form/components/Input.js
Normal 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;
|
26
addons/a11y/.storybook/components/Form/components/Label.js
Normal 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;
|
21
addons/a11y/.storybook/components/Form/components/Row.js
Normal 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;
|
@ -0,0 +1,9 @@
|
||||
import Input from './Input';
|
||||
import Label from './Label';
|
||||
import Row from './Row';
|
||||
|
||||
export {
|
||||
Input,
|
||||
Label,
|
||||
Row,
|
||||
};
|
36
addons/a11y/.storybook/components/Form/stories.js
Normal 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}
|
||||
/>}
|
||||
/>
|
||||
))
|
20
addons/a11y/.storybook/components/Image/component.js
Normal 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;
|
29
addons/a11y/.storybook/components/Image/stories.js
Normal 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}
|
||||
/>
|
||||
));
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -0,0 +1,9 @@
|
||||
import Heading from './Heading';
|
||||
import Link from './Link';
|
||||
import Text from './Text';
|
||||
|
||||
export {
|
||||
Heading,
|
||||
Link,
|
||||
Text,
|
||||
};
|
41
addons/a11y/.storybook/components/Typography/stories.js
Normal 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)}...`} />
|
||||
));
|
9
addons/a11y/.storybook/config.js
Executable 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
@ -0,0 +1,51 @@
|
||||
# storybook-addon-a11y
|
||||
|
||||
This storybook addon can be helpfull to make your UI components more accessibile.
|
||||
|
||||

|
||||
|
||||
## 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
|
BIN
addons/a11y/docs/screenshot.png
Normal file
After Width: | Height: | Size: 342 KiB |
3
addons/a11y/manager.js
Executable file
@ -0,0 +1,3 @@
|
||||
const manager = require('./dist/register');
|
||||
|
||||
manager.init();
|
36
addons/a11y/package.json
Normal 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
@ -0,0 +1 @@
|
||||
require('./manager');
|
14
addons/a11y/src/A11yManager.js
Normal 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;
|
63
addons/a11y/src/components/Panel.js
Normal 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;
|
59
addons/a11y/src/components/Report/Elements.js
Normal 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;
|
39
addons/a11y/src/components/Report/Info.js
Normal 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;
|
63
addons/a11y/src/components/Report/Item.js
Normal 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;
|
83
addons/a11y/src/components/Report/Rules.js
Normal 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;
|
35
addons/a11y/src/components/Report/Tags.js
Normal 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;
|
44
addons/a11y/src/components/Report/index.js
Normal 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;
|
105
addons/a11y/src/components/Tabs.js
Normal 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;
|
37
addons/a11y/src/components/WrapStory.js
Normal 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
@ -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 };
|
18
addons/a11y/src/register.js
Normal 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 };
|
6
addons/a11y/src/shared/index.js
Executable 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 };
|
@ -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": "*"
|
||||
|
@ -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": "*"
|
||||
}
|
||||
|
@ -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": "*"
|
||||
}
|
||||
|
@ -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": "*"
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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": "*"
|
||||
},
|
||||
|
@ -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": "*"
|
||||
|
@ -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": {
|
||||
|
@ -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}
|
||||
/>
|
||||
|
@ -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 = {
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -1,8 +1,8 @@
|
||||
const warn = 1;
|
||||
|
||||
module.exports = {
|
||||
settings: {
|
||||
'import/core-modules': ['config'],
|
||||
globals: {
|
||||
graphql: false,
|
||||
},
|
||||
rules: {
|
||||
'import/no-unresolved': warn,
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
|
2
docs/.storybook/preview-head.html
Normal 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>
|
@ -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;
|
@ -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
@ -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',
|
||||
],
|
||||
};
|
@ -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,
|
||||
},
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
@ -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)}`;
|
||||
};
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
@ -1 +0,0 @@
|
||||
export { default } from '../_docstemplate';
|
@ -1 +0,0 @@
|
||||
export { default } from '../_docstemplate';
|
@ -1 +0,0 @@
|
||||
export { default } from '../_docstemplate';
|
@ -1 +0,0 @@
|
||||
export { default } from '../_docstemplate';
|
@ -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)}>
|
@ -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 }) => (
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
@ -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>
|
@ -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';
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
135
docs/src/components/Homepage/MainLinks/index.js
Normal 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;
|
@ -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 }) => (
|