Skip to content

Commit

Permalink
Merge pull request #20541 from storybookjs/shilman/detect-play-functions
Browse files Browse the repository at this point in the history
Telemetry: Count play function usage
  • Loading branch information
shilman authored Jan 10, 2023
2 parents 65f82ad + d1e9f2d commit e4e6387
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 4 deletions.
1 change: 1 addition & 0 deletions code/lib/core-server/src/utils/StoryIndexGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type SpecifierStoriesCache = Record<Path, CacheEntry>;

export const AUTODOCS_TAG = 'autodocs';
export const STORIES_MDX_TAG = 'stories-mdx';
export const PLAY_FN_TAG = 'play-fn';

/** Was this docs entry generated by a .mdx file? (see discussion below) */
export function isMdxEntry({ tags }: DocsIndexEntry) {
Expand Down
11 changes: 8 additions & 3 deletions code/lib/core-server/src/utils/summarizeIndex.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { StoryIndex } from '@storybook/types';

import { STORIES_MDX_TAG, isMdxEntry, AUTODOCS_TAG } from './StoryIndexGenerator';
import { STORIES_MDX_TAG, isMdxEntry, AUTODOCS_TAG, PLAY_FN_TAG } from './StoryIndexGenerator';

const PAGE_REGEX = /(page|screen)/i;

Expand All @@ -9,6 +9,7 @@ export const isPageStory = (storyId: string) => PAGE_REGEX.test(storyId);
export function summarizeIndex(storyIndex: StoryIndex) {
let storyCount = 0;
let pageStoryCount = 0;
let playStoryCount = 0;
let autodocsCount = 0;
let storiesMdxCount = 0;
let mdxCount = 0;
Expand All @@ -18,19 +19,23 @@ export function summarizeIndex(storyIndex: StoryIndex) {
if (isPageStory(entry.title)) {
pageStoryCount += 1;
}
if (entry.tags?.includes(PLAY_FN_TAG)) {
playStoryCount += 1;
}
} else if (entry.type === 'docs') {
if (isMdxEntry(entry)) {
mdxCount += 1;
} else if (entry.tags.includes(STORIES_MDX_TAG)) {
} else if (entry.tags?.includes(STORIES_MDX_TAG)) {
storiesMdxCount += 1;
} else if (entry.tags.includes(AUTODOCS_TAG)) {
} else if (entry.tags?.includes(AUTODOCS_TAG)) {
autodocsCount += 1;
}
}
});
return {
storyCount,
pageStoryCount,
playStoryCount,
autodocsCount,
storiesMdxCount,
mdxCount,
Expand Down
100 changes: 100 additions & 0 deletions code/lib/csf-tools/src/CsfFile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -866,4 +866,104 @@ describe('CsfFile', () => {
).toThrow('CSF: Expected tag to be string literal');
});
});

describe('play functions', () => {
it('story csf2', () => {
expect(
parse(
dedent`
export default { title: 'foo/bar', tags: ['X'] };
export const A = () => {};
A.play = () => {};
A.tags = ['Y'];
`
)
).toMatchInlineSnapshot(`
meta:
title: foo/bar
tags:
- X
stories:
- id: foo-bar--a
name: A
tags:
- 'Y'
- play-fn
`);
});

it('story csf3', () => {
expect(
parse(
dedent`
export default { title: 'foo/bar', tags: ['X'] };
export const A = {
render: () => {},
play: () => {},
tags: ['Y'],
};
`
)
).toMatchInlineSnapshot(`
meta:
title: foo/bar
tags:
- X
stories:
- id: foo-bar--a
name: A
tags:
- 'Y'
- play-fn
`);
});

it('meta csf2', () => {
expect(
parse(
dedent`
export default { title: 'foo/bar', play: () => {}, tags: ['X'] };
export const A = {
render: () => {},
tags: ['Y'],
};
`
)
).toMatchInlineSnapshot(`
meta:
title: foo/bar
tags:
- X
- play-fn
stories:
- id: foo-bar--a
name: A
tags:
- 'Y'
`);
});

it('meta csf3', () => {
expect(
parse(
dedent`
export default { title: 'foo/bar', play: () => {}, tags: ['X'] };
export const A = () => {};
A.tags = ['Y'];
`
)
).toMatchInlineSnapshot(`
meta:
title: foo/bar
tags:
- X
- play-fn
stories:
- id: foo-bar--a
name: A
tags:
- 'Y'
`);
});
});
});
8 changes: 7 additions & 1 deletion code/lib/csf-tools/src/CsfFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,9 @@ export class CsfFile {
// default export can come at any point in the file, so we do this post processing last
const entries = Object.entries(self._stories);
self._meta.title = this._makeTitle(self._meta.title);
if (self._metaAnnotations.play) {
self._meta.tags = [...(self._meta.tags || []), 'play-fn'];
}
self._stories = entries.reduce((acc, [key, story]) => {
if (isExportStory(key, self._meta)) {
const id = toId(self._meta.id || self._meta.title, storyNameFromExport(key));
Expand All @@ -466,13 +469,16 @@ export class CsfFile {
parameters.docsOnly = true;
}
acc[key] = { ...story, id, parameters };
const { tags } = self._storyAnnotations[key];
const { tags, play } = self._storyAnnotations[key];
if (tags) {
const node = t.isIdentifier(tags)
? findVarInitialization(tags.name, this._ast.program)
: tags;
acc[key].tags = parseTags(node);
}
if (play) {
acc[key].tags = [...(acc[key].tags || []), 'play-fn'];
}
}
return acc;
}, {} as Record<string, StaticStory>);
Expand Down
23 changes: 23 additions & 0 deletions code/lib/store/template/stories/component-play.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { global as globalThis } from '@storybook/global';
import type { PartialStoryFn, PlayFunctionContext, StoryContext } from '@storybook/types';
import { within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';

export default {
component: globalThis.Components.Pre,
play: async ({ canvasElement, name }: PlayFunctionContext) => {
await expect(JSON.parse(within(canvasElement).getByTestId('pre').innerText)).toEqual({
name,
});
},
// Render the story name into the Pre
decorators: [
(storyFn: PartialStoryFn, context: StoryContext) => {
const { name } = context;
return storyFn({ args: { object: { name } } });
},
],
};

export const StoryOne = {};
export const StoryTwo = {};

0 comments on commit e4e6387

Please sign in to comment.