From 2341c67ac65c388603e66f078f17c92224e66f6c Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Wed, 28 Dec 2022 21:14:37 +1100 Subject: [PATCH 01/29] Show a "booting" progress message until the story is specified (Or an error occurs). --- code/lib/manager-api/src/modules/stories.ts | 8 ++++++-- code/lib/manager-api/src/tests/stories.test.ts | 18 ++++++++++++++++++ .../manager/src/components/preview/preview.tsx | 12 ++++++++++-- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index d9a70c6c56da..25e5ff8ec95e 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -54,6 +54,7 @@ export interface SubState { storiesHash: API_StoriesHash; storyId: StoryId; viewMode: ViewMode; + storySpecified: boolean; storiesConfigured: boolean; storiesFailed?: Error; } @@ -387,9 +388,11 @@ export const init: ModuleFn = ({ }) { const { sourceType } = getEventMetadata(this, fullAPI); - if (fullAPI.isSettingsScreenActive()) return; - if (sourceType === 'local') { + store.setState({ storySpecified: true }); + + if (fullAPI.isSettingsScreenActive()) return; + // Special case -- if we are already at the story being specified (i.e. the user started at a given story), // we don't need to change URL. See https://github.com/storybookjs/storybook/issues/11677 const state = store.getState(); @@ -518,6 +521,7 @@ export const init: ModuleFn = ({ storiesHash: {}, storyId: initialStoryId, viewMode: initialViewMode, + storySpecified: false, storiesConfigured: false, hasCalledSetOptions: false, }, diff --git a/code/lib/manager-api/src/tests/stories.test.ts b/code/lib/manager-api/src/tests/stories.test.ts index f7926cd1e4b2..613529b9c182 100644 --- a/code/lib/manager-api/src/tests/stories.test.ts +++ b/code/lib/manager-api/src/tests/stories.test.ts @@ -107,6 +107,7 @@ describe('stories API', () => { } as ModuleArgs); expect(state).toEqual({ + storySpecified: false, storiesConfigured: false, storiesHash: {}, storyId: 'id', @@ -617,6 +618,23 @@ describe('stories API', () => { // Can't currently run these tests as cannot set this on the events describe('STORY_SPECIFIED event', () => { + it('sets the storySpecified state', async () => { + const navigate = jest.fn(); + const fullAPI = Object.assign(new EventEmitter(), { + isSettingsScreenActive() { + return false; + }, + }); + const store = createMockStore({}); + const { init, api } = initStories({ store, navigate, provider, fullAPI } as any); + + Object.assign(fullAPI, api); + init(); + fullAPI.emit(STORY_SPECIFIED, { storyId: 'a--1', viewMode: 'story' }); + + expect(store.getState().storySpecified).toBe(true); + }); + it('navigates to the story', async () => { const navigate = jest.fn(); const fullAPI = Object.assign(new EventEmitter(), { diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx index e9d5a21ddf68..1ea9bd819e65 100644 --- a/code/ui/manager/src/components/preview/preview.tsx +++ b/code/ui/manager/src/components/preview/preview.tsx @@ -37,6 +37,7 @@ const canvasMapper = ({ state, api }: Combo) => ({ queryParams: state.customQueryParams, getElements: api.getElements, entry: api.getData(state.storyId, state.refId), + storySpecified: state.storySpecified, storiesConfigured: state.storiesConfigured, storiesFailed: state.storiesFailed, refs: state.refs, @@ -60,6 +61,7 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A viewMode, queryParams, getElements, + storySpecified, storiesConfigured, storiesFailed, active, @@ -90,15 +92,21 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A const isLoading = entry ? refLoading || rootLoading : (!storiesFailed && !storiesConfigured) || rootLoading; + const isBooting = !storySpecified && !storiesFailed; + console.log({ withLoader, isLoading, isBooting, progress }); return ( {({ value: scale }) => { return ( <> - {withLoader && isLoading && ( + {withLoader && (isLoading || isBooting) && ( - + )} Date: Thu, 29 Dec 2022 16:46:44 +1100 Subject: [PATCH 02/29] Apply suggestions from code review Co-authored-by: Jeppe Reinhold --- code/ui/manager/src/components/preview/preview.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx index 1ea9bd819e65..071f804f1d1e 100644 --- a/code/ui/manager/src/components/preview/preview.tsx +++ b/code/ui/manager/src/components/preview/preview.tsx @@ -94,7 +94,6 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A : (!storiesFailed && !storiesConfigured) || rootLoading; const isBooting = !storySpecified && !storiesFailed; - console.log({ withLoader, isLoading, isBooting, progress }); return ( {({ value: scale }) => { @@ -105,7 +104,7 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A )} From 4cdecb98ff7d898033be0f5424b72e76c1cc2bf1 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 29 Dec 2022 16:56:39 +1100 Subject: [PATCH 03/29] Don't show a progressbar if booting --- code/ui/manager/src/components/preview/preview.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx index 071f804f1d1e..86b56c53174a 100644 --- a/code/ui/manager/src/components/preview/preview.tsx +++ b/code/ui/manager/src/components/preview/preview.tsx @@ -72,7 +72,6 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A ); const [progress, setProgress] = useState(undefined); - useEffect(() => { if (FEATURES?.storyStoreV7 && global.CONFIG_TYPE === 'DEVELOPMENT') { try { @@ -101,11 +100,7 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A <> {withLoader && (isLoading || isBooting) && ( - + )} Date: Thu, 29 Dec 2022 16:58:52 +1100 Subject: [PATCH 04/29] Simplify things further --- code/ui/manager/src/components/preview/preview.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx index 86b56c53174a..338ddba5673c 100644 --- a/code/ui/manager/src/components/preview/preview.tsx +++ b/code/ui/manager/src/components/preview/preview.tsx @@ -88,10 +88,10 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A const refLoading = !!refs[refId] && !refs[refId].ready; const rootLoading = !refId && !(progress?.value === 1 || progress === undefined); - const isLoading = entry - ? refLoading || rootLoading - : (!storiesFailed && !storiesConfigured) || rootLoading; const isBooting = !storySpecified && !storiesFailed; + const isLoading = entry + ? isBooting || refLoading || rootLoading + : isBooting || rootLoading; return ( From e91aa76cc52158c96f6057be97dc737e94108eb5 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 29 Dec 2022 17:04:41 +1100 Subject: [PATCH 05/29] Simplify a little --- code/ui/manager/src/components/preview/preview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx index 338ddba5673c..0f382b971aa3 100644 --- a/code/ui/manager/src/components/preview/preview.tsx +++ b/code/ui/manager/src/components/preview/preview.tsx @@ -86,9 +86,9 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A } }, []); + const isBooting = !storySpecified && !storiesFailed; const refLoading = !!refs[refId] && !refs[refId].ready; const rootLoading = !refId && !(progress?.value === 1 || progress === undefined); - const isBooting = !storySpecified && !storiesFailed; const isLoading = entry ? isBooting || refLoading || rootLoading : isBooting || rootLoading; @@ -98,7 +98,7 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A {({ value: scale }) => { return ( <> - {withLoader && (isLoading || isBooting) && ( + {withLoader && isLoading && ( From 8756b5d0af76f7bd02baad69e6649774e3b34da5 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 29 Dec 2022 17:06:08 +1100 Subject: [PATCH 06/29] Simplify --- code/ui/manager/src/components/preview/preview.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx index 0f382b971aa3..6ca0302877b8 100644 --- a/code/ui/manager/src/components/preview/preview.tsx +++ b/code/ui/manager/src/components/preview/preview.tsx @@ -38,7 +38,6 @@ const canvasMapper = ({ state, api }: Combo) => ({ getElements: api.getElements, entry: api.getData(state.storyId, state.refId), storySpecified: state.storySpecified, - storiesConfigured: state.storiesConfigured, storiesFailed: state.storiesFailed, refs: state.refs, active: !!(state.viewMode && state.viewMode.match(/^(story|docs)$/)), @@ -62,7 +61,6 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A queryParams, getElements, storySpecified, - storiesConfigured, storiesFailed, active, }) => { From 9d2a9b57f3fa99c9af8be443f33dd34d4d1930ec Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Tue, 3 Jan 2023 08:18:54 +1100 Subject: [PATCH 07/29] Add some comments to explain things --- code/ui/manager/src/components/preview/preview.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx index 6ca0302877b8..dad99cf98448 100644 --- a/code/ui/manager/src/components/preview/preview.tsx +++ b/code/ui/manager/src/components/preview/preview.tsx @@ -84,8 +84,13 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A } }, []); + // Booting here means the preview is still booting up and hasn't rendered anything + // (including a spinner during preparation) yet. + // We are done booting when a story is selected. const isBooting = !storySpecified && !storiesFailed; + // A ref is ready when it emits STORY_PREPARED const refLoading = !!refs[refId] && !refs[refId].ready; + // The root is ready when it no longer is waiting on webpack const rootLoading = !refId && !(progress?.value === 1 || progress === undefined); const isLoading = entry ? isBooting || refLoading || rootLoading From 8f60c3dfd1635dc0c3b99554308c40bdd9579c0a Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Tue, 3 Jan 2023 11:09:59 +1100 Subject: [PATCH 08/29] Remove `storiesConfigured` et al, and simply to just `ready`. This is all we had for refs too, and the logic for keeping it simple is the same. --- code/lib/manager-api/src/modules/stories.ts | 45 ++++--- .../lib/manager-api/src/tests/stories.test.ts | 112 +++++++++--------- .../src/components/layout/app.mockdata.tsx | 2 +- .../src/components/preview/preview.tsx | 22 ++-- .../components/sidebar/Sidebar.stories.tsx | 31 +---- .../src/components/sidebar/Sidebar.tsx | 18 +-- .../sidebar/__tests__/Sidebar.test.tsx | 2 +- code/ui/manager/src/containers/sidebar.tsx | 6 +- code/ui/manager/src/index.tsx | 2 +- 9 files changed, 107 insertions(+), 133 deletions(-) diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index 25e5ff8ec95e..5881d03a1e64 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -13,6 +13,7 @@ import { STORY_SPECIFIED, STORY_INDEX_INVALIDATED, CONFIG_ERROR, + CURRENT_STORY_WAS_SET, } from '@storybook/core-events'; import { logger } from '@storybook/client-logger'; @@ -54,9 +55,7 @@ export interface SubState { storiesHash: API_StoriesHash; storyId: StoryId; viewMode: ViewMode; - storySpecified: boolean; - storiesConfigured: boolean; - storiesFailed?: Error; + ready: boolean; } export interface SubAPI { @@ -326,10 +325,7 @@ export const init: ModuleFn = ({ await fullAPI.setIndex(storyIndex); } catch (err) { - store.setState({ - storiesConfigured: true, - storiesFailed: err, - }); + store.setState({ ready: true }); } }, // The story index we receive on SET_INDEX is "prepared" in that it has parameters @@ -346,8 +342,7 @@ export const init: ModuleFn = ({ await store.setState({ storiesHash: addPreparedStories(newHash, oldHash), - storiesConfigured: true, - storiesFailed: null, + ready: true, }); }, updateStory: async ( @@ -389,8 +384,6 @@ export const init: ModuleFn = ({ const { sourceType } = getEventMetadata(this, fullAPI); if (sourceType === 'local') { - store.setState({ storySpecified: true }); - if (fullAPI.isSettingsScreenActive()) return; // Special case -- if we are already at the story being specified (i.e. the user started at a given story), @@ -403,6 +396,20 @@ export const init: ModuleFn = ({ } ); + // The CURRENT_STORY_WAS_SET event is the best event to use to tell if a ref is ready. + // Until the ref has a selection, it will not render anything (e.g. while waiting for + // the preview.js file or the index to load). Once it has a selection, it will render its own + // preparing spinner. + fullAPI.on(CURRENT_STORY_WAS_SET, function handler() { + const { ref } = getEventMetadata(this, fullAPI); + + if (!ref) { + store.setState({ ready: true }); + } else { + fullAPI.updateRef(ref.id, { ready: true }); + } + }); + fullAPI.on(STORY_CHANGED, function handler() { const { sourceType } = getEventMetadata(this, fullAPI); @@ -425,8 +432,6 @@ export const init: ModuleFn = ({ fullAPI.setOptions(removeRemovedOptions(options)); store.setState({ hasCalledSetOptions: true }); } - } else { - fullAPI.updateRef(ref.id, { ready: true }); } if (sourceType === 'local') { @@ -503,10 +508,13 @@ export const init: ModuleFn = ({ ); fullAPI.on(CONFIG_ERROR, function handleConfigError(err) { - store.setState({ - storiesConfigured: true, - storiesFailed: err, - }); + const { ref } = getEventMetadata(this, fullAPI); + + if (!ref) { + store.setState({ ready: true }); + } else { + fullAPI.updateRef(ref.id, { ready: true }); + } }); if (FEATURES?.storyStoreV7) { @@ -521,8 +529,7 @@ export const init: ModuleFn = ({ storiesHash: {}, storyId: initialStoryId, viewMode: initialViewMode, - storySpecified: false, - storiesConfigured: false, + ready: false, hasCalledSetOptions: false, }, init: initModule, diff --git a/code/lib/manager-api/src/tests/stories.test.ts b/code/lib/manager-api/src/tests/stories.test.ts index 613529b9c182..1d72a7f72770 100644 --- a/code/lib/manager-api/src/tests/stories.test.ts +++ b/code/lib/manager-api/src/tests/stories.test.ts @@ -9,6 +9,7 @@ import { STORY_INDEX_INVALIDATED, CONFIG_ERROR, SET_INDEX, + CURRENT_STORY_WAS_SET, } from '@storybook/core-events'; import { EventEmitter } from 'events'; import { global } from '@storybook/global'; @@ -107,8 +108,7 @@ describe('stories API', () => { } as ModuleArgs); expect(state).toEqual({ - storySpecified: false, - storiesConfigured: false, + ready: false, storiesHash: {}, storyId: 'id', viewMode: 'story', @@ -576,9 +576,8 @@ describe('stories API', () => { await init(); - const { storiesConfigured, storiesFailed } = store.getState(); - expect(storiesConfigured).toBe(true); - expect(storiesFailed?.message).toMatch(/sorting error/); + const { ready } = store.getState(); + expect(ready).toBe(true); }); it('watches for the INVALIDATE event and refetches -- and resets the hash', async () => { @@ -616,25 +615,7 @@ describe('stories API', () => { }); }); - // Can't currently run these tests as cannot set this on the events describe('STORY_SPECIFIED event', () => { - it('sets the storySpecified state', async () => { - const navigate = jest.fn(); - const fullAPI = Object.assign(new EventEmitter(), { - isSettingsScreenActive() { - return false; - }, - }); - const store = createMockStore({}); - const { init, api } = initStories({ store, navigate, provider, fullAPI } as any); - - Object.assign(fullAPI, api); - init(); - fullAPI.emit(STORY_SPECIFIED, { storyId: 'a--1', viewMode: 'story' }); - - expect(store.getState().storySpecified).toBe(true); - }); - it('navigates to the story', async () => { const navigate = jest.fn(); const fullAPI = Object.assign(new EventEmitter(), { @@ -683,6 +664,43 @@ describe('stories API', () => { }); }); + describe('CURRENT_STORY_WAS_SET event', () => { + it('sets the local ready state', async () => { + const navigate = jest.fn(); + const fullAPI = Object.assign(new EventEmitter()); + const store = createMockStore({}); + const { init, api } = initStories({ store, navigate, provider, fullAPI } as any); + + Object.assign(fullAPI, api); + await init(); + fullAPI.emit(CURRENT_STORY_WAS_SET, { id: 'a--1' }); + + expect(store.getState().ready).toBe(true); + }); + + it('sets a ref to ready', async () => { + const navigate = jest.fn(); + const fullAPI = Object.assign(new EventEmitter(), { updateRef: jest.fn() }); + const store = createMockStore(); + const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + + Object.assign(fullAPI, api); + + getEventMetadataMock.mockReturnValueOnce({ + sourceType: 'external', + ref: { id: 'refId', stories: { 'a--1': { args: { a: 'b' } } } }, + } as any); + await init(); + fullAPI.emit(CURRENT_STORY_WAS_SET, { id: 'a--1' }); + + expect(fullAPI.updateRef.mock.calls.length).toBe(1); + + expect(fullAPI.updateRef.mock.calls[0][1]).toEqual({ + ready: true, + }); + }); + }); + describe('args handling', () => { const parameters = {}; const preparedEntries: API_PreparedStoryIndex['entries'] = { @@ -1302,56 +1320,44 @@ describe('stories API', () => { expect(fullAPI.setOptions).not.toHaveBeenCalled(); }); + }); - it('sets the ref to ready when it is an external story', async () => { + describe('CONFIG_ERROR', () => { + it('sets ready to true, local', async () => { const navigate = jest.fn(); const store = createMockStore(); - const fullAPI = Object.assign(new EventEmitter(), { - setStories: jest.fn(), - updateRef: jest.fn(), - }); + const fullAPI = Object.assign(new EventEmitter(), {}); const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); - getEventMetadataMock.mockReturnValueOnce({ - sourceType: 'external', - ref: { id: 'refId', stories: { 'a--1': { args: { a: 'b' } } } }, - } as any); await init(); - fullAPI.emit(STORY_PREPARED, { - id: 'a--1', - }); - - expect(fullAPI.updateRef.mock.calls.length).toBe(2); - - expect(fullAPI.updateRef.mock.calls[0][1]).toEqual({ - stories: { 'a--1': { args: { a: 'b' }, prepared: true } }, - }); + fullAPI.emit(CONFIG_ERROR, { message: 'Failed to run configure' }); - expect(fullAPI.updateRef.mock.calls[1][1]).toEqual({ - ready: true, - }); + const { ready } = store.getState(); + expect(ready).toBe(true); }); - }); - describe('CONFIG_ERROR', () => { - it('shows error to user', async () => { + it('sets ready to true, ref', async () => { const navigate = jest.fn(); + const fullAPI = Object.assign(new EventEmitter(), { updateRef: jest.fn() }); const store = createMockStore(); - const fullAPI = Object.assign(new EventEmitter(), {}); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + Object.assign(fullAPI, api); + getEventMetadataMock.mockReturnValueOnce({ + sourceType: 'external', + ref: { id: 'refId', stories: { 'a--1': { args: { a: 'b' } } } }, + } as any); await init(); - fullAPI.emit(CONFIG_ERROR, { message: 'Failed to run configure' }); - const { storiesConfigured, storiesFailed } = store.getState(); - expect(storiesConfigured).toBe(true); - expect(storiesFailed?.message).toMatch(/Failed to run configure/); + expect(fullAPI.updateRef.mock.calls.length).toBe(1); + expect(fullAPI.updateRef.mock.calls[0][1]).toEqual({ + ready: true, + }); }); }); diff --git a/code/ui/manager/src/components/layout/app.mockdata.tsx b/code/ui/manager/src/components/layout/app.mockdata.tsx index 505b1ffeee4c..9ec209af9553 100644 --- a/code/ui/manager/src/components/layout/app.mockdata.tsx +++ b/code/ui/manager/src/components/layout/app.mockdata.tsx @@ -61,7 +61,7 @@ const realSidebarProps: SidebarProps = { stories: mockDataset.withRoot as SidebarProps['stories'], menu: [], refs: {}, - storiesConfigured: true, + ready: true, }; const PlaceholderBlock = styled.div(({ color }) => ({ diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx index dad99cf98448..c2e29fe6064e 100644 --- a/code/ui/manager/src/components/preview/preview.tsx +++ b/code/ui/manager/src/components/preview/preview.tsx @@ -37,8 +37,7 @@ const canvasMapper = ({ state, api }: Combo) => ({ queryParams: state.customQueryParams, getElements: api.getElements, entry: api.getData(state.storyId, state.refId), - storySpecified: state.storySpecified, - storiesFailed: state.storiesFailed, + ready: state.ready, refs: state.refs, active: !!(state.viewMode && state.viewMode.match(/^(story|docs)$/)), }); @@ -60,8 +59,7 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A viewMode, queryParams, getElements, - storySpecified, - storiesFailed, + ready, active, }) => { const wrappers = useMemo( @@ -83,18 +81,12 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A } } }, []); - - // Booting here means the preview is still booting up and hasn't rendered anything - // (including a spinner during preparation) yet. - // We are done booting when a story is selected. - const isBooting = !storySpecified && !storiesFailed; - // A ref is ready when it emits STORY_PREPARED + // A ref simply depends on its readiness const refLoading = !!refs[refId] && !refs[refId].ready; - // The root is ready when it no longer is waiting on webpack - const rootLoading = !refId && !(progress?.value === 1 || progress === undefined); - const isLoading = entry - ? isBooting || refLoading || rootLoading - : isBooting || rootLoading; + // The root also might need to wait on webpack + const isBuilding = !(progress?.value === 1 || progress === undefined); + const rootLoading = !refId && (!ready || isBuilding); + const isLoading = entry ? refLoading || rootLoading : rootLoading; return ( diff --git a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx index 2a1e76de0d70..e9bfa865eb01 100644 --- a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx @@ -38,45 +38,24 @@ const refs: Record = { }; export const Simple = () => ( - + ); export const Loading = () => ( - + ); export const Empty = () => ( - + ); export const WithRefs = () => ( - + ); export const LoadingWithRefs = () => ( { +const useCombination = (stories: StoriesHash, ready: boolean, refs: Refs): CombinedDataset => { const hash = useMemo( () => ({ [DEFAULT_REF_ID]: { @@ -72,7 +67,6 @@ const useCombination = ( id: DEFAULT_REF_ID, url: 'iframe.html', ready, - error, }, ...refs, }), @@ -83,8 +77,7 @@ const useCombination = ( export interface SidebarProps { stories: StoriesHash; - storiesConfigured: boolean; - storiesFailed?: Error; + ready: boolean; refs: State['refs']; menu: any[]; storyId?: string; @@ -97,8 +90,7 @@ export const Sidebar = React.memo(function Sidebar({ storyId = null, refId = DEFAULT_REF_ID, stories, - storiesConfigured, - storiesFailed, + ready, menu, menuHighlighted = false, enableShortcuts = true, @@ -106,8 +98,8 @@ export const Sidebar = React.memo(function Sidebar({ }: SidebarProps) { const selected: Selection = useMemo(() => storyId && { storyId, refId }, [storyId, refId]); - const dataset = useCombination(stories, storiesConfigured, storiesFailed, refs); - const isLoading = !dataset.hash[DEFAULT_REF_ID].ready; + const dataset = useCombination(stories, ready, refs); + const isLoading = !ready; const lastViewedProps = useLastViewed(selected); return ( diff --git a/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx b/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx index 3744b5890b66..717c5f05f45e 100644 --- a/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx +++ b/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx @@ -15,7 +15,7 @@ const factory = (props: Partial): RenderResult => { return render( - + ); }; diff --git a/code/ui/manager/src/containers/sidebar.tsx b/code/ui/manager/src/containers/sidebar.tsx index 4d5b29352be1..68139c8d5733 100755 --- a/code/ui/manager/src/containers/sidebar.tsx +++ b/code/ui/manager/src/containers/sidebar.tsx @@ -17,8 +17,7 @@ const Sidebar = React.memo(function Sideber() { refId, layout: { showToolbar, isFullscreen, showPanel, showNav }, storiesHash, - storiesConfigured, - storiesFailed, + ready, refs, } = state; @@ -28,8 +27,7 @@ const Sidebar = React.memo(function Sideber() { title: name, url, stories: storiesHash, - storiesFailed, - storiesConfigured, + ready, refs, storyId, refId, diff --git a/code/ui/manager/src/index.tsx b/code/ui/manager/src/index.tsx index 707be6f82d2e..d28e5f5a870b 100644 --- a/code/ui/manager/src/index.tsx +++ b/code/ui/manager/src/index.tsx @@ -56,7 +56,7 @@ const Main: FC<{ provider: Provider }> = ({ provider }) => { const story = api.getData(state.storyId, state.refId); const isLoading = story ? !!state.refs[state.refId] && !state.refs[state.refId].ready - : !state.storiesFailed && !state.storiesConfigured; + : !state.ready; return ( From 7ec1104a23c1d3a1c31c6d9d15dccbe5b3ea6cb0 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Tue, 3 Jan 2023 11:18:14 +1100 Subject: [PATCH 09/29] Show docs immediately on first load --- .../src/modules/preview-web/PreviewWithSelection.tsx | 2 +- code/lib/preview-api/src/modules/preview-web/View.ts | 4 ++-- code/lib/preview-api/src/modules/preview-web/WebView.ts | 8 ++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx b/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx index 20fe1c96397b..250353db1753 100644 --- a/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx +++ b/code/lib/preview-api/src/modules/preview-web/PreviewWithSelection.tsx @@ -268,7 +268,7 @@ export class PreviewWithSelection extends Preview { showNoPreview(): void; - showPreparingStory(options: { immediate: boolean }): void; + showPreparingStory(options?: { immediate: boolean }): void; - showPreparingDocs(): void; + showPreparingDocs(options?: { immediate: boolean }): void; showMain(): void; diff --git a/code/lib/preview-api/src/modules/preview-web/WebView.ts b/code/lib/preview-api/src/modules/preview-web/WebView.ts index 5d038e5230b4..6c39c90129bc 100644 --- a/code/lib/preview-api/src/modules/preview-web/WebView.ts +++ b/code/lib/preview-api/src/modules/preview-web/WebView.ts @@ -166,9 +166,13 @@ export class WebView implements View { } } - showPreparingDocs() { + showPreparingDocs({ immediate = false } = {}) { clearTimeout(this.preparingTimeout); - this.preparingTimeout = setTimeout(() => this.showMode(Mode.PREPARING_DOCS), PREPARING_DELAY); + if (immediate) { + this.showMode(Mode.PREPARING_DOCS); + } else { + this.preparingTimeout = setTimeout(() => this.showMode(Mode.PREPARING_DOCS), PREPARING_DELAY); + } } showMain() { From 5575b4d6605c6b2154814a0174903eeed0c7e18c Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Mon, 9 Jan 2023 10:07:06 +1100 Subject: [PATCH 10/29] Don't set ready until we are actually ready! --- code/lib/manager-api/src/modules/stories.ts | 5 +++-- code/lib/preview-api/src/modules/preview-web/WebView.ts | 2 ++ code/ui/manager/src/components/preview/preview.tsx | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index 5881d03a1e64..d55fcd64164a 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -325,7 +325,7 @@ export const init: ModuleFn = ({ await fullAPI.setIndex(storyIndex); } catch (err) { - store.setState({ ready: true }); + // Probably we should set the error here... TODO } }, // The story index we receive on SET_INDEX is "prepared" in that it has parameters @@ -342,7 +342,6 @@ export const init: ModuleFn = ({ await store.setState({ storiesHash: addPreparedStories(newHash, oldHash), - ready: true, }); }, updateStory: async ( @@ -403,6 +402,7 @@ export const init: ModuleFn = ({ fullAPI.on(CURRENT_STORY_WAS_SET, function handler() { const { ref } = getEventMetadata(this, fullAPI); + console.log(CURRENT_STORY_WAS_SET, 'ready'); if (!ref) { store.setState({ ready: true }); } else { @@ -511,6 +511,7 @@ export const init: ModuleFn = ({ const { ref } = getEventMetadata(this, fullAPI); if (!ref) { + console.log(CONFIG_ERROR, 'ready'); store.setState({ ready: true }); } else { fullAPI.updateRef(ref.id, { ready: true }); diff --git a/code/lib/preview-api/src/modules/preview-web/WebView.ts b/code/lib/preview-api/src/modules/preview-web/WebView.ts index 6c39c90129bc..e3a866334eda 100644 --- a/code/lib/preview-api/src/modules/preview-web/WebView.ts +++ b/code/lib/preview-api/src/modules/preview-web/WebView.ts @@ -118,6 +118,7 @@ export class WebView implements View { } showMode(mode: Mode) { + console.log('showMode', mode); clearTimeout(this.preparingTimeout); Object.keys(Mode).forEach((otherMode) => { if (otherMode === mode) { @@ -167,6 +168,7 @@ export class WebView implements View { } showPreparingDocs({ immediate = false } = {}) { + console.log('showPreparingDocs', immediate); clearTimeout(this.preparingTimeout); if (immediate) { this.showMode(Mode.PREPARING_DOCS); diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx index c2e29fe6064e..55fd2485f967 100644 --- a/code/ui/manager/src/components/preview/preview.tsx +++ b/code/ui/manager/src/components/preview/preview.tsx @@ -88,6 +88,7 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A const rootLoading = !refId && (!ready || isBuilding); const isLoading = entry ? refLoading || rootLoading : rootLoading; + console.log({ ready, isBuilding, rootLoading, isLoading }); return ( {({ value: scale }) => { From b839502c834a3747a5f0a71ef5a7d7ea47a8faf9 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Wed, 11 Jan 2023 22:48:13 +1100 Subject: [PATCH 11/29] Change refs + stories to use a common interface Add fields: - `stories` (index once loaded into a hash) - `indexError` (if there's an error loading that) - `previewInitialized` (if the preview is ready to render) --- code/lib/manager-api/src/modules/refs.ts | 14 ++-- code/lib/manager-api/src/modules/stories.ts | 77 +++++++++---------- code/lib/types/src/modules/api.ts | 16 ++-- .../src/components/preview/preview.tsx | 9 +-- .../manager/src/components/sidebar/Refs.tsx | 10 +-- .../components/sidebar/Sidebar.stories.tsx | 57 +++++++++++--- .../src/components/sidebar/Sidebar.tsx | 19 +++-- .../sidebar/__tests__/Sidebar.test.tsx | 4 +- code/ui/manager/src/containers/sidebar.tsx | 10 ++- 9 files changed, 129 insertions(+), 87 deletions(-) diff --git a/code/lib/manager-api/src/modules/refs.ts b/code/lib/manager-api/src/modules/refs.ts index 166994b956e6..9483ede79fb3 100644 --- a/code/lib/manager-api/src/modules/refs.ts +++ b/code/lib/manager-api/src/modules/refs.ts @@ -33,7 +33,7 @@ export interface SubAPI { getRefs: () => API_Refs; checkRef: (ref: API_SetRefData) => Promise; changeRefVersion: (id: string, url: string) => void; - changeRefState: (id: string, ready: boolean) => void; + changeRefState: (id: string, previewInitialized: boolean) => void; } export const getSourceType = (source: string, refId: string) => { @@ -83,8 +83,8 @@ async function handleRequest( } return json as API_SetRefData; - } catch (error) { - return { error }; + } catch (err) { + return { indexError: err }; } } @@ -118,10 +118,10 @@ export const init: ModuleFn = ( api.checkRef(ref); }, - changeRefState: (id, ready) => { + changeRefState: (id, previewInitialized) => { const { [id]: ref, ...updated } = api.getRefs(); - updated[id] = { ...ref, ready }; + updated[id] = { ...ref, previewInitialized }; store.setState({ refs: updated, @@ -175,7 +175,7 @@ export const init: ModuleFn = ( // In theory the `/iframe.html` could be private and the `stories.json` could not exist, but in practice // the only private servers we know about (Chromatic) always include `stories.json`. So we can tell // if the ref actually exists by simply checking `stories.json` w/ credentials. - loadedData.error = { + loadedData.indexError = { message: dedent` Error: Loading of ref failed at fetch (lib/api/src/modules/refs.ts) @@ -226,7 +226,7 @@ export const init: ModuleFn = ( } if (storiesHash) storiesHash = addRefIds(storiesHash, ref); - api.updateRef(id, { stories: storiesHash, ...rest, ready }); + api.updateRef(id, { stories: storiesHash, ...rest }); }, updateRef: (id, data) => { diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index d55fcd64164a..7c1d2885893c 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -28,6 +28,7 @@ import type { API_StoriesHash, API_StoryEntry, StoryIndex, + API_LoadedRefData, } from '@storybook/types'; // eslint-disable-next-line import/no-cycle import { getEventMetadata } from '../lib/events'; @@ -51,11 +52,9 @@ type ParameterName = string; type ViewMode = 'story' | 'info' | 'settings' | string | undefined; type StoryUpdate = Pick; -export interface SubState { - storiesHash: API_StoriesHash; +export interface SubState extends API_LoadedRefData { storyId: StoryId; viewMode: ViewMode; - ready: boolean; } export interface SubAPI { @@ -132,11 +131,11 @@ export const init: ModuleFn = ({ return data.type === 'story' ? data.prepared : true; }, resolveStory: (storyId, refId) => { - const { refs, storiesHash } = store.getState(); + const { refs, stories } = store.getState(); if (refId) { return refs[refId].stories ? refs[refId].stories[storyId] : undefined; } - return storiesHash ? storiesHash[storyId] : undefined; + return stories ? stories[storyId] : undefined; }, getCurrentStoryData: () => { const { storyId, refId } = store.getState(); @@ -168,7 +167,7 @@ export const init: ModuleFn = ({ return parameters || undefined; }, jumpToComponent: (direction) => { - const { storiesHash, storyId, refs, refId } = store.getState(); + const { stories, storyId, refs, refId } = store.getState(); const story = api.getData(storyId, refId); // cannot navigate when there's no current selection @@ -176,7 +175,7 @@ export const init: ModuleFn = ({ return; } - const hash = refId ? refs[refId].stories || {} : storiesHash; + const hash = refId ? refs[refId].stories || {} : stories; const result = api.findSiblingStoryId(storyId, hash, direction, true); if (result) { @@ -184,7 +183,7 @@ export const init: ModuleFn = ({ } }, jumpToStory: (direction) => { - const { storiesHash, storyId, refs, refId } = store.getState(); + const { stories, storyId, refs, refId } = store.getState(); const story = api.getData(storyId, refId); // cannot navigate when there's no current selection @@ -192,7 +191,7 @@ export const init: ModuleFn = ({ return; } - const hash = story.refId ? refs[story.refId].stories : storiesHash; + const hash = story.refId ? refs[story.refId].stories : stories; const result = api.findSiblingStoryId(storyId, hash, direction, false); if (result) { @@ -200,8 +199,8 @@ export const init: ModuleFn = ({ } }, selectFirstStory: () => { - const { storiesHash } = store.getState(); - const firstStory = Object.keys(storiesHash).find((id) => storiesHash[id].type === 'story'); + const { stories } = store.getState(); + const firstStory = Object.keys(stories).find((id) => stories[id].type === 'story'); if (firstStory) { api.selectStory(firstStory); @@ -212,9 +211,9 @@ export const init: ModuleFn = ({ }, selectStory: (titleOrId = undefined, name = undefined, options = {}) => { const { ref } = options; - const { storyId, storiesHash, refs } = store.getState(); + const { storyId, stories, refs } = store.getState(); - const hash = ref ? refs[ref].stories : storiesHash; + const hash = ref ? refs[ref].stories : stories; const kindSlug = storyId?.split('--', 2)[0]; @@ -249,17 +248,17 @@ export const init: ModuleFn = ({ } } }, - findLeafEntry(storiesHash, storyId) { - const entry = storiesHash[storyId]; + findLeafEntry(stories, storyId) { + const entry = stories[storyId]; if (entry.type === 'docs' || entry.type === 'story') { return entry; } const childStoryId = entry.children[0]; - return api.findLeafEntry(storiesHash, childStoryId); + return api.findLeafEntry(stories, childStoryId); }, - findLeafStoryId(storiesHash, storyId) { - return api.findLeafEntry(storiesHash, storyId)?.id; + findLeafStoryId(stories, storyId) { + return api.findLeafEntry(stories, storyId)?.id; }, findSiblingStoryId(storyId, hash, direction, toSiblingGroup) { if (toSiblingGroup) { @@ -311,6 +310,7 @@ export const init: ModuleFn = ({ }); }, fetchIndex: async () => { + console.log('fetch index'); try { const result = await fetch(STORY_INDEX_PATH); if (result.status !== 200) throw new Error(await result.text()); @@ -323,9 +323,11 @@ export const init: ModuleFn = ({ return; } + console.log('set index'); await fullAPI.setIndex(storyIndex); } catch (err) { - // Probably we should set the error here... TODO + console.log('index error'); + await store.setState({ indexError: err }); } }, // The story index we receive on SET_INDEX is "prepared" in that it has parameters @@ -338,11 +340,11 @@ export const init: ModuleFn = ({ }); // Now we need to patch in the existing prepared stories - const oldHash = store.getState().storiesHash; + const oldHash = store.getState().stories; - await store.setState({ - storiesHash: addPreparedStories(newHash, oldHash), - }); + console.log('setState'); + await store.setState({ stories: addPreparedStories(newHash, oldHash) }); + console.log(store.getState()); }, updateStory: async ( storyId: StoryId, @@ -350,12 +352,12 @@ export const init: ModuleFn = ({ ref?: API_ComposedRef ): Promise => { if (!ref) { - const { storiesHash } = store.getState(); - storiesHash[storyId] = { - ...storiesHash[storyId], + const { stories } = store.getState(); + stories[storyId] = { + ...stories[storyId], ...update, } as API_StoryEntry; - await store.setState({ storiesHash }); + await store.setState({ stories }); } else { const { id: refId, stories } = ref; stories[storyId] = { @@ -402,11 +404,10 @@ export const init: ModuleFn = ({ fullAPI.on(CURRENT_STORY_WAS_SET, function handler() { const { ref } = getEventMetadata(this, fullAPI); - console.log(CURRENT_STORY_WAS_SET, 'ready'); if (!ref) { - store.setState({ ready: true }); + store.setState({ previewInitialized: true }); } else { - fullAPI.updateRef(ref.id, { ready: true }); + fullAPI.updateRef(ref.id, { previewInitialized: true }); } }); @@ -435,13 +436,13 @@ export const init: ModuleFn = ({ } if (sourceType === 'local') { - const { storyId, storiesHash, refId } = store.getState(); + const { storyId, stories, refId } = store.getState(); // create a list of related stories to be preloaded const toBePreloaded = Array.from( new Set([ - api.findSiblingStoryId(storyId, storiesHash, 1, true), - api.findSiblingStoryId(storyId, storiesHash, -1, true), + api.findSiblingStoryId(storyId, stories, 1, true), + api.findSiblingStoryId(storyId, stories, -1, true), ]) ).filter(Boolean); @@ -507,14 +508,13 @@ export const init: ModuleFn = ({ } ); + // When there's a preview error, we don't show it in the manager, but simply fullAPI.on(CONFIG_ERROR, function handleConfigError(err) { const { ref } = getEventMetadata(this, fullAPI); - if (!ref) { - console.log(CONFIG_ERROR, 'ready'); - store.setState({ ready: true }); + store.setState({ previewInitialized: true }); } else { - fullAPI.updateRef(ref.id, { ready: true }); + fullAPI.updateRef(ref.id, { previewInitialized: true }); } }); @@ -527,11 +527,10 @@ export const init: ModuleFn = ({ return { api, state: { - storiesHash: {}, storyId: initialStoryId, viewMode: initialViewMode, - ready: false, hasCalledSetOptions: false, + previewInitialized: false, }, init: initModule, }; diff --git a/code/lib/types/src/modules/api.ts b/code/lib/types/src/modules/api.ts index 11a0d74793f7..dd98031b0c0e 100644 --- a/code/lib/types/src/modules/api.ts +++ b/code/lib/types/src/modules/api.ts @@ -142,18 +142,22 @@ export type API_SetRefData = Partial< >; export type API_StoryMapper = (ref: API_ComposedRef, story: SetStoriesStory) => SetStoriesStory; -export interface API_ComposedRef { + +export interface API_LoadedRefData { + stories?: API_StoriesHash; + indexError?: Error; + previewInitialized: boolean; +} + +export interface API_ComposedRef extends API_LoadedRefData { id: string; title?: string; url: string; type?: 'auto-inject' | 'unknown' | 'lazy' | 'server-checked'; expanded?: boolean; - stories: API_StoriesHash; versions?: API_Versions; loginUrl?: string; version?: string; - ready?: boolean; - error?: any; } export type API_ComposedRefUpdate = Partial< @@ -166,8 +170,8 @@ export type API_ComposedRefUpdate = Partial< | 'versions' | 'loginUrl' | 'version' - | 'ready' - | 'error' + | 'indexError' + | 'previewInitialized' > >; diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx index 55fd2485f967..9d01ed28eaf5 100644 --- a/code/ui/manager/src/components/preview/preview.tsx +++ b/code/ui/manager/src/components/preview/preview.tsx @@ -37,7 +37,7 @@ const canvasMapper = ({ state, api }: Combo) => ({ queryParams: state.customQueryParams, getElements: api.getElements, entry: api.getData(state.storyId, state.refId), - ready: state.ready, + previewInitialized: state.previewInitialized, refs: state.refs, active: !!(state.viewMode && state.viewMode.match(/^(story|docs)$/)), }); @@ -59,7 +59,7 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A viewMode, queryParams, getElements, - ready, + previewInitialized, active, }) => { const wrappers = useMemo( @@ -82,13 +82,12 @@ const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): A } }, []); // A ref simply depends on its readiness - const refLoading = !!refs[refId] && !refs[refId].ready; + const refLoading = !!refs[refId] && !refs[refId].previewInitialized; // The root also might need to wait on webpack const isBuilding = !(progress?.value === 1 || progress === undefined); - const rootLoading = !refId && (!ready || isBuilding); + const rootLoading = !refId && (!previewInitialized || isBuilding); const isLoading = entry ? refLoading || rootLoading : rootLoading; - console.log({ ready, isBuilding, rootLoading, isLoading }); return ( {({ value: scale }) => { diff --git a/code/ui/manager/src/components/sidebar/Refs.tsx b/code/ui/manager/src/components/sidebar/Refs.tsx index 793244517fb9..d9c11f42a841 100644 --- a/code/ui/manager/src/components/sidebar/Refs.tsx +++ b/code/ui/manager/src/components/sidebar/Refs.tsx @@ -110,16 +110,16 @@ export const Ref: FC = React.memo(function Ref(props) { loginUrl, type, expanded = true, - ready, - error, + indexError, + previewInitialized, } = props; const length = useMemo(() => (stories ? Object.keys(stories).length : 0), [stories]); const indicatorRef = useRef(null); const isMain = refId === DEFAULT_REF_ID; - const isLoadingInjected = type === 'auto-inject' && !ready; + const isLoadingInjected = type === 'auto-inject' && !previewInitialized; const isLoading = isLoadingMain || isLoadingInjected || type === 'unknown'; - const isError = !!error; + const isError = !!indexError; const isEmpty = !isLoading && length === 0; const isAuthRequired = !!loginUrl && length === 0; @@ -153,7 +153,7 @@ export const Ref: FC = React.memo(function Ref(props) { {isExpanded && ( {state === 'auth' && } - {state === 'error' && } + {state === 'error' && } {state === 'loading' && } {state === 'empty' && } {state === 'ready' && ( diff --git a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx index e9bfa865eb01..5fb527ea68a8 100644 --- a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx @@ -1,5 +1,6 @@ import React from 'react'; +import type { StoriesHash } from 'lib/manager-api/src'; import { Sidebar, DEFAULT_REF_ID } from './Sidebar'; import { standardData as standardHeaderData } from './Heading.stories'; import * as ExplorerStories from './Explorer.stories'; @@ -23,39 +24,63 @@ const refId = DEFAULT_REF_ID; const storyId = 'root-1-child-a2--grandchild-a1-1'; export const simpleData = { menu, stories, storyId }; -export const loadingData = { menu, stories: {} }; +export const loadingData = { menu }; const refs: Record = { optimized: { id: 'optimized', title: 'This is a ref', url: 'https://example.com', - ready: false, type: 'lazy', // @ts-expect-error (needs to be converted to CSF3) stories, + previewInitialized: true, + }, +}; + +const indexError = new Error('Failed to load index'); + +const refsError = { + optimized: { + ...refs.optimized, + stories: undefined as StoriesHash, + indexError, }, }; export const Simple = () => ( - + ); export const Loading = () => ( - + ); export const Empty = () => ( - + ); -export const WithRefs = () => ( - +export const IndexError = () => ( + ); -export const LoadingWithRefs = () => ( +export const WithRefs = () => ( ( refs={refs} /> ); + +export const LoadingWithRefs = () => ( + +); + +export const LoadingWithRefError = () => ( + +); diff --git a/code/ui/manager/src/components/sidebar/Sidebar.tsx b/code/ui/manager/src/components/sidebar/Sidebar.tsx index 5c3d59bf78af..0d17362aa6da 100644 --- a/code/ui/manager/src/components/sidebar/Sidebar.tsx +++ b/code/ui/manager/src/components/sidebar/Sidebar.tsx @@ -4,6 +4,7 @@ import { styled } from '@storybook/theming'; import { ScrollArea, Spaced } from '@storybook/components'; import type { StoriesHash, State } from '@storybook/manager-api'; +import type { API_LoadedRefData } from 'lib/types/src'; import { Heading } from './Heading'; // eslint-disable-next-line import/no-cycle @@ -58,26 +59,23 @@ const Swap = React.memo(function Swap({ ); }); -const useCombination = (stories: StoriesHash, ready: boolean, refs: Refs): CombinedDataset => { +const useCombination = (defaultRefData: API_LoadedRefData, refs: Refs): CombinedDataset => { const hash = useMemo( () => ({ [DEFAULT_REF_ID]: { - stories, + ...defaultRefData, title: null, id: DEFAULT_REF_ID, url: 'iframe.html', - ready, }, ...refs, }), - [refs, stories] + [refs, defaultRefData] ); return useMemo(() => ({ hash, entries: Object.entries(hash) }), [hash]); }; -export interface SidebarProps { - stories: StoriesHash; - ready: boolean; +export interface SidebarProps extends API_LoadedRefData { refs: State['refs']; menu: any[]; storyId?: string; @@ -90,7 +88,8 @@ export const Sidebar = React.memo(function Sidebar({ storyId = null, refId = DEFAULT_REF_ID, stories, - ready, + indexError, + previewInitialized, menu, menuHighlighted = false, enableShortcuts = true, @@ -98,8 +97,8 @@ export const Sidebar = React.memo(function Sidebar({ }: SidebarProps) { const selected: Selection = useMemo(() => storyId && { storyId, refId }, [storyId, refId]); - const dataset = useCombination(stories, ready, refs); - const isLoading = !ready; + const dataset = useCombination({ stories, indexError, previewInitialized }, refs); + const isLoading = !stories && !indexError; const lastViewedProps = useLastViewed(selected); return ( diff --git a/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx b/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx index 717c5f05f45e..3400e570d5b7 100644 --- a/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx +++ b/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx @@ -15,7 +15,7 @@ const factory = (props: Partial): RenderResult => { return render( - + ); }; @@ -78,7 +78,7 @@ describe('Sidebar', () => { [refId]: { stories: refStories, id: refId, - ready: true, + previewInitialized: true, title: refId, url: 'https://ref.url', }, diff --git a/code/ui/manager/src/containers/sidebar.tsx b/code/ui/manager/src/containers/sidebar.tsx index 68139c8d5733..a656ed8f72c5 100755 --- a/code/ui/manager/src/containers/sidebar.tsx +++ b/code/ui/manager/src/containers/sidebar.tsx @@ -16,8 +16,9 @@ const Sidebar = React.memo(function Sideber() { storyId, refId, layout: { showToolbar, isFullscreen, showPanel, showNav }, - storiesHash, - ready, + stories, + indexError, + previewInitialized, refs, } = state; @@ -26,8 +27,9 @@ const Sidebar = React.memo(function Sideber() { return { title: name, url, - stories: storiesHash, - ready, + stories, + indexError, + previewInitialized, refs, storyId, refId, From 5f105af6f4c2c6ff055bf1d725238cf9be5c3a33 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Wed, 11 Jan 2023 22:55:06 +1100 Subject: [PATCH 12/29] Drop some logs --- code/lib/manager-api/src/modules/stories.ts | 5 ----- code/lib/preview-api/src/modules/preview-web/WebView.ts | 2 -- 2 files changed, 7 deletions(-) diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index 7c1d2885893c..a5e61da79355 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -310,7 +310,6 @@ export const init: ModuleFn = ({ }); }, fetchIndex: async () => { - console.log('fetch index'); try { const result = await fetch(STORY_INDEX_PATH); if (result.status !== 200) throw new Error(await result.text()); @@ -323,10 +322,8 @@ export const init: ModuleFn = ({ return; } - console.log('set index'); await fullAPI.setIndex(storyIndex); } catch (err) { - console.log('index error'); await store.setState({ indexError: err }); } }, @@ -342,9 +339,7 @@ export const init: ModuleFn = ({ // Now we need to patch in the existing prepared stories const oldHash = store.getState().stories; - console.log('setState'); await store.setState({ stories: addPreparedStories(newHash, oldHash) }); - console.log(store.getState()); }, updateStory: async ( storyId: StoryId, diff --git a/code/lib/preview-api/src/modules/preview-web/WebView.ts b/code/lib/preview-api/src/modules/preview-web/WebView.ts index e3a866334eda..6c39c90129bc 100644 --- a/code/lib/preview-api/src/modules/preview-web/WebView.ts +++ b/code/lib/preview-api/src/modules/preview-web/WebView.ts @@ -118,7 +118,6 @@ export class WebView implements View { } showMode(mode: Mode) { - console.log('showMode', mode); clearTimeout(this.preparingTimeout); Object.keys(Mode).forEach((otherMode) => { if (otherMode === mode) { @@ -168,7 +167,6 @@ export class WebView implements View { } showPreparingDocs({ immediate = false } = {}) { - console.log('showPreparingDocs', immediate); clearTimeout(this.preparingTimeout); if (immediate) { this.showMode(Mode.PREPARING_DOCS); From 1714df8385f4f935323e3175567e8b59177f234a Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 12 Jan 2023 12:48:46 +1100 Subject: [PATCH 13/29] Update tests --- .../lib/manager-api/src/tests/stories.test.ts | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/code/lib/manager-api/src/tests/stories.test.ts b/code/lib/manager-api/src/tests/stories.test.ts index 1d72a7f72770..9a0a0aac2ba8 100644 --- a/code/lib/manager-api/src/tests/stories.test.ts +++ b/code/lib/manager-api/src/tests/stories.test.ts @@ -108,8 +108,7 @@ describe('stories API', () => { } as ModuleArgs); expect(state).toEqual({ - ready: false, - storiesHash: {}, + previewInitialized: false, storyId: 'id', viewMode: 'story', hasCalledSetOptions: false, @@ -126,7 +125,7 @@ describe('stories API', () => { Object.assign(fullAPI, api); api.setIndex({ v: 4, entries: mockEntries }); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); // We need exact key ordering, even if in theory JS doesn't guarantee it expect(Object.keys(storedStoriesHash)).toEqual([ @@ -175,7 +174,7 @@ describe('stories API', () => { }, }, }); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); // We need exact key ordering, even if in theory JS doesn't guarantee it expect(Object.keys(storedStoriesHash)).toEqual([ @@ -219,7 +218,7 @@ describe('stories API', () => { ...mockEntries, }, }); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); // We need exact key ordering, even if in theory JS doesn't guarantee it expect(Object.keys(storedStoriesHash)).toEqual([ @@ -261,7 +260,7 @@ describe('stories API', () => { }, }); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); // We need exact key ordering, even if in theory JS doens't guarantee it expect(Object.keys(storedStoriesHash)).toEqual(['a', 'a-b', 'a-b--1']); @@ -307,7 +306,7 @@ describe('stories API', () => { }, }); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); // We need exact key ordering, even if in theory JS doens't guarantee it expect(Object.keys(storedStoriesHash)).toEqual(['a', 'a--1']); @@ -345,7 +344,7 @@ describe('stories API', () => { }, }); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); // We need exact key ordering, even if in theory JS doens't guarantee it expect(Object.keys(storedStoriesHash)).toEqual(['a', 'a--1', 'a--2', 'b', 'b--1']); @@ -386,7 +385,7 @@ describe('stories API', () => { }, }); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); expect(storedStoriesHash['prepared--story']).toMatchObject({ type: 'story', @@ -418,7 +417,7 @@ describe('stories API', () => { }); // Let the promise/await chain resolve await new Promise((r) => setTimeout(r, 0)); - expect(store.getState().storiesHash['component-a--story-1'] as API_StoryEntry).toMatchObject({ + expect(store.getState().stories['component-a--story-1'] as API_StoryEntry).toMatchObject({ prepared: true, parameters: { a: 'b' }, args: { c: 'd' }, @@ -428,7 +427,7 @@ describe('stories API', () => { // Let the promise/await chain resolve await new Promise((r) => setTimeout(r, 0)); - expect(store.getState().storiesHash['component-a--story-1'] as API_StoryEntry).toMatchObject({ + expect(store.getState().stories['component-a--story-1'] as API_StoryEntry).toMatchObject({ prepared: true, parameters: { a: 'b' }, args: { c: 'd' }, @@ -479,7 +478,7 @@ describe('stories API', () => { api.setIndex({ v: 4, entries: docsEntries }); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); // We need exact key ordering, even if in theory JS doesn't guarantee it expect(Object.keys(storedStoriesHash)).toEqual([ @@ -514,7 +513,7 @@ describe('stories API', () => { api.setIndex({ v: 4, entries: docsEntries }); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); expect(Object.keys(storedStoriesHash)).toEqual(['component-b', 'component-b--docs']); }); @@ -576,8 +575,8 @@ describe('stories API', () => { await init(); - const { ready } = store.getState(); - expect(ready).toBe(true); + const { indexError } = store.getState(); + expect(indexError).toBeDefined(); }); it('watches for the INVALIDATE event and refetches -- and resets the hash', async () => { @@ -609,7 +608,7 @@ describe('stories API', () => { // Let the promise/await chain resolve await new Promise((r) => setTimeout(r, 0)); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); expect(Object.keys(storedStoriesHash)).toEqual(['component-a', 'component-a--story-1']); }); @@ -665,7 +664,7 @@ describe('stories API', () => { }); describe('CURRENT_STORY_WAS_SET event', () => { - it('sets the local ready state', async () => { + it('sets previewInitialized', async () => { const navigate = jest.fn(); const fullAPI = Object.assign(new EventEmitter()); const store = createMockStore({}); @@ -675,10 +674,10 @@ describe('stories API', () => { await init(); fullAPI.emit(CURRENT_STORY_WAS_SET, { id: 'a--1' }); - expect(store.getState().ready).toBe(true); + expect(store.getState().previewInitialized).toBe(true); }); - it('sets a ref to ready', async () => { + it('sets a ref to previewInitialized', async () => { const navigate = jest.fn(); const fullAPI = Object.assign(new EventEmitter(), { updateRef: jest.fn() }); const store = createMockStore(); @@ -696,7 +695,7 @@ describe('stories API', () => { expect(fullAPI.updateRef.mock.calls.length).toBe(1); expect(fullAPI.updateRef.mock.calls[0][1]).toEqual({ - ready: true, + previewInitialized: true, }); }); }); @@ -734,14 +733,14 @@ describe('stories API', () => { const { setIndex } = Object.assign(fullAPI, api); setIndex({ v: 4, entries: preparedEntries }); - const { storiesHash: initialStoriesHash } = store.getState(); + const { stories: initialStoriesHash } = store.getState(); expect((initialStoriesHash['a--1'] as API_StoryEntry).args).toEqual({ a: 'b' }); expect((initialStoriesHash['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); init(); fullAPI.emit(STORY_ARGS_UPDATED, { storyId: 'a--1', args: { foo: 'bar' } }); - const { storiesHash: changedStoriesHash } = store.getState(); + const { stories: changedStoriesHash } = store.getState(); expect((changedStoriesHash['a--1'] as API_StoryEntry).args).toEqual({ foo: 'bar' }); expect((changedStoriesHash['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); }); @@ -789,7 +788,7 @@ describe('stories API', () => { }, }); - const { storiesHash: changedStoriesHash } = store.getState(); + const { stories: changedStoriesHash } = store.getState(); expect((changedStoriesHash['a--1'] as API_StoryEntry).args).toEqual({ a: 'b' }); expect((changedStoriesHash['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); }); @@ -840,7 +839,7 @@ describe('stories API', () => { }, }); - const { storiesHash: changedStoriesHash } = store.getState(); + const { stories: changedStoriesHash } = store.getState(); expect((changedStoriesHash['a--1'] as API_StoryEntry).args).toEqual({ a: 'b' }); expect((changedStoriesHash['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); }); @@ -1001,7 +1000,7 @@ describe('stories API', () => { store.setState(state); setIndex({ v: 4, entries: navigationEntries }); - const result = findSiblingStoryId(storyId, store.getState().storiesHash, 1, false); + const result = findSiblingStoryId(storyId, store.getState().stories, 1, false); expect(result).toBe('a--2'); }); it('works forward toSiblingGroup', () => { @@ -1016,7 +1015,7 @@ describe('stories API', () => { store.setState(state); setIndex({ v: 4, entries: navigationEntries }); - const result = findSiblingStoryId(storyId, store.getState().storiesHash, 1, true); + const result = findSiblingStoryId(storyId, store.getState().stories, 1, true); expect(result).toBe('b-c--1'); }); }); @@ -1280,7 +1279,7 @@ describe('stories API', () => { args: { c: 'd' }, }); - const { storiesHash: storedStoriesHash } = store.getState(); + const { stories: storedStoriesHash } = store.getState(); expect(storedStoriesHash['component-a--story-1']).toMatchObject({ type: 'story', id: 'component-a--story-1', @@ -1323,7 +1322,7 @@ describe('stories API', () => { }); describe('CONFIG_ERROR', () => { - it('sets ready to true, local', async () => { + it('sets previewInitialized to true, local', async () => { const navigate = jest.fn(); const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter(), {}); @@ -1335,11 +1334,11 @@ describe('stories API', () => { fullAPI.emit(CONFIG_ERROR, { message: 'Failed to run configure' }); - const { ready } = store.getState(); - expect(ready).toBe(true); + const { previewInitialized } = store.getState(); + expect(previewInitialized).toBe(true); }); - it('sets ready to true, ref', async () => { + it('sets previewInitialized to true, ref', async () => { const navigate = jest.fn(); const fullAPI = Object.assign(new EventEmitter(), { updateRef: jest.fn() }); const store = createMockStore(); @@ -1356,7 +1355,7 @@ describe('stories API', () => { expect(fullAPI.updateRef.mock.calls.length).toBe(1); expect(fullAPI.updateRef.mock.calls[0][1]).toEqual({ - ready: true, + previewInitialized: true, }); }); }); From 4052c46ff2271648b8880e363d9c9c4e3300256b Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 12 Jan 2023 12:50:27 +1100 Subject: [PATCH 14/29] Missed this --- code/ui/manager/src/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/ui/manager/src/index.tsx b/code/ui/manager/src/index.tsx index d28e5f5a870b..0660ababc796 100644 --- a/code/ui/manager/src/index.tsx +++ b/code/ui/manager/src/index.tsx @@ -55,8 +55,8 @@ const Main: FC<{ provider: Provider }> = ({ provider }) => { const panelCount = Object.keys(api.getPanels()).length; const story = api.getData(state.storyId, state.refId); const isLoading = story - ? !!state.refs[state.refId] && !state.refs[state.refId].ready - : !state.ready; + ? !!state.refs[state.refId] && !state.refs[state.refId].previewInitialized + : !state.previewInitialized; return ( From 242a87b3bcd3f0b2d070c597edf21a9b716a16c4 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 12 Jan 2023 13:29:26 +1100 Subject: [PATCH 15/29] Update snapshots --- code/lib/manager-api/src/tests/refs.test.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/code/lib/manager-api/src/tests/refs.test.js b/code/lib/manager-api/src/tests/refs.test.js index 17a7abe9bf90..123d4164896f 100644 --- a/code/lib/manager-api/src/tests/refs.test.js +++ b/code/lib/manager-api/src/tests/refs.test.js @@ -252,7 +252,8 @@ describe('Refs API', () => { Object { "refs": Object { "fake": Object { - "error": Object { + "id": "fake", + "indexError": Object { "message": "Error: Loading of ref failed at fetch (lib/api/src/modules/refs.ts) @@ -263,8 +264,6 @@ describe('Refs API', () => { Please check your dev-tools network tab.", }, - "id": "fake", - "ready": false, "stories": undefined, "title": "Fake", "type": "auto-inject", @@ -340,7 +339,6 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", - "ready": false, "stories": Object {}, "title": "Fake", "type": "lazy", @@ -418,7 +416,6 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", - "ready": false, "stories": Object {}, "title": "Fake", "type": "lazy", @@ -497,7 +494,6 @@ describe('Refs API', () => { "fake": Object { "id": "fake", "loginUrl": "https://example.com/login", - "ready": false, "stories": undefined, "title": "Fake", "type": "auto-inject", @@ -577,7 +573,6 @@ describe('Refs API', () => { "fake": Object { "id": "fake", "loginUrl": "https://example.com/login", - "ready": false, "stories": undefined, "title": "Fake", "type": "auto-inject", @@ -658,7 +653,6 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", - "ready": false, "stories": Object {}, "title": "Fake", "type": "lazy", @@ -736,7 +730,6 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", - "ready": false, "stories": Object {}, "title": "Fake", "type": "lazy", From 93e9558d40573f834bcfe0f301a3cb99eec2e18a Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 12 Jan 2023 13:33:58 +1100 Subject: [PATCH 16/29] Update various tests/stories --- .../src/components/layout/app.mockdata.tsx | 2 +- .../src/components/preview/FramesRenderer.tsx | 2 +- .../src/components/sidebar/Refs.stories.tsx | 21 +++++++++++-------- .../src/components/sidebar/Search.stories.tsx | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/code/ui/manager/src/components/layout/app.mockdata.tsx b/code/ui/manager/src/components/layout/app.mockdata.tsx index 9ec209af9553..14489e57e8f4 100644 --- a/code/ui/manager/src/components/layout/app.mockdata.tsx +++ b/code/ui/manager/src/components/layout/app.mockdata.tsx @@ -61,7 +61,7 @@ const realSidebarProps: SidebarProps = { stories: mockDataset.withRoot as SidebarProps['stories'], menu: [], refs: {}, - ready: true, + previewInitialized: true, }; const PlaceholderBlock = styled.div(({ color }) => ({ diff --git a/code/ui/manager/src/components/preview/FramesRenderer.tsx b/code/ui/manager/src/components/preview/FramesRenderer.tsx index bf2ff9b916d8..be4d83c6e376 100644 --- a/code/ui/manager/src/components/preview/FramesRenderer.tsx +++ b/code/ui/manager/src/components/preview/FramesRenderer.tsx @@ -80,7 +80,7 @@ export const FramesRenderer: FC = ({ useEffect(() => { const newFrames = Object.values(refs) .filter((r) => { - if (r.error) { + if (r.indexError) { return false; } if (r.type === 'auto-inject') { diff --git a/code/ui/manager/src/components/sidebar/Refs.stories.tsx b/code/ui/manager/src/components/sidebar/Refs.stories.tsx index d50812586301..1b19b872fc75 100644 --- a/code/ui/manager/src/components/sidebar/Refs.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Refs.stories.tsx @@ -28,7 +28,7 @@ const storyId = '1-12-121'; export const simpleData = { menu, stories, storyId }; export const loadingData = { menu, stories: {} }; -const error: Error = (() => { +const indexError: Error = (() => { try { throw new Error('There was a severe problem'); } catch (e) { @@ -41,7 +41,7 @@ const refs: Record = { id: 'optimized', title: 'It is optimized', url: 'https://example.com', - ready: false, + previewInitialized: false, type: 'lazy', // @ts-expect-error (invalid input) stories, @@ -50,16 +50,16 @@ const refs: Record = { id: 'empty', title: 'It is empty because no stories were loaded', url: 'https://example.com', - ready: false, type: 'lazy', stories: {}, + previewInitialized: false, }, startInjected_unknown: { id: 'startInjected_unknown', title: 'It started injected and is unknown', url: 'https://example.com', type: 'unknown', - ready: false, + previewInitialized: false, // @ts-expect-error (invalid input) stories, }, @@ -68,7 +68,7 @@ const refs: Record = { title: 'It started injected and is loading', url: 'https://example.com', type: 'auto-inject', - ready: false, + previewInitialized: false, // @ts-expect-error (invalid input) stories, }, @@ -77,7 +77,7 @@ const refs: Record = { title: 'It started injected and is ready', url: 'https://example.com', type: 'auto-inject', - ready: true, + previewInitialized: true, // @ts-expect-error (invalid input) stories, }, @@ -89,6 +89,7 @@ const refs: Record = { // @ts-expect-error (invalid input) stories, versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com' }, + previewInitialized: true, }, versionsMissingCurrent: { id: 'versions_missing_current', @@ -98,22 +99,23 @@ const refs: Record = { // @ts-expect-error (invalid input) stories, versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com/v2' }, + previewInitialized: true, }, error: { id: 'error', title: 'This has problems', url: 'https://example.com', type: 'lazy', - stories: {}, - error, + indexError, + previewInitialized: true, }, auth: { id: 'Authentication', title: 'This requires a login', url: 'https://example.com', type: 'lazy', - stories: {}, loginUrl: 'https://example.com', + previewInitialized: true, }, long: { id: 'long', @@ -126,6 +128,7 @@ const refs: Record = { '111.111.888-new': 'https://example.com/new', '111.111.888': 'https://example.com', }, + previewInitialized: true, }, }; diff --git a/code/ui/manager/src/components/sidebar/Search.stories.tsx b/code/ui/manager/src/components/sidebar/Search.stories.tsx index f4cfda731a28..633376cfc530 100644 --- a/code/ui/manager/src/components/sidebar/Search.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Search.stories.tsx @@ -9,7 +9,7 @@ import { DEFAULT_REF_ID } from './Sidebar'; import type { Selection } from './types'; const refId = DEFAULT_REF_ID; -const data = { [refId]: { id: refId, url: '/', stories } }; +const data = { [refId]: { id: refId, url: '/', stories, previewInitialized: true } }; const dataset = { hash: data, entries: Object.entries(data) }; const getLastViewed = () => Object.values(stories) From 6ebf577fdf7708f244822b5543fc2a252b228895 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Thu, 12 Jan 2023 13:47:27 +1100 Subject: [PATCH 17/29] One more --- code/ui/manager/src/components/sidebar/Sidebar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ui/manager/src/components/sidebar/Sidebar.tsx b/code/ui/manager/src/components/sidebar/Sidebar.tsx index 0d17362aa6da..21112673d487 100644 --- a/code/ui/manager/src/components/sidebar/Sidebar.tsx +++ b/code/ui/manager/src/components/sidebar/Sidebar.tsx @@ -2,7 +2,7 @@ import React, { useMemo } from 'react'; import { styled } from '@storybook/theming'; import { ScrollArea, Spaced } from '@storybook/components'; -import type { StoriesHash, State } from '@storybook/manager-api'; +import type { State } from '@storybook/manager-api'; import type { API_LoadedRefData } from 'lib/types/src'; import { Heading } from './Heading'; From 48828c75ca9caafaa6c7fbeb57aef3fe48fa829f Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 12 Jan 2023 18:58:54 +0100 Subject: [PATCH 18/29] make changes to typings files to ensure this the check command succeeds --- code/addons/backgrounds/src/typings.d.ts | 1 + code/addons/docs/src/typings.d.ts | 2 ++ code/addons/storyshots-core/src/typings.d.ts | 10 +++++- code/addons/toolbars/src/typings.d.ts | 1 + code/frameworks/angular/src/typings.d.ts | 13 ++++++-- code/lib/cli/src/typings.d.ts | 1 + code/lib/client-api/src/typings.d.ts | 1 + code/lib/core-client/src/typings.d.ts | 1 + code/lib/core-server/src/typings.d.ts | 9 ++++- code/lib/instrumenter/src/typings.d.ts | 12 ++++++- code/lib/manager-api-shim/src/typings.d.ts | 1 + code/lib/manager-api/src/typings.d.ts | 12 ++++++- code/lib/preview-api/src/typings.d.ts | 33 ++++++++++--------- code/lib/preview-web/src/typings.d.ts | 1 + code/lib/preview/src/typings.d.ts | 1 + code/lib/store/src/typings.d.ts | 1 + code/renderers/react/src/typings.d.ts | 1 + code/renderers/svelte/src/typings.d.ts | 1 + code/renderers/vue/src/typings.d.ts | 1 + .../renderers/web-components/src/typings.d.ts | 1 + code/ui/blocks/src/typings.d.ts | 1 + code/ui/manager/src/settings/typings.d.ts | 10 +++++- code/ui/manager/src/typings.d.ts | 17 ++++++++++ 23 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 code/addons/backgrounds/src/typings.d.ts create mode 100644 code/addons/toolbars/src/typings.d.ts create mode 100644 code/lib/client-api/src/typings.d.ts create mode 100644 code/lib/core-client/src/typings.d.ts create mode 100644 code/lib/manager-api-shim/src/typings.d.ts create mode 100644 code/lib/preview-web/src/typings.d.ts create mode 100644 code/lib/preview/src/typings.d.ts create mode 100644 code/lib/store/src/typings.d.ts diff --git a/code/addons/backgrounds/src/typings.d.ts b/code/addons/backgrounds/src/typings.d.ts new file mode 100644 index 000000000000..bfd9e55123ff --- /dev/null +++ b/code/addons/backgrounds/src/typings.d.ts @@ -0,0 +1 @@ +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/addons/docs/src/typings.d.ts b/code/addons/docs/src/typings.d.ts index 5528b7d9f1c6..18881b5e33b7 100644 --- a/code/addons/docs/src/typings.d.ts +++ b/code/addons/docs/src/typings.d.ts @@ -8,3 +8,5 @@ declare module 'vue/dist/vue'; declare module 'sveltedoc-parser' { export function parse(options: any): Promise; } + +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/addons/storyshots-core/src/typings.d.ts b/code/addons/storyshots-core/src/typings.d.ts index 8a3b5786945d..d03407487507 100644 --- a/code/addons/storyshots-core/src/typings.d.ts +++ b/code/addons/storyshots-core/src/typings.d.ts @@ -9,6 +9,14 @@ declare module '@storybook/babel-plugin-require-context-hook/register'; declare var STORYBOOK_ENV: any; declare var STORIES: any; -declare var FEATURES: any; + +declare var FEATURES: + | { + storyStoreV7?: boolean; + breakingChangesV7?: boolean; + argTypeTargetsV7?: boolean; + } + | undefined; + declare var __STORYBOOK_STORY_STORE__: any; declare var __requireContext: any; diff --git a/code/addons/toolbars/src/typings.d.ts b/code/addons/toolbars/src/typings.d.ts new file mode 100644 index 000000000000..bfd9e55123ff --- /dev/null +++ b/code/addons/toolbars/src/typings.d.ts @@ -0,0 +1 @@ +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/frameworks/angular/src/typings.d.ts b/code/frameworks/angular/src/typings.d.ts index 9af3b65be338..204e3428d601 100644 --- a/code/frameworks/angular/src/typings.d.ts +++ b/code/frameworks/angular/src/typings.d.ts @@ -11,9 +11,18 @@ declare var __STORYBOOK_PREVIEW__: any; declare var __STORYBOOK_STORY_STORE__: any; declare var CHANNEL_OPTIONS: any; declare var DOCS_OPTIONS: any; -declare var FEATURES: any; + +declare var FEATURES: + | { + storyStoreV7?: boolean; + interactionsDebugger?: boolean; + breakingChangesV7?: boolean; + argTypeTargetsV7?: boolean; + } + | undefined; + declare var IS_STORYBOOK: any; -declare var LOGLEVEL: any; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; declare var STORIES: any; declare var STORYBOOK_ENV: 'angular'; declare var STORYBOOK_HOOKS_CONTEXT: any; diff --git a/code/lib/cli/src/typings.d.ts b/code/lib/cli/src/typings.d.ts index 409993b0710b..a11ae0c2dac5 100644 --- a/code/lib/cli/src/typings.d.ts +++ b/code/lib/cli/src/typings.d.ts @@ -1 +1,2 @@ declare module 'envinfo'; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/lib/client-api/src/typings.d.ts b/code/lib/client-api/src/typings.d.ts new file mode 100644 index 000000000000..bfd9e55123ff --- /dev/null +++ b/code/lib/client-api/src/typings.d.ts @@ -0,0 +1 @@ +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/lib/core-client/src/typings.d.ts b/code/lib/core-client/src/typings.d.ts new file mode 100644 index 000000000000..bfd9e55123ff --- /dev/null +++ b/code/lib/core-client/src/typings.d.ts @@ -0,0 +1 @@ +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/lib/core-server/src/typings.d.ts b/code/lib/core-server/src/typings.d.ts index 5ba2b96e7559..b51124a22ab7 100644 --- a/code/lib/core-server/src/typings.d.ts +++ b/code/lib/core-server/src/typings.d.ts @@ -9,4 +9,11 @@ declare module '@storybook/manager'; declare module '@discoveryjs/json-ext'; declare module 'watchpack'; -declare var FEATURES: any; +declare var FEATURES: + | { + storyStoreV7?: boolean; + interactionsDebugger?: boolean; + breakingChangesV7?: boolean; + argTypeTargetsV7?: boolean; + } + | undefined; diff --git a/code/lib/instrumenter/src/typings.d.ts b/code/lib/instrumenter/src/typings.d.ts index 2f4fd5fee113..146c167c406a 100644 --- a/code/lib/instrumenter/src/typings.d.ts +++ b/code/lib/instrumenter/src/typings.d.ts @@ -1,5 +1,15 @@ /* eslint-disable no-underscore-dangle, @typescript-eslint/naming-convention */ -declare var FEATURES: any; + +declare var FEATURES: + | { + storyStoreV7?: boolean; + interactionsDebugger?: boolean; + breakingChangesV7?: boolean; + argTypeTargetsV7?: boolean; + } + | undefined; + declare var __STORYBOOK_PREVIEW__: any; declare var __STORYBOOK_ADDON_INTERACTIONS_INSTRUMENTER_STATE__: any; declare var __STORYBOOK_ADDON_INTERACTIONS_INSTRUMENTER__: any; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/lib/manager-api-shim/src/typings.d.ts b/code/lib/manager-api-shim/src/typings.d.ts new file mode 100644 index 000000000000..bfd9e55123ff --- /dev/null +++ b/code/lib/manager-api-shim/src/typings.d.ts @@ -0,0 +1 @@ +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/lib/manager-api/src/typings.d.ts b/code/lib/manager-api/src/typings.d.ts index 6c531d799db0..917dce0019f3 100644 --- a/code/lib/manager-api/src/typings.d.ts +++ b/code/lib/manager-api/src/typings.d.ts @@ -1,6 +1,16 @@ /* eslint-disable no-underscore-dangle, @typescript-eslint/naming-convention */ declare var RELEASE_NOTES_DATA: any; declare var __STORYBOOK_ADDONS_MANAGER: any; -declare var FEATURES: { storyStoreV7?: boolean } | undefined; + +declare var FEATURES: + | { + storyStoreV7?: boolean; + interactionsDebugger?: boolean; + breakingChangesV7?: boolean; + argTypeTargetsV7?: boolean; + } + | undefined; + declare var REFS: any; declare var VERSIONCHECK: any; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/lib/preview-api/src/typings.d.ts b/code/lib/preview-api/src/typings.d.ts index 2c963acb62c8..195827936c63 100644 --- a/code/lib/preview-api/src/typings.d.ts +++ b/code/lib/preview-api/src/typings.d.ts @@ -7,27 +7,30 @@ declare module 'better-opn'; declare module 'open'; declare module '@aw-web-design/x-default-browser'; -type Features = { - storyStoreV7?: boolean; - breakingChangesV7?: boolean; - argTypeTargetsV7?: boolean; -}; +declare var FEATURES: + | { + storyStoreV7?: boolean; + interactionsDebugger?: boolean; + breakingChangesV7?: boolean; + argTypeTargetsV7?: boolean; + } + | undefined; -var FEATURES: Features | undefined; -var STORIES: any; -var DOCS_OPTIONS: any; +declare var STORIES: any; +declare var DOCS_OPTIONS: any; // To enable user code to detect if it is running in Storybook -var IS_STORYBOOK: boolean; +declare var IS_STORYBOOK: boolean; // ClientApi (and StoreStore) are really singletons. However they are not created until the // relevant framework instantiates them via `start.js`. The good news is this happens right away. -var __STORYBOOK_ADDONS_CHANNEL__: any; -var __STORYBOOK_ADDONS_PREVIEW: any; -var __STORYBOOK_CLIENT_API__: import('./modules/client-api/ClientApi').ClientApi; -var __STORYBOOK_PREVIEW__: import('./modules/preview-web/PreviewWeb').PreviewWeb; -var __STORYBOOK_STORY_STORE__: any; -var STORYBOOK_HOOKS_CONTEXT: any; +declare var __STORYBOOK_ADDONS_CHANNEL__: any; +declare var __STORYBOOK_ADDONS_PREVIEW: any; +declare var __STORYBOOK_CLIENT_API__: import('./modules/client-api/ClientApi').ClientApi; +declare var __STORYBOOK_PREVIEW__: import('./modules/preview-web/PreviewWeb').PreviewWeb; +declare var __STORYBOOK_STORY_STORE__: any; +declare var STORYBOOK_HOOKS_CONTEXT: any; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; declare module 'ansi-to-html'; declare class AnsiToHtml { diff --git a/code/lib/preview-web/src/typings.d.ts b/code/lib/preview-web/src/typings.d.ts new file mode 100644 index 000000000000..bfd9e55123ff --- /dev/null +++ b/code/lib/preview-web/src/typings.d.ts @@ -0,0 +1 @@ +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/lib/preview/src/typings.d.ts b/code/lib/preview/src/typings.d.ts new file mode 100644 index 000000000000..bfd9e55123ff --- /dev/null +++ b/code/lib/preview/src/typings.d.ts @@ -0,0 +1 @@ +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/lib/store/src/typings.d.ts b/code/lib/store/src/typings.d.ts new file mode 100644 index 000000000000..bfd9e55123ff --- /dev/null +++ b/code/lib/store/src/typings.d.ts @@ -0,0 +1 @@ +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/renderers/react/src/typings.d.ts b/code/renderers/react/src/typings.d.ts index 440b5a4e24a4..380256bcd4e7 100644 --- a/code/renderers/react/src/typings.d.ts +++ b/code/renderers/react/src/typings.d.ts @@ -45,3 +45,4 @@ declare module 'react-dom/client' { declare var STORYBOOK_ENV: 'react'; declare var FRAMEWORK_OPTIONS: any; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/renderers/svelte/src/typings.d.ts b/code/renderers/svelte/src/typings.d.ts index 09aff65be53a..109dcd62da0e 100644 --- a/code/renderers/svelte/src/typings.d.ts +++ b/code/renderers/svelte/src/typings.d.ts @@ -1 +1,2 @@ declare var STORYBOOK_ENV: 'svelte'; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/renderers/vue/src/typings.d.ts b/code/renderers/vue/src/typings.d.ts index 437680c4e974..daefb35a5f65 100644 --- a/code/renderers/vue/src/typings.d.ts +++ b/code/renderers/vue/src/typings.d.ts @@ -1 +1,2 @@ declare var STORYBOOK_ENV: 'vue'; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/renderers/web-components/src/typings.d.ts b/code/renderers/web-components/src/typings.d.ts index 5563d6cc27a6..1dafbd1d10c9 100644 --- a/code/renderers/web-components/src/typings.d.ts +++ b/code/renderers/web-components/src/typings.d.ts @@ -2,3 +2,4 @@ declare var STORYBOOK_ENV: 'web-components'; declare var __STORYBOOK_CUSTOM_ELEMENTS_MANIFEST__: any; declare var __STORYBOOK_CUSTOM_ELEMENTS__: any; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/ui/blocks/src/typings.d.ts b/code/ui/blocks/src/typings.d.ts index da56fcf64777..b773bc6bcc58 100644 --- a/code/ui/blocks/src/typings.d.ts +++ b/code/ui/blocks/src/typings.d.ts @@ -5,3 +5,4 @@ declare module '*.md'; declare var __DOCS_CONTEXT__: any; declare var PREVIEW_URL: any; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; diff --git a/code/ui/manager/src/settings/typings.d.ts b/code/ui/manager/src/settings/typings.d.ts index 176b5f3d014a..8e01308b8bc8 100644 --- a/code/ui/manager/src/settings/typings.d.ts +++ b/code/ui/manager/src/settings/typings.d.ts @@ -2,7 +2,15 @@ /* eslint-disable @typescript-eslint/naming-convention */ declare module '@storybook/components/src/treeview/utils'; -declare var FEATURES: any; +declare var FEATURES: + | { + storyStoreV7?: boolean; + interactionsDebugger?: boolean; + breakingChangesV7?: boolean; + argTypeTargetsV7?: boolean; + } + | undefined; + declare var SERVER_CHANNEL_URL: any; declare var __REACT__: any; diff --git a/code/ui/manager/src/typings.d.ts b/code/ui/manager/src/typings.d.ts index 6919b2826ee3..203d098a14bf 100644 --- a/code/ui/manager/src/typings.d.ts +++ b/code/ui/manager/src/typings.d.ts @@ -1,5 +1,22 @@ +/* eslint-disable no-underscore-dangle, @typescript-eslint/naming-convention */ declare module 'chromatic/isChromatic'; declare var DOCS_OPTIONS: any; declare var CONFIG_TYPE: any; declare var PREVIEW_URL: any; + +declare var __STORYBOOK_ADDONS_MANAGER: any; +declare var RELEASE_NOTES_DATA: any; + +declare var FEATURES: + | { + storyStoreV7?: boolean; + interactionsDebugger?: boolean; + breakingChangesV7?: boolean; + argTypeTargetsV7?: boolean; + } + | undefined; + +declare var REFS: any; +declare var VERSIONCHECK: any; +declare var LOGLEVEL: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent' | undefined; From 4ed7408055e43ae6bfab5a44f663a10a163bc7fc Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 13 Jan 2023 13:01:20 +1100 Subject: [PATCH 19/29] Update stories->index and add back-compatibility --- code/lib/manager-api/src/modules/refs.ts | 16 +- code/lib/manager-api/src/modules/stories.ts | 119 +++--- code/lib/manager-api/src/tests/refs.test.js | 18 +- .../lib/manager-api/src/tests/stories.test.ts | 360 +++++++++++------- code/lib/types/src/modules/api-stories.ts | 4 +- code/lib/types/src/modules/api.ts | 6 +- 6 files changed, 310 insertions(+), 213 deletions(-) diff --git a/code/lib/manager-api/src/modules/refs.ts b/code/lib/manager-api/src/modules/refs.ts index ac3518e05ee6..c56f08fed9e3 100644 --- a/code/lib/manager-api/src/modules/refs.ts +++ b/code/lib/manager-api/src/modules/refs.ts @@ -6,7 +6,7 @@ import type { API_Refs, API_SetRefData, SetStoriesStoryData, - API_StoriesHash, + API_IndexHash, API_StoryMapper, } from '@storybook/types'; // eslint-disable-next-line import/no-cycle @@ -56,10 +56,10 @@ export const defaultStoryMapper: API_StoryMapper = (b, a) => { return { ...a, kind: a.kind.replace('|', '/') }; }; -const addRefIds = (input: API_StoriesHash, ref: API_ComposedRef): API_StoriesHash => { +const addRefIds = (input: API_IndexHash, ref: API_ComposedRef): API_IndexHash => { return Object.entries(input).reduce((acc, [id, item]) => { return { ...acc, [id]: { ...item, refId: ref.id } }; - }, {} as API_StoriesHash); + }, {} as API_IndexHash); }; async function handleRequest( @@ -245,18 +245,18 @@ export const init: ModuleFn = ( const { storyMapper = defaultStoryMapper } = provider.getConfig(); const ref = api.getRefs()[id]; - let storiesHash: API_StoriesHash; + let index: API_IndexHash; if (setStoriesData) { - storiesHash = transformSetStoriesStoryDataToStoriesHash( + index = transformSetStoriesStoryDataToStoriesHash( map(setStoriesData, ref, { storyMapper }), { provider, docsOptions } ); } else if (storyIndex) { - storiesHash = transformStoryIndexToStoriesHash(storyIndex, { provider, docsOptions }); + index = transformStoryIndexToStoriesHash(storyIndex, { provider, docsOptions }); } - if (storiesHash) storiesHash = addRefIds(storiesHash, ref); + if (index) index = addRefIds(index, ref); - api.updateRef(id, { stories: storiesHash, ...rest }); + api.updateRef(id, { index, ...rest }); }, updateRef: (id, data) => { diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index a5e61da79355..8eaa9f95225a 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -15,7 +15,7 @@ import { CONFIG_ERROR, CURRENT_STORY_WAS_SET, } from '@storybook/core-events'; -import { logger } from '@storybook/client-logger'; +import { deprecate, logger } from '@storybook/client-logger'; import type { StoryId, @@ -25,10 +25,10 @@ import type { API_LeafEntry, API_PreparedStoryIndex, SetStoriesPayload, - API_StoriesHash, API_StoryEntry, StoryIndex, API_LoadedRefData, + API_IndexHash, } from '@storybook/types'; // eslint-disable-next-line import/no-cycle import { getEventMetadata } from '../lib/events'; @@ -55,6 +55,19 @@ type StoryUpdate = Pick(parameterName?: ParameterName): S; updateStoryArgs(story: API_StoryEntry, newArgs: Args): void; resetStoryArgs: (story: API_StoryEntry, argNames?: string[]) => void; - findLeafEntry(StoriesHash: API_StoriesHash, storyId: StoryId): API_LeafEntry; - findLeafStoryId(StoriesHash: API_StoriesHash, storyId: StoryId): StoryId; + findLeafEntry(index: API_IndexHash, storyId: StoryId): API_LeafEntry; + findLeafStoryId(index: API_IndexHash, storyId: StoryId): StoryId; findSiblingStoryId( storyId: StoryId, - hash: API_StoriesHash, + index: API_IndexHash, direction: Direction, toSiblingGroup: boolean // when true, skip over leafs within the same group ): StoryId; @@ -131,11 +144,11 @@ export const init: ModuleFn = ({ return data.type === 'story' ? data.prepared : true; }, resolveStory: (storyId, refId) => { - const { refs, stories } = store.getState(); + const { refs, index } = store.getState(); if (refId) { - return refs[refId].stories ? refs[refId].stories[storyId] : undefined; + return refs[refId].index ? refs[refId].index[storyId] : undefined; } - return stories ? stories[storyId] : undefined; + return index ? index[storyId] : undefined; }, getCurrentStoryData: () => { const { storyId, refId } = store.getState(); @@ -167,7 +180,7 @@ export const init: ModuleFn = ({ return parameters || undefined; }, jumpToComponent: (direction) => { - const { stories, storyId, refs, refId } = store.getState(); + const { index, storyId, refs, refId } = store.getState(); const story = api.getData(storyId, refId); // cannot navigate when there's no current selection @@ -175,7 +188,7 @@ export const init: ModuleFn = ({ return; } - const hash = refId ? refs[refId].stories || {} : stories; + const hash = refId ? refs[refId].index || {} : index; const result = api.findSiblingStoryId(storyId, hash, direction, true); if (result) { @@ -183,7 +196,7 @@ export const init: ModuleFn = ({ } }, jumpToStory: (direction) => { - const { stories, storyId, refs, refId } = store.getState(); + const { index, storyId, refs, refId } = store.getState(); const story = api.getData(storyId, refId); // cannot navigate when there's no current selection @@ -191,7 +204,7 @@ export const init: ModuleFn = ({ return; } - const hash = story.refId ? refs[story.refId].stories : stories; + const hash = story.refId ? refs[story.refId].index : index; const result = api.findSiblingStoryId(storyId, hash, direction, false); if (result) { @@ -199,8 +212,8 @@ export const init: ModuleFn = ({ } }, selectFirstStory: () => { - const { stories } = store.getState(); - const firstStory = Object.keys(stories).find((id) => stories[id].type === 'story'); + const { index } = store.getState(); + const firstStory = Object.keys(index).find((id) => index[id].type === 'story'); if (firstStory) { api.selectStory(firstStory); @@ -211,9 +224,9 @@ export const init: ModuleFn = ({ }, selectStory: (titleOrId = undefined, name = undefined, options = {}) => { const { ref } = options; - const { storyId, stories, refs } = store.getState(); + const { storyId, index, refs } = store.getState(); - const hash = ref ? refs[ref].stories : stories; + const hash = ref ? refs[ref].index : index; const kindSlug = storyId?.split('--', 2)[0]; @@ -248,50 +261,50 @@ export const init: ModuleFn = ({ } } }, - findLeafEntry(stories, storyId) { - const entry = stories[storyId]; + findLeafEntry(index, storyId) { + const entry = index[storyId]; if (entry.type === 'docs' || entry.type === 'story') { return entry; } const childStoryId = entry.children[0]; - return api.findLeafEntry(stories, childStoryId); + return api.findLeafEntry(index, childStoryId); }, - findLeafStoryId(stories, storyId) { - return api.findLeafEntry(stories, storyId)?.id; + findLeafStoryId(index, storyId) { + return api.findLeafEntry(index, storyId)?.id; }, - findSiblingStoryId(storyId, hash, direction, toSiblingGroup) { + findSiblingStoryId(storyId, index, direction, toSiblingGroup) { if (toSiblingGroup) { - const lookupList = getComponentLookupList(hash); - const index = lookupList.findIndex((i) => i.includes(storyId)); + const lookupList = getComponentLookupList(index); + const position = lookupList.findIndex((i) => i.includes(storyId)); // cannot navigate beyond fist or last - if (index === lookupList.length - 1 && direction > 0) { + if (position === lookupList.length - 1 && direction > 0) { return; } - if (index === 0 && direction < 0) { + if (position === 0 && direction < 0) { return; } - if (lookupList[index + direction]) { + if (lookupList[position + direction]) { // eslint-disable-next-line consistent-return - return lookupList[index + direction][0]; + return lookupList[position + direction][0]; } return; } - const lookupList = getStoriesLookupList(hash); - const index = lookupList.indexOf(storyId); + const lookupList = getStoriesLookupList(index); + const position = lookupList.indexOf(storyId); // cannot navigate beyond fist or last - if (index === lookupList.length - 1 && direction > 0) { + if (position === lookupList.length - 1 && direction > 0) { return; } - if (index === 0 && direction < 0) { + if (position === 0 && direction < 0) { return; } // eslint-disable-next-line consistent-return - return lookupList[index + direction]; + return lookupList[position + direction]; }, updateStoryArgs: (story, updatedArgs) => { const { id: storyId, refId } = story; @@ -337,9 +350,9 @@ export const init: ModuleFn = ({ }); // Now we need to patch in the existing prepared stories - const oldHash = store.getState().stories; + const oldHash = store.getState().index; - await store.setState({ stories: addPreparedStories(newHash, oldHash) }); + await store.setState({ index: addPreparedStories(newHash, oldHash) }); }, updateStory: async ( storyId: StoryId, @@ -347,19 +360,19 @@ export const init: ModuleFn = ({ ref?: API_ComposedRef ): Promise => { if (!ref) { - const { stories } = store.getState(); - stories[storyId] = { - ...stories[storyId], + const { index } = store.getState(); + index[storyId] = { + ...index[storyId], ...update, } as API_StoryEntry; - await store.setState({ stories }); + await store.setState({ index }); } else { - const { id: refId, stories } = ref; - stories[storyId] = { - ...stories[storyId], + const { id: refId, index } = ref; + index[storyId] = { + ...index[storyId], ...update, } as API_StoryEntry; - await fullAPI.updateRef(refId, { stories }); + await fullAPI.updateRef(refId, { index }); } }, }; @@ -431,13 +444,13 @@ export const init: ModuleFn = ({ } if (sourceType === 'local') { - const { storyId, stories, refId } = store.getState(); + const { storyId, index, refId } = store.getState(); // create a list of related stories to be preloaded const toBePreloaded = Array.from( new Set([ - api.findSiblingStoryId(storyId, stories, 1, true), - api.findSiblingStoryId(storyId, stories, -1, true), + api.findSiblingStoryId(storyId, index, 1, true), + api.findSiblingStoryId(storyId, index, -1, true), ]) ).filter(Boolean); @@ -526,6 +539,20 @@ export const init: ModuleFn = ({ viewMode: initialViewMode, hasCalledSetOptions: false, previewInitialized: false, + + // deprecated fields for back-compat + get storiesHash() { + deprecate('state.storiesHash is deprecated, please use state.index'); + return this.index || {}; + }, + get storiesConfigured() { + deprecate('state.storiesConfigured is deprecated, please use state.previewInitialized'); + return this.previewInitialized; + }, + get storiesFailed() { + deprecate('state.storiesFailed is deprecated, please use state.indexError'); + return this.indexError; + }, }, init: initModule, }; diff --git a/code/lib/manager-api/src/tests/refs.test.js b/code/lib/manager-api/src/tests/refs.test.js index 7e97b56626ca..d2ddd297e397 100644 --- a/code/lib/manager-api/src/tests/refs.test.js +++ b/code/lib/manager-api/src/tests/refs.test.js @@ -253,6 +253,7 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", + "index": undefined, "indexError": Object { "message": "Error: Loading of ref failed at fetch (lib/api/src/modules/refs.ts) @@ -264,7 +265,6 @@ describe('Refs API', () => { Please check your dev-tools network tab.", }, - "stories": undefined, "title": "Fake", "type": "auto-inject", "url": "https://example.com", @@ -339,7 +339,7 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", - "stories": Object {}, + "index": Object {}, "title": "Fake", "type": "lazy", "url": "https://example.com", @@ -416,7 +416,7 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", - "stories": Object {}, + "index": Object {}, "title": "Fake", "type": "lazy", "url": "https://example.com", @@ -493,8 +493,8 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", + "index": undefined, "loginUrl": "https://example.com/login", - "stories": undefined, "title": "Fake", "type": "auto-inject", "url": "https://example.com", @@ -634,8 +634,8 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", + "index": undefined, "loginUrl": "https://example.com/login", - "stories": undefined, "title": "Fake", "type": "auto-inject", "url": "https://example.com", @@ -715,7 +715,7 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", - "stories": Object {}, + "index": Object {}, "title": "Fake", "type": "lazy", "url": "https://example.com", @@ -792,7 +792,7 @@ describe('Refs API', () => { "refs": Object { "fake": Object { "id": "fake", - "stories": Object {}, + "index": Object {}, "title": "Fake", "type": "lazy", "url": "https://example.com", @@ -859,7 +859,7 @@ describe('Refs API', () => { }); const { refs } = store.setState.mock.calls[0][0]; - const hash = refs.fake.stories; + const hash = refs.fake.index; // We need exact key ordering, even if in theory JS doesn't guarantee it expect(Object.keys(hash)).toEqual([ @@ -915,7 +915,7 @@ describe('Refs API', () => { }); const { refs } = store.setState.mock.calls[0][0]; - const hash = refs.fake.stories; + const hash = refs.fake.index; // We need exact key ordering, even if in theory JS doesn't guarantee it expect(Object.keys(hash)).toEqual(['component-a', 'component-a--docs']); diff --git a/code/lib/manager-api/src/tests/stories.test.ts b/code/lib/manager-api/src/tests/stories.test.ts index d0898709c78b..4ece445bd7a9 100644 --- a/code/lib/manager-api/src/tests/stories.test.ts +++ b/code/lib/manager-api/src/tests/stories.test.ts @@ -81,6 +81,18 @@ function createMockStore(initialState = {}) { } as any as Store; } +function initStoriesAndSetState({ store, ...options }: any) { + const { state, ...result } = initStories({ store, ...options } as any); + + // Remove deprecated fields (which would trigger warnings) + delete state.storiesHash; + delete state.storiesConfigured; + delete state.storiesFailed; + store?.setState(state); + + return { state, ...result }; +} + const provider = { getConfig: jest.fn().mockReturnValue({}), serverChannel: mockChannel() }; beforeEach(() => { @@ -102,11 +114,16 @@ beforeEach(() => { describe('stories API', () => { it('sets a sensible initialState', () => { - const { state } = initStories({ + const { state } = initStoriesAndSetState({ storyId: 'id', viewMode: 'story', } as ModuleArgs); + // Remove deprecated fields (which would trigger warnings) + delete state.storiesHash; + delete state.storiesConfigured; + delete state.storiesFailed; + expect(state).toEqual({ previewInitialized: false, storyId: 'id', @@ -121,27 +138,27 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter()); - const { api } = initStories({ store, navigate, provider, fullAPI } as any); + const { api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); api.setIndex({ v: 4, entries: mockEntries }); - const { stories: storedStoriesHash } = store.getState(); + const { index } = store.getState(); // We need exact key ordering, even if in theory JS doesn't guarantee it - expect(Object.keys(storedStoriesHash)).toEqual([ + expect(Object.keys(index)).toEqual([ 'component-a', 'component-a--story-1', 'component-a--story-2', 'component-b', 'component-b--story-3', ]); - expect(storedStoriesHash['component-a']).toMatchObject({ + expect(index['component-a']).toMatchObject({ type: 'component', id: 'component-a', children: ['component-a--story-1', 'component-a--story-2'], }); - expect(storedStoriesHash['component-a--story-1']).toMatchObject({ + expect(index['component-a--story-1']).toMatchObject({ type: 'story', id: 'component-a--story-1', parent: 'component-a', @@ -150,7 +167,7 @@ describe('stories API', () => { prepared: false, }); expect( - (storedStoriesHash['component-a--story-1'] as API_StoryEntry as API_StoryEntry).args + (index['component-a--story-1'] as API_StoryEntry as API_StoryEntry).args ).toBeUndefined(); }); @@ -159,7 +176,7 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter()); - const { api } = initStories({ store, navigate, provider, fullAPI } as any); + const { api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); api.setIndex({ @@ -174,23 +191,23 @@ describe('stories API', () => { }, }, }); - const { stories: storedStoriesHash } = store.getState(); + const { index } = store.getState(); // We need exact key ordering, even if in theory JS doesn't guarantee it - expect(Object.keys(storedStoriesHash)).toEqual([ + expect(Object.keys(index)).toEqual([ 'design-system', 'design-system-some-component', 'design-system-some-component--my-story', ]); - expect(storedStoriesHash['design-system']).toMatchObject({ + expect(index['design-system']).toMatchObject({ type: 'root', name: 'Design System', // root name originates from `kind`, so it gets trimmed }); - expect(storedStoriesHash['design-system-some-component']).toMatchObject({ + expect(index['design-system-some-component']).toMatchObject({ type: 'component', name: 'Some Component', // component name originates from `kind`, so it gets trimmed }); - expect(storedStoriesHash['design-system-some-component--my-story']).toMatchObject({ + expect(index['design-system-some-component--my-story']).toMatchObject({ type: 'story', title: ' Design System / Some Component ', // title is kept as-is, because it may be used as identifier name: ' My Story ', // story name is kept as-is, because it's set directly on the story @@ -202,7 +219,7 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter()); - const { api } = initStories({ store, navigate, provider, fullAPI } as any); + const { api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); api.setIndex({ @@ -218,10 +235,10 @@ describe('stories API', () => { ...mockEntries, }, }); - const { stories: storedStoriesHash } = store.getState(); + const { index } = store.getState(); // We need exact key ordering, even if in theory JS doesn't guarantee it - expect(Object.keys(storedStoriesHash)).toEqual([ + expect(Object.keys(index)).toEqual([ 'component-a', 'component-a--story-1', 'component-a--story-2', @@ -231,7 +248,7 @@ describe('stories API', () => { 'root-first', 'root-first--story-1', ]); - expect(storedStoriesHash.root).toMatchObject({ + expect(index.root).toMatchObject({ type: 'root', id: 'root', children: ['root-first'], @@ -243,7 +260,7 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter()); - const { api } = initStories({ store, navigate, provider, fullAPI } as any); + const { api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); provider.getConfig.mockReturnValue({ sidebar: { showRoots: true } }); @@ -260,22 +277,22 @@ describe('stories API', () => { }, }); - const { stories: storedStoriesHash } = store.getState(); + const { index } = store.getState(); // We need exact key ordering, even if in theory JS doens't guarantee it - expect(Object.keys(storedStoriesHash)).toEqual(['a', 'a-b', 'a-b--1']); - expect(storedStoriesHash.a).toMatchObject({ + expect(Object.keys(index)).toEqual(['a', 'a-b', 'a-b--1']); + expect(index.a).toMatchObject({ type: 'root', id: 'a', children: ['a-b'], }); - expect(storedStoriesHash['a-b']).toMatchObject({ + expect(index['a-b']).toMatchObject({ type: 'component', id: 'a-b', parent: 'a', children: ['a-b--1'], }); - expect(storedStoriesHash['a-b--1']).toMatchObject({ + expect(index['a-b--1']).toMatchObject({ type: 'story', id: 'a-b--1', parent: 'a-b', @@ -289,7 +306,7 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter()); - const { api } = initStories({ store, navigate, provider, fullAPI } as any); + const { api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); provider.getConfig.mockReturnValue({ sidebar: { showRoots: true } }); @@ -306,16 +323,16 @@ describe('stories API', () => { }, }); - const { stories: storedStoriesHash } = store.getState(); + const { index } = store.getState(); // We need exact key ordering, even if in theory JS doens't guarantee it - expect(Object.keys(storedStoriesHash)).toEqual(['a', 'a--1']); - expect(storedStoriesHash.a).toMatchObject({ + expect(Object.keys(index)).toEqual(['a', 'a--1']); + expect(index.a).toMatchObject({ type: 'component', id: 'a', children: ['a--1'], }); - expect(storedStoriesHash['a--1']).toMatchObject({ + expect(index['a--1']).toMatchObject({ type: 'story', id: 'a--1', parent: 'a', @@ -331,7 +348,7 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter()); - const { api } = initStories({ store, navigate, provider, fullAPI } as any); + const { api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); provider.getConfig.mockReturnValue({ sidebar: { showRoots: true } }); @@ -344,17 +361,17 @@ describe('stories API', () => { }, }); - const { stories: storedStoriesHash } = store.getState(); + const { index } = store.getState(); // We need exact key ordering, even if in theory JS doens't guarantee it - expect(Object.keys(storedStoriesHash)).toEqual(['a', 'a--1', 'a--2', 'b', 'b--1']); - expect(storedStoriesHash.a).toMatchObject({ + expect(Object.keys(index)).toEqual(['a', 'a--1', 'a--2', 'b', 'b--1']); + expect(index.a).toMatchObject({ type: 'component', id: 'a', children: ['a--1', 'a--2'], }); - expect(storedStoriesHash.b).toMatchObject({ + expect(index.b).toMatchObject({ type: 'component', id: 'b', children: ['b--1'], @@ -367,7 +384,7 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter()); - const { api } = initStories({ store, navigate, provider, fullAPI } as any); + const { api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); api.setIndex({ @@ -385,9 +402,9 @@ describe('stories API', () => { }, }); - const { stories: storedStoriesHash } = store.getState(); + const { index } = store.getState(); - expect(storedStoriesHash['prepared--story']).toMatchObject({ + expect(index['prepared--story']).toMatchObject({ type: 'story', id: 'prepared--story', parent: 'prepared', @@ -404,7 +421,7 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter(), { setOptions: jest.fn() }); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); init(); @@ -417,7 +434,7 @@ describe('stories API', () => { }); // Let the promise/await chain resolve await new Promise((r) => setTimeout(r, 0)); - expect(store.getState().stories['component-a--story-1'] as API_StoryEntry).toMatchObject({ + expect(store.getState().index['component-a--story-1'] as API_StoryEntry).toMatchObject({ prepared: true, parameters: { a: 'b' }, args: { c: 'd' }, @@ -427,7 +444,7 @@ describe('stories API', () => { // Let the promise/await chain resolve await new Promise((r) => setTimeout(r, 0)); - expect(store.getState().stories['component-a--story-1'] as API_StoryEntry).toMatchObject({ + expect(store.getState().index['component-a--story-1'] as API_StoryEntry).toMatchObject({ prepared: true, parameters: { a: 'b' }, args: { c: 'd' }, @@ -473,15 +490,15 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter()); - const { api } = initStories({ store, navigate, provider, fullAPI } as any); + const { api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); api.setIndex({ v: 4, entries: docsEntries }); - const { stories: storedStoriesHash } = store.getState(); + const { index } = store.getState(); // We need exact key ordering, even if in theory JS doesn't guarantee it - expect(Object.keys(storedStoriesHash)).toEqual([ + expect(Object.keys(index)).toEqual([ 'component-a', 'component-a--page', 'component-a--story-2', @@ -490,10 +507,10 @@ describe('stories API', () => { 'component-c', 'component-c--story-4', ]); - expect(storedStoriesHash['component-a--page'].type).toBe('story'); - expect(storedStoriesHash['component-a--story-2'].type).toBe('story'); - expect(storedStoriesHash['component-b--docs'].type).toBe('docs'); - expect(storedStoriesHash['component-c--story-4'].type).toBe('story'); + expect(index['component-a--page'].type).toBe('story'); + expect(index['component-a--story-2'].type).toBe('story'); + expect(index['component-b--docs'].type).toBe('docs'); + expect(index['component-c--story-4'].type).toBe('story'); }); describe('when DOCS_MODE = true', () => { @@ -502,7 +519,7 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter()); - const { api } = initStories({ + const { api } = initStoriesAndSetState({ store, navigate, provider, @@ -513,9 +530,9 @@ describe('stories API', () => { api.setIndex({ v: 4, entries: docsEntries }); - const { stories: storedStoriesHash } = store.getState(); + const { index } = store.getState(); - expect(Object.keys(storedStoriesHash)).toEqual(['component-b', 'component-b--docs']); + expect(Object.keys(index)).toEqual(['component-b', 'component-b--docs']); }); }); }); @@ -527,7 +544,7 @@ describe('stories API', () => { const navigate = jest.fn(); const store = createMockStore(); - const { init, api } = initStories({ store, navigate, provider, fullAPI } as any); + const { init, api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api, { setIndex: jest.fn(), setOptions: jest.fn(), @@ -544,7 +561,7 @@ describe('stories API', () => { const navigate = jest.fn(); const store = createMockStore(); - const { init, api } = initStories({ store, navigate, provider, fullAPI } as any); + const { init, api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api, { setIndex: jest.fn(), setOptions: jest.fn(), @@ -570,7 +587,7 @@ describe('stories API', () => { text: async () => new Error('sorting error'), } as any as Response) ); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); await init(); @@ -586,7 +603,7 @@ describe('stories API', () => { setIndex: jest.fn(), }); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); (global.fetch as jest.Mock>).mockClear(); @@ -608,9 +625,9 @@ describe('stories API', () => { // Let the promise/await chain resolve await new Promise((r) => setTimeout(r, 0)); - const { stories: storedStoriesHash } = store.getState(); + const { index } = store.getState(); - expect(Object.keys(storedStoriesHash)).toEqual(['component-a', 'component-a--story-1']); + expect(Object.keys(index)).toEqual(['component-a', 'component-a--story-1']); }); }); @@ -623,7 +640,7 @@ describe('stories API', () => { }, }); const store = createMockStore({}); - const { init, api } = initStories({ store, navigate, provider, fullAPI } as any); + const { init, api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); init(); @@ -640,7 +657,7 @@ describe('stories API', () => { }, }); const store = createMockStore({ viewMode: 'story', storyId: 'a--1' }); - initStories({ store, navigate, provider, fullAPI } as any); + initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); fullAPI.emit(STORY_SPECIFIED, { storyId: 'a--1', viewMode: 'story' }); @@ -655,7 +672,7 @@ describe('stories API', () => { }, }); const store = createMockStore({ viewMode: 'settings', storyId: 'about' }); - initStories({ store, navigate, provider, fullAPI } as any); + initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); fullAPI.emit(STORY_SPECIFIED, { storyId: 'a--1', viewMode: 'story' }); @@ -668,7 +685,7 @@ describe('stories API', () => { const navigate = jest.fn(); const fullAPI = Object.assign(new EventEmitter()); const store = createMockStore({}); - const { init, api } = initStories({ store, navigate, provider, fullAPI } as any); + const { init, api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); await init(); @@ -681,13 +698,13 @@ describe('stories API', () => { const navigate = jest.fn(); const fullAPI = Object.assign(new EventEmitter(), { updateRef: jest.fn() }); const store = createMockStore(); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); getEventMetadataMock.mockReturnValueOnce({ sourceType: 'external', - ref: { id: 'refId', stories: { 'a--1': { args: { a: 'b' } } } }, + ref: { id: 'refId', index: { 'a--1': { args: { a: 'b' } } } }, } as any); await init(); fullAPI.emit(CURRENT_STORY_WAS_SET, { id: 'a--1' }); @@ -728,21 +745,21 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = new EventEmitter(); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); const { setIndex } = Object.assign(fullAPI, api); setIndex({ v: 4, entries: preparedEntries }); - const { stories: initialStoriesHash } = store.getState(); - expect((initialStoriesHash['a--1'] as API_StoryEntry).args).toEqual({ a: 'b' }); - expect((initialStoriesHash['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); + const { index } = store.getState(); + expect((index['a--1'] as API_StoryEntry).args).toEqual({ a: 'b' }); + expect((index['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); init(); fullAPI.emit(STORY_ARGS_UPDATED, { storyId: 'a--1', args: { foo: 'bar' } }); - const { stories: changedStoriesHash } = store.getState(); - expect((changedStoriesHash['a--1'] as API_StoryEntry).args).toEqual({ foo: 'bar' }); - expect((changedStoriesHash['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); + const { index: changedIndex } = store.getState(); + expect((changedIndex['a--1'] as API_StoryEntry).args).toEqual({ foo: 'bar' }); + expect((changedIndex['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); }); it('changes reffed args properly, per story when receiving STORY_ARGS_UPDATED', () => { @@ -750,7 +767,7 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = new EventEmitter(); - const { init, api } = initStories({ store, navigate, provider, fullAPI } as any); + const { init, api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api, { updateRef: jest.fn(), }); @@ -758,11 +775,11 @@ describe('stories API', () => { init(); getEventMetadataMock.mockReturnValueOnce({ sourceType: 'external', - ref: { id: 'refId', stories: { 'a--1': { args: { a: 'b' } } } }, + ref: { id: 'refId', index: { 'a--1': { args: { a: 'b' } } } }, } as any); fullAPI.emit(STORY_ARGS_UPDATED, { storyId: 'a--1', args: { foo: 'bar' } }); expect((fullAPI as any).updateRef).toHaveBeenCalledWith('refId', { - stories: { 'a--1': { args: { foo: 'bar' } } }, + index: { 'a--1': { args: { foo: 'bar' } } }, }); }); @@ -773,7 +790,7 @@ describe('stories API', () => { const fullAPI = { emit, on }; const store = createMockStore(); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); const { setIndex } = Object.assign(fullAPI, api); setIndex({ v: 4, entries: preparedEntries }); @@ -788,9 +805,9 @@ describe('stories API', () => { }, }); - const { stories: changedStoriesHash } = store.getState(); - expect((changedStoriesHash['a--1'] as API_StoryEntry).args).toEqual({ a: 'b' }); - expect((changedStoriesHash['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); + const { index } = store.getState(); + expect((index['a--1'] as API_StoryEntry).args).toEqual({ a: 'b' }); + expect((index['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); }); it('updateStoryArgs emits UPDATE_STORY_ARGS to the right frame', () => { @@ -800,7 +817,7 @@ describe('stories API', () => { const fullAPI = { emit, on }; const store = createMockStore(); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); const { setIndex } = Object.assign(fullAPI, api); setIndex({ v: 4, entries: preparedEntries }); @@ -824,7 +841,7 @@ describe('stories API', () => { const fullAPI = { emit, on }; const store = createMockStore(); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); const { setIndex } = Object.assign(fullAPI, api); setIndex({ v: 4, entries: preparedEntries }); @@ -839,9 +856,9 @@ describe('stories API', () => { }, }); - const { stories: changedStoriesHash } = store.getState(); - expect((changedStoriesHash['a--1'] as API_StoryEntry).args).toEqual({ a: 'b' }); - expect((changedStoriesHash['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); + const { index } = store.getState(); + expect((index['a--1'] as API_StoryEntry).args).toEqual({ a: 'b' }); + expect((index['b--1'] as API_StoryEntry).args).toEqual({ x: 'y' }); }); it('resetStoryArgs emits RESET_STORY_ARGS to the right frame', () => { @@ -851,7 +868,7 @@ describe('stories API', () => { const fullAPI = { emit, on }; const store = createMockStore(); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); const { setIndex } = Object.assign(fullAPI, api); setIndex({ v: 4, entries: preparedEntries }); @@ -916,11 +933,17 @@ describe('stories API', () => { describe('jumpToStory', () => { it('works forward', () => { const navigate = jest.fn(); - const store = createMockStore({ storyId: 'a--1', viewMode: 'story' }); + const store = createMockStore(); const { api: { setIndex, jumpToStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ + store, + storyId: 'a--1', + viewMode: 'story', + navigate, + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); jumpToStory(1); @@ -929,11 +952,17 @@ describe('stories API', () => { it('works backwards', () => { const navigate = jest.fn(); - const store = createMockStore({ storyId: 'a--2', viewMode: 'story' }); + const store = createMockStore(); const { api: { setIndex, jumpToStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ + store, + storyId: 'a--2', + viewMode: 'story', + navigate, + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); jumpToStory(-1); @@ -942,15 +971,14 @@ describe('stories API', () => { it('does nothing if you are at the last story and go forward', () => { const navigate = jest.fn(); - const store = createMockStore({ - storyId: 'custom-id--1', - viewMode: 'story', - }); + const store = createMockStore(); const { api: { setIndex, jumpToStory }, - } = initStories({ + } = initStoriesAndSetState({ store, + storyId: 'custom-id--1', + viewMode: 'story', navigate, provider, } as any); @@ -962,11 +990,17 @@ describe('stories API', () => { it('does nothing if you are at the first story and go backward', () => { const navigate = jest.fn(); - const store = createMockStore({ storyId: 'a--1', viewMode: 'story' }); + const store = createMockStore(); const { api: { setIndex, jumpToStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ + store, + storyId: 'a--1', + viewMode: 'story', + navigate, + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); jumpToStory(-1); @@ -979,7 +1013,7 @@ describe('stories API', () => { const { api: { setIndex, jumpToStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ store, navigate, provider } as any); setIndex({ v: 4, entries: navigationEntries }); jumpToStory(1); @@ -995,12 +1029,10 @@ describe('stories API', () => { const storyId = 'a--1'; const { api: { setIndex, findSiblingStoryId }, - state, - } = initStories({ store, navigate, storyId, viewMode: 'story', provider } as any); - store.setState(state); + } = initStoriesAndSetState({ store, navigate, storyId, viewMode: 'story', provider } as any); setIndex({ v: 4, entries: navigationEntries }); - const result = findSiblingStoryId(storyId, store.getState().stories, 1, false); + const result = findSiblingStoryId(storyId, store.getState().index, 1, false); expect(result).toBe('a--2'); }); it('works forward toSiblingGroup', () => { @@ -1010,12 +1042,10 @@ describe('stories API', () => { const storyId = 'a--1'; const { api: { setIndex, findSiblingStoryId }, - state, - } = initStories({ store, navigate, storyId, viewMode: 'story', provider } as any); - store.setState(state); + } = initStoriesAndSetState({ store, navigate, storyId, viewMode: 'story', provider } as any); setIndex({ v: 4, entries: navigationEntries }); - const result = findSiblingStoryId(storyId, store.getState().stories, 1, true); + const result = findSiblingStoryId(storyId, store.getState().index, 1, true); expect(result).toBe('b-c--1'); }); }); @@ -1026,9 +1056,13 @@ describe('stories API', () => { const { api: { setIndex, jumpToComponent }, - state, - } = initStories({ store, navigate, storyId: 'a--1', viewMode: 'story', provider } as any); - store.setState(state); + } = initStoriesAndSetState({ + store, + navigate, + storyId: 'a--1', + viewMode: 'story', + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); jumpToComponent(1); @@ -1041,9 +1075,13 @@ describe('stories API', () => { const { api: { setIndex, jumpToComponent }, - state, - } = initStories({ store, navigate, storyId: 'b-c--1', viewMode: 'story', provider } as any); - store.setState(state); + } = initStoriesAndSetState({ + store, + navigate, + storyId: 'b-c--1', + viewMode: 'story', + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); jumpToComponent(-1); @@ -1056,15 +1094,13 @@ describe('stories API', () => { const { api: { setIndex, jumpToComponent }, - state, - } = initStories({ + } = initStoriesAndSetState({ store, navigate, storyId: 'custom-id--1', viewMode: 'story', provider, } as any); - store.setState(state); setIndex({ v: 4, entries: navigationEntries }); jumpToComponent(1); @@ -1077,9 +1113,13 @@ describe('stories API', () => { const { api: { setIndex, jumpToComponent }, - state, - } = initStories({ store, navigate, storyId: 'a--2', viewMode: 'story', provider } as any); - store.setState(state); + } = initStoriesAndSetState({ + store, + navigate, + storyId: 'a--2', + viewMode: 'story', + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); jumpToComponent(-1); @@ -1093,7 +1133,7 @@ describe('stories API', () => { const store = createMockStore({ storyId: 'a--1', viewMode: 'story' }); const { api: { setIndex, selectStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ store, navigate, provider } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory('a--2'); @@ -1105,7 +1145,7 @@ describe('stories API', () => { const store = createMockStore({ storyId: 'a--1', viewMode: 'docs' }); const { api: { setIndex, selectStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ store, navigate, provider } as any); setIndex({ v: 4, entries: { @@ -1128,10 +1168,16 @@ describe('stories API', () => { describe('legacy api', () => { it('allows navigating to a combination of title + name', () => { const navigate = jest.fn(); - const store = createMockStore({ storyId: 'a--1', viewMode: 'story' }); + const store = createMockStore(); const { api: { setIndex, selectStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ + store, + storyId: 'a--1', + viewMode: 'story', + navigate, + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory('a', '2'); @@ -1140,10 +1186,16 @@ describe('stories API', () => { it('allows navigating to a given name (in the current component)', () => { const navigate = jest.fn(); - const store = createMockStore({ storyId: 'a--1', viewMode: 'story' }); + const store = createMockStore(); const { api: { setIndex, selectStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ + store, + storyId: 'a--1', + viewMode: 'story', + navigate, + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory(undefined, '2'); @@ -1156,7 +1208,7 @@ describe('stories API', () => { const store = createMockStore({ storyId: 'a--1', viewMode: 'settings' }); const { api: { setIndex, selectStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ store, navigate, provider } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory('a--2'); @@ -1165,10 +1217,16 @@ describe('stories API', () => { it('allows navigating to first story in component on call by component id', () => { const navigate = jest.fn(); - const store = createMockStore({ storyId: 'a--1', viewMode: 'story' }); + const store = createMockStore(); const { api: { setIndex, selectStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ + store, + storyId: 'a--1', + viewMode: 'story', + navigate, + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory('a'); @@ -1177,10 +1235,16 @@ describe('stories API', () => { it('allows navigating to first story in group on call by group id', () => { const navigate = jest.fn(); - const store = createMockStore({ storyId: 'a--1', viewMode: 'story' }); + const store = createMockStore(); const { api: { setIndex, selectStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ + store, + storyId: 'a--1', + viewMode: 'story', + navigate, + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory('b'); @@ -1189,10 +1253,16 @@ describe('stories API', () => { it('allows navigating to first story in component on call by title', () => { const navigate = jest.fn(); - const store = createMockStore({ storyId: 'a--1', viewMode: 'story' }); + const store = createMockStore(); const { api: { setIndex, selectStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ + store, + storyId: 'a--1', + viewMode: 'story', + navigate, + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory('A'); @@ -1201,10 +1271,16 @@ describe('stories API', () => { it('allows navigating to the first story of the current component if passed nothing', () => { const navigate = jest.fn(); - const store = createMockStore({ storyId: 'a--2', viewMode: 'story' }); + const store = createMockStore(); const { api: { setIndex, selectStory }, - } = initStories({ store, navigate, provider } as any); + } = initStoriesAndSetState({ + store, + storyId: 'a--2', + viewMode: 'story', + navigate, + provider, + } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory(); @@ -1218,9 +1294,7 @@ describe('stories API', () => { const { api: { selectStory, setIndex }, - state, - } = initStories({ store, navigate, provider } as any); - store.setState(state); + } = initStoriesAndSetState({ store, navigate, provider } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory('b/e', '1'); @@ -1233,9 +1307,7 @@ describe('stories API', () => { const { api: { selectStory, setIndex }, - state, - } = initStories({ store, navigate, provider } as any); - store.setState(state); + } = initStoriesAndSetState({ store, navigate, provider } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory('custom-id', '1'); @@ -1248,9 +1320,7 @@ describe('stories API', () => { const { api: { selectStory, setIndex }, - state, - } = initStories({ store, navigate, provider } as any); - store.setState(state); + } = initStoriesAndSetState({ store, navigate, provider } as any); setIndex({ v: 4, entries: navigationEntries }); selectStory('b/e'); @@ -1268,7 +1338,7 @@ describe('stories API', () => { setOptions: jest.fn(), }); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); await init(); @@ -1278,8 +1348,8 @@ describe('stories API', () => { args: { c: 'd' }, }); - const { stories: storedStoriesHash } = store.getState(); - expect(storedStoriesHash['component-a--story-1']).toMatchObject({ + const { index } = store.getState(); + expect(index['component-a--story-1']).toMatchObject({ type: 'story', id: 'component-a--story-1', parent: 'component-a', @@ -1299,7 +1369,7 @@ describe('stories API', () => { setOptions: jest.fn(), }); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); await init(); @@ -1326,7 +1396,7 @@ describe('stories API', () => { const store = createMockStore(); const fullAPI = Object.assign(new EventEmitter(), {}); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); await init(); @@ -1341,7 +1411,7 @@ describe('stories API', () => { const navigate = jest.fn(); const fullAPI = Object.assign(new EventEmitter(), { updateRef: jest.fn() }); const store = createMockStore(); - const { api, init } = initStories({ store, navigate, provider, fullAPI } as any); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api); @@ -1365,7 +1435,7 @@ describe('stories API', () => { const navigate = jest.fn(); const store = createMockStore(); - const { init, api } = initStories({ store, navigate, provider, fullAPI } as any); + const { init, api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api, { setIndex: jest.fn(), findRef: jest.fn(), @@ -1404,7 +1474,7 @@ describe('stories API', () => { const navigate = jest.fn(); const store = createMockStore(); - const { init, api } = initStories({ store, navigate, provider, fullAPI } as any); + const { init, api } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); Object.assign(fullAPI, api, { setIndex: jest.fn(), findRef: jest.fn(), diff --git a/code/lib/types/src/modules/api-stories.ts b/code/lib/types/src/modules/api-stories.ts index b571e9faa00c..26999b6f22a6 100644 --- a/code/lib/types/src/modules/api-stories.ts +++ b/code/lib/types/src/modules/api-stories.ts @@ -115,12 +115,12 @@ export type API_Group = API_GroupEntry | API_ComponentEntry; export type API_Story = API_LeafEntry; /** - * The `StoriesHash` is our manager-side representation of the `StoryIndex`. + * The `IndexHash` is our manager-side representation of the `StoryIndex`. * We create entries in the hash not only for each story or docs entry, but * also for each "group" of the component (split on '/'), as that's how things * are manipulated in the manager (i.e. in the sidebar) */ -export interface API_StoriesHash { +export interface API_IndexHash { [id: string]: API_HashEntry; } // We used to received a bit more data over the channel on the SET_STORIES event, including diff --git a/code/lib/types/src/modules/api.ts b/code/lib/types/src/modules/api.ts index dd98031b0c0e..d9d2c54ec5de 100644 --- a/code/lib/types/src/modules/api.ts +++ b/code/lib/types/src/modules/api.ts @@ -5,7 +5,7 @@ import type { Channel } from '../../../channels/src'; import type { ThemeVars } from '../../../theming/src/types'; import type { ViewMode } from './csf'; import type { DocsOptions } from './core-common'; -import type { API_HashEntry, API_StoriesHash } from './api-stories'; +import type { API_HashEntry, API_IndexHash } from './api-stories'; import type { SetStoriesStory, SetStoriesStoryData } from './channelApi'; import type { Addon_Types } from './addons'; import type { StoryIndex } from './storyIndex'; @@ -144,7 +144,7 @@ export type API_SetRefData = Partial< export type API_StoryMapper = (ref: API_ComposedRef, story: SetStoriesStory) => SetStoriesStory; export interface API_LoadedRefData { - stories?: API_StoriesHash; + index?: API_IndexHash; indexError?: Error; previewInitialized: boolean; } @@ -166,7 +166,7 @@ export type API_ComposedRefUpdate = Partial< | 'title' | 'type' | 'expanded' - | 'stories' + | 'index' | 'versions' | 'loginUrl' | 'version' From 24f35c5f5c40c699c11a28cb00bfbd9c23ea25e3 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 13 Jan 2023 13:09:27 +1100 Subject: [PATCH 20/29] Various updates --- code/lib/manager-api/src/index.tsx | 4 ++-- .../src/components/sidebar/Refs.stories.tsx | 18 +++++++++--------- .../src/components/sidebar/Search.stories.tsx | 8 ++++---- .../src/components/sidebar/Sidebar.stories.tsx | 13 ++++++------- .../manager/src/components/sidebar/Sidebar.tsx | 6 +++--- .../sidebar/__tests__/Sidebar.test.tsx | 17 +++++++++-------- .../src/components/sidebar/mockdata.large.ts | 2 +- code/ui/manager/src/containers/sidebar.tsx | 4 ++-- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/code/lib/manager-api/src/index.tsx b/code/lib/manager-api/src/index.tsx index d1c1096e74fc..edbcdf876075 100644 --- a/code/lib/manager-api/src/index.tsx +++ b/code/lib/manager-api/src/index.tsx @@ -18,13 +18,13 @@ import type { API_DocsEntry, API_GroupEntry, API_HashEntry, + API_IndexHash, API_LeafEntry, API_OptionsData, API_ProviderData, API_Refs, API_RootEntry, API_StateMerger, - API_StoriesHash, API_StoryEntry, Parameters, StoryId, @@ -326,7 +326,7 @@ export function useStorybookApi(): API { } export type { - API_StoriesHash as StoriesHash, + API_IndexHash as StoriesHash, API_RootEntry as RootEntry, API_GroupEntry as GroupEntry, API_ComponentEntry as ComponentEntry, diff --git a/code/ui/manager/src/components/sidebar/Refs.stories.tsx b/code/ui/manager/src/components/sidebar/Refs.stories.tsx index 1b19b872fc75..fd63b12914bc 100644 --- a/code/ui/manager/src/components/sidebar/Refs.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Refs.stories.tsx @@ -22,10 +22,10 @@ export default { }; const { menu } = standardHeaderData; -const stories = mockDataset.withRoot; +const index = mockDataset.withRoot; const storyId = '1-12-121'; -export const simpleData = { menu, stories, storyId }; +export const simpleData = { menu, index, storyId }; export const loadingData = { menu, stories: {} }; const indexError: Error = (() => { @@ -51,7 +51,7 @@ const refs: Record = { title: 'It is empty because no stories were loaded', url: 'https://example.com', type: 'lazy', - stories: {}, + index: {}, previewInitialized: false, }, startInjected_unknown: { @@ -61,7 +61,7 @@ const refs: Record = { type: 'unknown', previewInitialized: false, // @ts-expect-error (invalid input) - stories, + index, }, startInjected_loading: { id: 'startInjected_loading', @@ -70,7 +70,7 @@ const refs: Record = { type: 'auto-inject', previewInitialized: false, // @ts-expect-error (invalid input) - stories, + index, }, startInjected_ready: { id: 'startInjected_ready', @@ -79,7 +79,7 @@ const refs: Record = { type: 'auto-inject', previewInitialized: true, // @ts-expect-error (invalid input) - stories, + index, }, versions: { id: 'versions', @@ -87,7 +87,7 @@ const refs: Record = { url: 'https://example.com', type: 'lazy', // @ts-expect-error (invalid input) - stories, + index, versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com' }, previewInitialized: true, }, @@ -97,7 +97,7 @@ const refs: Record = { url: 'https://example.com', type: 'lazy', // @ts-expect-error (invalid input) - stories, + index, versions: { '1.0.0': 'https://example.com/v1', '2.0.0': 'https://example.com/v2' }, previewInitialized: true, }, @@ -122,7 +122,7 @@ const refs: Record = { title: 'This storybook has a very very long name for some reason', url: 'https://example.com', // @ts-expect-error (invalid input) - stories, + index, type: 'lazy', versions: { '111.111.888-new': 'https://example.com/new', diff --git a/code/ui/manager/src/components/sidebar/Search.stories.tsx b/code/ui/manager/src/components/sidebar/Search.stories.tsx index 633376cfc530..00f8ba61d3c5 100644 --- a/code/ui/manager/src/components/sidebar/Search.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Search.stories.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { action } from '@storybook/addon-actions'; -import { stories } from './mockdata.large'; +import { index } from './mockdata.large'; import { Search } from './Search'; import { SearchResults } from './SearchResults'; import { noResults } from './SearchResults.stories'; @@ -9,11 +9,11 @@ import { DEFAULT_REF_ID } from './Sidebar'; import type { Selection } from './types'; const refId = DEFAULT_REF_ID; -const data = { [refId]: { id: refId, url: '/', stories, previewInitialized: true } }; +const data = { [refId]: { id: refId, url: '/', index, previewInitialized: true } }; const dataset = { hash: data, entries: Object.entries(data) }; const getLastViewed = () => - Object.values(stories) - .filter((item, index) => item.type === 'component' && item.parent && index % 20 === 0) + Object.values(index) + .filter((item, i) => item.type === 'component' && item.parent && i % 20 === 0) .map((component) => ({ storyId: component.id, refId })); export default { diff --git a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx index 5fb527ea68a8..d02ea0f3c47f 100644 --- a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx @@ -19,11 +19,11 @@ export default { }; const { menu } = standardHeaderData; -const stories = mockDataset.withRoot; +const index = mockDataset.withRoot; const refId = DEFAULT_REF_ID; const storyId = 'root-1-child-a2--grandchild-a1-1'; -export const simpleData = { menu, stories, storyId }; +export const simpleData = { menu, index, storyId }; export const loadingData = { menu }; const refs: Record = { @@ -32,8 +32,7 @@ const refs: Record = { title: 'This is a ref', url: 'https://example.com', type: 'lazy', - // @ts-expect-error (needs to be converted to CSF3) - stories, + index, previewInitialized: true, }, }; @@ -52,7 +51,7 @@ export const Simple = () => ( ( ); export const Empty = () => ( - + ); export const IndexError = () => ( @@ -82,7 +81,7 @@ export const WithRefs = () => ( storyId && { storyId, refId }, [storyId, refId]); - const dataset = useCombination({ stories, indexError, previewInitialized }, refs); - const isLoading = !stories && !indexError; + const dataset = useCombination({ index, indexError, previewInitialized }, refs); + const isLoading = !index && !indexError; const lastViewedProps = useLastViewed(selected); return ( diff --git a/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx b/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx index 3400e570d5b7..2e409618e8ed 100644 --- a/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx +++ b/code/ui/manager/src/components/sidebar/__tests__/Sidebar.test.tsx @@ -2,9 +2,10 @@ import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; import { ThemeProvider, ensure, themes } from '@storybook/theming'; -import type { HashEntry, StoriesHash, Refs } from '@storybook/manager-api'; +import type { HashEntry, Refs } from '@storybook/manager-api'; import type { Theme } from '@storybook/theming'; import type { RenderResult } from '@testing-library/react'; +import type { API_IndexHash } from '@storybook/types'; import { Sidebar } from '../Sidebar'; import type { SidebarProps } from '../Sidebar'; @@ -15,12 +16,12 @@ const factory = (props: Partial): RenderResult => { return render( - + ); }; -const generateStories = ({ title, refId }: { title: string; refId?: string }): StoriesHash => { +const generateStories = ({ title, refId }: { title: string; refId?: string }): API_IndexHash => { const [root, componentName]: [string, string] = title.split('/') as any; const rootId: string = root.toLowerCase().replace(/\s+/g, '-'); const hypenatedComponentName: string = componentName.toLowerCase().replace(/\s+/g, '-'); @@ -61,7 +62,7 @@ const generateStories = ({ title, refId }: { title: string; refId?: string }): S }, ]; - return storyBase.reduce((accumulator: StoriesHash, current: HashEntry): StoriesHash => { + return storyBase.reduce((accumulator: API_IndexHash, current: HashEntry): API_IndexHash => { accumulator[current.id] = current; return accumulator; }, {}); @@ -71,12 +72,12 @@ describe('Sidebar', () => { test.skip("should not render an extra nested 'Page'", async () => { const refId = 'next'; const title = 'Getting Started/Install'; - const refStories: StoriesHash = generateStories({ refId, title }); - const internalStories: StoriesHash = generateStories({ title: 'Welcome/Example' }); + const refIndex: API_IndexHash = generateStories({ refId, title }); + const internalIndex: API_IndexHash = generateStories({ title: 'Welcome/Example' }); const refs: Refs = { [refId]: { - stories: refStories, + index: refIndex, id: refId, previewInitialized: true, title: refId, @@ -87,7 +88,7 @@ describe('Sidebar', () => { factory({ refs, refId, - stories: internalStories, + index: internalIndex, }); fireEvent.click(screen.getByText('Install')); diff --git a/code/ui/manager/src/components/sidebar/mockdata.large.ts b/code/ui/manager/src/components/sidebar/mockdata.large.ts index 34d83fb5ea5b..b4c2a0b99399 100644 --- a/code/ui/manager/src/components/sidebar/mockdata.large.ts +++ b/code/ui/manager/src/components/sidebar/mockdata.large.ts @@ -14,7 +14,7 @@ import type { Dataset } from './types'; // @ts-expect-error (TODO) -export const stories = { +export const index = { images: { name: 'Images', id: 'images', diff --git a/code/ui/manager/src/containers/sidebar.tsx b/code/ui/manager/src/containers/sidebar.tsx index a656ed8f72c5..78a05c817ff3 100755 --- a/code/ui/manager/src/containers/sidebar.tsx +++ b/code/ui/manager/src/containers/sidebar.tsx @@ -16,7 +16,7 @@ const Sidebar = React.memo(function Sideber() { storyId, refId, layout: { showToolbar, isFullscreen, showPanel, showNav }, - stories, + index, indexError, previewInitialized, refs, @@ -27,7 +27,7 @@ const Sidebar = React.memo(function Sideber() { return { title: name, url, - stories, + index, indexError, previewInitialized, refs, From 0ae65e8ed0dd69e2117ddb0b50e85683c349a950 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 13 Jan 2023 13:14:51 +1100 Subject: [PATCH 21/29] More fixes --- code/ui/manager/src/components/sidebar/Refs.tsx | 6 +++--- code/ui/manager/src/components/sidebar/Sidebar.tsx | 1 + .../src/components/sidebar/Tree.stories.tsx | 14 +++++++------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/code/ui/manager/src/components/sidebar/Refs.tsx b/code/ui/manager/src/components/sidebar/Refs.tsx index d9c11f42a841..29599dd393a2 100644 --- a/code/ui/manager/src/components/sidebar/Refs.tsx +++ b/code/ui/manager/src/components/sidebar/Refs.tsx @@ -99,7 +99,7 @@ export const Ref: FC = React.memo(function Ref(props) { const { docsOptions } = useStorybookState(); const api = useStorybookApi(); const { - stories, + index, id: refId, title = refId, isLoading: isLoadingMain, @@ -113,7 +113,7 @@ export const Ref: FC = React.memo(function Ref(props) { indexError, previewInitialized, } = props; - const length = useMemo(() => (stories ? Object.keys(stories).length : 0), [stories]); + const length = useMemo(() => (index ? Object.keys(index).length : 0), [index]); const indicatorRef = useRef(null); const isMain = refId === DEFAULT_REF_ID; @@ -161,7 +161,7 @@ export const Ref: FC = React.memo(function Ref(props) { isBrowsing={isBrowsing} isMain={isMain} refId={refId} - data={stories} + data={index} docsMode={docsOptions.docsMode} selectedStoryId={selectedStoryId} onSelectStoryId={onSelectStoryId} diff --git a/code/ui/manager/src/components/sidebar/Sidebar.tsx b/code/ui/manager/src/components/sidebar/Sidebar.tsx index e6d8ba1ca285..f9bde3ac270d 100644 --- a/code/ui/manager/src/components/sidebar/Sidebar.tsx +++ b/code/ui/manager/src/components/sidebar/Sidebar.tsx @@ -97,6 +97,7 @@ export const Sidebar = React.memo(function Sidebar({ }: SidebarProps) { const selected: Selection = useMemo(() => storyId && { storyId, refId }, [storyId, refId]); + console.log({ index, indexError, previewInitialized }); const dataset = useCombination({ index, indexError, previewInitialized }, refs); const isLoading = !index && !indexError; const lastViewedProps = useLastViewed(selected); diff --git a/code/ui/manager/src/components/sidebar/Tree.stories.tsx b/code/ui/manager/src/components/sidebar/Tree.stories.tsx index 29923a404a4a..336493b1f830 100644 --- a/code/ui/manager/src/components/sidebar/Tree.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Tree.stories.tsx @@ -5,7 +5,7 @@ import type { ComponentEntry, StoriesHash } from '@storybook/manager-api'; import { screen } from '@testing-library/dom'; import { Tree } from './Tree'; -import { stories } from './mockdata.large'; +import { index } from './mockdata.large'; import { DEFAULT_REF_ID } from './Sidebar'; export default { @@ -17,7 +17,7 @@ export default { }; const refId = DEFAULT_REF_ID; -const storyId = Object.values(stories).find((story) => story.type === 'story').id; +const storyId = Object.values(index).find((story) => story.type === 'story').id; const log = (id: string) => console.log(id); @@ -29,7 +29,7 @@ export const Full = () => { isBrowsing isMain refId={refId} - data={stories} + data={index} highlightedRef={{ current: { itemId: selectedId, refId } }} setHighlightedItemId={log} selectedStoryId={selectedId} @@ -38,10 +38,10 @@ export const Full = () => { ); }; -const tooltipStories = Object.keys(stories).reduce((acc, key) => { +const tooltipStories = Object.keys(index).reduce((acc, key) => { if (key === 'tooltip-tooltipselect--default') { acc['tooltip-tooltipselect--tooltipselect'] = { - ...stories[key], + ...index[key], id: 'tooltip-tooltipselect--tooltipselect', name: 'TooltipSelect', }; @@ -49,12 +49,12 @@ const tooltipStories = Object.keys(stories).reduce((acc, key) => { } if (key === 'tooltip-tooltipselect') { acc[key] = { - ...(stories[key] as ComponentEntry), + ...(index[key] as ComponentEntry), children: ['tooltip-tooltipselect--tooltipselect'], }; return acc; } - if (key.startsWith('tooltip')) acc[key] = stories[key]; + if (key.startsWith('tooltip')) acc[key] = index[key]; return acc; }, {} as StoriesHash); From 668565a317038888e378fa87085dc90638ddb6d6 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 13 Jan 2023 13:42:41 +1100 Subject: [PATCH 22/29] Fix up app stories to be initialized --- code/lib/manager-api/src/modules/stories.ts | 17 +++-- code/ui/manager/src/app.stories.tsx | 64 +++++++++++-------- .../src/components/sidebar/Sidebar.tsx | 1 - 3 files changed, 47 insertions(+), 35 deletions(-) diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index 8eaa9f95225a..f3a12505c64b 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -41,7 +41,7 @@ import { addPreparedStories, } from '../lib/stories'; -import type { ModuleFn } from '../index'; +import type { ComposedRef, ModuleFn } from '../index'; const { FEATURES, fetch } = global; const STORY_INDEX_PATH = './index.json'; @@ -102,6 +102,7 @@ export interface SubAPI { ): StoryId; fetchIndex: () => Promise; updateStory: (storyId: StoryId, update: StoryUpdate, ref?: API_ComposedRef) => Promise; + setPreviewInitialized: (ref?: ComposedRef) => Promise; } const removedOptions = ['enableShortcuts', 'theme', 'showRoots']; @@ -375,6 +376,13 @@ export const init: ModuleFn = ({ await fullAPI.updateRef(refId, { index }); } }, + setPreviewInitialized: async (ref?: ComposedRef): Promise => { + if (!ref) { + store.setState({ previewInitialized: true }); + } else { + fullAPI.updateRef(ref.id, { previewInitialized: true }); + } + }, }; const initModule = async () => { @@ -411,12 +419,7 @@ export const init: ModuleFn = ({ // preparing spinner. fullAPI.on(CURRENT_STORY_WAS_SET, function handler() { const { ref } = getEventMetadata(this, fullAPI); - - if (!ref) { - store.setState({ previewInitialized: true }); - } else { - fullAPI.updateRef(ref.id, { previewInitialized: true }); - } + fullAPI.setPreviewInitialized(ref); }); fullAPI.on(STORY_CHANGED, function handler() { diff --git a/code/ui/manager/src/app.stories.tsx b/code/ui/manager/src/app.stories.tsx index f55af04a6560..c213f64faabe 100644 --- a/code/ui/manager/src/app.stories.tsx +++ b/code/ui/manager/src/app.stories.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { Provider as ManagerProvider } from '@storybook/manager-api'; +import type { API } from '@storybook/manager-api'; +import { Consumer, Provider as ManagerProvider } from '@storybook/manager-api'; import { LocationProvider } from '@storybook/router'; import { HelmetProvider } from 'react-helmet-async'; import { styled } from '@storybook/theming'; @@ -37,32 +38,41 @@ const ThemeStack = styled.div( }) ); -export const Default = () => ( - {}} - docsOptions={{ docsMode: false }} - > - - -); +function setPreviewInitialized({ api }: { api: API }) { + api.setPreviewInitialized(); + return {}; +} + +export const Default = () => { + const provider = new FakeProvider(); + return ( + {}} + docsOptions={{ docsMode: false }} + > + {() => <>} + + + ); +}; export const LoadingState = () => ( storyId && { storyId, refId }, [storyId, refId]); - console.log({ index, indexError, previewInitialized }); const dataset = useCombination({ index, indexError, previewInitialized }, refs); const isLoading = !index && !indexError; const lastViewedProps = useLastViewed(selected); From b25ec312a152c4f16f6e4b35cf987970434f1a64 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 13 Jan 2023 15:25:46 +1100 Subject: [PATCH 23/29] Bunch of updates I missed --- code/jest.config.js | 2 +- code/lib/manager-api/src/index.tsx | 2 ++ code/lib/manager-api/src/lib/stories.ts | 14 ++++++------- .../src/components/layout/app.mockdata.tsx | 2 +- .../components/sidebar/Explorer.stories.tsx | 20 +++++++++---------- .../src/components/sidebar/RefIndicator.tsx | 2 +- .../manager/src/components/sidebar/Search.tsx | 6 +++--- .../sidebar/SearchResults.stories.tsx | 4 ++-- .../components/sidebar/Sidebar.stories.tsx | 3 ++- .../src/components/sidebar/Tree.stories.tsx | 10 +++++----- .../manager/src/components/sidebar/utils.ts | 8 ++++---- 11 files changed, 38 insertions(+), 35 deletions(-) diff --git a/code/jest.config.js b/code/jest.config.js index ee2ebd55a9ac..6e72f4acb974 100644 --- a/code/jest.config.js +++ b/code/jest.config.js @@ -4,7 +4,7 @@ module.exports = { '/frameworks/!(angular)*', '/lib/*', '/renderers/*', - '/ui/*', + '/ui/!(node_modules)*', ], collectCoverage: false, collectCoverageFrom: [ diff --git a/code/lib/manager-api/src/index.tsx b/code/lib/manager-api/src/index.tsx index edbcdf876075..28ba051652a0 100644 --- a/code/lib/manager-api/src/index.tsx +++ b/code/lib/manager-api/src/index.tsx @@ -326,7 +326,9 @@ export function useStorybookApi(): API { } export type { + /** @deprecated now IndexHash */ API_IndexHash as StoriesHash, + API_IndexHash as IndexHash, API_RootEntry as RootEntry, API_GroupEntry as GroupEntry, API_ComponentEntry as ComponentEntry, diff --git a/code/lib/manager-api/src/lib/stories.ts b/code/lib/manager-api/src/lib/stories.ts index 955579139292..3db7b6480e07 100644 --- a/code/lib/manager-api/src/lib/stories.ts +++ b/code/lib/manager-api/src/lib/stories.ts @@ -15,7 +15,7 @@ import type { API_RootEntry, API_GroupEntry, API_ComponentEntry, - API_StoriesHash, + API_IndexHash, API_DocsEntry, API_StoryEntry, API_HashEntry, @@ -122,7 +122,7 @@ export const transformStoryIndexToStoriesHash = ( provider: API_Provider; docsOptions: DocsOptions; } -): API_StoriesHash => { +): API_IndexHash => { if (!index.v) throw new Error('Composition: Missing stories.json version'); const v4Index = index.v === 4 ? index : transformStoryIndexV3toV4(index as any); @@ -241,10 +241,10 @@ export const transformStoryIndexToStoriesHash = ( } as API_DocsEntry | API_StoryEntry; return acc; - }, {} as API_StoriesHash); + }, {} as API_IndexHash); // This function adds a "root" or "orphan" and all of its descendents to the hash. - function addItem(acc: API_StoriesHash, item: API_HashEntry) { + function addItem(acc: API_IndexHash, item: API_HashEntry) { // If we were already inserted as part of a group, that's great. if (acc[item.id]) { return acc; @@ -268,7 +268,7 @@ export const transformStoryIndexToStoriesHash = ( .reduce(addItem, orphanHash); }; -export const addPreparedStories = (newHash: API_StoriesHash, oldHash?: API_StoriesHash) => { +export const addPreparedStories = (newHash: API_IndexHash, oldHash?: API_IndexHash) => { if (!oldHash) return newHash; return Object.fromEntries( @@ -283,7 +283,7 @@ export const addPreparedStories = (newHash: API_StoriesHash, oldHash?: API_Stori ); }; -export const getComponentLookupList = memoize(1)((hash: API_StoriesHash) => { +export const getComponentLookupList = memoize(1)((hash: API_IndexHash) => { return Object.entries(hash).reduce((acc, i) => { const value = i[1]; if (value.type === 'component') { @@ -293,6 +293,6 @@ export const getComponentLookupList = memoize(1)((hash: API_StoriesHash) => { }, [] as StoryId[][]); }); -export const getStoriesLookupList = memoize(1)((hash: API_StoriesHash) => { +export const getStoriesLookupList = memoize(1)((hash: API_IndexHash) => { return Object.keys(hash).filter((k) => ['story', 'docs'].includes(hash[k].type)); }); diff --git a/code/ui/manager/src/components/layout/app.mockdata.tsx b/code/ui/manager/src/components/layout/app.mockdata.tsx index 14489e57e8f4..6f5b2657aeee 100644 --- a/code/ui/manager/src/components/layout/app.mockdata.tsx +++ b/code/ui/manager/src/components/layout/app.mockdata.tsx @@ -58,7 +58,7 @@ export const panels: Addon_Collection = { }; const realSidebarProps: SidebarProps = { - stories: mockDataset.withRoot as SidebarProps['stories'], + index: mockDataset.withRoot as SidebarProps['index'], menu: [], refs: {}, previewInitialized: true, diff --git a/code/ui/manager/src/components/sidebar/Explorer.stories.tsx b/code/ui/manager/src/components/sidebar/Explorer.stories.tsx index f8ac82068824..6e33e63b4ad8 100644 --- a/code/ui/manager/src/components/sidebar/Explorer.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Explorer.stories.tsx @@ -25,9 +25,9 @@ const simple: Record = { title: undefined, id: 'storybook_internal', url: 'iframe.html', - ready: true, + previewInitialized: true, // @ts-expect-error (invalid input) - stories: mockDataset.withRoot, + index: mockDataset.withRoot, }, }; @@ -37,37 +37,37 @@ const withRefs: Record = { id: 'basic', title: 'Basic ref', url: 'https://example.com', - ready: true, + previewInitialized: true, type: 'auto-inject', // @ts-expect-error (invalid input) - stories: mockDataset.noRoot, + index: mockDataset.noRoot, }, injected: { id: 'injected', title: 'Not ready', url: 'https://example.com', - ready: false, + previewInitialized: false, type: 'auto-inject', // @ts-expect-error (invalid input) - stories: mockDataset.noRoot, + index: mockDataset.noRoot, }, unknown: { id: 'unknown', title: 'Unknown ref', url: 'https://example.com', - ready: true, + previewInitialized: true, type: 'unknown', // @ts-expect-error (invalid input) - stories: mockDataset.noRoot, + index: mockDataset.noRoot, }, lazy: { id: 'lazy', title: 'Lazy loaded ref', url: 'https://example.com', - ready: false, + previewInitialized: false, type: 'lazy', // @ts-expect-error (invalid input) - stories: mockDataset.withRoot, + index: mockDataset.withRoot, }, }; diff --git a/code/ui/manager/src/components/sidebar/RefIndicator.tsx b/code/ui/manager/src/components/sidebar/RefIndicator.tsx index fad585fabd4c..0d116a63f5c0 100644 --- a/code/ui/manager/src/components/sidebar/RefIndicator.tsx +++ b/code/ui/manager/src/components/sidebar/RefIndicator.tsx @@ -168,7 +168,7 @@ export const RefIndicator = React.memo( forwardRef }>( ({ state, ...ref }, forwardedRef) => { const api = useStorybookApi(); - const list = useMemo(() => Object.values(ref.stories || {}), [ref.stories]); + const list = useMemo(() => Object.values(ref.index || {}), [ref.index]); const componentCount = useMemo( () => list.filter((v) => v.type === 'component').length, [list] diff --git a/code/ui/manager/src/components/sidebar/Search.tsx b/code/ui/manager/src/components/sidebar/Search.tsx index c8fe29fa3d2e..5a7b5acd4ca0 100644 --- a/code/ui/manager/src/components/sidebar/Search.tsx +++ b/code/ui/manager/src/components/sidebar/Search.tsx @@ -176,9 +176,9 @@ export const Search = React.memo<{ ); const list: SearchItem[] = useMemo(() => { - return dataset.entries.reduce((acc: SearchItem[], [refId, { stories }]) => { - if (stories) { - acc.push(...Object.values(stories).map((item) => searchItem(item, dataset.hash[refId]))); + return dataset.entries.reduce((acc: SearchItem[], [refId, { index }]) => { + if (index) { + acc.push(...Object.values(index).map((item) => searchItem(item, dataset.hash[refId]))); } return acc; }, []); diff --git a/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx b/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx index e0194299220f..d9c75a5b2588 100644 --- a/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx +++ b/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx @@ -37,10 +37,10 @@ const combinedDataset = (refs: Record): CombinedDataset => // @ts-expect-error (invalid input) const dataset = combinedDataset({ internal: mockDataset.withRoot, composed: mockDataset.noRoot }); -const internal = Object.values(dataset.hash.internal.stories).map((item) => +const internal = Object.values(dataset.hash.internal.index).map((item) => searchItem(item, dataset.hash.internal) ); -const composed = Object.values(dataset.hash.composed.stories).map((item) => +const composed = Object.values(dataset.hash.composed.index).map((item) => searchItem(item, dataset.hash.composed) ); const stories: SearchItem[] = internal.concat(composed); diff --git a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx index d02ea0f3c47f..397329f8f9c9 100644 --- a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type { StoriesHash } from 'lib/manager-api/src'; +import type { API_IndexHash } from 'lib/types/src'; import { Sidebar, DEFAULT_REF_ID } from './Sidebar'; import { standardData as standardHeaderData } from './Heading.stories'; import * as ExplorerStories from './Explorer.stories'; @@ -19,7 +20,7 @@ export default { }; const { menu } = standardHeaderData; -const index = mockDataset.withRoot; +const index = mockDataset.withRoot as API_IndexHash; const refId = DEFAULT_REF_ID; const storyId = 'root-1-child-a2--grandchild-a1-1'; diff --git a/code/ui/manager/src/components/sidebar/Tree.stories.tsx b/code/ui/manager/src/components/sidebar/Tree.stories.tsx index 336493b1f830..807b861cccf2 100644 --- a/code/ui/manager/src/components/sidebar/Tree.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Tree.stories.tsx @@ -1,7 +1,7 @@ /* eslint-disable storybook/use-storybook-testing-library */ // @TODO: use addon-interactions and remove the rule disable above import React from 'react'; -import type { ComponentEntry, StoriesHash } from '@storybook/manager-api'; +import type { ComponentEntry, IndexHash } from '@storybook/manager-api'; import { screen } from '@testing-library/dom'; import { Tree } from './Tree'; @@ -56,9 +56,9 @@ const tooltipStories = Object.keys(index).reduce((acc, key) => { } if (key.startsWith('tooltip')) acc[key] = index[key]; return acc; -}, {} as StoriesHash); +}, {} as IndexHash); -const singleStoryComponent: StoriesHash = { +const singleStoryComponent: IndexHash = { // @ts-expect-error (invalid input) single: { type: 'component', @@ -102,7 +102,7 @@ export const SingleStoryComponents = () => { ); }; -const docsOnlySinglesStoryComponent: StoriesHash = { +const docsOnlySinglesStoryComponent: IndexHash = { // @ts-expect-error (invalid input) single: { type: 'component', @@ -147,7 +147,7 @@ export const SkipToCanvasLinkFocused = { isBrowsing: true, isMain: true, refId, - data: stories, + data: index, highlightedRef: { current: { itemId: 'tooltip-tooltipbuildlist--default', refId } }, setHighlightedItemId: log, selectedStoryId: 'tooltip-tooltipbuildlist--default', diff --git a/code/ui/manager/src/components/sidebar/utils.ts b/code/ui/manager/src/components/sidebar/utils.ts index 5406989df10d..e751ef5e9bd8 100644 --- a/code/ui/manager/src/components/sidebar/utils.ts +++ b/code/ui/manager/src/components/sidebar/utils.ts @@ -1,7 +1,7 @@ import memoize from 'memoizerific'; import { global } from '@storybook/global'; import type { SyntheticEvent } from 'react'; -import type { HashEntry, StoriesHash } from '@storybook/manager-api'; +import type { HashEntry, IndexHash } from '@storybook/manager-api'; // eslint-disable-next-line import/no-cycle import { DEFAULT_REF_ID } from './Sidebar'; @@ -30,11 +30,11 @@ export const getParents = memoize(1000)((id: string, dataset: Dataset): Item[] = const parent = getParent(id, dataset); return parent ? [parent, ...getParents(parent.id, dataset)] : []; }); -export const getAncestorIds = memoize(1000)((data: StoriesHash, id: string): string[] => +export const getAncestorIds = memoize(1000)((data: IndexHash, id: string): string[] => getParents(id, data).map((item) => item.id) ); export const getDescendantIds = memoize(1000)( - (data: StoriesHash, id: string, skipLeafs: boolean): string[] => { + (data: IndexHash, id: string, skipLeafs: boolean): string[] => { const entry = data[id]; const children = entry.type === 'story' || entry.type === 'docs' ? [] : entry.children; return children.reduce((acc, childId) => { @@ -47,7 +47,7 @@ export const getDescendantIds = memoize(1000)( ); export function getPath(item: Item, ref: RefType): string[] { - const parent = item.type !== 'root' && item.parent ? ref.stories[item.parent] : null; + const parent = item.type !== 'root' && item.parent ? ref.index[item.parent] : null; if (parent) return [...getPath(parent, ref), parent.name]; return ref.id === DEFAULT_REF_ID ? [] : [ref.title || ref.id]; } From f5dba5981f75d27d42dab7af00ba0ccfb703b60a Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 13 Jan 2023 17:49:52 +1100 Subject: [PATCH 24/29] Missed one more --- code/ui/manager/src/components/sidebar/Search.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/ui/manager/src/components/sidebar/Search.tsx b/code/ui/manager/src/components/sidebar/Search.tsx index 5a7b5acd4ca0..b485bd53039e 100644 --- a/code/ui/manager/src/components/sidebar/Search.tsx +++ b/code/ui/manager/src/components/sidebar/Search.tsx @@ -314,9 +314,9 @@ export const Search = React.memo<{ if (lastViewed && lastViewed.length) { results = lastViewed.reduce((acc, { storyId, refId }) => { const data = dataset.hash[refId]; - if (data && data.stories && data.stories[storyId]) { - const story = data.stories[storyId]; - const item = story.type === 'story' ? data.stories[story.parent] : story; + if (data && data.index && data.index[storyId]) { + const story = data.index[storyId]; + const item = story.type === 'story' ? data.index[story.parent] : story; // prevent duplicates if (!acc.some((res) => res.item.refId === refId && res.item.id === item.id)) { acc.push({ item: searchItem(item, dataset.hash[refId]), matches: [], score: 0 }); From 996fdde7208c4e7f9a59c7e4dd2a7282592b3ecd Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 13 Jan 2023 18:22:20 +1100 Subject: [PATCH 25/29] Even more fixes --- code/ui/manager/src/components/sidebar/Refs.stories.tsx | 4 ++-- code/ui/manager/src/components/sidebar/Sidebar.stories.tsx | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/code/ui/manager/src/components/sidebar/Refs.stories.tsx b/code/ui/manager/src/components/sidebar/Refs.stories.tsx index fd63b12914bc..f765f62daff0 100644 --- a/code/ui/manager/src/components/sidebar/Refs.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Refs.stories.tsx @@ -26,7 +26,7 @@ const index = mockDataset.withRoot; const storyId = '1-12-121'; export const simpleData = { menu, index, storyId }; -export const loadingData = { menu, stories: {} }; +export const loadingData = { menu, index: {} }; const indexError: Error = (() => { try { @@ -44,7 +44,7 @@ const refs: Record = { previewInitialized: false, type: 'lazy', // @ts-expect-error (invalid input) - stories, + index, }, empty: { id: 'empty', diff --git a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx index 397329f8f9c9..936789d0cde0 100644 --- a/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx +++ b/code/ui/manager/src/components/sidebar/Sidebar.stories.tsx @@ -1,7 +1,6 @@ import React from 'react'; -import type { StoriesHash } from 'lib/manager-api/src'; -import type { API_IndexHash } from 'lib/types/src'; +import type { IndexHash } from 'lib/manager-api/src'; import { Sidebar, DEFAULT_REF_ID } from './Sidebar'; import { standardData as standardHeaderData } from './Heading.stories'; import * as ExplorerStories from './Explorer.stories'; @@ -20,7 +19,7 @@ export default { }; const { menu } = standardHeaderData; -const index = mockDataset.withRoot as API_IndexHash; +const index = mockDataset.withRoot as IndexHash; const refId = DEFAULT_REF_ID; const storyId = 'root-1-child-a2--grandchild-a1-1'; @@ -43,7 +42,7 @@ const indexError = new Error('Failed to load index'); const refsError = { optimized: { ...refs.optimized, - stories: undefined as StoriesHash, + index: undefined as IndexHash, indexError, }, }; From 39f5da81c707332705007c4b18425ee13dbd3e79 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 13 Jan 2023 18:47:06 +1100 Subject: [PATCH 26/29] Another issue --- .../manager/src/components/sidebar/SearchResults.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx b/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx index d9c75a5b2588..d2c72e0c721f 100644 --- a/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx +++ b/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx @@ -18,10 +18,10 @@ export default { const combinedDataset = (refs: Record): CombinedDataset => { const hash: Refs = Object.entries(refs).reduce( - (acc, [refId, stories]) => + (acc, [refId, index]) => Object.assign(acc, { [refId]: { - stories, + index, title: null, id: refId, url: 'iframe.html', From c65b8ab5e936fb5e488ebd20b4ed0d99346f37d6 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 13 Jan 2023 19:40:24 +1100 Subject: [PATCH 27/29] Catch missing stories --- code/lib/manager-api/src/modules/stories.ts | 12 +++--- .../lib/manager-api/src/tests/stories.test.ts | 40 +++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index f3a12505c64b..415b801d788f 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -14,6 +14,7 @@ import { STORY_INDEX_INVALIDATED, CONFIG_ERROR, CURRENT_STORY_WAS_SET, + STORY_MISSING, } from '@storybook/core-events'; import { deprecate, logger } from '@storybook/client-logger'; @@ -522,11 +523,12 @@ export const init: ModuleFn = ({ // When there's a preview error, we don't show it in the manager, but simply fullAPI.on(CONFIG_ERROR, function handleConfigError(err) { const { ref } = getEventMetadata(this, fullAPI); - if (!ref) { - store.setState({ previewInitialized: true }); - } else { - fullAPI.updateRef(ref.id, { previewInitialized: true }); - } + fullAPI.setPreviewInitialized(ref); + }); + + fullAPI.on(STORY_MISSING, function handleConfigError(err) { + const { ref } = getEventMetadata(this, fullAPI); + fullAPI.setPreviewInitialized(ref); }); if (FEATURES?.storyStoreV7) { diff --git a/code/lib/manager-api/src/tests/stories.test.ts b/code/lib/manager-api/src/tests/stories.test.ts index 4ece445bd7a9..83410d1604e2 100644 --- a/code/lib/manager-api/src/tests/stories.test.ts +++ b/code/lib/manager-api/src/tests/stories.test.ts @@ -10,6 +10,7 @@ import { CONFIG_ERROR, SET_INDEX, CURRENT_STORY_WAS_SET, + STORY_MISSING, } from '@storybook/core-events'; import { EventEmitter } from 'events'; import { global } from '@storybook/global'; @@ -1429,6 +1430,45 @@ describe('stories API', () => { }); }); + describe('STORY_MISSING', () => { + it('sets previewInitialized to true, local', async () => { + const navigate = jest.fn(); + const store = createMockStore(); + const fullAPI = Object.assign(new EventEmitter(), {}); + + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); + Object.assign(fullAPI, api); + + await init(); + + fullAPI.emit(STORY_MISSING, { message: 'Failed to run configure' }); + + const { previewInitialized } = store.getState(); + expect(previewInitialized).toBe(true); + }); + + it('sets previewInitialized to true, ref', async () => { + const navigate = jest.fn(); + const fullAPI = Object.assign(new EventEmitter(), { updateRef: jest.fn() }); + const store = createMockStore(); + const { api, init } = initStoriesAndSetState({ store, navigate, provider, fullAPI } as any); + + Object.assign(fullAPI, api); + + getEventMetadataMock.mockReturnValueOnce({ + sourceType: 'external', + ref: { id: 'refId', stories: { 'a--1': { args: { a: 'b' } } } }, + } as any); + await init(); + fullAPI.emit(STORY_MISSING, { message: 'Failed to run configure' }); + + expect(fullAPI.updateRef.mock.calls.length).toBe(1); + expect(fullAPI.updateRef.mock.calls[0][1]).toEqual({ + previewInitialized: true, + }); + }); + }); + describe('v2 SET_STORIES event', () => { it('normalizes parameters and calls setRef for external stories', () => { const fullAPI = Object.assign(new EventEmitter()); From 02cee42f5d0d4bab833351a810d7cfa383ef5853 Mon Sep 17 00:00:00 2001 From: Tom Coleman Date: Fri, 13 Jan 2023 19:40:46 +1100 Subject: [PATCH 28/29] Add debugging to built-in sb --- code/ui/.storybook/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ui/.storybook/main.ts b/code/ui/.storybook/main.ts index 52bb8b72192d..a2d538c35738 100644 --- a/code/ui/.storybook/main.ts +++ b/code/ui/.storybook/main.ts @@ -73,6 +73,7 @@ const config: StorybookConfig = { sourcemap: process.env.CI !== 'true', }, }), + logLevel: 'debug', }; export default config; From f3bfca42a912f75fc4ef7dd3205b0501d4f5274e Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 18 Jan 2023 09:39:24 +0100 Subject: [PATCH 29/29] when testing for regressions, this didn't show up at all. renaming it fixed it --- .../src/Colors/{colorpalette.stories.mdx => colorpalette.mdx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename code/ui/components/src/Colors/{colorpalette.stories.mdx => colorpalette.mdx} (100%) diff --git a/code/ui/components/src/Colors/colorpalette.stories.mdx b/code/ui/components/src/Colors/colorpalette.mdx similarity index 100% rename from code/ui/components/src/Colors/colorpalette.stories.mdx rename to code/ui/components/src/Colors/colorpalette.mdx