diff --git a/CHANGELOG.md b/CHANGELOG.md index b1c0c5d81f88..444fe7ae0bea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 8.2.6 + +- CPC: Fix missing exports for addon-kit - [#28691](https://github.com/storybookjs/storybook/pull/28691), thanks @ndelangen! + ## 8.2.5 - CPC: Add the globals export for manager - [#28650](https://github.com/storybookjs/storybook/pull/28650), thanks @ndelangen! diff --git a/code/.storybook/main.ts b/code/.storybook/main.ts index 962bef2124da..7c4f74c8c2ef 100644 --- a/code/.storybook/main.ts +++ b/code/.storybook/main.ts @@ -8,34 +8,78 @@ const managerApiPath = path.join(__dirname, '../core/src/manager-api'); const config: StorybookConfig = { stories: [ + { + directory: '../core/template/stories', + titlePrefix: 'core', + }, { directory: '../core/src/manager', - titlePrefix: '@manager', + titlePrefix: 'manager', }, { directory: '../core/src/preview-api', - titlePrefix: '@preview', + titlePrefix: 'preview', + }, + { + directory: '../core/src/components/brand', + titlePrefix: 'brand', }, { - directory: '../core/src/components', - titlePrefix: '@components', + directory: '../core/src/components/components', + titlePrefix: 'components', }, { directory: '../lib/blocks/src', - titlePrefix: '@blocks', + titlePrefix: 'blocks', + }, + { + directory: '../addons/a11y/template/stories', + titlePrefix: 'addons/a11y', + }, + { + directory: '../addons/actions/template/stories', + titlePrefix: 'addons/actions', + }, + { + directory: '../addons/backgrounds/template/stories', + titlePrefix: 'addons/backgrounds', }, { directory: '../addons/controls/src', - titlePrefix: '@addons/controls', + titlePrefix: 'addons/controls', + }, + { + directory: '../addons/controls/template/stories', + titlePrefix: 'addons/controls', + }, + { + directory: '../addons/docs/template/stories', + titlePrefix: 'addons/docs', + }, + { + directory: '../addons/links/template/stories', + titlePrefix: 'addons/links', + }, + { + directory: '../addons/viewport/template/stories', + titlePrefix: 'addons/viewport', + }, + { + directory: '../addons/toolbars/template/stories', + titlePrefix: 'addons/toolbars', }, { directory: '../addons/onboarding/src', - titlePrefix: '@addons/onboarding', + titlePrefix: 'addons/onboarding', }, { directory: '../addons/interactions/src', - titlePrefix: '@addons/interactions', + titlePrefix: 'addons/interactions', }, + // { + // directory: '../addons/interactions/template/stories', + // titlePrefix: 'addons/interactions', + // }, ], addons: [ '@storybook/addon-links', @@ -46,6 +90,11 @@ const config: StorybookConfig = { '@storybook/addon-a11y', '@chromatic-com/storybook', ], + previewAnnotations: [ + './core/template/stories/preview.ts', + './addons/toolbars/template/stories/preview.ts', + './renderers/react/template/components/index.js', + ], build: { test: { // we have stories for the blocks here, we can't exclude them @@ -58,9 +107,21 @@ const config: StorybookConfig = { name: '@storybook/react-vite', options: {}, }, + refs: { + icons: { + title: 'Icons', + url: 'https://main--64b56e737c0aeefed9d5e675.chromatic.com', + expanded: false, + }, + }, core: { disableTelemetry: true, }, + features: { + viewportStoryGlobals: true, + themesStoryGlobals: true, + backgroundsStoryGlobals: true, + }, viteFinal: (viteConfig, { configType }) => mergeConfig(viteConfig, { resolve: { @@ -81,7 +142,7 @@ const config: StorybookConfig = { sourcemap: process.env.CI !== 'true', }, }), - logLevel: 'debug', + // logLevel: 'debug', }; export default config; diff --git a/code/.storybook/preview.tsx b/code/.storybook/preview.tsx index e1c81fe960e7..c80bdcea2937 100644 --- a/code/.storybook/preview.tsx +++ b/code/.storybook/preview.tsx @@ -296,20 +296,3 @@ export const parameters = { ], }, }; - -export const globalTypes = { - theme: { - name: 'Theme', - description: 'Global theme for components', - toolbar: { - icon: 'circlehollow', - title: 'Theme', - items: [ - { value: 'light', icon: 'circlehollow', title: 'light' }, - { value: 'dark', icon: 'circle', title: 'dark' }, - { value: 'side-by-side', icon: 'sidebar', title: 'side by side' }, - { value: 'stacked', icon: 'bottombar', title: 'stacked' }, - ], - }, - }, -}; diff --git a/code/addons/docs/docs/recipes.md b/code/addons/docs/docs/recipes.md index e98c89145b78..0a982974f390 100644 --- a/code/addons/docs/docs/recipes.md +++ b/code/addons/docs/docs/recipes.md @@ -184,7 +184,7 @@ The Storybook UI is a workshop for developing components in isolation. Storybook To address this, we’ve added a CLI flag to only export the docs. This flag is also available in dev mode: ```sh -yarn build-storybook --docs +yarn storybook build --docs ``` ## Disabling docs stories diff --git a/code/addons/links/package.json b/code/addons/links/package.json index d766b0899819..d38a54820858 100644 --- a/code/addons/links/package.json +++ b/code/addons/links/package.json @@ -67,7 +67,7 @@ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts" }, "dependencies": { - "@storybook/csf": "0.1.11", + "@storybook/csf": "^0.1.11", "@storybook/global": "^5.0.0", "ts-dedent": "^2.0.0" }, diff --git a/code/core/package.json b/code/core/package.json index 8603f9ea33d8..e1a53367825d 100644 --- a/code/core/package.json +++ b/code/core/package.json @@ -253,7 +253,7 @@ "prep": "bun ./scripts/prep.ts" }, "dependencies": { - "@storybook/csf": "0.1.11", + "@storybook/csf": "^0.1.11", "@types/express": "^4.17.21", "@types/node": "^18.0.0", "browser-assert": "^1.2.1", diff --git a/code/core/src/common/js-package-manager/JsPackageManager.ts b/code/core/src/common/js-package-manager/JsPackageManager.ts index 1000cc7bf174..5a18b572f248 100644 --- a/code/core/src/common/js-package-manager/JsPackageManager.ts +++ b/code/core/src/common/js-package-manager/JsPackageManager.ts @@ -64,22 +64,6 @@ export abstract class JsPackageManager { return packageJSON ? packageJSON.version ?? null : null; } - // NOTE: for some reason yarn prefers the npm registry in - // local development, so always use npm - async setRegistryURL(url: string) { - if (url) { - await this.executeCommand({ command: 'npm', args: ['config', 'set', 'registry', url] }); - } else { - await this.executeCommand({ command: 'npm', args: ['config', 'delete', 'registry'] }); - } - } - - async getRegistryURL() { - const res = await this.executeCommand({ command: 'npm', args: ['config', 'get', 'registry'] }); - const url = res.trim(); - return url === 'undefined' ? undefined : url; - } - constructor(options?: JsPackageManagerOptions) { this.cwd = options?.cwd || process.cwd(); } @@ -487,6 +471,8 @@ export abstract class JsPackageManager { ): // Use generic and conditional type to force `string[]` if fetchAllVersions is true and `string` if false Promise; + public abstract getRegistryURL(): Promise; + public abstract runPackageCommand( command: string, args: string[], diff --git a/code/core/src/common/js-package-manager/NPMProxy.test.ts b/code/core/src/common/js-package-manager/NPMProxy.test.ts index d1bb289112f1..9c88ea60af45 100644 --- a/code/core/src/common/js-package-manager/NPMProxy.test.ts +++ b/code/core/src/common/js-package-manager/NPMProxy.test.ts @@ -34,21 +34,6 @@ describe('NPM Proxy', () => { }); }); - describe('setRegistryUrl', () => { - it('should run `npm config set registry https://foo.bar`', async () => { - const executeCommandSpy = vi.spyOn(npmProxy, 'executeCommand').mockResolvedValueOnce(''); - - await npmProxy.setRegistryURL('https://foo.bar'); - - expect(executeCommandSpy).toHaveBeenCalledWith( - expect.objectContaining({ - command: 'npm', - args: ['config', 'set', 'registry', 'https://foo.bar'], - }) - ); - }); - }); - describe('installDependencies', () => { describe('npm6', () => { it('should run `npm install`', async () => { diff --git a/code/core/src/common/js-package-manager/NPMProxy.ts b/code/core/src/common/js-package-manager/NPMProxy.ts index ff77aedfa95a..d4d711915925 100644 --- a/code/core/src/common/js-package-manager/NPMProxy.ts +++ b/code/core/src/common/js-package-manager/NPMProxy.ts @@ -181,6 +181,17 @@ export class NPMProxy extends JsPackageManager { }); } + public async getRegistryURL() { + const res = await this.executeCommand({ + command: 'npm', + // "npm config" commands are not allowed in workspaces per default + // https://github.com/npm/cli/issues/6099#issuecomment-1847584792 + args: ['config', 'get', 'registry', '-ws=false', '-iwr'], + }); + const url = res.trim(); + return url === 'undefined' ? undefined : url; + } + protected async runAddDeps(dependencies: string[], installAsDevDependencies: boolean) { const { logStream, readLogFile, moveLogFile, removeLogFile } = await createLogStream(); let args = [...dependencies]; diff --git a/code/core/src/common/js-package-manager/PNPMProxy.test.ts b/code/core/src/common/js-package-manager/PNPMProxy.test.ts index 2430221cc2e8..cca2ca4ea364 100644 --- a/code/core/src/common/js-package-manager/PNPMProxy.test.ts +++ b/code/core/src/common/js-package-manager/PNPMProxy.test.ts @@ -24,21 +24,6 @@ describe('PNPM Proxy', () => { }); }); - describe('setRegistryUrl', () => { - it('should run `npm config set registry https://foo.bar`', async () => { - const executeCommandSpy = vi.spyOn(pnpmProxy, 'executeCommand').mockResolvedValueOnce(''); - - await pnpmProxy.setRegistryURL('https://foo.bar'); - - expect(executeCommandSpy).toHaveBeenCalledWith( - expect.objectContaining({ - command: 'npm', - args: ['config', 'set', 'registry', 'https://foo.bar'], - }) - ); - }); - }); - describe('installDependencies', () => { it('should run `pnpm install`', async () => { const executeCommandSpy = vi diff --git a/code/core/src/common/js-package-manager/PNPMProxy.ts b/code/core/src/common/js-package-manager/PNPMProxy.ts index 41c2858763c8..6d3f434c87c9 100644 --- a/code/core/src/common/js-package-manager/PNPMProxy.ts +++ b/code/core/src/common/js-package-manager/PNPMProxy.ts @@ -90,6 +90,15 @@ export class PNPMProxy extends JsPackageManager { }); } + public async getRegistryURL() { + const res = await this.executeCommand({ + command: 'pnpm', + args: ['config', 'get', 'registry'], + }); + const url = res.trim(); + return url === 'undefined' ? undefined : url; + } + async runPackageCommand(command: string, args: string[], cwd?: string): Promise { return this.executeCommand({ command: 'pnpm', diff --git a/code/core/src/common/js-package-manager/Yarn1Proxy.test.ts b/code/core/src/common/js-package-manager/Yarn1Proxy.test.ts index a26ce81efeb9..c20f496fed80 100644 --- a/code/core/src/common/js-package-manager/Yarn1Proxy.test.ts +++ b/code/core/src/common/js-package-manager/Yarn1Proxy.test.ts @@ -25,21 +25,6 @@ describe('Yarn 1 Proxy', () => { }); }); - describe('setRegistryUrl', () => { - it('should run `yarn config set npmRegistryServer https://foo.bar`', async () => { - const executeCommandSpy = vi.spyOn(yarn1Proxy, 'executeCommand').mockResolvedValueOnce(''); - - await yarn1Proxy.setRegistryURL('https://foo.bar'); - - expect(executeCommandSpy).toHaveBeenCalledWith( - expect.objectContaining({ - command: 'npm', - args: ['config', 'set', 'registry', 'https://foo.bar'], - }) - ); - }); - }); - describe('installDependencies', () => { it('should run `yarn`', async () => { const executeCommandSpy = vi.spyOn(yarn1Proxy, 'executeCommand').mockResolvedValueOnce(''); diff --git a/code/core/src/common/js-package-manager/Yarn1Proxy.ts b/code/core/src/common/js-package-manager/Yarn1Proxy.ts index b193d4db4f15..44a8f0ca3d45 100644 --- a/code/core/src/common/js-package-manager/Yarn1Proxy.ts +++ b/code/core/src/common/js-package-manager/Yarn1Proxy.ts @@ -83,6 +83,15 @@ export class Yarn1Proxy extends JsPackageManager { return JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as Record; } + public async getRegistryURL() { + const res = await this.executeCommand({ + command: 'yarn', + args: ['config', 'get', 'registry'], + }); + const url = res.trim(); + return url === 'undefined' ? undefined : url; + } + public async findInstallations(pattern: string[], { depth = 99 }: { depth?: number } = {}) { const yarnArgs = ['list', '--pattern', pattern.map((p) => `"${p}"`).join(' '), '--json']; diff --git a/code/core/src/common/js-package-manager/Yarn2Proxy.test.ts b/code/core/src/common/js-package-manager/Yarn2Proxy.test.ts index 21656ff1ccd2..4e4441aedb4d 100644 --- a/code/core/src/common/js-package-manager/Yarn2Proxy.test.ts +++ b/code/core/src/common/js-package-manager/Yarn2Proxy.test.ts @@ -53,21 +53,6 @@ describe('Yarn 2 Proxy', () => { }); }); - describe('setRegistryUrl', () => { - it('should run `yarn config set npmRegistryServer https://foo.bar`', async () => { - const executeCommandSpy = vi.spyOn(yarn2Proxy, 'executeCommand').mockResolvedValueOnce(''); - - await yarn2Proxy.setRegistryURL('https://foo.bar'); - - expect(executeCommandSpy).toHaveBeenCalledWith( - expect.objectContaining({ - command: 'npm', - args: ['config', 'set', 'registry', 'https://foo.bar'], - }) - ); - }); - }); - describe('addDependencies', () => { it('with devDep it should run `yarn install -D @storybook/core`', async () => { const executeCommandSpy = vi.spyOn(yarn2Proxy, 'executeCommand').mockResolvedValueOnce(''); diff --git a/code/core/src/common/js-package-manager/Yarn2Proxy.ts b/code/core/src/common/js-package-manager/Yarn2Proxy.ts index 7917bc7e1ebd..317a56b40090 100644 --- a/code/core/src/common/js-package-manager/Yarn2Proxy.ts +++ b/code/core/src/common/js-package-manager/Yarn2Proxy.ts @@ -239,6 +239,15 @@ export class Yarn2Proxy extends JsPackageManager { await removeLogFile(); } + public async getRegistryURL() { + const res = await this.executeCommand({ + command: 'yarn', + args: ['config', 'get', 'npmRegistryServer'], + }); + const url = res.trim(); + return url === 'undefined' ? undefined : url; + } + protected async runRemoveDeps(dependencies: string[]) { const args = [...dependencies]; diff --git a/code/core/src/core-server/README.md b/code/core/src/core-server/README.md index 4af52cb89409..3dcc1dfcbac2 100644 --- a/code/core/src/core-server/README.md +++ b/code/core/src/core-server/README.md @@ -6,8 +6,8 @@ It contains: - CLI arg parsing - Storybook UI "manager" webpack configuration -- `start-storybook` dev server -- `build-storybook` static builder +- `storybook dev` dev server +- `storybook build` static builder - presets handling The "preview" (aka iframe) side is implemented in pluggable builders: diff --git a/code/frameworks/angular/src/client/angular-beta/ComputesTemplateFromComponent.test.ts b/code/frameworks/angular/src/client/angular-beta/ComputesTemplateFromComponent.test.ts index fca3ee9e37b4..cc12902b5c05 100644 --- a/code/frameworks/angular/src/client/angular-beta/ComputesTemplateFromComponent.test.ts +++ b/code/frameworks/angular/src/client/angular-beta/ComputesTemplateFromComponent.test.ts @@ -1,11 +1,314 @@ import { Component } from '@angular/core'; import { ArgTypes } from 'storybook/internal/types'; import { describe, it, expect } from 'vitest'; -import { computesTemplateSourceFromComponent } from './ComputesTemplateFromComponent'; +import { + computesTemplateFromComponent, + computesTemplateSourceFromComponent, +} from './ComputesTemplateFromComponent'; import { ISomeInterface, ButtonAccent, InputComponent } from './__testfixtures__/input.component'; +describe('angular template decorator', () => { + it('with props should generate tag with properties', () => { + const component = InputComponent; + const props = { + isDisabled: true, + label: 'Hello world', + accent: ButtonAccent.High, + counter: 4, + 'aria-label': 'Hello world', + }; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual( + `` + ); + }); + + it('with props should generate tag with outputs', () => { + const component = InputComponent; + const props = { + isDisabled: true, + label: 'Hello world', + onClick: ($event: any) => {}, + 'dash-out': ($event: any) => {}, + }; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual( + `` + ); + }); + + it('with no props should generate simple tag', () => { + const component = InputComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(''); + }); + + describe('with component without selector', () => { + @Component({ + template: `The content`, + }) + class WithoutSelectorComponent {} + + it('should add component ng-container', async () => { + const component = WithoutSelectorComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with attribute selector', () => { + @Component({ + selector: 'doc-button[foo]', + template: '', + }) + class WithAttributeComponent {} + + it('should add attribute to template', async () => { + const component = WithAttributeComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with attribute and value selector', () => { + @Component({ + selector: 'doc-button[foo="bar"]', + template: '', + }) + class WithAttributeValueComponent {} + + it('should add attribute to template', async () => { + const component = WithAttributeValueComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with attribute only selector', () => { + @Component({ + selector: '[foo]', + template: '', + }) + class WithAttributeOnlyComponent {} + + it('should create a div and add attribute to template', async () => { + const component = WithAttributeOnlyComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(`
`); + }); + }); + + describe('with component with void element and attribute selector', () => { + @Component({ + selector: 'input[foo]', + template: '', + }) + class VoidElementWithAttributeComponent {} + + it('should create without separate closing tag', async () => { + const component = VoidElementWithAttributeComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with attribute and value only selector', () => { + @Component({ + selector: '[foo="bar"]', + template: '', + }) + class WithAttributeOnlyComponent {} + + it('should create a div and add attribute to template', async () => { + const component = WithAttributeOnlyComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(`
`); + }); + }); + + describe('with component with void element, attribute and value only selector', () => { + @Component({ + selector: 'input[foo="bar"]', + template: '', + }) + class VoidElementWithAttributeComponent {} + + it('should create and add attribute to template without separate closing tag', async () => { + const component = VoidElementWithAttributeComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with class selector', () => { + @Component({ + selector: 'doc-button.foo', + template: '', + }) + class WithClassComponent {} + + it('should add class to template', async () => { + const component = WithClassComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with class only selector', () => { + @Component({ + selector: '.foo', + template: '', + }) + class WithClassComponent {} + + it('should create a div and add attribute to template', async () => { + const component = WithClassComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(`
`); + }); + }); + + describe('with component with multiple selectors', () => { + @Component({ + selector: 'doc-button, doc-button2', + template: '', + }) + class WithMultipleSelectorsComponent {} + + it('should use the first selector', async () => { + const component = WithMultipleSelectorsComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with multiple selectors starting with attribute', () => { + @Component({ + selector: 'doc-button[foo], doc-button2', + template: '', + }) + class WithMultipleSelectorsComponent {} + + it('should use the first selector', async () => { + const component = WithMultipleSelectorsComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with multiple selectors starting with attribute and value', () => { + @Component({ + selector: 'doc-button[foo="bar"], doc-button2', + template: '', + }) + class WithMultipleSelectorsComponent {} + + it('should use the first selector', async () => { + const component = WithMultipleSelectorsComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with multiple selectors including 2 attributes and a class', () => { + @Component({ + selector: 'doc-button, button[foo], .button[foo], button[baz]', + template: '', + }) + class WithMultipleSelectorsComponent {} + + it('should use the first selector', async () => { + const component = WithMultipleSelectorsComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with multiple selectors with line breaks', () => { + @Component({ + selector: `doc-button, + doc-button2`, + template: '', + }) + class WithMultipleSelectorsComponent {} + + it('should use the first selector', async () => { + const component = WithMultipleSelectorsComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); + }); + + describe('with component with multiple selectors starting with attribute only with line breaks', () => { + @Component({ + selector: `[foo], + doc-button2`, + template: '', + }) + class WithMultipleSelectorsComponent {} + + it('should use the first selector', async () => { + const component = WithMultipleSelectorsComponent; + const props = {}; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(`
`); + }); + }); + + it('with props should generate tag with properties', () => { + const component = InputComponent; + const props = { + isDisabled: true, + label: 'Hello world', + accent: ButtonAccent.High, + counter: 4, + }; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual( + `` + ); + }); + + it('with props should generate tag with outputs', () => { + const component = InputComponent; + const props = { + isDisabled: true, + label: 'Hello world', + onClick: ($event: any) => {}, + }; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual( + `` + ); + }); + + it('should generate correct property for overridden name for Input', () => { + const component = InputComponent; + const props = { + color: '#ffffff', + }; + const source = computesTemplateFromComponent(component, props); + expect(source).toEqual(``); + }); +}); + describe('angular source decorator', () => { - it('With no props should generate simple tag', () => { + it('with no props should generate simple tag', () => { const component = InputComponent; const props = {}; const argTypes: ArgTypes = {}; @@ -264,32 +567,34 @@ describe('angular source decorator', () => { const source = computesTemplateSourceFromComponent(component, props, argTypes); expect(source).toEqual(``); }); - it('With props should generate tag with properties', () => { + it('with props should generate tag with properties', () => { const component = InputComponent; const props = { isDisabled: true, label: 'Hello world', accent: ButtonAccent.High, counter: 4, + 'aria-label': 'Hello world', }; const argTypes: ArgTypes = {}; const source = computesTemplateSourceFromComponent(component, props, argTypes); expect(source).toEqual( - `` + `` ); }); - it('With props should generate tag with outputs', () => { + it('with props should generate tag with outputs', () => { const component = InputComponent; const props = { isDisabled: true, label: 'Hello world', onClick: ($event: any) => {}, + 'dash-out': ($event: any) => {}, }; const argTypes: ArgTypes = {}; const source = computesTemplateSourceFromComponent(component, props, argTypes); expect(source).toEqual( - `` + `` ); }); @@ -305,7 +610,7 @@ describe('angular source decorator', () => { }); describe('with argTypes (from compodoc)', () => { - it('Should handle enum as strongly typed enum', () => { + it('should handle enum as strongly typed enum', () => { const component = InputComponent; const props = { isDisabled: false, @@ -335,7 +640,7 @@ describe('angular source decorator', () => { ); }); - it('Should handle enum without values as string', () => { + it('should handle enum without values as string', () => { const component = InputComponent; const props = { isDisabled: false, @@ -365,7 +670,7 @@ describe('angular source decorator', () => { ); }); - it('Should handle objects correctly', () => { + it('should handle simple object as stringified', () => { const component = InputComponent; const someDataObject: ISomeInterface = { @@ -400,5 +705,42 @@ describe('angular source decorator', () => { `` ); }); + + it('should handle circular object as stringified', () => { + const component = InputComponent; + + const someDataObject: ISomeInterface = { + one: 'Hello world', + two: true, + three: [ + `a string literal with "double quotes"`, + `a string literal with 'single quotes'`, + 'a single quoted string with "double quotes"', + "a double quoted string with 'single quotes'", + // eslint-disable-next-line prettier/prettier + 'a single quoted string with escaped \'single quotes\'', + // eslint-disable-next-line prettier/prettier + "a double quoted string with escaped \"double quotes\"", + + `a string literal with \'escaped single quotes\'`, + + `a string literal with \"escaped double quotes\"`, + ], + }; + someDataObject.ref = someDataObject; + + const props = { + isDisabled: false, + label: 'Hello world', + someDataObject, + }; + + const source = computesTemplateSourceFromComponent(component, props, null); + // Ideally we should stringify the object, but that could cause the story to break because of unescaped values in the JSON object. + // This will have to do for now + expect(source).toEqual( + `` + ); + }); }); }); diff --git a/code/frameworks/angular/src/client/angular-beta/ComputesTemplateFromComponent.ts b/code/frameworks/angular/src/client/angular-beta/ComputesTemplateFromComponent.ts index 7c585b8ff489..9e0d38f17986 100644 --- a/code/frameworks/angular/src/client/angular-beta/ComputesTemplateFromComponent.ts +++ b/code/frameworks/angular/src/client/angular-beta/ComputesTemplateFromComponent.ts @@ -7,6 +7,20 @@ import { getComponentInputsOutputs, } from './utils/NgComponentAnalyzer'; +/** + * Check if the name matches the criteria for a valid identifier. + * A valid identifier can only contain letters, digits, underscores, or dollar signs. + * It cannot start with a digit. + */ +const isValidIdentifier = (name: string): boolean => /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name); + +/** + * Returns the property name, if it can be accessed with dot notation. If not, + * it returns `this['propertyName']`. + */ +export const formatPropInTemplate = (propertyName: string) => + isValidIdentifier(propertyName) ? propertyName : `this['${propertyName}']`; + const separateInputsOutputsAttributes = ( ngComponentInputsOutputs: ComponentInputsOutputs, props: ICollection = {} @@ -50,10 +64,12 @@ export const computesTemplateFromComponent = ( ); const templateInputs = - initialInputs.length > 0 ? ` ${initialInputs.map((i) => `[${i}]="${i}"`).join(' ')}` : ''; + initialInputs.length > 0 + ? ` ${initialInputs.map((i) => `[${i}]="${formatPropInTemplate(i)}"`).join(' ')}` + : ''; const templateOutputs = initialOutputs.length > 0 - ? ` ${initialOutputs.map((i) => `(${i})="${i}($event)"`).join(' ')}` + ? ` ${initialOutputs.map((i) => `(${i})="${formatPropInTemplate(i)}($event)"`).join(' ')}` : ''; return buildTemplate( @@ -64,6 +80,22 @@ export const computesTemplateFromComponent = ( ); }; +/** + * Stringify an object with a placholder in the circular references. + */ +function stringifyCircular(obj: any) { + const seen = new Set(); + return JSON.stringify(obj, (key, value) => { + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { + return '[Circular]'; + } + seen.add(value); + } + return value; + }); +} + const createAngularInputProperty = ({ propertyName, value, @@ -79,7 +111,7 @@ const createAngularInputProperty = ({ templateValue = `'${value}'`; break; case 'object': - templateValue = JSON.stringify(value) + templateValue = stringifyCircular(value) .replace(/'/g, '\u2019') .replace(/\\"/g, '\u201D') .replace(/"([^-"]+)":/g, '$1: ') @@ -137,7 +169,7 @@ export const computesTemplateSourceFromComponent = ( : ''; const templateOutputs = initialOutputs.length > 0 - ? ` ${initialOutputs.map((i) => `(${i})="${i}($event)"`).join(' ')}` + ? ` ${initialOutputs.map((i) => `(${i})="${formatPropInTemplate(i)}($event)"`).join(' ')}` : ''; return buildTemplate(ngComponentMetadata.selector, '', templateInputs, templateOutputs); diff --git a/code/frameworks/angular/src/client/angular-beta/__testfixtures__/input.component.ts b/code/frameworks/angular/src/client/angular-beta/__testfixtures__/input.component.ts index abf4205eeaf5..b0ae93c94b99 100644 --- a/code/frameworks/angular/src/client/angular-beta/__testfixtures__/input.component.ts +++ b/code/frameworks/angular/src/client/angular-beta/__testfixtures__/input.component.ts @@ -11,6 +11,7 @@ export interface ISomeInterface { one: string; two: boolean; three: any[]; + ref?: ISomeInterface; } @Component({ @@ -39,9 +40,13 @@ export class InputComponent { @Input() public label: string; + @Input('aria-label') public ariaLabel: string; + /** Specifies some arbitrary object */ @Input() public someDataObject: ISomeInterface; @Output() public onClick = new EventEmitter(); + + @Output('dash-out') public dashOut = new EventEmitter(); } diff --git a/code/frameworks/angular/src/client/argsToTemplate.test.ts b/code/frameworks/angular/src/client/argsToTemplate.test.ts index b7405aba645a..29a51acb1b9d 100644 --- a/code/frameworks/angular/src/client/argsToTemplate.test.ts +++ b/code/frameworks/angular/src/client/argsToTemplate.test.ts @@ -100,4 +100,10 @@ describe('argsToTemplate', () => { const result = argsToTemplate(args, {}); expect(result).toEqual('[input]="input" (event1)="event1($event)"'); }); + + it('should format for non dot notation', () => { + const args = { 'non-dot': 'Value1', 'dash-out': () => {} }; + const result = argsToTemplate(args, {}); + expect(result).toEqual('[non-dot]="this[\'non-dot\']" (dash-out)="this[\'dash-out\']($event)"'); + }); }); diff --git a/code/frameworks/angular/src/client/argsToTemplate.ts b/code/frameworks/angular/src/client/argsToTemplate.ts index 0072aa84743d..5b29b627029f 100644 --- a/code/frameworks/angular/src/client/argsToTemplate.ts +++ b/code/frameworks/angular/src/client/argsToTemplate.ts @@ -1,3 +1,5 @@ +import { formatPropInTemplate } from './angular-beta/ComputesTemplateFromComponent'; + /** * Options for controlling the behavior of the argsToTemplate function. * @@ -68,7 +70,9 @@ export function argsToTemplate>( return true; }) .map(([key, value]) => - typeof value === 'function' ? `(${key})="${key}($event)"` : `[${key}]="${key}"` + typeof value === 'function' + ? `(${key})="${formatPropInTemplate(key)}($event)"` + : `[${key}]="${formatPropInTemplate(key)}"` ) .join(' '); } diff --git a/code/lib/blocks/package.json b/code/lib/blocks/package.json index c02019bc00a2..0acb8450e13b 100644 --- a/code/lib/blocks/package.json +++ b/code/lib/blocks/package.json @@ -44,7 +44,7 @@ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@storybook/csf": "0.1.11", + "@storybook/csf": "^0.1.11", "@storybook/global": "^5.0.0", "@storybook/icons": "^1.2.5", "@types/lodash": "^4.14.167", diff --git a/code/lib/codemod/package.json b/code/lib/codemod/package.json index fc3a27cb61de..5c14d632cd36 100644 --- a/code/lib/codemod/package.json +++ b/code/lib/codemod/package.json @@ -58,7 +58,7 @@ "@babel/preset-env": "^7.24.4", "@babel/types": "^7.24.0", "@storybook/core": "workspace:*", - "@storybook/csf": "0.1.11", + "@storybook/csf": "^0.1.11", "@types/cross-spawn": "^6.0.2", "cross-spawn": "^7.0.3", "globby": "^14.0.1", diff --git a/code/lib/source-loader/package.json b/code/lib/source-loader/package.json index b4c0a48bef18..5c2963555975 100644 --- a/code/lib/source-loader/package.json +++ b/code/lib/source-loader/package.json @@ -45,7 +45,7 @@ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@storybook/csf": "0.1.11", + "@storybook/csf": "^0.1.11", "estraverse": "^5.2.0", "lodash": "^4.17.21", "prettier": "^3.1.1" diff --git a/code/lib/test/package.json b/code/lib/test/package.json index dee867c3a356..8fbcf1ba574f 100644 --- a/code/lib/test/package.json +++ b/code/lib/test/package.json @@ -44,7 +44,7 @@ "prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@storybook/csf": "0.1.11", + "@storybook/csf": "^0.1.11", "@storybook/instrumenter": "workspace:*", "@testing-library/dom": "10.1.0", "@testing-library/jest-dom": "6.4.5", diff --git a/code/package.json b/code/package.json index 526bfacf36ac..2814fd94ba64 100644 --- a/code/package.json +++ b/code/package.json @@ -118,7 +118,7 @@ "@storybook/codemod": "workspace:*", "@storybook/core": "workspace:*", "@storybook/core-webpack": "workspace:*", - "@storybook/csf": "0.1.11", + "@storybook/csf": "^0.1.11", "@storybook/csf-plugin": "workspace:*", "@storybook/ember": "workspace:*", "@storybook/eslint-config-storybook": "^4.0.0", @@ -206,7 +206,7 @@ "typescript": "^5.4.3", "util": "^0.12.4", "vite": "^4.0.0", - "vitest": "^1.2.2", + "vitest": "^1.6.0", "wait-on": "^7.0.1" }, "dependenciesMeta": { diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json index 1039c32b46af..3646540c0d07 100644 --- a/code/renderers/server/package.json +++ b/code/renderers/server/package.json @@ -47,7 +47,7 @@ }, "dependencies": { "@storybook/components": "workspace:^", - "@storybook/csf": "0.1.11", + "@storybook/csf": "^0.1.11", "@storybook/global": "^5.0.0", "@storybook/manager-api": "workspace:^", "@storybook/preview-api": "workspace:^", diff --git a/code/yarn.lock b/code/yarn.lock index ed97ee265727..1b831f85c140 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5287,7 +5287,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/addon-links@workspace:addons/links" dependencies: - "@storybook/csf": "npm:0.1.11" + "@storybook/csf": "npm:^0.1.11" "@storybook/global": "npm:^5.0.0" fs-extra: "npm:^11.1.0" ts-dedent: "npm:^2.0.0" @@ -5514,7 +5514,7 @@ __metadata: resolution: "@storybook/blocks@workspace:lib/blocks" dependencies: "@storybook/addon-actions": "workspace:*" - "@storybook/csf": "npm:0.1.11" + "@storybook/csf": "npm:^0.1.11" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.5" "@storybook/react": "workspace:*" @@ -5667,7 +5667,7 @@ __metadata: "@babel/preset-env": "npm:^7.24.4" "@babel/types": "npm:^7.24.0" "@storybook/core": "workspace:*" - "@storybook/csf": "npm:0.1.11" + "@storybook/csf": "npm:^0.1.11" "@types/cross-spawn": "npm:^6.0.2" "@types/jscodeshift": "npm:^0.11.10" ansi-regex: "npm:^6.0.1" @@ -5761,7 +5761,7 @@ __metadata: "@radix-ui/react-dialog": "npm:^1.0.5" "@radix-ui/react-scroll-area": "npm:^1.0.5" "@radix-ui/react-slot": "npm:^1.0.2" - "@storybook/csf": "npm:0.1.11" + "@storybook/csf": "npm:^0.1.11" "@storybook/docs-mdx": "npm:4.0.0-next.1" "@storybook/global": "npm:^5.0.0" "@storybook/icons": "npm:^1.2.5" @@ -5897,15 +5897,6 @@ __metadata: languageName: unknown linkType: soft -"@storybook/csf@npm:0.1.11": - version: 0.1.11 - resolution: "@storybook/csf@npm:0.1.11" - dependencies: - type-fest: "npm:^2.19.0" - checksum: 10c0/c5329fc13e7d762049b5c91df1bc1c0e510a1a898c401b72b68f1ff64139a85ab64a92f8e681d2fcb226c0a4a55d0f23b569b2bdb517e0f067bd05ea46228356 - languageName: node - linkType: hard - "@storybook/csf@npm:^0.0.1": version: 0.0.1 resolution: "@storybook/csf@npm:0.0.1" @@ -5915,6 +5906,15 @@ __metadata: languageName: node linkType: hard +"@storybook/csf@npm:^0.1.11": + version: 0.1.11 + resolution: "@storybook/csf@npm:0.1.11" + dependencies: + type-fest: "npm:^2.19.0" + checksum: 10c0/c5329fc13e7d762049b5c91df1bc1c0e510a1a898c401b72b68f1ff64139a85ab64a92f8e681d2fcb226c0a4a55d0f23b569b2bdb517e0f067bd05ea46228356 + languageName: node + linkType: hard + "@storybook/docs-mdx@npm:4.0.0-next.1": version: 4.0.0-next.1 resolution: "@storybook/docs-mdx@npm:4.0.0-next.1" @@ -6507,7 +6507,7 @@ __metadata: "@storybook/codemod": "workspace:*" "@storybook/core": "workspace:*" "@storybook/core-webpack": "workspace:*" - "@storybook/csf": "npm:0.1.11" + "@storybook/csf": "npm:^0.1.11" "@storybook/csf-plugin": "workspace:*" "@storybook/ember": "workspace:*" "@storybook/eslint-config-storybook": "npm:^4.0.0" @@ -6595,7 +6595,7 @@ __metadata: typescript: "npm:^5.4.3" util: "npm:^0.12.4" vite: "npm:^4.0.0" - vitest: "npm:^1.2.2" + vitest: "npm:^1.6.0" wait-on: "npm:^7.0.1" dependenciesMeta: ejs: @@ -6643,7 +6643,7 @@ __metadata: resolution: "@storybook/server@workspace:renderers/server" dependencies: "@storybook/components": "workspace:^" - "@storybook/csf": "npm:0.1.11" + "@storybook/csf": "npm:^0.1.11" "@storybook/global": "npm:^5.0.0" "@storybook/manager-api": "workspace:^" "@storybook/preview-api": "workspace:^" @@ -6662,7 +6662,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/source-loader@workspace:lib/source-loader" dependencies: - "@storybook/csf": "npm:0.1.11" + "@storybook/csf": "npm:^0.1.11" estraverse: "npm:^5.2.0" lodash: "npm:^4.17.21" prettier: "npm:^3.1.1" @@ -6768,7 +6768,7 @@ __metadata: version: 0.0.0-use.local resolution: "@storybook/test@workspace:lib/test" dependencies: - "@storybook/csf": "npm:0.1.11" + "@storybook/csf": "npm:^0.1.11" "@storybook/instrumenter": "workspace:*" "@testing-library/dom": "npm:10.1.0" "@testing-library/jest-dom": "npm:6.4.5" @@ -8487,34 +8487,25 @@ __metadata: languageName: node linkType: hard -"@vitest/runner@npm:1.2.2": - version: 1.2.2 - resolution: "@vitest/runner@npm:1.2.2" +"@vitest/runner@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/runner@npm:1.6.0" dependencies: - "@vitest/utils": "npm:1.2.2" + "@vitest/utils": "npm:1.6.0" p-limit: "npm:^5.0.0" pathe: "npm:^1.1.1" - checksum: 10c0/25a9c03cca5b40738fe606757b14ee9d60d25193115b4674e3cc402c2b2c3844d234902d48bfa7646cb205455ea27891fef96733e033a570b85fe74ed29ff81c + checksum: 10c0/27d67fa51f40effe0e41ee5f26563c12c0ef9a96161f806036f02ea5eb9980c5cdf305a70673942e7a1e3d472d4d7feb40093ae93024ef1ccc40637fc65b1d2f languageName: node linkType: hard -"@vitest/snapshot@npm:1.2.2": - version: 1.2.2 - resolution: "@vitest/snapshot@npm:1.2.2" +"@vitest/snapshot@npm:1.6.0": + version: 1.6.0 + resolution: "@vitest/snapshot@npm:1.6.0" dependencies: magic-string: "npm:^0.30.5" pathe: "npm:^1.1.1" pretty-format: "npm:^29.7.0" - checksum: 10c0/0f8a69a289aa6466c7dd56f8327190d56a0bc7ad10412127de001c94784f6dba5e5bccb757def21f565f4efa3e00c307b92e8b6c302f11fc57889b743ba18a95 - languageName: node - linkType: hard - -"@vitest/spy@npm:1.2.2": - version: 1.2.2 - resolution: "@vitest/spy@npm:1.2.2" - dependencies: - tinyspy: "npm:^2.2.0" - checksum: 10c0/5480048d26c0d82b524317552fbdcc05fed6ea626d887620647826453a344798a360f2a75af477512a1569b1b6c918eae62338e8b35575f875fc2d7ef51419f3 + checksum: 10c0/be027fd268d524589ff50c5fad7b4faa1ac5742b59ac6c1dc6f5a3930aad553560e6d8775e90ac4dfae4be746fc732a6f134ba95606a1519707ce70db3a772a5 languageName: node linkType: hard @@ -8527,18 +8518,6 @@ __metadata: languageName: node linkType: hard -"@vitest/utils@npm:1.2.2": - version: 1.2.2 - resolution: "@vitest/utils@npm:1.2.2" - dependencies: - diff-sequences: "npm:^29.6.3" - estree-walker: "npm:^3.0.3" - loupe: "npm:^2.3.7" - pretty-format: "npm:^29.7.0" - checksum: 10c0/32449cb7eca8ecea56e0fce280c9770f65fa6b60bbba73be06ca2891096818899b4b3220bd3c815df8beb4266034db394fcf235e4de8959cce686b8b360948d1 - languageName: node - linkType: hard - "@vitest/utils@npm:1.6.0, @vitest/utils@npm:^1.3.1": version: 1.6.0 resolution: "@vitest/utils@npm:1.6.0" @@ -17835,6 +17814,13 @@ __metadata: languageName: node linkType: hard +"js-tokens@npm:^9.0.0": + version: 9.0.0 + resolution: "js-tokens@npm:9.0.0" + checksum: 10c0/4ad1c12f47b8c8b2a3a99e29ef338c1385c7b7442198a425f3463f3537384dab6032012791bfc2f056ea5ecdb06b1ed4f70e11a3ab3f388d3dcebfe16a52b27d + languageName: node + linkType: hard + "js-yaml@npm:4.1.0, js-yaml@npm:^4.1.0": version: 4.1.0 resolution: "js-yaml@npm:4.1.0" @@ -25841,12 +25827,12 @@ __metadata: languageName: node linkType: hard -"strip-literal@npm:^1.3.0": - version: 1.3.0 - resolution: "strip-literal@npm:1.3.0" +"strip-literal@npm:^2.0.0": + version: 2.1.0 + resolution: "strip-literal@npm:2.1.0" dependencies: - acorn: "npm:^8.10.0" - checksum: 10c0/3c0c9ee41eb346e827eede61ef288457f53df30e3e6ff8b94fa81b636933b0c13ca4ea5c97d00a10d72d04be326da99ac819f8769f0c6407ba8177c98344a916 + js-tokens: "npm:^9.0.0" + checksum: 10c0/bc8b8c8346125ae3c20fcdaf12e10a498ff85baf6f69597b4ab2b5fbf2e58cfd2827f1a44f83606b852da99a5f6c8279770046ddea974c510c17c98934c9cc24 languageName: node linkType: hard @@ -26376,10 +26362,10 @@ __metadata: languageName: node linkType: hard -"tinypool@npm:^0.8.2": - version: 0.8.2 - resolution: "tinypool@npm:0.8.2" - checksum: 10c0/8998626614172fc37c394e9a14e701dc437727fc6525488a4d4fd42044a4b2b59d6f076d750cbf5c699f79c58dd4e40599ab09e2f1ae0df4b23516b98c9c3055 +"tinypool@npm:^0.8.3": + version: 0.8.4 + resolution: "tinypool@npm:0.8.4" + checksum: 10c0/779c790adcb0316a45359652f4b025958c1dff5a82460fe49f553c864309b12ad732c8288be52f852973bc76317f5e7b3598878aee0beb8a33322c0e72c4a66c languageName: node linkType: hard @@ -27756,9 +27742,9 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:1.2.2": - version: 1.2.2 - resolution: "vite-node@npm:1.2.2" +"vite-node@npm:1.6.0": + version: 1.6.0 + resolution: "vite-node@npm:1.6.0" dependencies: cac: "npm:^6.7.14" debug: "npm:^4.3.4" @@ -27767,7 +27753,7 @@ __metadata: vite: "npm:^5.0.0" bin: vite-node: vite-node.mjs - checksum: 10c0/39a5b9d9c806a012aab208eee0f59e4e12446ec19a4cf149a6459e7ff86491c289e189fda4f55a63b7e37d713f5edbda0e9efed95af4f7ebefa6d39eee093c0b + checksum: 10c0/0807e6501ac7763e0efa2b4bd484ce99fb207e92c98624c9f8999d1f6727ac026e457994260fa7fdb7060d87546d197081e46a705d05b0136a38b6f03715cbc2 languageName: node linkType: hard @@ -27863,17 +27849,16 @@ __metadata: languageName: node linkType: hard -"vitest@npm:^1.2.2": - version: 1.2.2 - resolution: "vitest@npm:1.2.2" +"vitest@npm:^1.6.0": + version: 1.6.0 + resolution: "vitest@npm:1.6.0" dependencies: - "@vitest/expect": "npm:1.2.2" - "@vitest/runner": "npm:1.2.2" - "@vitest/snapshot": "npm:1.2.2" - "@vitest/spy": "npm:1.2.2" - "@vitest/utils": "npm:1.2.2" + "@vitest/expect": "npm:1.6.0" + "@vitest/runner": "npm:1.6.0" + "@vitest/snapshot": "npm:1.6.0" + "@vitest/spy": "npm:1.6.0" + "@vitest/utils": "npm:1.6.0" acorn-walk: "npm:^8.3.2" - cac: "npm:^6.7.14" chai: "npm:^4.3.10" debug: "npm:^4.3.4" execa: "npm:^8.0.1" @@ -27882,17 +27867,17 @@ __metadata: pathe: "npm:^1.1.1" picocolors: "npm:^1.0.0" std-env: "npm:^3.5.0" - strip-literal: "npm:^1.3.0" + strip-literal: "npm:^2.0.0" tinybench: "npm:^2.5.1" - tinypool: "npm:^0.8.2" + tinypool: "npm:^0.8.3" vite: "npm:^5.0.0" - vite-node: "npm:1.2.2" + vite-node: "npm:1.6.0" why-is-node-running: "npm:^2.2.2" peerDependencies: "@edge-runtime/vm": "*" "@types/node": ^18.0.0 || >=20.0.0 - "@vitest/browser": ^1.0.0 - "@vitest/ui": ^1.0.0 + "@vitest/browser": 1.6.0 + "@vitest/ui": 1.6.0 happy-dom: "*" jsdom: "*" peerDependenciesMeta: @@ -27910,7 +27895,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 10c0/085cb62146191b32dc98fac1a5b0de6d1c63c44cc1e7946a7d38309dd4135539432ec27b4bfad38ce79736688a0ce20d9b93f58de4ce4a41677cb3c5ca6ad980 + checksum: 10c0/065da5b8ead51eb174d93dac0cd50042ca9539856dc25e340ea905d668c41961f7e00df3e388e6c76125b2c22091db2e8465f993d0f6944daf9598d549e562e7 languageName: node linkType: hard diff --git a/docs/api/main-config/main-config-build.mdx b/docs/api/main-config/main-config-build.mdx index 73d56d975273..d488fd06b0aa 100644 --- a/docs/api/main-config/main-config-build.mdx +++ b/docs/api/main-config/main-config-build.mdx @@ -31,7 +31,9 @@ Type: `TestBuildFlags` Configures Storybook's production builds for performance testing purposes by disabling certain features from the build. When running `build-storybook`, this feature is enabled by setting the `--test` [flag](../cli-options.mdx#build). - The options documented on this page are automatically enabled when the `--test` flag is provided to the `build-storybook` command. We encourage you to override these options only if you need to disable a specific feature for your project or if you are debugging a build issue. + + The options documented on this page are automatically enabled when the `--test` flag is provided to the [`storybook build`](../cli-options.mdx#build) command. We encourage you to override these options only if you need to disable a specific feature for your project or if you are debugging a build issue. + ### `test.disableBlocks` diff --git a/docs/writing-docs/build-documentation.mdx b/docs/writing-docs/build-documentation.mdx index b32752fce3eb..19f052996a4d 100644 --- a/docs/writing-docs/build-documentation.mdx +++ b/docs/writing-docs/build-documentation.mdx @@ -33,7 +33,7 @@ There's some caveats to this build mode, as to the normal Storybook build: ## Publish Storybook's documentation -You can also publish your documentation, the same you would [publish](../sharing/publish-storybook.mdx) your Storybook. You can use the `--docs` flag with `build-storybook` command. We recommend as well including it as a script in your `package.json` file: +You can also publish your documentation the same you would [publish](../sharing/publish-storybook.mdx) your Storybook. You can use the `--docs` flag with the [`storybook build`](../api/cli-options.mdx#build) command. We recommend as well including it as a script in your `package.json` file: ```json { diff --git a/scripts/sandbox/generate.ts b/scripts/sandbox/generate.ts index 22c50716cc81..88871e6eec4d 100755 --- a/scripts/sandbox/generate.ts +++ b/scripts/sandbox/generate.ts @@ -45,18 +45,34 @@ const sbInit = async ( await runCommand(`${sbCliBinaryPath} init ${fullFlags.join(' ')}`, { cwd, env }, debug); }; -const withLocalRegistry = async (packageManager: JsPackageManager, action: () => Promise) => { +type LocalRegistryProps = { + packageManager: JsPackageManager; + action: () => Promise; + cwd: string; + env: Record; + debug: boolean; +}; + +const withLocalRegistry = async ({ + packageManager, + action, + cwd, + env, + debug, +}: LocalRegistryProps) => { const prevUrl = await packageManager.getRegistryURL(); let error; try { console.log(`📦 Configuring local registry: ${LOCAL_REGISTRY_URL}`); - packageManager.setRegistryURL(LOCAL_REGISTRY_URL); + // NOTE: for some reason yarn prefers the npm registry in + // local development, so always use npm + await runCommand(`npm config set registry ${LOCAL_REGISTRY_URL}`, { cwd, env }, debug); await action(); } catch (e) { error = e; } finally { console.log(`📦 Restoring registry: ${prevUrl}`); - await packageManager.setRegistryURL(prevUrl); + await runCommand(`npm config set registry ${prevUrl}`, { cwd, env }, debug); if (error) { throw error; @@ -88,14 +104,20 @@ const addStorybook = async ({ const packageManager = JsPackageManagerFactory.getPackageManager({ force: 'yarn1' }, tmpDir); if (localRegistry) { - await withLocalRegistry(packageManager, async () => { - await packageManager.addPackageResolutions({ - ...storybookVersions, - // Yarn1 Issue: https://github.com/storybookjs/storybook/issues/22431 - jackspeak: '2.1.1', - }); - - await sbInit(tmpDir, env, [...flags, '--package-manager=yarn1'], debug); + await withLocalRegistry({ + packageManager, + action: async () => { + await packageManager.addPackageResolutions({ + ...storybookVersions, + // Yarn1 Issue: https://github.com/storybookjs/storybook/issues/22431 + jackspeak: '2.1.1', + }); + + await sbInit(tmpDir, env, [...flags, '--package-manager=yarn1'], debug); + }, + cwd: tmpDir, + env, + debug, }); } else { await sbInit(tmpDir, env, [...flags, '--package-manager=yarn1'], debug); @@ -159,7 +181,7 @@ const runGenerators = async ( const baseDir = join(REPROS_DIRECTORY, dirName); const beforeDir = join(baseDir, BEFORE_DIR_NAME); try { - let flags: string[] = []; + let flags: string[] = ['--no-dev']; if (expected.renderer === '@storybook/html') flags = ['--type html']; else if (expected.renderer === '@storybook/server') flags = ['--type server']; diff --git a/test-storybooks/portable-stories-kitchen-sink/svelte/package.json b/test-storybooks/portable-stories-kitchen-sink/svelte/package.json index fecb60d35bfc..dd8841800a3f 100644 --- a/test-storybooks/portable-stories-kitchen-sink/svelte/package.json +++ b/test-storybooks/portable-stories-kitchen-sink/svelte/package.json @@ -99,6 +99,6 @@ "tslib": "^2.6.2", "typescript": "^5.2.2", "vite": "^5.1.4", - "vitest": "^1.3.1" + "vitest": "^1.6.0" } } \ No newline at end of file