Skip to content

Commit

Permalink
feat: build prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
danhayden committed Jul 19, 2017
1 parent c489f60 commit f350db3
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 4 deletions.
8 changes: 8 additions & 0 deletions .storybook/config.js
@@ -0,0 +1,8 @@
import {configure} from '@storybook/react'

function loadStories() {
const storyFiles = require.context('../src/', true, /\.stories\.js$/)
storyFiles.keys().forEach(filename => storyFiles(filename))
}

configure(loadStories, module)
Binary file added docs/favicon.ico
Binary file not shown.
22 changes: 22 additions & 0 deletions docs/iframe.html
@@ -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>Storybook</title>


</head>
<body>
<div id="root"></div>
<div id="error-display"></div>
<script src="static/preview.306107e2529a59ca0606.bundle.js"></script>
</body>
</html>

46 changes: 46 additions & 0 deletions docs/index.html
@@ -0,0 +1,46 @@

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="storybook-version" content="3.1.9">
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
<title>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.7ac5971b94b4969a12fb.bundle.js"></script>
</body>
</html>

1 change: 1 addition & 0 deletions docs/static/manager.7ac5971b94b4969a12fb.bundle.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/static/preview.306107e2529a59ca0606.bundle.js

Large diffs are not rendered by default.

15 changes: 12 additions & 3 deletions package.json
Expand Up @@ -23,8 +23,10 @@
"changelog": "standard-version",
"size": "npx gzip-size-cli ./dist/react-simple-experiment.js ",
"precommit": "lint-staged",
"prepush": "npm test",
"prepublish": "npm run build"
"prepush": "npm test && npm run build-storybook",
"prepublish": "npm run build",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook -o docs"
},
"lint-staged": {
"src/**/*.{js,json}": [
Expand All @@ -35,8 +37,13 @@
"peerDependencies": {
"react": "^15.0.0-0"
},
"dependencies": {},
"dependencies": {
"localforage": "1.5.0",
"pick-one-by-weight": "1.0.2",
"prop-types": "15.5.10"
},
"devDependencies": {
"@storybook/react": "3.1.9",
"babel-eslint": "7.2.3",
"babel-preset-env": "1.6.0",
"babel-preset-react": "6.24.1",
Expand All @@ -55,6 +62,8 @@
"lint-staged": "4.0.2",
"prettier": "1.5.3",
"prettier-eslint-cli": "4.1.1",
"react": "^15.0.0-0",
"react-dom": "15.6.1",
"rollup": "0.45.2",
"rollup-plugin-babel": "2.7.1",
"rollup-plugin-node-resolve": "3.0.0",
Expand Down
7 changes: 7 additions & 0 deletions rollup.config.js
Expand Up @@ -8,6 +8,13 @@ const pkg = JSON.parse(fs.readFileSync('./package.json'))
export default {
entry: 'src/react-simple-experiment.js',
moduleName: 'ReactSimpleExperiment',
external: ['react', 'prop-types', 'pick-one-by-weight', 'localforage'],
globals: {
react: 'React',
'prop-types': 'PropTypes',
'pick-one-by-weight': 'pickOneByWeight',
localforage: 'localforage'
},
targets: [
{dest: pkg.main, format: 'cjs'},
{dest: pkg.module, format: 'es'},
Expand Down
61 changes: 60 additions & 1 deletion src/react-simple-experiment.js
@@ -1 +1,60 @@
export default {}
import React from 'react'
import PropTypes from 'prop-types'
import pickOneByWeight from 'pick-one-by-weight'
import Storage from './storage'

const storage = Storage({name: 'react-simple-experiment'})

export class Experiment extends React.Component {
static displayName = 'Experiment'
static propTypes = {
name: PropTypes.string.isRequired,
children: PropTypes.array.isRequired,
onLoad: PropTypes.func
}

state = {variant: null}

componentDidMount () {
const storageName = `experiment--${this.props.name}`
const variantNames = []
const data = {}

React.Children.forEach(this.props.children, variant => {
variantNames.push(variant.props.name)
data[variant.props.name] = parseInt(variant.props.weight, 10)
})

storage.getItem(storageName).then(variant => {
if (!variant || !variantNames.includes(variant)) {
variant = pickOneByWeight(data)
storage.setItem(storageName, variant)
}

this.setState({variant}, () => {
this.props.onLoad(this.props.name, this.state.variant)
})
})
}

render () {
if (!this.props.children) return null
const variant = this.props.children.find(
child => child.props.name === this.state.variant
)
return variant || null
}
}

export class Variant extends React.Component {
static displayName = 'Variant'
static propTypes = {
name: PropTypes.string.isRequired,
weight: PropTypes.number.isRequired,
children: PropTypes.node
}

render () {
return this.props.children
}
}
22 changes: 22 additions & 0 deletions src/react-simple-experiment.stories.js
@@ -0,0 +1,22 @@
import React from 'react'
import {storiesOf} from '@storybook/react'
import {Experiment, Variant} from './react-simple-experiment'

storiesOf('React Simple Experiment', module).add('Default variation', () =>
<Experiment
name='storybook-test'
onLoad={(name, variant) => console.log(name, variant)}
>
<Variant name='variant1' weight={60}>
<div>Variant1</div>
</Variant>

<Variant name='variant2' weight={20}>
<div>Variant2</div>
</Variant>

<Variant name='variant3' weight={20}>
<div>Variant3</div>
</Variant>
</Experiment>
)
49 changes: 49 additions & 0 deletions src/storage.js
@@ -0,0 +1,49 @@
import localforage from 'localforage'

let storage = {}

function getItem (key) {
return new Promise(resolve => {
try {
localforage
.getItem(key)
.then(value => resolve(value || storage[key]))
.catch(() => resolve(storage[key]))
} catch (error) {
resolve(storage[key])
}
})
}

function setItem (key, value) {
storage[key] = value
return new Promise(resolve => {
try {
// avoid clone error from localforage
const safeValue = JSON.parse(JSON.stringify(value))
localforage.setItem(key, safeValue).then(resolve).catch(resolve)
} catch (error) {
resolve()
}
})
}

function removeItem (key) {
if (storage[key]) delete storage[key]
return new Promise(resolve => {
try {
localforage.removeItem(key).then(resolve).catch(resolve)
} catch (error) {
resolve()
}
})
}

export default function (options) {
localforage.config(options)
return {
getItem,
setItem,
removeItem
}
}

0 comments on commit f350db3

Please sign in to comment.