Skip to content

Commit

Permalink
feat: move non-partition collapsible forms to side panel (#5349)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jay-Topher authored Mar 15, 2024
1 parent 7840a85 commit 7d88fb2
Show file tree
Hide file tree
Showing 20 changed files with 468 additions and 197 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,6 @@ describe("TableActionsDropdown", () => {
// Open menu and click the actions
await userEvent.click(screen.getByRole("button"));
await userEvent.click(screen.getByRole("button", { name: "Action 1" }));
expect(onActionClick).toHaveBeenCalledWith("action-1");
expect(onActionClick).toHaveBeenCalledWith("action-1", undefined);
});
});
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import type { ButtonProps } from "@canonical/react-components";
import type { ButtonProps, ValueOf } from "@canonical/react-components";

import TableMenu from "@/app/base/components/TableMenu";
import type { DataTestElement } from "@/app/base/types";
import type { MachineSidePanelViews } from "@/app/machines/constants";

export type TableAction<A> = {
label: string;
show?: boolean;
type: A;
view?: ValueOf<typeof MachineSidePanelViews>;
};

// This allows the "data-testid" attribute to be used for the action links, which
Expand All @@ -16,7 +18,7 @@ type TableActionsLink = DataTestElement<ButtonProps>;
type Props<A> = {
actions: TableAction<A>[];
disabled?: boolean;
onActionClick: (action: A) => void;
onActionClick: (action: A, view?: TableAction<A>["view"]) => void;
};

const TableActionsDropdown = <A extends string>({
Expand All @@ -30,7 +32,7 @@ const TableActionsDropdown = <A extends string>({
links.push({
children: action.label,
"data-testid": action.type,
onClick: () => onActionClick(action.type),
onClick: () => onActionClick(action.type, action?.view),
});
}
return links;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const AddPartitionFields = ({

return (
<Row>
<Col size={5}>
<Col size={12}>
<Input
aria-label="Name"
disabled
Expand Down Expand Up @@ -67,7 +67,7 @@ export const AddPartitionFields = ({
]}
/>
</Col>
<Col emptyLarge={7} size={5}>
<Col size={12}>
<FilesystemFields systemId={systemId} />
</Col>
</Row>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import configureStore from "redux-mock-store";

import AvailableStorageTable from "./AvailableStorageTable";

import * as sidePanelHooks from "@/app/base/side-panel-context";
import { MachineSidePanelViews } from "@/app/machines/constants";
import { actions as machineActions } from "@/app/store/machine";
import { MIN_PARTITION_SIZE } from "@/app/store/machine/constants";
import type { RootState } from "@/app/store/root/types";
import { DiskTypes, StorageLayout } from "@/app/store/types/enum";
import { DiskTypes } from "@/app/store/types/enum";
import {
controllerDetails as controllerDetailsFactory,
controllerState as controllerStateFactory,
Expand Down Expand Up @@ -37,6 +39,11 @@ const getAvailableDisk = (name = "available-disk") =>
type: DiskTypes.PHYSICAL,
});

const setSidePanelContent = vi.fn();
afterEach(() => {
vi.restoreAllMocks();
});

it("can show an empty message", () => {
const machine = machineDetailsFactory({
disks: [],
Expand Down Expand Up @@ -251,6 +258,12 @@ describe("performing machine actions", () => {
});

it("can open the add partition form if disk can be partitioned", async () => {
vi.spyOn(sidePanelHooks, "useSidePanel").mockReturnValue({
setSidePanelContent,
sidePanelContent: null,
setSidePanelSize: vi.fn(),
sidePanelSize: "regular",
});
const disk = getAvailableDisk();
const machine = machineDetailsFactory({
disks: [disk],
Expand All @@ -264,23 +277,20 @@ describe("performing machine actions", () => {
}),
}),
});
const store = mockStore(state);
render(
<Provider store={store}>
<MemoryRouter>
<CompatRouter>
<AvailableStorageTable canEditStorage node={machine} />
</CompatRouter>
</MemoryRouter>
</Provider>

renderWithBrowserRouter(
<AvailableStorageTable canEditStorage node={machine} />,
{ state }
);

await userEvent.click(screen.getByRole("button", { name: /Take action/ }));
await userEvent.click(
screen.getByRole("button", { name: /Add partition/ })
);

expect(screen.getByLabelText("Add partition form")).toBeInTheDocument();
expect(setSidePanelContent).toHaveBeenCalledWith(
expect.objectContaining({ view: MachineSidePanelViews.CREATE_PARTITION })
);
});

it("can open the edit partition form if partition can be edited", async () => {
Expand All @@ -301,15 +311,10 @@ describe("performing machine actions", () => {
}),
}),
});
const store = mockStore(state);
render(
<Provider store={store}>
<MemoryRouter>
<CompatRouter>
<AvailableStorageTable canEditStorage node={machine} />
</CompatRouter>
</MemoryRouter>
</Provider>

renderWithBrowserRouter(
<AvailableStorageTable canEditStorage node={machine} />,
{ state }
);

await userEvent.click(screen.getByRole("button", { name: /Take action/ }));
Expand Down Expand Up @@ -359,6 +364,12 @@ describe("performing machine actions", () => {
});

it("can open the edit disk form if the disk is not a volume group", async () => {
vi.spyOn(sidePanelHooks, "useSidePanel").mockReturnValue({
setSidePanelContent,
sidePanelContent: null,
setSidePanelSize: vi.fn(),
sidePanelSize: "regular",
});
const disk = diskFactory({ type: DiskTypes.PHYSICAL });
const machine = machineDetailsFactory({
disks: [disk],
Expand All @@ -372,23 +383,20 @@ describe("performing machine actions", () => {
}),
}),
});
const store = mockStore(state);
render(
<Provider store={store}>
<MemoryRouter>
<CompatRouter>
<AvailableStorageTable canEditStorage node={machine} />
</CompatRouter>
</MemoryRouter>
</Provider>

renderWithBrowserRouter(
<AvailableStorageTable canEditStorage node={machine} />,
{ state }
);

await userEvent.click(screen.getByRole("button", { name: /Take action/ }));
await userEvent.click(
screen.getByRole("button", { name: /Edit physical disk/ })
);

expect(screen.getByLabelText("Edit disk form")).toBeInTheDocument();
expect(setSidePanelContent).toHaveBeenCalledWith(
expect.objectContaining({ view: MachineSidePanelViews.EDIT_DISK })
);
});

it("can open the create bcache form if the machine has at least one cache set", async () => {
Expand Down Expand Up @@ -469,52 +477,6 @@ describe("performing machine actions", () => {
).toBeDisabled();
});

it("can create a cache set from a disk", async () => {
const disk = diskFactory({
available_size: MIN_PARTITION_SIZE + 1,
partitions: [],
type: DiskTypes.PHYSICAL,
});
const machine = machineDetailsFactory({
disks: [disk],
system_id: "abc123",
});
const state = rootStateFactory({
machine: machineStateFactory({
items: [machine],
statuses: machineStatusesFactory({
abc123: machineStatusFactory(),
}),
}),
});
const store = mockStore(state);
render(
<Provider store={store}>
<MemoryRouter>
<CompatRouter>
<AvailableStorageTable canEditStorage node={machine} />
</CompatRouter>
</MemoryRouter>
</Provider>
);

await userEvent.click(screen.getByRole("button", { name: /Take action/ }));
await userEvent.click(
screen.getByRole("button", { name: /Create cache set/ })
);
await userEvent.click(
screen.getByRole("button", { name: /Create cache set/ })
);

const expectedAction = machineActions.createCacheSet({
blockId: disk.id,
systemId: machine.system_id,
});
expect(
store.getActions().find((action) => action.type === expectedAction.type)
).toStrictEqual(expectedAction);
});

it("can create a cache set from a partition", async () => {
const partition = partitionFactory({ filesystem: null });
const machine = machineDetailsFactory({
Expand Down Expand Up @@ -562,111 +524,6 @@ describe("performing machine actions", () => {
).toStrictEqual(expectedAction);
});

it("can set the boot disk", async () => {
const [nonBootDisk, bootDisk] = [
diskFactory({
available_size: MIN_PARTITION_SIZE + 1,
is_boot: false,
type: DiskTypes.PHYSICAL,
}),
diskFactory({
available_size: MIN_PARTITION_SIZE + 1,
is_boot: true,
type: DiskTypes.PHYSICAL,
}),
];
const machine = machineDetailsFactory({
detected_storage_layout: StorageLayout.BLANK,
disks: [nonBootDisk, bootDisk],
system_id: "abc123",
});
const state = rootStateFactory({
machine: machineStateFactory({
items: [machine],
statuses: machineStatusesFactory({
abc123: machineStatusFactory(),
}),
}),
});
const store = mockStore(state);
render(
<Provider store={store}>
<MemoryRouter>
<CompatRouter>
<AvailableStorageTable canEditStorage node={machine} />
</CompatRouter>
</MemoryRouter>
</Provider>
);

await userEvent.click(
screen.getAllByRole("button", { name: /Take action/ })[0]
);
await userEvent.click(
screen.getByRole("button", { name: /Set boot disk/ })
);
await userEvent.click(
screen.getByRole("button", { name: /Set boot disk/ })
);

const expectedAction = machineActions.setBootDisk({
blockId: nonBootDisk.id,
systemId: machine.system_id,
});
expect(
store.getActions().find((action) => action.type === expectedAction.type)
).toStrictEqual(expectedAction);
});

it("can delete a disk", async () => {
const disk = diskFactory({
available_size: MIN_PARTITION_SIZE + 1,
partitions: [],
type: DiskTypes.PHYSICAL,
});
const machine = machineDetailsFactory({
disks: [disk],
system_id: "abc123",
});
const state = rootStateFactory({
machine: machineStateFactory({
items: [machine],
statuses: machineStatusesFactory({
abc123: machineStatusFactory(),
}),
}),
});
const store = mockStore(state);
render(
<Provider store={store}>
<MemoryRouter>
<CompatRouter>
<AvailableStorageTable canEditStorage node={machine} />
</CompatRouter>
</MemoryRouter>
</Provider>
);

await userEvent.click(screen.getByRole("button", { name: /Take action/ }));
await userEvent.click(
screen.getByRole("button", { name: /Remove physical disk/ })
);
await userEvent.click(
screen.getByRole("button", { name: /Remove physical disk/ })
);

const expectedAction = machineActions.deleteDisk({
blockId: disk.id,
systemId: machine.system_id,
});
expect(
screen.getByText("Are you sure you want to remove this physical disk?")
).toBeInTheDocument();
expect(
store.getActions().find((action) => action.type === expectedAction.type)
).toStrictEqual(expectedAction);
});

it("can delete a volume group", async () => {
const disk = diskFactory({
available_size: MIN_PARTITION_SIZE + 1,
Expand Down
Loading

0 comments on commit 7d88fb2

Please sign in to comment.