Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e96694c
Telemetry: Centralize disable logic with module-level flag
valentinpalkovic Apr 2, 2026
7f4084b
Merge branch 'next' into worktree-ticklish-prancing-umbrella
valentinpalkovic Apr 7, 2026
62f4304
Merge branch 'next' into worktree-ticklish-prancing-umbrella
valentinpalkovic Apr 7, 2026
1283506
telemetry: add timestamp and force fields to Options type
valentinpalkovic Apr 8, 2026
461e919
telemetry: implement three-state machine with event queue
valentinpalkovic Apr 8, 2026
fa6374e
telemetry: add force flag and fallback disable in withTelemetry
valentinpalkovic Apr 8, 2026
97217f0
telemetry: resolve telemetry state after preset evaluation
valentinpalkovic Apr 8, 2026
4032e43
telemetry: increase beforeEach timeout for module reset in tests
valentinpalkovic Apr 8, 2026
58b8031
Small refactoring
valentinpalkovic Apr 8, 2026
7e8d66c
telemetry: resolve telemetry state from core preset in vitest plugin
valentinpalkovic Apr 8, 2026
a4fd73f
telemetry: flush queue on success, drop on error for CLI commands wit…
valentinpalkovic Apr 8, 2026
c4a2d4a
telemetry: resolve state from config for CLI commands without preset …
valentinpalkovic Apr 8, 2026
4b97476
Resolve telemetry config for cli commands
valentinpalkovic Apr 8, 2026
9415826
Handle cancelTelemetry event gracefully
valentinpalkovic Apr 8, 2026
c19b47f
Apply requested review changes
valentinpalkovic Apr 13, 2026
deb1169
Update code/addons/vitest/src/preset.ts
valentinpalkovic Apr 13, 2026
6fd66bd
Update code/core/src/telemetry/index.ts
valentinpalkovic Apr 13, 2026
658c2a9
Sidebar: route clearStatuses through per-provider clear() API
valentinpalkovic Apr 13, 2026
ac535d9
Isolate clearStatuses per-provider with try/catch
valentinpalkovic Apr 13, 2026
eef8d53
Update @chromatic-com/storybook to ^5.1.2; add clearStatuses unit tests
valentinpalkovic Apr 13, 2026
f236f2e
chore: commit yarn.lock for @chromatic-com/storybook@5.1.2 and enable…
valentinpalkovic Apr 13, 2026
bf9bd3f
Merge branch 'next' into valentin/fix-clearing-change-detection-statuses
valentinpalkovic Apr 13, 2026
b058e01
Remove tests and stories depending on the ComponentFactoryResolver fr…
JReinhold Apr 14, 2026
f09ada2
Merge pull request #34535 from storybookjs/jeppe/fix-angular-componen…
JReinhold Apr 14, 2026
68c8fa3
Merge branch 'next-release' into next
storybook-bot Apr 14, 2026
3dd8c5f
telemetry: auto-send error telemetry when payload factory errors
valentinpalkovic Apr 14, 2026
a168d41
Merge pull request #34485 from storybookjs/worktree-ticklish-prancing…
valentinpalkovic Apr 14, 2026
4336cd6
Merge branch 'next' into valentin/fix-clearing-change-detection-statuses
valentinpalkovic Apr 15, 2026
88238a6
Build: Add FIX_ON_COMMIT env var to force autofix in pre-commit hook
kasperpeulen Apr 15, 2026
4a0ffb3
Build: remove vite-plus as devDependency
huang-julien Apr 15, 2026
7c14123
chore: add ts-ignore
huang-julien Apr 15, 2026
2b4fe00
fix: make clearStatuses and ensurePanel resilient to null providers
valentinpalkovic Apr 15, 2026
e8dfd05
Merge pull request #34550 from storybookjs/fix/vite-plus-devdepsoxfmt
huang-julien Apr 15, 2026
6a58ae9
Merge pull request #34478 from storybookjs/valentin/fix-clearing-chan…
valentinpalkovic Apr 15, 2026
a79f398
Merge pull request #34547 from storybookjs/kasper/fix-on-commit-env
kasperpeulen Apr 15, 2026
6d323be
Write changelog for 10.4.0-alpha.10 [skip ci]
storybook-bot Apr 15, 2026
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,5 @@ CLAUDE.local.md
.cursor/mcp.json
.vscode/mcp.json
.mcp.json
.nx/polygraph
.nx/polygraph
.omc
8 changes: 5 additions & 3 deletions .lintstagedrc.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { detectAgent } from 'std-env';

const fmtCmd = detectAgent().name ? 'oxfmt' : 'oxfmt --check';
const autofix = process.env.FIX_ON_COMMIT || detectAgent().name;
const fmtCmd = autofix ? 'oxfmt' : 'oxfmt --check';
const lintSuffix = autofix ? ' --fix' : '';

export default {
'code/**/*.{js,jsx,mjs,ts,tsx,html,json}': [fmtCmd, 'yarn --cwd code lint:js:cmd'],
'scripts/**/*.{html,js,json,jsx,mjs,ts,tsx}': ['yarn --cwd scripts lint:js:cmd'],
'code/**/*.{js,jsx,mjs,ts,tsx,html,json}': [fmtCmd, `yarn --cwd code lint:js:cmd${lintSuffix}`],
'scripts/**/*.{html,js,json,jsx,mjs,ts,tsx}': [`yarn --cwd scripts lint:js:cmd${lintSuffix}`],
'docs/_snippets/**/*.{js,jsx,mjs,ts,tsx,html,json}': [fmtCmd],
'**/*.ejs': ['yarn --cwd scripts exec ejslint'],
'**/package.json': ['yarn --cwd scripts lint:package'],
Expand Down
7 changes: 5 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ For routine agent work, prefer the faster non-production commands first. Add `-c
yarn
yarn task compile
yarn nx run-many -t compile
yarn nx compile <package-name>
yarn nx compile <nx-project-name>
```

### Lint and typecheck
Expand All @@ -122,7 +122,7 @@ yarn storybook:vitest
| Scenario | Command |
| ------------------------------- | ------------------------------------------------------------------------------ |
| Compile everything quickly | `yarn nx run-many -t compile` |
| Compile one package | `yarn nx compile <package-name>` |
| Compile one project | `yarn nx compile <nx-project-name>` |
| Check TypeScript errors quickly | `yarn nx run-many -t check` |
| Start the internal Storybook UI | `cd code && yarn storybook:ui` |
| Build the internal Storybook UI | `cd code && yarn storybook:ui:build` |
Expand Down Expand Up @@ -160,6 +160,8 @@ Key points:
- `react-vite/default-ts` is the default sandbox template
- `--no-link` is opt-in, not the default
- NX handles task dependencies via `nx.json`
- NX target commands use Nx project names (from `project.json` / Nx graph), not `package.json` names
- Example: `yarn nx compile core` (project `core` is published as package `storybook`)

## Sandbox Notes

Expand Down Expand Up @@ -266,6 +268,7 @@ Avoid `console.log`, `console.warn`, and `console.error` unless the file is isol
| `STORYBOOK_DISABLE_TELEMETRY` | Disable telemetry |
| `STORYBOOK_TELEMETRY_DEBUG` | Log telemetry events |
| `DEBUG` | Enable debug logging |
| `FIX_ON_COMMIT` | Force autofix for fmt & lint in pre-commit hook |

## Commands To Avoid

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.prerelease.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 10.4.0-alpha.10

- Sidebar: Fix clear status button to only clear test statuses - [#34478](https://github.com/storybookjs/storybook/pull/34478), thanks @valentinpalkovic!
- Telemetry: Centralize disable logic with module-level flag - [#34485](https://github.com/storybookjs/storybook/pull/34485), thanks @valentinpalkovic!

## 10.4.0-alpha.9

- A11y: Improve boolean control contrast in forced colors mode - [#34204](https://github.com/storybookjs/storybook/pull/34204), thanks @anchmelev!
Expand Down
9 changes: 5 additions & 4 deletions code/.storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,19 +143,21 @@ const config = defineMain({
},
core: {
disableTelemetry: true,
changeDetection: true,
},
features: {
developmentModeForBuild: true,
experimentalTestSyntax: true,
changeDetection: true,
},
staticDirs: [{ from: './bench/bundle-analyzer', to: '/bundle-analyzer' }],
viteFinal: async (viteConfig, { configType }) => {
const { mergeConfig } = await import('vite');

return mergeConfig(viteConfig, {
resolve: {
alias: {
...(configType === 'DEVELOPMENT'
alias:
configType === 'DEVELOPMENT'
? {
'storybook/internal/components': componentsPath,
'storybook/manager-api': managerApiPath,
Expand All @@ -165,8 +167,7 @@ const config = defineMain({
}
: {
'storybook/manager-api': managerApiPath,
}),
},
},
},
plugins: [react()],
build: {
Expand Down
6 changes: 0 additions & 6 deletions code/addons/onboarding/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ type Event = {
};

export const experimental_serverChannel = async (channel: Channel, options: Options) => {
const { disableTelemetry } = await options.presets.apply<CoreConfig>('core', {});

if (disableTelemetry) {
return channel;
}

channel.on(ADDON_ONBOARDING_CHANNEL, ({ type, ...event }: Event) => {
if (type === 'telemetry') {
telemetry('addon-onboarding', { ...event, addonVersion });
Expand Down
4 changes: 4 additions & 0 deletions code/addons/vitest/src/manager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ addons.register(ADDON_ID, (api) => {

addons.add(TEST_PROVIDER_ID, {
type: Addon_TypesEnum.experimental_TEST_PROVIDER,
clear: () => {
componentTestStatusStore.unset();
a11yStatusStore.unset();
},
render: () => {
const [isModalOpen, setModalOpen] = useState(false);
const {
Expand Down
78 changes: 38 additions & 40 deletions code/addons/vitest/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,49 +257,47 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti
});
});

if (!core.disableTelemetry) {
const enableCrashReports = core.enableCrashReports || options.enableCrashReports;

channel.on(STORYBOOK_ADDON_TEST_CHANNEL, (event: Event) => {
if (event.type !== 'test-run-completed') {
telemetry('addon-test', {
...event,
payload: {
...event.payload,
storyId: oneWayHash(event.payload.storyId),
},
});
}
});
const enableCrashReports = core?.enableCrashReports || options.enableCrashReports;

channel.on(STORYBOOK_ADDON_TEST_CHANNEL, (event: Event) => {
if (event.type !== 'test-run-completed') {
telemetry('addon-test', () => ({
...event,
payload: {
...event.payload,
storyId: oneWayHash(event.payload.storyId),
},
}));
}
});

store.subscribe('TOGGLE_WATCHING', async (event) => {
await telemetry('addon-test', {
watchMode: event.payload.to,
});
});
store.subscribe('TEST_RUN_COMPLETED', async (event) => {
const { unhandledErrors, startedAt, finishedAt, ...currentRun } = event.payload;
await telemetry('addon-test', {
...currentRun,
duration: (finishedAt ?? 0) - (startedAt ?? 0),
unhandledErrorCount: unhandledErrors.length,
...(enableCrashReports &&
unhandledErrors.length > 0 && {
unhandledErrors: unhandledErrors.map((error) => {
const { stacks, ...errorWithoutStacks } = error;
return sanitizeError(errorWithoutStacks);
}),
store.subscribe('TOGGLE_WATCHING', async (event) => {
await telemetry('addon-test', () => ({
watchMode: event.payload.to,
}));
});
store.subscribe('TEST_RUN_COMPLETED', async (event) => {
const { unhandledErrors, startedAt, finishedAt, ...currentRun } = event.payload;
await telemetry('addon-test', () => ({
...currentRun,
duration: (finishedAt ?? 0) - (startedAt ?? 0),
unhandledErrorCount: unhandledErrors.length,
...(enableCrashReports &&
unhandledErrors.length > 0 && {
unhandledErrors: unhandledErrors.map((error) => {
const { stacks, ...errorWithoutStacks } = error;
return sanitizeError(errorWithoutStacks);
}),
});
});
}),
}));
});

if (enableCrashReports) {
store.subscribe('FATAL_ERROR', async (event) => {
await telemetry('addon-test', {
fatalError: cleanPaths(event.payload.error.message),
});
});
}
if (enableCrashReports) {
store.subscribe('FATAL_ERROR', async (event) => {
await telemetry('addon-test', () => ({
fatalError: cleanPaths(event.payload.error.message),
}));
});
}

return channel;
Expand Down
39 changes: 15 additions & 24 deletions code/addons/vitest/src/vitest-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type { ViteUserConfig } from 'vitest/config';
import {
DEFAULT_FILES_PATTERN,
getInterpretedFile,
loadPreviewOrConfigFile,
normalizeStories,
optionalEnvToBoolean,
resolvePathInStorybookCache,
Expand All @@ -19,14 +18,9 @@ import {
experimental_loadStorybook,
mapStaticDir,
} from 'storybook/internal/core-server';
import {
componentTransform,
isCsfFactoryPreview,
readConfig,
vitestTransform,
} from 'storybook/internal/csf-tools';
import { componentTransform, readConfig, vitestTransform } from 'storybook/internal/csf-tools';
import { MainFileMissingError } from 'storybook/internal/server-errors';
import { telemetry } from 'storybook/internal/telemetry';
import { setTelemetryEnabled, telemetry } from 'storybook/internal/telemetry';
import { oneWayHash } from 'storybook/internal/telemetry';
import type { Presets } from 'storybook/internal/types';

Expand Down Expand Up @@ -224,6 +218,8 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
presets.apply('features', {}),
]);

await setTelemetryEnabled(!core?.disableTelemetry);

const pluginsToIgnore = [
'storybook:react-docgen-plugin',
'vite:react-docgen-typescript', // aka @joshwooding/vite-plugin-react-docgen-typescript
Expand Down Expand Up @@ -448,22 +444,17 @@ export const storybookTest = async (options?: UserOptions): Promise<Plugin[]> =>
configureVitest(context) {
context.vitest.config.coverage.exclude.push('storybook-static');

if (
!core?.disableTelemetry &&
!optionalEnvToBoolean(process.env.STORYBOOK_DISABLE_TELEMETRY)
) {
// NOTE: we start telemetry immediately but do not wait on it. Typically it should complete
// before the tests do. If not we may miss the event, we are OK with that.
telemetry(
'test-run',
{
runner: 'vitest',
watch: context.vitest.config.watch,
coverage: !!context.vitest.config.coverage?.enabled,
},
{ configDir: finalOptions.configDir }
);
}
// NOTE: we start telemetry immediately but do not wait on it. Typically it should complete
// before the tests do. If not we may miss the event, we are OK with that.
telemetry(
'test-run',
{
runner: 'vitest',
watch: context.vitest.config.watch,
coverage: !!context.vitest.config.coverage?.enabled,
},
{ configDir: finalOptions.configDir }
);
},
async configureServer(server) {
if (staticDirs) {
Expand Down
1 change: 0 additions & 1 deletion code/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,6 @@
"typescript": "^5.8.3",
"unique-string": "^3.0.0",
"use-resize-observer": "^9.1.0",
"vite-plus": "^0.1.16",
"watchpack": "^2.5.0",
"wrap-ansi": "^9.0.2",
"zod": "^3.25.76"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export async function getVitePlusVersions(): Promise<Record<string, string> | nu
}

try {
// @ts-expect-error - This is a dynamic import of a potentially non-existent package. Vite-plus is currently a peer dependency.
const mod = await import('vite-plus/versions');
const versions = mod.versions ?? mod;

Expand Down
10 changes: 5 additions & 5 deletions code/core/src/core-server/build-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from 'storybook/internal/common';
import { CLI_COLORS, deprecate, logger, prompt } from 'storybook/internal/node-logger';
import { MissingBuilderError, NoStatsForViteDevError } from 'storybook/internal/server-errors';
import { oneWayHash, telemetry } from 'storybook/internal/telemetry';
import { oneWayHash, setTelemetryEnabled, telemetry } from 'storybook/internal/telemetry';
import type { BuilderOptions, CLIOptions, LoadOptions, Options } from 'storybook/internal/types';

import { global } from '@storybook/global';
Expand Down Expand Up @@ -181,6 +181,8 @@ export async function buildDevStandalone(

const { allowedHosts, renderer, builder, disableTelemetry } = await presets.apply('core', {});

await setTelemetryEnabled(!disableTelemetry);

// '0.0.0.0' binds to all interfaces, which is useful for Docker and other containerized environments.
// By default we allow requests from all hosts in this case, but the user should be made aware of the risk.
if (
Expand All @@ -198,10 +200,8 @@ export async function buildDevStandalone(
throw new MissingBuilderError();
}

if (!options.disableTelemetry && !disableTelemetry) {
if (versionCheck.success && !versionCheck.cached) {
telemetry('version-update');
}
if (versionCheck.success && !versionCheck.cached) {
telemetry('version-update');
}

const resolvedPreviewBuilder = typeof builder === 'string' ? builder : builder.name;
Expand Down
8 changes: 5 additions & 3 deletions code/core/src/core-server/build-static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
resolveAddonName,
} from 'storybook/internal/common';
import { logger } from 'storybook/internal/node-logger';
import { getPrecedingUpgrade, telemetry } from 'storybook/internal/telemetry';
import { getPrecedingUpgrade, setTelemetryEnabled, telemetry } from 'storybook/internal/telemetry';
import type { BuilderOptions, CLIOptions, LoadOptions, Options } from 'storybook/internal/types';

import { global } from '@storybook/global';
Expand Down Expand Up @@ -111,8 +111,10 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
presets.apply('staticDirs'),
]);

await setTelemetryEnabled(!core?.disableTelemetry);

const invokedBy = process.env.STORYBOOK_INVOKED_BY;
if (!core?.disableTelemetry && invokedBy) {
if (invokedBy) {
// NOTE: we don't await this event to avoid slowing things down.
// This could result in telemetry events being lost.
telemetry('test-run', { runner: invokedBy, watch: false }, { configDir: options.configDir });
Expand Down Expand Up @@ -208,7 +210,7 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption

// Now the code has successfully built, we can count this as a 'build' event.
// NOTE: we don't send the 'build' event for test runs as we want to be as fast as possible.
if (!core?.disableTelemetry && !options.test) {
if (!options.test) {
try {
const generator = await storyIndexGeneratorPromise;
const storyIndex = await generator?.getIndex();
Expand Down
12 changes: 7 additions & 5 deletions code/core/src/core-server/dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { Options } from 'storybook/internal/types';
import compression from '@polka/compression';
import polka from 'polka';

import { telemetry } from '../telemetry/index.ts';
import { isTelemetryModuleEnabled, telemetry } from '../telemetry/index.ts';
import { ChangeDetectionService } from './change-detection/index.ts';
import { setChangeDetectionReadiness } from './change-detection/readiness.ts';
import { getStatusStoreByTypeId } from './stores/status.ts';
Expand Down Expand Up @@ -203,6 +203,10 @@ export async function storybookDevServer(
doTelemetry(app, core, storyIndexGeneratorPromise, options);

async function cancelTelemetry() {
if (!isTelemetryModuleEnabled()) {
return;
}

const payload = { eventType: 'dev' };
try {
const generator = await storyIndexGeneratorPromise;
Expand All @@ -219,10 +223,8 @@ export async function storybookDevServer(
process.exit(0);
}

if (!core?.disableTelemetry) {
process.on('SIGINT', cancelTelemetry);
process.on('SIGTERM', cancelTelemetry);
}
process.on('SIGINT', cancelTelemetry);
process.on('SIGTERM', cancelTelemetry);

return { previewResult, managerResult };
}
Loading
Loading