1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Introduce modifier for ctrl or command based on platform in use

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen 2023-04-03 08:10:01 +03:00
parent 0622b8ddaa
commit 926e7e2eb7
No known key found for this signature in database
GPG Key ID: 8C6CFB2FFFE8F68A
4 changed files with 179 additions and 18 deletions

View File

@ -6,6 +6,7 @@ import {
KeyboardShortcut, KeyboardShortcut,
keyboardShortcutInjectionToken, keyboardShortcutInjectionToken,
} from "./keyboard-shortcut-injection-token"; } from "./keyboard-shortcut-injection-token";
import platformInjectable from "./platform.injectable";
export type InvokeShortcut = (event: KeyboardEvent) => void; export type InvokeShortcut = (event: KeyboardEvent) => void;
@ -28,36 +29,58 @@ const toShortcutsWithMatchingScope = (shortcut: KeyboardShortcut) => {
const toBindingWithDefaults = (binding: Binding) => const toBindingWithDefaults = (binding: Binding) =>
isString(binding) isString(binding)
? { code: binding, shift: false, ctrl: false, altOrOption: false, meta: false } ? {
: { ctrl: false, shift: false, altOrOption: false, meta: false, ...binding }; code: binding,
shift: false,
ctrl: false,
altOrOption: false,
meta: false,
ctrlOrCommand: false,
}
: {
ctrl: false,
shift: false,
altOrOption: false,
meta: false,
ctrlOrCommand: false,
...binding,
};
const toShortcutsWithMatchingBinding = (event: KeyboardEvent) => (shortcut: KeyboardShortcut) => { const toShortcutsWithMatchingBinding =
const binding = toBindingWithDefaults(shortcut.binding); (event: KeyboardEvent, platform: string) => (shortcut: KeyboardShortcut) => {
const binding = toBindingWithDefaults(shortcut.binding);
const shiftModifierMatches = binding.shift === event.shiftKey; const shiftModifierMatches = binding.shift === event.shiftKey;
const ctrlModifierMatches = binding.ctrl === event.ctrlKey; const altModifierMatches = binding.altOrOption === event.altKey;
const altModifierMatches = binding.altOrOption === event.altKey;
const metaModifierMatches = binding.meta === event.metaKey;
return ( const isMac = platform === "darwin";
event.code === binding.code &&
shiftModifierMatches && const ctrlModifierMatches =
ctrlModifierMatches && binding.ctrl === event.ctrlKey || (!isMac && binding.ctrlOrCommand === event.ctrlKey);
altModifierMatches &&
metaModifierMatches const metaModifierMatches =
); binding.meta === event.metaKey || (isMac && binding.ctrlOrCommand === event.metaKey);
};
return (
event.code === binding.code &&
shiftModifierMatches &&
ctrlModifierMatches &&
altModifierMatches &&
metaModifierMatches
);
};
const invokeShortcutInjectable = getInjectable({ const invokeShortcutInjectable = getInjectable({
id: "invoke-shortcut", id: "invoke-shortcut",
instantiate: (di): InvokeShortcut => { instantiate: (di): InvokeShortcut => {
const getShortcuts = () => di.injectMany(keyboardShortcutInjectionToken); const getShortcuts = () => di.injectMany(keyboardShortcutInjectionToken);
const platform = di.inject(platformInjectable);
return (event) => { return (event) => {
const shortcutsToInvoke = pipeline( const shortcutsToInvoke = pipeline(
getShortcuts(), getShortcuts(),
filter(toShortcutsWithMatchingBinding(event)), filter(toShortcutsWithMatchingBinding(event, platform)),
filter(toShortcutsWithMatchingScope), filter(toShortcutsWithMatchingScope),
); );

View File

@ -2,7 +2,14 @@ import { getInjectionToken } from "@ogre-tools/injectable";
export type Binding = export type Binding =
| string | string
| { code: string; shift?: boolean; ctrl?: boolean; altOrOption?: boolean; meta?: boolean }; | {
code: string;
shift?: boolean;
ctrl?: boolean;
altOrOption?: boolean;
meta?: boolean;
ctrlOrCommand?: boolean;
};
export type KeyboardShortcut = { export type KeyboardShortcut = {
binding: Binding; binding: Binding;

View File

@ -14,6 +14,7 @@ import { Discover, discoverFor } from "@k8slens/react-testing-library-discovery"
import { startApplicationInjectionToken } from "@k8slens/application"; import { startApplicationInjectionToken } from "@k8slens/application";
import { renderInjectionToken } from "@k8slens/react-application"; import { renderInjectionToken } from "@k8slens/react-application";
import { reactApplicationChildrenInjectionToken } from "@k8slens/react-application"; import { reactApplicationChildrenInjectionToken } from "@k8slens/react-application";
import platformInjectable from "./platform.injectable";
describe("keyboard-shortcuts", () => { describe("keyboard-shortcuts", () => {
let di: DiContainer; let di: DiContainer;
@ -203,6 +204,8 @@ describe("keyboard-shortcuts", () => {
].forEach(({ binding, keyboard, scenario, shouldCallCallback }) => { ].forEach(({ binding, keyboard, scenario, shouldCallCallback }) => {
// eslint-disable-next-line jest/valid-title // eslint-disable-next-line jest/valid-title
it(scenario, () => { it(scenario, () => {
const invokeMock = jest.fn();
const shortcutInjectable = getInjectable({ const shortcutInjectable = getInjectable({
id: "shortcut", id: "shortcut",
@ -230,4 +233,121 @@ describe("keyboard-shortcuts", () => {
}); });
}); });
}); });
describe("given in mac and keyboard shortcut with modifier for ctrl or command", () => {
beforeEach(async () => {
di.override(platformInjectable, () => "darwin");
invokeMock = jest.fn();
const shortcutInjectable = getInjectable({
id: "shortcut",
instantiate: () => ({
binding: { code: "KeyK", ctrlOrCommand: true },
invoke: invokeMock,
}),
injectionToken: keyboardShortcutInjectionToken,
});
runInAction(() => {
di.register(shortcutInjectable);
});
const startApplication = di.inject(startApplicationInjectionToken);
await startApplication();
});
it("when pressing the keyboard shortcut with command, calls the callback", () => {
userEvent.keyboard("{Meta>}[KeyK]");
expect(invokeMock).toHaveBeenCalled();
});
it("when pressing the keyboard shortcut with ctrl, does not call the callback", () => {
userEvent.keyboard("{Control>}[KeyK]");
expect(invokeMock).not.toHaveBeenCalled();
});
});
describe("given in windows and keyboard shortcut with modifier for ctrl or command", () => {
beforeEach(async () => {
di.override(platformInjectable, () => "win32");
invokeMock = jest.fn();
const shortcutInjectable = getInjectable({
id: "shortcut",
instantiate: () => ({
binding: { code: "KeyK", ctrlOrCommand: true },
invoke: invokeMock,
}),
injectionToken: keyboardShortcutInjectionToken,
});
runInAction(() => {
di.register(shortcutInjectable);
});
const startApplication = di.inject(startApplicationInjectionToken);
await startApplication();
});
it("when pressing the keyboard shortcut with windows, does not call the callback", () => {
userEvent.keyboard("{Meta>}[KeyK]");
expect(invokeMock).not.toHaveBeenCalled();
});
it("when pressing the keyboard shortcut with ctrl, calls the callback", () => {
userEvent.keyboard("{Control>}[KeyK]");
expect(invokeMock).toHaveBeenCalled();
});
});
describe("given in any other platform and keyboard shortcut with modifier for ctrl or command", () => {
beforeEach(async () => {
di.override(platformInjectable, () => "some-other-platform");
invokeMock = jest.fn();
const shortcutInjectable = getInjectable({
id: "shortcut",
instantiate: () => ({
binding: { code: "KeyK", ctrlOrCommand: true },
invoke: invokeMock,
}),
injectionToken: keyboardShortcutInjectionToken,
});
runInAction(() => {
di.register(shortcutInjectable);
});
const startApplication = di.inject(startApplicationInjectionToken);
await startApplication();
});
it("when pressing the keyboard shortcut with meta, does not call the callback", () => {
userEvent.keyboard("{Meta>}[KeyK]");
expect(invokeMock).not.toHaveBeenCalled();
});
it("when pressing the keyboard shortcut with ctrl, calls the callback", () => {
userEvent.keyboard("{Control>}[KeyK]");
expect(invokeMock).toHaveBeenCalled();
});
});
}); });

View File

@ -0,0 +1,11 @@
import { getInjectable } from "@ogre-tools/injectable";
export const allPlatforms = ["win32", "darwin", "linux"] as const;
const platformInjectable = getInjectable({
id: "platform",
instantiate: () => process.platform as (typeof allPlatforms)[number],
causesSideEffects: true,
});
export default platformInjectable;