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/CHANGELOG.md b/CHANGELOG.md
index 6493743dae6d..a0388a028a51 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 10.2.7
+
+- CSF: Fix cross-file story imports in csf-factories codemod - [#33723](https://github.com/storybookjs/storybook/pull/33723), thanks @yatishgoel!
+- Core: Fix rendering of View Transitions in Firefox - [#33651](https://github.com/storybookjs/storybook/pull/33651), thanks @ghengeveld!
+- Globals: Repair dynamicTitle: false for user-defined tools - [#33284](https://github.com/storybookjs/storybook/pull/33284), thanks @ia319!
+- Logger: Honor --loglevel for npmlog output - [#33776](https://github.com/storybookjs/storybook/pull/33776), thanks @LouisLau-art!
+
## 10.2.6
- Addon-Vitest: Skip postinstall setup when configured - [#33712](https://github.com/storybookjs/storybook/pull/33712), thanks @valentinpalkovic!
diff --git a/CHANGELOG.prerelease.md b/CHANGELOG.prerelease.md
index 5d26eb530d5a..9b76002fa154 100644
--- a/CHANGELOG.prerelease.md
+++ b/CHANGELOG.prerelease.md
@@ -5,6 +5,10 @@
- Core: Ignore empty files when indexing - [#33782](https://github.com/storybookjs/storybook/pull/33782), thanks @JReinhold!
- Globals: Repair dynamicTitle: false for user-defined tools - [#33284](https://github.com/storybookjs/storybook/pull/33284), thanks @ia319!
- Logger: Honor --loglevel for npmlog output - [#33776](https://github.com/storybookjs/storybook/pull/33776), thanks @LouisLau-art!
+- 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.3.0-alpha.4
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 cd9ab70adc9d..381b6e8e064c 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';
@@ -271,7 +271,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 d90b8235b07c..ba7192ab4e20 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/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) => {
diff --git a/code/core/src/telemetry/types.ts b/code/core/src/telemetry/types.ts
index a86917ee5386..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'
@@ -42,6 +43,7 @@ export type EventType =
| 'migrate'
| 'preview-first-load'
| 'doctor'
+ | 'share'
| 'ghost-stories';
export interface Dependency {
version: string | undefined;
diff --git a/code/frameworks/nextjs-vite/src/index.ts b/code/frameworks/nextjs-vite/src/index.ts
index b0d1de86853d..88d59f5de7c6 100644
--- a/code/frameworks/nextjs-vite/src/index.ts
+++ b/code/frameworks/nextjs-vite/src/index.ts
@@ -31,4 +31,4 @@ export function definePreview[]>(
});
}
-interface NextPreview extends ReactPreview {}
+export interface NextPreview extends ReactPreview {}
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.'
);
diff --git a/code/package.json b/code/package.json
index 25f738268513..30b7329e1cab 100644
--- a/code/package.json
+++ b/code/package.json
@@ -220,5 +220,6 @@
"Dependency Upgrades"
]
]
- }
+ },
+ "deferredNextVersion": "10.3.0-alpha.5"
}
diff --git a/docs/versions/next.json b/docs/versions/next.json
index 85719cf980ba..5b84914b77c0 100644
--- a/docs/versions/next.json
+++ b/docs/versions/next.json
@@ -1 +1 @@
-{"version":"10.3.0-alpha.5","info":{"plain":"- CLI: Support addon-vitest setup when --skip-install is passed - [#33718](https://github.com/storybookjs/storybook/pull/33718), thanks @valentinpalkovic!\n- CSF: Fix cross-file story imports in csf-factories codemod - [#33723](https://github.com/storybookjs/storybook/pull/33723), thanks @yatishgoel!\n- Core: Ignore empty files when indexing - [#33782](https://github.com/storybookjs/storybook/pull/33782), thanks @JReinhold!\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.3.0-alpha.5","info":{"plain":"- CLI: Support addon-vitest setup when --skip-install is passed - [#33718](https://github.com/storybookjs/storybook/pull/33718), thanks @valentinpalkovic!\n- CSF: Fix cross-file story imports in csf-factories codemod - [#33723](https://github.com/storybookjs/storybook/pull/33723), thanks @yatishgoel!\n- Core: Ignore empty files when indexing - [#33782](https://github.com/storybookjs/storybook/pull/33782), thanks @JReinhold!\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!\n- 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!"}}
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