storybook-eol/addon-smart-knobs

Can't seem to get Smart Knobs working, no error messages.

DylanLyon opened this issue · 3 comments

Hey all,

I've been configuring Storybook to be our new UI documentation method but I'm running into issues with Smart Knobs. I'm not getting any error messages, I'm just not getting automatically filled out knobs.

I can make my own knobs no problem, but getting them to display automatically is causing some pain. Here's my story file, looks identical to the example story in the readme but maybe I'm just missing something? The component is just a simple slider, nothing crazy about it. The only thing I can think of is that importing PropTypes from 'prop-types' might cause some pain? Apparently it's been moved from 'react' so it should still be the same and work fine. I must just be missing some small config that wasn't mentioned in the readme or something...

import React from 'react';
import { storiesOf, addDecorator } from '@storybook/react';
import PropTypes from 'prop-types';
import SliderBar from '../../imports/ui/components/shoreSlider';
import { withKnobs, text, boolean, number } from '@storybook/addon-knobs';
import { withSmartKnobs } from 'storybook-addon-smart-knobs';

const Slider = ({   resultText, 
                    value, 
                    onChange, 
                    min, 
                    max, 
                    step, 
                    defaultValue, 
                    isDisabled }) => (

                    <SliderBar 
                        resultText={resultText} 
                        value={value} 
                        onChange={onChange} 
                        min={min} 
                        max={max} 
                        step={step} 
                        defaultValue={defaultValue} 
                        isDisabled={isDisabled} 
                    />
                )

Slider.displayName = 'Slider';
Slider.propTypes = {
    resultText: PropTypes.string,
    value: PropTypes.number,
    onChange: PropTypes.func,
    min: PropTypes.number,
    max: PropTypes.number,
    step: PropTypes.number,
    defaultValue: PropTypes.number,
    isDisabled: PropTypes.bool,
};


storiesOf('Slider')
    .addDecorator(withSmartKnobs)
    .addDecorator(withKnobs)
    .add('with knobs', () =>   <Slider  /> );

I wrote a decorator that works but is of course a bit less powerful. This is an actual (method) decorator because I wanted to wrap the native API a bit to look more class based (see below).
But I should not be difficult to adjust it to your needs, I guess 😉

import PropTypes from "prop-types"

const decorated = decorator => {
    return (prototype_or_class, name, descriptor) => {
        const wrapped_method = descriptor.value
        descriptor.value = decorator(wrapped_method)
        return descriptor
    }
}

const knobByPropType = new Map()
knobByPropType.set(PropTypes.string, text)
knobByPropType.set(PropTypes.number, number)
knobByPropType.set(PropTypes.bool, boolean)
knobByPropType.set(PropTypes.object, object)
knobByPropType.set(PropTypes.node, text)
knobByPropType.set(PropTypes.element, text)
export const auto_knob = decorated(wrapped_method => {
    return function() {
        const element = wrapped_method.call(this)
        const {propTypes={}, defaultProps={}} = element.type
        const props = {}
        for (const [name, type] of Object.entries(propTypes)) {
            const knob = knobByPropType.get(type)
            if (knob) {
                const defaultValue = defaultProps[name]
                props[name] = knob(name, defaultValue)
            }
        }
        return React.cloneElement(
            element,
            {...element.props, ...props},
        )
    }
})

My API

import {action} from "@storybook/addon-actions"
import {withKnobs} from "@storybook/addon-knobs"

new class MyButtonStories extends Story {
    // This is just used for `.addDecorator`
    static addons = [withKnobs]

    @auto_knob
    with_text() {
        return <Button onClick={action("clicked")}>
            Hello Button
        </Button>
    }
}

.addDecorator(withSmartKnobs())

To anybody reading this in the future: It looks like you can add the decorator withKnobs without parenthesis fine (though it also works as withKnobs(), so I'm not sure whether that's 'correct' or not), but you must add the smart knobs decorator as withSmartKnobs() (note the parenthesis) to get it to work.