From 5a336071e8a63b974179b6a6f8b635aa6b1eaac0 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Wed, 29 Nov 2023 14:25:49 +0100 Subject: [PATCH] fixes --- code/lib/manager-api/src/index.tsx | 2 + code/lib/manager-api/src/lib/store-setup.ts | 2 + code/lib/manager-api/src/lib/stories.ts | 1 - code/lib/manager-api/src/modules/channel.ts | 10 +-- code/lib/manager-api/src/modules/globals.ts | 10 +-- code/lib/manager-api/src/modules/layout.ts | 2 +- code/lib/manager-api/src/modules/refs.ts | 1 + code/lib/manager-api/src/modules/shortcuts.ts | 2 +- code/lib/manager-api/src/modules/stories.ts | 76 +++++++++++++------ code/lib/manager-api/src/modules/url.ts | 16 ++-- code/lib/manager-api/src/modules/versions.ts | 8 +- code/lib/manager-api/src/modules/whatsnew.ts | 8 +- 12 files changed, 85 insertions(+), 53 deletions(-) diff --git a/code/lib/manager-api/src/index.tsx b/code/lib/manager-api/src/index.tsx index 1c1798830583..c9098edc3844 100644 --- a/code/lib/manager-api/src/index.tsx +++ b/code/lib/manager-api/src/index.tsx @@ -305,6 +305,7 @@ function ManagerConsumer

({ const comboData = filterer.current(managerContext); const comboDataArray = useMemo(() => { + // @ts-expect-error (No overload matches this call) return [...Object.entries(comboData).reduce((acc, keyval) => acc.concat(keyval), [])]; }, [managerContext.state]); @@ -412,6 +413,7 @@ export function useSharedState(stateId: string, defaultState?: S) { useEffect(() => { if (quicksync) { + // @ts-expect-error (Argument of type 'S | undefined' is not assignable) api.setAddonState(stateId, defaultState); } }, [quicksync]); diff --git a/code/lib/manager-api/src/lib/store-setup.ts b/code/lib/manager-api/src/lib/store-setup.ts index 3ee481a35013..c29a0b2ad282 100644 --- a/code/lib/manager-api/src/lib/store-setup.ts +++ b/code/lib/manager-api/src/lib/store-setup.ts @@ -5,9 +5,11 @@ import { parse, stringify } from 'telejson'; // setting up the store, overriding set and get to use telejson export default (_: any) => { _.fn('set', function (key: string, data: object) { + // @ts-expect-error('this' implicitly has type 'any') return _.set(this._area, this._in(key), stringify(data, { maxDepth: 50 })); }); _.fn('get', function (key: string, alt: string) { + // @ts-expect-error('this' implicitly has type 'any') const value = _.get(this._area, this._in(key)); return value !== null ? parse(value) : alt || value; }); diff --git a/code/lib/manager-api/src/lib/stories.ts b/code/lib/manager-api/src/lib/stories.ts index 0ed013cd9ebc..dffc3dd45a86 100644 --- a/code/lib/manager-api/src/lib/stories.ts +++ b/code/lib/manager-api/src/lib/stories.ts @@ -21,7 +21,6 @@ import type { API_HashEntry, SetStoriesPayload, StoryIndexV2, - Renderer, } from '@storybook/types'; // eslint-disable-next-line import/no-cycle import { type API, combineParameters, type State } from '../index'; diff --git a/code/lib/manager-api/src/modules/channel.ts b/code/lib/manager-api/src/modules/channel.ts index c83c342c5253..0ab1dc910087 100644 --- a/code/lib/manager-api/src/modules/channel.ts +++ b/code/lib/manager-api/src/modules/channel.ts @@ -56,12 +56,12 @@ export const init: ModuleFn = ({ provider }) => { const api: SubAPI = { getChannel: () => provider.channel, on: (type, handler) => { - provider.channel.on(type, handler); + provider.channel?.on(type, handler); - return () => provider.channel.off(type, handler); + return () => provider.channel?.off(type, handler); }, - off: (type, handler) => provider.channel.off(type, handler), - once: (type, handler) => provider.channel.once(type, handler), + off: (type, handler) => provider.channel?.off(type, handler), + once: (type, handler) => provider.channel?.once(type, handler), emit: (type, data, ...args) => { if ( data?.options?.target && @@ -73,7 +73,7 @@ export const init: ModuleFn = ({ provider }) => { ? `storybook-ref-${data.options.target}` : 'storybook-preview-iframe'; } - provider.channel.emit(type, data, ...args); + provider.channel?.emit(type, data, ...args); }, collapseAll: () => { api.emit(STORIES_COLLAPSE_ALL, {}); diff --git a/code/lib/manager-api/src/modules/globals.ts b/code/lib/manager-api/src/modules/globals.ts index 17c35bbb7224..e9f33a75e202 100644 --- a/code/lib/manager-api/src/modules/globals.ts +++ b/code/lib/manager-api/src/modules/globals.ts @@ -35,14 +35,14 @@ export interface SubAPI { export const init: ModuleFn = ({ store, fullAPI, provider }) => { const api: SubAPI = { getGlobals() { - return store.getState().globals; + return store.getState().globals as Globals; }, getGlobalTypes() { - return store.getState().globalTypes; + return store.getState().globalTypes as GlobalTypes; }, updateGlobals(newGlobals) { // Only emit the message to the local ref - provider.channel.emit(UPDATE_GLOBALS, { + provider.channel?.emit(UPDATE_GLOBALS, { globals: newGlobals, options: { target: 'storybook-preview-iframe', @@ -62,7 +62,7 @@ export const init: ModuleFn = ({ store, fullAPI, provider }) = } }; - provider.channel.on( + provider.channel?.on( GLOBALS_UPDATED, function handleGlobalsUpdated(this: any, { globals }: { globals: Globals }) { const { ref } = getEventMetadata(this, fullAPI)!; @@ -78,7 +78,7 @@ export const init: ModuleFn = ({ store, fullAPI, provider }) = ); // Emitted by the preview on initialization - provider.channel.on( + provider.channel?.on( SET_GLOBALS, function handleSetStories(this: any, { globals, globalTypes }: SetGlobalsPayload) { const { ref } = getEventMetadata(this, fullAPI)!; diff --git a/code/lib/manager-api/src/modules/layout.ts b/code/lib/manager-api/src/modules/layout.ts index a7d529868613..7c8401e3c173 100644 --- a/code/lib/manager-api/src/modules/layout.ts +++ b/code/lib/manager-api/src/modules/layout.ts @@ -385,7 +385,7 @@ export const init: ModuleFn = ({ store, provider, singleStory const persisted = pick(store.getState(), 'layout', 'selectedPanel'); - provider.channel.on(SET_CONFIG, () => { + provider.channel?.on(SET_CONFIG, () => { api.setOptions(merge(api.getInitialOptions(), persisted)); }); diff --git a/code/lib/manager-api/src/modules/refs.ts b/code/lib/manager-api/src/modules/refs.ts index f1d52c2adf65..8a5159e9fdb9 100644 --- a/code/lib/manager-api/src/modules/refs.ts +++ b/code/lib/manager-api/src/modules/refs.ts @@ -295,6 +295,7 @@ export const init: ModuleFn = ( ) : storyIndex; + // @ts-expect-error (could be undefined) index = transformStoryIndexToStoriesHash(storyIndex, { provider, docsOptions, diff --git a/code/lib/manager-api/src/modules/shortcuts.ts b/code/lib/manager-api/src/modules/shortcuts.ts index 25f9d14095c6..d050028f8b8b 100644 --- a/code/lib/manager-api/src/modules/shortcuts.ts +++ b/code/lib/manager-api/src/modules/shortcuts.ts @@ -392,7 +392,7 @@ export const init: ModuleFn = ({ store, fullAPI, provider }) => { }); // Also listen to keydown events sent over the channel - provider.channel.on(PREVIEW_KEYDOWN, (data: { event: KeyboardEventLike }) => { + provider.channel?.on(PREVIEW_KEYDOWN, (data: { event: KeyboardEventLike }) => { api.handleKeydownEvent(data.event); }); }; diff --git a/code/lib/manager-api/src/modules/stories.ts b/code/lib/manager-api/src/modules/stories.ts index 828eabe1aa0c..b4a027f9be1c 100644 --- a/code/lib/manager-api/src/modules/stories.ts +++ b/code/lib/manager-api/src/modules/stories.ts @@ -93,7 +93,7 @@ export interface SubAPI { * @param {string} [refsId] - The ID of the refs to use for resolving the story. * @returns {API_HashEntry} - The hash entry corresponding to the given story ID. */ - resolveStory: (storyId: StoryId, refsId?: string) => API_HashEntry; + resolveStory: (storyId: StoryId, refsId?: string) => API_HashEntry | undefined; /** * Selects the first story to display in the Storybook UI. * @@ -322,9 +322,10 @@ export const init: ModuleFn = ({ resolveStory: (storyId, refId) => { const { refs, index } = store.getState(); if (refId && !refs[refId]) { - return null; + return undefined; } if (refId) { + // @ts-expect-error (possibly undefined) return refs[refId].index ? refs[refId].index[storyId] : undefined; } return index ? index[storyId] : undefined; @@ -353,7 +354,7 @@ export const init: ModuleFn = ({ }, getCurrentParameter: (parameterName) => { const { storyId, refId } = store.getState(); - const parameters = api.getParameters({ storyId, refId }, parameterName); + const parameters = api.getParameters({ storyId, refId: refId as string }, parameterName); // FIXME Returning falsey parameters breaks a bunch of toolbars code, // so this strange logic needs to be here until various client code is updated. return parameters || undefined; @@ -368,6 +369,11 @@ export const init: ModuleFn = ({ } const hash = refId ? refs[refId].index || {} : index; + + if (!hash) { + return; + } + const result = api.findSiblingStoryId(storyId, hash, direction, true); if (result) { @@ -384,6 +390,11 @@ export const init: ModuleFn = ({ } const hash = story.refId ? refs[story.refId].index : index; + + if (!hash) { + return; + } + const result = api.findSiblingStoryId(storyId, hash, direction, false); if (result) { @@ -392,6 +403,9 @@ export const init: ModuleFn = ({ }, selectFirstStory: () => { const { index } = store.getState(); + if (!index) { + return; + } const firstStory = Object.keys(index).find((id) => index[id].type === 'story'); if (firstStory) { @@ -409,6 +423,10 @@ export const init: ModuleFn = ({ const kindSlug = storyId?.split('--', 2)[0]; + if (!hash) { + return; + } + if (!name) { // Find the entry (group, component or story) that is referred to const entry = titleOrId ? hash[titleOrId] || hash[sanitize(titleOrId)] : hash[kindSlug]; @@ -491,15 +509,15 @@ export const init: ModuleFn = ({ }, updateStoryArgs: (story, updatedArgs) => { const { id: storyId, refId } = story; - provider.channel.emit(UPDATE_STORY_ARGS, { + provider.channel?.emit(UPDATE_STORY_ARGS, { storyId, updatedArgs, options: { target: refId }, }); }, - resetStoryArgs: (story, argNames?: [string]) => { + resetStoryArgs: (story, argNames) => { const { id: storyId, refId } = story; - provider.channel.emit(RESET_STORY_ARGS, { + provider.channel?.emit(RESET_STORY_ARGS, { storyId, argNames, options: { target: refId }, @@ -519,7 +537,7 @@ export const init: ModuleFn = ({ } await api.setIndex(storyIndex); - } catch (err) { + } catch (err: any) { await store.setState({ indexError: err }); } }, @@ -547,6 +565,9 @@ export const init: ModuleFn = ({ ): Promise => { if (!ref) { const { index } = store.getState(); + if (!index) { + return; + } index[storyId] = { ...index[storyId], ...update, @@ -568,6 +589,9 @@ export const init: ModuleFn = ({ ): Promise => { if (!ref) { const { index } = store.getState(); + if (!index) { + return; + } index[docsId] = { ...index[docsId], ...update, @@ -643,7 +667,7 @@ export const init: ModuleFn = ({ // On initial load, the local iframe will select the first story (or other "selection specifier") // and emit STORY_SPECIFIED with the id. We need to ensure we respond to this change. - provider.channel.on( + provider.channel?.on( STORY_SPECIFIED, function handler( this: any, @@ -664,7 +688,7 @@ export const init: ModuleFn = ({ state.path === '/' || state.viewMode === 'story' || state.viewMode === 'docs'; const stateHasSelection = state.viewMode && state.storyId; const stateSelectionDifferent = state.viewMode !== viewMode || state.storyId !== storyId; - const { type } = state.index[state.storyId] || {}; + const { type } = state.index?.[state.storyId] || {}; const isStory = !(type === 'root' || type === 'component' || type === 'group'); /** @@ -677,7 +701,7 @@ export const init: ModuleFn = ({ if (isCanvasRoute) { if (stateHasSelection && stateSelectionDifferent && isStory) { // The manager state is correct, the preview state is lagging behind - provider.channel.emit(SET_CURRENT_STORY, { + provider.channel?.emit(SET_CURRENT_STORY, { storyId: state.storyId, viewMode: state.viewMode, }); @@ -694,12 +718,12 @@ export const init: ModuleFn = ({ // 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. - provider.channel.on(CURRENT_STORY_WAS_SET, function handler(this: any) { + provider.channel?.on(CURRENT_STORY_WAS_SET, function handler(this: any) { const { ref } = getEventMetadata(this, fullAPI)!; api.setPreviewInitialized(ref); }); - provider.channel.on(STORY_CHANGED, function handler(this: any) { + provider.channel?.on(STORY_CHANGED, function handler(this: any) { const { sourceType } = getEventMetadata(this, fullAPI)!; if (sourceType === 'local') { @@ -711,7 +735,7 @@ export const init: ModuleFn = ({ } }); - provider.channel.on( + provider.channel?.on( STORY_PREPARED, function handler(this: any, { id, ...update }: StoryPreparedPayload) { const { ref, sourceType } = getEventMetadata(this, fullAPI)!; @@ -728,6 +752,10 @@ export const init: ModuleFn = ({ if (sourceType === 'local') { const { storyId, index, refId } = store.getState(); + if (!index) { + return; + } + // create a list of related stories to be preloaded const toBePreloaded = Array.from( new Set([ @@ -736,7 +764,7 @@ export const init: ModuleFn = ({ ]) ).filter(Boolean); - provider.channel.emit(PRELOAD_ENTRIES, { + provider.channel?.emit(PRELOAD_ENTRIES, { ids: toBePreloaded, options: { target: refId }, }); @@ -744,7 +772,7 @@ export const init: ModuleFn = ({ } ); - provider.channel.on( + provider.channel?.on( DOCS_PREPARED, function handler(this: any, { id, ...update }: DocsPreparedPayload) { const { ref } = getEventMetadata(this, fullAPI)!; @@ -752,7 +780,7 @@ export const init: ModuleFn = ({ } ); - provider.channel.on(SET_INDEX, function handler(this: any, index: API_PreparedStoryIndex) { + provider.channel?.on(SET_INDEX, function handler(this: any, index: API_PreparedStoryIndex) { const { ref } = getEventMetadata(this, fullAPI)!; if (!ref) { @@ -765,7 +793,7 @@ export const init: ModuleFn = ({ }); // For composition back-compatibilty - provider.channel.on(SET_STORIES, function handler(this: any, data: SetStoriesPayload) { + provider.channel?.on(SET_STORIES, function handler(this: any, data: SetStoriesPayload) { const { ref } = getEventMetadata(this, fullAPI)!; const setStoriesData = data.v ? denormalizeStoryParameters(data) : data.stories; @@ -776,7 +804,7 @@ export const init: ModuleFn = ({ } }); - provider.channel.on( + provider.channel?.on( SELECT_STORY, function handler( this: any, @@ -806,7 +834,7 @@ export const init: ModuleFn = ({ } ); - provider.channel.on( + provider.channel?.on( STORY_ARGS_UPDATED, function handleStoryArgsUpdated( this: any, @@ -818,17 +846,17 @@ export const init: ModuleFn = ({ ); // When there's a preview error, we don't show it in the manager, but simply - provider.channel.on(CONFIG_ERROR, function handleConfigError(this: any, err: any) { + provider.channel?.on(CONFIG_ERROR, function handleConfigError(this: any, err: any) { const { ref } = getEventMetadata(this, fullAPI)!; api.setPreviewInitialized(ref); }); - provider.channel.on(STORY_MISSING, function handleConfigError(this: any, err: any) { + provider.channel?.on(STORY_MISSING, function handleConfigError(this: any, err: any) { const { ref } = getEventMetadata(this, fullAPI)!; api.setPreviewInitialized(ref); }); - provider.channel.on(SET_CONFIG, () => { + provider.channel?.on(SET_CONFIG, () => { const config = provider.getConfig(); if (config?.sidebar?.filters) { store.setState({ @@ -845,7 +873,7 @@ export const init: ModuleFn = ({ return { api, state: { - storyId: initialStoryId, + storyId: initialStoryId as string, viewMode: initialViewMode, hasCalledSetOptions: false, previewInitialized: false, @@ -854,7 +882,7 @@ export const init: ModuleFn = ({ }, init: async () => { if (FEATURES?.storyStoreV7) { - provider.channel.on(STORY_INDEX_INVALIDATED, () => api.fetchIndex()); + provider.channel?.on(STORY_INDEX_INVALIDATED, () => api.fetchIndex()); await api.fetchIndex(); } }, diff --git a/code/lib/manager-api/src/modules/url.ts b/code/lib/manager-api/src/modules/url.ts index 27d8cef5258f..e9431f04a1f5 100644 --- a/code/lib/manager-api/src/modules/url.ts +++ b/code/lib/manager-api/src/modules/url.ts @@ -10,7 +10,7 @@ import { queryFromLocation, buildArgsParam } from '@storybook/router'; import { dequal as deepEqual } from 'dequal'; import { global } from '@storybook/global'; -import type { API_Layout, API_UI } from '@storybook/types'; +import type { API_Layout, API_UI, Args } from '@storybook/types'; import type { ModuleArgs, ModuleFn } from '../lib/types'; import { defaultLayoutState } from './layout'; @@ -101,7 +101,7 @@ const initialUrlSupport = ({ }; export interface QueryParams { - [key: string]: string | null; + [key: string]: string | undefined; } /** @@ -185,7 +185,7 @@ export const init: ModuleFn = (moduleArgs) => { }; if (!deepEqual(customQueryParams, update)) { store.setState({ customQueryParams: update }); - provider.channel.emit(UPDATE_QUERY_PARAMS, update); + provider.channel?.emit(UPDATE_QUERY_PARAMS, update); } }, navigateUrl(url, options) { @@ -204,15 +204,15 @@ export const init: ModuleFn = (moduleArgs) => { if (currentStory?.type !== 'story') return; const { args, initialArgs } = currentStory; - const argsString = buildArgsParam(initialArgs, args); + const argsString = buildArgsParam(initialArgs, args as Args); navigateTo(path, { ...queryParams, args: argsString }, { replace: true }); api.setQueryParams({ args: argsString }); }; - provider.channel.on(SET_CURRENT_STORY, () => updateArgsParam()); + provider.channel?.on(SET_CURRENT_STORY, () => updateArgsParam()); let handleOrId: any; - provider.channel.on(STORY_ARGS_UPDATED, () => { + provider.channel?.on(STORY_ARGS_UPDATED, () => { if ('requestIdleCallback' in globalWindow) { if (handleOrId) globalWindow.cancelIdleCallback(handleOrId); handleOrId = globalWindow.requestIdleCallback(updateArgsParam, { timeout: 1000 }); @@ -222,14 +222,14 @@ export const init: ModuleFn = (moduleArgs) => { } }); - provider.channel.on(GLOBALS_UPDATED, ({ globals, initialGlobals }: any) => { + provider.channel?.on(GLOBALS_UPDATED, ({ globals, initialGlobals }: any) => { const { path, queryParams } = api.getUrlState(); const globalsString = buildArgsParam(initialGlobals, globals); navigateTo(path, { ...queryParams, globals: globalsString }, { replace: true }); api.setQueryParams({ globals: globalsString }); }); - provider.channel.on(NAVIGATE_URL, (url: string, options: NavigateOptions) => { + provider.channel?.on(NAVIGATE_URL, (url: string, options: NavigateOptions) => { api.navigateUrl(url, options); }); diff --git a/code/lib/manager-api/src/modules/versions.ts b/code/lib/manager-api/src/modules/versions.ts index 15a8a724e651..a230da3a501f 100644 --- a/code/lib/manager-api/src/modules/versions.ts +++ b/code/lib/manager-api/src/modules/versions.ts @@ -62,16 +62,16 @@ export const init: ModuleFn = ({ store }) => { const { versions: { current }, } = store.getState(); - return current; + return current as API_Version; }, getLatestVersion: () => { const { versions: { latest, next, current }, } = store.getState(); if (current && semver.prerelease(current.version) && next) { - return latest && semver.gt(latest.version, next.version) ? latest : next; + return (latest && semver.gt(latest.version, next.version) ? latest : next) as API_Version; } - return latest; + return latest as API_Version; }, versionUpdateAvailable: () => { const latest = api.getLatestVersion(); @@ -110,7 +110,7 @@ export const init: ModuleFn = ({ store }) => { const { latest, next } = getVersionCheckData(); await store.setState({ - versions: { ...versions, latest, next }, + versions: { ...versions, latest, next } as API_Versions & API_UnknownEntries, }); }; diff --git a/code/lib/manager-api/src/modules/whatsnew.ts b/code/lib/manager-api/src/modules/whatsnew.ts index 27488f702d2e..ab460aa11486 100644 --- a/code/lib/manager-api/src/modules/whatsnew.ts +++ b/code/lib/manager-api/src/modules/whatsnew.ts @@ -47,7 +47,7 @@ export const init: ModuleFn = ({ fullAPI, store, provider }) => { ...state.whatsNewData, disableWhatsNewNotifications: !state.whatsNewData.disableWhatsNewNotifications, }); - provider.channel.emit(TOGGLE_WHATS_NEW_NOTIFICATIONS, { + provider.channel?.emit(TOGGLE_WHATS_NEW_NOTIFICATIONS, { disableWhatsNewNotifications: state.whatsNewData.disableWhatsNewNotifications, }); } @@ -55,17 +55,17 @@ export const init: ModuleFn = ({ fullAPI, store, provider }) => { }; function getLatestWhatsNewPost(): Promise { - provider.channel.emit(REQUEST_WHATS_NEW_DATA); + provider.channel?.emit(REQUEST_WHATS_NEW_DATA); return new Promise((resolve) => - provider.channel.once(RESULT_WHATS_NEW_DATA, ({ data }: { data: WhatsNewData }) => + provider.channel?.once(RESULT_WHATS_NEW_DATA, ({ data }: { data: WhatsNewData }) => resolve(data) ) ); } function setWhatsNewCache(cache: WhatsNewCache): void { - provider.channel.emit(SET_WHATS_NEW_CACHE, cache); + provider.channel?.emit(SET_WHATS_NEW_CACHE, cache); } const initModule = async () => {