This commit is contained in:
Norbert de Langen 2023-11-29 14:25:49 +01:00
parent 0db9c480f0
commit 5a336071e8
12 changed files with 85 additions and 53 deletions

View File

@ -305,6 +305,7 @@ function ManagerConsumer<P = Combo>({
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<S>(stateId: string, defaultState?: S) {
useEffect(() => {
if (quicksync) {
// @ts-expect-error (Argument of type 'S | undefined' is not assignable)
api.setAddonState<S>(stateId, defaultState);
}
}, [quicksync]);

View File

@ -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;
});

View File

@ -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';

View File

@ -56,12 +56,12 @@ export const init: ModuleFn<SubAPI, SubState> = ({ 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<SubAPI, SubState> = ({ 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, {});

View File

@ -35,14 +35,14 @@ export interface SubAPI {
export const init: ModuleFn<SubAPI, SubState> = ({ 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<SubAPI, SubState> = ({ 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<SubAPI, SubState> = ({ 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)!;

View File

@ -385,7 +385,7 @@ export const init: ModuleFn<SubAPI, SubState> = ({ 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));
});

View File

@ -295,6 +295,7 @@ export const init: ModuleFn<SubAPI, SubState> = (
)
: storyIndex;
// @ts-expect-error (could be undefined)
index = transformStoryIndexToStoriesHash(storyIndex, {
provider,
docsOptions,

View File

@ -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);
});
};

View File

@ -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<SubAPI, SubState> = ({
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<SubAPI, SubState> = ({
},
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<SubAPI, SubState> = ({
}
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<SubAPI, SubState> = ({
}
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<SubAPI, SubState> = ({
},
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<SubAPI, SubState> = ({
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<SubAPI, SubState> = ({
},
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<SubAPI, SubState> = ({
}
await api.setIndex(storyIndex);
} catch (err) {
} catch (err: any) {
await store.setState({ indexError: err });
}
},
@ -547,6 +565,9 @@ export const init: ModuleFn<SubAPI, SubState> = ({
): Promise<void> => {
if (!ref) {
const { index } = store.getState();
if (!index) {
return;
}
index[storyId] = {
...index[storyId],
...update,
@ -568,6 +589,9 @@ export const init: ModuleFn<SubAPI, SubState> = ({
): Promise<void> => {
if (!ref) {
const { index } = store.getState();
if (!index) {
return;
}
index[docsId] = {
...index[docsId],
...update,
@ -643,7 +667,7 @@ export const init: ModuleFn<SubAPI, SubState> = ({
// 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<SubAPI, SubState> = ({
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<SubAPI, SubState> = ({
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<SubAPI, SubState> = ({
// 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<SubAPI, SubState> = ({
}
});
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<SubAPI, SubState> = ({
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<SubAPI, SubState> = ({
])
).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<SubAPI, SubState> = ({
}
);
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<SubAPI, SubState> = ({
}
);
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<SubAPI, SubState> = ({
});
// 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<SubAPI, SubState> = ({
}
});
provider.channel.on(
provider.channel?.on(
SELECT_STORY,
function handler(
this: any,
@ -806,7 +834,7 @@ export const init: ModuleFn<SubAPI, SubState> = ({
}
);
provider.channel.on(
provider.channel?.on(
STORY_ARGS_UPDATED,
function handleStoryArgsUpdated(
this: any,
@ -818,17 +846,17 @@ export const init: ModuleFn<SubAPI, SubState> = ({
);
// 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<SubAPI, SubState> = ({
return {
api,
state: {
storyId: initialStoryId,
storyId: initialStoryId as string,
viewMode: initialViewMode,
hasCalledSetOptions: false,
previewInitialized: false,
@ -854,7 +882,7 @@ export const init: ModuleFn<SubAPI, SubState> = ({
},
init: async () => {
if (FEATURES?.storyStoreV7) {
provider.channel.on(STORY_INDEX_INVALIDATED, () => api.fetchIndex());
provider.channel?.on(STORY_INDEX_INVALIDATED, () => api.fetchIndex());
await api.fetchIndex();
}
},

View File

@ -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<SubAPI, SubState> = (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<SubAPI, SubState> = (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<SubAPI, SubState> = (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);
});

View File

@ -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,
});
};

View File

@ -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<WhatsNewData> {
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 () => {