storybookjs / testing-react

Testing utilities that allow you to reuse your Storybook stories in your React unit tests!
MIT License
588 stars 24 forks source link

Component exports fail type checking if not explicitly typed #76

Closed IanVS closed 2 years ago

IanVS commented 2 years ago

Describe the bug Using v1.2.2, I started getting some type failures in my code:

src/components/atoms/buttons/hamburger/Hamburger.test.tsx:4:9 - error TS2339: Property 'Closed' does not exist on type 'Omit<StoriesWithPartialProps<typeof import("/Users/ianvs/code/project/src/components/atoms/buttons/hamburger/Hamburger.stories")>, keyof StoryFile>'.

4 const { Closed, Controls } = composeStories(Stories);
          ~~~~~~

To Reproduce Here's a reproduction of what I'm seeing: https://stackblitz.com/edit/node-grzpdq?file=src/app.test.tsx

If I have a story file like this:

import * as React from 'react';
import type { ComponentStory, Meta } from '@storybook/react';
import { App } from './app';

const meta: Meta = {
  title: 'App',
  component: App,
};
export default meta;

export const Args: ComponentStory<typeof App> = (args) => {
  return <App {...args} />;
};

export const NoArgs = () => {
  return <App />;
};

Then when I destructure from composeStories, I get an error on NoArgs, but not Args.

Expected behavior I should continue to be able to use type inference on exported story components.

yannbf commented 2 years ago

Hey @IanVS, thanks for opening this issue!

This behaviour is actually correct. The type StoriesWithPartialProps infers stories that either extend StoryFn or StoryObj. If you're using Storybook 6.4, the types ComponentStory | Story extend StoryFn (will likely extend StoryObj in 6.5 or 7.0).

Basically, you could have Non-story exports that happen to be functions, and they would be picked up by the types, which is wrong. The solution is to just type your stories:

import * as React from 'react';
-import type { ComponentStory, Meta } from '@storybook/react';
+import type { Story, ComponentStory, Meta } from '@storybook/react';
import { App } from './app';

const meta: Meta = {
  title: 'App',
  component: App,
};
export default meta;

export const Args: ComponentStory<typeof App> = (args) => {
  return <App {...args} />;
};

-export const NoArgs = () => {
+export const NoArgs: Story = () => {
  return <App />;
};
IanVS commented 2 years ago

Ok interesting. Is that documented anywhere already, that I missed? If not, at least people can find this issue now and hopefully not be as confused as I was. ;). Thanks for the response!

yannbf commented 2 years ago

Ok interesting. Is that documented anywhere already, that I missed? If not, at least people can find this issue now and hopefully not be as confused as I was. ;). Thanks for the response!

Yes it's been documented here: https://github.com/storybookjs/testing-react#disclaimer

If you feel like it could be improved though, we can definitely do that