diff --git a/code/e2e-tests/composition.spec.ts b/code/e2e-tests/composition.spec.ts new file mode 100644 index 000000000000..2cc2e18f42ca --- /dev/null +++ b/code/e2e-tests/composition.spec.ts @@ -0,0 +1,43 @@ +import { test, expect } from '@playwright/test'; +import { SbPage } from './util'; + +const storybookUrl = process.env.STORYBOOK_URL || 'http://localhost:6006'; + +// This is a slow test, and (presumably) framework independent, so only run it on one sandbox +const skipTest = process.env.STORYBOOK_TEMPLATE_NAME !== 'react-vite/default-ts'; + +test.describe('composition', () => { + test.beforeEach(async ({ page }) => { + if (skipTest) return; + await page.goto(storybookUrl); + await new SbPage(page).waitUntilLoaded(); + }); + + test('should correctly filter composed stories', async ({ page }) => { + if (skipTest) return; + + // Expect that composed Storybooks are visible + await expect(await page.getByTitle('Storybook 8.0.0')).toBeVisible(); + await expect(await page.getByTitle('Storybook 7.6.18')).toBeVisible(); + + // Expect composed stories to be available in the sidebar + await page.locator('[id="storybook\\@8\\.0\\.0_components-badge"]').click(); + await expect( + await page.locator('[id="storybook\\@8\\.0\\.0_components-badge--default"]') + ).toBeVisible(); + + await page.locator('[id="storybook\\@7\\.6\\.18_components-badge"]').click(); + await expect( + await page.locator('[id="storybook\\@7\\.6\\.18_components-badge--default"]') + ).toBeVisible(); + + // Expect composed stories `to be available in the search + await page.getByPlaceholder('Find components').fill('Button'); + await expect( + await page.getByRole('option', { name: 'Button Storybook 8.0.0 / @blocks / examples' }) + ).toBeVisible(); + await expect( + await page.getByRole('option', { name: 'Button Storybook 7.6.18 / @blocks / examples' }) + ).toBeVisible(); + }); +}); diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts index 0e51a725470a..ccce3fe481bc 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.test.ts @@ -88,7 +88,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -119,7 +119,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -150,7 +150,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -192,7 +192,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -294,7 +294,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -440,7 +440,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -593,7 +593,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -656,7 +656,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -714,7 +714,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -771,7 +771,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -789,7 +789,7 @@ describe('StoryIndexGenerator', () => { expect(await generator.getIndex()).toMatchInlineSnapshot(` { "entries": {}, - "v": 4, + "v": 5, } `); }); @@ -832,7 +832,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); @@ -933,7 +933,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, }, - "v": 4, + "v": 5, } `); }); @@ -1066,7 +1066,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, }, - "v": 4, + "v": 5, } `); }); @@ -1127,7 +1127,7 @@ describe('StoryIndexGenerator', () => { "type": "docs", }, }, - "v": 4, + "v": 5, } `); }); @@ -1176,7 +1176,7 @@ describe('StoryIndexGenerator', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }); diff --git a/code/lib/core-server/src/utils/StoryIndexGenerator.ts b/code/lib/core-server/src/utils/StoryIndexGenerator.ts index 34838d1e2eb7..ca8149656992 100644 --- a/code/lib/core-server/src/utils/StoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/StoryIndexGenerator.ts @@ -592,7 +592,7 @@ export class StoryIndexGenerator { ); this.lastIndex = { - v: 4, + v: 5, entries: sorted, }; diff --git a/code/lib/core-server/src/utils/stories-json.test.ts b/code/lib/core-server/src/utils/stories-json.test.ts index 21a901ada040..0a7a2f4a3b5c 100644 --- a/code/lib/core-server/src/utils/stories-json.test.ts +++ b/code/lib/core-server/src/utils/stories-json.test.ts @@ -263,7 +263,7 @@ describe('useStoriesJson', () => { "type": "story", }, }, - "v": 4, + "v": 5, } `); }, 20_000); diff --git a/code/lib/core-server/src/utils/summarizeIndex.test.ts b/code/lib/core-server/src/utils/summarizeIndex.test.ts index a2702a38a704..8d71317f3918 100644 --- a/code/lib/core-server/src/utils/summarizeIndex.test.ts +++ b/code/lib/core-server/src/utils/summarizeIndex.test.ts @@ -38,7 +38,7 @@ describe('summarizeIndex', () => { it('example stories', () => { expect( summarizeIndex({ - v: 4, + v: 5, entries: { 'example-introduction--docs': { id: 'example-introduction--docs', @@ -146,14 +146,14 @@ describe('summarizeIndex', () => { "playStoryCount": 0, "storiesMdxCount": 0, "storyCount": 0, - "version": 4, + "version": 5, } `); }); it('onboarding stories', () => { expect( summarizeIndex({ - v: 4, + v: 5, entries: { 'example-introduction--docs': { id: 'example-introduction--docs', @@ -204,14 +204,14 @@ describe('summarizeIndex', () => { "playStoryCount": 0, "storiesMdxCount": 0, "storyCount": 0, - "version": 4, + "version": 5, } `); }); it('user stories', () => { expect( summarizeIndex({ - v: 4, + v: 5, entries: { 'stories-renderers-react-errors--story-contains-unrenderable': { id: 'stories-renderers-react-errors--story-contains-unrenderable', @@ -260,14 +260,14 @@ describe('summarizeIndex', () => { "playStoryCount": 0, "storiesMdxCount": 0, "storyCount": 4, - "version": 4, + "version": 5, } `); }); it('pages', () => { expect( summarizeIndex({ - v: 4, + v: 5, entries: { 'example-page--logged-out': { id: 'example-page--logged-out', @@ -317,14 +317,14 @@ describe('summarizeIndex', () => { "playStoryCount": 1, "storiesMdxCount": 0, "storyCount": 1, - "version": 4, + "version": 5, } `); }); it('storiesMdx', () => { expect( summarizeIndex({ - v: 4, + v: 5, entries: { 'stories-renderers-react-react-mdx--docs': { id: 'stories-renderers-react-react-mdx--docs', @@ -374,14 +374,14 @@ describe('summarizeIndex', () => { "playStoryCount": 0, "storiesMdxCount": 1, "storyCount": 3, - "version": 4, + "version": 5, } `); }); it('autodocs', () => { expect( summarizeIndex({ - v: 4, + v: 5, entries: { 'example-button--docs': { id: 'example-button--docs', @@ -432,14 +432,14 @@ describe('summarizeIndex', () => { "playStoryCount": 0, "storiesMdxCount": 0, "storyCount": 0, - "version": 4, + "version": 5, } `); }); it('mdx', () => { expect( summarizeIndex({ - v: 4, + v: 5, entries: { 'example-introduction--docs': { id: 'example-introduction--docs', @@ -483,7 +483,7 @@ describe('summarizeIndex', () => { "playStoryCount": 0, "storiesMdxCount": 0, "storyCount": 0, - "version": 4, + "version": 5, } `); }); diff --git a/code/lib/manager-api/src/lib/stories.test.ts b/code/lib/manager-api/src/lib/stories.test.ts index fcce4a94d874..f8a7c5882cfb 100644 --- a/code/lib/manager-api/src/lib/stories.test.ts +++ b/code/lib/manager-api/src/lib/stories.test.ts @@ -1,6 +1,11 @@ import { describe, it, expect } from 'vitest'; -import type { StoryIndexV2, StoryIndexV3 } from '@storybook/types'; -import { transformStoryIndexV2toV3, transformStoryIndexV3toV4 } from './stories'; +import type { StoryIndexV2, StoryIndexV3, API_PreparedStoryIndex } from '@storybook/types'; +import { + transformStoryIndexV2toV3, + transformStoryIndexV3toV4, + transformStoryIndexV4toV5, +} from './stories'; +import { mockEntries } from '../tests/mockStoriesEntries'; const baseV2: StoryIndexV2['stories'][0] = { id: '1', @@ -151,3 +156,61 @@ describe('transformStoryIndexV3toV4', () => { `); }); }); + +describe('transformStoryIndexV4toV5', () => { + it('transforms a StoryIndexV4 to an API_PreparedStoryIndex correctly', () => { + const indexV4: API_PreparedStoryIndex = { + v: 4, + entries: mockEntries, + }; + + expect(transformStoryIndexV4toV5(indexV4)).toMatchInlineSnapshot(` + { + "entries": { + "component-a--docs": { + "id": "component-a--docs", + "importPath": "./path/to/component-a.ts", + "name": "Docs", + "storiesImports": [], + "tags": [ + "dev", + ], + "title": "Component A", + "type": "docs", + }, + "component-a--story-1": { + "id": "component-a--story-1", + "importPath": "./path/to/component-a.ts", + "name": "Story 1", + "tags": [ + "dev", + ], + "title": "Component A", + "type": "story", + }, + "component-a--story-2": { + "id": "component-a--story-2", + "importPath": "./path/to/component-a.ts", + "name": "Story 2", + "tags": [ + "dev", + ], + "title": "Component A", + "type": "story", + }, + "component-b--story-3": { + "id": "component-b--story-3", + "importPath": "./path/to/component-b.ts", + "name": "Story 3", + "tags": [ + "dev", + ], + "title": "Component B", + "type": "story", + }, + }, + "v": 5, + } + `); + }); +}); diff --git a/code/lib/manager-api/src/lib/stories.ts b/code/lib/manager-api/src/lib/stories.ts index 9d6b1817e677..b409754bb106 100644 --- a/code/lib/manager-api/src/lib/stories.ts +++ b/code/lib/manager-api/src/lib/stories.ts @@ -86,7 +86,7 @@ export const transformSetStoriesStoryDataToPreparedStoryIndex = ( {} as API_PreparedStoryIndex['entries'] ); - return { v: 4, entries }; + return { v: 5, entries }; }; export const transformStoryIndexV2toV3 = (index: StoryIndexV2): StoryIndexV3 => { @@ -139,6 +139,30 @@ export const transformStoryIndexV3toV4 = (index: StoryIndexV3): API_PreparedStor }; }; +/** + * Storybook 8.0 and below did not automatically tag stories with 'dev'. + * Therefore Storybook 8.1 and above would not show composed 8.0 stories by default. + * This function adds the 'dev' tag to all stories in the index to workaround this issue. + */ +export const transformStoryIndexV4toV5 = ( + index: API_PreparedStoryIndex +): API_PreparedStoryIndex => { + return { + v: 5, + entries: Object.values(index.entries).reduce( + (acc, entry) => { + acc[entry.id] = { + ...entry, + tags: entry.tags ? [...entry.tags, 'dev'] : ['dev'], + }; + + return acc; + }, + {} as API_PreparedStoryIndex['entries'] + ), + }; +}; + type ToStoriesHashOptions = { provider: API_Provider; docsOptions: DocsOptions; @@ -157,6 +181,7 @@ export const transformStoryIndexToStoriesHash = ( let index = input; index = index.v === 2 ? transformStoryIndexV2toV3(index as any) : index; index = index.v === 3 ? transformStoryIndexV3toV4(index as any) : index; + index = index.v === 4 ? transformStoryIndexV4toV5(index as any) : index; index = index as API_PreparedStoryIndex; const entryValues = Object.values(index.entries).filter((entry: any) => { diff --git a/code/lib/manager-api/src/tests/refs.test.ts b/code/lib/manager-api/src/tests/refs.test.ts index 950b5e3e7a63..022573616835 100644 --- a/code/lib/manager-api/src/tests/refs.test.ts +++ b/code/lib/manager-api/src/tests/refs.test.ts @@ -448,7 +448,7 @@ describe('Refs API', () => { setupResponses({ indexPrivate: { ok: true, - response: async () => ({ v: 4, entries: {} }), + response: async () => ({ v: 5, entries: {} }), }, storiesPrivate: { ok: true, @@ -506,7 +506,7 @@ describe('Refs API', () => { "index": {}, "internal_index": { "entries": {}, - "v": 4, + "v": 5, }, "title": "Fake", "type": "lazy", @@ -524,7 +524,7 @@ describe('Refs API', () => { "index": {}, "internal_index": { "entries": {}, - "v": 4, + "v": 5, }, "title": "Fake", "type": "lazy", @@ -542,7 +542,7 @@ describe('Refs API', () => { setupResponses({ indexPrivate: { ok: true, - response: async () => ({ v: 4, entries: {} }), + response: async () => ({ v: 5, entries: {} }), }, storiesPrivate: { ok: true, @@ -603,7 +603,7 @@ describe('Refs API', () => { "index": {}, "internal_index": { "entries": {}, - "v": 4, + "v": 5, }, "title": "Fake", "type": "lazy", @@ -622,7 +622,7 @@ describe('Refs API', () => { setupResponses({ indexPrivate: { ok: true, - response: async () => ({ v: 4, entries: {} }), + response: async () => ({ v: 5, entries: {} }), }, storiesPrivate: { ok: true, @@ -684,7 +684,7 @@ describe('Refs API', () => { "index": {}, "internal_index": { "entries": {}, - "v": 4, + "v": 5, }, "title": "Fake", "type": "lazy", @@ -781,7 +781,7 @@ describe('Refs API', () => { setupResponses({ indexPrivate: { ok: true, - response: async () => ({ v: 4, entries: {} }), + response: async () => ({ v: 5, entries: {} }), }, storiesPrivate: { ok: true, @@ -927,7 +927,7 @@ describe('Refs API', () => { setupResponses({ indexPublic: { ok: true, - response: async () => ({ v: 4, entries: {} }), + response: async () => ({ v: 5, entries: {} }), }, storiesPublic: { ok: true, @@ -989,7 +989,7 @@ describe('Refs API', () => { "index": {}, "internal_index": { "entries": {}, - "v": 4, + "v": 5, }, "title": "Fake", "type": "lazy", @@ -1008,7 +1008,7 @@ describe('Refs API', () => { setupResponses({ indexPrivate: { ok: true, - response: async () => ({ v: 4, entries: {} }), + response: async () => ({ v: 5, entries: {} }), }, storiesPrivate: { ok: true, @@ -1070,7 +1070,7 @@ describe('Refs API', () => { "index": {}, "internal_index": { "entries": {}, - "v": 4, + "v": 5, }, "title": "Fake", "type": "lazy", @@ -1206,7 +1206,7 @@ describe('Refs API', () => { describe('setRef', () => { it('can filter', async () => { const index: StoryIndex = { - v: 4, + v: 5, entries: { 'a--1': { id: 'a--1', diff --git a/code/lib/manager-api/src/tests/stories.test.ts b/code/lib/manager-api/src/tests/stories.test.ts index 9adec207d158..7fe6102d0fd4 100644 --- a/code/lib/manager-api/src/tests/stories.test.ts +++ b/code/lib/manager-api/src/tests/stories.test.ts @@ -38,7 +38,7 @@ vi.mock('../lib/events', () => ({ vi.mock('@storybook/global', () => ({ global: { ...globalThis, - fetch: vi.fn(() => ({ json: () => ({ v: 4, entries: mockGetEntries() }) })), + fetch: vi.fn(() => ({ json: () => ({ v: 5, entries: mockGetEntries() }) })), CONFIG_TYPE: 'DEVELOPMENT', }, })); @@ -97,7 +97,7 @@ describe('stories API', () => { const moduleArgs = createMockModuleArgs({}); const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - api.setIndex({ v: 4, entries: mockEntries }); + api.setIndex({ v: 5, entries: mockEntries }); const { index } = store.getState(); // We need exact key ordering, even if in theory JS doesn't guarantee it expect(Object.keys(index!)).toEqual([ @@ -139,7 +139,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; api.setIndex({ - v: 4, + v: 5, entries: { 'design-system-some-component--my-story': { type: 'story', @@ -176,7 +176,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; api.setIndex({ - v: 4, + v: 5, entries: { 'root-first--story-1': { type: 'story', @@ -213,7 +213,7 @@ describe('stories API', () => { const { store, provider } = moduleArgs; provider.getConfig.mockReturnValue({ sidebar: { showRoots: true } }); api.setIndex({ - v: 4, + v: 5, entries: { 'a-b--1': { type: 'story', @@ -252,7 +252,7 @@ describe('stories API', () => { const { store, provider } = moduleArgs; provider.getConfig.mockReturnValue({ sidebar: { showRoots: true } }); api.setIndex({ - v: 4, + v: 5, entries: { 'a--1': { type: 'story', @@ -287,7 +287,7 @@ describe('stories API', () => { const { store, provider } = moduleArgs; provider.getConfig.mockReturnValue({ sidebar: { showRoots: true } }); api.setIndex({ - v: 4, + v: 5, entries: { 'a--1': { type: 'story', title: 'a', name: '1', id: 'a--1', importPath: './a.ts' }, 'b--1': { type: 'story', title: 'b', name: '1', id: 'b--1', importPath: './b.ts' }, @@ -314,7 +314,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; api.setIndex({ - v: 4, + v: 5, entries: { 'prepared--story': { type: 'story', @@ -344,7 +344,7 @@ describe('stories API', () => { const moduleArgs = createMockModuleArgs({ fullAPI }); const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store, provider } = moduleArgs; - api.setIndex({ v: 4, entries: mockEntries }); + api.setIndex({ v: 5, entries: mockEntries }); provider.channel.emit(STORY_PREPARED, { id: 'component-a--story-1', parameters: { a: 'b' }, @@ -357,7 +357,7 @@ describe('stories API', () => { parameters: { a: 'b' }, args: { c: 'd' }, }); - api.setIndex({ v: 4, entries: mockEntries }); + api.setIndex({ v: 5, entries: mockEntries }); // Let the promise/await chain resolve await new Promise((r) => setTimeout(r, 0)); expect(store.getState().index!['component-a--story-1'] as API_StoryEntry).toMatchObject({ @@ -373,7 +373,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - api.setIndex({ v: 4, entries: docsEntries }); + api.setIndex({ v: 5, entries: docsEntries }); const { index } = store.getState(); // We need exact key ordering, even if in theory JS doesn't guarantee it expect(Object.keys(index!)).toEqual([ @@ -398,7 +398,7 @@ describe('stories API', () => { docsOptions: { docsMode: true }, }); const { store } = moduleArgs; - api.setIndex({ v: 4, entries: docsEntries }); + api.setIndex({ v: 5, entries: docsEntries }); const { index } = store.getState(); expect(Object.keys(index!)).toEqual(['component-b', 'component-b--docs']); }); @@ -413,7 +413,7 @@ describe('stories API', () => { initStories(moduleArgs as unknown as ModuleArgs); const { store, provider } = moduleArgs; - provider.channel.emit(SET_INDEX, { v: 4, entries: mockEntries }); + provider.channel.emit(SET_INDEX, { v: 5, entries: mockEntries }); expect(store.getState().index).toEqual( expect.objectContaining({ 'component-a': expect.any(Object), @@ -433,7 +433,7 @@ describe('stories API', () => { getCurrentParameter: vi.fn().mockReturnValue('options'), }); - provider.channel.emit(SET_INDEX, { v: 4, entries: mockEntries }); + provider.channel.emit(SET_INDEX, { v: 5, entries: mockEntries }); expect(fullAPI.setOptions).toHaveBeenCalledWith('options'); }); }); @@ -461,7 +461,7 @@ describe('stories API', () => { status: 200, ok: true, json: () => ({ - v: 4, + v: 5, entries: { 'component-a--story-1': { type: 'story', @@ -512,7 +512,7 @@ describe('stories API', () => { status: 200, ok: true, json: () => ({ - v: 4, + v: 5, entries: { 'component-a--story-1': { type: 'story', @@ -616,7 +616,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { provider, store } = moduleArgs; - api.setIndex({ v: 4, entries: preparedEntries }); + api.setIndex({ v: 5, entries: preparedEntries }); const { index } = store.getState(); expect((index!['a--1'] as API_StoryEntry).args).toEqual({ a: 'b' }); expect((index!['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); @@ -653,7 +653,7 @@ describe('stories API', () => { const listener = vi.fn(); provider.channel.on(UPDATE_STORY_ARGS, listener); - api.setIndex({ v: 4, entries: preparedEntries }); + api.setIndex({ v: 5, entries: preparedEntries }); api.updateStoryArgs({ id: 'a--1' } as API_StoryEntry, { foo: 'bar' }); expect(listener).toHaveBeenCalledWith({ @@ -677,7 +677,7 @@ describe('stories API', () => { const listener = vi.fn(); provider.channel.on(UPDATE_STORY_ARGS, listener); - api.setIndex({ v: 4, entries: preparedEntries }); + api.setIndex({ v: 5, entries: preparedEntries }); api.updateStoryArgs({ id: 'a--1', refId: 'refId' } as API_StoryEntry, { foo: 'bar' }); expect(listener).toHaveBeenCalledWith({ storyId: 'a--1', @@ -695,7 +695,7 @@ describe('stories API', () => { const listener = vi.fn(); provider.channel.on(RESET_STORY_ARGS, listener); - api.setIndex({ v: 4, entries: preparedEntries }); + api.setIndex({ v: 5, entries: preparedEntries }); api.resetStoryArgs({ id: 'a--1' } as API_StoryEntry, ['foo']); expect(listener).toHaveBeenCalledWith({ @@ -719,7 +719,7 @@ describe('stories API', () => { const listener = vi.fn(); provider.channel.on(RESET_STORY_ARGS, listener); - api.setIndex({ v: 4, entries: preparedEntries }); + api.setIndex({ v: 5, entries: preparedEntries }); api.resetStoryArgs({ id: 'a--1', refId: 'refId' } as API_StoryEntry, ['foo']); expect(listener).toHaveBeenCalledWith({ storyId: 'a--1', @@ -738,7 +738,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.jumpToStory(1); expect(navigate).toHaveBeenCalledWith('/story/a--2'); @@ -749,7 +749,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.jumpToStory(-1); expect(navigate).toHaveBeenCalledWith('/story/a--1'); @@ -764,7 +764,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.jumpToStory(1); expect(navigate).not.toHaveBeenCalled(); }); @@ -774,7 +774,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.jumpToStory(-1); expect(navigate).not.toHaveBeenCalled(); }); @@ -784,7 +784,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.jumpToStory(1); expect(navigate).not.toHaveBeenCalled(); }); @@ -797,7 +797,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); const result = api.findSiblingStoryId('a--1', store.getState().index!, 1, false); expect(result).toBe('a--2'); }); @@ -807,7 +807,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); const result = api.findSiblingStoryId('a--1', store.getState().index!, 1, true); expect(result).toBe('b-c--1'); }); @@ -819,7 +819,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.jumpToComponent(1); expect(navigate).toHaveBeenCalledWith('/story/b-c--1'); }); @@ -833,7 +833,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.jumpToComponent(-1); expect(navigate).toHaveBeenCalledWith('/story/a--1'); }); @@ -847,7 +847,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.jumpToComponent(1); expect(navigate).not.toHaveBeenCalled(); }); @@ -857,7 +857,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.jumpToComponent(-1); expect(navigate).not.toHaveBeenCalled(); }); @@ -869,7 +869,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory('a--2'); expect(navigate).toHaveBeenCalledWith('/story/a--2'); }); @@ -880,7 +880,7 @@ describe('stories API', () => { const { navigate } = moduleArgs; api.setIndex({ - v: 4, + v: 5, entries: { ...navigationEntries, 'intro--docs': { @@ -902,7 +902,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory('a--1'); expect(store.getState().settings.lastTrackedStoryId).toBe('a--1'); }); @@ -913,7 +913,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory('a', '2'); expect(navigate).toHaveBeenCalledWith('/story/a--2'); }); @@ -923,7 +923,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory(undefined, '2'); expect(navigate).toHaveBeenCalledWith('/story/a--2'); }); @@ -934,7 +934,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory('a--2'); expect(navigate).toHaveBeenCalledWith('/story/a--2'); }); @@ -944,7 +944,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory('a'); expect(navigate).toHaveBeenCalledWith('/story/a--1'); }); @@ -954,7 +954,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory('b'); expect(navigate).toHaveBeenCalledWith('/story/b-c--1'); }); @@ -964,7 +964,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory('A'); expect(navigate).toHaveBeenCalledWith('/story/a--1'); }); @@ -974,7 +974,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory(); expect(navigate).toHaveBeenCalledWith('/story/a--1'); }); @@ -985,7 +985,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory('b/e', '1'); expect(navigate).toHaveBeenCalledWith('/story/custom-id--1'); }); @@ -995,7 +995,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory('custom-id', '1'); expect(navigate).toHaveBeenCalledWith('/story/custom-id--1'); }); @@ -1005,7 +1005,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { navigate } = moduleArgs; - api.setIndex({ v: 4, entries: navigationEntries }); + api.setIndex({ v: 5, entries: navigationEntries }); api.selectStory('b/e'); expect(navigate).toHaveBeenCalledWith('/story/custom-id--1'); }); @@ -1019,7 +1019,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { provider, store } = moduleArgs; - api.setIndex({ v: 4, entries: mockEntries }); + api.setIndex({ v: 5, entries: mockEntries }); provider.channel.emit(STORY_PREPARED, { id: 'component-a--story-1', @@ -1045,7 +1045,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { provider } = moduleArgs; - api.setIndex({ v: 4, entries: mockEntries }); + api.setIndex({ v: 5, entries: mockEntries }); provider.channel.emit(STORY_PREPARED, { id: 'component-a--story-1', @@ -1068,7 +1068,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { provider, store } = moduleArgs; - api.setIndex({ v: 4, entries: mockEntries }); + api.setIndex({ v: 5, entries: mockEntries }); provider.channel.emit(DOCS_PREPARED, { id: 'component-a--docs', @@ -1092,7 +1092,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { provider, store } = moduleArgs; - api.setIndex({ v: 4, entries: mockEntries }); + api.setIndex({ v: 5, entries: mockEntries }); provider.channel.emit(CONFIG_ERROR, { message: 'Failed to run configure' }); const { previewInitialized } = store.getState(); @@ -1104,7 +1104,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { provider } = moduleArgs; - api.setIndex({ v: 4, entries: mockEntries }); + api.setIndex({ v: 5, entries: mockEntries }); getEventMetadata.mockReturnValueOnce({ sourceType: 'external', @@ -1225,7 +1225,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - await api.setIndex({ v: 4, entries: mockEntries }); + await api.setIndex({ v: 5, entries: mockEntries }); await expect( api.experimental_updateStatus('a-addon-id', { @@ -1279,7 +1279,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - await api.setIndex({ v: 4, entries: mockEntries }); + await api.setIndex({ v: 5, entries: mockEntries }); await expect( api.experimental_updateStatus('a-addon-id', { @@ -1315,7 +1315,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - await api.setIndex({ v: 4, entries: mockEntries }); + await api.setIndex({ v: 5, entries: mockEntries }); await expect( api.experimental_updateStatus('a-addon-id', { @@ -1353,7 +1353,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - await api.setIndex({ v: 4, entries: mockEntries }); + await api.setIndex({ v: 5, entries: mockEntries }); // setup initial state await expect( @@ -1403,7 +1403,7 @@ describe('stories API', () => { const moduleArgs = createMockModuleArgs({}); const { state, api } = initStories(moduleArgs as unknown as ModuleArgs); - await api.setIndex({ v: 4, entries: mockEntries }); + await api.setIndex({ v: 5, entries: mockEntries }); expect(state).toEqual( expect.objectContaining({ @@ -1416,7 +1416,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - await api.setIndex({ v: 4, entries: mockEntries }); + await api.setIndex({ v: 5, entries: mockEntries }); api.experimental_setFilter('myCustomFilter', () => true); @@ -1434,7 +1434,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - await api.setIndex({ v: 4, entries: navigationEntries }); + await api.setIndex({ v: 5, entries: navigationEntries }); await api.experimental_setFilter('myCustomFilter', (item: any) => item.id.startsWith('a')); const { index } = store.getState(); @@ -1484,7 +1484,7 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - await api.setIndex({ v: 4, entries: navigationEntries }); + await api.setIndex({ v: 5, entries: navigationEntries }); await api.experimental_setFilter( 'myCustomFilter', (item: any) => @@ -1538,10 +1538,10 @@ describe('stories API', () => { const { api } = initStories(moduleArgs as unknown as ModuleArgs); const { store } = moduleArgs; - await api.setIndex({ v: 4, entries: navigationEntries }); + await api.setIndex({ v: 5, entries: navigationEntries }); await api.experimental_setFilter('myCustomFilter', (item: any) => item.id.startsWith('a')); - await api.setIndex({ v: 4, entries: navigationEntries }); + await api.setIndex({ v: 5, entries: navigationEntries }); const { index } = store.getState(); diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.mockdata.ts b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.mockdata.ts index 88dae40068dc..d2a7f884730d 100644 --- a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.mockdata.ts +++ b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.mockdata.ts @@ -76,7 +76,7 @@ export const projectAnnotations = { export const getProjectAnnotations = vi.fn(() => projectAnnotations as any); export const storyIndex: StoryIndex = { - v: 4, + v: 5, entries: { 'component-one--docs': { type: 'docs', diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts index 0ba45568789d..60110bc4686b 100644 --- a/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts +++ b/code/lib/preview-api/src/modules/preview-web/PreviewWeb.test.ts @@ -314,7 +314,7 @@ describe('PreviewWeb', () => { preview.onStoriesChanged({ importFn: newImportFn, storyIndex: { - v: 4, + v: 5, entries: { ...storyIndex.entries, 'component-one--missing': { @@ -369,7 +369,7 @@ describe('PreviewWeb', () => { preview.onStoriesChanged({ importFn: newImportFn, storyIndex: { - v: 4, + v: 5, entries: { ...storyIndex.entries, 'component-one--missing': { @@ -2977,7 +2977,7 @@ describe('PreviewWeb', () => { const newImportFn = vi.fn(async (path) => ({ ...componentOneExports })); const newStoryIndex = { - v: 4, + v: 5, entries: { ...storyIndex.entries, 'component-one--a': { @@ -3174,7 +3174,7 @@ describe('PreviewWeb', () => { }); const newStoryIndex = { - v: 4, + v: 5, entries: { 'component-one--b': storyIndex.entries['component-one--b'], }, diff --git a/code/lib/preview-api/src/modules/store/StoryIndexStore.test.ts b/code/lib/preview-api/src/modules/store/StoryIndexStore.test.ts index f9ec0b6102a3..84c80b5041f2 100644 --- a/code/lib/preview-api/src/modules/store/StoryIndexStore.test.ts +++ b/code/lib/preview-api/src/modules/store/StoryIndexStore.test.ts @@ -6,7 +6,7 @@ import { StoryIndexStore } from './StoryIndexStore'; vi.mock('@storybook/channel-websocket', () => () => ({ on: vi.fn() })); const storyIndex: StoryIndex = { - v: 4, + v: 5, entries: { 'component-one--a': { type: 'story', @@ -34,7 +34,7 @@ const storyIndex: StoryIndex = { const makeStoryIndex = (titlesAndNames: any) => { return { - v: 4, + v: 5, entries: Object.fromEntries( titlesAndNames.map(([title, name]: [any, any]) => { const id = `${title}--${name}`.replace('/', '-'); diff --git a/code/lib/preview-api/src/modules/store/StoryIndexStore.ts b/code/lib/preview-api/src/modules/store/StoryIndexStore.ts index 803e96553586..b56074778f6b 100644 --- a/code/lib/preview-api/src/modules/store/StoryIndexStore.ts +++ b/code/lib/preview-api/src/modules/store/StoryIndexStore.ts @@ -24,7 +24,7 @@ const getImportPathMap = memoize(1)((entries: StoryIndex['entries']) => export class StoryIndexStore { entries: StoryIndex['entries']; - constructor({ entries }: StoryIndex = { v: 4, entries: {} }) { + constructor({ entries }: StoryIndex = { v: 5, entries: {} }) { this.entries = entries; } diff --git a/code/lib/preview-api/src/modules/store/StoryStore.test.ts b/code/lib/preview-api/src/modules/store/StoryStore.test.ts index 5df413e09ceb..51d7664b468d 100644 --- a/code/lib/preview-api/src/modules/store/StoryStore.test.ts +++ b/code/lib/preview-api/src/modules/store/StoryStore.test.ts @@ -49,7 +49,7 @@ const projectAnnotations: ProjectAnnotations = { }; const storyIndex: StoryIndex = { - v: 4, + v: 5, entries: { 'component-one--a': { type: 'story', @@ -183,7 +183,7 @@ describe('StoryStore', () => { expect(prepareStory).toHaveBeenCalledTimes(1); // The stories are no longer in the index - await store.onStoriesChanged({ storyIndex: { v: 4, entries: {} } }); + await store.onStoriesChanged({ storyIndex: { v: 5, entries: {} } }); await expect(store.loadStory({ storyId: 'component-one--a' })).rejects.toThrow(); @@ -202,7 +202,7 @@ describe('StoryStore', () => { // Add a new story to the index that isn't different await store.onStoriesChanged({ storyIndex: { - v: 4, + v: 5, entries: { ...storyIndex.entries, 'new-component--story': { @@ -233,7 +233,7 @@ describe('StoryStore', () => { await store.onStoriesChanged({ importFn: newImportFn, storyIndex: { - v: 4, + v: 5, entries: { 'component-one--a': { type: 'story', @@ -262,7 +262,7 @@ describe('StoryStore', () => { await store.onStoriesChanged({ importFn: newImportFn, storyIndex: { - v: 4, + v: 5, entries: { 'component-one--a': { type: 'story', @@ -334,7 +334,7 @@ describe('StoryStore', () => { it('returns them in the order they are in the index, not the file', async () => { const reversedIndex = { - v: 4, + v: 5, entries: { 'component-one--b': storyIndex.entries['component-one--b'], 'component-one--a': storyIndex.entries['component-one--a'], @@ -587,7 +587,7 @@ describe('StoryStore', () => { it('does not include (modern) docs entries ever', async () => { const unnattachedStoryIndex: StoryIndex = { - v: 4, + v: 5, entries: { ...storyIndex.entries, 'introduction--docs': { diff --git a/code/ui/blocks/src/blocks/external/ExternalPreview.ts b/code/ui/blocks/src/blocks/external/ExternalPreview.ts index bb6fc24764f2..203ac14ee3a0 100644 --- a/code/ui/blocks/src/blocks/external/ExternalPreview.ts +++ b/code/ui/blocks/src/blocks/external/ExternalPreview.ts @@ -31,7 +31,7 @@ export class ExternalPreview extends Prev private titles = new ConstantMap('title-'); - private storyIndex: StoryIndex = { v: 4, entries: {} }; + private storyIndex: StoryIndex = { v: 5, entries: {} }; private moduleExportsByImportPath: Record = {}; diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index bee4513a4326..d4042b130b42 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -282,6 +282,23 @@ function addStoriesEntry(mainConfig: ConfigFile, path: string, disableDocs: bool mainConfig.setFieldValue(['stories'], [...stories, entry]); } +// Add refs to older versions of storybook to test out composition +function addRefs(mainConfig: ConfigFile) { + const refs = mainConfig.getFieldValue(['refs']) as Record; + + mainConfig.setFieldValue(['refs'], { + ...refs, + 'storybook@8.0.0': { + title: 'Storybook 8.0.0', + url: 'https://635781f3500dd2c49e189caf-gckybvsekn.chromatic.com/', + }, + 'storybook@7.6.18': { + title: 'Storybook 7.6.18', + url: 'https://635781f3500dd2c49e189caf-oljwjdrftz.chromatic.com/', + }, + } as Record); +} + function getStoriesFolderWithVariant(variant?: string, folder = 'stories') { return variant ? `${folder}_${variant}` : folder; } @@ -516,9 +533,14 @@ export const addStories: Task['run'] = async ( await writeConfig(mainConfig); }; -export const extendMain: Task['run'] = async ({ template, sandboxDir }, { disableDocs }) => { +export const extendMain: Task['run'] = async ({ template, sandboxDir, key }, { disableDocs }) => { logger.log('📝 Extending main.js'); const mainConfig = await readMainConfig({ cwd: sandboxDir }); + + if (key === 'react-vite/default-ts') { + addRefs(mainConfig); + } + const templateConfig = template.modifications?.mainConfig || {}; const configToAdd = { ...templateConfig,