diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b3dd726dbd7..b1c0c5d81f88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 8.2.5 + +- CPC: Add the globals export for manager - [#28650](https://github.com/storybookjs/storybook/pull/28650), thanks @ndelangen! +- CPC: Correct path to the `@storybook/theming/create` alias - [#28643](https://github.com/storybookjs/storybook/pull/28643), thanks @Averethel! +- Components: Remove external overrides - [#28632](https://github.com/storybookjs/storybook/pull/28632), thanks @kasperpeulen! +- Core: Fix header for MountMustBeDestructuredError message - [#28590](https://github.com/storybookjs/storybook/pull/28590), thanks @0916dhkim! +- Onboarding: Fix code snippet when story name differs from export name - [#28649](https://github.com/storybookjs/storybook/pull/28649), thanks @ghengeveld! +- Telemetry: Add mount, beforeEach, moduleMock stats - [#28624](https://github.com/storybookjs/storybook/pull/28624), thanks @shilman! +- Telemetry: CSF feature usage - [#28622](https://github.com/storybookjs/storybook/pull/28622), thanks @shilman! + ## 8.2.4 - CLI: Add diagnostic when the `storybook` package is missing - [#28604](https://github.com/storybookjs/storybook/pull/28604), thanks @kasperpeulen! diff --git a/code/addons/docs/src/preset.ts b/code/addons/docs/src/preset.ts index 0ad19b34bd58..8d359204c227 100644 --- a/code/addons/docs/src/preset.ts +++ b/code/addons/docs/src/preset.ts @@ -68,11 +68,11 @@ async function webpack( * * In the future the `@storybook/theming` and `@storybook/components` can be removed, as they should be singletons in the future due to the peerDependency on `storybook` package. */ - const cliPath = require.resolve('storybook/package.json'); - const themingPath = join(cliPath, '..', 'core', 'theming', 'index.js'); + const cliPath = dirname(require.resolve('storybook/package.json')); + const themingPath = join(cliPath, 'core', 'theming', 'index.js'); const themingCreatePath = join(cliPath, 'core', 'theming', 'create.js'); - const componentsPath = join(cliPath, '..', 'core', 'components', 'index.js'); + const componentsPath = join(cliPath, 'core', 'components', 'index.js'); const blocksPath = dirname(require.resolve('@storybook/blocks/package.json')); if (Array.isArray(webpackConfig.resolve?.alias)) { alias = [...webpackConfig.resolve?.alias]; diff --git a/code/addons/onboarding/src/Onboarding.tsx b/code/addons/onboarding/src/Onboarding.tsx index e387659cf7ce..fde1526aa483 100644 --- a/code/addons/onboarding/src/Onboarding.tsx +++ b/code/addons/onboarding/src/Onboarding.tsx @@ -75,6 +75,7 @@ export default function Onboarding({ api }: { api: API }) { const [createNewStoryForm, setCreateNewStoryForm] = useState(); const [createdStory, setCreatedStory] = useState<{ newStoryName: string; + newStoryExportName: string; sourceFileContent: string; sourceFileName: string; } | null>(); @@ -158,8 +159,8 @@ export default function Onboarding({ api }: { api: API }) { } const source = createdStory?.sourceFileContent; - const startIndex = source?.lastIndexOf(`export const ${createdStory?.newStoryName}`); - const snippet = source?.slice(startIndex); + const startIndex = source?.lastIndexOf(`export const ${createdStory?.newStoryExportName}`); + const snippet = source?.slice(startIndex).trim(); const startingLineNumber = source?.slice(0, startIndex).split('\n').length; const steps: StepDefinition[] = [ diff --git a/code/core/package.json b/code/core/package.json index cc832dc7b873..ddb05823afdc 100644 --- a/code/core/package.json +++ b/code/core/package.json @@ -142,6 +142,11 @@ "import": "./dist/manager/globals-module-info.js", "require": "./dist/manager/globals-module-info.cjs" }, + "./manager/globals": { + "types": "./dist/manager/globals.d.ts", + "import": "./dist/manager/globals.js", + "require": "./dist/manager/globals.cjs" + }, "./preview/globals": { "types": "./dist/preview/globals.d.ts", "import": "./dist/preview/globals.js", @@ -229,6 +234,9 @@ "manager/globals-module-info": [ "./dist/manager/globals-module-info.d.ts" ], + "manager/globals": [ + "./dist/manager/globals.d.ts" + ], "preview/globals": [ "./dist/preview/globals.d.ts" ] diff --git a/code/core/scripts/entries.ts b/code/core/scripts/entries.ts index 85297c1b0c56..a618dac3b623 100644 --- a/code/core/scripts/entries.ts +++ b/code/core/scripts/entries.ts @@ -28,17 +28,13 @@ export const getEntries = (cwd: string) => { define('src/preview-api/index.ts', ['browser', 'node'], true), define('src/manager-api/index.ts', ['browser', 'node'], true, ['react']), define('src/router/index.ts', ['browser', 'node'], true, ['react']), - define('src/components/index.ts', ['browser', 'node'], true, [ - 'react', - 'react-dom', - '@storybook/csf', - '@storybook/global', - ]), + define('src/components/index.ts', ['browser', 'node'], true, ['react', 'react-dom']), define('src/theming/index.ts', ['browser', 'node'], true, ['react']), define('src/theming/create.ts', ['browser', 'node'], true, ['react']), define('src/docs-tools/index.ts', ['browser', 'node'], true), define('src/manager/globals-module-info.ts', ['node'], true), + define('src/manager/globals.ts', ['node'], true), define('src/preview/globals.ts', ['node'], true), ]; }; diff --git a/code/core/src/core-events/data/save-story.ts b/code/core/src/core-events/data/save-story.ts index 8bb4091e513e..0162ecdb0819 100644 --- a/code/core/src/core-events/data/save-story.ts +++ b/code/core/src/core-events/data/save-story.ts @@ -9,7 +9,9 @@ export interface SaveStoryResponsePayload { csfId: string; newStoryId?: string; newStoryName?: string; + newStoryExportName?: string; sourceFileContent?: string; sourceFileName?: string; sourceStoryName?: string; + sourceStoryExportName?: string; } diff --git a/code/core/src/core-server/utils/StoryIndexGenerator.test.ts b/code/core/src/core-server/utils/StoryIndexGenerator.test.ts index 2d2b8d2b19c4..e07b46ba83af 100644 --- a/code/core/src/core-server/utils/StoryIndexGenerator.test.ts +++ b/code/core/src/core-server/utils/StoryIndexGenerator.test.ts @@ -68,7 +68,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex, stats } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "a--story-one": { @@ -89,6 +90,18 @@ describe('StoryIndexGenerator', () => { "v": 5, } `); + + expect(stats).toMatchInlineSnapshot(` + { + "beforeEach": 0, + "loaders": 0, + "moduleMock": 0, + "mount": 0, + "play": 0, + "render": 0, + "storyFn": 0, + } + `); }); }); describe('single file .story specifier', () => { @@ -101,7 +114,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "f--story-one": { @@ -133,7 +147,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "stories--story-one": { @@ -165,7 +180,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "componentpath-extension--story-one": { @@ -245,7 +261,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex, stats } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "a--story-one": { @@ -336,6 +353,68 @@ describe('StoryIndexGenerator', () => { "title": "first-nested/deeply/F", "type": "story", }, + "first-nested-deeply-features--with-csf-1": { + "componentPath": undefined, + "id": "first-nested-deeply-features--with-csf-1", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With CSF 1", + "tags": [ + "dev", + "test", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-play": { + "componentPath": undefined, + "id": "first-nested-deeply-features--with-play", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Play", + "tags": [ + "dev", + "test", + "play-fn", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-render": { + "componentPath": undefined, + "id": "first-nested-deeply-features--with-render", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Render", + "tags": [ + "dev", + "test", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-story-fn": { + "componentPath": undefined, + "id": "first-nested-deeply-features--with-story-fn", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Story Fn", + "tags": [ + "dev", + "test", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-test": { + "componentPath": undefined, + "id": "first-nested-deeply-features--with-test", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Test", + "tags": [ + "dev", + "test", + "play-fn", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, "h--story-one": { "componentPath": undefined, "id": "h--story-one", @@ -378,6 +457,18 @@ describe('StoryIndexGenerator', () => { "v": 5, } `); + + expect(stats).toMatchInlineSnapshot(` + { + "beforeEach": 1, + "loaders": 1, + "moduleMock": 0, + "mount": 1, + "play": 2, + "render": 1, + "storyFn": 1, + } + `); }); }); @@ -395,7 +486,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], autodocsOptions); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex, stats } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "a--story-one": { @@ -512,6 +604,68 @@ describe('StoryIndexGenerator', () => { "title": "first-nested/deeply/F", "type": "story", }, + "first-nested-deeply-features--with-csf-1": { + "componentPath": undefined, + "id": "first-nested-deeply-features--with-csf-1", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With CSF 1", + "tags": [ + "dev", + "test", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-play": { + "componentPath": undefined, + "id": "first-nested-deeply-features--with-play", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Play", + "tags": [ + "dev", + "test", + "play-fn", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-render": { + "componentPath": undefined, + "id": "first-nested-deeply-features--with-render", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Render", + "tags": [ + "dev", + "test", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-story-fn": { + "componentPath": undefined, + "id": "first-nested-deeply-features--with-story-fn", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Story Fn", + "tags": [ + "dev", + "test", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-test": { + "componentPath": undefined, + "id": "first-nested-deeply-features--with-test", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Test", + "tags": [ + "dev", + "test", + "play-fn", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, "h--docs": { "id": "h--docs", "importPath": "./src/H.stories.mjs", @@ -567,6 +721,18 @@ describe('StoryIndexGenerator', () => { "v": 5, } `); + + expect(stats).toMatchInlineSnapshot(` + { + "beforeEach": 1, + "loaders": 1, + "moduleMock": 0, + "mount": 1, + "play": 2, + "render": 1, + "storyFn": 1, + } + `); }); const autodocsTrueOptions = { @@ -603,6 +769,12 @@ describe('StoryIndexGenerator', () => { "componentpath-package--story-one", "first-nested-deeply-f--docs", "first-nested-deeply-f--story-one", + "first-nested-deeply-features--docs", + "first-nested-deeply-features--with-play", + "first-nested-deeply-features--with-story-fn", + "first-nested-deeply-features--with-render", + "first-nested-deeply-features--with-test", + "first-nested-deeply-features--with-csf-1", "nested-button--docs", "nested-button--story-one", "second-nested-g--docs", @@ -639,6 +811,12 @@ describe('StoryIndexGenerator', () => { "componentpath-package--story-one", "first-nested-deeply-f--docs", "first-nested-deeply-f--story-one", + "first-nested-deeply-features--docs", + "first-nested-deeply-features--with-play", + "first-nested-deeply-features--with-story-fn", + "first-nested-deeply-features--with-render", + "first-nested-deeply-features--with-test", + "first-nested-deeply-features--with-csf-1", "nested-button--docs", "nested-button--story-one", "second-nested-g--docs", @@ -730,7 +908,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], autodocsOptions); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "b--docs": { @@ -794,7 +973,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], autodocsOptions); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "b--docs": { @@ -862,7 +1042,8 @@ describe('StoryIndexGenerator', () => { ); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "a--docs": { @@ -919,7 +1100,8 @@ describe('StoryIndexGenerator', () => { generator.getProjectTags = () => ['dev', 'test', 'autodocs']; await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "a--docs": { @@ -970,7 +1152,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], autodocsOptions); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "duplicate-a--docs": { @@ -1030,7 +1213,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([csfSpecifier], autodocsOptions); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": {}, "v": 5, @@ -1047,7 +1231,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([csfSpecifier], autodocsOptions); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "my-component-a--docs": { @@ -1088,7 +1273,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([storiesSpecifier, docsSpecifier], options); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "a--metaof": { @@ -1222,7 +1408,8 @@ describe('StoryIndexGenerator', () => { }); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "a--metaof": { @@ -1328,7 +1515,8 @@ describe('StoryIndexGenerator', () => { options ); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "a--story-one": { @@ -1395,7 +1583,8 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([csfSpecifier, docsSpecifier], options); await generator.initialize(); - expect(await generator.getIndex()).toMatchInlineSnapshot(` + const { storyIndex } = await generator.getIndexAndStats(); + expect(storyIndex).toMatchInlineSnapshot(` { "entries": { "my-component-b--docs": { @@ -1603,6 +1792,11 @@ describe('StoryIndexGenerator', () => { "componentpath-noextension--story-one", "componentpath-package--story-one", "first-nested-deeply-f--story-one", + "first-nested-deeply-features--with-play", + "first-nested-deeply-features--with-story-fn", + "first-nested-deeply-features--with-render", + "first-nested-deeply-features--with-test", + "first-nested-deeply-features--with-csf-1", ] `); }); @@ -1621,7 +1815,7 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); await generator.getIndex(); - expect(readCsfMock).toHaveBeenCalledTimes(10); + expect(readCsfMock).toHaveBeenCalledTimes(11); readCsfMock.mockClear(); await generator.getIndex(); @@ -1679,7 +1873,7 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); await generator.getIndex(); - expect(readCsfMock).toHaveBeenCalledTimes(10); + expect(readCsfMock).toHaveBeenCalledTimes(11); generator.invalidate(specifier, './src/B.stories.ts', false); @@ -1764,7 +1958,7 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); await generator.getIndex(); - expect(readCsfMock).toHaveBeenCalledTimes(10); + expect(readCsfMock).toHaveBeenCalledTimes(11); generator.invalidate(specifier, './src/B.stories.ts', true); @@ -1803,7 +1997,7 @@ describe('StoryIndexGenerator', () => { const generator = new StoryIndexGenerator([specifier], options); await generator.initialize(); await generator.getIndex(); - expect(readCsfMock).toHaveBeenCalledTimes(10); + expect(readCsfMock).toHaveBeenCalledTimes(11); generator.invalidate(specifier, './src/B.stories.ts', true); diff --git a/code/core/src/core-server/utils/StoryIndexGenerator.ts b/code/core/src/core-server/utils/StoryIndexGenerator.ts index 29f71429bf95..d3bcb707f6ce 100644 --- a/code/core/src/core-server/utils/StoryIndexGenerator.ts +++ b/code/core/src/core-server/utils/StoryIndexGenerator.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-underscore-dangle */ import path from 'node:path'; import chalk from 'chalk'; import fs from 'fs-extra'; @@ -17,6 +18,7 @@ import type { StoryIndex, Indexer, StorybookConfigRaw, + IndexInputStats, } from '@storybook/core/types'; import { userOrAutoTitleFromSpecifier, sortStoriesV7 } from '@storybook/core/preview-api'; import { commonGlobOptions, normalizeStoryPath } from '@storybook/core/common'; @@ -27,14 +29,17 @@ import { analyze } from '@storybook/docs-mdx'; import { dedent } from 'ts-dedent'; import { autoName } from './autoName'; import { IndexingError, MultipleIndexingError } from './IndexingError'; +import { addStats, type IndexStatsSummary } from './summarizeStats'; // Extended type to keep track of the csf meta id so we know the component id when referencing docs in `extractDocs` -type StoryIndexEntryWithMetaId = StoryIndexEntry & { metaId?: string }; +type StoryIndexEntryWithExtra = StoryIndexEntry & { + extra: { metaId?: string; stats: IndexInputStats }; +}; /** A .mdx file will produce a docs entry */ type DocsCacheEntry = DocsIndexEntry; /** A *.stories.* file will produce a list of stories and possibly a docs entry */ type StoriesCacheEntry = { - entries: (StoryIndexEntryWithMetaId | DocsIndexEntry)[]; + entries: (StoryIndexEntryWithExtra | DocsIndexEntry)[]; dependents: Path[]; type: 'stories'; }; @@ -104,6 +109,9 @@ export class StoryIndexGenerator { // - the preview changes [not yet implemented] private lastIndex?: StoryIndex | null; + // Cache the last value stats calculation, mirroring lastIndex + private lastStats?: IndexStatsSummary; + // Same as the above but for the error case private lastError?: Error | null; @@ -222,7 +230,7 @@ export class StoryIndexGenerator { projectTags, }: { projectTags?: Tag[]; - }): Promise<(IndexEntry | ErrorEntry)[]> { + }): Promise<{ entries: (IndexEntry | ErrorEntry)[]; stats: IndexStatsSummary }> { // First process all the story files. Then, in a second pass, // process the docs files. The reason for this is that the docs // files may use the `` syntax, which requires @@ -237,7 +245,8 @@ export class StoryIndexGenerator { this.extractDocs(specifier, absolutePath, projectTags) ); - return this.specifiers.flatMap((specifier) => { + const statsSummary = {} as IndexStatsSummary; + const entries = this.specifiers.flatMap((specifier) => { const cache = this.specifierToCache.get(specifier); invariant( cache, @@ -252,12 +261,17 @@ export class StoryIndexGenerator { return entry.entries.map((item) => { if (item.type === 'docs') return item; - // Drop the meta id as it isn't part of the index, we just used it for record keeping in `extractDocs` - const { metaId, ...existing } = item; + + addStats(item.extra.stats, statsSummary); + + // Drop extra data used for internal bookkeeping + const { extra, ...existing } = item; return existing; }); }); }); + + return { entries, stats: statsSummary }; } findDependencies(absoluteImports: Path[]) { @@ -341,7 +355,7 @@ export class StoryIndexGenerator { ]); } - const entries: ((StoryIndexEntryWithMetaId | DocsCacheEntry) & { tags: Tag[] })[] = + const entries: ((StoryIndexEntryWithExtra | DocsCacheEntry) & { tags: Tag[] })[] = indexInputs.map((input) => { const name = input.name ?? storyNameFromExport(input.exportName); const componentPath = @@ -349,14 +363,16 @@ export class StoryIndexGenerator { this.resolveComponentPath(input.rawComponentPath, absolutePath, matchPath); const title = input.title ?? defaultMakeTitle(); - // eslint-disable-next-line no-underscore-dangle const id = input.__id ?? toId(input.metaId ?? title, storyNameFromExport(input.exportName)); const tags = combineTags(...projectTags, ...(input.tags ?? [])); return { type: 'story', id, - metaId: input.metaId, + extra: { + metaId: input.metaId, + stats: input.__stats ?? {}, + }, name, title, importPath, @@ -428,12 +444,12 @@ export class StoryIndexGenerator { // Also, if `result.of` is set, it means that we're using the `` syntax, // so find the `title` defined the file that `meta` points to. - let csfEntry: StoryIndexEntryWithMetaId | undefined; + let csfEntry: StoryIndexEntryWithExtra | undefined; if (result.of) { const absoluteOf = makeAbsolute(result.of, normalizedPath, this.options.workingDir); dependencies.forEach((dep) => { if (dep.entries.length > 0) { - const first = dep.entries.find((e) => e.type !== 'docs') as StoryIndexEntryWithMetaId; + const first = dep.entries.find((e) => e.type !== 'docs') as StoryIndexEntryWithExtra; if ( path @@ -475,7 +491,7 @@ export class StoryIndexGenerator { result.name || (csfEntry ? autoName(importPath, csfEntry.importPath, defaultName) : defaultName); - const id = toId(csfEntry?.metaId || title, name); + const id = toId(csfEntry?.extra.metaId || title, name); const tags = combineTags( ...projectTags, @@ -598,7 +614,12 @@ export class StoryIndexGenerator { } async getIndex() { - if (this.lastIndex) return this.lastIndex; + return (await this.getIndexAndStats()).storyIndex; + } + + async getIndexAndStats(): Promise<{ storyIndex: StoryIndex; stats: IndexStatsSummary }> { + if (this.lastIndex && this.lastStats) + return { storyIndex: this.lastIndex, stats: this.lastStats }; if (this.lastError) throw this.lastError; const previewCode = await this.getPreviewCode(); @@ -606,7 +627,7 @@ export class StoryIndexGenerator { // Extract any entries that are currently missing // Pull out each file's stories into a list of stories, to be composed and sorted - const storiesList = await this.ensureExtracted({ projectTags }); + const { entries: storiesList, stats } = await this.ensureExtracted({ projectTags }); try { const errorEntries = storiesList.filter((entry) => entry.type === 'error'); @@ -635,12 +656,13 @@ export class StoryIndexGenerator { previewCode && getStorySortParameter(previewCode) ); + this.lastStats = stats; this.lastIndex = { v: 5, entries: sorted, }; - return this.lastIndex; + return { storyIndex: this.lastIndex, stats: this.lastStats }; } catch (err) { this.lastError = err == null || err instanceof Error ? err : undefined; invariant(this.lastError); diff --git a/code/core/src/core-server/utils/__mockdata__/src/first-nested/deeply/Features.stories.jsx b/code/core/src/core-server/utils/__mockdata__/src/first-nested/deeply/Features.stories.jsx new file mode 100644 index 000000000000..90d96b590b6a --- /dev/null +++ b/code/core/src/core-server/utils/__mockdata__/src/first-nested/deeply/Features.stories.jsx @@ -0,0 +1,27 @@ +const component = {}; +export default { + component, +}; + +export const WithPlay = { + play: async () => {}, +}; + +export const WithStoryFn = () => {}; + +export const WithRender = { + render: () => {}, +}; + +export const WithTest = { + beforeEach: async () => {}, + play: async ({ mount }) => { + await mount(); + }, +}; + +export const WithCSF1 = { + parameters: {}, + decorators: [], + loaders: [], +}; diff --git a/code/core/src/core-server/utils/__tests__/index-extraction.test.ts b/code/core/src/core-server/utils/__tests__/index-extraction.test.ts index 01a880479b99..6fbbdeb41c6c 100644 --- a/code/core/src/core-server/utils/__tests__/index-extraction.test.ts +++ b/code/core/src/core-server/utils/__tests__/index-extraction.test.ts @@ -62,9 +62,12 @@ describe('story extraction', () => { "entries": [ { "componentPath": undefined, + "extra": { + "metaId": "a", + "stats": {}, + }, "id": "a--story-one", "importPath": "./src/A.stories.js", - "metaId": "a", "name": "Story One", "tags": [ "story-tag-from-indexer", @@ -74,9 +77,12 @@ describe('story extraction', () => { }, { "componentPath": undefined, + "extra": { + "metaId": "custom-id", + "stats": {}, + }, "id": "some-fully-custom-id", "importPath": "./src/A.stories.js", - "metaId": "custom-id", "name": "Another Story Name", "tags": [ "story-tag-from-indexer", @@ -118,9 +124,12 @@ describe('story extraction', () => { "entries": [ { "componentPath": undefined, + "extra": { + "metaId": undefined, + "stats": {}, + }, "id": "f--story-one", "importPath": "./src/first-nested/deeply/F.stories.js", - "metaId": undefined, "name": "Story One", "tags": [], "title": "F", @@ -164,9 +173,12 @@ describe('story extraction', () => { "entries": [ { "componentPath": undefined, + "extra": { + "metaId": "a", + "stats": {}, + }, "id": "a--story-one", "importPath": "./src/first-nested/deeply/F.stories.js", - "metaId": "a", "name": "Story One", "tags": [ "story-tag-from-indexer", @@ -212,9 +224,12 @@ describe('story extraction', () => { "entries": [ { "componentPath": undefined, + "extra": { + "metaId": "a", + "stats": {}, + }, "id": "a--story-one", "importPath": "./src/A.stories.js", - "metaId": "a", "name": "Story One", "tags": [ "story-tag-from-indexer", @@ -278,9 +293,12 @@ describe('story extraction', () => { "entries": [ { "componentPath": undefined, + "extra": { + "metaId": undefined, + "stats": {}, + }, "id": "a--story-one", "importPath": "./src/A.stories.js", - "metaId": undefined, "name": "Story One", "tags": [ "story-tag-from-indexer", @@ -290,9 +308,12 @@ describe('story extraction', () => { }, { "componentPath": undefined, + "extra": { + "metaId": undefined, + "stats": {}, + }, "id": "custom-title--story-two", "importPath": "./src/A.stories.js", - "metaId": undefined, "name": "Custom Name For Second Story", "tags": [ "story-tag-from-indexer", @@ -302,9 +323,12 @@ describe('story extraction', () => { }, { "componentPath": undefined, + "extra": { + "metaId": "custom-meta-id", + "stats": {}, + }, "id": "custom-meta-id--story-three", "importPath": "./src/A.stories.js", - "metaId": "custom-meta-id", "name": "Story Three", "tags": [ "story-tag-from-indexer", @@ -347,9 +371,12 @@ describe('story extraction', () => { "entries": [ { "componentPath": undefined, + "extra": { + "metaId": undefined, + "stats": {}, + }, "id": "a--story-one", "importPath": "./src/A.stories.js", - "metaId": undefined, "name": "Story One", "tags": [ "story-tag-from-indexer", @@ -397,9 +424,12 @@ describe('docs entries from story extraction', () => { "entries": [ { "componentPath": undefined, + "extra": { + "metaId": undefined, + "stats": {}, + }, "id": "a--story-one", "importPath": "./src/A.stories.js", - "metaId": undefined, "name": "Story One", "tags": [ "story-tag-from-indexer", @@ -457,9 +487,12 @@ describe('docs entries from story extraction', () => { }, { "componentPath": undefined, + "extra": { + "metaId": undefined, + "stats": {}, + }, "id": "a--story-one", "importPath": "./src/A.stories.js", - "metaId": undefined, "name": "Story One", "tags": [ "autodocs", @@ -506,9 +539,12 @@ describe('docs entries from story extraction', () => { "entries": [ { "componentPath": undefined, + "extra": { + "metaId": undefined, + "stats": {}, + }, "id": "a--story-one", "importPath": "./src/A.stories.js", - "metaId": undefined, "name": "Story One", "tags": [ "autodocs", diff --git a/code/core/src/core-server/utils/doTelemetry.ts b/code/core/src/core-server/utils/doTelemetry.ts index b7d039119c3b..be9479cf3348 100644 --- a/code/core/src/core-server/utils/doTelemetry.ts +++ b/code/core/src/core-server/utils/doTelemetry.ts @@ -15,9 +15,9 @@ export async function doTelemetry( ) { if (!core?.disableTelemetry) { initializedStoryIndexGenerator.then(async (generator) => { - let storyIndex: StoryIndex | undefined; + let indexAndStats; try { - storyIndex = await generator?.getIndex(); + indexAndStats = await generator?.getIndexAndStats(); } catch (err) { // If we fail to get the index, treat it as a recoverable error, but send it up to telemetry // as if we crashed. In the future we will revisit this to send a distinct error @@ -36,10 +36,11 @@ export async function doTelemetry( const payload = { precedingUpgrade: await getPrecedingUpgrade(), }; - if (storyIndex) { + if (indexAndStats) { Object.assign(payload, { versionStatus: versionUpdates && versionCheck ? versionStatus(versionCheck) : 'disabled', - storyIndex: summarizeIndex(storyIndex), + storyIndex: summarizeIndex(indexAndStats.storyIndex), + storyStats: indexAndStats.stats, }); } telemetry('dev', payload, { configDir: options.configDir }); diff --git a/code/core/src/core-server/utils/save-story/save-story.ts b/code/core/src/core-server/utils/save-story/save-story.ts index 8ad7ef738ff3..5369d99a4cba 100644 --- a/code/core/src/core-server/utils/save-story/save-story.ts +++ b/code/core/src/core-server/utils/save-story/save-story.ts @@ -111,9 +111,11 @@ export function initializeSaveStory(channel: Channel, options: Options, coreConf csfId, newStoryId, newStoryName, + newStoryExportName: name, sourceFileContent: code, sourceFileName, sourceStoryName, + sourceStoryExportName: storyName, }, error: null, } satisfies ResponseData); diff --git a/code/core/src/core-server/utils/stories-json.test.ts b/code/core/src/core-server/utils/stories-json.test.ts index 84b201ab62f1..a4ad0fa780b7 100644 --- a/code/core/src/core-server/utils/stories-json.test.ts +++ b/code/core/src/core-server/utils/stories-json.test.ts @@ -263,6 +263,63 @@ describe('useStoriesJson', () => { "title": "first-nested/deeply/F", "type": "story", }, + "first-nested-deeply-features--with-csf-1": { + "id": "first-nested-deeply-features--with-csf-1", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With CSF 1", + "tags": [ + "dev", + "test", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-play": { + "id": "first-nested-deeply-features--with-play", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Play", + "tags": [ + "dev", + "test", + "play-fn", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-render": { + "id": "first-nested-deeply-features--with-render", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Render", + "tags": [ + "dev", + "test", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-story-fn": { + "id": "first-nested-deeply-features--with-story-fn", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Story Fn", + "tags": [ + "dev", + "test", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, + "first-nested-deeply-features--with-test": { + "id": "first-nested-deeply-features--with-test", + "importPath": "./src/first-nested/deeply/Features.stories.jsx", + "name": "With Test", + "tags": [ + "dev", + "test", + "play-fn", + ], + "title": "first-nested/deeply/Features", + "type": "story", + }, "h--story-one": { "id": "h--story-one", "importPath": "./src/H.stories.mjs", diff --git a/code/core/src/core-server/utils/summarizeStats.test.ts b/code/core/src/core-server/utils/summarizeStats.test.ts new file mode 100644 index 000000000000..e1e342e94260 --- /dev/null +++ b/code/core/src/core-server/utils/summarizeStats.test.ts @@ -0,0 +1,18 @@ +import { it, expect } from 'vitest'; +import { summarizeStats } from './summarizeStats'; + +it('should summarize stats', () => { + const stats = [ + { play: true, render: true, storyFn: false }, + { play: true, render: false, storyFn: false }, + { play: false, render: false, storyFn: false }, + ]; + const result = summarizeStats(stats); + expect(result).toMatchInlineSnapshot(` + { + "play": 2, + "render": 1, + "storyFn": 0, + } + `); +}); diff --git a/code/core/src/core-server/utils/summarizeStats.ts b/code/core/src/core-server/utils/summarizeStats.ts new file mode 100644 index 000000000000..bdd0ef026ce5 --- /dev/null +++ b/code/core/src/core-server/utils/summarizeStats.ts @@ -0,0 +1,18 @@ +import type { IndexInputStats } from '@storybook/core/types'; + +export type IndexStatsSummary = Record; + +export const addStats = (stat: IndexInputStats, acc: IndexStatsSummary) => { + Object.entries(stat).forEach(([key, value]) => { + const statsKey = key as keyof IndexInputStats; + if (!acc[statsKey]) acc[statsKey] = 0; + acc[statsKey] += value ? 1 : 0; + }); +}; + +export const summarizeStats = (stats: IndexInputStats[]): IndexStatsSummary => { + return stats.reduce((acc, stat) => { + addStats(stat, acc); + return acc; + }, {} as IndexStatsSummary); +}; diff --git a/code/core/src/csf-tools/CsfFile.test.ts b/code/core/src/csf-tools/CsfFile.test.ts index 4c87576d2474..0988e938a51b 100644 --- a/code/core/src/csf-tools/CsfFile.test.ts +++ b/code/core/src/csf-tools/CsfFile.test.ts @@ -2,7 +2,7 @@ import { dedent } from 'ts-dedent'; import { describe, it, expect, vi } from 'vitest'; import yaml from 'js-yaml'; -import { loadCsf } from './CsfFile'; +import { loadCsf, isModuleMock } from './CsfFile'; expect.addSnapshotSerializer({ print: (val: any) => yaml.dump(val).trimEnd(), @@ -42,11 +42,27 @@ describe('CsfFile', () => { parameters: __isArgsStory: false __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: foo-bar--b name: B parameters: __isArgsStory: true __id: foo-bar--b + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -69,10 +85,26 @@ describe('CsfFile', () => { name: A parameters: __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false - id: foo-bar--b name: B parameters: __id: foo-bar--b + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -94,6 +126,14 @@ describe('CsfFile', () => { parameters: __isArgsStory: false __id: foo-bar--basic + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -116,6 +156,14 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -136,6 +184,14 @@ describe('CsfFile', () => { stories: - id: foo-bar--include-a name: Include A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -154,6 +210,14 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: Some story + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -173,8 +237,24 @@ describe('CsfFile', () => { stories: - id: default-title--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: default-title--b name: B + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -194,8 +274,24 @@ describe('CsfFile', () => { stories: - id: custom-id--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: custom-id--b name: B + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -215,8 +311,24 @@ describe('CsfFile', () => { stories: - id: custom-meta-id--just-custom-meta-id name: Just Custom Meta Id + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false - id: custom-id name: Custom Paremeters Id + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -237,8 +349,24 @@ describe('CsfFile', () => { stories: - id: foo-bar-baz--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: foo-bar-baz--b name: B + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -263,11 +391,27 @@ describe('CsfFile', () => { parameters: __isArgsStory: true __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false - id: foo-bar--b name: B parameters: __isArgsStory: true __id: foo-bar--b + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -292,11 +436,27 @@ describe('CsfFile', () => { parameters: __isArgsStory: true __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false - id: foo-bar--b name: B parameters: __isArgsStory: true __id: foo-bar--b + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -318,8 +478,24 @@ describe('CsfFile', () => { stories: - id: foo-bar-baz--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: foo-bar-baz--b name: B + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -341,8 +517,24 @@ describe('CsfFile', () => { stories: - id: foo-bar-baz--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: foo-bar-baz--b name: B + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -362,8 +554,24 @@ describe('CsfFile', () => { stories: - id: default-title--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: default-title--b name: B + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -387,6 +595,14 @@ describe('CsfFile', () => { parameters: __isArgsStory: true __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -409,6 +625,14 @@ describe('CsfFile', () => { parameters: __isArgsStory: false __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -432,6 +656,14 @@ describe('CsfFile', () => { __isArgsStory: false __id: foo-bar--page docsOnly: true + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -460,6 +692,14 @@ describe('CsfFile', () => { __isArgsStory: false __id: foo-bar--page docsOnly: true + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -483,11 +723,27 @@ describe('CsfFile', () => { parameters: __isArgsStory: false __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: foo-bar--b name: B parameters: __isArgsStory: true __id: foo-bar--b + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -506,8 +762,24 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false - id: foo-bar--b name: B + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -531,11 +803,27 @@ describe('CsfFile', () => { parameters: __isArgsStory: true __id: foo-bar--b + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: foo-bar--a name: A parameters: __isArgsStory: false __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -560,6 +848,14 @@ describe('CsfFile', () => { name: A parameters: __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -631,8 +927,24 @@ describe('CsfFile', () => { stories: - id: default-title--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: default-title--b name: B + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); @@ -677,8 +989,24 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false - id: foo-bar--b name: B + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false `); }); }); @@ -747,6 +1075,14 @@ describe('CsfFile', () => { parameters: __isArgsStory: false __id: foo-bar--a + __stats: + play: false + render: true + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -770,6 +1106,14 @@ describe('CsfFile', () => { parameters: __isArgsStory: true __id: foo-bar--a + __stats: + play: false + render: true + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -791,6 +1135,14 @@ describe('CsfFile', () => { parameters: __isArgsStory: true __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -814,6 +1166,14 @@ describe('CsfFile', () => { parameters: __isArgsStory: true __id: foo-bar--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -886,6 +1246,14 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false tags: - 'Y' `); @@ -910,6 +1278,14 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: A + __stats: + play: false + render: true + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false tags: - 'Y' `); @@ -936,6 +1312,14 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: A + __stats: + play: false + render: true + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false tags: - 'Y' `); @@ -987,6 +1371,14 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: A + __stats: + play: true + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false tags: - 'Y' - play-fn @@ -1013,12 +1405,108 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: A + __stats: + play: true + render: true + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false tags: - 'Y' - play-fn `); }); + it('mount', () => { + expect( + parse( + dedent` + export default { title: 'foo/bar' }; + export const A = { + play: ({ mount }) => {}, + }; + ` + ) + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + __stats: + play: true + render: false + loaders: false + beforeEach: false + storyFn: false + mount: true + moduleMock: false + tags: + - play-fn + `); + }); + + it('mount renamed', () => { + expect( + parse( + dedent` + export default { title: 'foo/bar' }; + export const A = { + play: ({ mount: mountRenamed, context }) => {}, + }; + ` + ) + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + __stats: + play: true + render: false + loaders: false + beforeEach: false + storyFn: false + mount: true + moduleMock: false + tags: + - play-fn + `); + }); + + it('mount meta', () => { + expect( + parse( + dedent` + export default { + title: 'foo/bar', + play: ({ context, mount: mountRenamed }) => {}, + }; + export const A = {}; + ` + ) + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + tags: + - play-fn + stories: + - id: foo-bar--a + name: A + __stats: + play: true + render: false + loaders: false + beforeEach: false + storyFn: false + mount: true + moduleMock: false + `); + }); + it('meta csf2', () => { expect( parse( @@ -1026,6 +1514,7 @@ describe('CsfFile', () => { export default { title: 'foo/bar', play: () => {}, tags: ['X'] }; export const A = { render: () => {}, + loaders: [], tags: ['Y'], }; ` @@ -1039,6 +1528,14 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: A + __stats: + play: true + render: true + loaders: true + beforeEach: false + storyFn: false + mount: false + moduleMock: false tags: - 'Y' `); @@ -1062,6 +1559,14 @@ describe('CsfFile', () => { stories: - id: foo-bar--a name: A + __stats: + play: true + render: false + loaders: false + beforeEach: false + storyFn: true + mount: false + moduleMock: false tags: - 'Y' `); @@ -1103,6 +1608,14 @@ describe('CsfFile', () => { - story-tag - play-fn __id: component-id--a + __stats: + play: true + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false - type: story importPath: foo/bar.stories.js exportName: B @@ -1114,6 +1627,14 @@ describe('CsfFile', () => { - story-tag - play-fn __id: component-id--b + __stats: + play: true + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -1143,6 +1664,14 @@ describe('CsfFile', () => { tags: - component-tag __id: custom-story-id + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -1177,6 +1706,14 @@ describe('CsfFile', () => { - story-tag-dup - inherit-tag-dup __id: custom-foo-title--a + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -1226,6 +1763,14 @@ describe('CsfFile', () => { title: custom foo title tags: [] __id: custom-foo-title--a + __stats: + play: false + render: true + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -1254,6 +1799,14 @@ describe('CsfFile', () => { title: custom foo title tags: [] __id: custom-foo-title--a + __stats: + play: false + render: true + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -1282,6 +1835,14 @@ describe('CsfFile', () => { title: custom foo title tags: [] __id: custom-foo-title--a + __stats: + play: false + render: true + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false `); }); @@ -1310,7 +1871,117 @@ describe('CsfFile', () => { title: custom foo title tags: [] __id: custom-foo-title--a + __stats: + play: false + render: true + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: false + `); + }); + }); + + describe('beforeEach', () => { + it('basic', () => { + expect( + parse( + dedent` + export default { title: 'foo/bar' }; + export const A = { + beforeEach: async () => {}, + }; + ` + ) + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + __stats: + play: false + render: false + loaders: false + beforeEach: true + storyFn: false + mount: false + moduleMock: false `); }); }); + + describe('module mocks', () => { + it('alias', () => { + expect( + parse( + dedent` + import foo from '#bar.mock'; + export default { title: 'foo/bar' }; + export const A = {}; + ` + ) + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: true + `); + }); + it('relative', () => { + expect( + parse( + dedent` + import foo from './bar.mock'; + export default { title: 'foo/bar' }; + export const A = {}; + ` + ) + ).toMatchInlineSnapshot(` + meta: + title: foo/bar + stories: + - id: foo-bar--a + name: A + __stats: + play: false + render: false + loaders: false + beforeEach: false + storyFn: false + mount: false + moduleMock: true + `); + }); + }); +}); + +describe('isModuleMock', () => { + it('prefix', () => { + expect(isModuleMock('#foo.mock')).toBe(true); + expect(isModuleMock('./foo.mock')).toBe(true); + expect(isModuleMock('../foo.mock')).toBe(true); + expect(isModuleMock('/foo.mock')).toBe(true); + + expect(isModuleMock('foo.mock')).toBe(false); + expect(isModuleMock('@/foo.mock')).toBe(false); + }); + it('sufixes', () => { + expect(isModuleMock('#foo.mock.js')).toBe(true); + expect(isModuleMock('#foo.mock.mjs')).toBe(true); + expect(isModuleMock('#foo.mock.vue')).toBe(true); + + expect(isModuleMock('#foo.mocktail')).toBe(false); + expect(isModuleMock('#foo.mock.test.ts')).toBe(false); + }); }); diff --git a/code/core/src/csf-tools/CsfFile.ts b/code/core/src/csf-tools/CsfFile.ts index 2ab3bfa27408..149a182bcf7d 100644 --- a/code/core/src/csf-tools/CsfFile.ts +++ b/code/core/src/csf-tools/CsfFile.ts @@ -9,12 +9,18 @@ import bt from '@babel/traverse'; import * as recast from 'recast'; import { toId, isExportStory, storyNameFromExport } from '@storybook/csf'; -import type { ComponentAnnotations, StoryAnnotations, Tag } from '@storybook/core/types'; +import type { + Tag, + StoryAnnotations, + ComponentAnnotations, + IndexedCSFFile, + IndexInput, + IndexInputStats, +} from '@storybook/core/types'; import type { Options } from 'recast'; import { babelParse } from './babelParse'; import { findVarInitialization } from './findVarInitialization'; import type { PrintResultType } from './PrintResultType'; -import type { IndexInput, IndexedCSFFile } from '@storybook/core/types'; // @ts-expect-error (needed due to it's use of `exports.default`) const traverse = (bt.default || bt) as typeof bt; @@ -54,6 +60,8 @@ const formatLocation = (node: t.Node, fileName?: string) => { return `${fileName || ''} (line ${line}, col ${column})`.trim(); }; +export const isModuleMock = (importPath: string) => MODULE_MOCK_REGEX.test(importPath); + const isArgsStory = (init: t.Node, parent: t.Node, csf: CsfFile) => { let storyFn: t.Node = init; // export const Foo = Bar.bind({}) @@ -110,6 +118,25 @@ const sortExports = (exportByName: Record, order: string[]) => { ); }; +const hasMount = (play: t.Node | undefined) => { + if (t.isArrowFunctionExpression(play) || t.isFunctionDeclaration(play)) { + const params = play.params; + if (params.length >= 1) { + const [arg] = params; + if (t.isObjectPattern(arg)) { + return !!arg.properties.find((prop) => { + if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) { + return prop.key.name === 'mount'; + } + }); + } + } + } + return false; +}; + +const MODULE_MOCK_REGEX = /^[.\/#].*\.mock($|\.[^.]*$)/i; + export interface CsfOptions { fileName?: string; makeTitle: (userTitle: string) => string; @@ -136,6 +163,7 @@ export interface StaticMeta export interface StaticStory extends Pick { id: string; + __stats: IndexInputStats; } export class CsfFile { @@ -392,6 +420,7 @@ export class CsfFile { id: 'FIXME', name, parameters, + __stats: {}, }; } }); @@ -422,7 +451,12 @@ export class CsfFile { } } else { self._storyAnnotations[exportName] = {}; - self._stories[exportName] = { id: 'FIXME', name: exportName, parameters: {} }; + self._stories[exportName] = { + id: 'FIXME', + name: exportName, + parameters: {}, + __stats: {}, + }; } } }); @@ -520,7 +554,8 @@ export class CsfFile { parameters.docsOnly = true; } acc[key] = { ...story, id, parameters }; - const { tags, play } = self._storyAnnotations[key]; + const storyAnnotations = self._storyAnnotations[key]; + const { tags, play } = storyAnnotations; if (tags) { const node = t.isIdentifier(tags) ? findVarInitialization(tags.name, this._ast.program) @@ -530,6 +565,18 @@ export class CsfFile { if (play) { acc[key].tags = [...(acc[key].tags || []), 'play-fn']; } + const stats = acc[key].__stats; + ['play', 'render', 'loaders', 'beforeEach'].forEach((annotation) => { + stats[annotation as keyof IndexInputStats] = + !!storyAnnotations[annotation] || !!self._metaAnnotations[annotation]; + }); + const storyExport = self.getStoryExport(key); + stats.storyFn = !!( + t.isArrowFunctionExpression(storyExport) || t.isFunctionDeclaration(storyExport) + ); + stats.mount = hasMount(storyAnnotations.play ?? self._metaAnnotations.play); + stats.moduleMock = !!self.imports.find((fname) => isModuleMock(fname)); + return acc; }, {} as Record @@ -589,6 +636,7 @@ export class CsfFile { metaId: this.meta?.id, tags, __id: story.id, + __stats: story.__stats, }; }); } diff --git a/code/core/src/preview-errors.ts b/code/core/src/preview-errors.ts index 31341bb6132c..c1d070f7c10a 100644 --- a/code/core/src/preview-errors.ts +++ b/code/core/src/preview-errors.ts @@ -214,6 +214,7 @@ export class MountMustBeDestructuredError extends StorybookError { category: Category.PREVIEW_API, code: 12, message: dedent` + Incorrect use of mount in the play function. To use mount in the play function, you must satisfy the following two requirements: diff --git a/code/core/src/types/modules/indexer.ts b/code/core/src/types/modules/indexer.ts index ceb3bf915e51..4e92b647a489 100644 --- a/code/core/src/types/modules/indexer.ts +++ b/code/core/src/types/modules/indexer.ts @@ -87,6 +87,16 @@ export type DocsIndexEntry = BaseIndexEntry & { export type IndexEntry = StoryIndexEntry | DocsIndexEntry; +export interface IndexInputStats { + loaders?: boolean; + play?: boolean; + render?: boolean; + storyFn?: boolean; + mount?: boolean; + beforeEach?: boolean; + moduleMock?: boolean; +} + /** * The base input for indexing a story or docs entry. */ @@ -115,6 +125,10 @@ export type BaseIndexInput = { * Only use this if you need to override the auto-generated id. */ __id?: StoryId; + /** + * Stats about language feature usage that the indexer can optionally report + */ + __stats?: IndexInputStats; }; /** diff --git a/code/deprecated/manager/globals.js b/code/deprecated/manager/globals.js new file mode 100644 index 000000000000..d6ee8ec2d144 --- /dev/null +++ b/code/deprecated/manager/globals.js @@ -0,0 +1 @@ +module.exports = require('storybook/internal/manager/globals'); diff --git a/code/deprecated/preview/globals.js b/code/deprecated/preview/globals.js new file mode 100644 index 000000000000..91340ce6505b --- /dev/null +++ b/code/deprecated/preview/globals.js @@ -0,0 +1 @@ +module.exports = require('storybook/internal/preview/globals'); diff --git a/code/lib/cli/core/manager/globals.cjs b/code/lib/cli/core/manager/globals.cjs new file mode 100644 index 000000000000..3dc797bf4cd7 --- /dev/null +++ b/code/lib/cli/core/manager/globals.cjs @@ -0,0 +1 @@ +module.exports = require('@storybook/core/manager/globals'); diff --git a/code/lib/cli/core/manager/globals.d.ts b/code/lib/cli/core/manager/globals.d.ts new file mode 100644 index 000000000000..47746a38ada4 --- /dev/null +++ b/code/lib/cli/core/manager/globals.d.ts @@ -0,0 +1,2 @@ +export * from '@storybook/core/manager/globals'; +export type * from '@storybook/core/manager/globals'; diff --git a/code/lib/cli/core/manager/globals.js b/code/lib/cli/core/manager/globals.js new file mode 100644 index 000000000000..779828900cda --- /dev/null +++ b/code/lib/cli/core/manager/globals.js @@ -0,0 +1 @@ +export * from '@storybook/core/manager/globals'; diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index 501178157d0d..19b44d23e564 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -167,6 +167,11 @@ "types": "./core/preview/globals.d.ts", "import": "./core/preview/globals.js", "require": "./core/preview/globals.cjs" + }, + "./internal/manager/globals": { + "types": "./core/manager/globals.d.ts", + "import": "./core/manager/globals.js", + "require": "./core/manager/globals.cjs" } }, "main": "dist/index.cjs", @@ -219,6 +224,9 @@ "internal/manager-errors": [ "./core/manager-errors.d.ts" ], + "internal/manager/globals": [ + "./core/manager/globals.d.ts" + ], "internal/manager/globals-module-info": [ "./core/manager/globals-module-info.d.ts" ], diff --git a/code/lib/cli/src/sandbox-templates.ts b/code/lib/cli/src/sandbox-templates.ts index 30773df41ea6..e7a55f4ba360 100644 --- a/code/lib/cli/src/sandbox-templates.ts +++ b/code/lib/cli/src/sandbox-templates.ts @@ -514,7 +514,7 @@ const baseTemplates = { }, 'qwik-vite/default-ts': { name: 'Qwik CLI Latest (Vite | TypeScript)', - script: 'npm create qwik basic {{beforeDir}}', + script: 'npm create qwik playground {{beforeDir}}', // TODO: The community template does not provide standard stories, which is required for e2e tests. Reenable once it does. inDevelopment: true, expected: { diff --git a/code/package.json b/code/package.json index 2b7aa53b3d22..b7d4bedf523f 100644 --- a/code/package.json +++ b/code/package.json @@ -278,5 +278,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "8.2.5" } diff --git a/code/renderers/react/template/stories/csf1.stories.tsx b/code/renderers/react/template/stories/csf1.stories.tsx new file mode 100644 index 000000000000..63a53672b396 --- /dev/null +++ b/code/renderers/react/template/stories/csf1.stories.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +export default { + component: {}, + parameters: { + chromatic: { disable: true }, + }, +}; + +export const Hello1 = () =>
Hello1
; +export const Hello2 = () =>
Hello2
; diff --git a/code/renderers/react/template/stories/csf2.stories.tsx b/code/renderers/react/template/stories/csf2.stories.tsx new file mode 100644 index 000000000000..6b78e54e36e2 --- /dev/null +++ b/code/renderers/react/template/stories/csf2.stories.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +export default { + component: {}, + parameters: { + chromatic: { disable: true }, + }, +}; + +const Template = ({ label }: { label: string }) =>
{label}
; +Template.args = { label: 'Hello' }; + +export const Hello1 = Template.bind({}); + +export const Hello2 = Template.bind({}); + +export const Hello3 = Template.bind({}); diff --git a/docs/addons/addon-knowledge-base.mdx b/docs/addons/addon-knowledge-base.mdx index 11ebc0eec556..c2dbdb497d30 100644 --- a/docs/addons/addon-knowledge-base.mdx +++ b/docs/addons/addon-knowledge-base.mdx @@ -44,33 +44,35 @@ Addon authors can develop their UIs using any React library. But we recommend us Use the components listed below with your next addon. -| Component | Source | Story | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| Action Bar | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/ActionBar/ActionBar.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-actionbar--single-item) | -| Addon Panel | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/addon-panel/addon-panel.tsx) | N/A | -| Badge | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/Badge/Badge.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-badge--all-badges) | -| Button | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/Button/Button.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-button--all-buttons) | -| Form | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/form/index.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-form-button--sizes) | -| Loader | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/Loader/Loader.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-loader--progress-bar) | -| PlaceHolder | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/placeholder/placeholder.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-placeholder--single-child) | -| Scroll Area | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/ScrollArea/ScrollArea.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-scrollarea--vertical) | -| Space | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/spaced/Spaced.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-spaced--row) | -| Syntax Highlighter | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/syntaxhighlighter/syntaxhighlighter.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-syntaxhighlighter--bash) | -| Tabs | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/tabs/tabs.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-tabs--stateful-static) | -| ToolBar | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/bar/bar.tsx) | N/A | -| ToolTip | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/tooltip/Tooltip.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-tooltip-tooltip--basic-default) | -| Zoom | [See component implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/Zoom/Zoom.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-zoom--element-actual-size) | +| Component | Source | Story | +| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | +| Action Bar | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/ActionBar/ActionBar.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-actionbar--single-item) | +| Addon Panel | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/addon-panel/addon-panel.tsx) | N/A | +| Badge | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/Badge/Badge.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-badge--all-badges) | +| Button | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/Button/Button.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-button--all-buttons) | +| Form | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/form/index.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-form-button--sizes) | +| Loader | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/Loader/Loader.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-loader--progress-bar) | +| PlaceHolder | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/placeholder/placeholder.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-placeholder--single-child) | +| Scroll Area | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/ScrollArea/ScrollArea.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-scrollarea--vertical) | +| Space | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/spaced/Spaced.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-spaced--row) | +| Syntax Highlighter | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/syntaxhighlighter/syntaxhighlighter.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-syntaxhighlighter--bash) | +| Tabs | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/tabs/tabs.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-tabs--stateful-static) | +| ToolBar | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/bar/bar.tsx) | N/A | +| ToolTip | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/tooltip/Tooltip.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-tooltip-tooltip--basic-default) | +| Zoom | [See component implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/Zoom/Zoom.tsx) | [See component story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-zoom--element-actual-size) | Complementing the components, also included is a set of UI primitives. Use the content listed below as a reference for styling your addon. -| Component | Source | Story | -| ------------------------------ | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | -| Color Palette (see note below) | [See implementation](https://github.com/storybookjs/storybook/tree/master/code/ui/components/src/Colors) | [See story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-colorpalette--page) | -| Icon | [See implementation](https://github.com/storybookjs/storybook/blob/main/code/ui/components/src/icon/icons.tsx) | [See story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-icon--labels) | -| Typography | [See implementation](https://github.com/storybookjs/storybook/tree/master/code/ui/components/src/typography) | [See story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-typography--all) | +| Component | Source | Story | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | +| Color Palette (see note below) | [See implementation](https://github.com/storybookjs/storybook/tree/next/code/core/src/components/components/Colors) | [See story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-colorpalette--page) | +| Icon | [See implementation](https://github.com/storybookjs/storybook/blob/next/code/core/src/components/components/icon/icon.tsx)| [See story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-icon--labels) | +| Typography | [See implementation](https://github.com/storybookjs/storybook/tree/next/code/core/src/components/components/typography) | [See story](https://main--5a375b97f4b14f0020b0cda3.chromatic.com/?path=/story/basics-typography--all) | + The color palette implemented by `@storybook/components` is a high-level abstraction of the [`@storybook/theming`](https://github.com/storybookjs/storybook/tree/next/code/lib/theming/src) package. + ### Build system diff --git a/docs/versions/latest.json b/docs/versions/latest.json index 7efb4ef64527..4ff251def4c9 100644 --- a/docs/versions/latest.json +++ b/docs/versions/latest.json @@ -1 +1 @@ -{"version":"8.2.4","info":{"plain":"- CLI: Add diagnostic when the `storybook` package is missing - [#28604](https://github.com/storybookjs/storybook/pull/28604), thanks @kasperpeulen!\n- CLI: Make a few automigrations run on all version upgrades - [#28601](https://github.com/storybookjs/storybook/pull/28601), thanks @yannbf!\n- CPC: Direct dependencies on shim packages in renderers - [#28599](https://github.com/storybookjs/storybook/pull/28599), thanks @ndelangen!"}} +{"version":"8.2.5","info":{"plain":"- CPC: Add the globals export for manager - [#28650](https://github.com/storybookjs/storybook/pull/28650), thanks @ndelangen!\n- CPC: Correct path to the `@storybook/theming/create` alias - [#28643](https://github.com/storybookjs/storybook/pull/28643), thanks @Averethel!\n- Core: Fix header for MountMustBeDestructuredError message - [#28590](https://github.com/storybookjs/storybook/pull/28590), thanks @0916dhkim!\n- Telemetry: Add mount, beforeEach, moduleMock stats - [#28624](https://github.com/storybookjs/storybook/pull/28624), thanks @shilman!\n- Telemetry: CSF feature usage - [#28622](https://github.com/storybookjs/storybook/pull/28622), thanks @shilman!"}} diff --git a/docs/versions/next.json b/docs/versions/next.json index 5ca4ed6ffcb7..f2daa3fb9b6b 100644 --- a/docs/versions/next.json +++ b/docs/versions/next.json @@ -1 +1 @@ -{"version":"8.3.0-alpha.0","info":{"plain":""}} +{"version":"8.3.0-alpha.2","info":{"plain":"- Addon-Interactions: Fix status in panel tab - [#28580](https://github.com/storybookjs/storybook/pull/28580), thanks @yannbf!\n- Build: Remove external overrides, use package.json as source of truth - [#28632](https://github.com/storybookjs/storybook/pull/28632), thanks @kasperpeulen!\n- CLI: Add conditional logging for manager and preview start - [#28603](https://github.com/storybookjs/storybook/pull/28603), thanks @tobiasdiez!\n- CPC: Add the globals export for manager - [#28650](https://github.com/storybookjs/storybook/pull/28650), thanks @ndelangen!\n- CPC: Correct path to the `@storybook/theming/create` alias - [#28643](https://github.com/storybookjs/storybook/pull/28643), thanks @Averethel!\n- Core: Fix manager-builder `tsconfig` to emit `react-jsx` - [#28541](https://github.com/storybookjs/storybook/pull/28541), thanks @williamhelmrath!\n- Fix: Add header for MountMustBeDestructuredError message - [#28590](https://github.com/storybookjs/storybook/pull/28590), thanks @0916dhkim!\n- Fix: Prevent iframe from capturing mouse events in composed Storybooks - [#28568](https://github.com/storybookjs/storybook/pull/28568), thanks @Vincentdevreede!\n- Onboarding: Fix code snippet when story name differs from export name - [#28649](https://github.com/storybookjs/storybook/pull/28649), thanks @ghengeveld!\n- Vue: Fix out of memory error when using vue-component-meta - [#28589](https://github.com/storybookjs/storybook/pull/28589), thanks @larsrickert!"}} diff --git a/package.json b/package.json index 8328557199b0..5626789cdb0a 100644 --- a/package.json +++ b/package.json @@ -15,5 +15,6 @@ "vite-ecosystem-ci:before-test": "node ./scripts/vite-ecosystem-ci/before-test.js && cd ./sandbox/react-vite-default-ts && yarn install", "vite-ecosystem-ci:build": "yarn task --task sandbox --template react-vite/default-ts", "vite-ecosystem-ci:test": "yarn task --task test-runner-dev --template react-vite/default-ts --start-from=dev" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" }