Skip to content

Commit 5b7b12f

Browse files
authored
Merge pull request #27085 from storybookjs/version-non-patch-from-8.1.0-beta.0
Release: Prerelease 8.1.0-beta.1
2 parents b7f2bbe + 17ad320 commit 5b7b12f

File tree

25 files changed

+265
-70
lines changed

25 files changed

+265
-70
lines changed

CHANGELOG.prerelease.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## 8.1.0-beta.1
2+
3+
- API: Add API access to sidebar renderLabel - [#27099](https://github.com/storybookjs/storybook/pull/27099), thanks @shilman!
4+
- CLI: Add main.js `docs.autodocs` automigration - [#27089](https://github.com/storybookjs/storybook/pull/27089), thanks @shilman!
5+
- CLI: Fix eslint configuration for string `extends` - [#27097](https://github.com/storybookjs/storybook/pull/27097), thanks @shilman!
6+
- Indexer: Escape special characters in storyImport regex - [#22545](https://github.com/storybookjs/storybook/pull/22545), thanks @VojGin!
7+
- Next.js: Fix Compatibility with <v14.0.4 - [#27082](https://github.com/storybookjs/storybook/pull/27082), thanks @JReinhold!
8+
- Tags: Fix missing default tags if no `preview.js` - [#27098](https://github.com/storybookjs/storybook/pull/27098), thanks @shilman!
9+
110
## 8.1.0-beta.0
211

312
- Dependencies: Upgrade `@joshwooding/vite-plugin-react-docgen-typescript` to `0.3.1` - [#26673](https://github.com/storybookjs/storybook/pull/26673), thanks @joshwooding!

code/addons/actions/src/addArgsHelpers.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,11 @@ export const addActionsFromArgTypes: ArgsEnhancer<Renderer> = (context) => {
5151
return {};
5252
}
5353

54-
const argTypesWithAction = Object.entries(argTypes).filter(
55-
([name, argType]) => !!argType['action']
56-
);
54+
const argTypesWithAction = Object.entries(argTypes).filter(([name, argType]) => !!argType.action);
5755

5856
return argTypesWithAction.reduce((acc, [name, argType]) => {
5957
if (isInInitialArgs(name, initialArgs)) {
60-
acc[name] = action(typeof argType['action'] === 'string' ? argType['action'] : name);
58+
acc[name] = action(typeof argType.action === 'string' ? argType.action : name);
6159
}
6260
return acc;
6361
}, {} as Args);

code/addons/actions/src/decorator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ export const withActions: <T extends Renderer>(storyFn: PartialStoryFn<T>) => T[
6262
parameterName: PARAM_KEY,
6363
skipIfNoParametersOrOptions: true,
6464
wrapper: (getStory, context, { parameters }) => {
65-
if (parameters?.['handles']) {
66-
applyEventHandlers(actions, ...parameters['handles']);
65+
if (parameters?.handles) {
66+
applyEventHandlers(actions, ...parameters.handles);
6767
}
6868

6969
return getStory(context);

code/frameworks/nextjs/src/compatibility/compatibility-map.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ import semver from 'semver';
33
import { getNextjsVersion, addScopedAlias } from '../utils';
44

55
const mapping: Record<string, Record<string, string>> = {
6-
'<14.0.0': {
6+
'<14.1.0': {
7+
// https://github.com/vercel/next.js/blob/v14.1.0/packages/next/src/shared/lib/segment.ts
78
'next/dist/shared/lib/segment': '@storybook/nextjs/dist/compatibility/segment.compat',
9+
},
10+
'<14.0.4': {
11+
// https://github.com/vercel/next.js/blob/v14.0.4/packages/next/src/client/components/redirect-status-code.ts
812
'next/dist/client/components/redirect-status-code':
913
'@storybook/nextjs/dist/compatibility/redirect-status-code.compat',
1014
},
@@ -14,7 +18,7 @@ export const getCompatibilityAliases = () => {
1418
const version = getNextjsVersion();
1519
const result: Record<string, string> = {};
1620

17-
Object.keys(mapping).filter((key) => {
21+
Object.keys(mapping).forEach((key) => {
1822
if (semver.intersects(version, key)) {
1923
Object.assign(result, mapping[key]);
2024
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { describe, expect, it } from 'vitest';
2+
import type { StorybookConfig } from '@storybook/types';
3+
import { autodocsTags } from './autodocs-tags';
4+
5+
const check = async ({
6+
main: mainConfig,
7+
storybookVersion = '7.0.0',
8+
previewConfigPath,
9+
}: {
10+
main: Partial<StorybookConfig> & Record<string, unknown>;
11+
storybookVersion?: string;
12+
previewConfigPath?: string;
13+
}) => {
14+
return autodocsTags.check({
15+
packageManager: {} as any,
16+
configDir: '',
17+
mainConfig: mainConfig as any,
18+
storybookVersion,
19+
previewConfigPath,
20+
});
21+
};
22+
23+
it('with no docs setting', async () => {
24+
await expect(
25+
check({
26+
main: {},
27+
})
28+
).resolves.toBeFalsy();
29+
});
30+
31+
describe('docs.autodocs = true', () => {
32+
it('errors with no preview.js', async () => {
33+
await expect(
34+
check({
35+
main: {
36+
docs: { autodocs: true },
37+
},
38+
})
39+
).rejects.toThrowError();
40+
});
41+
42+
it('continues with preview.js', async () => {
43+
await expect(
44+
check({
45+
main: {
46+
docs: { autodocs: true },
47+
},
48+
previewConfigPath: '.storybook/preview.js',
49+
})
50+
).resolves.toBeTruthy();
51+
});
52+
});
53+
54+
describe('docs.autodocs != true', () => {
55+
it('docs.autodocs = false', async () => {
56+
await expect(
57+
check({
58+
main: {
59+
docs: { autodocs: false },
60+
},
61+
})
62+
).resolves.toBeTruthy();
63+
});
64+
65+
it('docs.autodocs = "tag"', async () => {
66+
await expect(
67+
check({
68+
main: {
69+
docs: { autodocs: 'tag' },
70+
},
71+
})
72+
).resolves.toBeTruthy();
73+
});
74+
});
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { dedent } from 'ts-dedent';
2+
import chalk from 'chalk';
3+
import type { DocsOptions } from '@storybook/types';
4+
import { readConfig, writeConfig } from '@storybook/csf-tools';
5+
import { updateMainConfig } from '../helpers/mainConfigFile';
6+
import type { Fix } from '../types';
7+
8+
const logger = console;
9+
10+
const MIGRATION =
11+
'https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mainjs-docsautodocs-is-deprecated';
12+
13+
interface Options {
14+
autodocs: DocsOptions['autodocs'];
15+
mainConfigPath?: string;
16+
previewConfigPath?: string;
17+
}
18+
19+
/**
20+
*/
21+
export const autodocsTags: Fix<Options> = {
22+
id: 'autodocs-tags',
23+
versionRange: ['*.*.*', '>=8.0.*'],
24+
async check({ mainConfig, mainConfigPath, previewConfigPath }) {
25+
const autodocs = mainConfig?.docs?.autodocs;
26+
if (autodocs === undefined) return null;
27+
28+
if (autodocs === true && !previewConfigPath) {
29+
throw Error(dedent`
30+
❌ Failed to remove the deprecated ${chalk.cyan('docs.autodocs')} setting from ${chalk.cyan(
31+
mainConfigPath
32+
)}.
33+
34+
There is no preview config file in which to add the ${chalk.cyan('autodocs')} tag.
35+
36+
Please perform the migration by hand: ${chalk.yellow(MIGRATION)}
37+
`);
38+
return null;
39+
}
40+
41+
return { autodocs, mainConfigPath, previewConfigPath };
42+
},
43+
44+
prompt({ autodocs, mainConfigPath, previewConfigPath }) {
45+
let falseMessage = '',
46+
trueMessage = '';
47+
48+
if (autodocs === false) {
49+
falseMessage = dedent`
50+
51+
52+
There is no ${chalk.cyan('docs.autodocs = false')} equivalent.
53+
You'll need to check your stories to ensure none are tagged with ${chalk.cyan('autodocs')}.
54+
`;
55+
} else if (autodocs === true) {
56+
trueMessage = ` and update ${chalk.cyan(previewConfigPath)}`;
57+
}
58+
59+
return dedent`
60+
The ${chalk.cyan('docs.autodocs')} setting in ${chalk.cyan(
61+
mainConfigPath
62+
)} is deprecated.${falseMessage}
63+
64+
Learn more: ${chalk.yellow(MIGRATION)}
65+
66+
Remove ${chalk.cyan('docs.autodocs')}${trueMessage}?
67+
`;
68+
},
69+
70+
async run({ dryRun, mainConfigPath, result }) {
71+
if (!dryRun) {
72+
if (result.autodocs === true) {
73+
logger.info(`✅ Adding "autodocs" tag to ${result.previewConfigPath}`);
74+
const previewConfig = await readConfig(result.previewConfigPath!);
75+
const tags = previewConfig.getFieldNode(['tags']);
76+
if (tags) {
77+
previewConfig.appendValueToArray(['tags'], 'autodocs');
78+
} else {
79+
previewConfig.setFieldValue(['tags'], ['autodocs']);
80+
}
81+
await writeConfig(previewConfig);
82+
}
83+
84+
await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => {
85+
logger.info(`✅ Removing "docs.autodocs" from ${mainConfigPath}`);
86+
main.removeField(['docs', 'autodocs']);
87+
});
88+
}
89+
},
90+
};

code/lib/cli/src/automigrate/fixes/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { mdx1to3 } from './mdx-1-to-3';
2828
import { addonPostCSS } from './addon-postcss';
2929
import { vta } from './vta';
3030
import { upgradeStorybookRelatedDependencies } from './upgrade-storybook-related-dependencies';
31+
import { autodocsTags } from './autodocs-tags';
3132

3233
export * from '../types';
3334

@@ -60,6 +61,7 @@ export const allFixes: Fix[] = [
6061
mdx1to3,
6162
upgradeStorybookRelatedDependencies,
6263
vta,
64+
autodocsTags,
6365
];
6466

6567
export const initFixes: Fix[] = [eslintPlugin];
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { normalizeExtends } from './eslintPlugin';
3+
4+
describe('normalizeExtends', () => {
5+
it('returns empty array when existingExtends is falsy', () => {
6+
expect(normalizeExtends(null)).toEqual([]);
7+
expect(normalizeExtends(undefined)).toEqual([]);
8+
});
9+
10+
it('returns existingExtends when it is a string', () => {
11+
expect(normalizeExtends('foo')).toEqual(['foo']);
12+
});
13+
14+
it('returns existingExtends when it is an array', () => {
15+
expect(normalizeExtends(['foo'])).toEqual(['foo']);
16+
});
17+
18+
it('throws when existingExtends is not a string or array', () => {
19+
expect(() => normalizeExtends(true)).toThrowError('Invalid eslint extends true');
20+
});
21+
});

code/lib/cli/src/automigrate/helpers/eslintPlugin.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ export async function extractEslintInfo(packageManager: JsPackageManager): Promi
4747
return { hasEslint, isStorybookPluginInstalled, eslintConfigFile };
4848
}
4949

50+
export const normalizeExtends = (existingExtends: any): string[] => {
51+
if (!existingExtends) return [];
52+
if (typeof existingExtends === 'string') return [existingExtends];
53+
if (Array.isArray(existingExtends)) return existingExtends;
54+
throw new Error(`Invalid eslint extends ${existingExtends}`);
55+
};
56+
5057
export async function configureEslintPlugin(
5158
eslintFile: string | undefined,
5259
packageManager: JsPackageManager
@@ -55,38 +62,29 @@ export async function configureEslintPlugin(
5562
paddedLog(`Configuring Storybook ESLint plugin at ${eslintFile}`);
5663
if (eslintFile.endsWith('json')) {
5764
const eslintConfig = (await readJson(eslintFile)) as { extends?: string[] };
58-
const existingConfigValue = Array.isArray(eslintConfig.extends)
59-
? eslintConfig.extends
60-
: [eslintConfig.extends].filter(Boolean);
61-
eslintConfig.extends = [
62-
...(existingConfigValue || []),
63-
'plugin:storybook/recommended',
64-
] as string[];
65+
const existingExtends = normalizeExtends(eslintConfig.extends).filter(Boolean);
66+
eslintConfig.extends = [...existingExtends, 'plugin:storybook/recommended'] as string[];
6567

6668
const eslintFileContents = await readFile(eslintFile, 'utf8');
6769
const spaces = detectIndent(eslintFileContents).amount || 2;
6870
await writeJson(eslintFile, eslintConfig, { spaces });
6971
} else {
7072
const eslint = await readConfig(eslintFile);
71-
const extendsConfig = eslint.getFieldValue(['extends']) || [];
72-
const existingConfigValue = Array.isArray(extendsConfig)
73-
? extendsConfig
74-
: [extendsConfig].filter(Boolean);
75-
eslint.setFieldValue(
76-
['extends'],
77-
[...(existingConfigValue || []), 'plugin:storybook/recommended']
78-
);
73+
const existingExtends = normalizeExtends(eslint.getFieldValue(['extends'])).filter(Boolean);
74+
eslint.setFieldValue(['extends'], [...existingExtends, 'plugin:storybook/recommended']);
7975

8076
await writeConfig(eslint);
8177
}
8278
} else {
8379
paddedLog(`Configuring eslint-plugin-storybook in your package.json`);
8480
const packageJson = await packageManager.retrievePackageJson();
81+
const existingExtends = normalizeExtends(packageJson.eslintConfig?.extends).filter(Boolean);
82+
8583
await packageManager.writePackageJson({
8684
...packageJson,
8785
eslintConfig: {
8886
...packageJson.eslintConfig,
89-
extends: [...(packageJson.eslintConfig?.extends || []), 'plugin:storybook/recommended'],
87+
extends: [...existingExtends, 'plugin:storybook/recommended'],
9088
},
9189
});
9290
}

code/lib/core-common/src/utils/get-storybook-info.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export const findConfigFile = (prefix: string, configDir: string) => {
9696

9797
export const getConfigInfo = (packageJson: PackageJson, configDir?: string) => {
9898
let storybookConfigDir = configDir ?? '.storybook';
99-
const storybookScript = packageJson.scripts?.['storybook'];
99+
const storybookScript = packageJson.scripts?.storybook;
100100
if (storybookScript && !configDir) {
101101
const configParam = getStorybookConfiguration(storybookScript, '-c', '--config-dir');
102102
if (configParam) storybookConfigDir = configParam;

0 commit comments

Comments
 (0)