Skip to content

Conversation

@kasperpeulen
Copy link
Contributor

@kasperpeulen kasperpeulen commented Dec 15, 2025

Closes #

What I did

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

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

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-storybook/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 publish.yml --field pr=<PR_NUMBER>

Summary by CodeRabbit

  • New Features

    • Added a Vue 3 preview integration with typed meta/story APIs and exported preview surface.
  • Refactor

    • Tightened and clarified TypeScript typings for React and Vue story/meta APIs.
    • Story factory API now accepts optional argument forms.
    • Improved story discovery and annotation normalization during processing.
  • Tests

    • Added comprehensive Vue3 CSF tests exercising typing, decorators, slots, and play behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings December 15, 2025 14:09
@nx-cloud
Copy link

nx-cloud bot commented Dec 15, 2025

View your CI Pipeline Execution ↗ for commit 6e5a012

Command Status Duration Result
nx run-many -t compile,check,knip,test,pretty-d... ✅ Succeeded 9m 40s View ↗

☁️ Nx Cloud last updated this comment at 2025-12-16 10:22:26 UTC

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Changed CSF story/test dispatch and story detection logic; tightened React preview meta/story typings and tests; added a new Vue 3 preview surface with strongly-typed meta/story APIs and associated tests and re-exports.

Changes

Cohort / File(s) Summary
CSF factories & processing
code/core/src/csf/csf-factories.ts, code/core/src/preview-api/modules/store/csf/processCSFFile.ts
Dispatch path for test-play now destructures { mount, context } (with lint-ignore for unused mount); story detection uses factoryStory rather than assuming first export; normalization and project annotations use factoryStory.meta.*; export iteration now verifies exported items are stories.
React preview typings & tests
code/renderers/react/src/preview.tsx, code/renderers/react/src/csf-factories.test.tsx
Adjusted ReactPreview.meta/ReactMeta.story signatures (Omit-based args reshaping, story parameter made non-optional in primary overloads, added variadic conditional overload); added ts-expect-error marker and updated tests to allow meta.story() without args and to validate meta.input.play composition.
Vue 3 preview API & tests
code/renderers/vue3/src/preview.tsx, code/renderers/vue3/src/csf-factories.test.ts, code/renderers/vue3/src/index.ts, code/renderers/vue3/src/types.ts
Added __definePreview factory and VuePreview/VueMeta/VueStory types plus utilities (DecoratorsArgs, InferArgs, etc.); new CSF factory tests validating typing, decorators, slots, and play/mount behavior; re-exported preview and added VueTypes alias.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • code/renderers/react/src/preview.tsx — complex conditional/variadic overloads and Omit-based args reshaping.
    • code/renderers/vue3/src/preview.tsx — new, dense generic types and overloads tying decorators, props, and args.
    • code/core/src/preview-api/modules/store/csf/processCSFFile.ts — story detection and normalization changes that affect downstream composition.

Possibly related issues

Possibly related PRs

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
code/renderers/vue3/src/preview.tsx (2)

20-20: Use type-only import for ComponentProps.

ComponentProps from vue-component-type-helpers appears to be used only as a type utility. Using import type ensures it's erased at compile time and prevents potential bundling issues.

-import { ComponentProps } from 'vue-component-type-helpers';
+import type { ComponentProps } from 'vue-component-type-helpers';

24-24: Use type-only import for VueTypes.

VueTypes is only used in type positions (type parameters, extends clauses). Using import type is more appropriate.

-import { VueTypes } from './types';
+import type { VueTypes } from './types';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4033f33 and a6d79ae.

📒 Files selected for processing (5)
  • code/core/src/csf/csf-factories.ts (1 hunks)
  • code/core/src/preview-api/modules/store/csf/processCSFFile.ts (2 hunks)
  • code/renderers/react/src/csf-factories.test.tsx (2 hunks)
  • code/renderers/react/src/preview.tsx (4 hunks)
  • code/renderers/vue3/src/preview.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/renderers/vue3/src/preview.tsx
  • code/renderers/react/src/csf-factories.test.tsx
  • code/core/src/csf/csf-factories.ts
  • code/renderers/react/src/preview.tsx
  • code/core/src/preview-api/modules/store/csf/processCSFFile.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode

Files:

  • code/renderers/vue3/src/preview.tsx
  • code/renderers/react/src/csf-factories.test.tsx
  • code/core/src/csf/csf-factories.ts
  • code/renderers/react/src/preview.tsx
  • code/core/src/preview-api/modules/store/csf/processCSFFile.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/vue3/src/preview.tsx
  • code/renderers/react/src/csf-factories.test.tsx
  • code/core/src/csf/csf-factories.ts
  • code/renderers/react/src/preview.tsx
  • code/core/src/preview-api/modules/store/csf/processCSFFile.ts
code/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions that need to be tested from their modules

Files:

  • code/renderers/vue3/src/preview.tsx
  • code/renderers/react/src/csf-factories.test.tsx
  • code/core/src/csf/csf-factories.ts
  • code/renderers/react/src/preview.tsx
  • code/core/src/preview-api/modules/store/csf/processCSFFile.ts
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/renderers/vue3/src/preview.tsx
  • code/renderers/react/src/csf-factories.test.tsx
  • code/core/src/csf/csf-factories.ts
  • code/renderers/react/src/preview.tsx
  • code/core/src/preview-api/modules/store/csf/processCSFFile.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{test,spec}.{ts,tsx}: Test files should follow the naming pattern *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx
Follow the spy mocking rules defined in .cursor/rules/spy-mocking.mdc for consistent mocking patterns with Vitest

Files:

  • code/renderers/react/src/csf-factories.test.tsx
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)

**/*.test.{ts,tsx,js,jsx}: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access the mocked functions in Vitest tests
Implement mock behaviors in beforeEach blocks in Vitest tests
Mock all required dependencies that the test subject uses
Each mock implementation should return a Promise for async functions in Vitest
Mock implementations should match the expected return type of the original function
Mock all required properties and methods that the test subject uses in Vitest tests
Avoid direct function mocking without vi.mocked() in Vitest tests
Avoid mock implementations outside of beforeEach blocks in Vitest tests
Avoid mocking without the spy: true option in Vitest tests
Avoid inline mock implementations within test cases in Vitest tests
Avoid mocking only a subset of required dependencies in Vitest tests
Mock at the highest level of abstraction needed in Vitest tests
Keep mock implementations simple and focused in Vitest tests
Use type-safe mocking with vi.mocked() in Vitest tests
Document complex mock behaviors in Vitest tests
Group related mocks together in Vitest tests

Files:

  • code/renderers/react/src/csf-factories.test.tsx
code/**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{test,spec}.{ts,tsx,js,jsx}: Write meaningful unit tests that actually import and call the functions being tested
Mock external dependencies using vi.mock() for file system, loggers, and other external dependencies in tests
Aim for high test coverage of business logic (75%+ for statements/lines) using coverage reports

Files:

  • code/renderers/react/src/csf-factories.test.tsx
🧠 Learnings (16)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Follow existing patterns and conventions in the Storybook codebase
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/renderers/vue3/src/preview.tsx
  • code/renderers/react/src/preview.tsx
  • code/core/src/preview-api/modules/store/csf/processCSFFile.ts
📚 Learning: 2025-11-05T09:37:25.920Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.

Applied to files:

  • code/renderers/vue3/src/preview.tsx
  • code/renderers/react/src/preview.tsx
📚 Learning: 2025-11-05T09:38:47.712Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.

Applied to files:

  • code/renderers/react/src/csf-factories.test.tsx
  • code/renderers/react/src/preview.tsx
📚 Learning: 2025-11-05T09:36:55.944Z
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Tabs/Tabs.stories.tsx:222-227
Timestamp: 2025-11-05T09:36:55.944Z
Learning: Repo: storybookjs/storybook PR: 32458 — In code/core/src/components/components/Button/Button.tsx (React/TypeScript), ButtonProps includes ariaLabel?: string | false and the component maps it to the DOM aria-label. Convention: ariaLabel is mandatory on all Button usages — provide a descriptive string for icon-only buttons; set ariaLabel=false when the button’s children already serve as the accessible name. Do not suggest using a raw aria-label prop on Button call sites.

Applied to files:

  • code/renderers/react/src/csf-factories.test.tsx
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases in Vitest tests

Applied to files:

  • code/core/src/csf/csf-factories.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies in Vitest tests

Applied to files:

  • code/core/src/csf/csf-factories.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mock implementations outside of `beforeEach` blocks in Vitest tests

Applied to files:

  • code/core/src/csf/csf-factories.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx} : Export functions that need to be tested from their modules

Applied to files:

  • code/core/src/csf/csf-factories.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid direct function mocking without `vi.mocked()` in Vitest tests

Applied to files:

  • code/core/src/csf/csf-factories.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors in Vitest tests

Applied to files:

  • code/core/src/csf/csf-factories.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{test,spec}.{ts,tsx,js,jsx} : Write meaningful unit tests that actually import and call the functions being tested

Applied to files:

  • code/core/src/csf/csf-factories.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused in Vitest tests

Applied to files:

  • code/core/src/csf/csf-factories.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking without the `spy: true` option in Vitest tests

Applied to files:

  • code/core/src/csf/csf-factories.ts
📚 Learning: 2025-11-24T17:49:31.838Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T17:49:31.838Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Follow the spy mocking rules defined in `.cursor/rules/spy-mocking.mdc` for consistent mocking patterns with Vitest

Applied to files:

  • code/core/src/csf/csf-factories.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Each mock implementation should return a Promise for async functions in Vitest

Applied to files:

  • code/core/src/csf/csf-factories.ts
🧬 Code graph analysis (4)
code/renderers/vue3/src/preview.tsx (1)
code/core/src/csf/csf-factories.ts (5)
  • PreviewAddon (64-65)
  • InferTypes (34-36)
  • Meta (77-96)
  • story (114-118)
  • Story (122-154)
code/core/src/csf/csf-factories.ts (1)
code/frameworks/angular/src/client/types.ts (1)
  • StoryContext (49-49)
code/renderers/react/src/preview.tsx (1)
code/core/src/csf/csf-factories.ts (1)
  • story (114-118)
code/core/src/preview-api/modules/store/csf/processCSFFile.ts (2)
code/core/src/csf/csf-factories.ts (1)
  • isStory (156-158)
code/core/src/core-server/presets/common-preset.ts (1)
  • title (136-137)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: normal
  • GitHub Check: nx
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (8)
code/core/src/preview-api/modules/store/csf/processCSFFile.ts (1)

52-88: LGTM! Clean separation between CSF4 factory stories and legacy CSF exports.

The refactored logic properly:

  1. Identifies factory stories using isStory<TRenderer> type guard
  2. Validates exports with both isExportStory and isStory checks
  3. Sources meta configuration from factoryStory.meta.input
  4. Falls back gracefully to legacy CSF processing when no factory story is found
code/renderers/react/src/csf-factories.test.tsx (2)

388-403: LGTM! Good test coverage for meta.input.play composition.

The test correctly validates that:

  1. A play function defined at the meta level can be accessed via meta.input.play
  2. Extended stories can compose the meta-level play with their own interactions
  3. Optional chaining is used appropriately since play may be undefined

79-81: LGTM! Updated test to validate the new variadic overload.

The change from meta.story({}) to meta.story() properly tests the new zero-argument story invocation pattern, verifying it correctly fails when required args are not provided.

code/renderers/vue3/src/preview.tsx (2)

26-35: LGTM! Vue preview factory follows established patterns.

The implementation correctly:

  1. Prepends Vue-specific annotations before user addons
  2. Uses the base definePreviewBase from storybook internals
  3. Casts to VuePreview type for proper type inference

This mirrors the React implementation pattern appropriately.


119-120: Consider whether VueStory needs additional properties like ReactStory.

ReactStory (in code/renderers/react/src/preview.tsx) includes a Component property:

export interface ReactStory<...> extends Story<T, TInput> {
  Component: ComponentType<Partial<T['args']>>;
}

If Vue stories should expose a similar construct for portable story compatibility, this interface may need to be extended. Otherwise, if intentionally minimal, this is fine as-is.

code/renderers/react/src/preview.tsx (2)

80-93: LGTM! Improved type inference for meta args.

The refactored return type using Omit<..., 'args'> & { args: ... } pattern provides clearer type boundaries:

  • Omits the original args from ComponentAnnotations
  • Re-attaches a computed args type that is {} when meta provides all required args, or TMetaArgs otherwise

This enables better TypeScript inference for story definitions.


128-135: LGTM! Clever variadic overload for conditional zero-argument stories.

The conditional type check elegantly enforces that story() can only be called without arguments when all required args are satisfied by the meta:

..._args: Partial<T['args']> extends SetOptional<...> ? [] : [never]
  • Returns [] (empty tuple) when valid → allows story() with no args
  • Returns [never] when invalid → makes overload unmatchable

This provides excellent developer experience with proper type errors.

code/core/src/csf/csf-factories.ts (1)

205-216: The code is correct — no destructuring issue exists.

The StoryContext interface defines a context: this property (line 262) that is a self-reference to the full context object. When destructuring { mount, context } from StoryContext<TRenderer>, both properties are extracted correctly: mount (the function) and context (the full context via self-reference). Both code branches pass the same complete context object to this.play and testFunction, making them functionally equivalent. The mount destructuring is intentional — the mountDestructured utility checks if a function parameter includes mount, requiring the context to be destructured in this form for mount to be accessible as a named parameter.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds CSF (Component Story Format) factories support for Vue3, bringing it to feature parity with the React renderer. The changes introduce a new preview.tsx file for Vue3 with factory functions and type definitions, improve the CSF file processing logic to better detect factory stories, and add support for explicit mount destructuring in test functions.

Key Changes

  • New CSF factories API for Vue3 with __definePreview, VuePreview, VueMeta, and VueStory interfaces
  • Enhanced processCSFFile to use .find() for locating factory stories and add additional validation
  • Fixed mount parameter handling in test functions by explicitly destructuring it

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
code/renderers/vue3/src/preview.tsx Introduces new CSF factories support for Vue3 with preview, meta, and story type definitions
code/renderers/react/src/preview.tsx Refactors type definitions for better consistency and adds third story overload for no-args cases
code/renderers/react/src/csf-factories.test.tsx Removes optional parameter syntax and adds test for meta.input.play composition
code/core/src/preview-api/modules/store/csf/processCSFFile.ts Improves factory story detection using .find() and adds explicit isStory() check
code/core/src/csf/csf-factories.ts Explicitly destructures mount parameter in test play functions to satisfy linting


import * as vueAnnotations from './entry-preview';
import * as vueDocsAnnotations from './entry-preview-docs';
import { VueTypes } from './types';
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import statement references VueTypes which does not exist in the types.ts file. The types.ts file only exports VueRenderer.

Looking at the React implementation, it has a ReactTypes interface that extends ReactRenderer with additional parameters. Vue3 needs a similar VueTypes type/interface to be defined in types.ts, or this code should use VueRenderer instead.

If following the React pattern, you would need to add to types.ts:

export interface VueTypes extends VueRenderer {
  // Add any Vue-specific parameters here if needed
}

Alternatively, if no additional types are needed, replace all references to VueTypes with VueRenderer throughout this file.

Copilot uses AI. Check for mistakes.
Comment on lines 26 to 120
export function __definePreview<Addons extends PreviewAddon<never>[]>(
input: { addons: Addons } & ProjectAnnotations<VueTypes & InferTypes<Addons>>
): VuePreview<VueTypes & InferTypes<Addons>> {
const preview = definePreviewBase({
...input,
addons: [vueAnnotations, vueDocsAnnotations, ...(input.addons ?? [])],
}) as unknown as VuePreview<VueTypes & InferTypes<Addons>>;

return preview;
}

/** @ts-expect-error We cannot implement the meta faithfully here, but that is okay. */
export interface VuePreview<T extends AddonTypes> extends Preview<VueTypes & T> {
meta<
C,
Decorators extends DecoratorFunction<VueTypes & T, any>,
// Try to make Exact<Partial<TArgs>, TMetaArgs> work
TMetaArgs extends Partial<ComponentProps<C>>,
>(
meta: {
render?: ArgsStoryFn<VueTypes & T, ComponentProps<C>>;
component?: C;
decorators?: Decorators | Decorators[];
args?: TMetaArgs;
} & Omit<
ComponentAnnotations<VueTypes & T, ComponentProps<C>>,
'decorators' | 'component' | 'args' | 'render'
>
): VueMeta<
VueTypes &
T & {
args: Simplify<
ComponentProps<C> &
Simplify<RemoveIndexSignature<DecoratorsArgs<VueTypes & T, Decorators>>>
>;
},
Omit<
ComponentAnnotations<
VueTypes &
T & {
args: Simplify<
ComponentProps<C> &
Simplify<RemoveIndexSignature<DecoratorsArgs<VueTypes & T, Decorators>>>
>;
}
>,
'args'
> & {
args: Partial<ComponentProps<C>> extends TMetaArgs ? {} : TMetaArgs;
}
>;
}

type DecoratorsArgs<TRenderer extends Renderer, Decorators> = UnionToIntersection<
Decorators extends DecoratorFunction<TRenderer, infer TArgs> ? TArgs : unknown
>;

export interface VueMeta<T extends VueTypes, MetaInput extends ComponentAnnotations<T>>
/** @ts-expect-error hard */
extends Meta<T, MetaInput> {
// Required args don't need to be provided when the user uses an empty render
story<
TInput extends
| (() => VueTypes['storyResult'])
| (StoryAnnotations<T, T['args']> & {
render: () => VueTypes['storyResult'];
}),
>(
story: TInput
): VueStory<T, TInput extends () => VueTypes['storyResult'] ? { render: TInput } : TInput>;

story<
TInput extends Simplify<
StoryAnnotations<
T,
T['args'],
SetOptional<T['args'], keyof T['args'] & keyof MetaInput['args']>
>
>,
>(
story: TInput
): VueStory<T, TInput>;

story(
..._args: Partial<T['args']> extends SetOptional<
T['args'],
keyof T['args'] & keyof MetaInput['args']
>
? []
: [never]
): VueStory<T, {}>;
}

export interface VueStory<T extends VueTypes, TInput extends StoryAnnotations<T, T['args']>>
extends Story<T, TInput> {}
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds CSF factories support for Vue3 by introducing a new preview.tsx file with __definePreview, VuePreview, VueMeta, and VueStory interfaces. However, there are no corresponding test files to verify this functionality works correctly.

The React renderer has comprehensive tests in csf-factories.test.tsx that verify:

  • Args can be provided in multiple ways
  • Type safety for required vs optional args
  • Story composition and inheritance
  • Decorator args inference
  • Play function composition (as shown in the test added to React)

Vue3 should have similar test coverage to ensure the CSF factories implementation works correctly and maintains API consistency with React. Consider adding a csf-factories.test.ts file for Vue3 that mirrors the React test structure.

Copilot uses AI. Check for mistakes.
});
// @ts-expect-error disabled not provided ❌
const Basic = meta.story({});
const Basic = meta.story();
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable Basic.

Copilot uses AI. Check for mistakes.
Comment on lines +396 to +402
const ExtendedInteractionsStory = meta.story({
play: async ({ canvas, ...rest }) => {
await meta.input.play?.({ canvas, ...rest });

/** Do some extra interactions */
},
});
Copy link

Copilot AI Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable ExtendedInteractionsStory.

Suggested change
const ExtendedInteractionsStory = meta.story({
play: async ({ canvas, ...rest }) => {
await meta.input.play?.({ canvas, ...rest });
/** Do some extra interactions */
},
});

Copilot uses AI. Check for mistakes.
@storybook-app-bot
Copy link

storybook-app-bot bot commented Dec 15, 2025

Package Benchmarks

Commit: 6e5a012, ran on 16 December 2025 at 10:22:37 UTC

The following packages have significant changes to their size or dependencies:

@storybook/nextjs-vite

Before After Difference
Dependency count 127 128 🚨 +1 🚨
Self size 1.12 MB 1.12 MB 🎉 -7 B 🎉
Dependency size 21.97 MB 21.96 MB 🎉 -10 KB 🎉
Bundle Size Analyzer Link Link

@storybook/react-native-web-vite

Before After Difference
Dependency count 159 160 🚨 +1 🚨
Self size 30 KB 30 KB 0 B
Dependency size 23.15 MB 23.14 MB 🎉 -10 KB 🎉
Bundle Size Analyzer Link Link

@storybook/react-vite

Before After Difference
Dependency count 117 118 🚨 +1 🚨
Self size 35 KB 35 KB 🎉 -10 B 🎉
Dependency size 19.76 MB 19.75 MB 🎉 -10 KB 🎉
Bundle Size Analyzer Link Link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
code/renderers/vue3/src/csf-factories.test.ts (2)

42-47: Consider using underscore prefix for intentionally unused variables.

Several variables like meta (line 46) are declared solely for type-checking purposes. To avoid potential lint warnings and signal intent, consider prefixing with underscore.

  it('Generic parameter of Meta can be a component', () => {
-   const meta = preview.meta({
+   const _meta = preview.meta({
      component: Button,
      args: { label: 'good', disabled: false },
    });
  });

Similar pattern applies to other intentionally unused declarations (e.g., Story, Basic variables throughout the file).


177-184: Consider adding runtime assertion to the mount test.

This test only validates types but doesn't execute the play function. If runtime behavior matters, consider adding an integration test that actually calls play.

For type-only validation, this is sufficient. If you want to also verify runtime behavior in the future, you could mark this with a TODO comment.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a6d79ae and 6e5a012.

📒 Files selected for processing (4)
  • code/renderers/vue3/src/csf-factories.test.ts (1 hunks)
  • code/renderers/vue3/src/index.ts (1 hunks)
  • code/renderers/vue3/src/preview.tsx (1 hunks)
  • code/renderers/vue3/src/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • code/renderers/vue3/src/preview.tsx
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use ESLint and Prettier configurations that are enforced in the codebase

Files:

  • code/renderers/vue3/src/types.ts
  • code/renderers/vue3/src/index.ts
  • code/renderers/vue3/src/csf-factories.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Enable TypeScript strict mode

Files:

  • code/renderers/vue3/src/types.ts
  • code/renderers/vue3/src/index.ts
  • code/renderers/vue3/src/csf-factories.test.ts
code/**/*.{ts,tsx,js,jsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{ts,tsx,js,jsx,mjs}: Use server-side logger from 'storybook/internal/node-logger' for Node.js code
Use client-side logger from 'storybook/internal/client-logger' for browser code
Do not use console.log, console.warn, or console.error directly unless in isolated files where importing loggers would significantly increase bundle size

Files:

  • code/renderers/vue3/src/types.ts
  • code/renderers/vue3/src/index.ts
  • code/renderers/vue3/src/csf-factories.test.ts
code/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Export functions that need to be tested from their modules

Files:

  • code/renderers/vue3/src/types.ts
  • code/renderers/vue3/src/index.ts
  • code/renderers/vue3/src/csf-factories.test.ts
code/**/*.{js,jsx,json,html,ts,tsx,mjs}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{js,jsx,json,html,ts,tsx,mjs}: Run Prettier with --write flag to format code before committing
Run ESLint with yarn lint:js:cmd to check for linting issues and fix errors before committing

Files:

  • code/renderers/vue3/src/types.ts
  • code/renderers/vue3/src/index.ts
  • code/renderers/vue3/src/csf-factories.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (.cursorrules)

**/*.{test,spec}.{ts,tsx}: Test files should follow the naming pattern *.test.ts, *.test.tsx, *.spec.ts, or *.spec.tsx
Follow the spy mocking rules defined in .cursor/rules/spy-mocking.mdc for consistent mocking patterns with Vitest

Files:

  • code/renderers/vue3/src/csf-factories.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/spy-mocking.mdc)

**/*.test.{ts,tsx,js,jsx}: Use vi.mock() with the spy: true option for all package and file mocks in Vitest tests
Place all mocks at the top of the test file before any test cases
Use vi.mocked() to type and access the mocked functions in Vitest tests
Implement mock behaviors in beforeEach blocks in Vitest tests
Mock all required dependencies that the test subject uses
Each mock implementation should return a Promise for async functions in Vitest
Mock implementations should match the expected return type of the original function
Mock all required properties and methods that the test subject uses in Vitest tests
Avoid direct function mocking without vi.mocked() in Vitest tests
Avoid mock implementations outside of beforeEach blocks in Vitest tests
Avoid mocking without the spy: true option in Vitest tests
Avoid inline mock implementations within test cases in Vitest tests
Avoid mocking only a subset of required dependencies in Vitest tests
Mock at the highest level of abstraction needed in Vitest tests
Keep mock implementations simple and focused in Vitest tests
Use type-safe mocking with vi.mocked() in Vitest tests
Document complex mock behaviors in Vitest tests
Group related mocks together in Vitest tests

Files:

  • code/renderers/vue3/src/csf-factories.test.ts
code/**/*.{test,spec}.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

code/**/*.{test,spec}.{ts,tsx,js,jsx}: Write meaningful unit tests that actually import and call the functions being tested
Mock external dependencies using vi.mock() for file system, loggers, and other external dependencies in tests
Aim for high test coverage of business logic (75%+ for statements/lines) using coverage reports

Files:

  • code/renderers/vue3/src/csf-factories.test.ts
🧠 Learnings (14)
📓 Common learnings
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/Select/Select.tsx:200-204
Timestamp: 2025-11-05T09:38:47.712Z
Learning: Repo: storybookjs/storybook — Guidance: Until Storybook 11 is released, do not suggest using React.useId anywhere (e.g., in code/core/src/components/components/Select/Select.tsx) to maintain compatibility with React 17 runtimes. Prefer advising: accept a caller-provided props.id and, if needed, generate a client-only fallback id to minimize SSR hydration issues — but avoid useId. Resume prompting for useId after Storybook 11.
Learnt from: Sidnioulz
Repo: storybookjs/storybook PR: 32458
File: code/core/src/components/components/tooltip/WithTooltip.tsx:54-96
Timestamp: 2025-11-05T09:37:25.920Z
Learning: Repo: storybookjs/storybook — In code/core/src/components/components/tooltip/WithTooltip.tsx, the legacy WithTooltip implementation is intentionally reintroduced for backward compatibility and is deprecated; maintainers (per Sidnioulz) do not want maintenance or improvements on it. Prefer WithTooltipNew/Popover; avoid suggesting changes to WithTooltip.* going forward.
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{ts,tsx,js,jsx} : Export functions that need to be tested from their modules

Applied to files:

  • code/renderers/vue3/src/index.ts
📚 Learning: 2025-09-24T09:39:39.233Z
Learnt from: ndelangen
Repo: storybookjs/storybook PR: 32507
File: code/core/src/manager/globals/globals-module-info.ts:25-33
Timestamp: 2025-09-24T09:39:39.233Z
Learning: In Storybook, storybook/actions/decorator is a preview-only entrypoint and should not be included in manager globals configuration. The duplicatedKeys array in code/core/src/manager/globals/globals-module-info.ts is specifically for manager-side externalization, not preview entrypoints.

Applied to files:

  • code/renderers/vue3/src/index.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Document complex mock behaviors in Vitest tests

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid inline mock implementations within test cases in Vitest tests

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mocking only a subset of required dependencies in Vitest tests

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock all required properties and methods that the test subject uses in Vitest tests

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Group related mocks together in Vitest tests

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Implement mock behaviors in `beforeEach` blocks in Vitest tests

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Keep mock implementations simple and focused in Vitest tests

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Avoid mock implementations outside of `beforeEach` blocks in Vitest tests

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
📚 Learning: 2025-11-28T14:50:24.889Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-11-28T14:50:24.889Z
Learning: Applies to code/**/*.{test,spec}.{ts,tsx,js,jsx} : Write meaningful unit tests that actually import and call the functions being tested

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
📚 Learning: 2025-11-24T17:49:59.279Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursor/rules/spy-mocking.mdc:0-0
Timestamp: 2025-11-24T17:49:59.279Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Mock at the highest level of abstraction needed in Vitest tests

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
📚 Learning: 2025-11-24T17:49:31.838Z
Learnt from: CR
Repo: storybookjs/storybook PR: 0
File: .cursorrules:0-0
Timestamp: 2025-11-24T17:49:31.838Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Follow the spy mocking rules defined in `.cursor/rules/spy-mocking.mdc` for consistent mocking patterns with Vitest

Applied to files:

  • code/renderers/vue3/src/csf-factories.test.ts
🧬 Code graph analysis (1)
code/renderers/vue3/src/csf-factories.test.ts (2)
code/renderers/vue3/src/preview.tsx (1)
  • __definePreview (26-35)
code/core/src/csf/csf-factories.ts (2)
  • test (197-227)
  • Story (122-154)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: normal
  • GitHub Check: nx
  • GitHub Check: Core Unit Tests, windows-latest
🔇 Additional comments (4)
code/renderers/vue3/src/types.ts (1)

36-37: LGTM!

The empty interface pattern for VueTypes extending VueRenderer is appropriate for creating a public type alias that can be extended in the future without breaking changes.

code/renderers/vue3/src/index.ts (1)

6-7: LGTM!

The re-export follows the existing barrel export pattern and properly exposes the new Vue3 preview API surface.

code/renderers/vue3/src/csf-factories.test.ts (2)

1-2: LGTM!

Good practice documenting upfront that this file primarily validates TypeScript types. The use of expectTypeOf from Vitest is appropriate for compile-time type assertions.


85-101: Good use of @ts-expect-error for negative type testing.

The pattern of using @ts-expect-error to verify that TypeScript correctly rejects invalid configurations is effective for ensuring type safety. This validates that the type system catches missing required args.

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.

2 participants