mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Change Cluster Settings button to be consistent with cluster icon menu (#2065)
Co-authored-by: Sebastian Malton <sebastian@malton.name> Co-authored-by: Alex Culliere <alozhkin@mirantis.com>
This commit is contained in:
parent
72e1915ef4
commit
e718b250cc
@ -225,6 +225,12 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
workspaceStore.setLastActiveClusterId(clusterId);
|
||||
}
|
||||
|
||||
deactivate(id: ClusterId) {
|
||||
if (this.isActive(id)) {
|
||||
this.setActive(null);
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
swapIconOrders(workspace: WorkspaceId, from: number, to: number) {
|
||||
const clusters = this.getByWorkspaceId(workspace);
|
||||
|
||||
50
src/renderer/components/cluster-manager/cluster-actions.tsx
Normal file
50
src/renderer/components/cluster-manager/cluster-actions.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import React from "react";
|
||||
import uniqueId from "lodash/uniqueId";
|
||||
import { clusterSettingsURL } from "../+cluster-settings";
|
||||
import { landingURL } from "../+landing-page";
|
||||
|
||||
import { clusterStore } from "../../../common/cluster-store";
|
||||
import { broadcastMessage, requestMain } from "../../../common/ipc";
|
||||
import { clusterDisconnectHandler } from "../../../common/cluster-ipc";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { Cluster } from "../../../main/cluster";
|
||||
import { Tooltip } from "../../components//tooltip";
|
||||
|
||||
const navigate = (route: string) =>
|
||||
broadcastMessage("renderer:navigate", route);
|
||||
|
||||
/**
|
||||
* Creates handlers for high-level actions
|
||||
* that could be performed on an individual cluster
|
||||
* @param cluster Cluster
|
||||
*/
|
||||
export const ClusterActions = (cluster: Cluster) => ({
|
||||
showSettings: () => navigate(clusterSettingsURL({
|
||||
params: { clusterId: cluster.id }
|
||||
})),
|
||||
disconnect: async () => {
|
||||
clusterStore.deactivate(cluster.id);
|
||||
navigate(landingURL());
|
||||
await requestMain(clusterDisconnectHandler, cluster.id);
|
||||
},
|
||||
remove: () => {
|
||||
const tooltipId = uniqueId("tooltip_target_");
|
||||
|
||||
return ConfirmDialog.open({
|
||||
okButtonProps: {
|
||||
primary: false,
|
||||
accent: true,
|
||||
label: "Remove"
|
||||
},
|
||||
ok: () => {
|
||||
clusterStore.deactivate(cluster.id);
|
||||
clusterStore.removeById(cluster.id);
|
||||
navigate(landingURL());
|
||||
},
|
||||
message: <p>
|
||||
Are you sure want to remove cluster <b id={tooltipId}>{cluster.name}</b>?
|
||||
<Tooltip targetId={tooltipId}>{cluster.id}</Tooltip>
|
||||
</p>
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -2,7 +2,6 @@ import "./clusters-menu.scss";
|
||||
|
||||
import React from "react";
|
||||
import { remote } from "electron";
|
||||
import { requestMain } from "../../../common/ipc";
|
||||
import type { Cluster } from "../../../main/cluster";
|
||||
import { DragDropContext, Draggable, DraggableProvided, Droppable, DroppableProvided, DropResult } from "react-beautiful-dnd";
|
||||
import { observer } from "mobx-react";
|
||||
@ -13,12 +12,10 @@ import { Icon } from "../icon";
|
||||
import { autobind, cssNames, IClassName } from "../../utils";
|
||||
import { isActiveRoute, navigate } from "../../navigation";
|
||||
import { addClusterURL } from "../+add-cluster";
|
||||
import { clusterSettingsURL } from "../+cluster-settings";
|
||||
import { landingURL } from "../+landing-page";
|
||||
import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { clusterViewURL } from "./cluster-view.route";
|
||||
import { ClusterActions } from "./cluster-actions";
|
||||
import { getExtensionPageUrl, globalPageMenuRegistry, globalPageRegistry } from "../../../extensions/registries";
|
||||
import { clusterDisconnectHandler } from "../../../common/cluster-ipc";
|
||||
import { commandRegistry } from "../../../extensions/registries/command-registry";
|
||||
import { CommandOverlay } from "../command-palette/command-container";
|
||||
import { computed, observable } from "mobx";
|
||||
@ -40,51 +37,24 @@ export class ClustersMenu extends React.Component<Props> {
|
||||
showContextMenu = (cluster: Cluster) => {
|
||||
const { Menu, MenuItem } = remote;
|
||||
const menu = new Menu();
|
||||
const actions = ClusterActions(cluster);
|
||||
|
||||
menu.append(new MenuItem({
|
||||
label: `Settings`,
|
||||
click: () => {
|
||||
navigate(clusterSettingsURL({
|
||||
params: {
|
||||
clusterId: cluster.id
|
||||
}
|
||||
}));
|
||||
}
|
||||
click: actions.showSettings
|
||||
}));
|
||||
|
||||
if (cluster.online) {
|
||||
menu.append(new MenuItem({
|
||||
label: `Disconnect`,
|
||||
click: async () => {
|
||||
if (clusterStore.isActive(cluster.id)) {
|
||||
navigate(landingURL());
|
||||
clusterStore.setActive(null);
|
||||
}
|
||||
await requestMain(clusterDisconnectHandler, cluster.id);
|
||||
}
|
||||
click: actions.disconnect
|
||||
}));
|
||||
}
|
||||
|
||||
if (!cluster.isManaged) {
|
||||
menu.append(new MenuItem({
|
||||
label: `Remove`,
|
||||
click: () => {
|
||||
ConfirmDialog.open({
|
||||
okButtonProps: {
|
||||
primary: false,
|
||||
accent: true,
|
||||
label: `Remove`,
|
||||
},
|
||||
ok: () => {
|
||||
if (clusterStore.activeClusterId === cluster.id) {
|
||||
navigate(landingURL());
|
||||
clusterStore.setActive(null);
|
||||
}
|
||||
clusterStore.removeById(cluster.id);
|
||||
},
|
||||
message: <p>Are you sure want to remove cluster <b title={cluster.id}>{cluster.contextName}</b>?</p>,
|
||||
});
|
||||
}
|
||||
click: actions.remove
|
||||
}));
|
||||
}
|
||||
menu.popup({
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export * from "./cluster-manager";
|
||||
export * from "./cluster-actions";
|
||||
|
||||
@ -7,15 +7,18 @@ import "@testing-library/jest-dom/extend-expect";
|
||||
import { MainLayoutHeader } from "../main-layout-header";
|
||||
import { Cluster } from "../../../../main/cluster";
|
||||
import { workspaceStore } from "../../../../common/workspace-store";
|
||||
import { broadcastMessage } from "../../../../common/ipc";
|
||||
import { broadcastMessage, requestMain } from "../../../../common/ipc";
|
||||
import { clusterDisconnectHandler } from "../../../../common/cluster-ipc";
|
||||
import { ConfirmDialog } from "../../confirm-dialog";
|
||||
|
||||
const mockBroadcastIpc = broadcastMessage as jest.MockedFunction<typeof broadcastMessage>;
|
||||
const mockRequestMain = requestMain as jest.MockedFunction<typeof requestMain>;
|
||||
|
||||
const cluster: Cluster = new Cluster({
|
||||
id: "foo",
|
||||
contextName: "minikube",
|
||||
kubeConfigPath: "minikube-config.yml",
|
||||
workspace: workspaceStore.currentWorkspaceId
|
||||
workspace: workspaceStore.currentWorkspaceId,
|
||||
});
|
||||
|
||||
describe("<MainLayoutHeader />", () => {
|
||||
@ -25,20 +28,12 @@ describe("<MainLayoutHeader />", () => {
|
||||
expect(container).toBeInstanceOf(HTMLElement);
|
||||
});
|
||||
|
||||
it("renders gear icon", () => {
|
||||
it("renders three dots icon", () => {
|
||||
const { container } = render(<MainLayoutHeader cluster={cluster} />);
|
||||
const icon = container.querySelector(".Icon .icon");
|
||||
|
||||
expect(icon).toBeInstanceOf(HTMLElement);
|
||||
expect(icon).toHaveTextContent("settings");
|
||||
});
|
||||
|
||||
it("navigates to cluster settings", () => {
|
||||
const { container } = render(<MainLayoutHeader cluster={cluster} />);
|
||||
const icon = container.querySelector(".Icon");
|
||||
|
||||
fireEvent.click(icon);
|
||||
expect(mockBroadcastIpc).toBeCalledWith("renderer:navigate", "/cluster/foo/settings");
|
||||
expect(icon).toHaveTextContent("more_vert");
|
||||
});
|
||||
|
||||
it("renders cluster name", () => {
|
||||
@ -46,4 +41,60 @@ describe("<MainLayoutHeader />", () => {
|
||||
|
||||
expect(getByText("minikube")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe("Cluster Actions Menu", () => {
|
||||
let settingsBtn: Element;
|
||||
let disconnectBtn: Element;
|
||||
let removeBtn: Element;
|
||||
|
||||
beforeEach(() => {
|
||||
const { container } = render(<div>
|
||||
<MainLayoutHeader cluster={cluster} />
|
||||
<ConfirmDialog />
|
||||
</div>);
|
||||
const icon = container.querySelector(".Icon");
|
||||
|
||||
cluster.online = true;
|
||||
fireEvent.click(icon);
|
||||
|
||||
[settingsBtn, disconnectBtn, removeBtn] = Array.from(document.querySelectorAll("ul.ClusterActionsMenu > li"))
|
||||
.map(el => el.querySelector("span"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cluster.online = false;
|
||||
});
|
||||
|
||||
it("renders cluster menu items", () => {
|
||||
expect(settingsBtn).toBeDefined();
|
||||
expect(settingsBtn.textContent).toBe("Settings");
|
||||
expect(disconnectBtn).toBeDefined();
|
||||
expect(disconnectBtn.textContent).toBe("Disconnect");
|
||||
expect(removeBtn).toBeDefined();
|
||||
expect(removeBtn.textContent).toBe("Remove");
|
||||
});
|
||||
|
||||
it("navigates to cluster settings", () => {
|
||||
fireEvent.click(settingsBtn);
|
||||
expect(mockBroadcastIpc).toBeCalledWith("renderer:navigate", "/cluster/foo/settings");
|
||||
});
|
||||
|
||||
it("disconnects from cluster", () => {
|
||||
fireEvent.click(disconnectBtn);
|
||||
expect(mockRequestMain).toBeCalledWith(clusterDisconnectHandler, cluster.id);
|
||||
});
|
||||
|
||||
it("opens 'Remove cluster' dialog", async () => {
|
||||
fireEvent.click(removeBtn);
|
||||
|
||||
const dialog = document.querySelector(".ConfirmDialog");
|
||||
|
||||
expect(dialog).toBeDefined();
|
||||
expect(dialog).not.toBe(null);
|
||||
|
||||
const okBtn = dialog.querySelector("button.ok");
|
||||
|
||||
expect(okBtn.textContent).toBe("Remove");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
import { observer } from "mobx-react";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { clusterSettingsURL } from "../+cluster-settings";
|
||||
import { broadcastMessage } from "../../../common/ipc";
|
||||
import { ClusterActions } from "../cluster-manager";
|
||||
import { Cluster } from "../../../main/cluster";
|
||||
import { cssNames } from "../../utils";
|
||||
import { Icon } from "../icon";
|
||||
import { MenuActions, MenuItem } from "../menu";
|
||||
|
||||
interface Props {
|
||||
cluster: Cluster
|
||||
@ -13,21 +12,28 @@ interface Props {
|
||||
}
|
||||
|
||||
export const MainLayoutHeader = observer(({ cluster, className }: Props) => {
|
||||
const actions = ClusterActions(cluster);
|
||||
const renderMenu = () => (
|
||||
<MenuActions autoCloseOnSelect className="ClusterActionsMenu">
|
||||
<MenuItem onClick={actions.showSettings}>
|
||||
<span>Settings</span>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={actions.disconnect}>
|
||||
<span>Disconnect</span>
|
||||
</MenuItem>
|
||||
{
|
||||
!cluster.isManaged && (
|
||||
<MenuItem onClick={actions.remove}>
|
||||
<span>Remove</span>
|
||||
</MenuItem>
|
||||
)
|
||||
}
|
||||
</MenuActions>);
|
||||
|
||||
return (
|
||||
<header className={cssNames("flex gaps align-center justify-space-between", className)}>
|
||||
<span className="cluster">{cluster.name}</span>
|
||||
<Icon
|
||||
material="settings"
|
||||
tooltip="Open cluster settings"
|
||||
interactive
|
||||
onClick={() => {
|
||||
broadcastMessage("renderer:navigate", clusterSettingsURL({
|
||||
params: {
|
||||
clusterId: cluster.id
|
||||
}
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
{renderMenu()}
|
||||
</header>
|
||||
);
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user