react-cosmos / rfcs

Change requests for React Cosmos
MIT License
1 stars 2 forks source link

Fixture options #16

Closed ovidiuch closed 2 weeks ago

ovidiuch commented 4 years ago

Follow-up to How to pass parameters to a decorator? https://github.com/react-cosmos/react-cosmos/issues/1234 by @frankandrobot

Need

Cosmos provides a flexible way of creating fixture decorators, but it doesn't provide a way to parametrize decorators per fixture. This was possible in Cosmos Classic (albeit in an unsafe way), and the request has already been raised before by multiple people. There is even a channel on Slack dedicated to this issue.

The official solution for parametrizing decorators at the moment is basically to not use Cosmos decorators and use wrapper components directly in fixtures. This is not a bad solution per se, but it falls short in this situation: What if you have 100 fixtures wrapped by one decorator, and you only want to parametrize the decorator in 10 fixtures? By having to manually add a wrapper component in all fixtures, you change 100 fixtures instead of 10. Ideally, 90 fixtures would use the decorator defaults, and only the other 10 fixtures provide custom decorator params.

Solution

An optional options fixture export. This option represents "fixture options" and can be annotated with a TypeScript/Flow/etc type.

The reason why these options are called "fixture options" and not "decorator params" is because I envision these options to be also used in other places besides decorators, like in a visual regression test suite that, for example, can have its viewport configured through fixture options.

The fixture options are passed as fixtureOptions in decorator props. Currently, decorator props only contain the fixture element as children.

The examples below feature TypeScript, which is 100% optional.

Here's a Redux example, because it's possibly the most popular use case that would benefit from fixture options.

// fixtureHelpers/fixtureOptions.ts (you can put this anywhere in your codebase – only for TS users)
export type FixtureOptions = {
  reduxState: MyReduxState;
};
// cosmos.decorator.tsx
type DecoratorProps = {
  children?: ReactNode;
  fixtureOptions: FixtureOptions;
};

export default ({ children, fixtureOptions }: DecoratorProps) => {
  const { reduxState = defaultReduxState } = fixtureOptions;
  return <Provider store={configureStore(reduxState)}>{children}</Provider>;
};
// MyComponent1.fixture.tsx (uses default Redux state)
export default <MyComponent foo="bar" />;
// MyComponent2.fixture.tsx (uses custom Redux state)
export options: FixtureOptions = {
  reduxState: {
    // ...
  }
}

export default <MyComponent foo="bar" />;

I believe this is a feature that will have a large impact for React Cosmos users. Let me know if you have any feedback. New ideas, as well as examples of other use cases that fixture options can address are more than welcome. Thanks!

frankandrobot commented 4 years ago

Looks good to me 👍. I also can't think of a use case to use a function for options instead a plain object.

ovidiuch commented 2 weeks ago

This was implemented in https://github.com/react-cosmos/react-cosmos/pull/1649.