CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-storybook--react

React renderer for Storybook framework providing TypeScript support, portable stories, and React-specific functionality

Pending
Overview
Eval results
Files

portable-stories.mddocs/

Portable Stories

Functionality for using Storybook stories outside of the Storybook environment, particularly useful for unit testing, component documentation, and integration testing. Portable stories allow you to reuse your story definitions in Jest, Vitest, Playwright, and other testing frameworks.

Type Dependencies

The portable stories API depends on core Storybook types:

// Main portable stories exports
import { setProjectAnnotations, composeStory, composeStories } from "@storybook/react";

// Core Storybook internal types
import type {
  Args,
  NamedOrDefaultProjectAnnotations,
  NormalizedProjectAnnotations,
  ProjectAnnotations,
  StoryAnnotationsOrFn,
  Store_CSFExports,
  StoriesWithPartialProps,
  ComposedStoryFn
} from "storybook/internal/types";

// React renderer type (see React Renderer Types documentation)
import type { ReactRenderer } from "./react-renderer.md#react-renderer-interface";

// Meta type (see Story Types & Metadata documentation)  
import type { Meta } from "./story-types.md#meta-type";

Capabilities

Set Project Annotations

Configures global project settings for story composition, typically used in test setup files.

/**
 * Sets the global configuration for story composition.
 * Should be called once in your test setup to apply global decorators, parameters, and other configurations.
 * 
 * @param projectAnnotations - Global project configuration (e.g., from .storybook/preview.ts)
 * @returns Normalized project annotations for the React renderer
 */
function setProjectAnnotations(
  projectAnnotations:
    | NamedOrDefaultProjectAnnotations<any>
    | NamedOrDefaultProjectAnnotations<any>[]
): NormalizedProjectAnnotations<ReactRenderer>;

Usage Example:

// setup-tests.ts
import { setProjectAnnotations } from "@storybook/react";
import * as projectAnnotations from "../.storybook/preview";

// Apply global configuration to all composed stories
setProjectAnnotations(projectAnnotations);

Compose Story

Creates a composed story component from a story definition and meta, ready for use in testing or other contexts.

/**
 * Composes a single story with its metadata and configuration into a React component.
 * The resulting component can be rendered directly in tests or other environments.
 * 
 * @param story - Story definition (object or function)
 * @param componentAnnotations - Component metadata (Meta export)
 * @param projectAnnotations - Optional project configuration (if not set globally)
 * @param exportsName - Optional name for the story (for debugging)
 * @returns Composed story component ready for rendering
 */
function composeStory<TArgs extends Args = Args>(
  story: StoryAnnotationsOrFn<ReactRenderer, TArgs>,
  componentAnnotations: Meta<TArgs | any>,
  projectAnnotations?: ProjectAnnotations<ReactRenderer>,
  exportsName?: string
): ComposedStoryFn<ReactRenderer, Partial<TArgs>>;

Usage Example:

// Button.test.tsx
import { render, screen } from "@testing-library/react";
import { composeStory } from "@storybook/react";
import Meta, { Primary, Secondary } from "./Button.stories";

// Compose individual stories
const PrimaryButton = composeStory(Primary, Meta);
const SecondaryButton = composeStory(Secondary, Meta);

test("renders primary button", () => {
  render(<PrimaryButton />);
  expect(screen.getByRole("button")).toHaveClass("primary");
});

test("renders secondary button with custom props", () => {
  render(<SecondaryButton label="Custom Label" />);
  expect(screen.getByText("Custom Label")).toBeInTheDocument();
});

// Test with overridden args
test("renders button with specific size", () => {
  render(<PrimaryButton size="large" />);
  expect(screen.getByRole("button")).toHaveClass("large");
});

Compose Stories

Creates composed components for all stories in a story file, providing an object with all stories ready for testing.

/**
 * Composes all stories from a story file into an object of React components.
 * Each story becomes a component that can be rendered independently.
 * 
 * @param csfExports - All exports from a .stories file (import * as stories)
 * @param projectAnnotations - Optional project configuration (if not set globally)
 * @returns Object containing all composed stories, excluding CSF metadata
 */
function composeStories<TModule extends Store_CSFExports<ReactRenderer, any>>(
  csfExports: TModule,
  projectAnnotations?: ProjectAnnotations<ReactRenderer>
): Omit<StoriesWithPartialProps<ReactRenderer, TModule>, keyof Store_CSFExports>;

Usage Example:

// Button.test.tsx
import { render, screen } from "@testing-library/react";
import { composeStories } from "@storybook/react";
import * as stories from "./Button.stories";

// Compose all stories from the file
const { Primary, Secondary, Large, Small } = composeStories(stories);

describe("Button Stories", () => {
  test("Primary story renders correctly", () => {
    render(<Primary />);
    expect(screen.getByRole("button")).toHaveClass("primary");
  });

  test("Secondary story renders correctly", () => {
    render(<Secondary />);
    expect(screen.getByRole("button")).toHaveClass("secondary");
  });

  // Test all stories programmatically
  Object.entries(composeStories(stories)).forEach(([name, Story]) => {
    test(`${name} story renders without errors`, () => {
      render(<Story />);
      expect(screen.getByRole("button")).toBeInTheDocument();
    });
  });
});

Advanced Testing Patterns

Testing with React Testing Library:

import { render, screen, fireEvent } from "@testing-library/react";
import { composeStory } from "@storybook/react";
import Meta, { Interactive } from "./Button.stories";

const InteractiveButton = composeStory(Interactive, Meta);

test("button handles click events", async () => {
  const mockFn = vi.fn();
  render(<InteractiveButton onClick={mockFn} />);
  
  fireEvent.click(screen.getByRole("button"));
  expect(mockFn).toHaveBeenCalledTimes(1);
});

Testing with Custom Context:

import { render } from "@testing-library/react";
import { composeStory } from "@storybook/react";
import { ThemeProvider } from "./ThemeProvider";
import Meta, { Themed } from "./Button.stories";

const ThemedButton = composeStory(Themed, Meta);

test("button renders with custom theme", () => {
  render(
    <ThemeProvider theme="dark">
      <ThemedButton />
    </ThemeProvider>
  );
  // Test themed button behavior
});

Playwright Integration:

import { test, expect } from "@playwright/test";
import { composeStories } from "@storybook/react";
import * as stories from "./Button.stories";

const { Primary } = composeStories(stories);

test("visual regression test", async ({ page }) => {
  // Mount the composed story in Playwright
  await page.goto("/test-page");
  await page.evaluate(() => {
    // Render the composed story
    const root = ReactDOM.createRoot(document.getElementById("root"));
    root.render(React.createElement(Primary));
  });
  
  await expect(page).toHaveScreenshot("primary-button.png");
});

Internal Default Annotations

Default project configuration used internally by the React renderer.

/**
 * Internal default project annotations for React renderer.
 * Includes React-specific rendering configuration and compatibility layers.
 */
const INTERNAL_DEFAULT_PROJECT_ANNOTATIONS: ProjectAnnotations<ReactRenderer>;

This constant is used internally and typically doesn't need to be accessed directly, but it provides the base React rendering configuration that can be extended with custom project annotations.

Type Definitions

Composed Story Function Type

/**
 * Type for composed story functions that can be rendered as React components.
 * Supports partial args and maintains type safety.
 */
type ComposedStoryFn<TRenderer extends Renderer, TArgs> = React.ComponentType<Partial<TArgs>> & {
  args: TArgs;
  argTypes: ArgTypes;
  parameters: Parameters;
  storyName: string;
};

Story Annotations Type

/**
 * Type for story definitions that can be functions or objects.
 * Used by composeStory for flexible story input.
 */
type StoryAnnotationsOrFn<TRenderer extends Renderer, TArgs> = 
  | StoryAnnotations<TRenderer, TArgs> 
  | AnnotatedStoryFn<TRenderer, TArgs>;

Install with Tessl CLI

npx tessl i tessl/npm-storybook--react

docs

index.md

portable-stories.md

preview-configuration.md

react-renderer.md

story-types.md

testing-integration.md

tile.json