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

Groups of knobs to organize multiple components on the same page #75

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions example/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,13 @@ storiesOf('Ignore Props', module)
.addDecorator(withSmartKnobs({ ignoreProps: ['number'] }))
.addDecorator(withKnobs)
.add('proptypes', () => <SmartKnobedComponent number={ date('date', new Date()) } />)

storiesOf('Multiple components', module)
.addDecorator(withSmartKnobs())
.addDecorator(withKnobs)
.add('example', () => (
<>
<SmartKnobedComponent />
<SmartKnobedComponent />
</>
))
41 changes: 26 additions & 15 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@ export const addKnobResolver = ({ name, resolver, weight = 0 }) => (knobResolver
* --------------------------------
*/

export const propTypeKnobParams = (knob, propName, value, groupId, ...args) => {
switch (propName) {
case 'number':
return knob(propName, value, {}, groupId, ...args)
default:
return knob(propName, value, groupId, ...args)
}
}

export const propTypeKnobResolver = (name, regexp, knob, ...args) =>
(propName, propType, value) =>
(propName, propType, value, groupId) =>
(propType.type.name === name || (regexp && regexp.test(propType.type.name)))
? knob(propName, value, ...args)
? propTypeKnobParams(knob, propName, value, groupId, ...args)
: undefined

const flowTypeKnobsMap = [
Expand Down Expand Up @@ -49,35 +58,35 @@ typeKnobsMap.forEach(({ name, regexp, knob, args = [] }, weight) => addKnobResol

const optionsReducer = (res, value) => ({ ...res, [value]: value })
const withDefaultOption = (options) => ({ '--': undefined, ...options })
const createSelect = (propName, elements, defaultValue, isRequired) => {
const createSelect = (propName, elements, defaultValue, isRequired, groupId) => {
try {
const options = elements
// Cleanup string quotes, if any.
.map(value => cleanupValue(value.value))
.reduce(optionsReducer, {})
const value = defaultValue || (isRequired && Object.values(options)[0]) || undefined
return select(propName, isRequired ? options : withDefaultOption(options), value)
return select(propName, isRequired ? options : withDefaultOption(options), value, groupId)
}
catch (e) { }
}

// Register 'oneOf' PropType knob resolver.
addKnobResolver({
name: 'PropTypes.oneOf',
resolver: (propName, propType, defaultValue) => {
resolver: (propName, propType, defaultValue, groupId) => {
if (propType.type.name === 'enum' && propType.type.value.length) {
return createSelect(propName, propType.type.value, defaultValue, propType.required)
return createSelect(propName, propType.type.value, defaultValue, propType.required, groupId)
}
// for flow support
if (propType.type.name === 'union' && propType.type.elements) {
return createSelect(propName, propType.type.elements, defaultValue, propType.required)
return createSelect(propName, propType.type.elements, defaultValue, propType.required, groupId)
}
}
})

const ensureType = (item) => item.flowType ? ({ ...item, type: item.flowType }) : item

const getNewProps = (target, context, opts) => {
const getNewProps = (target, context, opts, id) => {
const { __docgenInfo: { props } = { props: {} } } = target.type
const { ignoreProps = [] } = opts
const defaultProps = {
Expand All @@ -104,19 +113,21 @@ const getNewProps = (target, context, opts) => {
}
}, {}) : {}

return resolvePropValues(finalProps, defaultProps)
return resolvePropValues(finalProps, defaultProps, id)
}

const mutateChildren = (component, context, opts) => {
return cloneElement(component, { children: Children.map(component.props.children, (child) => {
const mutateChildren = (component, context, opts, index = 0) => {
const shouldGroup = Children.count(component.props.children) > 1

return cloneElement(component, { children: Children.map(component.props.children, (child, i) => {
if (child.type && child.type.__docgenInfo) {
const newProps = getNewProps(child, context, opts)
const newProps = getNewProps(child, context, opts, shouldGroup ? index + '-' + i : null)

return cloneElement(child, { ...child.props, ...newProps })
}

if (child.props && child.props.children) {
return mutateChildren(child, context, opts)
return mutateChildren(child, context, opts, index + 1)
}

return child
Expand All @@ -135,7 +146,7 @@ export const withSmartKnobs = (opts = {}) => (story, context) => {
return cloneElement(component, newProps)
}

const resolvePropValues = (propTypes, defaultProps) => {
const resolvePropValues = (propTypes, defaultProps, groupId) => {
const propNames = Object.keys(propTypes)
const resolvers = Object.keys(knobResolvers)
.sort((a, b) => knobResolvers[a].weight < knobResolvers[b].weight)
Expand All @@ -148,7 +159,7 @@ const resolvePropValues = (propTypes, defaultProps) => {
const defaultValue = defaultProps[propName] || (propType.defaultValue && cleanupValue(propType.defaultValue.value || '')) || undefined

return value !== undefined ? value
: resolver(propName, propType, defaultValue)
: resolver(propName, propType, defaultValue, groupId)
},
undefined
))
Expand Down