Skip to content
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
5 changes: 5 additions & 0 deletions CHANGELOG.prerelease.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 8.6.0-beta.2

- CLI: Reimplement features prompt logic to handle `--yes` and fix `--features` - [#30534](https://github.com/storybookjs/storybook/pull/30534), thanks @ghengeveld!
- Telemetry: Don't count example stories towards CSF feature stats - [#30561](https://github.com/storybookjs/storybook/pull/30561), thanks @shilman!

## 8.6.0-beta.1

- Builder-Vite: Fix defaulting to allowing all hosts - [#30523](https://github.com/storybookjs/storybook/pull/30523), thanks @JReinhold!
Expand Down
39 changes: 35 additions & 4 deletions code/core/src/core-server/utils/StoryIndexGenerator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,19 @@ describe('StoryIndexGenerator', () => {
"title": "D",
"type": "story",
},
"example-button--story-one": {
"componentPath": undefined,
"id": "example-button--story-one",
"importPath": "./src/Button.stories.ts",
"name": "Story One",
"tags": [
"dev",
"test",
"foobar",
],
"title": "Example/Button",
"type": "story",
},
"first-nested-deeply-f--story-one": {
"componentPath": undefined,
"id": "first-nested-deeply-f--story-one",
Expand Down Expand Up @@ -599,6 +612,19 @@ describe('StoryIndexGenerator', () => {
"title": "D",
"type": "story",
},
"example-button--story-one": {
"componentPath": undefined,
"id": "example-button--story-one",
"importPath": "./src/Button.stories.ts",
"name": "Story One",
"tags": [
"dev",
"test",
"foobar",
],
"title": "Example/Button",
"type": "story",
},
"first-nested-deeply-f--story-one": {
"componentPath": undefined,
"id": "first-nested-deeply-f--story-one",
Expand Down Expand Up @@ -767,6 +793,8 @@ describe('StoryIndexGenerator', () => {
"a--story-one",
"b--docs",
"b--story-one",
"example-button--docs",
"example-button--story-one",
"d--docs",
"d--story-one",
"h--docs",
Expand Down Expand Up @@ -809,6 +837,8 @@ describe('StoryIndexGenerator', () => {
"a--story-one",
"b--docs",
"b--story-one",
"example-button--docs",
"example-button--story-one",
"d--docs",
"d--story-one",
"h--docs",
Expand Down Expand Up @@ -1797,6 +1827,7 @@ describe('StoryIndexGenerator', () => {
"second-nested-g--story-one",
"componentreference--docs",
"notitle--docs",
"example-button--story-one",
"h--story-one",
"componentpath-extension--story-one",
"componentpath-noextension--story-one",
Expand Down Expand Up @@ -1825,7 +1856,7 @@ describe('StoryIndexGenerator', () => {
const generator = new StoryIndexGenerator([specifier], options);
await generator.initialize();
await generator.getIndex();
expect(readCsfMock).toHaveBeenCalledTimes(11);
expect(readCsfMock).toHaveBeenCalledTimes(12);

readCsfMock.mockClear();
await generator.getIndex();
Expand Down Expand Up @@ -1883,7 +1914,7 @@ describe('StoryIndexGenerator', () => {
const generator = new StoryIndexGenerator([specifier], options);
await generator.initialize();
await generator.getIndex();
expect(readCsfMock).toHaveBeenCalledTimes(11);
expect(readCsfMock).toHaveBeenCalledTimes(12);

generator.invalidate(specifier, './src/B.stories.ts', false);

Expand Down Expand Up @@ -1968,7 +1999,7 @@ describe('StoryIndexGenerator', () => {
const generator = new StoryIndexGenerator([specifier], options);
await generator.initialize();
await generator.getIndex();
expect(readCsfMock).toHaveBeenCalledTimes(11);
expect(readCsfMock).toHaveBeenCalledTimes(12);

generator.invalidate(specifier, './src/B.stories.ts', true);

Expand Down Expand Up @@ -2007,7 +2038,7 @@ describe('StoryIndexGenerator', () => {
const generator = new StoryIndexGenerator([specifier], options);
await generator.initialize();
await generator.getIndex();
expect(readCsfMock).toHaveBeenCalledTimes(11);
expect(readCsfMock).toHaveBeenCalledTimes(12);

generator.invalidate(specifier, './src/B.stories.ts', true);

Expand Down
6 changes: 5 additions & 1 deletion code/core/src/core-server/utils/StoryIndexGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { dirname, extname, join, normalize, relative, resolve, sep } from 'node:

import { commonGlobOptions, normalizeStoryPath } from '@storybook/core/common';
import { combineTags, storyNameFromExport, toId } from '@storybook/core/csf';
import { isExampleStoryId } from '@storybook/core/telemetry';
import type {
DocsIndexEntry,
DocsOptions,
Expand Down Expand Up @@ -269,7 +270,10 @@ export class StoryIndexGenerator {
return item;
}

addStats(item.extra.stats, statsSummary);
// don't count example stories towards feature usage stats
if (!isExampleStoryId(item.id)) {
addStats(item.extra.stats, statsSummary);
}

// Drop extra data used for internal bookkeeping
const { extra, ...existing } = item;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const component = {};
export default {
title: 'Example/Button',
component,
tags: ['foobar'],
};

export const StoryOne = {};
12 changes: 12 additions & 0 deletions code/core/src/core-server/utils/stories-json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,18 @@ describe('useStoriesJson', () => {
"title": "docs2/Yabbadabbadooo",
"type": "docs",
},
"example-button--story-one": {
"id": "example-button--story-one",
"importPath": "./src/Button.stories.ts",
"name": "Story One",
"tags": [
"dev",
"test",
"foobar",
],
"title": "Example/Button",
"type": "story",
},
"first-nested-deeply-f--story-one": {
"id": "first-nested-deeply-f--story-one",
"importPath": "./src/first-nested/deeply/F.stories.js",
Expand Down
2 changes: 1 addition & 1 deletion code/lib/create-storybook/src/bin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const createStorybookProgram = program
// default value is false, but if the user sets STORYBOOK_DISABLE_TELEMETRY, it can be true
process.env.STORYBOOK_DISABLE_TELEMETRY && process.env.STORYBOOK_DISABLE_TELEMETRY !== 'false'
)
.option('--features <...list>', 'What features of storybook are you interested in?')
.option('--features <list...>', 'What features of storybook are you interested in?')
.option('--debug', 'Get more logs in debug mode')
.option('--enable-crash-reports', 'Enable sending crash reports to telemetry data')
.option('-f --force', 'Force add Storybook')
Expand Down
4 changes: 3 additions & 1 deletion code/lib/create-storybook/src/generators/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ export type Generator<T = void> = (
commandOptions?: CommandOptions
) => Promise<T>;

export type GeneratorFeature = 'docs' | 'test';

export type CommandOptions = {
packageManager: PackageManagerName;
usePnp?: boolean;
features: string[];
features: GeneratorFeature[];
type?: ProjectType;
force?: any;
html?: boolean;
Expand Down
69 changes: 48 additions & 21 deletions code/lib/create-storybook/src/initiate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import svelteKitGenerator from './generators/SVELTEKIT';
import vue3Generator from './generators/VUE3';
import webComponentsGenerator from './generators/WEB-COMPONENTS';
import webpackReactGenerator from './generators/WEBPACK_REACT';
import type { CommandOptions, GeneratorOptions } from './generators/types';
import type { CommandOptions, GeneratorFeature, GeneratorOptions } from './generators/types';
import { packageVersions } from './ink/steps/checks/packageVersions';
import { vitestConfigFiles } from './ink/steps/checks/vitestConfigFiles';
import { currentDirectoryIsEmpty, scaffoldNewProject } from './scaffold-new-project';
Expand Down Expand Up @@ -294,27 +294,49 @@ export async function doInitiate(options: CommandOptions): Promise<

const isInteractive = process.stdout.isTTY && !process.env.CI;

let features = options.features || isInteractive ? ['dev', 'docs', 'test'] : ['dev', 'docs'];
const selectableFeatures: Record<GeneratorFeature, string> = {
docs: 'Documentation',
test: 'Testing',
};
let selectedFeatures = new Set<GeneratorFeature>();
selectedFeatures.toString = () =>
selectedFeatures.size === 0
? 'none'
: Array.from(selectedFeatures)
.map((f) => selectableFeatures[f])
.join(', ');

if (options.features?.length > 0) {
if (options.features.includes('docs')) {
selectedFeatures.add('docs');
}
if (options.features.includes('test')) {
selectedFeatures.add('test');
}
logger.log(`Selected features: ${selectedFeatures}`);
} else if (options.yes || !isInteractive) {
selectedFeatures.add('docs');

if (isInteractive && !options.features) {
if (isInteractive) {
// Don't automatically add test feature in CI
selectedFeatures.add('test');
}
logger.log(`Selected features: ${selectedFeatures}`);
} else {
const out = await prompts({
type: 'multiselect',
name: 'features',
message: `What are you using Storybook for?`,
choices: [
{ title: 'Development', value: 'dev', selected: true, disabled: true },
{ title: 'Documentation', value: 'docs', selected: true },
{ title: 'Testing', value: 'test', selected: true },
],
choices: Object.entries(selectableFeatures).map(([value, title]) => ({
title,
value,
selected: true,
})),
});
features = out.features;
selectedFeatures = new Set(out.features);
}

if (!features.includes('dev')) {
features.push('dev');
}

const telemetryFeatures = [...features];
const telemetryFeatures = ['dev', ...selectedFeatures];

// Check if the current directory is empty.
if (options.force !== true && currentDirectoryIsEmpty(packageManager.type)) {
Expand Down Expand Up @@ -377,7 +399,7 @@ export async function doInitiate(options: CommandOptions): Promise<
}
}

if (features.includes('test')) {
if (selectedFeatures.has('test')) {
const packageVersionsData = await packageVersions.condition({ packageManager }, {} as any);
if (packageVersionsData.type === 'incompatible') {
const { ignorePackageVersions } = isInteractive
Expand All @@ -393,14 +415,14 @@ export async function doInitiate(options: CommandOptions): Promise<
])
: { ignorePackageVersions: true };
if (ignorePackageVersions) {
features.splice(features.indexOf('test'), 1);
selectedFeatures.delete('test');
} else {
process.exit(0);
}
}
}

if (features.includes('test')) {
if (selectedFeatures.has('test')) {
const vitestConfigFilesData = await vitestConfigFiles.condition(
{ babel, findUp, fs } as any,
{ directory: process.cwd() } as any
Expand All @@ -419,7 +441,7 @@ export async function doInitiate(options: CommandOptions): Promise<
])
: { ignoreVitestConfigFiles: true };
if (ignoreVitestConfigFiles) {
features.splice(features.indexOf('test'), 1);
selectedFeatures.delete('test');
} else {
process.exit(0);
}
Expand All @@ -430,11 +452,14 @@ export async function doInitiate(options: CommandOptions): Promise<
await packageManager.installDependencies();
}

// update the mutated value
options.features = features;
// Update the options object with the selected features before passing it down to the generator
options.features = Array.from(selectedFeatures);

const installResult = await installStorybook(projectType as ProjectType, packageManager, options);

// Sync features back because they may have been mutated by the generator (e.g. in case of undetected project type)
selectedFeatures = new Set(options.features);

if (!options.skipInstall) {
await packageManager.installDependencies();
}
Expand Down Expand Up @@ -484,7 +509,7 @@ export async function doInitiate(options: CommandOptions): Promise<
? `ng run ${installResult.projectName}:storybook`
: packageManager.getRunStorybookCommand();

if (features.includes('test')) {
if (selectedFeatures.has('test')) {
logger.log(
`> npx storybook@${versions.storybook} add @storybook/experimental-addon-test@${versions['@storybook/experimental-addon-test']}`
);
Expand All @@ -498,6 +523,8 @@ export async function doInitiate(options: CommandOptions): Promise<
boxen(
dedent`
Storybook was successfully installed in your project! 🎉
Additional features: ${selectedFeatures}

To run Storybook manually, run ${picocolors.yellow(
picocolors.bold(storybookCommand)
)}. CTRL+C to stop.
Expand Down
3 changes: 2 additions & 1 deletion code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,5 +303,6 @@
"Dependency Upgrades"
]
]
}
},
"deferredNextVersion": "8.6.0-beta.2"
}
8 changes: 5 additions & 3 deletions docs/configure/telemetry.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Specifically, we track the following information in our telemetry events:
* Testing tools (e.g. [Jest](https://jestjs.io/), [Vitest](https://vitest.dev/), [Playwright](https://playwright.dev/)).
* Package manager information (e.g., `npm`, `yarn`).
* Monorepo information (e.g., [NX](https://nx.dev/), [Turborepo](https://turborepo.org/)).
* In-app events (e.g., [Storybook guided tour](https://github.com/storybookjs/addon-onboarding)).
* In-app events (e.g., [Storybook guided tour](https://github.com/storybookjs/addon-onboarding), [UI test run](https://storybook.js.org/docs/writing-tests/test-addon#storybook-ui)).

Access to the raw data is highly controlled, limited to select members of Storybook's core team who maintain the telemetry. We cannot identify individual users from the dataset: it is anonymized and untraceable back to the user.

Expand Down Expand Up @@ -75,17 +75,19 @@ Will generate the following output:
"exampleDocsCount": 3,
"onboardingStoryCount": 0,
"onboardingDocsCount": 0,
"version": 4
"version": 5
},
"storyStats": {
"factory": 0,
"play": 0,
"render": 1,
"loaders": 0,
"beforeEach": 0,
"globals": 0,
"storyFn": 5,
"mount": 0,
"moduleMock": 0
"moduleMock": 0,
"tags": 0
}
},
"metadata": {
Expand Down
2 changes: 1 addition & 1 deletion docs/versions/next.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"8.6.0-beta.1","info":{"plain":"- Builder-Vite: Fix defaulting to allowing all hosts - [#30523](https://github.com/storybookjs/storybook/pull/30523), thanks @JReinhold!\n- React: Fix incorrect import in preview.ts - [#30542](https://github.com/storybookjs/storybook/pull/30542), thanks @mrginglymus!\n- Tags: Add story/meta usage telemetry - [#30555](https://github.com/storybookjs/storybook/pull/30555), thanks @shilman!\n- UI: Fix tags sort for browser back-compat - [#30547](https://github.com/storybookjs/storybook/pull/30547), thanks @shilman!"}}
{"version":"8.6.0-beta.2","info":{"plain":"- CLI: Reimplement features prompt logic to handle `--yes` and fix `--features` - [#30534](https://github.com/storybookjs/storybook/pull/30534), thanks @ghengeveld!\n- Telemetry: Don't count example stories towards CSF feature stats - [#30561](https://github.com/storybookjs/storybook/pull/30561), thanks @shilman!"}}