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:
parent
9f19fdceb2
commit
66e4ec1f53
@ -3,6 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import { noop } from "lodash/fp";
|
||||
import { action } from "mobx";
|
||||
import type { ApplicationBuilder } 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: {
|
||||
appMenus: [
|
||||
{
|
||||
id: "some-non-shown-item",
|
||||
parentId: "some-top-menu-item",
|
||||
click: noop,
|
||||
label: "Irrelevant",
|
||||
visible: false,
|
||||
},
|
||||
|
||||
{
|
||||
id: "some-clickable-item",
|
||||
parentId: "some-top-menu-item",
|
||||
@ -50,6 +59,21 @@ describe("application-menu-in-legacy-extension-api", () => {
|
||||
parentId: "some-top-menu-item",
|
||||
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);
|
||||
});
|
||||
|
||||
it("menu related items exist", () => {
|
||||
it("related menu items exist", () => {
|
||||
const menuItemPathsForExtension = builder.applicationMenu.items.filter(
|
||||
(x) =>
|
||||
x.startsWith("root.some-top-menu-item.some-extension-name"),
|
||||
);
|
||||
|
||||
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/application-menu-item/separator(1)",
|
||||
"root.some-top-menu-item.some-extension-name/some-clickable-item",
|
||||
// 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", () => {
|
||||
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();
|
||||
@ -100,8 +128,11 @@ describe("application-menu-in-legacy-extension-api", () => {
|
||||
);
|
||||
|
||||
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/application-menu-item/separator(1)",
|
||||
"root.some-top-menu-item.some-extension-name/some-clickable-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",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@ -2,12 +2,19 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* 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 { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
|
||||
import type { LensExtension } from "../../../extensions/lens-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 type { MenuRegistration } from "./menu-registration";
|
||||
|
||||
const applicationMenuItemRegistratorInjectable = getInjectable({
|
||||
id: "application-menu-item-registrator",
|
||||
@ -15,44 +22,122 @@ const applicationMenuItemRegistratorInjectable = getInjectable({
|
||||
instantiate: () => (ext: LensExtension) => {
|
||||
const extension = ext as LensMainExtension;
|
||||
|
||||
return extension.appMenus.map((registration, index) => {
|
||||
const registrationId = registration.id || index;
|
||||
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,
|
||||
});
|
||||
});
|
||||
return extension.appMenus.flatMap(
|
||||
toRecursedInjectables([extension.sanitizedExtensionId]),
|
||||
);
|
||||
},
|
||||
|
||||
injectionToken: extensionRegistratorInjectionToken,
|
||||
});
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user