mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add ability to hide KubeObjectMenu Edit and Remove buttons in extensions (#5107)
This commit is contained in:
1 parent
e532b90b72
commit
dbdde19222
51 files changed
+1245
-765
No files matched your search
+51
-65
@@ -5,15 +5,14 @@ exports[`kube-object-menu given kube object renders 1`] = `
|
||||
<div>
|
||||
<div>
|
||||
<ul
|
||||
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
class="Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
>
|
||||
<li>
|
||||
Some menu item
|
||||
</li>
|
||||
<li
|
||||
class="MenuItem"
|
||||
data-testid="menu-action-remove"
|
||||
data-testid="menu-action-delete"
|
||||
tabindex="0"
|
||||
>
|
||||
<i
|
||||
@@ -45,15 +44,14 @@ exports[`kube-object-menu given kube object when removing kube object renders 1`
|
||||
<div>
|
||||
<div>
|
||||
<ul
|
||||
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
class="Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
>
|
||||
<li>
|
||||
Some menu item
|
||||
</li>
|
||||
<li
|
||||
class="MenuItem"
|
||||
data-testid="menu-action-remove"
|
||||
data-testid="menu-action-delete"
|
||||
tabindex="0"
|
||||
>
|
||||
<i
|
||||
@@ -78,9 +76,8 @@ exports[`kube-object-menu given kube object when removing kube object renders 1`
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Animate opacity-scale Dialog flex center ConfirmDialog modal"
|
||||
class="Dialog flex center ConfirmDialog modal"
|
||||
data-testid="confirmation-dialog"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
>
|
||||
<div
|
||||
class="box"
|
||||
@@ -99,21 +96,19 @@ exports[`kube-object-menu given kube object when removing kube object renders 1`
|
||||
</span>
|
||||
</i>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
Remove
|
||||
some-kind
|
||||
|
||||
<b>
|
||||
some-namespace/some-name
|
||||
</b>
|
||||
from
|
||||
<b>
|
||||
Some name
|
||||
</b>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
Remove
|
||||
some-kind
|
||||
|
||||
<b>
|
||||
some-namespace/some-name
|
||||
</b>
|
||||
from
|
||||
<b>
|
||||
Some name
|
||||
</b>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="confirm-buttons"
|
||||
@@ -142,15 +137,14 @@ exports[`kube-object-menu given kube object with namespace when removing kube ob
|
||||
<div>
|
||||
<div>
|
||||
<ul
|
||||
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
class="Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
>
|
||||
<li>
|
||||
Some menu item
|
||||
</li>
|
||||
<li
|
||||
class="MenuItem"
|
||||
data-testid="menu-action-remove"
|
||||
data-testid="menu-action-delete"
|
||||
tabindex="0"
|
||||
>
|
||||
<i
|
||||
@@ -175,9 +169,8 @@ exports[`kube-object-menu given kube object with namespace when removing kube ob
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Animate opacity-scale Dialog flex center ConfirmDialog modal"
|
||||
class="Dialog flex center ConfirmDialog modal"
|
||||
data-testid="confirmation-dialog"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
>
|
||||
<div
|
||||
class="box"
|
||||
@@ -196,21 +189,19 @@ exports[`kube-object-menu given kube object with namespace when removing kube ob
|
||||
</span>
|
||||
</i>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
Remove
|
||||
some-kind
|
||||
|
||||
<b>
|
||||
some-namespace/some-name
|
||||
</b>
|
||||
from
|
||||
<b>
|
||||
Some name
|
||||
</b>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
Remove
|
||||
some-kind
|
||||
|
||||
<b>
|
||||
some-namespace/some-name
|
||||
</b>
|
||||
from
|
||||
<b>
|
||||
Some name
|
||||
</b>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="confirm-buttons"
|
||||
@@ -239,15 +230,14 @@ exports[`kube-object-menu given kube object without namespace when removing kube
|
||||
<div>
|
||||
<div>
|
||||
<ul
|
||||
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
class="Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
>
|
||||
<li>
|
||||
Some menu item
|
||||
</li>
|
||||
<li
|
||||
class="MenuItem"
|
||||
data-testid="menu-action-remove"
|
||||
data-testid="menu-action-delete"
|
||||
tabindex="0"
|
||||
>
|
||||
<i
|
||||
@@ -272,9 +262,8 @@ exports[`kube-object-menu given kube object without namespace when removing kube
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="Animate opacity-scale Dialog flex center ConfirmDialog modal"
|
||||
class="Dialog flex center ConfirmDialog modal"
|
||||
data-testid="confirmation-dialog"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
>
|
||||
<div
|
||||
class="box"
|
||||
@@ -293,21 +282,19 @@ exports[`kube-object-menu given kube object without namespace when removing kube
|
||||
</span>
|
||||
</i>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
Remove
|
||||
some-kind
|
||||
|
||||
<b>
|
||||
some-name
|
||||
</b>
|
||||
from
|
||||
<b>
|
||||
Some name
|
||||
</b>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
Remove
|
||||
some-kind
|
||||
|
||||
<b>
|
||||
some-name
|
||||
</b>
|
||||
from
|
||||
<b>
|
||||
Some name
|
||||
</b>
|
||||
?
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
class="confirm-buttons"
|
||||
@@ -335,8 +322,7 @@ exports[`kube-object-menu given no kube object, renders 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<ul
|
||||
class="Animate opacity Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||
class="Menu MenuActions flex KubeObjectMenu toolbar gaps right bottom"
|
||||
/>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+1
-2
@@ -5,7 +5,7 @@
|
||||
import { conforms, includes, eq } from "lodash/fp";
|
||||
import type { KubeObject } from "../../../../../common/k8s-api/kube-object";
|
||||
import type { LensRendererExtension } from "../../../../../extensions/lens-renderer-extension";
|
||||
import { staticKubeObjectMenuItems as staticMenuItems } from "./static-kube-object-menu-items";
|
||||
import { staticKubeObjectMenuItems as staticMenuItems } from "../static-kube-object-menu-items";
|
||||
|
||||
interface Dependencies {
|
||||
extensions: LensRendererExtension[];
|
||||
@@ -31,7 +31,6 @@ export const getKubeObjectMenuItems = ({
|
||||
apiVersions: includes(kubeObject.apiVersion),
|
||||
}),
|
||||
)
|
||||
|
||||
.map((item) => item.components.MenuItem);
|
||||
};
|
||||
|
||||
+4
-12
@@ -2,11 +2,10 @@
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { ServiceAccountMenu } from "../../../+user-management/+service-accounts/service-account-menu";
|
||||
import { CronJobMenu } from "../../../+workloads-cronjobs/cron-job-menu";
|
||||
import { DeploymentMenu } from "../../../+workloads-deployments/deployment-menu";
|
||||
import { ReplicaSetMenu } from "../../../+workloads-replicasets/replica-set-menu";
|
||||
import { StatefulSetMenu } from "../../../+workloads-statefulsets/stateful-set-menu";
|
||||
import { ServiceAccountMenu } from "../../+user-management/+service-accounts/service-account-menu";
|
||||
import { CronJobMenu } from "../../+workloads-cronjobs/cron-job-menu";
|
||||
import { DeploymentMenu } from "../../+workloads-deployments/deployment-menu";
|
||||
import { ReplicaSetMenu } from "../../+workloads-replicasets/replica-set-menu";
|
||||
|
||||
export const staticKubeObjectMenuItems = [
|
||||
{
|
||||
@@ -37,11 +36,4 @@ export const staticKubeObjectMenuItems = [
|
||||
MenuItem: ReplicaSetMenu,
|
||||
},
|
||||
},
|
||||
{
|
||||
kind: "StatefulSet",
|
||||
apiVersions: ["apps/v1"],
|
||||
components: {
|
||||
MenuItem: StatefulSetMenu,
|
||||
},
|
||||
},
|
||||
];
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import type React from "react";
|
||||
import type { KubeObject } from "../../../../../common/k8s-api/kube-object";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
|
||||
export interface KubeObjectMenuItemProps {
|
||||
object: KubeObject;
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import React from "react";
|
||||
import { screen } from "@testing-library/react";
|
||||
import { screen, waitFor } from "@testing-library/react";
|
||||
import "@testing-library/jest-dom/extend-expect";
|
||||
import { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
@@ -21,7 +21,7 @@ import type { Cluster } from "../../../common/cluster/cluster";
|
||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||
import apiManagerInjectable from "./dependencies/api-manager.injectable";
|
||||
import { KubeObjectMenu } from "./index";
|
||||
import type { KubeObjectMenuRegistration } from "./dependencies/kube-object-menu-items/kube-object-menu-registration";
|
||||
import type { KubeObjectMenuRegistration } from "./kube-object-menu-registration";
|
||||
import { computed } from "mobx";
|
||||
import { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
|
||||
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
|
||||
@@ -30,6 +30,8 @@ import createEditResourceTabInjectable from "../dock/edit-resource/edit-resource
|
||||
// TODO: Make tooltips free of side effects by making it deterministic
|
||||
jest.mock("../tooltip");
|
||||
|
||||
// TODO: make `animated={false}` not required to make tests deterministic
|
||||
|
||||
class SomeTestExtension extends LensRendererExtension {
|
||||
constructor(
|
||||
kubeObjectMenuItems: KubeObjectMenuRegistration[],
|
||||
@@ -144,7 +146,7 @@ describe("kube-object-menu", () => {
|
||||
|
||||
({ baseElement } = render(
|
||||
<div>
|
||||
<ConfirmDialog />
|
||||
<ConfirmDialog animated={false} />
|
||||
|
||||
<KubeObjectMenu
|
||||
object={objectStub}
|
||||
@@ -164,23 +166,18 @@ describe("kube-object-menu", () => {
|
||||
});
|
||||
|
||||
describe("when removing kube object", () => {
|
||||
beforeEach(() => {
|
||||
const menuItem = screen.getByTestId("menu-action-remove");
|
||||
|
||||
userEvent.click(menuItem);
|
||||
beforeEach(async () => {
|
||||
userEvent.click(await screen.findByTestId("menu-action-delete"));
|
||||
});
|
||||
|
||||
it("renders", () => {
|
||||
it("renders", async () => {
|
||||
await screen.findByTestId("confirmation-dialog");
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("opens a confirmation dialog", () => {
|
||||
screen.getByTestId("confirmation-dialog");
|
||||
});
|
||||
|
||||
describe("when remove is confirmed", () => {
|
||||
beforeEach(() => {
|
||||
const confirmRemovalButton = screen.getByTestId("confirm");
|
||||
beforeEach(async () => {
|
||||
const confirmRemovalButton = await screen.findByTestId("confirm");
|
||||
|
||||
userEvent.click(confirmRemovalButton);
|
||||
});
|
||||
@@ -189,14 +186,15 @@ describe("kube-object-menu", () => {
|
||||
expect(removeActionMock).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it("does not close the confirmation dialog yet", () => {
|
||||
screen.getByTestId("confirmation-dialog");
|
||||
it("does not close the confirmation dialog yet", async () => {
|
||||
await screen.findByTestId("confirmation-dialog");
|
||||
});
|
||||
|
||||
it("when removal resolves, closes the confirmation dialog", async () => {
|
||||
await removeActionMock.resolve();
|
||||
|
||||
expect(screen.queryByTestId("confirmation-dialog")).toBeNull();
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId("confirmation-dialog")).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -219,7 +217,7 @@ describe("kube-object-menu", () => {
|
||||
|
||||
({ baseElement } = render(
|
||||
<div>
|
||||
<ConfirmDialog />
|
||||
<ConfirmDialog animated={false} />
|
||||
|
||||
<KubeObjectMenu
|
||||
object={objectStub}
|
||||
@@ -230,8 +228,8 @@ describe("kube-object-menu", () => {
|
||||
));
|
||||
});
|
||||
|
||||
it("when removing kube object, renders confirmation dialog with namespace", () => {
|
||||
const menuItem = screen.getByTestId("menu-action-remove");
|
||||
it("when removing kube object, renders confirmation dialog with namespace", async () => {
|
||||
const menuItem = await screen.findByTestId("menu-action-delete");
|
||||
|
||||
userEvent.click(menuItem);
|
||||
|
||||
@@ -256,7 +254,7 @@ describe("kube-object-menu", () => {
|
||||
|
||||
({ baseElement } = render(
|
||||
<div>
|
||||
<ConfirmDialog />
|
||||
<ConfirmDialog animated={false} />
|
||||
|
||||
<KubeObjectMenu
|
||||
object={objectStub}
|
||||
@@ -267,8 +265,8 @@ describe("kube-object-menu", () => {
|
||||
));
|
||||
});
|
||||
|
||||
it("when removing kube object, renders confirmation dialog without namespace", () => {
|
||||
const menuItem = screen.getByTestId("menu-action-remove");
|
||||
it("when removing kube object, renders confirmation dialog without namespace", async () => {
|
||||
const menuItem = await screen.findByTestId("menu-action-delete");
|
||||
|
||||
userEvent.click(menuItem);
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { autoBind, cssNames } from "../../utils";
|
||||
import { cssNames } from "../../utils";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import type { MenuActionsProps } from "../menu";
|
||||
import { MenuActions } from "../menu";
|
||||
import { MenuItem, MenuActions } from "../menu";
|
||||
import identity from "lodash/identity";
|
||||
import type { ApiManager } from "../../../common/k8s-api/api-manager";
|
||||
import { withInjectables } from "@ogre-tools/injectable-react";
|
||||
@@ -16,6 +16,16 @@ import createEditResourceTabInjectable from "../dock/edit-resource/edit-resource
|
||||
import hideDetailsInjectable from "./dependencies/hide-details.injectable";
|
||||
import kubeObjectMenuItemsInjectable from "./dependencies/kube-object-menu-items/kube-object-menu-items.injectable";
|
||||
import apiManagerInjectable from "./dependencies/api-manager.injectable";
|
||||
import type { OnKubeObjectContextMenuOpen } from "./on-context-menu-open.injectable";
|
||||
import onKubeObjectContextMenuOpenInjectable from "./on-context-menu-open.injectable";
|
||||
import type { KubeObjectContextMenuItem } from "../../kube-object/handler";
|
||||
import { observable, runInAction } from "mobx";
|
||||
import type { WithConfirmation } from "../confirm-dialog/with-confirm.injectable";
|
||||
import type { Navigate } from "../../navigation/navigate.injectable";
|
||||
import { Icon } from "../icon";
|
||||
import navigateInjectable from "../../navigation/navigate.injectable";
|
||||
import withConfirmationInjectable from "../confirm-dialog/with-confirm.injectable";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
export interface KubeObjectMenuProps<TKubeObject extends KubeObject> extends MenuActionsProps {
|
||||
object: TKubeObject | null | undefined;
|
||||
@@ -29,52 +39,17 @@ interface Dependencies {
|
||||
clusterName: string;
|
||||
hideDetails: () => void;
|
||||
createEditResourceTab: (kubeObject: KubeObject) => void;
|
||||
onContextMenuOpen: OnKubeObjectContextMenuOpen;
|
||||
withConfirmation: WithConfirmation;
|
||||
navigate: Navigate;
|
||||
}
|
||||
|
||||
class NonInjectedKubeObjectMenu<TKubeObject extends KubeObject, Props extends KubeObjectMenuProps<TKubeObject> & Dependencies> extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
autoBind(this);
|
||||
}
|
||||
|
||||
get store() {
|
||||
const { object } = this.props;
|
||||
|
||||
if (!object) return null;
|
||||
|
||||
return this.props.apiManager.getStore(object.selfLink);
|
||||
}
|
||||
|
||||
get isEditable() {
|
||||
return this.props.editable ?? Boolean(this.store?.patch);
|
||||
}
|
||||
|
||||
get isRemovable() {
|
||||
return this.props.removable ?? Boolean(this.store?.remove);
|
||||
}
|
||||
|
||||
async update() {
|
||||
this.props.hideDetails();
|
||||
this.props.createEditResourceTab(this.props.object);
|
||||
}
|
||||
|
||||
async remove() {
|
||||
this.props.hideDetails();
|
||||
const { object, removeAction } = this.props;
|
||||
|
||||
if (removeAction) await removeAction();
|
||||
else await this.store.remove(object);
|
||||
}
|
||||
|
||||
renderRemoveMessage() {
|
||||
const { object } = this.props;
|
||||
|
||||
if (!object) {
|
||||
return null;
|
||||
}
|
||||
@observer
|
||||
class NonInjectedKubeObjectMenu<Kube extends KubeObject> extends React.Component<KubeObjectMenuProps<Kube> & Dependencies> {
|
||||
private menuItems = observable.array<KubeObjectContextMenuItem>();
|
||||
|
||||
private renderRemoveMessage(object: KubeObject) {
|
||||
const breadcrumbParts = [object.getNs(), object.getName()];
|
||||
|
||||
const breadcrumb = breadcrumbParts.filter(identity).join("/");
|
||||
|
||||
return (
|
||||
@@ -84,7 +59,7 @@ class NonInjectedKubeObjectMenu<TKubeObject extends KubeObject, Props extends Ku
|
||||
);
|
||||
}
|
||||
|
||||
getMenuItems(): React.ReactChild[] {
|
||||
private renderMenuItems() {
|
||||
const { object, toolbar } = this.props;
|
||||
|
||||
return this.props.kubeObjectMenuItems.map((MenuItem, index) => (
|
||||
@@ -92,43 +67,134 @@ class NonInjectedKubeObjectMenu<TKubeObject extends KubeObject, Props extends Ku
|
||||
));
|
||||
}
|
||||
|
||||
private emitOnContextMenuOpen(object: KubeObject) {
|
||||
const {
|
||||
apiManager,
|
||||
editable,
|
||||
removable,
|
||||
hideDetails,
|
||||
createEditResourceTab,
|
||||
withConfirmation,
|
||||
removeAction,
|
||||
onContextMenuOpen,
|
||||
navigate,
|
||||
updateAction,
|
||||
} = this.props;
|
||||
|
||||
const store = apiManager.getStore(object.selfLink);
|
||||
const isEditable = editable ?? (Boolean(store?.patch) || Boolean(updateAction));
|
||||
const isRemovable = removable ?? (Boolean(store?.remove) || Boolean(removeAction));
|
||||
|
||||
runInAction(() => {
|
||||
this.menuItems.clear();
|
||||
|
||||
if (isRemovable) {
|
||||
this.menuItems.push({
|
||||
title: "Delete",
|
||||
icon: "delete",
|
||||
onClick: withConfirmation({
|
||||
message: this.renderRemoveMessage(object),
|
||||
labelOk: "Remove",
|
||||
ok: async () => {
|
||||
hideDetails();
|
||||
|
||||
if (removeAction) {
|
||||
await removeAction();
|
||||
} else if (store?.remove) {
|
||||
await store.remove(object);
|
||||
}
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (isEditable) {
|
||||
this.menuItems.push({
|
||||
title: "Edit",
|
||||
icon: "edit",
|
||||
onClick: async () => {
|
||||
hideDetails();
|
||||
|
||||
if (updateAction) {
|
||||
await updateAction();
|
||||
} else {
|
||||
createEditResourceTab(object);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onContextMenuOpen(object, {
|
||||
menuItems: this.menuItems,
|
||||
navigate,
|
||||
});
|
||||
}
|
||||
|
||||
private renderContextMenuItems = (object: KubeObject) => (
|
||||
[...this.menuItems]
|
||||
.reverse() // This is done because the order that we "grow" is right->left
|
||||
.map(({ icon, ...rest }) => ({
|
||||
...rest,
|
||||
icon: typeof icon === "string"
|
||||
? { material: icon }
|
||||
: icon,
|
||||
}))
|
||||
.map((item, index) => (
|
||||
<MenuItem
|
||||
key={`context-menu-item-${index}`}
|
||||
onClick={() => item.onClick(object)}
|
||||
data-testid={`menu-action-${item.title.toLowerCase().replace(/\s+/, "-")}`}
|
||||
>
|
||||
<Icon
|
||||
{...item.icon}
|
||||
interactive={this.props.toolbar}
|
||||
tooltip={item.title}
|
||||
/>
|
||||
<span className="title">
|
||||
{item.title}
|
||||
</span>
|
||||
</MenuItem>
|
||||
))
|
||||
);
|
||||
|
||||
render() {
|
||||
const { remove, update, renderRemoveMessage, isEditable, isRemovable } = this;
|
||||
const { className, editable, removable, ...menuProps } = this.props;
|
||||
const {
|
||||
className,
|
||||
editable,
|
||||
removable,
|
||||
object,
|
||||
removeAction, // This is here so we don't pass it down to `<MenuAction>`
|
||||
removeConfirmationMessage, // This is here so we don't pass it down to `<MenuAction>`
|
||||
updateAction, // This is here so we don't pass it down to `<MenuAction>`
|
||||
...menuProps
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<MenuActions
|
||||
className={cssNames("KubeObjectMenu", className)}
|
||||
updateAction={isEditable ? update : undefined}
|
||||
removeAction={isRemovable ? remove : undefined}
|
||||
removeConfirmationMessage={renderRemoveMessage}
|
||||
onOpen={object ? () => this.emitOnContextMenuOpen(object) : undefined}
|
||||
{...menuProps}
|
||||
>
|
||||
{this.getMenuItems()}
|
||||
{this.renderMenuItems()}
|
||||
{object && this.renderContextMenuItems(object)}
|
||||
</MenuActions>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const InjectedKubeObjectMenu = withInjectables<Dependencies, KubeObjectMenuProps<KubeObject>>(
|
||||
NonInjectedKubeObjectMenu,
|
||||
{
|
||||
getProps: (di, props) => ({
|
||||
clusterName: di.inject(clusterNameInjectable),
|
||||
apiManager: di.inject(apiManagerInjectable),
|
||||
createEditResourceTab: di.inject(createEditResourceTabInjectable),
|
||||
hideDetails: di.inject(hideDetailsInjectable),
|
||||
|
||||
kubeObjectMenuItems: di.inject(kubeObjectMenuItemsInjectable, {
|
||||
kubeObject: props.object,
|
||||
}),
|
||||
...props,
|
||||
export const KubeObjectMenu = withInjectables<Dependencies, KubeObjectMenuProps<KubeObject>>(NonInjectedKubeObjectMenu, {
|
||||
getProps: (di, props) => ({
|
||||
...props,
|
||||
clusterName: di.inject(clusterNameInjectable),
|
||||
apiManager: di.inject(apiManagerInjectable),
|
||||
createEditResourceTab: di.inject(createEditResourceTabInjectable),
|
||||
hideDetails: di.inject(hideDetailsInjectable),
|
||||
kubeObjectMenuItems: di.inject(kubeObjectMenuItemsInjectable, {
|
||||
kubeObject: props.object,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
export function KubeObjectMenu<T extends KubeObject>(
|
||||
props: KubeObjectMenuProps<T>,
|
||||
) {
|
||||
return <InjectedKubeObjectMenu {...props} />;
|
||||
}
|
||||
onContextMenuOpen: di.inject(onKubeObjectContextMenuOpenInjectable),
|
||||
navigate: di.inject(navigateInjectable),
|
||||
withConfirmation: di.inject(withConfirmationInjectable),
|
||||
}),
|
||||
}) as <T extends KubeObject>(props: KubeObjectMenuProps<T>) => React.ReactElement;
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import { getInjectable } from "@ogre-tools/injectable";
|
||||
import type { KubeObject } from "../../../common/k8s-api/kube-object";
|
||||
import type { KubeObjectOnContextMenuOpenContext } from "../../kube-object/handler";
|
||||
import kubeObjectHandlersInjectable from "../../kube-object/handlers.injectable";
|
||||
|
||||
export type OnKubeObjectContextMenuOpen = (obj: KubeObject, ctx: KubeObjectOnContextMenuOpenContext) => void;
|
||||
|
||||
const onKubeObjectContextMenuOpenInjectable = getInjectable({
|
||||
id: "on-kube-object-context-menu-open",
|
||||
instantiate: (di): OnKubeObjectContextMenuOpen => {
|
||||
const handlers = di.inject(kubeObjectHandlersInjectable);
|
||||
|
||||
return (obj, ctx) => {
|
||||
const specificHandlers = handlers.get().get(obj.apiVersion)?.get(obj.kind) ?? [];
|
||||
|
||||
for (const { onContextMenuOpen } of specificHandlers) {
|
||||
onContextMenuOpen?.(ctx);
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default onKubeObjectContextMenuOpenInjectable;
|
||||
Reference in new issue
Block a user