diff --git a/CHANGELOG.md b/CHANGELOG.md index 55cbd250828e..13751f6c92f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 10.2.3 + +- Addon-Vitest: Normalize Windows paths in addon-vitest automigration - [#33340](https://github.com/storybookjs/storybook/pull/33340), thanks @tanujbhaud! +- Core: Fix `previewHref` when current path does not end with a slash - [#33647](https://github.com/storybookjs/storybook/pull/33647), thanks @ghengeveld! + ## 10.2.2 - Addon Vitest: Support simple vite.config without defineConfig helper - [#33694](https://github.com/storybookjs/storybook/pull/33694), thanks @valentinpalkovic! diff --git a/code/addons/vitest/src/updateVitestFile.test.ts b/code/addons/vitest/src/updateVitestFile.test.ts index c316e0f281e2..89cfe368a6c1 100644 --- a/code/addons/vitest/src/updateVitestFile.test.ts +++ b/code/addons/vitest/src/updateVitestFile.test.ts @@ -1176,7 +1176,7 @@ describe('updateWorkspaceFile', () => { + + // More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon + export default ['packages/*', 'ROOT_CONFIG', { - + extends: '', + + extends: '.', + plugins: [ + // The plugin will run tests for the stories defined in your Storybook config + // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest @@ -1235,7 +1235,7 @@ describe('updateWorkspaceFile', () => { + + // More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon + export default defineWorkspace(['packages/*', 'ROOT_CONFIG', { - + extends: '', + + extends: '.', + plugins: [ + // The plugin will run tests for the stories defined in your Storybook config + // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest @@ -1258,3 +1258,32 @@ describe('updateWorkspaceFile', () => { `); }); }); + +describe('loadTemplate', () => { + it('normalizes Windows paths to forward slashes', async () => { + // Windows-style path with backslashes (need to escape them in JS strings) + const windowsPath = '.\\apps\\frontend-storybook\\.storybook'; + + const result = await loadTemplate('vitest.config.template.ts', { + CONFIG_DIR: windowsPath, + SETUP_FILE: '.\\apps\\frontend-storybook\\.storybook\\vitest.setup.ts', + }); + + // Should contain forward slashes, not backslashes + expect(result).toContain('apps/frontend-storybook/.storybook'); + expect(result).not.toContain('\\apps\\'); + }); + + it('preserves forward slashes in paths', async () => { + // Unix-style path with forward slashes + const unixPath = './apps/frontend-storybook/.storybook'; + + const result = await loadTemplate('vitest.config.template.ts', { + CONFIG_DIR: unixPath, + SETUP_FILE: './apps/frontend-storybook/.storybook/vitest.setup.ts', + }); + + // Should still contain forward slashes + expect(result).toContain('apps/frontend-storybook/.storybook'); + }); +}); diff --git a/code/addons/vitest/src/updateVitestFile.ts b/code/addons/vitest/src/updateVitestFile.ts index 567df3de2114..46661c099849 100644 --- a/code/addons/vitest/src/updateVitestFile.ts +++ b/code/addons/vitest/src/updateVitestFile.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs/promises'; import type { BabelFile, types as t } from 'storybook/internal/babel'; -import { join } from 'pathe'; +import { join, normalize } from 'pathe'; import { resolvePackageDir } from '../../../core/src/shared/utils/module'; @@ -11,7 +11,10 @@ export const loadTemplate = async (name: string, replacements: Record (template = template.replace(key, value))); + // Normalize Windows paths (backslashes) to forward slashes for JavaScript string compatibility + Object.entries(replacements).forEach( + ([key, value]) => (template = template.replace(key, normalize(value))) + ); return template; }; diff --git a/code/core/src/manager-api/modules/url.ts b/code/core/src/manager-api/modules/url.ts index 83633a00de4e..b7f37e179e59 100644 --- a/code/core/src/manager-api/modules/url.ts +++ b/code/core/src/manager-api/modules/url.ts @@ -247,13 +247,14 @@ export const init: ModuleFn = (moduleArgs) => { throw new Error(`Invalid refId: ${refId}`); } - const originAddress = global.window.location.origin + location.pathname; + const pathname = location.pathname || '/'; + const originAddress = global.window.location.origin + pathname; const networkAddress = global.STORYBOOK_NETWORK_ADDRESS ?? originAddress; const managerBase = - base === 'origin' ? originAddress : base === 'network' ? networkAddress : location.pathname; + base === 'origin' ? originAddress : base === 'network' ? networkAddress : pathname; const previewBase = refId ? refs[refId].url + '/iframe.html' - : global.PREVIEW_URL || `${managerBase}iframe.html`; + : global.PREVIEW_URL || `${managerBase.replace(/\/[^/]*$/, '/')}iframe.html`; const refParam = refId ? `&refId=${encodeURIComponent(refId)}` : ''; const { args = '', globals = '', ...otherParams } = queryParams; diff --git a/code/core/src/manager-api/tests/url.test.js b/code/core/src/manager-api/tests/url.test.js index a32e062cbd31..aedbc591d13e 100644 --- a/code/core/src/manager-api/tests/url.test.js +++ b/code/core/src/manager-api/tests/url.test.js @@ -469,5 +469,21 @@ describe('getStoryHrefs', () => { const { managerHref, previewHref } = api.getStoryHrefs('test--story'); expect(managerHref).toEqual('/?path=/story/test--story'); expect(previewHref).toEqual('https://custom.preview.url/?id=test--story&viewMode=story'); + delete global.PREVIEW_URL; + }); + + it('correctly links from /index.html', () => { + const { api, state } = initURL({ + store, + provider: { channel: new EventEmitter() }, + state: { location: { pathname: '/index.html', search: '' } }, + navigate: vi.fn(), + fullAPI: { getCurrentStoryData: () => ({ id: 'test--story' }) }, + }); + store.setState(state); + + const { managerHref, previewHref } = api.getStoryHrefs('test--story'); + expect(managerHref).toEqual('/index.html?path=/story/test--story'); + expect(previewHref).toEqual('/iframe.html?id=test--story&viewMode=story'); }); }); diff --git a/code/package.json b/code/package.json index 7093a5d20551..1c58cff91205 100644 --- a/code/package.json +++ b/code/package.json @@ -220,5 +220,6 @@ "Dependency Upgrades" ] ] - } + }, + "deferredNextVersion": "10.2.3" } diff --git a/docs/versions/latest.json b/docs/versions/latest.json index de67f2eb604e..1ee94b9180e2 100644 --- a/docs/versions/latest.json +++ b/docs/versions/latest.json @@ -1 +1 @@ -{"version":"10.2.2","info":{"plain":"- Addon Vitest: Support simple vite.config without defineConfig helper - [#33694](https://github.com/storybookjs/storybook/pull/33694), thanks @valentinpalkovic!\n- Addon-Vitest: Append Storybook project to existing test.projects array without double nesting - [#33708](https://github.com/storybookjs/storybook/pull/33708), thanks @valentinpalkovic!\n- Addon-Vitest: Update Vitest plugin configuration to disable requireAssertions for expect - [#33693](https://github.com/storybookjs/storybook/pull/33693), thanks @valentinpalkovic!\n- Composition: Handle 401 responses with loginUrl from Chromatic - [#33705](https://github.com/storybookjs/storybook/pull/33705), thanks @kasperpeulen!\n- Telemetry: Add agent detection - [#33675](https://github.com/storybookjs/storybook/pull/33675), thanks @valentinpalkovic!"}} \ No newline at end of file +{"version":"10.2.3","info":{"plain":"- Addon-Vitest: Normalize Windows paths in addon-vitest automigration - [#33340](https://github.com/storybookjs/storybook/pull/33340), thanks @tanujbhaud!\n- Core: Fix `previewHref` when current path does not end with a slash - [#33647](https://github.com/storybookjs/storybook/pull/33647), thanks @ghengeveld!"}} \ No newline at end of file