Skip to content

Commit

Permalink
Introduce modifier for ctrl or command based on platform in use
Browse files Browse the repository at this point in the history
Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
  • Loading branch information
jansav committed Apr 3, 2023
1 parent 0622b8d commit 926e7e2
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
KeyboardShortcut,
keyboardShortcutInjectionToken,
} from "./keyboard-shortcut-injection-token";
import platformInjectable from "./platform.injectable";

export type InvokeShortcut = (event: KeyboardEvent) => void;

Expand All @@ -28,36 +29,58 @@ const toShortcutsWithMatchingScope = (shortcut: KeyboardShortcut) => {

const toBindingWithDefaults = (binding: Binding) =>
isString(binding)
? { code: binding, shift: false, ctrl: false, altOrOption: false, meta: false }
: { ctrl: false, shift: false, altOrOption: false, meta: false, ...binding };

const toShortcutsWithMatchingBinding = (event: KeyboardEvent) => (shortcut: KeyboardShortcut) => {
const binding = toBindingWithDefaults(shortcut.binding);

const shiftModifierMatches = binding.shift === event.shiftKey;
const ctrlModifierMatches = binding.ctrl === event.ctrlKey;
const altModifierMatches = binding.altOrOption === event.altKey;
const metaModifierMatches = binding.meta === event.metaKey;

return (
event.code === binding.code &&
shiftModifierMatches &&
ctrlModifierMatches &&
altModifierMatches &&
metaModifierMatches
);
};
? {
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, platform: string) => (shortcut: KeyboardShortcut) => {
const binding = toBindingWithDefaults(shortcut.binding);

const shiftModifierMatches = binding.shift === event.shiftKey;
const altModifierMatches = binding.altOrOption === event.altKey;

const isMac = platform === "darwin";

const ctrlModifierMatches =
binding.ctrl === event.ctrlKey || (!isMac && binding.ctrlOrCommand === event.ctrlKey);

const metaModifierMatches =
binding.meta === event.metaKey || (isMac && binding.ctrlOrCommand === event.metaKey);

return (
event.code === binding.code &&
shiftModifierMatches &&
ctrlModifierMatches &&
altModifierMatches &&
metaModifierMatches
);
};

const invokeShortcutInjectable = getInjectable({
id: "invoke-shortcut",

instantiate: (di): InvokeShortcut => {
const getShortcuts = () => di.injectMany(keyboardShortcutInjectionToken);
const platform = di.inject(platformInjectable);

return (event) => {
const shortcutsToInvoke = pipeline(
getShortcuts(),
filter(toShortcutsWithMatchingBinding(event)),
filter(toShortcutsWithMatchingBinding(event, platform)),
filter(toShortcutsWithMatchingScope),
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import { getInjectionToken } from "@ogre-tools/injectable";

export type Binding =
| 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 = {
binding: Binding;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Discover, discoverFor } from "@k8slens/react-testing-library-discovery"
import { startApplicationInjectionToken } from "@k8slens/application";
import { renderInjectionToken } from "@k8slens/react-application";
import { reactApplicationChildrenInjectionToken } from "@k8slens/react-application";
import platformInjectable from "./platform.injectable";

describe("keyboard-shortcuts", () => {
let di: DiContainer;
Expand Down Expand Up @@ -203,6 +204,8 @@ describe("keyboard-shortcuts", () => {
].forEach(({ binding, keyboard, scenario, shouldCallCallback }) => {
// eslint-disable-next-line jest/valid-title
it(scenario, () => {
const invokeMock = jest.fn();

const shortcutInjectable = getInjectable({
id: "shortcut",

Expand Down Expand Up @@ -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();
});
});
});
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit 926e7e2

Please sign in to comment.