Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Portable stories: Pass story context to the play function of a composed story #25943

Merged
merged 1 commit into from
Feb 8, 2024

Conversation

yannbf
Copy link
Member

@yannbf yannbf commented Feb 7, 2024

Closes #25877

What I did

Before, the play function in a portable story would need to receive the entire story context. Now, the context is passed internally, plus the user can still override whatever they need.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

import { render, screen } from '@testing-library/react';
import { composeStory } from '@storybook/react';
import { vi, test, expect } from 'vitest';
import Meta, { Primary as PrimaryStory } from './Button.stories';

// Returns a component that already contain all decorators from story level, meta level and global level.
const Primary = composeStory(PrimaryStory, Meta);

test('onclick handler is called', async () => {
  const { container } = render(<Primary />);
  // this should call the play function with a full context, not just canvasElement
  await Primary.play({ canvasElement: container });
});

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook canary-release-pr.yml --field pr=<PR_NUMBER>

@yannbf yannbf force-pushed the yann/improve-play-fn-portable-stories branch from 2ce910f to a0f3e6b Compare February 8, 2024 11:05
@yannbf yannbf force-pushed the yann/improve-play-fn-portable-stories branch from a0f3e6b to 8e2a375 Compare February 8, 2024 12:06
@yannbf yannbf merged commit ea81001 into next Feb 8, 2024
58 checks passed
@yannbf yannbf deleted the yann/improve-play-fn-portable-stories branch February 8, 2024 13:26
@github-actions github-actions bot mentioned this pull request Feb 8, 2024
17 tasks
@skratchdot
Copy link

@yannbf - this is causing my unit tests to fail now. i use composeStories with a custom createStoryTests which runs all my stories through wtr (using a real browser)

My code looks like:

import { ReactRenderer, composeStories } from '@storybook/react';

import { ComposedStoryFn } from '@storybook/types';
import { createElement } from 'react';
import { render } from '@testing-library/react';

type AllStories = Parameters<typeof composeStories>[0];

type ComposedStory = ComposedStoryFn<ReactRenderer, Partial<unknown>>;

const buildTest = (storyName: string, Story: ComposedStory) => {
  it(storyName, async () => {
    const { container } = render(createElement(Story));
    if (typeof Story.play === 'function') {
      await Story.play({ canvasElement: container, ...Story });
    }
  });
};

export const createStoryTests = (stories: AllStories, title?: string) => {
  const storyId =
    title ?? stories.default.title ?? 'no title specified in default export';
  describe(`Composed Story Tests: ${storyId}`, () => {
    const composedStories = composeStories(stories);
    const entries = Object.entries<ComposedStory>(composedStories);
    for (const [storyName, Story] of entries) {
      buildTest(storyName, Story);
    }
  });
};

Now every story in my codebase that does not have a play function is erroring out. Is there any way we can default to not throwing an error (or making it a config like shouldThrowError)?

Maybe changing:

play: (async (extraContext: ComposedStoryPlayContext<TRenderer, TArgs>) => {
if (story.playFunction === undefined) {
throw new Error('The story does not have a play function. Make sure to add one.');
}
await story.playFunction({
...context,
...extraContext,
});
}) as unknown as ComposedStoryPlayFn<TRenderer, Partial<TArgs>>,

to:

      play: (async (extraContext: ComposedStoryPlayContext<TRenderer, TArgs>) => {
        if (typeof story.playFunction === 'function') {
          await story.playFunction({
            ...context,
            ...extraContext,
          });
        }
      }) as unknown as ComposedStoryPlayFn<TRenderer, Partial<TArgs>>,

@skratchdot
Copy link

The workaround for me is changing:

const composedStories = composeStories(stories);

to:

const composedStories = composeStories({
  ...stories,
  default: {
    play: () => {},
    ...stories.default,
  },
});

@yannbf
Copy link
Member Author

yannbf commented Feb 13, 2024

Thanks for flagging this @skratchdot, this will be fixed in the next beta release shortly!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Portable stories - Pass story context to play function
3 participants