Skip to content

Commit

Permalink
feat(images): Implement change source design MAASENG-4361 (#5612)
Browse files Browse the repository at this point in the history
  • Loading branch information
abuyukyi101198 authored Feb 19, 2025
1 parent 7f2c51e commit 2fa7b05
Show file tree
Hide file tree
Showing 78 changed files with 687 additions and 4,453 deletions.
4 changes: 3 additions & 1 deletion src/app/base/components/GenericTable/GenericTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type GenericTableProps<T extends { id: string | number }> = {
sortBy?: ColumnSort[];
rowSelection?: RowSelectionState;
setRowSelection?: Dispatch<SetStateAction<RowSelectionState>>;
variant?: "full-height" | "regular";
};

const GenericTable = <T extends { id: string | number }>({
Expand All @@ -58,6 +59,7 @@ const GenericTable = <T extends { id: string | number }>({
sortBy,
rowSelection,
setRowSelection,
variant = "full-height",
}: GenericTableProps<T>) => {
const [grouping, setGrouping] = useState<GroupingState>(groupBy ?? []);
const [expanded, setExpanded] = useState<ExpandedState>(true);
Expand Down Expand Up @@ -161,7 +163,7 @@ const GenericTable = <T extends { id: string | number }>({
});

return (
<DynamicTable className="p-generic-table" variant="full-height">
<DynamicTable className="p-generic-table" variant={variant}>
<thead>
{table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id}>
Expand Down
100 changes: 56 additions & 44 deletions src/app/images/components/ImagesForms/ImagesForms.test.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,67 @@
import configureStore from "redux-mock-store";

import ImagesForms from "./ImagesForms";

import { ImageSidePanelViews } from "@/app/images/constants";
import type { ImageSidePanelContent } from "@/app/images/types";
import { ConfigNames } from "@/app/store/config/types";
import type { RootState } from "@/app/store/root/types";
import * as factory from "@/testing/factories";
import { renderWithBrowserRouter, screen } from "@/testing/utils";
import {
getByTextContent,
renderWithBrowserRouter,
screen,
} from "@/testing/utils";

const mockStore = configureStore<RootState>();
let state: RootState;
describe("ImagesForms", () => {
it("renders DeleteImage form", () => {
const sidePanelContent: ImageSidePanelContent = {
view: ImageSidePanelViews.DELETE_IMAGE,
extras: { bootResource: factory.bootResource() },
};
renderWithBrowserRouter(
<ImagesForms
setSidePanelContent={vi.fn()}
sidePanelContent={sidePanelContent}
/>
);

beforeEach(() => {
state = factory.rootState({
bootresource: factory.bootResourceState({
resources: [
factory.bootResource({
arch: "amd64",
complete: true,
name: "ubuntu/focal",
title: "20.04 LTS",
}),
],
}),
config: factory.configState({
items: [
factory.config({
name: ConfigNames.COMMISSIONING_DISTRO_SERIES,
value: "focal",
}),
],
}),
expect(
screen.getByRole("form", { name: "Confirm image deletion" })
).toBeInTheDocument();
});
});

it("renders a form when appropriate sidepanel view is provided", () => {
const store = mockStore(state);
const sidePanelContent: ImageSidePanelContent = {
view: ImageSidePanelViews.CHANGE_SOURCE,
extras: { hasSources: false },
};
renderWithBrowserRouter(
<ImagesForms
setSidePanelContent={vi.fn()}
sidePanelContent={sidePanelContent}
/>,
{ store }
);
it("renders DeleteMultipleImages form", () => {
const sidePanelContent: ImageSidePanelContent = {
view: ImageSidePanelViews.DELETE_MULTIPLE_IMAGES,
extras: {
rowSelection: { 1: true },
setRowSelection: vi.fn,
},
};
renderWithBrowserRouter(
<ImagesForms
setSidePanelContent={vi.fn()}
sidePanelContent={sidePanelContent}
/>
);

expect(
screen.getByRole("form", { name: "Confirm image deletion" })
).toBeInTheDocument();
});

it("renders DownloadImages form", () => {
const sidePanelContent: ImageSidePanelContent = {
view: ImageSidePanelViews.DOWNLOAD_IMAGE,
};
renderWithBrowserRouter(
<ImagesForms
setSidePanelContent={vi.fn()}
sidePanelContent={sidePanelContent}
/>
);

expect(
screen.getByRole("form", { name: "Choose source" })
).toBeInTheDocument();
expect(
getByTextContent(
"Select images to be imported and kept in sync daily. Images will be available for deploying to machines managed by MAAS."
)
).toBeInTheDocument();
});
});
22 changes: 3 additions & 19 deletions src/app/images/components/ImagesForms/ImagesForms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@ import { useCallback } from "react";
import DeleteImageConfirm from "../ImagesTable/DeleteImageConfirm";

import type { SidePanelContentTypes } from "@/app/base/side-panel-context";
import DeleteImages from "@/app/images/components/SMImagesTable/DeleteImages";
import DownloadImages from "@/app/images/components/SMImagesTable/DownloadImages";
import DeleteImages from "@/app/images/components/ImagesTable/DeleteImages";
import DownloadImages from "@/app/images/components/ImagesTable/DownloadImages";
import { ImageSidePanelViews } from "@/app/images/constants";
import ChangeSource from "@/app/images/views/ImageList/SyncedImages/ChangeSource";

type Props = SidePanelContentTypes & {};

const ImagesForms = ({
sidePanelContent,
setSidePanelContent,
}: Props): JSX.Element | null => {
const ImagesForms = ({ sidePanelContent, setSidePanelContent }: Props) => {
const clearSidePanelContent = useCallback(
() => setSidePanelContent(null),
[setSidePanelContent]
Expand All @@ -23,19 +19,7 @@ const ImagesForms = ({
return null;
}

const hasSources =
sidePanelContent.extras && "hasSources" in sidePanelContent.extras
? sidePanelContent.extras.hasSources
: false;

switch (sidePanelContent.view) {
case ImageSidePanelViews.CHANGE_SOURCE:
return (
<ChangeSource
closeForm={hasSources ? () => clearSidePanelContent() : null}
inCard={false}
/>
);
case ImageSidePanelViews.DELETE_IMAGE: {
const bootResource =
sidePanelContent.extras && "bootResource" in sidePanelContent.extras
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Provider } from "react-redux";
import { MemoryRouter } from "react-router-dom";
import configureStore from "redux-mock-store";

import DownloadImages from "@/app/images/components/SMImagesTable/DownloadImages/DownloadImages";
import DownloadImages from "@/app/images/components/ImagesTable/DownloadImages/DownloadImages";
import { bootResourceActions } from "@/app/store/bootresource";
import { BootResourceSourceType } from "@/app/store/bootresource/types";
import type { RootState } from "@/app/store/root/types";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {
getDownloadableImages,
groupArchesByRelease,
groupImagesByOS,
} from "@/app/images/components/SMImagesTable/DownloadImages/DownloadImages";
import DownloadImagesSelect from "@/app/images/components/SMImagesTable/DownloadImages/DownloadImagesSelect/DownloadImagesSelect";
} from "@/app/images/components/ImagesTable/DownloadImages/DownloadImages";
import DownloadImagesSelect from "@/app/images/components/ImagesTable/DownloadImages/DownloadImagesSelect/DownloadImagesSelect";
import { ConfigNames } from "@/app/store/config/types";
import type { RootState } from "@/app/store/root/types";
import * as factory from "@/testing/factories";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React from "react";
import { MultiSelect, type MultiSelectItem } from "@canonical/react-components";
import { Field } from "formik";

import type { GroupedImages } from "@/app/images/components/SMImagesTable/DownloadImages/DownloadImages";
import type { GroupedImages } from "@/app/images/components/ImagesTable/DownloadImages/DownloadImages";

type DownloadImagesSelectProps = {
values: Record<string, MultiSelectItem[]>;
Expand Down
Loading

0 comments on commit 2fa7b05

Please sign in to comment.