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