Skip to content

Commit

Permalink
Merge pull request #14658 from Dschungelabenteuer/fix-issue-14401
Browse files Browse the repository at this point in the history
API: Add addon keyboard shortcuts & create shortcuts for addon-viewport
  • Loading branch information
shilman authored Apr 27, 2021
2 parents 1c4e048 + dc09772 commit 4818dae
Show file tree
Hide file tree
Showing 9 changed files with 367 additions and 6 deletions.
9 changes: 8 additions & 1 deletion addons/viewport/src/Tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { styled, Global, Theme, withTheme } from '@storybook/theming';

import { Icons, IconButton, WithTooltip, TooltipLinkList } from '@storybook/components';

import { useParameter, useAddonState } from '@storybook/api';
import { useStorybookApi, useParameter, useAddonState } from '@storybook/api';
import { registerShortcuts } from './shortcuts';
import { PARAM_KEY, ADDON_ID } from './constants';
import { MINIMAL_VIEWPORTS } from './defaults';
import { ViewportAddonParameter, ViewportMap, ViewportStyles, Styles } from './models';
Expand Down Expand Up @@ -135,13 +136,19 @@ export const ViewportTool: FunctionComponent = memo(
});

const list = toList(viewports);
const api = useStorybookApi();

if (!list.find((i) => i.id === defaultViewport)) {
// eslint-disable-next-line no-console
console.warn(
`Cannot find "defaultViewport" of "${defaultViewport}" in addon-viewport configs, please check the "viewports" setting in the configuration.`
);
}

useEffect(() => {
registerShortcuts(api, setState, Object.keys(viewports));
}, [viewports]);

useEffect(() => {
setState({
selected:
Expand Down
60 changes: 60 additions & 0 deletions addons/viewport/src/shortcuts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { API } from '@storybook/api';
import { ADDON_ID } from './constants';

const getCurrentViewportIndex = (viewportsKeys: string[], current: string): number =>
viewportsKeys.indexOf(current);

const getNextViewport = (viewportsKeys: string[], current: string): string => {
const currentViewportIndex = getCurrentViewportIndex(viewportsKeys, current);
return currentViewportIndex === viewportsKeys.length - 1
? viewportsKeys[0]
: viewportsKeys[currentViewportIndex + 1];
};

const getPreviousViewport = (viewportsKeys: string[], current: string): string => {
const currentViewportIndex = getCurrentViewportIndex(viewportsKeys, current);
return currentViewportIndex < 1
? viewportsKeys[viewportsKeys.length - 1]
: viewportsKeys[currentViewportIndex - 1];
};

export const registerShortcuts = async (api: API, setState: any, viewportsKeys: string[]) => {
await api.setAddonShortcut(ADDON_ID, {
label: 'Previous viewport',
defaultShortcut: ['shift', 'V'],
actionName: 'previous',
action: () => {
const { selected, isRotated } = api.getAddonState(ADDON_ID);
setState({
selected: getPreviousViewport(viewportsKeys, selected),
isRotated,
});
},
});

await api.setAddonShortcut(ADDON_ID, {
label: 'Next viewport',
defaultShortcut: ['V'],
actionName: 'next',
action: () => {
const { selected, isRotated } = api.getAddonState(ADDON_ID);
setState({
selected: getNextViewport(viewportsKeys, selected),
isRotated,
});
},
});

await api.setAddonShortcut(ADDON_ID, {
label: 'Reset viewport',
defaultShortcut: ['alt', 'V'],
actionName: 'reset',
action: () => {
const { isRotated } = api.getAddonState(ADDON_ID);
setState({
selected: 'reset',
isRotated,
});
},
});
};
9 changes: 9 additions & 0 deletions docs/essentials/viewport.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,12 @@ You can change your story through [parameters](../writing-stories/parameters.md)
/>

<!-- prettier-ignore-end -->

### Keyboard shortcuts

* Previous viewport: <kbd>shift</kbd> + <kbd>v</kbd>
* Next viewport: <kbd>v</kbd>
* Reset viewport: <kbd>alt</kbd> + <kbd>v</kbd>


These shortcuts can be edited in Storybook's Keyboard shortcuts page.
56 changes: 54 additions & 2 deletions lib/api/src/modules/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ export interface SubState {

export interface SubAPI {
getShortcutKeys(): Shortcuts;
getDefaultShortcuts(): Shortcuts | AddonShortcutDefaults;
getAddonsShortcuts(): AddonShortcuts;
getAddonsShortcutLabels(): AddonShortcutLabels;
getAddonsShortcutDefaults(): AddonShortcutDefaults;
setShortcuts(shortcuts: Shortcuts): Promise<Shortcuts>;
setShortcut(action: Action, value: KeyCollection): Promise<KeyCollection>;
setAddonShortcut(addon: string, shortcut: AddonShortcut): Promise<AddonShortcut>;
restoreAllDefaultShortcuts(): Promise<Shortcuts>;
restoreDefaultShortcut(action: Action): Promise<KeyCollection>;
handleKeydownEvent(event: Event): void;
Expand Down Expand Up @@ -52,6 +57,17 @@ export interface Shortcuts {

export type Action = keyof Shortcuts;

interface AddonShortcut {
label: string;
defaultShortcut: KeyCollection;
actionName: string;
showInMenu?: boolean;
action: (...args: any[]) => any;
}
type AddonShortcuts = Record<string, AddonShortcut>;
type AddonShortcutLabels = Record<string, string>;
type AddonShortcutDefaults = Record<string, KeyCollection>;

export const defaultShortcuts: Shortcuts = Object.freeze({
fullScreen: ['F'],
togglePanel: ['A'],
Expand All @@ -73,6 +89,7 @@ export const defaultShortcuts: Shortcuts = Object.freeze({
expandAll: [controlOrMetaKey(), 'shift', 'ArrowDown'],
});

const addonsShortcuts: AddonShortcuts = {};
export interface Event extends KeyboardEvent {
target: {
tagName: string;
Expand All @@ -96,20 +113,54 @@ export const init: ModuleFn = ({ store, fullAPI }) => {
getShortcutKeys(): Shortcuts {
return store.getState().shortcuts;
},
getDefaultShortcuts(): Shortcuts | AddonShortcutDefaults {
return {
...defaultShortcuts,
...api.getAddonsShortcutDefaults(),
};
},
getAddonsShortcuts(): AddonShortcuts {
return addonsShortcuts;
},
getAddonsShortcutLabels(): AddonShortcutLabels {
const labels: AddonShortcutLabels = {};
Object.entries(api.getAddonsShortcuts()).forEach(([actionName, { label }]) => {
labels[actionName] = label;
});

return labels;
},
getAddonsShortcutDefaults(): AddonShortcutDefaults {
const defaults: AddonShortcutDefaults = {};
Object.entries(api.getAddonsShortcuts()).forEach(([actionName, { defaultShortcut }]) => {
defaults[actionName] = defaultShortcut;
});

return defaults;
},
async setShortcuts(shortcuts: Shortcuts) {
await store.setState({ shortcuts }, { persistence: 'permanent' });
return shortcuts;
},
async restoreAllDefaultShortcuts() {
return api.setShortcuts(defaultShortcuts);
return api.setShortcuts(api.getDefaultShortcuts() as Shortcuts);
},
async setShortcut(action, value) {
const shortcuts = api.getShortcutKeys();
await api.setShortcuts({ ...shortcuts, [action]: value });
return value;
},
async setAddonShortcut(addon: string, shortcut: AddonShortcut) {
const shortcuts = api.getShortcutKeys();
await api.setShortcuts({
...shortcuts,
[`${addon}-${shortcut.actionName}`]: shortcut.defaultShortcut,
});
addonsShortcuts[`${addon}-${shortcut.actionName}`] = shortcut;
return shortcut;
},
async restoreDefaultShortcut(action) {
const defaultShortcut = defaultShortcuts[action];
const defaultShortcut = api.getDefaultShortcuts()[action];
return api.setShortcut(action, defaultShortcut);
},

Expand Down Expand Up @@ -277,6 +328,7 @@ export const init: ModuleFn = ({ store, fullAPI }) => {
break;
}
default:
addonsShortcuts[feature].action();
break;
}
},
Expand Down
Loading

0 comments on commit 4818dae

Please sign in to comment.