add new features and storybook static

added the ability to edit event payload
add live demo
This commit is contained in:
Evgeniy Zaitsev 2017-04-14 19:31:59 +03:00 committed by Norbert de Langen
parent e0f4944bf8
commit 968ec483a9
23 changed files with 5130 additions and 49 deletions

View File

@ -0,0 +1,9 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -2,3 +2,4 @@ node_modules
*.log
.idea
dist
.DS_Store

View File

@ -1,2 +1,5 @@
src
.babelrc
.storybook
stories
storybook-static

View File

@ -0,0 +1,3 @@
/* eslint-disable import/no-extraneous-dependencies*/
import '@kadira/storybook/addons';
import '../dist/register';

View File

@ -0,0 +1,7 @@
import { configure } from '@kadira/storybook';
function loadStories() {
require('../stories');
}
configure(loadStories, module);

View File

@ -2,7 +2,8 @@
This [Storybook](https://getstorybook.io) addon allows you to add events for your stories.
![Storybook Addon Events Demo](docs/Demo.png)
![Storybook Addon Events Example](docs/Demo.png)
![Storybook Addon Events Live Demo](https://z4o4z.github.io/storybook-addon-events/index.html)
### Getting Started
**note: addons require @kadira/storybook 2.x or greater*
@ -27,26 +28,68 @@ import React from 'react';
import EventEmiter from 'event-emiter';
import { storiesOf } from '@kadira/storybook';
import WithEvents from '@z4o4z/storybook-addon-events';
import MyContainerWithEmiter from './MyContainerWithEmiter';
import Logger from './Logger';
import * as EVENTS from './events';
const emiter = new EventEmiter();
const emit = emiter.emit.bind(emiter);
storiesOf('Button', module)
.add('with text', () => (
storiesOf('WithEvents', module)
.addDecorator(getStory => (
<WithEvents
emit={emiter.emit}
toggleLike={{
title: 'Toggle like 1',
name: 'togle-like',
payload: {},
}}
toggleLike2={{
title: 'Toggle like 2',
name: 'togle-like-2',
payload: {},
}}
emit={emit}
events={[
{
name: EVENTS.TEST_EVENT_1,
title: 'Test event 1',
payload: 0,
},
{
name: EVENTS.TEST_EVENT_2,
title: 'Test event 2',
payload: 'asdasdad asdasdasd',
},
{
name: EVENTS.TEST_EVENT_3,
title: 'Test event 3',
payload: {
string: 'value',
number: 123,
array: [1, 2, 3],
object: {
string: 'value',
number: 123,
array: [1, 2, 3],
},
},
},
{
name: EVENTS.TEST_EVENT_4,
title: 'Test event 4',
payload: [
{
string: 'value',
number: 123,
array: [1, 2, 3],
},
{
string: 'value',
number: 123,
array: [1, 2, 3],
},
{
string: 'value',
number: 123,
array: [1, 2, 3],
},
],
},
]}
>
<MyContainerWithEmiter emiter={emiter}/>
</WithEvents>
));
{getStory()}
</WithEvents>
))
.add('Logger', () => <Logger emiter={emiter} />);
```

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
if (window.parent !== window) {
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;
}
</script>
<title>React Storybook</title>
</head>
<body>
<div id="root"></div>
<div id="error-display"></div>
<script src="static/preview.7e578bc93a9ccff13804.bundle.js"></script>
</body>
</html>

View File

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="storybook-version" content="2.35.3">
<title>React Storybook</title>
<style>
/*
When resizing panels, the drag event breaks if the cursor
moves over the iframe. Add the 'dragging' class to the body
at drag start and remove it when the drag ends.
*/
.dragging iframe {
pointer-events: none;
}
/* Styling the fuzzy search box placeholders */
.searchBox::-webkit-input-placeholder { /* Chrome/Opera/Safari */
color: #ddd;
font-size: 16px;
}
.searchBox::-moz-placeholder { /* Firefox 19+ */
color: #ddd;
font-size: 16px;
}
.searchBox:focus{
border-color: #EEE !important;
}
.btn:hover{
background-color: #eee
}
</style>
</head>
<body style="margin: 0;">
<div id="root"></div>
<script src="static/manager.4082eb82d503ebc1ee9c.bundle.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"static/manager.4082eb82d503ebc1ee9c.bundle.js","sources":["webpack:///static/manager.4082eb82d503ebc1ee9c.bundle.js"],"mappings":"AAAA;AAkuDA;AAq4DA;AAs+DA;AA2wDA;AAiyEA;AA+9CA;AA6yDA;AAqgDA;AA24DA;AAipDA;AAw9CA;AA4nDA;AAsjEA;AAs3DA;AAk6CA;AA6yCA;AAqrDA;AAigEA;AA4gEA;AA8qDA;AA+0DA;AA40DA;AAyoDA;AAsiDA;AAmuEA;AAioGA;AA5jCA;AAq4GA;AAw7FA;AA6mDA;AAgKA;AAw6DA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"file":"static/preview.7e578bc93a9ccff13804.bundle.js","sources":["webpack:///static/preview.7e578bc93a9ccff13804.bundle.js"],"mappings":"AAAA;AAkuDA;AA61DA;AA8iEA;AAytDA;AAwgEA;AA0oDA;AAglDA;AAs/CA;AA48DA;AA6oDA;AAs9CA;AAqoDA;AAyiEA;AAy6DA;AAwgCA;AA+vDA;AAslDA;AA8pEA;AAi2DA;AA6yCA;AA+oEA;AA0+DA;AAy+CA","sourceRoot":""}

View File

@ -12,10 +12,11 @@
"prepublish": "npm run build",
"lint": "eslint src",
"lintfix": "eslint src --fix",
"storybook": "start-storybook -p 9010"
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"devDependencies": {
"@kadira/storybook": "^2.20.1",
"@kadira/storybook": "^2.21.0",
"babel-cli": "^6.5.0",
"babel-core": "^6.5.0",
"babel-eslint": "^7.0.0",
@ -29,6 +30,7 @@
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^4.0.0",
"eslint-plugin-react": "^6.2.2",
"eventemitter3": "^2.0.3",
"react": "^15.3.2",
"react-dom": "^15.3.2"
},
@ -37,7 +39,9 @@
"@kadira/storybook-addons": "^v1.3.1"
},
"dependencies": {
"babel-runtime": "^6.5.0"
"babel-runtime": "^6.5.0",
"format-json": "^1.0.3",
"react-textarea-autosize": "^4.0.5"
},
"main": "dist/index.js",
"engines": {
@ -49,4 +53,4 @@
"addon",
"events"
]
}
}

View File

@ -0,0 +1,133 @@
import React, { Component, PropTypes } from 'react';
import Textarea from 'react-textarea-autosize';
import json from 'format-json';
const styles = {
item: {
padding: '10 0',
},
buttonWrapper: {
textAlign: 'center',
},
button: {
display: 'inline-block',
fontFamily: 'Arial, sans-serif',
fontSize: 14,
padding: 10,
margin: 10,
width: '40%',
},
textArea: {
display: 'block',
boxSizing: 'border-box',
margin: 0,
width: '100%',
maxWidth: '100%',
verticalAlign: 'middle',
outline: 'none',
border: '1px solid #c7c7c7',
borderRadius: 2,
fontSize: 13,
padding: '5px',
color: 'rgb(51, 51, 51)',
fontFamily: 'Arial, sans-serif',
},
};
export default class Item extends Component {
static propTypes = {
name: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
onEmit: PropTypes.func.isRequired,
payload: PropTypes.any, // eslint-disable-line react/forbid-prop-types
};
static defaultProps = {
payload: {},
};
static getJSONFromString(str) {
try {
return JSON.parse(str);
} catch (e) {
return str;
}
}
state = {};
componentWillMount() {
const payloadString = json.plain(this.props.payload);
this.setState({
failed: false,
payload: Item.getJSONFromString(payloadString),
payloadString,
isTextAreaShowed: false,
});
}
onChange = ({ target: { value } }) => {
const newState = {
payloadString: value,
};
try {
newState.payload = JSON.parse(value.trim());
newState.failed = false;
} catch (err) {
newState.failed = true;
}
this.setState(newState);
};
onEmitClick = () => {
this.props.onEmit({
name: this.props.name,
payload: this.state.payload,
});
};
onToggleEditClick = () => {
this.setState(({ isTextAreaShowed }) => ({
isTextAreaShowed: !isTextAreaShowed,
}));
};
render() {
const { failed, isTextAreaShowed } = this.state;
const extraStyle = {
display: isTextAreaShowed ? 'block' : 'none',
};
if (failed) {
extraStyle.border = '1px solid #fadddd';
extraStyle.backgroundColor = '#fff5f5';
}
return (
<div style={{ width: '100%' }}>
<h3>{this.props.title}</h3>
<div style={styles.buttonWrapper}>
<button style={styles.button} onClick={this.onEmitClick} disabled={failed}>
Emit
</button>
<button style={styles.button} onClick={this.onToggleEditClick}>
{isTextAreaShowed ? 'Close' : 'Edit payload'}
</button>
</div>
<Textarea
ref={(ref) => {
this.input = ref;
}}
style={{ ...styles.textArea, ...extraStyle }}
value={this.state.payloadString}
minRows={3}
onChange={this.onChange}
/>
</div>
);
}
}

View File

@ -4,28 +4,35 @@ import addons from '@kadira/storybook-addons';
export default class WithEvents extends Component {
static propTypes = {
emit: PropTypes.func.isRequired,
events: PropTypes.arrayOf(
PropTypes.shape({
name: PropTypes.string,
title: PropTypes.string,
payload: PropTypes.any,
}),
).isRequired,
children: PropTypes.element.isRequired,
};
componentDidMount() {
const { emit, children, ...events } = this.props;
const { events } = this.props;
this.channel = addons.getChannel();
this.channel.on('z4o4z/events/emit', this.onEmit);
this.channel.emit('z4o4z/events/add', Object.values(events));
this.channel.emit('z4o4z/events/add', events);
}
componentWillReceiveProps(nextProps) {
const { emit, children, ...events } = nextProps;
const { events } = nextProps;
this.channel.emit('z4o4z/events/add', Object.values(events));
this.channel.emit('z4o4z/events/add', events);
}
componentWillUnmount() {
this.unmounted = true;
this.channel.removeListener('z4o4z/events/emit');
this.channel.removeListener('z4o4z/events/emit', this.onEmit);
}
onEmit = (event) => {

View File

@ -1,23 +1,17 @@
import React, { PropTypes, Component } from 'react';
import addons from '@kadira/storybook-addons';
import Item from './components/Item';
const styles = {
wrapper: {
margin: 10,
fontFamily: 'Arial',
fontFamily: 'Arial, sans-serif',
fontSize: 14,
width: '100%',
color: 'rgb(51, 51, 51)',
overflow: 'auto',
},
item: {
margin: 5,
},
button: {
fontFamily: 'Arial',
fontSize: 14,
padding: 10,
width: '100%',
},
};
export default class Events extends Component {
@ -57,21 +51,14 @@ export default class Events extends Component {
this.setState({ events });
};
render() {
const { events } = this.state;
onEmit = (event) => {
this.props.channel.emit('z4o4z/events/emit', event);
};
render() {
return (
<div style={styles.wrapper}>
{events.map((event, index) => (
<div style={styles.item} key={index}>
<button
style={styles.button}
onClick={() => this.props.channel.emit('z4o4z/events/emit', event)}
>
{event.title}
</button>
</div>
))}
{this.state.events.map((event, i) => <Item key={i} {...event} onEmit={this.onEmit} />)}
</div>
);
}

View File

@ -0,0 +1,61 @@
/* eslint-disable import/no-extraneous-dependencies*/
import React, { Component, PropTypes } from 'react';
import EventEmiter from 'eventemitter3';
/* eslint-enable import/no-extraneous-dependencies*/
import json from 'format-json';
import * as EVENTS from './events';
const styles = {
wrapper: {
padding: 20,
fontFamily: 'Areal, sans-serif',
color: 'rgb(51, 51, 51)',
},
item: {
listStyle: 'none',
marginBottom: 10,
},
};
export default class Logger extends Component {
static propTypes = {
emiter: PropTypes.instanceOf(EventEmiter).isRequired,
};
state = {
events: [],
};
componentWillMount() {
const { emiter } = this.props;
emiter.on(EVENTS.TEST_EVENT_1, this.onEventHandler(EVENTS.TEST_EVENT_1));
emiter.on(EVENTS.TEST_EVENT_2, this.onEventHandler(EVENTS.TEST_EVENT_2));
emiter.on(EVENTS.TEST_EVENT_3, this.onEventHandler(EVENTS.TEST_EVENT_3));
emiter.on(EVENTS.TEST_EVENT_4, this.onEventHandler(EVENTS.TEST_EVENT_4));
}
onEventHandler = name =>
(payload) => {
this.setState(({ events }) => ({
events: [...events, { name, payload }],
}));
};
render() {
return (
<div style={styles.wrapper}>
<h1>Logger</h1>
<dl>
{this.state.events.map((event, i) => (
<div style={styles.item} key={i}>
<dt><b>Event name:</b> {event.name}</dt>
<dd><b>Event payload:</b> {json.plain(event.payload)}</dd>
</div>
))}
</dl>
</div>
);
}
}

View File

@ -0,0 +1,4 @@
export const TEST_EVENT_1 = 'test-event-1';
export const TEST_EVENT_2 = 'test-event-2';
export const TEST_EVENT_3 = 'test-event-3';
export const TEST_EVENT_4 = 'test-event-4';

View File

@ -0,0 +1,70 @@
import React from 'react';
/* eslint-disable import/no-extraneous-dependencies */
import { storiesOf } from '@kadira/storybook';
import EventEmiter from 'eventemitter3';
/* eslint-enable import/no-extraneous-dependencies */
import WithEvents from '../dist/index';
import Logger from './Logger';
import * as EVENTS from './events';
const emiter = new EventEmiter();
const emit = emiter.emit.bind(emiter);
storiesOf('WithEvents', module)
.addDecorator(getStory => (
<WithEvents
emit={emit}
events={[
{
name: EVENTS.TEST_EVENT_1,
title: 'Test event 1',
payload: 0,
},
{
name: EVENTS.TEST_EVENT_2,
title: 'Test event 2',
payload: 'asdasdad asdasdasd',
},
{
name: EVENTS.TEST_EVENT_3,
title: 'Test event 3',
payload: {
string: 'value',
number: 123,
array: [1, 2, 3],
object: {
string: 'value',
number: 123,
array: [1, 2, 3],
},
},
},
{
name: EVENTS.TEST_EVENT_4,
title: 'Test event 4',
payload: [
{
string: 'value',
number: 123,
array: [1, 2, 3],
},
{
string: 'value',
number: 123,
array: [1, 2, 3],
},
{
string: 'value',
number: 123,
array: [1, 2, 3],
},
],
},
]}
>
{getStory()}
</WithEvents>
))
.add('Logger', () => <Logger emiter={emiter} />);

4622
addons/events/yarn.lock Normal file

File diff suppressed because it is too large Load Diff