mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-17 05:02:23 +08:00
Initial commit
This commit is contained in:
parent
38f2f49a95
commit
2a434ddef4
3
addons/a11y/.babelrc
Executable file
3
addons/a11y/.babelrc
Executable file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["react-app"]
|
||||
}
|
3
addons/a11y/.gitignore
vendored
Executable file
3
addons/a11y/.gitignore
vendored
Executable file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
coverage
|
||||
dist
|
2
addons/a11y/.npmignore
Executable file
2
addons/a11y/.npmignore
Executable file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
.babelrc
|
9
addons/a11y/.scripts/npm-prepublish.js
Executable file
9
addons/a11y/.scripts/npm-prepublish.js
Executable file
@ -0,0 +1,9 @@
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
var babel = ['node_modules', '.bin', 'babel'].join(path.sep);
|
||||
|
||||
// required for react-app preset
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
shell.rm('-rf', 'dist')
|
||||
shell.exec(babel + ' --ignore __tests__ src --out-dir dist')
|
1
addons/a11y/.storybook/addons.js
Executable file
1
addons/a11y/.storybook/addons.js
Executable file
@ -0,0 +1 @@
|
||||
import '../register';
|
2
addons/a11y/.storybook/config.js
Executable file
2
addons/a11y/.storybook/config.js
Executable file
@ -0,0 +1,2 @@
|
||||
import * as storybook from '@kadira/storybook';
|
||||
storybook.configure(() => require('./stories'), module);
|
24
addons/a11y/.storybook/stories.js
Executable file
24
addons/a11y/.storybook/stories.js
Executable file
@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@kadira/storybook';
|
||||
|
||||
import { checkA11y } from './../src';
|
||||
|
||||
storiesOf('Button', module)
|
||||
.addDecorator(checkA11y)
|
||||
.add('Default', () => (
|
||||
<button>
|
||||
Correct Button
|
||||
</button>
|
||||
))
|
||||
.add('Red button', () => (
|
||||
<div>
|
||||
<button style={{ backgroundColor: 'red', color: 'darkRed', }}>
|
||||
Incorrect Button
|
||||
</button>
|
||||
<button>
|
||||
Correct Button
|
||||
</button>
|
||||
<button style={{ backgroundColor: 'blue', color: 'lightBlue', height: '20px', width: '20px' }}>
|
||||
</button>
|
||||
</div>
|
||||
));
|
6
addons/a11y/CHANGELOG.md
Executable file
6
addons/a11y/CHANGELOG.md
Executable file
@ -0,0 +1,6 @@
|
||||
## Changelog
|
||||
|
||||
### v0.0.1
|
||||
|
||||
* Initial release
|
||||
* Implemented [axe-core](https://github.com/dequelabs/axe-core) as the Accessibility Engine.
|
41
addons/a11y/README.md
Executable file
41
addons/a11y/README.md
Executable file
@ -0,0 +1,41 @@
|
||||
# storybook-addon-a11y
|
||||
|
||||
This storybook addon can be helpfull to make you're UI components more accessibile.
|
||||
|
||||

|
||||
|
||||
## Getting started
|
||||
|
||||
First, install the addon.
|
||||
|
||||
```shell
|
||||
$ 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 you're stories for violations within your components.
|
||||
|
||||
```js
|
||||
import React from 'react';
|
||||
import { storiesOf } from '@kadira/storybook';
|
||||
|
||||
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>
|
||||
));
|
||||
```
|
7
addons/a11y/ROADMAP.md
Normal file
7
addons/a11y/ROADMAP.md
Normal file
@ -0,0 +1,7 @@
|
||||
* 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
BIN
addons/a11y/docs/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 342 KiB |
2
addons/a11y/manager.js
Executable file
2
addons/a11y/manager.js
Executable file
@ -0,0 +1,2 @@
|
||||
const manager = require('./src/register');
|
||||
manager.init();
|
44
addons/a11y/package.json
Normal file
44
addons/a11y/package.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "storybook-addon-a11y",
|
||||
"version": "0.0.1",
|
||||
"description": "a11y addon for storybook",
|
||||
"main": "preview.js",
|
||||
"scripts": {
|
||||
"prepublish": "node .scripts/npm-prepublish.js",
|
||||
"storybook": "start-storybook -p 9001",
|
||||
"test": "jest --coverage --watch"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/jbovenschen/storybook-addon-a11y.git"
|
||||
},
|
||||
"keywords": [
|
||||
"storybook"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jbovenschen/storybook-addon-a11y/issues"
|
||||
},
|
||||
"homepage": "https://github.com/jbovenschen/storybook-addon-a11y#readme",
|
||||
"devDependencies": {
|
||||
"@kadira/storybook": "^2.20.1",
|
||||
"babel-cli": "^6.14.0",
|
||||
"babel-jest": "^15.0.0",
|
||||
"babel-polyfill": "^6.13.0",
|
||||
"babel-preset-react-app": "^0.2.1",
|
||||
"jest": "^15.1.1",
|
||||
"lodash": "^4.16.2",
|
||||
"react": "^15.3.1",
|
||||
"react-addons-test-utils": "^15.3.2",
|
||||
"react-dom": "^15.3.2",
|
||||
"shelljs": "^0.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@kadira/storybook-addons": "^1.5.0",
|
||||
"react": "^0.14.7 || ^15.0.0",
|
||||
"react-dom": "^0.14.7 || ^15.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axe-core": "^2.0.7"
|
||||
}
|
||||
}
|
2
addons/a11y/preview.js
Executable file
2
addons/a11y/preview.js
Executable file
@ -0,0 +1,2 @@
|
||||
const preview = require('./dist/preview');
|
||||
preview.init();
|
3
addons/a11y/register.js
Executable file
3
addons/a11y/register.js
Executable file
@ -0,0 +1,3 @@
|
||||
// NOTE: loading addons using this file is deprecated!
|
||||
// please use manager.js and preview.js files instead
|
||||
require('./manager');
|
14
addons/a11y/src/A11yManager.js
Normal file
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;
|
83
addons/a11y/src/components/Panel.js
Normal file
83
addons/a11y/src/components/Panel.js
Normal file
@ -0,0 +1,83 @@
|
||||
import React, { Component } from 'react';
|
||||
import addons from '@kadira/storybook-addons';
|
||||
import { EVENT_ID } from './../shared';
|
||||
|
||||
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"
|
||||
/>
|
||||
)
|
||||
}]}
|
||||
/>
|
||||
)
|
||||
|
||||
return <div>{this.state.text}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Panel;
|
50
addons/a11y/src/components/Report/Elements.js
Normal file
50
addons/a11y/src/components/Report/Elements.js
Normal file
@ -0,0 +1,50 @@
|
||||
import React from 'react';
|
||||
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
||||
function Elements({ elements, passes }) {
|
||||
return (
|
||||
<ol style={styles.element}>
|
||||
{elements.map((element, index) => (
|
||||
<Element
|
||||
passes={passes}
|
||||
element={element}
|
||||
key={index}
|
||||
/>
|
||||
))}
|
||||
</ol>
|
||||
);
|
||||
}
|
||||
|
||||
export default Elements;
|
41
addons/a11y/src/components/Report/Info.js
Normal file
41
addons/a11y/src/components/Report/Info.js
Normal file
@ -0,0 +1,41 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
|
||||
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.object,
|
||||
};
|
||||
|
||||
export default Info;
|
63
addons/a11y/src/components/Report/Item.js
Normal file
63
addons/a11y/src/components/Report/Item.js
Normal file
@ -0,0 +1,63 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
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: {
|
||||
margin: '12px 0',
|
||||
display: 'block',
|
||||
width: '100%',
|
||||
},
|
||||
}
|
||||
|
||||
class Item extends Component {
|
||||
static propTypes = {
|
||||
item: PropTypes.object,
|
||||
passes: PropTypes.bool,
|
||||
}
|
||||
|
||||
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}>
|
||||
<div
|
||||
style={styles.headerBar}
|
||||
onClick={() => this.onToggle()}
|
||||
>
|
||||
{item.description}
|
||||
</div>
|
||||
{ open && (<Info item={item} />) }
|
||||
{ open && (
|
||||
<Elements
|
||||
elements={item.nodes}
|
||||
passes={passes}
|
||||
/>
|
||||
) }
|
||||
{ open && (<Tags tags={item.tags} />) }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Item;
|
77
addons/a11y/src/components/Report/Rules.js
Normal file
77
addons/a11y/src/components/Report/Rules.js
Normal file
@ -0,0 +1,77 @@
|
||||
import React from 'react';
|
||||
|
||||
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>
|
||||
)
|
||||
}
|
||||
|
||||
function Rules({ rules, passes }) {
|
||||
return (
|
||||
<div style={styles.rules}>
|
||||
{rules.map((rule, index) => (
|
||||
<Rule
|
||||
passes={passes}
|
||||
rule={rule}
|
||||
key={index}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Rules;
|
34
addons/a11y/src/components/Report/Tags.js
Normal file
34
addons/a11y/src/components/Report/Tags.js
Normal file
@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
export default Tags;
|
44
addons/a11y/src/components/Report/index.js
Normal file
44
addons/a11y/src/components/Report/index.js
Normal file
@ -0,0 +1,44 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import Item from './Item';
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
fontFamily: '-apple-system, ".SFNSText-Regular", "San Francisco", Roboto, "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif',
|
||||
fontSize: '12px',
|
||||
},
|
||||
empty: {
|
||||
fontFamily: '-apple-system, ".SFNSText-Regular", "San Francisco", Roboto, "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif',
|
||||
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.array,
|
||||
empty: PropTypes.string,
|
||||
}
|
||||
|
||||
export default Report;
|
101
addons/a11y/src/components/Tabs.js
Normal file
101
addons/a11y/src/components/Tabs.js
Normal file
@ -0,0 +1,101 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
const styles = {
|
||||
container: {
|
||||
width: '100%',
|
||||
},
|
||||
tabs: {
|
||||
borderBottom: '1px solid rgb(234, 234, 234)',
|
||||
flexWrap: 'wrap',
|
||||
display: 'flex',
|
||||
},
|
||||
tab: {
|
||||
fontFamily: '-apple-system, ".SFNSText-Regular", "San Francisco", Roboto, "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif',
|
||||
color: 'rgb(68, 68, 68)',
|
||||
fontSize: '11px',
|
||||
textDecoration: 'none',
|
||||
textTransform: 'uppercase',
|
||||
padding: '10px 15px',
|
||||
letterSpacing: '1px',
|
||||
cursor: 'pointer',
|
||||
fontWeight: 500,
|
||||
opacity: 0.7,
|
||||
},
|
||||
tabActive: {
|
||||
opacity: 1,
|
||||
fontWeight: 600,
|
||||
}
|
||||
}
|
||||
|
||||
class Tabs extends Component {
|
||||
static propTypes = {
|
||||
tabs: PropTypes.arrayOf(PropTypes.shape({
|
||||
label: PropTypes.element,
|
||||
panel: PropTypes.element,
|
||||
})),
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return (
|
||||
<div style={styles.tabs}>
|
||||
{tabs.map((tab, index) => (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
...styles.tab,
|
||||
...(index === active ? styles.tabActive : undefined)
|
||||
}}
|
||||
onClick={() => this.onToggle(index)}
|
||||
>
|
||||
{ tab.label }
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
const { tabs } = this.props;
|
||||
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
{this.renderTabs()}
|
||||
{this.renderPanel()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Tabs;
|
30
addons/a11y/src/components/WrapStory.js
Normal file
30
addons/a11y/src/components/WrapStory.js
Normal file
@ -0,0 +1,30 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import axe from 'axe-core';
|
||||
|
||||
class WrapStory extends Component {
|
||||
static propTypes = {
|
||||
context: PropTypes.object,
|
||||
storyFn: PropTypes.func,
|
||||
channel: PropTypes.object,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { channel } = this.props;
|
||||
|
||||
axe.a11yCheck(this.wrapper, {}, (results) => {
|
||||
channel.emit('addon:a11y:check', results);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { storyFn, context } = this.props;
|
||||
|
||||
return (<span
|
||||
ref={ (container) => { this.wrapper = container; } }
|
||||
>
|
||||
{storyFn(context)}
|
||||
</span>)
|
||||
}
|
||||
}
|
||||
|
||||
export default WrapStory;
|
11
addons/a11y/src/index.js
Normal file
11
addons/a11y/src/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
import addons from '@kadira/storybook-addons';
|
||||
import A11yManager from './A11yManager';
|
||||
|
||||
const manager = new A11yManager();
|
||||
|
||||
function checkA11y(storyFn, context) {
|
||||
const channel = addons.getChannel();
|
||||
return manager.wrapStory(channel, storyFn, context);
|
||||
}
|
||||
|
||||
export { checkA11y };
|
18
addons/a11y/src/register.js
Normal file
18
addons/a11y/src/register.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import addons from '@kadira/storybook-addons';
|
||||
|
||||
import Panel from './components/Panel';
|
||||
import { ADDON_ID, PANEL_ID } from './shared';
|
||||
|
||||
function init() {
|
||||
addons.register(ADDON_ID, api => {
|
||||
addons.addPanel(PANEL_ID, {
|
||||
title: 'Accessibility',
|
||||
render() {
|
||||
return <Panel />;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export { init }
|
4
addons/a11y/src/shared/index.js
Executable file
4
addons/a11y/src/shared/index.js
Executable file
@ -0,0 +1,4 @@
|
||||
// addons, panels and events get unique names using a prefix
|
||||
export const ADDON_ID = 'jbovenschen/storybook-addon-a11y';
|
||||
export const PANEL_ID = `${ADDON_ID}/addon-panel`;
|
||||
export const EVENT_ID = `${ADDON_ID}/addon-event`;
|
Loading…
x
Reference in New Issue
Block a user