Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 0 additions & 177 deletions code/core/src/component-testing/components/test-fn.stories.tsx

This file was deleted.

5 changes: 4 additions & 1 deletion code/core/src/test/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,11 @@ const enhanceContext: LoaderFunction = async (context) => {

// userEvent.setup() cannot be called in non browser environment and will attempt to access window.navigator.clipboard
// which will throw an error in react native for example.
const userEventDisabled = globalThis?.FEATURES?.userEventSetup === false;

const clipboard = globalThis.window?.navigator?.clipboard;
if (clipboard) {
// TODO: Remove clipboard check in SB 11
if (!userEventDisabled && clipboard) {
Comment on lines +98 to +102

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Story-level disableUserEvent parameter is not consumed.

The code defines userEventDisabled based solely on the global feature flag globalThis?.FEATURES?.userEventSetup, but the story-level parameter parameters.test.disableUserEvent (defined at lines 163-164) is never checked or used in the enhanceContext function.

The UserEventDisabled story in test-fn.stories.tsx sets parameters: { test: { disableUserEvent: true } } and expects userEvent to be undefined, but this won't work because the parameter is not being consumed.

Apply this diff to check the story-level parameter:

 const enhanceContext: LoaderFunction = async (context) => {
   if (globalThis.HTMLElement && context.canvasElement instanceof globalThis.HTMLElement) {
     context.canvas = within(context.canvasElement);
   }
 
   // userEvent.setup() cannot be called in non browser environment and will attempt to access window.navigator.clipboard
   // which will throw an error in react native for example.
-  const userEventDisabled = globalThis?.FEATURES?.userEventSetup === false;
+  const userEventDisabled = 
+    globalThis?.FEATURES?.userEventSetup === false || 
+    context.parameters?.test?.disableUserEvent === true;
 
   const clipboard = globalThis.window?.navigator?.clipboard;
   // TODO: Remove clipboard check in SB 11
   if (!userEventDisabled && clipboard) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const userEventDisabled = globalThis?.FEATURES?.userEventSetup === false;
const clipboard = globalThis.window?.navigator?.clipboard;
if (clipboard) {
// TODO: Remove clipboard check in SB 11
if (!userEventDisabled && clipboard) {
const userEventDisabled =
globalThis?.FEATURES?.userEventSetup === false ||
context.parameters?.test?.disableUserEvent === true;
const clipboard = globalThis.window?.navigator?.clipboard;
// TODO: Remove clipboard check in SB 11
if (!userEventDisabled && clipboard) {
🤖 Prompt for AI Agents
In code/core/src/test/preview.ts around lines 98 to 102, the current
userEventDisabled boolean only checks the global feature flag and ignores the
story-level parameter parameters.test.disableUserEvent — update the logic in
enhanceContext to read ctx.parameters?.test?.disableUserEvent (or equivalent for
the story context) and treat the story-level flag as authoritative (i.e.,
userEventDisabled = ctx.parameters?.test?.disableUserEvent === true ||
globalThis?.FEATURES?.userEventSetup === false), then use that combined value
when deciding whether to initialize userEvent/clipboard so stories that set
parameters.test.disableUserEvent:true correctly get userEvent undefined.

context.userEvent = instrument(
{ userEvent: uninstrumentedUserEvent.setup() },
{
Expand Down
6 changes: 6 additions & 0 deletions code/core/src/types/modules/core-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,12 @@ export interface StorybookConfigRaw {
*/
actions?: boolean;

/**
* Control automatic setup of @testing-library/user-event in the preview. Disabled in non-DOM
* environments (e.g., React Native) or when you want to manage interaction utilities manually.
*/
userEventSetup?: boolean;

/**
* @temporary This feature flag is a migration assistant, and is scheduled to be removed.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
```js filename=".storybook/main.js" renderer="common" language="js" tabTitle="CSF 3"
export default {
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
features: {
userEventSetup: false,
},
};
```

```ts filename=".storybook/main.ts" renderer="common" language="ts" tabTitle="CSF 3"
// Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc.
import type { StorybookConfig } from '@storybook/your-framework';

const config: StorybookConfig = {
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
features: {
userEventSetup: false,
},
};

export default config;
```

```ts filename=".storybook/main.ts" renderer="react" language="ts" tabTitle="CSF Next 🧪"
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';

export default defineMain({
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
features: {
userEventSetup: false,
},
});
```

```js filename=".storybook/main.js" renderer="react" language="js" tabTitle="CSF Next 🧪"
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { defineMain } from '@storybook/your-framework/node';

export default defineMain({
framework: '@storybook/your-framework',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
features: {
userEventSetup: false,
},
});
```
17 changes: 17 additions & 0 deletions docs/api/main-config/main-config-features.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Type:
controls?: boolean;
developmentModeForBuild?: boolean;
experimentalTestSyntax?: boolean;
userEventSetup?: boolean;
highlight?: boolean;
interactions?: boolean;
legacyDecoratorFileOrder?: boolean;
Expand All @@ -40,6 +41,7 @@ Type:
backgrounds?: boolean;
controls?: boolean;
developmentModeForBuild?: boolean;
userEventSetup?: boolean;
highlight?: boolean;
interactions?: boolean;
legacyDecoratorFileOrder?: boolean;
Expand All @@ -60,6 +62,7 @@ Type:
backgrounds?: boolean;
controls?: boolean;
developmentModeForBuild?: boolean;
userEventSetup?: boolean;
highlight?: boolean;
interactions?: boolean;
legacyDecoratorFileOrder?: boolean;
Expand Down Expand Up @@ -146,6 +149,20 @@ Enable the [experimental `.test` method with the CSF Next format](../csf/csf-nex

</If>

## `userEventSetup`

Type: `boolean`

Control automatic setup of Testing Library's `userEvent` in Storybook's preview runtime. When enabled, Storybook sets up `userEvent` and wires clipboard helpers for web-based environments. Disable this in non-DOM environments (e.g., React Native) or when you want full control over interaction utilities.

Defaults to enabled for web renderers. Can be turned off globally via `features`.

{/* prettier-ignore-start */}

<CodeSnippets path="main-config-features-user-event-instrumentation.md" />

{/* prettier-ignore-end */}

## `highlight`

Type: `boolean`
Expand Down