diff --git a/code/addons/docs/src/blocks/blocks/Canvas.tsx b/code/addons/docs/src/blocks/blocks/Canvas.tsx index 50f2c7b9d7d0..a4185020ff7d 100644 --- a/code/addons/docs/src/blocks/blocks/Canvas.tsx +++ b/code/addons/docs/src/blocks/blocks/Canvas.tsx @@ -1,7 +1,8 @@ /* eslint-disable react/destructuring-assignment */ -import React, { useContext } from 'react'; +import React, { useCallback, useContext } from 'react'; import type { FC } from 'react'; +import { FORCE_REMOUNT } from 'storybook/internal/core-events'; import type { ModuleExport, ModuleExports } from 'storybook/internal/types'; import type { Layout, PreviewProps as PurePreviewProps } from '../components'; @@ -82,6 +83,10 @@ const CanvasImpl: FC = (props) => { // By default, stories will be iframed, but most frameworks support inline rendering and override that in a docs entry file const inline = props.story?.inline ?? story.parameters?.docs?.story?.inline ?? false; + const handleReloadStory = useCallback(() => { + docsContext.channel.emit(FORCE_REMOUNT, { storyId: story.id }); + }, [docsContext.channel, story.id]); + return ( = (props) => { className={className} layout={layout} inline={inline} + onReloadStory={inline ? handleReloadStory : undefined} > diff --git a/code/addons/docs/src/blocks/components/Preview.tsx b/code/addons/docs/src/blocks/components/Preview.tsx index ceb904044056..cd8b8760b832 100644 --- a/code/addons/docs/src/blocks/components/Preview.tsx +++ b/code/addons/docs/src/blocks/components/Preview.tsx @@ -31,6 +31,7 @@ export type PreviewProps = PropsWithChildren<{ withToolbar?: boolean; className?: string; additionalActions?: ActionItem[]; + onReloadStory?: () => void; }>; export type Layout = 'padded' | 'fullscreen' | 'centered'; @@ -150,6 +151,7 @@ export const Preview: FC = ({ className, layout = 'padded', inline = false, + onReloadStory, ...props }) => { const [expanded, setExpanded] = useState(isExpanded); @@ -200,6 +202,7 @@ export const Preview: FC = ({ zoom={(z: number) => setScale(scale * z)} resetZoom={() => setScale(1)} storyId={!isLoading && childProps ? getStoryId(childProps, context) : undefined} + onReloadStory={onReloadStory} /> )} diff --git a/code/addons/docs/src/blocks/components/Toolbar.tsx b/code/addons/docs/src/blocks/components/Toolbar.tsx index afebccb47b68..a9faf0be1280 100644 --- a/code/addons/docs/src/blocks/components/Toolbar.tsx +++ b/code/addons/docs/src/blocks/components/Toolbar.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { Button, Toolbar as SharedToolbar } from 'storybook/internal/components'; -import { ShareAltIcon, ZoomIcon, ZoomOutIcon, ZoomResetIcon } from '@storybook/icons'; +import { ShareAltIcon, SyncIcon, ZoomIcon, ZoomOutIcon, ZoomResetIcon } from '@storybook/icons'; import { styled } from 'storybook/theming'; @@ -18,6 +18,10 @@ interface EjectProps { storyId?: string; } +interface ReloadProps { + onReloadStory?: () => void; +} + interface BarProps { border?: boolean; } @@ -26,7 +30,7 @@ interface LoadingProps { isLoading?: boolean; } -export type ToolbarProps = BarProps & ZoomProps & EjectProps & LoadingProps; +export type ToolbarProps = BarProps & ZoomProps & EjectProps & LoadingProps & ReloadProps; const AbsoluteBar = styled(SharedToolbar)({ position: 'absolute', @@ -53,13 +57,31 @@ const IconPlaceholder = styled.div(({ theme }) => ({ animation: `${theme.animation.glow} 1.5s ease-in-out infinite`, })); -export const Toolbar: FC = ({ isLoading, storyId, zoom, resetZoom, ...rest }) => ( +export const Toolbar: FC = ({ + isLoading, + storyId, + zoom, + resetZoom, + onReloadStory, + ...rest +}) => ( {isLoading ? ( [1, 2, 3].map((key) => ) ) : ( <> + {onReloadStory && ( + + )}