mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-04 22:21:27 +08:00
REFACTOR preview so it renders many iframes
This commit is contained in:
parent
845c1db058
commit
2e2ec0fe79
58
lib/ui/src/components/preview/FramesRenderer.tsx
Normal file
58
lib/ui/src/components/preview/FramesRenderer.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
import React, { Fragment, FunctionComponent, useMemo, useEffect, useRef } from 'react';
|
||||
import { Global, CSSObject } from '@storybook/theming';
|
||||
import { IFrame } from './iframe';
|
||||
import { FramesRendererProps } from './utils/types';
|
||||
import { stringifyQueryParams } from './utils/stringifyQueryParams';
|
||||
|
||||
export const FramesRenderer: FunctionComponent<FramesRendererProps> = ({
|
||||
refs,
|
||||
story,
|
||||
scale,
|
||||
viewMode,
|
||||
queryParams,
|
||||
storyId,
|
||||
}) => {
|
||||
const stringifiedQueryParams = stringifyQueryParams(queryParams);
|
||||
const active = story && story.refId ? `storybook-ref-${story.refId}` : 'storybook-preview-iframe';
|
||||
|
||||
const styles = useMemo<CSSObject>(() => {
|
||||
return {
|
||||
...Object.values(refs).reduce(
|
||||
(acc, r) => ({
|
||||
...acc,
|
||||
[`#storybook-ref-${r.id}`]: {
|
||||
visibility: active === `storybook-ref-${r.id}` ? 'visible' : 'hidden',
|
||||
},
|
||||
}),
|
||||
{} as CSSObject
|
||||
),
|
||||
'#storybook-preview-iframe': {
|
||||
visibility: active === 'storybook-preview-iframe' ? 'visible' : 'hidden',
|
||||
},
|
||||
};
|
||||
}, [storyId, story, refs]);
|
||||
|
||||
const frames = useRef<Record<string, string>>({
|
||||
'storybook-preview-iframe': `iframe.html?id=${storyId}&viewMode=${viewMode}${stringifiedQueryParams}`,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
Object.values(refs)
|
||||
.filter(r => r.startInjected || (story && r.id === story.refId))
|
||||
.forEach(r => {
|
||||
frames.current = {
|
||||
...frames.current,
|
||||
[`storybook-ref-${r.id}`]: `${r.url}/iframe.html?id=${storyId}&viewMode=${viewMode}${stringifiedQueryParams}`,
|
||||
};
|
||||
});
|
||||
}, [storyId, story, refs]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Global styles={styles} />
|
||||
{Object.entries(frames.current).map(([id, src]) => (
|
||||
<IFrame key={id} id={id} title={id} src={src} allowFullScreen scale={scale} />
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
@ -1,101 +1,87 @@
|
||||
import React, { Fragment, FunctionComponent, useMemo, useEffect } from 'react';
|
||||
import merge from '@storybook/api/dist/lib/merge';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
|
||||
import { API, Consumer, Combo } from '@storybook/api';
|
||||
import { SET_CURRENT_STORY } from '@storybook/core-events';
|
||||
import addons, { types, Addon } from '@storybook/addons';
|
||||
import merge from '@storybook/api/dist/lib/merge';
|
||||
|
||||
import { Loader } from '@storybook/components';
|
||||
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
|
||||
import { Location } from '@storybook/router';
|
||||
|
||||
import * as S from './utils/components';
|
||||
|
||||
import { ZoomProvider, ZoomConsumer } from './tools/zoom';
|
||||
|
||||
import { IFrame } from './iframe';
|
||||
import { PreviewProps, ApplyWrappersProps, IframeRenderer } from './utils/types';
|
||||
|
||||
import { defaultWrappers, ApplyWrappers } from './wrappers';
|
||||
import { stringifyQueryParams } from './utils/stringifyQueryParams';
|
||||
import { ToolbarComp } from './toolbar';
|
||||
import { FramesRenderer } from './FramesRenderer';
|
||||
|
||||
export const renderIframe: IframeRenderer = (
|
||||
storyId,
|
||||
viewMode,
|
||||
id,
|
||||
baseUrl,
|
||||
scale,
|
||||
queryParams
|
||||
) => (
|
||||
<IFrame
|
||||
key="iframe"
|
||||
id="storybook-preview-iframe"
|
||||
title={id || 'preview'}
|
||||
src={`${baseUrl}?id=${storyId}&viewMode=${viewMode}${stringifyQueryParams(queryParams)}`}
|
||||
allowFullScreen
|
||||
scale={scale}
|
||||
/>
|
||||
);
|
||||
import { PreviewProps } from './utils/types';
|
||||
|
||||
const getWrapper = (getFn: API['getElements']) => Object.values(getFn<Addon>(types.PREVIEW));
|
||||
const getTabs = (getFn: API['getElements']) => Object.values(getFn<Addon>(types.TAB));
|
||||
|
||||
export const getTools = (getFn: API['getElements']) => Object.values(getFn<Addon>(types.TOOL));
|
||||
|
||||
export const getToolsExtra = (getFn: API['getElements']) =>
|
||||
Object.values(getFn<Addon>(types.TOOLEXTRA));
|
||||
|
||||
const mapper = ({ state, api }: Combo) => ({
|
||||
const canvasMapper = ({ state, api }: Combo) => ({
|
||||
storyId: state.storyId,
|
||||
viewMode: state.viewMode,
|
||||
customCanvas: api.renderPreview,
|
||||
queryParams: state.customQueryParams,
|
||||
getElements: api.getElements,
|
||||
isLoading: !state.storiesConfigured,
|
||||
story: api.getData(state.storyId),
|
||||
refs: state.refs,
|
||||
});
|
||||
|
||||
const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): Addon => ({
|
||||
id: 'canvas',
|
||||
title: 'Canvas',
|
||||
route: p => `/story/${p.storyId}`,
|
||||
match: p => !!(p.viewMode && p.viewMode.match(/^(story|docs)$/)),
|
||||
render: p => {
|
||||
route: ({ storyId }) => `/story/${storyId}`,
|
||||
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
|
||||
render: ({ active, key }) => {
|
||||
return (
|
||||
<Consumer filter={mapper}>
|
||||
<Consumer filter={canvasMapper} key={key}>
|
||||
{({
|
||||
story,
|
||||
refs,
|
||||
customCanvas,
|
||||
storyId,
|
||||
viewMode,
|
||||
queryParams,
|
||||
getElements,
|
||||
isLoading,
|
||||
}: ReturnType<typeof mapper>) => (
|
||||
}: ReturnType<typeof canvasMapper>) => (
|
||||
<ZoomConsumer>
|
||||
{({ value: scale }) => {
|
||||
const wrappers = [...defaultWrappers, ...getWrapper(getElements)];
|
||||
const wrappers = useMemo(() => [...defaultWrappers, ...getWrapper(getElements)], [
|
||||
getElements,
|
||||
...defaultWrappers,
|
||||
]);
|
||||
|
||||
const data = [storyId, viewMode, id, baseUrl, scale, queryParams] as Parameters<
|
||||
IframeRenderer
|
||||
>;
|
||||
const content = customCanvas ? (
|
||||
customCanvas(storyId, viewMode, id, baseUrl, scale, queryParams)
|
||||
) : (
|
||||
<FramesRenderer
|
||||
refs={refs}
|
||||
scale={scale}
|
||||
story={story}
|
||||
viewMode={viewMode}
|
||||
queryParams={queryParams}
|
||||
storyId={storyId}
|
||||
/>
|
||||
);
|
||||
|
||||
const content = customCanvas ? customCanvas(...data) : renderIframe(...data);
|
||||
const props = {
|
||||
viewMode,
|
||||
active: p.active,
|
||||
wrappers,
|
||||
id,
|
||||
storyId,
|
||||
baseUrl,
|
||||
queryParams,
|
||||
scale,
|
||||
customCanvas,
|
||||
} as ApplyWrappersProps;
|
||||
const isLoading =
|
||||
(storyId && !story) || (story && story.refId && !refs[story.refId].startInjected);
|
||||
|
||||
return (
|
||||
<>
|
||||
{withLoader && isLoading && <Loader id="preview-loader" role="progressbar" />}
|
||||
<ApplyWrappers {...props}>{content}</ApplyWrappers>
|
||||
<ApplyWrappers
|
||||
id={id}
|
||||
storyId={storyId}
|
||||
viewMode={viewMode}
|
||||
active={active}
|
||||
wrappers={wrappers}
|
||||
>
|
||||
{content}
|
||||
</ApplyWrappers>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
@ -148,7 +134,11 @@ const Preview: FunctionComponent<PreviewProps> = props => {
|
||||
|
||||
useEffect(() => {
|
||||
if (story) {
|
||||
api.emit(SET_CURRENT_STORY, { storyId: story.id, viewMode });
|
||||
api.emit(SET_CURRENT_STORY, {
|
||||
storyId: story.knownAs || story.id,
|
||||
viewMode,
|
||||
options: { target: story.refId },
|
||||
});
|
||||
}
|
||||
}, [story, viewMode]);
|
||||
|
||||
@ -160,7 +150,7 @@ const Preview: FunctionComponent<PreviewProps> = props => {
|
||||
</Helmet>
|
||||
)}
|
||||
<ZoomProvider>
|
||||
<ToolbarComp story={story} api={api} isShown={isToolshown} tabs={tabs} />
|
||||
<ToolbarComp key="tools" story={story} api={api} isShown={isToolshown} tabs={tabs} />
|
||||
<S.FrameWrap key="frame" offset={isToolshown ? 40 : 0}>
|
||||
{tabs.map(({ render: Render, match, ...t }, i) => {
|
||||
// @ts-ignore
|
||||
|
@ -17,7 +17,7 @@ export interface PreviewProps {
|
||||
path: string;
|
||||
location: State['location'];
|
||||
queryParams: State['customQueryParams'];
|
||||
customCanvas?: IframeRenderer;
|
||||
customCanvas?: CustomCanvasRenderer;
|
||||
description: string;
|
||||
baseUrl: string;
|
||||
withLoader: boolean;
|
||||
@ -41,13 +41,9 @@ export interface ApplyWrappersProps {
|
||||
id: string;
|
||||
storyId: string;
|
||||
active: boolean;
|
||||
baseUrl: string;
|
||||
scale: number;
|
||||
queryParams: Record<string, any>;
|
||||
customCanvas?: IframeRenderer;
|
||||
}
|
||||
|
||||
export type IframeRenderer = (
|
||||
export type CustomCanvasRenderer = (
|
||||
storyId: string,
|
||||
viewMode: State['viewMode'],
|
||||
id: string,
|
||||
@ -55,3 +51,12 @@ export type IframeRenderer = (
|
||||
scale: number,
|
||||
queryParams: Record<string, any>
|
||||
) => ReactNode;
|
||||
|
||||
export interface FramesRendererProps {
|
||||
story: Story | Group;
|
||||
storyId: string;
|
||||
scale: number;
|
||||
viewMode: ViewMode;
|
||||
queryParams: State['customQueryParams'];
|
||||
refs: State['refs'];
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user