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

Make registrator for application menu items support all known scenarios

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
Iku-turso 2022-10-12 14:27:01 +03:00 committed by Janne Savolainen
parent 9f19fdceb2
commit 66e4ec1f53
No known key found for this signature in database
GPG Key ID: 8C6CFB2FFFE8F68A
2 changed files with 158 additions and 42 deletions

View File

@ -3,6 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { noop } from "lodash/fp";
import { action } from "mobx"; import { action } from "mobx";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
@ -40,6 +41,14 @@ describe("application-menu-in-legacy-extension-api", () => {
mainOptions: { mainOptions: {
appMenus: [ appMenus: [
{
id: "some-non-shown-item",
parentId: "some-top-menu-item",
click: noop,
label: "Irrelevant",
visible: false,
},
{ {
id: "some-clickable-item", id: "some-clickable-item",
parentId: "some-top-menu-item", parentId: "some-top-menu-item",
@ -50,6 +59,21 @@ describe("application-menu-in-legacy-extension-api", () => {
parentId: "some-top-menu-item", parentId: "some-top-menu-item",
type: "separator", type: "separator",
}, },
{
id: "some-os-action-menu-item-id",
parentId: "some-top-menu-item",
role: "help",
},
{
id: "some-submenu-with-explicit-children",
parentId: "some-top-menu-item",
submenu: [
{ id: "some-explicit-child", label: "Some explicit child", click: noop },
],
},
], ],
}, },
}; };
@ -57,21 +81,25 @@ describe("application-menu-in-legacy-extension-api", () => {
builder.extensions.enable(testExtensionOptions); builder.extensions.enable(testExtensionOptions);
}); });
it("menu related items exist", () => { it("related menu items exist", () => {
const menuItemPathsForExtension = builder.applicationMenu.items.filter( const menuItemPathsForExtension = builder.applicationMenu.items.filter(
(x) => (x) =>
x.startsWith("root.some-top-menu-item.some-extension-name"), x.startsWith("root.some-top-menu-item.some-extension-name"),
); );
expect(menuItemPathsForExtension).toEqual([ expect(menuItemPathsForExtension).toEqual([
"root.some-top-menu-item.some-extension-name/application-menu-item/clickable-menu-item(some-clickable-item)", "root.some-top-menu-item.some-extension-name/some-clickable-item",
"root.some-top-menu-item.some-extension-name/application-menu-item/separator(1)", // Note: anonymous index "1" is used by the non-visible menu item.
"root.some-top-menu-item.some-extension-name/2-separator",
"root.some-top-menu-item.some-extension-name/some-os-action-menu-item-id",
"root.some-top-menu-item.some-extension-name/some-submenu-with-explicit-children",
"root.some-top-menu-item.some-extension-name/some-submenu-with-explicit-children.some-extension-name/some-submenu-with-explicit-children/some-explicit-child",
]); ]);
}); });
it("when the extension-based clickable menu item is clicked, does so", () => { it("when the extension-based clickable menu item is clicked, does so", () => {
builder.applicationMenu.click( builder.applicationMenu.click(
"root.some-top-menu-item.some-extension-name/application-menu-item/clickable-menu-item(some-clickable-item)", "root.some-top-menu-item.some-extension-name/some-clickable-item",
); );
expect(onClickMock).toHaveBeenCalled(); expect(onClickMock).toHaveBeenCalled();
@ -100,8 +128,11 @@ describe("application-menu-in-legacy-extension-api", () => {
); );
expect(menuItemPathsForExtension).toEqual([ expect(menuItemPathsForExtension).toEqual([
"root.some-top-menu-item.some-extension-name/application-menu-item/clickable-menu-item(some-clickable-item)", "root.some-top-menu-item.some-extension-name/some-clickable-item",
"root.some-top-menu-item.some-extension-name/application-menu-item/separator(1)", "root.some-top-menu-item.some-extension-name/2-separator",
"root.some-top-menu-item.some-extension-name/some-os-action-menu-item-id",
"root.some-top-menu-item.some-extension-name/some-submenu-with-explicit-children",
"root.some-top-menu-item.some-extension-name/some-submenu-with-explicit-children.some-extension-name/some-submenu-with-explicit-children/some-explicit-child",
]); ]);
}); });
}); });

View File

@ -2,12 +2,19 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import type { Injectable } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token"; import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
import type { LensExtension } from "../../../extensions/lens-extension"; import type { LensExtension } from "../../../extensions/lens-extension";
import type { LensMainExtension } from "../../../extensions/lens-main-extension"; import type { LensMainExtension } from "../../../extensions/lens-main-extension";
import type { ClickableMenuItem, Separator } from "./menu-items/application-menu-item-injection-token"; import type {
ApplicationMenuItemTypes,
ClickableMenuItem,
OsActionMenuItem,
Separator,
} from "./menu-items/application-menu-item-injection-token";
import applicationMenuItemInjectionToken from "./menu-items/application-menu-item-injection-token"; import applicationMenuItemInjectionToken from "./menu-items/application-menu-item-injection-token";
import type { MenuRegistration } from "./menu-registration";
const applicationMenuItemRegistratorInjectable = getInjectable({ const applicationMenuItemRegistratorInjectable = getInjectable({
id: "application-menu-item-registrator", id: "application-menu-item-registrator",
@ -15,44 +22,122 @@ const applicationMenuItemRegistratorInjectable = getInjectable({
instantiate: () => (ext: LensExtension) => { instantiate: () => (ext: LensExtension) => {
const extension = ext as LensMainExtension; const extension = ext as LensMainExtension;
return extension.appMenus.map((registration, index) => { return extension.appMenus.flatMap(
const registrationId = registration.id || index; toRecursedInjectables([extension.sanitizedExtensionId]),
const applicationMenuId = `${extension.sanitizedExtensionId}/application-menu-item`; );
return getInjectable({
id: `${applicationMenuId}/${registrationId}`,
instantiate: () => {
const orderNumber = 1000 + index * 10;
if (registration.type === "separator") {
return {
kind: "separator" as const,
id: `${applicationMenuId}/separator(${registrationId})`,
parentId: registration.parentId,
orderNumber,
} as Separator;
}
return {
kind: "clickable-menu-item" as const,
id: `${applicationMenuId}/clickable-menu-item(${registrationId})`,
parentId: registration.parentId,
// Todo: hide electron evens from this abstraction.
onClick: registration.click,
label: registration.label,
isShown: registration.visible ?? true,
orderNumber,
...(registration.accelerator ? { keyboardShortcut: registration.accelerator as string } : {}),
} as ClickableMenuItem;
},
injectionToken: applicationMenuItemInjectionToken,
});
});
}, },
injectionToken: extensionRegistratorInjectionToken, injectionToken: extensionRegistratorInjectionToken,
}); });
export default applicationMenuItemRegistratorInjectable; export default applicationMenuItemRegistratorInjectable;
const toRecursedInjectables =
(previousIdPath: string[]) =>
(
registration: MenuRegistration,
index: number,
// Todo: new version of injectable would require less type parameters with defaults.
): Injectable<ApplicationMenuItemTypes, ApplicationMenuItemTypes, void>[] => {
const previousIdPathString = previousIdPath.join("/");
const registrationId = registration.id || index.toString();
const currentIdPath = [...previousIdPath, registrationId];
const currentIdPathString = currentIdPath.join("/");
const parentId = registration.parentId || previousIdPathString;
const menuItem = getApplicationMenuItem({
registration,
parentId,
currentIdPathString,
index,
});
if(!menuItem) {
return [];
}
return [
getInjectable({
id: `${currentIdPathString}/application-menu-item`,
instantiate: () => menuItem,
injectionToken: applicationMenuItemInjectionToken,
}),
...((registration.submenu as MenuRegistration[])
? (registration.submenu as MenuRegistration[]).flatMap(
toRecursedInjectables(currentIdPath),
)
: []),
];
};
const getApplicationMenuItem = ({
registration,
index,
currentIdPathString,
parentId,
}: {
registration: MenuRegistration;
index: number;
currentIdPathString: string;
parentId: string;
}): ApplicationMenuItemTypes | undefined => {
const orderNumber = 1000 + index * 10;
if (registration.type === "separator") {
return {
kind: "separator" as const,
id: `${currentIdPathString}-separator`,
parentId,
orderNumber,
} as Separator;
}
if (registration.submenu) {
return {
kind: "sub-menu" as const,
id: currentIdPathString,
parentId,
isShown: registration.visible ?? true,
orderNumber,
label: registration.label || "",
};
}
if (registration.click) {
return {
kind: "clickable-menu-item" as const,
id: currentIdPathString,
parentId,
// Todo: hide electron events from this abstraction.
onClick: registration.click,
label: registration.label,
isShown: registration.visible ?? true,
orderNumber,
...(registration.accelerator
? { keyboardShortcut: registration.accelerator as string }
: {}),
} as ClickableMenuItem;
}
if (registration.role) {
return {
kind: "os-action-menu-item" as const,
id: currentIdPathString,
parentId,
label: registration.label,
isShown: registration.visible ?? true,
orderNumber,
actionName: registration.role,
...(registration.accelerator
? { keyboardShortcut: registration.accelerator as string }
: {}),
} as OsActionMenuItem;
}
return undefined;
};