Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit 7a3d581

Browse files
authored
Merge pull request #29 from WhyThat/feat/adding-flow-support
feat: Adding flow support
2 parents a6b2626 + def727b commit 7a3d581

File tree

9 files changed

+895
-1134
lines changed

9 files changed

+895
-1134
lines changed

.babelrc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
{
22
"presets": [
3+
34
["@babel/preset-env", {
45
"shippedProposals": true, "useBuiltIns": "usage"
5-
}],
6-
"@babel/preset-react"
6+
}],
7+
"@babel/preset-react",
8+
"@babel/preset-flow"
79
]
810
}

.eslintrc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
{
2-
extends: [ "taller/react" ],
3-
globals: {
4-
__DEBUG__: true,
5-
location: true
2+
"parser": "babel-eslint",
3+
"extends": [ "taller/react", "plugin:flowtype/recommended"],
4+
"plugins": [
5+
"flowtype"
6+
],
7+
"globals": {
8+
"__DEBUG__": true,
9+
"location": true
610
}
711
}

.flowconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[ignore]
2+
3+
[include]
4+
5+
[libs]
6+
7+
[lints]
8+
9+
[options]
10+
11+
[strict]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Smart knobs addon for Storybook
22

3-
This Storybook plugin uses `@storybook/addon-knobs` but creates the knobs automatically based on PropTypes.
3+
This Storybook plugin uses `@storybook/addon-knobs` but creates the knobs automatically based on PropTypes and Flow.
44

55
## Installation:
66

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// @flow
2+
import React from 'react'
3+
import './SmartKnobedComponent.css'
4+
5+
/* eslint-disable */
6+
type PropType = {
7+
bool: boolean,
8+
number: number,
9+
string: string,
10+
func: () => void,
11+
oneOf: 'one' | 'two' | 'three',
12+
object: {}
13+
}
14+
/* eslint-enable */
15+
16+
const SmartKnobedComponent = (props: PropType) => (
17+
<table className='SmartKnobedComponent'>
18+
<thead>
19+
<tr>
20+
<th>Property</th>
21+
<th>PropType</th>
22+
<th>Value</th>
23+
<th>typeof</th>
24+
</tr>
25+
</thead>
26+
<tbody>
27+
{Object.keys(props).map(prop => (
28+
<tr key={ prop }>
29+
<th>{ prop }</th>
30+
<td>{ SmartKnobedComponent.__docgenInfo.props[prop].flowType.name }</td>
31+
<td>{ typeof props[prop] === 'function' ? <i>function</i> : JSON.stringify(props[prop]) || '(empty)' }</td>
32+
<td>{ typeof props[prop] }</td>
33+
</tr>
34+
))}
35+
</tbody>
36+
</table>
37+
)
38+
39+
export default SmartKnobedComponent

example/stories/index.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,24 @@ import { withKnobs, select } from '@storybook/addon-knobs'
55

66
import SmartKnobedComponent from './SmartKnobedComponent'
77
import SmartKnobedComponentMissingProps from './SmartKnobedComponentMissingProps'
8+
import SmartKnobedComponentWithFlow from './SmartKnobedComponentWithFlow'
89

910
const stub = fn => fn()
1011

11-
storiesOf('Example of smart Knobs', module)
12+
storiesOf('Basic', module)
1213
.addDecorator(withSmartKnobs)
1314
.addDecorator(withKnobs)
14-
.add('full example', () => <SmartKnobedComponent />)
15+
.add('proptypes', () => <SmartKnobedComponent />)
16+
.add('flow', () => <SmartKnobedComponentWithFlow />)
1517

16-
storiesOf('Smart Knobs missing props', module)
18+
storiesOf('Missing props', module)
1719
.addDecorator(withSmartKnobs)
1820
.addDecorator(withKnobs)
1921
.add('example', () => (
2022
<SmartKnobedComponentMissingProps foo='baz' />
2123
))
2224

23-
storiesOf('Smart Knobs with manual knobs', module)
25+
storiesOf('Manual knobs', module)
2426
.addDecorator(stub)
2527
.addDecorator(withSmartKnobs)
2628
.addDecorator(withKnobs)

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"@babel/core": "^7.2.0",
1818
"@babel/plugin-proposal-class-properties": "^7.2.0",
1919
"@babel/plugin-proposal-export-default-from": "^7.2.0",
20+
"@babel/preset-flow": "^7.0.0",
2021
"@babel/preset-react": "^7.0.0",
2122
"@storybook/addon-actions": "^4.1.1",
2223
"@storybook/addon-info": "^4.1.1",
@@ -27,6 +28,7 @@
2728
"babel-plugin-react-docgen": "^2.0.0",
2829
"css-loader": "^2.0.0",
2930
"eslint-config-taller": "^2.0.0",
31+
"eslint-plugin-flowtype": "^3.0.0",
3032
"jest": "^23.6.0",
3133
"prop-types": "^15.6.2",
3234
"react": "^16.6.3",

src/index.js

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { cloneElement } from 'react'
22
import { action } from '@storybook/addon-actions'
3-
import { withKnobs, text, boolean, number, object, select } from '@storybook/addon-knobs'
3+
import { text, boolean, number, object, select } from '@storybook/addon-knobs'
44

55
const knobResolvers = {}
66
export const addKnobResolver = ({ name, resolver, weight = 0 }) => (knobResolvers[name] = { name, resolver, weight })
@@ -14,6 +14,11 @@ export const propTypeKnobResolver = (name, knob, ...args) =>
1414
(propName, propType, value, propTypes, defaultProps) =>
1515
propType.type.name === name ? knob(propName, value, ...args) : undefined
1616

17+
const flowTypeKnobsMap = [
18+
{ name: 'signature', knob: (name, value) => value || action(name) },
19+
{ name: 'boolean', knob: boolean },
20+
]
21+
1722
/* eslint-disable no-multi-spaces */
1823
// Default simple PropType based knob map.
1924
const propTypeKnobsMap = [
@@ -26,34 +31,42 @@ const propTypeKnobsMap = [
2631
{ name: 'element', knob: text },
2732
]
2833

29-
propTypeKnobsMap.forEach(({ name, knob, args = [] }, weight) => addKnobResolver({
34+
const typeKnobsMap = [...flowTypeKnobsMap, ...propTypeKnobsMap]
35+
36+
typeKnobsMap.forEach(({ name, knob, args = [] }, weight) => addKnobResolver({
3037
weight: weight * 10,
3138
name: `PropTypes.${name}`,
3239
resolver: propTypeKnobResolver(name, knob, ...args)
3340
}))
3441

42+
const optionsReducer = (res, value) => ({ ...res, [value]: value })
43+
const withDefaultOption = (options) => ({ '': '--', ...options })
44+
const createSelect = (propName, elements, defaultProps) => {
45+
try {
46+
const options = elements
47+
// Cleanup string quotes, if any.
48+
.map(value => value.value.replace(/^'(.*)'$/, '$1'))
49+
.reduce(optionsReducer, {})
50+
return select(propName, withDefaultOption(options), defaultProps[propName])
51+
}
52+
catch (e) { }
53+
}
54+
3555
// Register 'oneOf' PropType knob resolver.
3656
addKnobResolver({
3757
name: 'PropTypes.oneOf',
3858
resolver: (propName, propType, value, propTypes, defaultProps) => {
39-
/* eslint-disable quotes */
4059
if (propType.type.name === 'enum' && propType.type.value.length) {
41-
try {
42-
const options = propType.type.value
43-
.map(value => value.value)
44-
// Cleanup string quotes, if any.
45-
.map(value => value[0] === "'" && value[value.length - 1] === "'"
46-
? '"' + value.replace(/'"'/g, '\\"').slice(1, value.length - 1) + '"' : value)
47-
.map(JSON.parse)
48-
.reduce((res, value) => ({ ...res, [value]: value }), {})
49-
50-
return select(propName, { '': '--', ...options }, defaultProps[propName])
51-
}
52-
catch (e) { }
60+
return createSelect(propName, propType.type.value, defaultProps)
61+
}
62+
// for flow support
63+
if (propType.type.name === 'union' && propType.type.elements) {
64+
return createSelect(propName, propType.type.elements, defaultProps)
5365
}
5466
}
5567
})
5668

69+
const ensureType = (item) => item.flowType ? ({ ...item, type: item.flowType }) : item
5770
export const withSmartKnobs = (story, context) => {
5871
const component = story(context)
5972

@@ -68,10 +81,11 @@ export const withSmartKnobs = (story, context) => {
6881
}
6982

7083
const finalProps = props ? Object.keys(props).reduce((acc, n) => {
71-
const item = props[n]
84+
const item = ensureType(props[n])
7285

7386
if (!item.type) {
74-
console.warn(`There is a prop with defaultValue ${item.defaultValue.value} but it wasnt specified on element.propTypes. Check story: "${context.kind}".`)
87+
const defaultValue = item.defaultValue ? item.defaultValue.value : 'Unkwnow'
88+
console.warn(`There is a prop with defaultValue ${defaultValue} but it wasnt specified on element.propTypes. Check story: "${context.kind}".`)
7589
return acc
7690
}
7791

0 commit comments

Comments
 (0)