diff --git a/code/core/src/manager-api/modules/versions.ts b/code/core/src/manager-api/modules/versions.ts index 89a84be2072a..c3b2f38ddcdc 100644 --- a/code/core/src/manager-api/modules/versions.ts +++ b/code/core/src/manager-api/modules/versions.ts @@ -49,17 +49,19 @@ export interface SubAPI { * Returns the URL of the Storybook documentation for the current version. * * @param options - The options for the documentation URL. - * @param options.asset - Links to the docs-assets directory instead of docs. + * @param options.asset - Like subpath, but links to the docs-assets directory. * @param options.subpath - The subpath of the documentation URL. * @param options.versioned - Whether to include the versioned path. * @param options.renderer - Whether to include the renderer path. + * @param options.ref - Tracking reference for the docs site. E.g. 'ui', 'error', 'upgrade', etc. * @returns {string} The URL of the Storybook Manager documentation. */ getDocsUrl: (options: { - asset?: boolean; + asset?: string; subpath?: string; versioned?: boolean; renderer?: boolean; + ref?: string; }) => string; /** * Checks if an update is available for the Storybook Manager. @@ -99,7 +101,7 @@ export const init: ModuleFn = ({ store }) => { return latest as API_Version; }, // TODO: Move this to it's own "info" module later - getDocsUrl: ({ asset, subpath, versioned, renderer }) => { + getDocsUrl: ({ asset, subpath = asset, versioned, renderer, ref = 'ui' }) => { const { versions: { latest, current }, } = store.getState(); @@ -135,6 +137,10 @@ export const init: ModuleFn = ({ store }) => { } } + if (ref) { + url += `${url.includes('?') ? '&' : '?'}ref=${ref}`; + } + if (hash) { url += `#${hash}`; } diff --git a/code/core/src/manager-api/tests/versions.test.js b/code/core/src/manager-api/tests/versions.test.js index 9ccf0de55fdd..5f3895bd0ce0 100644 --- a/code/core/src/manager-api/tests/versions.test.js +++ b/code/core/src/manager-api/tests/versions.test.js @@ -48,6 +48,38 @@ function createMockStore() { vi.mock('storybook/internal/client-logger'); +const latest = { + current: { version: '7.6.1' }, + latest: { version: '7.6.1' }, +}; +const patchDiff = { + current: { version: '7.6.1' }, + latest: { version: '7.6.10' }, +}; +const minorDiff = { + current: { version: '7.2.5' }, + latest: { version: '7.6.10' }, +}; +const majorDiff = { + current: { version: '6.2.1' }, + latest: { version: '7.6.10' }, +}; +const newerPrerelease = { + current: { version: '8.0.0-beta' }, + latest: { version: '7.6.10' }, +}; +const olderPrerelease = { + current: { version: '5.2.1-prerelease.0' }, + latest: { version: '6.2.1' }, +}; +const prereleaseAvailable = { + current: { version: '5.2.1' }, + latest: { version: '6.2.1-prerelease.0' }, +}; + +const setVersions = (store, state, versions) => + store.setState({ ...state, versions: { ...state.versions, ...versions } }); + describe('versions API', () => { it('sets initial state with current version', async () => { const store = createMockStore(); @@ -72,9 +104,8 @@ describe('versions API', () => { it('sets versions in the init function', async () => { const store = createMockStore(); - const { state: initialState, init } = initVersions({ - store, - }); + const { state: initialState, init } = initVersions({ store }); + store.setState(initialState); store.setState.mockReset(); @@ -91,13 +122,8 @@ describe('versions API', () => { it('getCurrentVersion works', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); + store.setState(initialState); await init(); @@ -109,13 +135,8 @@ describe('versions API', () => { it('getLatestVersion works', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); + store.setState(initialState); await init(); @@ -132,174 +153,101 @@ describe('versions API', () => { it('returns the latest url when current version is latest', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '7.6.1' }, - latest: { version: '7.6.1' }, - }, - }); + setVersions(store, initialState, latest); - expect(api.getDocsUrl({ versioned: true })).toEqual('https://storybook.js.org/docs/'); + expect(api.getDocsUrl({ versioned: true })).toEqual('https://storybook.js.org/docs/?ref=ui'); }); it('returns the latest url when version has patch diff with latest', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '7.6.1' }, - latest: { version: '7.6.10' }, - }, - }); + setVersions(store, initialState, patchDiff); - expect(api.getDocsUrl({ versioned: true })).toEqual('https://storybook.js.org/docs/'); + expect(api.getDocsUrl({ versioned: true })).toEqual('https://storybook.js.org/docs/?ref=ui'); }); it('returns the versioned url when current has different docs to latest', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '7.2.5' }, - latest: { version: '7.6.10' }, - }, - }); + setVersions(store, initialState, minorDiff); - expect(api.getDocsUrl({ versioned: true })).toEqual('https://storybook.js.org/docs/7.2/'); + expect(api.getDocsUrl({ versioned: true })).toEqual( + 'https://storybook.js.org/docs/7.2/?ref=ui' + ); }); it('returns the versioned url when current is a prerelease', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '8.0.0-beta' }, - latest: { version: '7.6.10' }, - }, - }); + setVersions(store, initialState, newerPrerelease); - expect(api.getDocsUrl({ versioned: true })).toEqual('https://storybook.js.org/docs/8.0/'); + expect(api.getDocsUrl({ versioned: true })).toEqual( + 'https://storybook.js.org/docs/8.0/?ref=ui' + ); }); it('returns a url with a renderer query param when "renderer" is true', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '5.2.1' }, - latest: { version: '5.2.1' }, - }, - }); + const { init, api, state: initialState } = initVersions({ store }); + + setVersions(store, initialState, latest); await init(); global.STORYBOOK_RENDERER = 'vue'; expect(api.getDocsUrl({ renderer: true })).toEqual( - 'https://storybook.js.org/docs/?renderer=vue' + 'https://storybook.js.org/docs/?renderer=vue&ref=ui' ); }); - it('returns a url with assets path when "asset" is true', async () => { + it('returns a url with a custom ref query param when provided', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '7.6.1' }, - latest: { version: '7.6.1' }, - }, - }); + setVersions(store, initialState, latest); + + expect(api.getDocsUrl({ ref: 'custom' })).toEqual( + 'https://storybook.js.org/docs/?ref=custom' + ); + }); + + it('returns a url without ref query param when empty', async () => { + const store = createMockStore(); + const { init, api, state: initialState } = initVersions({ store }); + + await init(); - expect(api.getDocsUrl({ asset: true })).toEqual('https://storybook.js.org/docs-assets/7.6/'); + setVersions(store, initialState, latest); + + expect(api.getDocsUrl({ ref: '' })).toEqual('https://storybook.js.org/docs/'); }); - it('returns a url with subpath when provided', async () => { + it('returns a versioned url with assets path when provided', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '7.2.5' }, - latest: { version: '7.6.10' }, - }, - }); + setVersions(store, initialState, latest); - expect(api.getDocsUrl({ asset: true, subpath: 'api/doc-block-controls.png' })).toEqual( - 'https://storybook.js.org/docs-assets/7.2/api/doc-block-controls.png' + expect(api.getDocsUrl({ asset: 'api/doc-block-controls.png' })).toEqual( + 'https://storybook.js.org/docs-assets/7.6/api/doc-block-controls.png?ref=ui' ); }); }); @@ -307,21 +255,9 @@ describe('versions API', () => { describe('versionUpdateAvailable', () => { it('matching version', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '5.2.1' }, - latest: { version: '5.2.1' }, - }, - }); + const { init, api, state: initialState } = initVersions({ store }); + + setVersions(store, initialState, latest); await init(); @@ -330,21 +266,9 @@ describe('versions API', () => { it('new patch version', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '5.2.1' }, - latest: { version: '5.2.2' }, - }, - }); + const { init, api, state: initialState } = initVersions({ store }); + + setVersions(store, initialState, patchDiff); await init(); @@ -353,72 +277,33 @@ describe('versions API', () => { it('new minor version', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '5.2.1' }, - latest: { version: '5.3.1' }, - }, - }); + setVersions(store, initialState, minorDiff); expect(api.versionUpdateAvailable()).toEqual(true); }); it('new major version', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '5.2.1' }, - latest: { version: '6.2.1' }, - }, - }); + setVersions(store, initialState, majorDiff); expect(api.versionUpdateAvailable()).toEqual(true); }); it('new prerelease version', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '5.2.1' }, - latest: { version: '6.2.1-prerelease.0' }, - }, - }); + setVersions(store, initialState, prereleaseAvailable); expect(api.versionUpdateAvailable()).toEqual(false); }); @@ -429,38 +314,18 @@ describe('versions API', () => { await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '5.2.1-prerelease.0' }, - latest: { version: '6.2.1' }, - }, - }); + setVersions(store, initialState, olderPrerelease); expect(api.versionUpdateAvailable()).toEqual(true); }); it('from newer prerelease version', async () => { const store = createMockStore(); - const { - init, - api, - state: initialState, - } = initVersions({ - store, - }); + const { init, api, state: initialState } = initVersions({ store }); await init(); - store.setState({ - ...initialState, - versions: { - ...initialState.versions, - current: { version: '5.2.1-prerelease.0' }, - latest: { version: '3.2.1' }, - }, - }); + setVersions(store, initialState, newerPrerelease); expect(api.versionUpdateAvailable()).toEqual(false); }); diff --git a/code/core/src/shared/checklist-store/checklistData.tsx b/code/core/src/shared/checklist-store/checklistData.tsx index 20c5d23bdf22..eac4cadd3821 100644 --- a/code/core/src/shared/checklist-store/checklistData.tsx +++ b/code/core/src/shared/checklist-store/checklistData.tsx @@ -196,14 +196,22 @@ export const checklistData = {
Rendering your components can often require{' '} setting up surrounding context in decorators {' '} or{' '} applying global styles @@ -242,7 +250,11 @@ export const Primary: Story = {
@@ -264,7 +276,10 @@ export const Primary: Story = { Autocomplete, or even full pages.
@@ -272,6 +287,7 @@ export const Primary: Story = { href={api.getDocsUrl({ subpath: 'get-started/whats-a-story#create-a-new-story', renderer: true, + ref: 'guide', })} target="_blank" withArrow @@ -302,8 +318,8 @@ export const Primary: Story = {
Read the{' '} Controls documentation @@ -398,14 +422,21 @@ export const Primary: Story = { built-in support for previewing stories in various device sizes.
Read the{' '} Viewports documentation @@ -449,7 +480,10 @@ export default {
Which would look like:
Expand the test widget and click the Run visual tests button.
introduction.mdx file and (using markdown and
Storybook's{' '}
doc blocks
@@ -1138,6 +1188,7 @@ npm install @my/awesome-project
href={api.getDocsUrl({
subpath: 'writing-docs/mdx',
renderer: true,
+ ref: 'guide',
})}
target="_blank"
>
@@ -1179,8 +1230,8 @@ npm install @my/awesome-project