mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-07 23:12:03 +08:00
Added the possibility to add addon shortcuts & created shortcuts for addon-viewport
This commit is contained in:
parent
ca184fc0b8
commit
8e45dbd361
@ -6,7 +6,8 @@ import { styled, Global, Theme, withTheme } from '@storybook/theming';
|
|||||||
|
|
||||||
import { Icons, IconButton, WithTooltip, TooltipLinkList } from '@storybook/components';
|
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 { PARAM_KEY, ADDON_ID } from './constants';
|
||||||
import { MINIMAL_VIEWPORTS } from './defaults';
|
import { MINIMAL_VIEWPORTS } from './defaults';
|
||||||
import { ViewportAddonParameter, ViewportMap, ViewportStyles, Styles } from './models';
|
import { ViewportAddonParameter, ViewportMap, ViewportStyles, Styles } from './models';
|
||||||
@ -135,8 +136,10 @@ export const ViewportTool: FunctionComponent = memo(
|
|||||||
});
|
});
|
||||||
|
|
||||||
const list = toList(viewports);
|
const list = toList(viewports);
|
||||||
|
const api = useStorybookApi();
|
||||||
|
|
||||||
if (!list.find((i) => i.id === defaultViewport)) {
|
if (!list.find((i) => i.id === defaultViewport)) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
console.warn(
|
console.warn(
|
||||||
`Cannot find "defaultViewport" of "${defaultViewport}" in addon-viewport configs, please check the "viewports" setting in the configuration.`
|
`Cannot find "defaultViewport" of "${defaultViewport}" in addon-viewport configs, please check the "viewports" setting in the configuration.`
|
||||||
);
|
);
|
||||||
@ -148,6 +151,7 @@ export const ViewportTool: FunctionComponent = memo(
|
|||||||
defaultViewport || (viewports[state.selected] ? state.selected : responsiveViewport.id),
|
defaultViewport || (viewports[state.selected] ? state.selected : responsiveViewport.id),
|
||||||
isRotated: state.isRotated,
|
isRotated: state.isRotated,
|
||||||
});
|
});
|
||||||
|
registerShortcuts(api, setState);
|
||||||
}, [defaultViewport]);
|
}, [defaultViewport]);
|
||||||
|
|
||||||
const { selected, isRotated } = state;
|
const { selected, isRotated } = state;
|
||||||
|
59
addons/viewport/src/shortcuts.ts
Normal file
59
addons/viewport/src/shortcuts.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { API } from '@storybook/api';
|
||||||
|
import { ADDON_ID } from './constants';
|
||||||
|
import { MINIMAL_VIEWPORTS } from './defaults';
|
||||||
|
|
||||||
|
const viewportsKeys = Object.keys(MINIMAL_VIEWPORTS);
|
||||||
|
const getCurrentViewportIndex = (current: string): number => viewportsKeys.indexOf(current);
|
||||||
|
const getNextViewport = (current: string): string => {
|
||||||
|
const currentViewportIndex = getCurrentViewportIndex(current);
|
||||||
|
return currentViewportIndex === viewportsKeys.length - 1
|
||||||
|
? viewportsKeys[0]
|
||||||
|
: viewportsKeys[currentViewportIndex + 1];
|
||||||
|
};
|
||||||
|
const getPreviousViewport = (current: string): string => {
|
||||||
|
const currentViewportIndex = getCurrentViewportIndex(current);
|
||||||
|
return currentViewportIndex < 1
|
||||||
|
? viewportsKeys[viewportsKeys.length - 1]
|
||||||
|
: viewportsKeys[currentViewportIndex - 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerShortcuts = async (api: API, setState: any) => {
|
||||||
|
await api.setAddonShortcut(ADDON_ID, {
|
||||||
|
label: 'Previous viewport',
|
||||||
|
defaultShortcut: ['shift', 'V'],
|
||||||
|
actionName: 'previous',
|
||||||
|
action: () => {
|
||||||
|
const { selected, isRotated } = api.getAddonState(ADDON_ID);
|
||||||
|
setState({
|
||||||
|
selected: getPreviousViewport(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(selected),
|
||||||
|
isRotated,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await api.setAddonShortcut(ADDON_ID, {
|
||||||
|
label: 'Reset viewport',
|
||||||
|
defaultShortcut: ['control', 'V'],
|
||||||
|
actionName: 'reset',
|
||||||
|
action: () => {
|
||||||
|
const { isRotated } = api.getAddonState(ADDON_ID);
|
||||||
|
setState({
|
||||||
|
selected: 'reset',
|
||||||
|
isRotated,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
@ -128,3 +128,11 @@ You can change your story through [parameters](../writing-stories/parameters.md)
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- prettier-ignore-end -->
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
### Keyboard shortcuts
|
||||||
|
|
||||||
|
* Previous viewport: `shift + v`
|
||||||
|
* Next viewport: `v`
|
||||||
|
* Reset viewport: `control + v`
|
||||||
|
|
||||||
|
These shortcuts can be edited in Storybook's Keyboard shortcuts page.
|
@ -20,8 +20,13 @@ export interface SubState {
|
|||||||
|
|
||||||
export interface SubAPI {
|
export interface SubAPI {
|
||||||
getShortcutKeys(): Shortcuts;
|
getShortcutKeys(): Shortcuts;
|
||||||
|
getDefaultShortcuts(): Shortcuts | AddonShortcutDefaults;
|
||||||
|
getAddonsShortcuts(): AddonShortcuts;
|
||||||
|
getAddonsShortcutLabels(): AddonShortcutLabels;
|
||||||
|
getAddonsShortcutDefaults(): AddonShortcutDefaults;
|
||||||
setShortcuts(shortcuts: Shortcuts): Promise<Shortcuts>;
|
setShortcuts(shortcuts: Shortcuts): Promise<Shortcuts>;
|
||||||
setShortcut(action: Action, value: KeyCollection): Promise<KeyCollection>;
|
setShortcut(action: Action, value: KeyCollection): Promise<KeyCollection>;
|
||||||
|
setAddonShortcut(addon: string, shortcut: AddonShortcut): Promise<AddonShortcut>;
|
||||||
restoreAllDefaultShortcuts(): Promise<Shortcuts>;
|
restoreAllDefaultShortcuts(): Promise<Shortcuts>;
|
||||||
restoreDefaultShortcut(action: Action): Promise<KeyCollection>;
|
restoreDefaultShortcut(action: Action): Promise<KeyCollection>;
|
||||||
handleKeydownEvent(event: Event): void;
|
handleKeydownEvent(event: Event): void;
|
||||||
@ -52,6 +57,17 @@ export interface Shortcuts {
|
|||||||
|
|
||||||
export type Action = keyof 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({
|
export const defaultShortcuts: Shortcuts = Object.freeze({
|
||||||
fullScreen: ['F'],
|
fullScreen: ['F'],
|
||||||
togglePanel: ['A'],
|
togglePanel: ['A'],
|
||||||
@ -73,6 +89,7 @@ export const defaultShortcuts: Shortcuts = Object.freeze({
|
|||||||
expandAll: [controlOrMetaKey(), 'shift', 'ArrowDown'],
|
expandAll: [controlOrMetaKey(), 'shift', 'ArrowDown'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const addonsShortcuts: AddonShortcuts = {};
|
||||||
export interface Event extends KeyboardEvent {
|
export interface Event extends KeyboardEvent {
|
||||||
target: {
|
target: {
|
||||||
tagName: string;
|
tagName: string;
|
||||||
@ -96,20 +113,54 @@ export const init: ModuleFn = ({ store, fullAPI }) => {
|
|||||||
getShortcutKeys(): Shortcuts {
|
getShortcutKeys(): Shortcuts {
|
||||||
return store.getState().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) {
|
async setShortcuts(shortcuts: Shortcuts) {
|
||||||
await store.setState({ shortcuts }, { persistence: 'permanent' });
|
await store.setState({ shortcuts }, { persistence: 'permanent' });
|
||||||
return shortcuts;
|
return shortcuts;
|
||||||
},
|
},
|
||||||
async restoreAllDefaultShortcuts() {
|
async restoreAllDefaultShortcuts() {
|
||||||
return api.setShortcuts(defaultShortcuts);
|
return api.setShortcuts(api.getDefaultShortcuts() as Shortcuts);
|
||||||
},
|
},
|
||||||
async setShortcut(action, value) {
|
async setShortcut(action, value) {
|
||||||
const shortcuts = api.getShortcutKeys();
|
const shortcuts = api.getShortcutKeys();
|
||||||
await api.setShortcuts({ ...shortcuts, [action]: value });
|
await api.setShortcuts({ ...shortcuts, [action]: value });
|
||||||
return 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) {
|
async restoreDefaultShortcut(action) {
|
||||||
const defaultShortcut = defaultShortcuts[action];
|
const defaultShortcut = api.getDefaultShortcuts()[action];
|
||||||
return api.setShortcut(action, defaultShortcut);
|
return api.setShortcut(action, defaultShortcut);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -277,6 +328,7 @@ export const init: ModuleFn = ({ store, fullAPI }) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
addonsShortcuts[feature].action();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -10,7 +10,134 @@ function createMockStore() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mockAddonShortcut = {
|
||||||
|
addon: 'my-addon',
|
||||||
|
shortcut: {
|
||||||
|
label: 'Do something',
|
||||||
|
defaultShortcut: ['O'],
|
||||||
|
actionName: 'doSomething',
|
||||||
|
action: () => {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockAddonSecondShortcut = {
|
||||||
|
addon: 'my-addon',
|
||||||
|
shortcut: {
|
||||||
|
label: 'Do something else',
|
||||||
|
defaultShortcut: ['P'],
|
||||||
|
actionName: 'doSomethingElse',
|
||||||
|
action: () => {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockSecondAddonShortcut = {
|
||||||
|
addon: 'my-other-addon',
|
||||||
|
shortcut: {
|
||||||
|
label: 'Create issue',
|
||||||
|
defaultShortcut: ['N'],
|
||||||
|
actionName: 'createIssue',
|
||||||
|
action: () => {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
describe('shortcuts api', () => {
|
describe('shortcuts api', () => {
|
||||||
|
it('gets defaults', () => {
|
||||||
|
const store = createMockStore();
|
||||||
|
|
||||||
|
const { api, state } = initShortcuts({ store });
|
||||||
|
store.setState(state);
|
||||||
|
|
||||||
|
expect(api.getDefaultShortcuts()).toHaveProperty('fullScreen', ['F']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets defaults including addon ones', async () => {
|
||||||
|
const store = createMockStore();
|
||||||
|
|
||||||
|
const { api, state } = initShortcuts({ store });
|
||||||
|
store.setState(state);
|
||||||
|
|
||||||
|
await api.setAddonShortcut(mockAddonShortcut.addon, mockAddonShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockAddonSecondShortcut.addon, mockAddonSecondShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockSecondAddonShortcut.addon, mockSecondAddonShortcut.shortcut);
|
||||||
|
|
||||||
|
expect(api.getDefaultShortcuts()).toHaveProperty('fullScreen', ['F']);
|
||||||
|
expect(api.getDefaultShortcuts()).toHaveProperty(
|
||||||
|
`${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`,
|
||||||
|
mockAddonShortcut.shortcut.defaultShortcut
|
||||||
|
);
|
||||||
|
expect(api.getDefaultShortcuts()).toHaveProperty(
|
||||||
|
`${mockAddonSecondShortcut.addon}-${mockAddonSecondShortcut.shortcut.actionName}`,
|
||||||
|
mockAddonSecondShortcut.shortcut.defaultShortcut
|
||||||
|
);
|
||||||
|
expect(api.getDefaultShortcuts()).toHaveProperty(
|
||||||
|
`${mockSecondAddonShortcut.addon}-${mockSecondAddonShortcut.shortcut.actionName}`,
|
||||||
|
mockSecondAddonShortcut.shortcut.defaultShortcut
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets addons shortcuts', async () => {
|
||||||
|
const store = createMockStore();
|
||||||
|
|
||||||
|
const { api, state } = initShortcuts({ store });
|
||||||
|
store.setState(state);
|
||||||
|
|
||||||
|
await api.setAddonShortcut(mockAddonShortcut.addon, mockAddonShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockAddonSecondShortcut.addon, mockAddonSecondShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockSecondAddonShortcut.addon, mockSecondAddonShortcut.shortcut);
|
||||||
|
|
||||||
|
expect(api.getAddonsShortcuts()).toStrictEqual({
|
||||||
|
[`${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`]: mockAddonShortcut.shortcut,
|
||||||
|
[`${mockAddonSecondShortcut.addon}-${mockAddonSecondShortcut.shortcut.actionName}`]: mockAddonSecondShortcut.shortcut,
|
||||||
|
[`${mockSecondAddonShortcut.addon}-${mockSecondAddonShortcut.shortcut.actionName}`]: mockSecondAddonShortcut.shortcut,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets addons shortcut labels', async () => {
|
||||||
|
const store = createMockStore();
|
||||||
|
|
||||||
|
const { api, state } = initShortcuts({ store });
|
||||||
|
store.setState(state);
|
||||||
|
|
||||||
|
await api.setAddonShortcut(mockAddonShortcut.addon, mockAddonShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockAddonSecondShortcut.addon, mockAddonSecondShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockSecondAddonShortcut.addon, mockSecondAddonShortcut.shortcut);
|
||||||
|
|
||||||
|
expect(api.getAddonsShortcutLabels()).toStrictEqual({
|
||||||
|
[`${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`]: mockAddonShortcut
|
||||||
|
.shortcut.label,
|
||||||
|
[`${mockAddonSecondShortcut.addon}-${mockAddonSecondShortcut.shortcut.actionName}`]: mockAddonSecondShortcut
|
||||||
|
.shortcut.label,
|
||||||
|
[`${mockSecondAddonShortcut.addon}-${mockSecondAddonShortcut.shortcut.actionName}`]: mockSecondAddonShortcut
|
||||||
|
.shortcut.label,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets addons shortcut defaults', async () => {
|
||||||
|
const store = createMockStore();
|
||||||
|
|
||||||
|
const { api, state } = initShortcuts({ store });
|
||||||
|
store.setState(state);
|
||||||
|
|
||||||
|
await api.setAddonShortcut(mockAddonShortcut.addon, mockAddonShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockAddonSecondShortcut.addon, mockAddonSecondShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockSecondAddonShortcut.addon, mockSecondAddonShortcut.shortcut);
|
||||||
|
|
||||||
|
expect(api.getAddonsShortcutDefaults()).toStrictEqual({
|
||||||
|
[`${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`]: mockAddonShortcut
|
||||||
|
.shortcut.defaultShortcut,
|
||||||
|
[`${mockAddonSecondShortcut.addon}-${mockAddonSecondShortcut.shortcut.actionName}`]: mockAddonSecondShortcut
|
||||||
|
.shortcut.defaultShortcut,
|
||||||
|
[`${mockSecondAddonShortcut.addon}-${mockSecondAddonShortcut.shortcut.actionName}`]: mockSecondAddonShortcut
|
||||||
|
.shortcut.defaultShortcut,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('sets defaults', () => {
|
it('sets defaults', () => {
|
||||||
const store = createMockStore();
|
const store = createMockStore();
|
||||||
|
|
||||||
@ -20,6 +147,31 @@ describe('shortcuts api', () => {
|
|||||||
expect(api.getShortcutKeys().fullScreen).toEqual(['F']);
|
expect(api.getShortcutKeys().fullScreen).toEqual(['F']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets addon shortcut with default value', async () => {
|
||||||
|
const store = createMockStore();
|
||||||
|
|
||||||
|
const { api, state } = initShortcuts({ store });
|
||||||
|
store.setState(state);
|
||||||
|
|
||||||
|
await api.setAddonShortcut(mockAddonShortcut.addon, mockAddonShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockAddonSecondShortcut.addon, mockAddonSecondShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockSecondAddonShortcut.addon, mockSecondAddonShortcut.shortcut);
|
||||||
|
|
||||||
|
expect(api.getDefaultShortcuts()).toHaveProperty('fullScreen', ['F']);
|
||||||
|
expect(api.getDefaultShortcuts()).toHaveProperty(
|
||||||
|
`${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`,
|
||||||
|
mockAddonShortcut.shortcut.defaultShortcut
|
||||||
|
);
|
||||||
|
expect(api.getDefaultShortcuts()).toHaveProperty(
|
||||||
|
`${mockAddonSecondShortcut.addon}-${mockAddonSecondShortcut.shortcut.actionName}`,
|
||||||
|
mockAddonSecondShortcut.shortcut.defaultShortcut
|
||||||
|
);
|
||||||
|
expect(api.getDefaultShortcuts()).toHaveProperty(
|
||||||
|
`${mockSecondAddonShortcut.addon}-${mockSecondAddonShortcut.shortcut.actionName}`,
|
||||||
|
mockSecondAddonShortcut.shortcut.defaultShortcut
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('sets defaults, augmenting anything that was persisted', () => {
|
it('sets defaults, augmenting anything that was persisted', () => {
|
||||||
const store = createMockStore();
|
const store = createMockStore();
|
||||||
store.setState({ shortcuts: { fullScreen: ['Z'] } });
|
store.setState({ shortcuts: { fullScreen: ['Z'] } });
|
||||||
@ -51,17 +203,38 @@ describe('shortcuts api', () => {
|
|||||||
expect(api.getShortcutKeys().fullScreen).toEqual(['X']);
|
expect(api.getShortcutKeys().fullScreen).toEqual(['X']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sets new values for addon shortcuts', async () => {
|
||||||
|
const store = createMockStore();
|
||||||
|
|
||||||
|
const { api, state } = initShortcuts({ store });
|
||||||
|
store.setState(state);
|
||||||
|
|
||||||
|
const { addon, shortcut } = mockAddonShortcut;
|
||||||
|
await api.setAddonShortcut(addon, shortcut);
|
||||||
|
|
||||||
|
await api.setShortcut(`${addon}-${shortcut.actionName}`, ['I']);
|
||||||
|
expect(api.getShortcutKeys()[`${addon}-${shortcut.actionName}`]).toEqual(['I']);
|
||||||
|
});
|
||||||
|
|
||||||
it('restores all defaults', async () => {
|
it('restores all defaults', async () => {
|
||||||
const store = createMockStore();
|
const store = createMockStore();
|
||||||
|
|
||||||
const { api, state } = initShortcuts({ store });
|
const { api, state } = initShortcuts({ store });
|
||||||
store.setState(state);
|
store.setState(state);
|
||||||
|
|
||||||
|
const { addon, shortcut } = mockAddonShortcut;
|
||||||
|
await api.setAddonShortcut(addon, shortcut);
|
||||||
|
|
||||||
await api.setShortcut('fullScreen', ['X']);
|
await api.setShortcut('fullScreen', ['X']);
|
||||||
await api.setShortcut('togglePanel', ['B']);
|
await api.setShortcut('togglePanel', ['B']);
|
||||||
|
await api.setShortcut(`${addon}-${shortcut.actionName}`, ['I']);
|
||||||
|
|
||||||
await api.restoreAllDefaultShortcuts();
|
await api.restoreAllDefaultShortcuts();
|
||||||
expect(api.getShortcutKeys().fullScreen).toEqual(['F']);
|
expect(api.getShortcutKeys().fullScreen).toEqual(['F']);
|
||||||
expect(api.getShortcutKeys().togglePanel).toEqual(['A']);
|
expect(api.getShortcutKeys().togglePanel).toEqual(['A']);
|
||||||
|
expect(api.getShortcutKeys()[`${addon}-${shortcut.actionName}`]).toEqual(
|
||||||
|
shortcut.defaultShortcut
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('restores single default', async () => {
|
it('restores single default', async () => {
|
||||||
@ -70,10 +243,42 @@ describe('shortcuts api', () => {
|
|||||||
const { api, state } = initShortcuts({ store });
|
const { api, state } = initShortcuts({ store });
|
||||||
store.setState(state);
|
store.setState(state);
|
||||||
|
|
||||||
|
await api.setAddonShortcut(mockAddonShortcut.addon, mockAddonShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockAddonSecondShortcut.addon, mockAddonSecondShortcut.shortcut);
|
||||||
|
await api.setAddonShortcut(mockSecondAddonShortcut.addon, mockSecondAddonShortcut.shortcut);
|
||||||
|
|
||||||
await api.setShortcut('fullScreen', ['X']);
|
await api.setShortcut('fullScreen', ['X']);
|
||||||
await api.setShortcut('togglePanel', ['B']);
|
await api.setShortcut('togglePanel', ['B']);
|
||||||
|
await api.setShortcut(`${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`, [
|
||||||
|
'I',
|
||||||
|
]);
|
||||||
|
await api.setShortcut(
|
||||||
|
`${mockAddonSecondShortcut.addon}-${mockAddonSecondShortcut.shortcut.actionName}`,
|
||||||
|
['H']
|
||||||
|
);
|
||||||
|
await api.setShortcut(
|
||||||
|
`${mockSecondAddonShortcut.addon}-${mockSecondAddonShortcut.shortcut.actionName}`,
|
||||||
|
['G']
|
||||||
|
);
|
||||||
await api.restoreDefaultShortcut('fullScreen');
|
await api.restoreDefaultShortcut('fullScreen');
|
||||||
|
await api.restoreDefaultShortcut(
|
||||||
|
`${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`
|
||||||
|
);
|
||||||
|
|
||||||
expect(api.getShortcutKeys().fullScreen).toEqual(['F']);
|
expect(api.getShortcutKeys().fullScreen).toEqual(['F']);
|
||||||
expect(api.getShortcutKeys().togglePanel).toEqual(['B']);
|
expect(api.getShortcutKeys().togglePanel).toEqual(['B']);
|
||||||
|
expect(
|
||||||
|
api.getShortcutKeys()[`${mockAddonShortcut.addon}-${mockAddonShortcut.shortcut.actionName}`]
|
||||||
|
).toEqual(mockAddonShortcut.shortcut.defaultShortcut);
|
||||||
|
expect(
|
||||||
|
api.getShortcutKeys()[
|
||||||
|
`${mockAddonSecondShortcut.addon}-${mockAddonSecondShortcut.shortcut.actionName}`
|
||||||
|
]
|
||||||
|
).toEqual(['H']);
|
||||||
|
expect(
|
||||||
|
api.getShortcutKeys()[
|
||||||
|
`${mockSecondAddonShortcut.addon}-${mockSecondAddonShortcut.shortcut.actionName}`
|
||||||
|
]
|
||||||
|
).toEqual(['G']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -205,6 +205,20 @@ export const useMenu = (
|
|||||||
[api, enableShortcuts, shortcutKeys]
|
[api, enableShortcuts, shortcutKeys]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getAddonsShortcuts = (): any[] => {
|
||||||
|
const addonsShortcuts = api.getAddonsShortcuts();
|
||||||
|
const keys = shortcutKeys as any;
|
||||||
|
return Object.entries(addonsShortcuts)
|
||||||
|
.filter(([actionName, { showInMenu }]) => showInMenu)
|
||||||
|
.map(([actionName, { label, action }]) => ({
|
||||||
|
id: actionName,
|
||||||
|
title: label,
|
||||||
|
onClick: () => action(),
|
||||||
|
right: enableShortcuts ? <Shortcut keys={keys[actionName]} /> : null,
|
||||||
|
left: <MenuItemIcon />,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => [
|
() => [
|
||||||
about,
|
about,
|
||||||
@ -221,6 +235,7 @@ export const useMenu = (
|
|||||||
prev,
|
prev,
|
||||||
next,
|
next,
|
||||||
collapse,
|
collapse,
|
||||||
|
...getAddonsShortcuts(),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
about,
|
about,
|
||||||
|
@ -143,10 +143,12 @@ export interface ShortcutsScreenState {
|
|||||||
activeFeature: Feature;
|
activeFeature: Feature;
|
||||||
successField: Feature;
|
successField: Feature;
|
||||||
shortcutKeys: Record<Feature, any>;
|
shortcutKeys: Record<Feature, any>;
|
||||||
|
addonsShortcutLabels?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ShortcutsScreenProps {
|
export interface ShortcutsScreenProps {
|
||||||
shortcutKeys: Record<Feature, any>;
|
shortcutKeys: Record<Feature, any>;
|
||||||
|
addonsShortcutLabels?: Record<string, string>;
|
||||||
setShortcut: Function;
|
setShortcut: Function;
|
||||||
restoreDefaultShortcut: Function;
|
restoreDefaultShortcut: Function;
|
||||||
restoreAllDefaultShortcuts: Function;
|
restoreAllDefaultShortcuts: Function;
|
||||||
@ -162,6 +164,7 @@ class ShortcutsScreen extends Component<ShortcutsScreenProps, ShortcutsScreenSta
|
|||||||
// As the user interacts with the page, the state stores the temporary, unsaved shortcuts
|
// As the user interacts with the page, the state stores the temporary, unsaved shortcuts
|
||||||
// This object also includes the error attached to each shortcut
|
// This object also includes the error attached to each shortcut
|
||||||
shortcutKeys: toShortcutState(props.shortcutKeys),
|
shortcutKeys: toShortcutState(props.shortcutKeys),
|
||||||
|
addonsShortcutLabels: props.addonsShortcutLabels,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,10 +264,10 @@ class ShortcutsScreen extends Component<ShortcutsScreenProps, ShortcutsScreenSta
|
|||||||
};
|
};
|
||||||
|
|
||||||
renderKeyInput = () => {
|
renderKeyInput = () => {
|
||||||
const { shortcutKeys } = this.state;
|
const { shortcutKeys, addonsShortcutLabels } = this.state;
|
||||||
const arr = Object.entries(shortcutKeys).map(([feature, { shortcut }]: [Feature, any]) => (
|
const arr = Object.entries(shortcutKeys).map(([feature, { shortcut }]: [Feature, any]) => (
|
||||||
<Row key={feature}>
|
<Row key={feature}>
|
||||||
<Description>{shortcutLabels[feature]}</Description>
|
<Description>{shortcutLabels[feature] || addonsShortcutLabels[feature]}</Description>
|
||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
spellCheck="false"
|
spellCheck="false"
|
||||||
@ -272,6 +275,7 @@ class ShortcutsScreen extends Component<ShortcutsScreenProps, ShortcutsScreenSta
|
|||||||
className="modalInput"
|
className="modalInput"
|
||||||
onBlur={this.onBlur}
|
onBlur={this.onBlur}
|
||||||
onFocus={this.onFocus(feature)}
|
onFocus={this.onFocus(feature)}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
onKeyDown={this.onKeyDown}
|
onKeyDown={this.onKeyDown}
|
||||||
value={shortcut ? shortcutToHumanString(shortcut) : ''}
|
value={shortcut ? shortcutToHumanString(shortcut) : ''}
|
||||||
|
@ -7,10 +7,17 @@ import { ShortcutsScreen } from './shortcuts';
|
|||||||
const ShortcutsPage: FunctionComponent<{}> = () => (
|
const ShortcutsPage: FunctionComponent<{}> = () => (
|
||||||
<Consumer>
|
<Consumer>
|
||||||
{({
|
{({
|
||||||
api: { getShortcutKeys, setShortcut, restoreDefaultShortcut, restoreAllDefaultShortcuts },
|
api: {
|
||||||
|
getShortcutKeys,
|
||||||
|
getAddonsShortcutLabels,
|
||||||
|
setShortcut,
|
||||||
|
restoreDefaultShortcut,
|
||||||
|
restoreAllDefaultShortcuts,
|
||||||
|
},
|
||||||
}) => (
|
}) => (
|
||||||
<ShortcutsScreen
|
<ShortcutsScreen
|
||||||
shortcutKeys={getShortcutKeys()}
|
shortcutKeys={getShortcutKeys()}
|
||||||
|
addonsShortcutLabels={getAddonsShortcutLabels()}
|
||||||
{...{ setShortcut, restoreDefaultShortcut, restoreAllDefaultShortcuts }}
|
{...{ setShortcut, restoreDefaultShortcut, restoreAllDefaultShortcuts }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user