Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Story index: Ensure that extract script works and SBs can be composed into v6 storybooks #18409

Merged
merged 3 commits into from
Jun 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 170 additions & 1 deletion lib/core-server/src/utils/stories-json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import Watchpack from 'watchpack';
import path from 'path';
import debounce from 'lodash/debounce';
import Events from '@storybook/core-events';
import type { StoryIndex } from '@storybook/store';

import { useStoriesJson, DEBOUNCE } from './stories-json';
import { useStoriesJson, DEBOUNCE, convertToIndexV3 } from './stories-json';
import { ServerChannel } from './get-server-channel';
import { StoryIndexGenerator } from './StoryIndexGenerator';

Expand Down Expand Up @@ -192,40 +193,126 @@ describe('useStoriesJson', () => {
expect(JSON.parse(send.mock.calls[0][0])).toMatchInlineSnapshot(`
Object {
"stories": Object {
"a--docs": Object {
"id": "a--docs",
"importPath": "./src/docs2/MetaOf.docs.mdx",
"kind": "A",
"name": "docs",
"parameters": Object {
"__id": "a--docs",
"docsOnly": true,
"fileName": "./src/docs2/MetaOf.docs.mdx",
},
"storiesImports": Array [
"./src/A.stories.js",
],
"story": "docs",
"title": "A",
},
"a--story-one": Object {
"id": "a--story-one",
"importPath": "./src/A.stories.js",
"kind": "A",
"name": "Story One",
"parameters": Object {
"__id": "a--story-one",
"docsOnly": false,
"fileName": "./src/A.stories.js",
},
"story": "Story One",
"title": "A",
},
"b--story-one": Object {
"id": "b--story-one",
"importPath": "./src/B.stories.ts",
"kind": "B",
"name": "Story One",
"parameters": Object {
"__id": "b--story-one",
"docsOnly": false,
"fileName": "./src/B.stories.ts",
},
"story": "Story One",
"title": "B",
},
"d--story-one": Object {
"id": "d--story-one",
"importPath": "./src/D.stories.jsx",
"kind": "D",
"name": "Story One",
"parameters": Object {
"__id": "d--story-one",
"docsOnly": false,
"fileName": "./src/D.stories.jsx",
},
"story": "Story One",
"title": "D",
},
"docs2-notitle--docs": Object {
"id": "docs2-notitle--docs",
"importPath": "./src/docs2/NoTitle.docs.mdx",
"kind": "docs2/NoTitle",
"name": "docs",
"parameters": Object {
"__id": "docs2-notitle--docs",
"docsOnly": true,
"fileName": "./src/docs2/NoTitle.docs.mdx",
},
"storiesImports": Array [],
"story": "docs",
"title": "docs2/NoTitle",
},
"docs2-yabbadabbadooo--docs": Object {
"id": "docs2-yabbadabbadooo--docs",
"importPath": "./src/docs2/Title.docs.mdx",
"kind": "docs2/Yabbadabbadooo",
"name": "docs",
"parameters": Object {
"__id": "docs2-yabbadabbadooo--docs",
"docsOnly": true,
"fileName": "./src/docs2/Title.docs.mdx",
},
"storiesImports": Array [],
"story": "docs",
"title": "docs2/Yabbadabbadooo",
},
"first-nested-deeply-f--story-one": Object {
"id": "first-nested-deeply-f--story-one",
"importPath": "./src/first-nested/deeply/F.stories.js",
"kind": "first-nested/deeply/F",
"name": "Story One",
"parameters": Object {
"__id": "first-nested-deeply-f--story-one",
"docsOnly": false,
"fileName": "./src/first-nested/deeply/F.stories.js",
},
"story": "Story One",
"title": "first-nested/deeply/F",
},
"nested-button--story-one": Object {
"id": "nested-button--story-one",
"importPath": "./src/nested/Button.stories.ts",
"kind": "nested/Button",
"name": "Story One",
"parameters": Object {
"__id": "nested-button--story-one",
"docsOnly": false,
"fileName": "./src/nested/Button.stories.ts",
},
"story": "Story One",
"title": "nested/Button",
},
"second-nested-g--story-one": Object {
"id": "second-nested-g--story-one",
"importPath": "./src/second-nested/G.stories.ts",
"kind": "second-nested/G",
"name": "Story One",
"parameters": Object {
"__id": "second-nested-g--story-one",
"docsOnly": false,
"fileName": "./src/second-nested/G.stories.ts",
},
"story": "Story One",
"title": "second-nested/G",
},
},
Expand Down Expand Up @@ -476,3 +563,85 @@ describe('useStoriesJson', () => {
});
});
});

describe('convertToIndexV3', () => {
it('converts v7 index.json to v6 stories.json', () => {
const indexJson: StoryIndex = {
v: 4,
entries: {
'a--docs': {
id: 'a--docs',
importPath: './src/docs2/MetaOf.docs.mdx',
name: 'docs',
storiesImports: ['./src/A.stories.js'],
title: 'A',
type: 'docs',
},
'a--story-one': {
id: 'a--story-one',
importPath: './src/A.stories.js',
name: 'Story One',
title: 'A',
type: 'story',
},
'b--story-one': {
id: 'b--story-one',
importPath: './src/B.stories.ts',
name: 'Story One',
title: 'B',
type: 'story',
},
},
};

expect(convertToIndexV3(indexJson)).toMatchInlineSnapshot(`
Object {
"stories": Object {
"a--docs": Object {
"id": "a--docs",
"importPath": "./src/docs2/MetaOf.docs.mdx",
"kind": "A",
"name": "docs",
"parameters": Object {
"__id": "a--docs",
"docsOnly": true,
"fileName": "./src/docs2/MetaOf.docs.mdx",
},
"storiesImports": Array [
"./src/A.stories.js",
],
"story": "docs",
"title": "A",
},
"a--story-one": Object {
"id": "a--story-one",
"importPath": "./src/A.stories.js",
"kind": "A",
"name": "Story One",
"parameters": Object {
"__id": "a--story-one",
"docsOnly": false,
"fileName": "./src/A.stories.js",
},
"story": "Story One",
"title": "A",
},
"b--story-one": Object {
"id": "b--story-one",
"importPath": "./src/B.stories.ts",
"kind": "B",
"name": "Story One",
"parameters": Object {
"__id": "b--story-one",
"docsOnly": false,
"fileName": "./src/B.stories.ts",
},
"story": "Story One",
"title": "B",
},
},
"v": 3,
}
`);
});
});
15 changes: 11 additions & 4 deletions lib/core-server/src/utils/stories-json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,17 @@ export function useStoriesJson({
export const convertToIndexV3 = (index: StoryIndex): StoryIndexV3 => {
const { entries } = index;
const stories = Object.entries(entries).reduce((acc, [id, entry]) => {
if (entry.type === 'story') {
const { type, ...rest } = entry;
acc[id] = rest;
}
const { type, ...rest } = entry;
acc[id] = {
...rest,
kind: rest.title,
story: rest.name,
parameters: {
__id: rest.id,
docsOnly: type === 'docs',
fileName: rest.importPath,
},
};
return acc;
}, {} as StoryIndexV3['stories']);
return {
Expand Down
35 changes: 0 additions & 35 deletions lib/store/src/StoryStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -986,41 +986,6 @@ describe('StoryStore', () => {
`);
});
});

describe('in non-back-compat mode', () => {
it('maps stories list to payload correctly', async () => {
const store = new StoryStore();
store.setProjectAnnotations(projectAnnotations);
store.initialize({ storyIndex, importFn, cache: false });
await store.cacheAllCSFFiles();

expect(store.getStoriesJsonData()).toMatchInlineSnapshot(`
Object {
"stories": Object {
"component-one--a": Object {
"id": "component-one--a",
"importPath": "./src/ComponentOne.stories.js",
"name": "A",
"title": "Component One",
},
"component-one--b": Object {
"id": "component-one--b",
"importPath": "./src/ComponentOne.stories.js",
"name": "B",
"title": "Component One",
},
"component-two--c": Object {
"id": "component-two--c",
"importPath": "./src/ComponentTwo.stories.js",
"name": "C",
"title": "Component Two",
},
},
"v": 3,
}
`);
});
});
});

describe('cacheAllCsfFiles', () => {
Expand Down
37 changes: 19 additions & 18 deletions lib/store/src/StoryStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,28 +303,29 @@ export class StoryStore<TFramework extends AnyFramework> {
};
}

// NOTE: this is legacy `stories.json` data for the `extract` script.
// It is used to allow v7 Storybooks to be composed in v6 Storybooks, which expect a
// `stories.json` file with legacy fields (`kind` etc).
getStoriesJsonData = (): StoryIndexV3 => {
const value = this.getSetStoriesPayload();
const allowedParameters = ['fileName', 'docsOnly', 'framework', '__id', '__isArgsStory'];

const stories: Record<StoryId, Omit<StoryIndexEntry, 'type'> | V2CompatIndexEntry> = mapValues(
value.stories,
(story) => {
const { importPath } = this.storyIndex.entries[story.id];
return {
...pick(story, ['id', 'name', 'title']),
importPath,
...(!global.FEATURES?.breakingChangesV7 && {
kind: story.title,
story: story.name,
parameters: {
...pick(story.parameters, allowedParameters),
fileName: importPath,
},
}),
};
}
);
const stories: Record<StoryId, V2CompatIndexEntry> = mapValues(value.stories, (story) => {
const { importPath } = this.storyIndex.entries[story.id];
return {
...pick(story, ['id', 'name', 'title']),
importPath,
// These 3 fields were going to be dropped in v7, but instead we will keep them for the
// 7.x cycle so that v7 Storybooks can be composed successfully in v6 Storybook.
// In v8 we will (likely) completely drop support for `extract` and `getStoriesJsonData`
kind: story.title,
story: story.name,
parameters: {
...pick(story.parameters, allowedParameters),
fileName: importPath,
},
};
});

return {
v: 3,
Expand Down
4 changes: 2 additions & 2 deletions lib/store/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,15 @@ export type DocsIndexEntry = BaseIndexEntry & {
};

export type IndexEntry = StoryIndexEntry | DocsIndexEntry;
export interface V2CompatIndexEntry extends StoryIndexEntry {
export interface V2CompatIndexEntry extends Omit<StoryIndexEntry, 'type'> {
kind: StoryIndexEntry['title'];
story: StoryIndexEntry['name'];
parameters: Parameters;
}

export interface StoryIndexV3 {
v: number;
stories: Record<StoryId, Omit<StoryIndexEntry, 'type'>>;
stories: Record<StoryId, V2CompatIndexEntry>;
}

export interface StoryIndex {
Expand Down