From 1c96212e70e06d521f753ffb71d80e9cd2392995 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 6 Feb 2026 08:58:22 +0100 Subject: [PATCH 1/5] Merge pull request #33783 from storybookjs/copilot/add-expo-telemetry-patch-label Telemetry: Add Expo metaframework (cherry picked from commit 5175dce0251297efd55cf36ceec66be8f398d35e) --- code/core/src/telemetry/storybook-metadata.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/code/core/src/telemetry/storybook-metadata.ts b/code/core/src/telemetry/storybook-metadata.ts index 689e0ca31fd8..79f14e2e69a7 100644 --- a/code/core/src/telemetry/storybook-metadata.ts +++ b/code/core/src/telemetry/storybook-metadata.ts @@ -41,6 +41,7 @@ export const metaFrameworks = { '@tanstack/react-router': 'tanstack-react', '@react-router/dev': 'react-router', '@remix-run/dev': 'remix', + expo: 'expo', } as Record; export const sanitizeAddonName = (name: string) => { From 0ca727889b1f353268a6d899ede3f525e72fc126 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 6 Feb 2026 09:05:49 +0100 Subject: [PATCH 2/5] Merge pull request #33766 from storybookjs/norbert/share-channel-events Telemetry: Add share events (cherry picked from commit ce42f59d76106c12957a18439e03030854497f4d) --- code/.eslintrc.js | 5 ++- code/core/src/core-events/index.ts | 7 ++++ .../src/core-server/presets/common-preset.ts | 4 +-- ...nnel.test.ts => telemetry-channel.test.ts} | 2 +- ...alized-channel.ts => telemetry-channel.ts} | 36 +++++++++++-------- .../preview/tools/share.stories.tsx | 3 +- .../components/preview/tools/share.tsx | 15 +++++++- code/core/src/manager/globals/exports.ts | 3 ++ code/core/src/telemetry/types.ts | 1 + 9 files changed, 54 insertions(+), 22 deletions(-) rename code/core/src/core-server/server-channel/{preview-initialized-channel.test.ts => telemetry-channel.test.ts} (97%) rename code/core/src/core-server/server-channel/{preview-initialized-channel.ts => telemetry-channel.ts} (60%) diff --git a/code/.eslintrc.js b/code/.eslintrc.js index 1d4c233d3caf..b1e2ad68d238 100644 --- a/code/.eslintrc.js +++ b/code/.eslintrc.js @@ -160,10 +160,9 @@ module.exports = { 'import-x/no-named-as-default-member': 'warn', 'react/destructuring-assignment': 'warn', - // This warns about importing interfaces and types in a normal import, it's arguably better to import with the `type` prefix separate from the runtime imports, - // I leave this as a warning right now because we haven't really decided yet, and the codebase is riddled with errors if I set to 'error'. + // Our codebase is mostly TypeScript, and typescript will warn when imports are not found. // It IS set to 'error' for JS files. - 'import-x/named': 'warn', + 'import-x/named': 'off', }, }, { diff --git a/code/core/src/core-events/index.ts b/code/core/src/core-events/index.ts index a7a47a66eb2a..36d75f3505f1 100644 --- a/code/core/src/core-events/index.ts +++ b/code/core/src/core-events/index.ts @@ -96,6 +96,10 @@ enum events { OPEN_IN_EDITOR_RESPONSE = 'openInEditorResponse', // Emitted when the manager UI sets up a focus trap MANAGER_INERT_ATTRIBUTE_CHANGED = 'managerInertAttributeChanged', + + SHARE_STORY_LINK = 'shareStoryLink', + SHARE_ISOLATE_MODE = 'shareIsolateMode', + SHARE_POPOVER_OPENED = 'sharePopoverOpened', } // Enables: `import Events from ...` @@ -167,6 +171,9 @@ export const { OPEN_IN_EDITOR_REQUEST, OPEN_IN_EDITOR_RESPONSE, MANAGER_INERT_ATTRIBUTE_CHANGED, + SHARE_STORY_LINK, + SHARE_ISOLATE_MODE, + SHARE_POPOVER_OPENED, } = events; export * from './data/create-new-story'; diff --git a/code/core/src/core-server/presets/common-preset.ts b/code/core/src/core-server/presets/common-preset.ts index 36f40b823c94..422b45c1e2fa 100644 --- a/code/core/src/core-server/presets/common-preset.ts +++ b/code/core/src/core-server/presets/common-preset.ts @@ -34,7 +34,7 @@ import { initCreateNewStoryChannel } from '../server-channel/create-new-story-ch import { initFileSearchChannel } from '../server-channel/file-search-channel'; import { initGhostStoriesChannel } from '../server-channel/ghost-stories-channel'; import { initOpenInEditorChannel } from '../server-channel/open-in-editor-channel'; -import { initPreviewInitializedChannel } from '../server-channel/preview-initialized-channel'; +import { initTelemetryChannel } from '../server-channel/telemetry-channel'; import { initializeChecklist } from '../utils/checklist'; import { defaultFavicon, defaultStaticDirs } from '../utils/constants'; import { initializeSaveStory } from '../utils/save-story/save-story'; @@ -264,7 +264,7 @@ export const experimental_serverChannel = async ( initCreateNewStoryChannel(channel, options, coreOptions); initGhostStoriesChannel(channel, options, coreOptions); initOpenInEditorChannel(channel, options, coreOptions); - initPreviewInitializedChannel(channel, options, coreOptions); + initTelemetryChannel(channel, options); return channel; }; diff --git a/code/core/src/core-server/server-channel/preview-initialized-channel.test.ts b/code/core/src/core-server/server-channel/telemetry-channel.test.ts similarity index 97% rename from code/core/src/core-server/server-channel/preview-initialized-channel.test.ts rename to code/core/src/core-server/server-channel/telemetry-channel.test.ts index 0055849979c0..591380e9cf19 100644 --- a/code/core/src/core-server/server-channel/preview-initialized-channel.test.ts +++ b/code/core/src/core-server/server-channel/telemetry-channel.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; -import { makePayload } from './preview-initialized-channel'; +import { makePayload } from './telemetry-channel'; describe('makePayload', () => { beforeEach(() => { diff --git a/code/core/src/core-server/server-channel/preview-initialized-channel.ts b/code/core/src/core-server/server-channel/telemetry-channel.ts similarity index 60% rename from code/core/src/core-server/server-channel/preview-initialized-channel.ts rename to code/core/src/core-server/server-channel/telemetry-channel.ts index 20b139c349ec..e4b2820611ea 100644 --- a/code/core/src/core-server/server-channel/preview-initialized-channel.ts +++ b/code/core/src/core-server/server-channel/telemetry-channel.ts @@ -1,9 +1,14 @@ import type { Channel } from 'storybook/internal/channels'; -import { PREVIEW_INITIALIZED } from 'storybook/internal/core-events'; +import { + PREVIEW_INITIALIZED, + SHARE_ISOLATE_MODE, + SHARE_POPOVER_OPENED, + SHARE_STORY_LINK, +} from 'storybook/internal/core-events'; import { type InitPayload, telemetry } from 'storybook/internal/telemetry'; import { type CacheEntry, getLastEvents } from 'storybook/internal/telemetry'; import { getSessionId } from 'storybook/internal/telemetry'; -import type { CoreConfig, Options } from 'storybook/internal/types'; +import type { Options } from 'storybook/internal/types'; export const makePayload = ( userAgent: string, @@ -24,13 +29,9 @@ export const makePayload = ( return payload; }; -export function initPreviewInitializedChannel( - channel: Channel, - options: Options, - _coreConfig: CoreConfig -) { - channel.on(PREVIEW_INITIALIZED, async ({ userAgent }) => { - if (!options.disableTelemetry) { +export function initTelemetryChannel(channel: Channel, options: Options) { + if (!options.disableTelemetry) { + channel.on(PREVIEW_INITIALIZED, async ({ userAgent }) => { try { const sessionId = await getSessionId(); const lastEvents = await getLastEvents(); @@ -40,9 +41,16 @@ export function initPreviewInitializedChannel( const payload = makePayload(userAgent, lastInit, sessionId); telemetry('preview-first-load', payload); } - } catch (e) { - // do nothing - } - } - }); + } catch {} + }); + channel.on(SHARE_POPOVER_OPENED, async () => { + telemetry('share', { action: 'popover-opened' }); + }); + channel.on(SHARE_STORY_LINK, async () => { + telemetry('share', { action: 'story-link-copied' }); + }); + channel.on(SHARE_ISOLATE_MODE, async () => { + telemetry('share', { action: 'isolate-mode-opened' }); + }); + } } diff --git a/code/core/src/manager/components/preview/tools/share.stories.tsx b/code/core/src/manager/components/preview/tools/share.stories.tsx index 2a7622bde711..0153d555d0bf 100644 --- a/code/core/src/manager/components/preview/tools/share.stories.tsx +++ b/code/core/src/manager/components/preview/tools/share.stories.tsx @@ -5,7 +5,7 @@ import { global } from '@storybook/global'; import type { StoryObj } from '@storybook/react-vite'; import { ManagerContext } from 'storybook/manager-api'; -import { expect, screen, waitFor } from 'storybook/test'; +import { expect, fn, screen, waitFor } from 'storybook/test'; import { shareTool } from './share'; @@ -15,6 +15,7 @@ const managerContext = { refId: undefined, }, api: { + emit: fn().mockName('api::emit'), getShortcutKeys: () => ({ copyStoryLink: ['alt', 'shift', 'l'], openInIsolation: ['alt', 'shift', 'i'], diff --git a/code/core/src/manager/components/preview/tools/share.tsx b/code/core/src/manager/components/preview/tools/share.tsx index 6c9863df3647..caa46f7c78d4 100644 --- a/code/core/src/manager/components/preview/tools/share.tsx +++ b/code/core/src/manager/components/preview/tools/share.tsx @@ -1,6 +1,11 @@ -import React, { useMemo, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { Button, PopoverProvider, TooltipLinkList } from 'storybook/internal/components'; +import { + SHARE_ISOLATE_MODE, + SHARE_POPOVER_OPENED, + SHARE_STORY_LINK, +} from 'storybook/internal/core-events'; import type { Addon_BaseType } from 'storybook/internal/types'; import { global } from '@storybook/global'; @@ -71,6 +76,10 @@ const ShareMenu = React.memo(function ShareMenu({ const copyStoryLink = shortcutKeys?.copyStoryLink; const openInIsolation = shortcutKeys?.openInIsolation; + useEffect(() => { + api.emit(SHARE_POPOVER_OPENED); + }, [api]); + const links = useMemo(() => { const copyTitle = copied ? 'Copied!' : 'Copy story link'; const originHrefs = api.getStoryHrefs(storyId, { base: 'origin', refId }); @@ -84,6 +93,7 @@ const ShareMenu = React.memo(function ShareMenu({ icon: , right: enableShortcuts ? : null, onClick: () => { + api.emit(SHARE_STORY_LINK, originHrefs.managerHref); copy(originHrefs.managerHref); setCopied(true); setTimeout(() => setCopied(false), 2000); @@ -94,6 +104,9 @@ const ShareMenu = React.memo(function ShareMenu({ title: 'Open in isolation mode', icon: , right: enableShortcuts ? : null, + onClick: () => { + api.emit(SHARE_ISOLATE_MODE, originHrefs.previewHref); + }, href: originHrefs.previewHref, target: '_blank', rel: 'noopener noreferrer', diff --git a/code/core/src/manager/globals/exports.ts b/code/core/src/manager/globals/exports.ts index 59703619f1de..e2b984eda3eb 100644 --- a/code/core/src/manager/globals/exports.ts +++ b/code/core/src/manager/globals/exports.ts @@ -615,6 +615,9 @@ export default { 'SET_WHATS_NEW_CACHE', 'SHARED_STATE_CHANGED', 'SHARED_STATE_SET', + 'SHARE_ISOLATE_MODE', + 'SHARE_POPOVER_OPENED', + 'SHARE_STORY_LINK', 'STORIES_COLLAPSE_ALL', 'STORIES_EXPAND_ALL', 'STORY_ARGS_UPDATED', diff --git a/code/core/src/telemetry/types.ts b/code/core/src/telemetry/types.ts index a86917ee5386..9a17ff040629 100644 --- a/code/core/src/telemetry/types.ts +++ b/code/core/src/telemetry/types.ts @@ -42,6 +42,7 @@ export type EventType = | 'migrate' | 'preview-first-load' | 'doctor' + | 'share' | 'ghost-stories'; export interface Dependency { version: string | undefined; From 78f274b91aed3648a9d164e3456f908c64134be0 Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Fri, 6 Feb 2026 08:58:33 +0100 Subject: [PATCH 3/5] Merge pull request #33773 from storybookjs/valentin/add-exit-telemetry Telemetry: Add init exit event (cherry picked from commit 5e1084cbf6d74638b69987928dae00393bfb0fa2) --- code/core/src/telemetry/types.ts | 1 + .../src/commands/ProjectDetectionCommand.ts | 8 ++++++++ code/lib/create-storybook/src/scaffold-new-project.ts | 7 +++++++ 3 files changed, 16 insertions(+) diff --git a/code/core/src/telemetry/types.ts b/code/core/src/telemetry/types.ts index 9a17ff040629..31a8427d43a6 100644 --- a/code/core/src/telemetry/types.ts +++ b/code/core/src/telemetry/types.ts @@ -19,6 +19,7 @@ export type EventType = | 'scaffolded-empty' | 'browser' | 'canceled' + | 'exit' | 'error' | 'error-metadata' | 'version-update' diff --git a/code/lib/create-storybook/src/commands/ProjectDetectionCommand.ts b/code/lib/create-storybook/src/commands/ProjectDetectionCommand.ts index f879edc78773..78c1db4a74ea 100644 --- a/code/lib/create-storybook/src/commands/ProjectDetectionCommand.ts +++ b/code/lib/create-storybook/src/commands/ProjectDetectionCommand.ts @@ -1,6 +1,7 @@ import { ProjectType } from 'storybook/internal/cli'; import type { JsPackageManager } from 'storybook/internal/common'; import { logger, prompt } from 'storybook/internal/node-logger'; +import { telemetry } from 'storybook/internal/telemetry'; import type { SupportedLanguage } from 'storybook/internal/types'; import picocolors from 'picocolors'; @@ -91,6 +92,13 @@ We assume that Storybook is already instantiated for your project. Do you still if (force || options.yes) { options.force = true; } else { + if (!options.disableTelemetry) { + await telemetry( + 'exit', + { eventType: 'init', reason: 'existing-installation' }, + { stripMetadata: true, immediate: true } + ); + } process.exit(0); } } diff --git a/code/lib/create-storybook/src/scaffold-new-project.ts b/code/lib/create-storybook/src/scaffold-new-project.ts index 5e12c445d030..a91c3061dbec 100644 --- a/code/lib/create-storybook/src/scaffold-new-project.ts +++ b/code/lib/create-storybook/src/scaffold-new-project.ts @@ -135,6 +135,13 @@ export const scaffoldNewProject = async ( } if (projectStrategy === 'other') { + if (!disableTelemetry) { + await telemetry( + 'exit', + { eventType: 'init', reason: 'scaffold-other' }, + { stripMetadata: true, immediate: true } + ); + } logger.warn( 'To install Storybook on another framework, first generate a project with that framework and then rerun this command.' ); From d9652ff9f543371537f3d84c26028c30ececfa8e Mon Sep 17 00:00:00 2001 From: Valentin Palkovic Date: Mon, 9 Feb 2026 13:10:03 +0100 Subject: [PATCH 4/5] Merge pull request #33787 from storybookjs/valentin/fix-event-view-undefined Test: Update event creation logic in user-event package (cherry picked from commit a4a0dff222f56a71364d2fdaedd48b9b7d550e5c) --- ...ary-user-event-npm-14.6.1-5da7e1d4e2.patch | 44 +++++++++++++++++++ yarn.lock | 4 +- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/.yarn/patches/@testing-library-user-event-npm-14.6.1-5da7e1d4e2.patch b/.yarn/patches/@testing-library-user-event-npm-14.6.1-5da7e1d4e2.patch index bc8d83558798..3d48828863d7 100644 --- a/.yarn/patches/@testing-library-user-event-npm-14.6.1-5da7e1d4e2.patch +++ b/.yarn/patches/@testing-library-user-event-npm-14.6.1-5da7e1d4e2.patch @@ -1,3 +1,25 @@ +diff --git a/dist/cjs/event/createEvent.js b/dist/cjs/event/createEvent.js +index b63c0093f492f5072ce2eabb74ca290144353548..5e68db5bb9f62aa43f1c849b185912b0b34021c8 100644 +--- a/dist/cjs/event/createEvent.js ++++ b/dist/cjs/event/createEvent.js +@@ -38,7 +38,16 @@ function createEvent(type, target, init) { + const window = getWindow.getWindow(target); + const { EventType, defaultInit } = eventMap.eventMap[type]; + const event = new (getEventConstructors(window))[EventType](type, defaultInit); +- eventInitializer[EventType].forEach((f)=>f(event, init !== null && init !== undefined ? init : {})); ++ var eventInit = {}; ++ for (var key in init) { ++ if (Object.prototype.hasOwnProperty.call(init, key)) { ++ eventInit[key] = init[key]; ++ } ++ } ++ eventInit.view = window; ++ eventInitializer[EventType].forEach(function(f) { ++ f(event, eventInit); ++ }); + return event; + } + /* istanbul ignore next */ function getEventConstructors(window) { diff --git a/dist/cjs/utils/dataTransfer/Clipboard.js b/dist/cjs/utils/dataTransfer/Clipboard.js index 434be791b156984a8b76287bc0cc6c8955df4203..e28a15e85e2dccff058a18b4b80b099b7016d688 100644 --- a/dist/cjs/utils/dataTransfer/Clipboard.js @@ -23,6 +45,28 @@ index 434be791b156984a8b76287bc0cc6c8955df4203..e28a15e85e2dccff058a18b4b80b099b } exports.attachClipboardStubToView = attachClipboardStubToView; +diff --git a/dist/esm/event/createEvent.js b/dist/esm/event/createEvent.js +index 1c741ba446f40917727236e9e4ad29a20357d03a..9a4b63935923a9a271bfc9fd161fe7c477303256 100644 +--- a/dist/esm/event/createEvent.js ++++ b/dist/esm/event/createEvent.js +@@ -36,7 +36,16 @@ function createEvent(type, target, init) { + const window = getWindow(target); + const { EventType, defaultInit } = eventMap[type]; + const event = new (getEventConstructors(window))[EventType](type, defaultInit); +- eventInitializer[EventType].forEach((f)=>f(event, init !== null && init !== undefined ? init : {})); ++ var eventInit = {}; ++ for (var key in init) { ++ if (Object.prototype.hasOwnProperty.call(init, key)) { ++ eventInit[key] = init[key]; ++ } ++ } ++ eventInit.view = window; ++ eventInitializer[EventType].forEach(function(f) { ++ f(event, eventInit); ++ }); + return event; + } + /* istanbul ignore next */ function getEventConstructors(window) { diff --git a/dist/esm/utils/dataTransfer/Clipboard.js b/dist/esm/utils/dataTransfer/Clipboard.js index 2ed2676b52adaee045d2594b051c08a4b133e7df..337e644ed268ad4ad0ce9a601d6d0aec73264d5e 100644 --- a/dist/esm/utils/dataTransfer/Clipboard.js diff --git a/yarn.lock b/yarn.lock index 4cecb506d0ee..7c29f8e7bba5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8951,10 +8951,10 @@ __metadata: "@testing-library/user-event@patch:@testing-library/user-event@npm%3A14.6.1#~/.yarn/patches/@testing-library-user-event-npm-14.6.1-5da7e1d4e2.patch": version: 14.6.1 - resolution: "@testing-library/user-event@patch:@testing-library/user-event@npm%3A14.6.1#~/.yarn/patches/@testing-library-user-event-npm-14.6.1-5da7e1d4e2.patch::version=14.6.1&hash=228211" + resolution: "@testing-library/user-event@patch:@testing-library/user-event@npm%3A14.6.1#~/.yarn/patches/@testing-library-user-event-npm-14.6.1-5da7e1d4e2.patch::version=14.6.1&hash=4faa83" peerDependencies: "@testing-library/dom": ">=7.21.4" - checksum: 10c0/837761d0a6f87268e0e4c69c1afaf14ddadea51aab44e82d22a12e1ae7c970f9af900b7d7379ed672132b76694c32f8cbd786020300261e805b499898b8f20f4 + checksum: 10c0/b3d9c3351e7a3bb9169bd5262aaad7911647cf38d4f24d217561481a028fe3a5d0fbd9e6320017a264c4ef2bd52947079e3c665bd6a768ff85780da1012e3672 languageName: node linkType: hard From 7c2a58735c83c4f4ad448e17628b698f85f577c9 Mon Sep 17 00:00:00 2001 From: storybook-bot <32066757+storybook-bot@users.noreply.github.com> Date: Mon, 9 Feb 2026 12:16:39 +0000 Subject: [PATCH 5/5] Write changelog for 10.2.8 [skip ci] --- CHANGELOG.md | 7 +++++++ code/package.json | 3 ++- docs/versions/latest.json | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0388a028a51..b67cbecc489b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 10.2.8 + +- Telemetry: Add Expo metaframework - [#33783](https://github.com/storybookjs/storybook/pull/33783), thanks @copilot-swe-agent! +- Telemetry: Add init exit event - [#33773](https://github.com/storybookjs/storybook/pull/33773), thanks @valentinpalkovic! +- Telemetry: Add share events - [#33766](https://github.com/storybookjs/storybook/pull/33766), thanks @ndelangen! +- Test: Update event creation logic in user-event package - [#33787](https://github.com/storybookjs/storybook/pull/33787), thanks @valentinpalkovic! + ## 10.2.7 - CSF: Fix cross-file story imports in csf-factories codemod - [#33723](https://github.com/storybookjs/storybook/pull/33723), thanks @yatishgoel! diff --git a/code/package.json b/code/package.json index d9f2c8feea28..4566cf4a6050 100644 --- a/code/package.json +++ b/code/package.json @@ -220,5 +220,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "10.2.8" } diff --git a/docs/versions/latest.json b/docs/versions/latest.json index 51ac9dcc9d6f..0d89c0607c81 100644 --- a/docs/versions/latest.json +++ b/docs/versions/latest.json @@ -1 +1 @@ -{"version":"10.2.7","info":{"plain":"- CSF: Fix cross-file story imports in csf-factories codemod - [#33723](https://github.com/storybookjs/storybook/pull/33723), thanks @yatishgoel!\n- Core: Fix rendering of View Transitions in Firefox - [#33651](https://github.com/storybookjs/storybook/pull/33651), thanks @ghengeveld!\n- Globals: Repair dynamicTitle: false for user-defined tools - [#33284](https://github.com/storybookjs/storybook/pull/33284), thanks @ia319!\n- Logger: Honor --loglevel for npmlog output - [#33776](https://github.com/storybookjs/storybook/pull/33776), thanks @LouisLau-art!"}} \ No newline at end of file +{"version":"10.2.8","info":{"plain":"- Telemetry: Add Expo metaframework - [#33783](https://github.com/storybookjs/storybook/pull/33783), thanks @copilot-swe-agent!\n- Telemetry: Add init exit event - [#33773](https://github.com/storybookjs/storybook/pull/33773), thanks @valentinpalkovic!\n- Telemetry: Add share events - [#33766](https://github.com/storybookjs/storybook/pull/33766), thanks @ndelangen!\n- Test: Update event creation logic in user-event package - [#33787](https://github.com/storybookjs/storybook/pull/33787), thanks @valentinpalkovic!"}} \ No newline at end of file