简体   繁体   中英

How to update components props in storybook

I am using storybook ( this ) to play with my components in isolation. I want to mock all the flux cycle (that in the full app it is done with the help of redux ) and update a property using a simple object in the story, but I am missing something.

  storiesOf('Color picker', module).add('base', () => {
    let colorPickerState = {
      changeColor: function(data) {
        this.color = data.color
      },
      color: '#00aced'
    }
    return (
      <ColorPicker
        name="color"
        onChange={colorPickerState.changeColor.bind(colorPickerState)}
        value={colorPickerState.color}
      />
    )
  }

I expect the value prop of <ColorPicker /> to be updated when the onChange is called; I can see the value of colorPickerState.color being updated correctly, but the component does not re-render.

What am I missing?

You can write a dummy-component which will render the real story component inside it, and then you get to have that dummy-component 's state property.

In the below example I'm using knobs addon in a story of a Slider component

stories.addDecorator(withKnobs)
    .add('Slider', () => {
        // create dummy component that wraps the Slider and allows state:
        class StoryComp extends React.Component {
            constructor( props ){
                super(props);

                this.state = {
                    value : this.props.value || 0,
                }
            }

            onValueChange = value => this.setState({ value })

            render(){
                const props = {
                    ...this.props, 
                    onValueChange:this.onValueChange, // <--- Reason "StoryComp" is needed
                    value:this.state.value            // <--- Reason "StoryComp" is needed
                }

                return <Slider {...props} />
            }
        }

        // knobs (customaziable props)
        const widthKnobOptions = {
            range : true,
            min   : 200,
            max   : 1500,
            step  : 1
        }

        const props = {
            value : number('value', 200000),
            min   : number('min', 100),
            step  : number('step', 1000),
            max   : number('max', 1000000),
            width : number('width', 700, widthKnobOptions)
        }

        return <StoryComp {...props} />
    }
);

You can use an addon to achieve this: https://github.com/Sambego/storybook-state

So your code would look like:

import { State, Store } from '@sambego/storybook-state';

const store = new Store({
  value: '#00aced',
});

storiesOf('Color picker', module).add('base', () => {
  return (
    <State store={store}>
      <ColorPicker
        name="color"
        onChange={(data) => store.set({ value: data.color })}
      />
    </State>
  )
}

I would try employing the useState hook from react - updating state values via its setters seems to have the effect of re-rendering your storybook component.

I had a similar problem where my data input form's state and event handling (including complex validation) is done in globally, up a level in the component tree. I was able to get a working demo of this component and its complex validation by writing a simple event handler to pass along to my component.

Do note that my code is in Typescript. I am pretty new to the React world so this might not be perfect.

// Things defined elsewhere: IComponentProps, IValidationResult, IFormInput, EntryForm
// useState and useCallbac comes from react
export const MyComponentDemo = (args: IComponentProps) => {

    const [validations, setValidations] = useState<IValidationResult[]>([]);
    const [theForm, setTheForm] = useState<IFormInput>(
        {
            ...args.formValues,
            // set other interesting default form values here
        }
    );

    const dummyValidationMethod = useCallback((form: IFormInput) => {

        let validations : IValidationResult[] = [];

        // Do validation using data from form

        setValidations(validations);
        setTheForm(form);

    }, []);

    return (
        <EntryForm
            {...args}
            formValues={theForm}
            onValidation={dummyValidationMethod}
            validatedLocations={validations}
        />
    );
};

In the latest version (v6) this functionality calls Args . Also you can use argTypes to see action's logs or specify props.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM