Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Add remount story shortcut #21401

Merged
merged 9 commits into from
Apr 11, 2023
4 changes: 4 additions & 0 deletions code/lib/manager-api/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import * as settings from './modules/settings';
import * as releaseNotes from './modules/release-notes';
// eslint-disable-next-line import/no-cycle
import * as stories from './modules/stories';
import * as toolbar from './modules/toolbar';

import * as refs from './modules/refs';
import * as layout from './modules/layout';
Expand Down Expand Up @@ -92,6 +93,7 @@ export type State = layout.SubState &
url.SubState &
shortcuts.SubState &
releaseNotes.SubState &
toolbar.SubState &
settings.SubState &
globals.SubState &
RouterData &
Expand All @@ -109,6 +111,7 @@ export type API = addons.SubAPI &
notifications.SubAPI &
shortcuts.SubAPI &
releaseNotes.SubAPI &
toolbar.SubAPI &
settings.SubAPI &
version.SubAPI &
url.SubAPI &
Expand Down Expand Up @@ -216,6 +219,7 @@ class ManagerProvider extends Component<ManagerProviderProps, State> {
releaseNotes,
shortcuts,
stories,
toolbar,
refs,
globals,
url,
Expand Down
6 changes: 6 additions & 0 deletions code/lib/manager-api/src/modules/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface API_Shortcuts {
escape: API_KeyCollection;
collapseAll: API_KeyCollection;
expandAll: API_KeyCollection;
remount: API_KeyCollection;
}

export type API_Action = keyof API_Shortcuts;
Expand Down Expand Up @@ -91,6 +92,7 @@ export const defaultShortcuts: API_Shortcuts = Object.freeze({
escape: ['escape'], // This one is not customizable
collapseAll: [controlOrMetaKey(), 'shift', 'ArrowUp'],
expandAll: [controlOrMetaKey(), 'shift', 'ArrowDown'],
remount: ['alt', 'R'],
});

const addonsShortcuts: API_AddonShortcuts = {};
Expand Down Expand Up @@ -320,6 +322,10 @@ export const init: ModuleFn = ({ store, fullAPI }) => {
fullAPI.expandAll();
break;
}
case 'remount': {
fullAPI.remount();
ndelangen marked this conversation as resolved.
Show resolved Hide resolved
break;
}
default:
addonsShortcuts[feature].action();
break;
Expand Down
30 changes: 30 additions & 0 deletions code/lib/manager-api/src/modules/toolbar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { FORCE_REMOUNT } from '@storybook/core-events';
import type { ModuleFn } from '../index';

export interface SubState {
remount: { isAnimating: boolean };
}

export interface SubAPI {
remount: () => void;
remountEnd: () => void;
}

export const init: ModuleFn = ({ store, fullAPI }) => {
const api: SubAPI = {
remount: () => {
const { storyId, remount } = store.getState();
if (!storyId) return;
store.setState({ remount: { ...remount, isAnimating: true } });
fullAPI.emit(FORCE_REMOUNT, { storyId });
},
remountEnd: () => {
const { remount } = store.getState();
store.setState({ remount: { ...remount, isAnimating: false } });
},
};

const state: SubState = { remount: { isAnimating: false } };

return { api, state };
};
60 changes: 60 additions & 0 deletions code/lib/manager-api/src/tests/toolbar.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { FORCE_REMOUNT } from '@storybook/core-events';
import { init as initToolbar } from '../modules/toolbar';

describe('toolbar API', () => {
function getValidMockedStore() {
const store = {
getState: () => ({
remount: { isAnimating: true },
storyId: 'primary-btn-test',
}),
setState: jest.fn(),
};
return store;
}

function getInvalidMockedStore() {
const store = {
getState: () => ({
remount: { isAnimating: false },
storyId: null,
}),
setState: jest.fn(),
};
return store;
}

it('does not remount component if storyId is null', () => {
const store = getInvalidMockedStore();

const { api } = initToolbar({ store });

api.remount();
expect(store.setState).not.toHaveBeenCalledWith();
});

it('remounts component and starts animation', () => {
const store = getValidMockedStore();
const { storyId } = store.getState();
const mockFullApi = { emit: jest.fn() };

const { api } = initToolbar({ store, fullAPI: mockFullApi });

api.remount();
expect(mockFullApi.emit).toHaveBeenCalledWith(FORCE_REMOUNT, { storyId });
expect(store.setState).toHaveBeenCalledWith({
remount: { isAnimating: true },
});
});

it('ends remount animation', () => {
const store = getValidMockedStore();

const { api } = initToolbar({ store });

api.remountEnd();
expect(store.setState).toHaveBeenCalledWith({
remount: { isAnimating: false },
});
});
});
1 change: 1 addition & 0 deletions code/ui/manager/src/components/layout/app.mockdata.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const shortcuts: State['shortcuts'] = {
escape: ['escape'],
collapseAll: ['ctrl', 'shift', 'ArrowUp'],
expandAll: ['ctrl', 'shift', 'ArrowDown'],
remount: ['alt', 'R'],
};

export const panels: Addon_Collection = {
Expand Down
23 changes: 9 additions & 14 deletions code/ui/manager/src/components/preview/tools/remount.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import type { ComponentProps } from 'react';
import React, { useState } from 'react';
import React from 'react';
import { IconButton, Icons } from '@storybook/components';
import { Consumer } from '@storybook/manager-api';
import type { Addon, Combo } from '@storybook/manager-api';
import { styled } from '@storybook/theming';
import { FORCE_REMOUNT } from '@storybook/core-events';

interface AnimatedButtonProps {
animating?: boolean;
Expand All @@ -20,10 +19,13 @@ const StyledAnimatedIconButton = styled(IconButton)<
}));

const menuMapper = ({ api, state }: Combo) => {
const { storyId } = state;
const { storyId, remount } = state;
const { isAnimating } = remount;
return {
storyId,
remount: () => api.emit(FORCE_REMOUNT, { storyId: state.storyId }),
remount: () => api.remount(),
remountEnd: () => api.remountEnd(),
isAnimating,
};
};

Expand All @@ -33,20 +35,13 @@ export const remountTool: Addon = {
match: ({ viewMode }) => viewMode === 'story',
render: () => (
<Consumer filter={menuMapper}>
{({ remount, storyId }) => {
const [isAnimating, setIsAnimating] = useState(false);
const animateAndReplay = () => {
if (!storyId) return;
setIsAnimating(true);
remount();
};

{({ remount, storyId, remountEnd, isAnimating }) => {
return (
<StyledAnimatedIconButton
key="remount"
title="Remount component"
onClick={animateAndReplay}
onAnimationEnd={() => setIsAnimating(false)}
onClick={remount}
onAnimationEnd={remountEnd}
animating={isAnimating}
disabled={!storyId}
>
Expand Down
1 change: 1 addition & 0 deletions code/ui/manager/src/settings/shortcuts.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const defaultShortcuts = {
escape: ['escape'], // This one is not customizable
collapseAll: ['ctrl', 'shift', 'ArrowUp'],
expandAll: ['ctrl', 'shift', 'ArrowDown'],
remount: ['alt', 'R'],
};

const actions = makeActions(
Expand Down
1 change: 1 addition & 0 deletions code/ui/manager/src/settings/shortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ const shortcutLabels = {
aboutPage: 'Go to about page',
collapseAll: 'Collapse all items on sidebar',
expandAll: 'Expand all items on sidebar',
remount: 'Remount component',
};

export type Feature = keyof typeof shortcutLabels;
Expand Down