Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ec31c9f

Browse files
authored
Merge pull request #30685 from storybookjs/version-patch-from-8.6.0
Release: Patch 8.6.1
2 parents a13c741 + 530992e commit ec31c9f

35 files changed

+2397
-2321
lines changed

.circleci/config.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,26 +1004,26 @@ workflows:
10041004
requires:
10051005
- build
10061006
- create-sandboxes:
1007-
parallelism: 37
1007+
parallelism: 36
10081008
requires:
10091009
- build
10101010
# - smoke-test-sandboxes: # disabled for now
10111011
# requires:
10121012
# - create-sandboxes
10131013
- chromatic-sandboxes:
1014-
parallelism: 34
1014+
parallelism: 33
10151015
requires:
10161016
- create-sandboxes
10171017
- e2e-production:
1018-
parallelism: 32
1018+
parallelism: 31
10191019
requires:
10201020
- create-sandboxes
10211021
- e2e-dev:
10221022
parallelism: 1
10231023
requires:
10241024
- create-sandboxes
10251025
- test-runner-production:
1026-
parallelism: 30
1026+
parallelism: 29
10271027
requires:
10281028
- create-sandboxes
10291029
- vitest-integration:

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 8.6.1
2+
3+
- CSF: Only export definePreview from the framework - [#30676](https://github.com/storybookjs/storybook/pull/30676), thanks @kasperpeulen!
4+
- Codemod: Only remove types when they are unused - [#30644](https://github.com/storybookjs/storybook/pull/30644), thanks @yannbf!
5+
16
## 8.6.0
27

38
The 8.6 release focuses on [Storybook Test](https://storybook.js.org/blog/storybook-test-sneak-peek/), which brings realtime component, accessibility, and visual UI tests to your favorite component workshop.

code/core/src/csf/csf-factories.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* eslint-disable no-underscore-dangle */
1+
/* eslint-disable no-underscore-dangle,@typescript-eslint/naming-convention */
22
import type {
33
Args,
44
ComponentAnnotations,
@@ -20,7 +20,8 @@ export interface Preview<TRenderer extends Renderer = Renderer> {
2020
meta(input: ComponentAnnotations<TRenderer>): Meta<TRenderer>;
2121
}
2222

23-
export function definePreview<TRenderer extends Renderer>(
23+
/** Do not use, use the definePreview exported from the framework instead. */
24+
export function __definePreview<TRenderer extends Renderer>(
2425
input: Preview<TRenderer>['input']
2526
): Preview<TRenderer> {
2627
let composed: NormalizedProjectAnnotations<TRenderer>;

code/frameworks/experimental-nextjs-vite/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ReactPreview } from '@storybook/react';
2-
import { definePreview as definePreviewBase } from '@storybook/react';
2+
import { __definePreview } from '@storybook/react';
33

44
import type vitePluginStorybookNextJs from 'vite-plugin-storybook-nextjs';
55

@@ -15,7 +15,7 @@ declare module '@storybook/experimental-nextjs-vite/vite-plugin' {
1515
}
1616

1717
export function definePreview(preview: NextPreview['input']) {
18-
return definePreviewBase({
18+
return __definePreview({
1919
...preview,
2020
addons: [nextPreview, ...(preview.addons ?? [])],
2121
}) as NextPreview;

code/frameworks/nextjs/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import type { ReactPreview } from '@storybook/react';
2-
import { definePreview as definePreviewBase } from '@storybook/react';
2+
import { __definePreview } from '@storybook/react';
33

44
import * as nextPreview from './preview';
55

66
export * from './types';
77
export * from './portable-stories';
88

99
export function definePreview(preview: NextPreview['input']) {
10-
return definePreviewBase({
10+
return __definePreview({
1111
...preview,
1212
addons: [nextPreview, ...(preview.addons ?? [])],
1313
}) as NextPreview;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
export type { FrameworkOptions, StorybookConfig } from './types';
22

3-
export { definePreview } from '@storybook/react';
3+
export { __definePreview as definePreview } from '@storybook/react';
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export { definePreview } from '@storybook/react';
1+
export { __definePreview as definePreview } from '@storybook/react';
22

33
export * from './types';
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export * from './types';
2-
export { definePreview } from '@storybook/react';
2+
export { __definePreview as definePreview } from '@storybook/react';

code/lib/cli-storybook/src/codemod/helpers/config-to-csf-factory.test.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ describe('main/preview codemod: general parsing functionality', () => {
129129
expect(transformed).toEqual(original);
130130
});
131131

132-
it('should remove legacy main config type imports', async () => {
132+
it('should remove legacy main config type imports if unused', async () => {
133133
await expect(
134134
transform(dedent`
135135
import { type StorybookConfig } from '@storybook/react-vite'
@@ -147,6 +147,35 @@ describe('main/preview codemod: general parsing functionality', () => {
147147
});
148148
`);
149149
});
150+
151+
it('should not remove legacy main config type imports if used', async () => {
152+
await expect(
153+
transform(dedent`
154+
import { type StorybookConfig } from '@storybook/react-vite'
155+
156+
const config: StorybookConfig = {
157+
stories: []
158+
};
159+
160+
const features: StorybookConfig['features'] = {
161+
foo: true,
162+
};
163+
164+
export default config;
165+
`)
166+
).resolves.toMatchInlineSnapshot(`
167+
import { type StorybookConfig } from '@storybook/react-vite';
168+
import { defineMain } from '@storybook/react-vite/node';
169+
170+
const features: StorybookConfig['features'] = {
171+
foo: true,
172+
};
173+
174+
export default defineMain({
175+
stories: [],
176+
});
177+
`);
178+
});
150179
});
151180

152181
describe('preview specific functionality', () => {
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import { describe, expect, it } from 'vitest';
2+
3+
import { types as t } from 'storybook/internal/babel';
4+
import { generate, parser } from 'storybook/internal/babel';
5+
6+
import {
7+
cleanupTypeImports,
8+
getConfigProperties,
9+
removeExportDeclarations,
10+
} from './csf-factories-utils';
11+
12+
expect.addSnapshotSerializer({
13+
serialize: (val: any) => {
14+
if (typeof val === 'string') {
15+
return val;
16+
}
17+
if (typeof val === 'object' && val !== null) {
18+
return JSON.stringify(val, null, 2);
19+
}
20+
return String(val);
21+
},
22+
test: (_val) => true,
23+
});
24+
25+
function parseCodeToProgramNode(code: string): t.Program {
26+
return parser.parse(code, { sourceType: 'module', plugins: ['typescript'] }).program;
27+
}
28+
29+
function generateCodeFromAST(node: t.Program) {
30+
return generate(node).code;
31+
}
32+
33+
describe('cleanupTypeImports', () => {
34+
it('removes disallowed imports from @storybook/*', () => {
35+
const code = `
36+
import { Story, SomethingElse } from '@storybook/react';
37+
import { Other } from 'some-other-package';
38+
`;
39+
40+
const programNode = parseCodeToProgramNode(code);
41+
const cleanedNodes = cleanupTypeImports(programNode, ['Story']);
42+
43+
expect(generateCodeFromAST({ ...programNode, body: cleanedNodes })).toMatchInlineSnapshot(`
44+
import { SomethingElse } from '@storybook/react';
45+
import { Other } from 'some-other-package';
46+
`);
47+
});
48+
49+
it('removes entire import if all specifiers are removed', () => {
50+
const code = `
51+
import { Story, Meta } from '@storybook/react';
52+
`;
53+
54+
const programNode = parseCodeToProgramNode(code);
55+
const cleanedNodes = cleanupTypeImports(programNode, ['Story', 'Meta']);
56+
57+
expect(generateCodeFromAST({ ...programNode, body: cleanedNodes })).toMatchInlineSnapshot(``);
58+
});
59+
60+
it('retains non storybook imports', () => {
61+
const code = `
62+
import { Preview } from 'internal-types';
63+
`;
64+
65+
const programNode = parseCodeToProgramNode(code);
66+
const cleanedNodes = cleanupTypeImports(programNode, ['Preview']);
67+
68+
expect(generateCodeFromAST({ ...programNode, body: cleanedNodes })).toMatchInlineSnapshot(
69+
`import { Preview } from 'internal-types';`
70+
);
71+
});
72+
73+
it('retains namespace imports', () => {
74+
const code = `
75+
import * as Storybook from '@storybook/react';
76+
`;
77+
78+
const programNode = parseCodeToProgramNode(code);
79+
const cleanedNodes = cleanupTypeImports(programNode, ['Preview']);
80+
81+
expect(generateCodeFromAST({ ...programNode, body: cleanedNodes })).toMatchInlineSnapshot(
82+
`import * as Storybook from '@storybook/react';`
83+
);
84+
});
85+
86+
it('retains imports if they are used', () => {
87+
const code = `
88+
import { Type1, type Type2 } from '@storybook/react';
89+
import type { Type3, ShouldBeRemoved, Type4 } from '@storybook/react';
90+
91+
const example: Type1 = {};
92+
const example2 = {} as Type2;
93+
const example3 = {} satisfies Type3;
94+
const example4 = {
95+
render: (args: Type4['args']) => {}
96+
};
97+
`;
98+
99+
const programNode = parseCodeToProgramNode(code);
100+
const cleanedNodes = cleanupTypeImports(programNode, [
101+
'Type1',
102+
'Type2',
103+
'Type3',
104+
'Type4',
105+
'ShouldBeRemoved',
106+
]);
107+
108+
const result = generateCodeFromAST({ ...programNode, body: cleanedNodes });
109+
110+
expect(result).toMatchInlineSnapshot(`
111+
import { Type1, type Type2 } from '@storybook/react';
112+
import type { Type3, Type4 } from '@storybook/react';
113+
const example: Type1 = {};
114+
const example2 = {} as Type2;
115+
const example3 = {} satisfies Type3;
116+
const example4 = {
117+
render: (args: Type4['args']) => {}
118+
};
119+
`);
120+
121+
expect(result).not.toContain('ShouldBeRemoved');
122+
});
123+
});
124+
125+
describe('removeExportDeclarations', () => {
126+
it('removes specified variable export declarations', () => {
127+
const code = `
128+
export const foo = 'foo';
129+
export const bar = 'bar';
130+
export const baz = 'baz';
131+
`;
132+
133+
const programNode = parseCodeToProgramNode(code);
134+
const exportDecls = {
135+
foo: t.variableDeclarator(t.identifier('foo')),
136+
baz: t.variableDeclarator(t.identifier('baz')),
137+
};
138+
139+
const cleanedNodes = removeExportDeclarations(programNode, exportDecls);
140+
const cleanedCode = generateCodeFromAST({ ...programNode, body: cleanedNodes });
141+
142+
expect(cleanedCode).toMatchInlineSnapshot(`export const bar = 'bar';`);
143+
});
144+
145+
it('removes specified function export declarations', () => {
146+
const code = `
147+
export function foo() { return 'foo'; }
148+
export function bar() { return 'bar'; }
149+
`;
150+
151+
const programNode = parseCodeToProgramNode(code);
152+
const exportDecls = {
153+
foo: t.functionDeclaration(t.identifier('foo'), [], t.blockStatement([])),
154+
};
155+
156+
const cleanedNodes = removeExportDeclarations(programNode, exportDecls);
157+
const cleanedCode = generateCodeFromAST({ ...programNode, body: cleanedNodes });
158+
159+
expect(cleanedCode).toMatchInlineSnapshot(`
160+
export function bar() {
161+
return 'bar';
162+
}
163+
`);
164+
});
165+
166+
it('retains exports not in the disallow list', () => {
167+
const code = `
168+
export const foo = 'foo';
169+
export const bar = 'bar';
170+
`;
171+
172+
const programNode = parseCodeToProgramNode(code);
173+
const exportDecls = {
174+
nonExistent: t.variableDeclarator(t.identifier('nonExistent')),
175+
};
176+
177+
const cleanedNodes = removeExportDeclarations(programNode, exportDecls);
178+
const cleanedCode = generateCodeFromAST({ ...programNode, body: cleanedNodes });
179+
180+
expect(cleanedCode).toMatchInlineSnapshot(`
181+
export const foo = 'foo';
182+
export const bar = 'bar';
183+
`);
184+
});
185+
});
186+
187+
describe('getConfigProperties', () => {
188+
it('returns object properties from variable declarations', () => {
189+
const exportDecls = {
190+
foo: t.variableDeclarator(t.identifier('foo'), t.stringLiteral('fooValue')),
191+
bar: t.variableDeclarator(t.identifier('bar'), t.numericLiteral(42)),
192+
};
193+
194+
const properties = getConfigProperties(exportDecls);
195+
196+
expect(properties).toHaveLength(2);
197+
expect(properties[0].key.name).toBe('foo');
198+
expect(properties[0].value.value).toBe('fooValue');
199+
expect(properties[1].key.name).toBe('bar');
200+
expect(properties[1].value.value).toBe(42);
201+
});
202+
203+
it('returns object properties from function declarations', () => {
204+
const exportDecls = {
205+
foo: t.functionDeclaration(t.identifier('foo'), [], t.blockStatement([])),
206+
};
207+
208+
const properties = getConfigProperties(exportDecls);
209+
210+
expect(properties).toHaveLength(1);
211+
expect(properties[0].key.name).toBe('foo');
212+
expect(properties[0].value.type).toBe('ArrowFunctionExpression');
213+
});
214+
});

0 commit comments

Comments
 (0)