Skip to content

Commit

Permalink
Merge pull request #29672 from Sidnioulz/sidnioulz/group-entry-tags-i…
Browse files Browse the repository at this point in the history
…ntersection

Manager: Add tags property to GroupEntry objects
  • Loading branch information
shilman authored Nov 28, 2024
2 parents 3a0f922 + 8496e3e commit adb2594
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 8 deletions.
2 changes: 1 addition & 1 deletion code/core/src/manager-api/lib/intersect.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default <T>(a: T[], b: T[]): T[] => {
// no point in intersecting if one of the input is ill-defined
if (!a || !b) {
if (!Array.isArray(a) || !Array.isArray(b) || !a.length || !b.length) {
return [];
}

Expand Down
20 changes: 16 additions & 4 deletions code/core/src/manager-api/lib/stories.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {
API_BaseEntry,
API_ComponentEntry,
API_DocsEntry,
API_GroupEntry,
Expand All @@ -16,6 +17,7 @@ import type {
StoryId,
StoryIndexV2,
StoryIndexV3,
Tag,
} from '@storybook/core/types';
import { sanitize } from '@storybook/csf';

Expand Down Expand Up @@ -248,6 +250,7 @@ export const transformStoryIndexToStoriesHash = (
type: 'root',
id,
name: names[idx],
tags: [],
depth: idx,
renderLabel,
startCollapsed: collapsedRoots.includes(id),
Expand All @@ -267,21 +270,20 @@ export const transformStoryIndexToStoriesHash = (
type: 'component',
id,
name: names[idx],
tags: [],
parent: paths[idx - 1],
depth: idx,
renderLabel,
...(childId && {
children: [childId],
}),
});
// merge computes a union of arrays but we want an intersection on this
// specific array property, so it's easier to add it after the merge.
acc[id].tags = intersect(acc[id]?.tags ?? item.tags, item.tags);
} else {
acc[id] = merge<API_GroupEntry>((acc[id] || {}) as API_GroupEntry, {
type: 'group',
id,
name: names[idx],
tags: [],
parent: paths[idx - 1],
depth: idx,
renderLabel,
Expand All @@ -295,6 +297,7 @@ export const transformStoryIndexToStoriesHash = (
// Finally add an entry for the docs/story itself
acc[item.id] = {
type: 'story',
tags: [],
...item,
depth: paths.length,
parent: paths[paths.length - 1],
Expand All @@ -313,9 +316,18 @@ export const transformStoryIndexToStoriesHash = (
}

acc[item.id] = item;
// Ensure we add the children depth-first *before* inserting any other entries
// Ensure we add the children depth-first *before* inserting any other entries,
// and compute tags from the children put in the accumulator afterwards, once
// they're all known and we can compute a sound intersection.
if (item.type === 'root' || item.type === 'group' || item.type === 'component') {
item.children.forEach((childId: any) => addItem(acc, storiesHashOutOfOrder[childId]));

item.tags = item.children.reduce((currentTags: Tag[] | null, childId: any): Tag[] => {
const child = acc[childId];

// On the first child, we have nothing to intersect against so we use it as a source of data.
return currentTags === null ? child.tags : intersect(currentTags, child.tags);
}, null);
}
return acc;
}
Expand Down
80 changes: 80 additions & 0 deletions code/core/src/manager-api/tests/stories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ describe('stories API', () => {
expect(index!['design-system']).toMatchObject({
type: 'root',
name: 'Design System', // root name originates from `kind`, so it gets trimmed
tags: [],
});
expect(index!['design-system-some-component']).toMatchObject({
type: 'component',
Expand All @@ -186,6 +187,7 @@ describe('stories API', () => {
title: 'Root/First',
name: 'Story 1',
importPath: './path/to/root/first.ts',
tags: [],
},
...mockEntries,
},
Expand All @@ -207,6 +209,7 @@ describe('stories API', () => {
type: 'root',
id: 'root',
children: ['root-first'],
tags: [],
});
});
it('sets roots when showRoots = true', () => {
Expand All @@ -222,6 +225,7 @@ describe('stories API', () => {
id: 'a-b--1',
title: 'a/b',
name: '1',
tags: [],
importPath: './a/b.ts',
},
},
Expand All @@ -233,6 +237,7 @@ describe('stories API', () => {
type: 'root',
id: 'a',
children: ['a-b'],
tags: [],
});
expect(index!['a-b']).toMatchObject({
type: 'component',
Expand Down Expand Up @@ -332,6 +337,76 @@ describe('stories API', () => {
tags: ['shared', 'two-specific'],
});
});

it('intersects story/docs tags to compute tags for root and group entries', () => {
const moduleArgs = createMockModuleArgs({});
const { api } = initStories(moduleArgs as unknown as ModuleArgs);
const { store } = moduleArgs;
api.setIndex({
v: 5,
entries: {
'a-sampleone': {
type: 'story',
id: 'a-sampleone',
title: 'A/SampleOne',
name: '1',
tags: ['shared', 'one-specific'],
importPath: './a.ts',
},
'a-sampletwo': {
type: 'story',
id: 'a-sampletwo',
title: 'A/SampleTwo',
name: '2',
tags: ['shared', 'two-specific'],
importPath: './a.ts',
},
'a-embedded-othertopic': {
type: 'docs',
id: 'a-embedded-othertopic',
title: 'A/Embedded/OtherTopic',
name: '3',
tags: ['shared', 'embedded-docs-specific', 'other'],
storiesImports: [],
importPath: './embedded/other.mdx',
},
'a-embedded-extras': {
type: 'docs',
id: 'a-embedded-extras',
title: 'A/Embedded/Extras',
name: '3',
tags: ['shared', 'embedded-docs-specific', 'extras'],
storiesImports: [],
importPath: './embedded/extras.mdx',
},
},
});
const { index } = store.getState();
// We need exact key ordering, even if in theory JS doesn't guarantee it
expect(Object.keys(index!)).toEqual([
'a',
'a-sampleone',
'a-sampletwo',
'a-embedded',
'a-embedded-othertopic',
'a-embedded-extras',
]);
// Acts as the root, so that the next level is a group we're testing.
expect(index!.a).toMatchObject({
type: 'root',
id: 'a',
children: ['a-sampleone', 'a-sampletwo', 'a-embedded'],
tags: ['shared'],
});
// The object of this test.
expect(index!['a-embedded']).toMatchObject({
type: 'group',
id: 'a-embedded',
parent: 'a',
name: 'Embedded',
tags: ['shared', 'embedded-docs-specific'],
});
});
// Stories can get out of order for a few reasons -- see reproductions on
// https://github.com/storybookjs/storybook/issues/5518
it('does the right thing for out of order stories', async () => {
Expand Down Expand Up @@ -1515,6 +1590,7 @@ describe('stories API', () => {
"parent": "a",
"prepared": false,
"renderLabel": undefined,
"tags": [],
"title": "a",
"type": "story",
},
Expand All @@ -1526,6 +1602,7 @@ describe('stories API', () => {
"parent": "a",
"prepared": false,
"renderLabel": undefined,
"tags": [],
"title": "a",
"type": "story",
},
Expand Down Expand Up @@ -1581,6 +1658,7 @@ describe('stories API', () => {
"parent": "a",
"prepared": false,
"renderLabel": undefined,
"tags": [],
"title": "a",
"type": "story",
},
Expand Down Expand Up @@ -1623,6 +1701,7 @@ describe('stories API', () => {
"parent": "a",
"prepared": false,
"renderLabel": undefined,
"tags": [],
"title": "a",
"type": "story",
},
Expand All @@ -1634,6 +1713,7 @@ describe('stories API', () => {
"parent": "a",
"prepared": false,
"renderLabel": undefined,
"tags": [],
"title": "a",
"type": "story",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const generateStories = ({ title, refId }: { title: string; refId?: string }): A
name: root,
children: [componentId],
startCollapsed: false,
tags: [],
},
{
type: 'component',
Expand Down
4 changes: 1 addition & 3 deletions code/core/src/types/modules/api-stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface API_BaseEntry {
id: StoryId;
depth: number;
name: string;
tags: Tag[];
refId?: string;
renderLabel?: (item: API_BaseEntry, api: any) => any;
}
Expand All @@ -27,15 +28,13 @@ export interface API_ComponentEntry extends API_BaseEntry {
type: 'component';
parent?: StoryId;
children: StoryId[];
tags: Tag[];
}

export interface API_DocsEntry extends API_BaseEntry {
type: 'docs';
parent: StoryId;
title: ComponentTitle;
importPath: Path;
tags: Tag[];
prepared: boolean;
parameters?: {
[parameterName: string]: any;
Expand All @@ -47,7 +46,6 @@ export interface API_StoryEntry extends API_BaseEntry {
parent: StoryId;
title: ComponentTitle;
importPath: Path;
tags: Tag[];
prepared: boolean;
parameters?: {
[parameterName: string]: any;
Expand Down

0 comments on commit adb2594

Please sign in to comment.