Merge pull request #124 from Gargron/feature-storybook

Add a storybook
release
Etienne Lemay 2017-09-27 21:09:51 -04:00 committed by GitHub
commit c5ca59284f
11 changed files with 1730 additions and 509 deletions

3
.storybook/addons.js Normal file
View File

@ -0,0 +1,3 @@
import '@storybook/addon-knobs/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-options/register';

11
.storybook/config.js Normal file
View File

@ -0,0 +1,11 @@
import { configure } from '@storybook/react';
import { setOptions } from '@storybook/addon-options';
setOptions({
name: 'Contribute on GitHub',
url: 'https://github.com/missive/emoji-mart',
downPanelInRight: true,
sidebarAnimations: false,
})
configure(() => require('../stories'), module);

View File

@ -0,0 +1,39 @@
const path = require('path')
const genDefaultConfig = require('@storybook/react/dist/server/config/defaults/webpack.config.js');
module.exports = (baseConfig, env) => {
const config = genDefaultConfig(baseConfig, env);
config.module.rules = [
{
test: /\.js$/,
loader: 'babel-loader',
include: [
path.resolve(__dirname, '../stories'),
path.resolve(__dirname, '../src'),
path.resolve(__dirname, '../node_modules/measure-scrollbar'),
path.resolve(__dirname, '../data'),
],
},
{
test: /\.svg$/,
loaders: ['babel-loader?presets[]=react', 'svg-jsx-loader?es6=true'],
include: [
path.resolve(__dirname, '../src/svgs'),
],
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ],
include: [
path.resolve(__dirname, '../css'),
],
},
];
config.devtool.sourcemaps = {
enabled: false,
};
return config;
};

View File

@ -218,8 +218,7 @@ Apple / Google / Twitter / EmojiOne / Messenger / Facebook
## Development
```sh
$ yarn run build:data
$ yarn start
$ open example/index.html
$ yarn storybook
```
## 🎩 Hat tips!

View File

@ -1,49 +0,0 @@
<html>
<head>
<meta charset="utf-8">
<title>Emoji Mart | One component to pick them all</title>
<link rel="stylesheet" href="../css/emoji-mart.css">
<style>
* {
margin: 0; padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", sans-serif;
font-size: 16px;
padding: 1em;
}
button + button { margin-left: .5em }
button {
padding: .4em .6em;
border-radius: 5px;
border: 1px solid rgba(0, 0, 0, .1);
background: #fff;
outline: 0;
cursor: pointer;
}
button[disabled] {
border-color: #ae65c5;
}
.row {
margin-top: 1em;
}
.demo-title {
display: inline-block;
}
iframe {
margin-left: 1em;
}
</style>
</head>
<body>
<div></div>
<script src="./bundle.js"></script>
</body>
</html>

View File

@ -1,313 +0,0 @@
import React from 'react'
import ReactDOM from 'react-dom'
import { Picker, Emoji } from '../src'
const CUSTOM_EMOJIS = [
{
name: 'Octocat',
short_names: ['octocat'],
keywords: ['github'],
imageUrl: 'https://assets-cdn.github.com/images/icons/emoji/octocat.png?v7'
},
{
name: 'Squirrel',
short_names: ['shipit', 'squirrel'],
keywords: ['github'],
imageUrl: 'https://assets-cdn.github.com/images/icons/emoji/shipit.png?v7'
}
]
const CATEGORIES = [
'recent',
'people',
'nature',
'foods',
'activity',
'places',
'objects',
'symbols',
'flags',
'custom',
]
class Example extends React.Component {
constructor(props) {
super(props)
this.state = {
emojiSize: 24,
perLine: 9,
skin: 1,
native: true,
set: 'apple',
hidden: false,
currentEmoji: { id: '+1' },
autoFocus: false,
include: [],
exclude: [],
}
}
handleInput(e) {
var { currentTarget } = e,
{ type } = currentTarget,
key = currentTarget.getAttribute('data-key'),
mount = currentTarget.getAttribute('data-mount'),
state = {}
if (type == 'checkbox') {
var { checked } = currentTarget,
value = currentTarget.getAttribute('data-value')
if (value) {
if (checked) {
state[key] = this.state[key].concat(value)
} else {
state[key] = this.state[key].filter((item) => { return item != value })
}
} else {
state[key] = checked
}
} else {
var { value } = currentTarget
state[key] = parseInt(value)
}
if (mount) {
this.setState({ hidden: true }, () => {
state.hidden = false
this.setState(state)
})
} else {
this.setState(state)
}
}
render() {
return <div>
<div>
<h1 className='demo-title'>Emoji Mart</h1>
<iframe
src='https://ghbtns.com/github-btn.html?user=missive&repo=emoji-mart&type=star&count=true'
frameBorder='0'
scrolling='0'
width='170px'
height='20px'
></iframe>
</div>
<div className="row">
{['native', 'apple', 'google', 'twitter', 'emojione', 'messenger', 'facebook'].map((set) => {
var props = { disabled: !this.state.native && set == this.state.set }
if (set == 'native' && this.state.native) {
props.disabled = true
}
return <button
key={set}
value={set}
onClick={() => {
if (set == 'native') {
this.setState({ native: true })
} else {
this.setState({ set: set, native: false })
}
}}
{...props}>
{set}
</button>
})}
</div>
<div className="row">
<button
onClick={() => this.setState({ hidden: !this.state.hidden })}
>{this.state.hidden ? 'Mount' : 'Unmount'}</button>
</div>
<pre style={{
fontSize: 18,
display: 'inline-block',
verticalAlign: 'top',
margin: '1em',
width: '460px',
}}>
<Operator>import</Operator> &#123;Picker&#125; <Operator>from</Operator> <String>'emoji-mart'</String>
<br />
<br /><Operator>&lt;</Operator><Variable>Picker</Variable>
<br /> emojiSize<Operator>=</Operator>&#123;<Variable>{this.state.emojiSize}</Variable>&#125; <input type='range' data-key='emojiSize' data-mount={true} onChange={this.handleInput.bind(this)} min='16' max='64' value={this.state.emojiSize} />
<br /> perLine<Operator>=</Operator>&#123;<Variable>{this.state.perLine}</Variable>&#125; {this.state.perLine < 10 ? ' ' : ' '} <input type='range' data-key='perLine' onChange={this.handleInput.bind(this)} min='7' max='16' value={this.state.perLine} />
<br /> skin<Operator>=</Operator>&#123;<Variable>{this.state.skin}</Variable>&#125; <input type='range' data-key='skin' onChange={this.handleInput.bind(this)} min='1' max='6' value={this.state.skin} />
<br /> set<Operator>=</Operator><String>'{this.state.set}'</String>
<br /> custom<Operator>=</Operator>&#123;<Variable>{'[]'}</Variable>&#125;
<br /> autoFocus<Operator>=</Operator>&#123;<Variable>{this.state.autoFocus ? 'true' : 'false'}</Variable>&#125;{this.state.autoFocus ? ' ' : ''} <input type='checkbox' data-key='autoFocus' data-mount={true} onChange={this.handleInput.bind(this)} checked={this.state.autoFocus} />
<div style={{ opacity: this.state.exclude.length ? 0.6 : 1 }}> include<Operator>=</Operator>&#91;
{[0, 2, 4, 6, 8].map((i) => {
let category1 = CATEGORIES[i],
category2 = CATEGORIES[i + 1]
return <div key={i}>
<label style={{ width: '200px', display: 'inline-block' }}> <input type='checkbox' data-key='include' data-value={category1} data-mount={true} onChange={this.handleInput.bind(this)} disabled={this.state.exclude.length} /> <String>'{category1}'</String></label>
{category2 && <label><input type='checkbox' data-key='include' data-value={category2} data-mount={true} onChange={this.handleInput.bind(this)} disabled={this.state.exclude.length} /> <String>'{category2}'</String></label>}
</div>
})}
  &#93;
</div>
<div style={{ opacity: this.state.include.length ? 0.6 : 1 }}> exclude<Operator>=</Operator>&#91;
{[0, 2, 4, 6, 8].map((i) => {
let category1 = CATEGORIES[i],
category2 = CATEGORIES[i + 1]
return <div key={i}>
<label style={{ width: '200px', display: 'inline-block' }}> <input type='checkbox' data-key='exclude' data-value={category1} data-mount={true} onChange={this.handleInput.bind(this)} disabled={this.state.include.length} /> <String>'{category1}'</String></label>
{category2 && <label><input type='checkbox' data-key='exclude' data-value={category2} data-mount={true} onChange={this.handleInput.bind(this)} disabled={this.state.include.length} /> <String>'{category2}'</String></label>}
</div>
})}
  &#93;
</div>
<Operator>/&gt;</Operator>
</pre>
{!this.state.hidden &&
<Picker
emojiSize={this.state.emojiSize}
perLine={this.state.perLine}
skin={this.state.skin}
native={this.state.native}
set={this.state.set}
custom={CUSTOM_EMOJIS}
autoFocus={this.state.autoFocus}
include={this.state.include}
exclude={this.state.exclude}
onClick={(emoji) => {
this.setState({ currentEmoji: emoji })
console.log(emoji)
}}
/>
}
<div>
<pre style={{
fontSize: 18,
display: 'inline-block',
verticalAlign: 'top',
margin: '1em',
width: '370px',
}}>
<Operator>import</Operator> &#123;Emoji&#125; <Operator>from</Operator> <String>'emoji-mart'</String>
<br />
<br /><Operator>&lt;</Operator><Variable>Emoji</Variable>
<br /> emoji<Operator>=</Operator>{this.state.currentEmoji.custom ? (<Variable>{'{}'}</Variable>) : (<String>'{this.state.currentEmoji.id}'</String>)}
<br /> size<Operator>=</Operator>&#123;<Variable>{64}</Variable>&#125;
<br /><Operator>/&gt;</Operator>
</pre>
<span style={{ display: 'inline-block', marginTop: 60 }}>
{Emoji({
emoji: this.state.currentEmoji.custom ? this.state.currentEmoji : this.state.currentEmoji.id,
size: 64,
set: this.state.set,
})}
</span>
</div>
<div>
<pre style={{
fontSize: 18,
display: 'inline-block',
verticalAlign: 'top',
margin: '1em',
width: '370px',
}}>
<br /><Operator>&lt;</Operator><Variable>Emoji</Variable>
<br /> emoji<Operator>=</Operator><String>':{this.state.currentEmoji.id}:'</String>
<br /> size<Operator>=</Operator>&#123;<Variable>{64}</Variable>&#125;
<br /><Operator>/&gt;</Operator>
</pre>
<span style={{ display: 'inline-block', marginTop: 40 }}>
{Emoji({
emoji: `:${this.state.currentEmoji.id}:`,
size: 64,
set: this.state.set,
})}
</span>
</div>
<div>
<pre style={{
fontSize: 18,
display: 'inline-block',
verticalAlign: 'top',
margin: '1em',
width: '370px',
}}>
<br /><Operator>&lt;</Operator><Variable>Emoji</Variable>
<br /> emoji<Operator>=</Operator><String>':{this.state.currentEmoji.id}::skin-tone-3:'</String>
<br /> size<Operator>=</Operator>&#123;<Variable>{64}</Variable>&#125;
<br /><Operator>/&gt;</Operator>
</pre>
<span style={{ display: 'inline-block', marginTop: 40 }}>
{Emoji({
emoji: `:${this.state.currentEmoji.id}::skin-tone-3:`,
size: 64,
set: this.state.set,
})}
</span>
</div>
<div>
<pre style={{
fontSize: 18,
display: 'inline-block',
verticalAlign: 'top',
margin: '1em',
width: '370px',
}}>
<br /><Operator>&lt;</Operator><Variable>Emoji</Variable>
<br /> emoji<Operator>=</Operator><String>':{this.state.currentEmoji.id}::skin-tone-3:'</String>
<br /> size<Operator>=</Operator>&#123;<Variable>{64}</Variable>&#125;
<br /> native<Operator>=</Operator>&#123;<Variable>{'true'}</Variable>&#125;
<br /><Operator>/&gt;</Operator>
</pre>
<span style={{ display: 'inline-block', marginTop: 60 }}>
{Emoji({
emoji: `:${this.state.currentEmoji.id}::skin-tone-3:`,
size: 64,
native: true,
})}
</span>
</div>
</div>
}
}
class Operator extends React.Component {
render() {
return <span style={{color: '#a71d5d'}}>
{this.props.children}
</span>
}
}
class Variable extends React.Component {
render() {
return <span style={{color: '#0086b3'}}>
{this.props.children}
</span>
}
}
class String extends React.Component {
render() {
return <span style={{color: '#183691'}}>
{this.props.children}
</span>
}
}
ReactDOM.render(<Example />, document.querySelector('div'))

View File

@ -1,53 +0,0 @@
var path = require('path')
var pack = require('../package.json')
var webpack = require('webpack')
module.exports = {
entry: path.resolve('example/index.js'),
output: {
path: path.resolve('example'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
include: [
path.resolve('src'),
path.resolve('node_modules/measure-scrollbar'),
path.resolve('data'),
path.resolve('example'),
],
},
{
test: /\.svg$/,
use: [
{
loader: 'babel-loader'
},
{
loader: 'svg-jsx-loader',
options: {
es6: true
}
},
],
include: [
path.resolve('src/svgs'),
],
},
],
},
resolve: {
extensions: ['.js'],
},
plugins: [
new webpack.DefinePlugin({
EMOJI_DATASOURCE_VERSION: `'${pack.devDependencies['emoji-datasource']}'`,
}),
],
}

View File

@ -25,6 +25,11 @@
"react": "^0.14.0 || ^15.0.0-0 || ^16.0.0"
},
"devDependencies": {
"@storybook/addon-actions": "^3.2.11",
"@storybook/addon-knobs": "^3.2.10",
"@storybook/addon-links": "^3.2.10",
"@storybook/addon-options": "3.2.10",
"@storybook/react": "^3.2.11",
"babel-cli": "^6.26.0",
"babel-core": "6.7.2",
"babel-loader": "^7.1.2",
@ -59,17 +64,18 @@
"clean": "rimraf data/data.js dist/",
"clean:dist": "rm -rf dist/",
"build:data": "node scripts/build-data",
"build:example": "node scripts/build-example",
"build:dist": "npm run clean:dist && babel src --out-dir dist --copy-files --ignore webpack.config.js",
"build": "npm run build:data && npm run build:example && npm run build:dist",
"build": "npm run build:data && npm run build:dist",
"watch": "babel src --watch --out-dir dist --copy-files --ignore webpack.config.js",
"start": "npm run watch",
"stats": "webpack --config ./example/webpack.config.js --json > stats.json",
"stats": "webpack --config ./src/webpack.config.js --json > stats.json",
"react:clean": "rimraf node_modules/{react,react-dom,react-addons-test-utils}",
"react:14": "npm run react:clean && npm i react@^0.14 react-dom@^0.14 react-addons-test-utils@^0.14 --save-dev",
"react:15": "npm run react:clean && npm i react@^15 react-dom@^15 react-addons-test-utils@^15 --save-dev",
"test": "NODE_ENV=test karma start && size-limit",
"prepublish": "npm run clean && npm run build"
"prepublish": "npm run clean && npm run build",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"size-limit": [
{

View File

@ -1,26 +0,0 @@
var path = require('path')
var webpack = require('webpack')
var config = require('../example/webpack.config.js')
webpack(config, (err, stats) => {
if (err) throw err
console.log(
stats.toString({
colors: true,
hash: true,
version: false,
timings: true,
assets: true,
chunks: false,
chunkModules: false,
modules: false,
children: false,
cached: false,
reasons: false,
source: false,
errorDetails: false,
chunkOrigins: false,
})
)
})

38
stories/index.js Normal file
View File

@ -0,0 +1,38 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { withKnobs, text, boolean, number, select, color } from '@storybook/addon-knobs';
import { Picker, Emoji } from '../src';
import '../css/emoji-mart.css';
const SETS = ['apple', 'google', 'twitter', 'emojione', 'messenger', 'facebook']
storiesOf('Picker', module)
.addDecorator(withKnobs)
.add('default', () => (
<Picker
onClick={action('clicked')}
native={boolean('Unicode', true)}
set={select('Emoji pack', SETS, SETS[0])}
emojiSize={number('Emoji size', 24)}
perLine={number('Per line', 9)}
title={text('Idle text', 'Your Title Here')}
emoji={text('Idle emoji', 'department_store')}
skin={number('Skin tone', 1)}
color={color('Highlight color', '#ae65c5')}
/>
));
storiesOf('Emoji', module)
.addDecorator(withKnobs)
.add('default', () => (
<Emoji
native={boolean('Unicode', true)}
set={select('Emoji pack', SETS, SETS[0])}
emoji={text('Emoji', '+1')}
size={number('Emoji size', 64)}
skin={number('Skin tone', 1)}
/>
));

1690
yarn.lock

File diff suppressed because it is too large Load Diff