#Difference between render and decorators in V7

9 messages · Page 1 of 1 (latest)

white bloom
#

I am using a mobx store to fetch the options for status. And before rendering the story I want to format the props.
Whenever that status is changed (using storybook control), I need to format the app data(which will be passed as the props to the component) before the storybook renders that story.

There are 2 question for me :

  1. Whether my code is correct ? Because the status is not going to be a prop rather the data will be prop. But I have assinged a argType for status.
  2. I can format the app data before it renders in 2 way one using the decorators and the other using the render. Both works as expected and so I am confused now, which is the appropriate or the permissible way to use, either the decorators or the render.

export default {
title: “Component/New",
component: Indicator,
argTypes: {
status: {
control: { type: "select" },
options: Object.keys(FlowStatus)
}
},
decorators: [
(Story, context) => {
const { store } = useStores();
const app = store.apps[0];
context.args.app = app;
return <Story app={getApp(app, context.args.Status)} />;
}
],
render: args => {
const { store } = useStores();
const app = store.apps[0];
return <Indicator app={getApp(app, args.Status} />;
}
};

export const Default = {
name: “New”
};

covert granite
#

Hi! 👋

First, to answer questions:

  1. Your code is close to correct, I think! It's totally OK to have an argType that doesn't correspond to a prop accepted by your component. A common pattern is exactly what you're doing: add a control for something then do some processing to be able to pass the actual prop to the component in either a decorator or custom render function.

  2. Either decorator or render is fine in CSF 3. I think I'd prefer the render function here.

Second, if you choose to use the decorator or need to write another one later, this line will not work correctly: context.args.app = app. You cannot write to args like that. Instead, you pass whatever args you want to the story like this:

(Story, context) => {
  const { store } = useStores();
  const app = store.apps[0];
  const args = {
    ...context.args,
    app: getApp(app, context.args.Status)
  }
  return <Story args={args} />;
}

If you do need to manipulate args, we have the useArgs hook for that: https://storybook.js.org/docs/react/addons/addons-api#useargs

white bloom
#

Great, Thanks !

white bloom
#

Can we have render defined at the component level not in the stories.

#

Wanted to know whether is the best practice

covert granite
#

I'm not sure I understand. Can you share an example to illustrate?

white bloom
#

export default {
title: “Component/New",
component: Indicator,
argTypes: {
status: {
control: { type: "select" },
options: Object.keys(FlowStatus)
}
},
render: args => {
return <Indicator app={getApp(app, args.Status} />;
}
};

export const Primary = {
args : {name: “Primary”}
};
export const Secondary = {
args :{ name: “Secondary”}
};

Here render is defined in the component and not for each stories(Primary, Secondary). My question here is can we define render in the component and use it in stories, similar to component args (but for render) (https://storybook.js.org/docs/react/writing-stories/args#component-args)

Storybook is a frontend workshop for building UI components and pages in isolation. Thousands of teams use it for UI development, testing, and documentation. It’s open source and free.

covert granite
#

Oh, yes, that's exactly how it works. render defined in your meta (export default) applies to all stories in that file.

white bloom
#

Cool , Thanks !