From 524d693350ba2093b4da6ab0659e4a113196f0f6 Mon Sep 17 00:00:00 2001
From: Vladimir Cucu <108150922+vladimir-cucu@users.noreply.github.com>
Date: Fri, 21 Jun 2024 09:13:19 +0300
Subject: [PATCH] WD-11593 - refactor: remove nested components from ActionLogs
(#1783)
---
.../Model/Logs/ActionLogs/ActionLogs.test.tsx | 19 -----
.../Model/Logs/ActionLogs/ActionLogs.tsx | 51 ++-----------
.../ActionPayloadModal.test.tsx | 71 +++++++++++++++++++
.../ActionPayloadModal/ActionPayloadModal.tsx | 50 +++++++++++++
.../ActionLogs/ActionPayloadModal/index.ts | 1 +
.../ActionLogs/ActionPayloadModal/types.ts | 4 ++
.../Model/Logs/ActionLogs/types.ts | 1 -
7 files changed, 130 insertions(+), 67 deletions(-)
create mode 100644 src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/ActionPayloadModal.test.tsx
create mode 100644 src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/ActionPayloadModal.tsx
create mode 100644 src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/index.ts
create mode 100644 src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/types.ts
diff --git a/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionLogs.test.tsx b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionLogs.test.tsx
index 4fde0ab63..5072c8dbb 100644
--- a/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionLogs.test.tsx
+++ b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionLogs.test.tsx
@@ -3,7 +3,6 @@ import userEvent from "@testing-library/user-event";
import { add } from "date-fns";
import { vi } from "vitest";
-import * as componentUtils from "components/utils";
import * as actionsHooks from "juju/api-hooks/actions";
import type { RootState } from "store/store";
import { rootStateFactory } from "testing/factories";
@@ -27,14 +26,6 @@ import { renderComponent } from "testing/utils";
import ActionLogs from "./ActionLogs";
import { Label, Output } from "./types";
-vi.mock("components/utils", async () => {
- const utils = await vi.importActual("components/utils");
- return {
- ...utils,
- copyToClipboard: vi.fn(),
- };
-});
-
const completed = new Date();
completed.setMonth(completed.getMonth() - 18);
@@ -364,16 +355,6 @@ describe("Action Logs", () => {
expect(modal).not.toBeInTheDocument();
});
- it("can copy the action result", async () => {
- renderComponent(, { path, url, state });
- await userEvent.click(await screen.findByTestId("show-output"));
- await userEvent.click(screen.getByRole("button", { name: Label.COPY }));
- expect(componentUtils.copyToClipboard).toHaveBeenCalledWith(`{
- "key1": "value1",
- "test": 123
-}`);
- });
-
it("should show error when fetching action logs and refetch action logs", async () => {
const consoleError = console.error;
console.error = vi.fn();
diff --git a/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionLogs.tsx b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionLogs.tsx
index 25824fb27..068e731b8 100644
--- a/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionLogs.tsx
+++ b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionLogs.tsx
@@ -4,11 +4,8 @@ import type {
} from "@canonical/jujulib/dist/api/facades/action/ActionV7";
import {
Button,
- CodeSnippet,
- CodeSnippetBlockAppearance,
ContextualMenu,
Icon,
- Modal,
ModularTable,
} from "@canonical/react-components";
import type { ReactNode } from "react";
@@ -23,7 +20,6 @@ import LoadingSpinner from "components/LoadingSpinner/LoadingSpinner";
import RelativeDate from "components/RelativeDate";
import type { EntityDetailsRoute } from "components/Routes";
import Status from "components/Status";
-import { copyToClipboard } from "components/utils";
import useInlineErrors from "hooks/useInlineErrors";
import { useQueryActionsList, useQueryOperationsList } from "juju/api-hooks";
import PanelInlineErrors from "panels/PanelInlineErrors";
@@ -33,6 +29,7 @@ import type { RootState } from "store/store";
import urls from "urls";
import "./_action-logs.scss";
+import ActionPayloadModal from "./ActionPayloadModal";
import { Label, Output } from "./types";
type Operations = OperationResult[];
@@ -66,18 +63,6 @@ enum InlineErrors {
FETCH = "fetch",
}
-function generateLinkToApp(
- appName: string,
- userName: string,
- modelName: string,
-) {
- return (
-
- {appName}
-
- );
-}
-
function generateAppIcon(
application: ApplicationData | undefined,
appName: string,
@@ -91,43 +76,15 @@ function generateAppIcon(
return (
<>
- {generateLinkToApp(appName, userName, modelName)}
+
+ {appName}
+
>
);
}
return <>{appName}>;
}
-function ActionPayloadModal(props: {
- payload: ActionResult["output"] | null;
- onClose: () => void;
-}) {
- if (!props.payload) return <>>;
- const json = JSON.stringify(props.payload, null, 2);
- return (
- copyToClipboard(json)}>
- {Label.COPY}
-
- }
- data-testid="action-payload-modal"
- >
-
-
- );
-}
-
const compare = (a: string | number, b: string | number) =>
a === b ? 0 : a > b ? 1 : -1;
diff --git a/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/ActionPayloadModal.test.tsx b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/ActionPayloadModal.test.tsx
new file mode 100644
index 000000000..b7feb0cb7
--- /dev/null
+++ b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/ActionPayloadModal.test.tsx
@@ -0,0 +1,71 @@
+import { screen, within } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { vi } from "vitest";
+
+import * as componentUtils from "components/utils";
+import { renderComponent } from "testing/utils";
+
+import ActionPayloadModal from "./ActionPayloadModal";
+import { Label } from "./types";
+
+vi.mock("components/utils", async () => {
+ const utils = await vi.importActual("components/utils");
+ return {
+ ...utils,
+ copyToClipboard: vi.fn(),
+ };
+});
+
+describe("ActionPayloadModal", () => {
+ const mockPayload = { key1: "value1", test: 123 };
+
+ it("should return null if payload is null", () => {
+ const {
+ result: { container },
+ } = renderComponent(
+ ,
+ );
+ expect(container.tagName).toBe("DIV");
+ expect(container.children.length).toBe(1);
+ expect(container.firstChild).toBeEmptyDOMElement();
+ });
+
+ it("should display action payload modal if payload is not null", async () => {
+ renderComponent(
+ ,
+ );
+ const actionPayloadModal = screen.getByTestId("action-payload-modal");
+ expect(actionPayloadModal).toBeInTheDocument();
+ expect(
+ within(actionPayloadModal).getByRole("dialog", { name: Label.TITLE }),
+ ).toBeInTheDocument();
+ const codeSnippetLines = document.querySelectorAll(".p-code-snippet__line");
+ expect(codeSnippetLines).toHaveLength(4);
+ expect(codeSnippetLines[0]).toHaveTextContent("{");
+ expect(codeSnippetLines[1]).toHaveTextContent('"key1": "value1",');
+ expect(codeSnippetLines[2]).toHaveTextContent('"test": 123');
+ expect(codeSnippetLines[3]).toHaveTextContent("}");
+ });
+
+ it("should copy the payload to clipboard", async () => {
+ renderComponent(
+ ,
+ );
+ await userEvent.click(screen.getByRole("button", { name: Label.COPY }));
+ expect(componentUtils.copyToClipboard).toHaveBeenCalledWith(`{
+ "key1": "value1",
+ "test": 123
+}`);
+ });
+
+ it("should close the modal", async () => {
+ const onClose = vi.fn();
+ renderComponent(
+ ,
+ );
+ await userEvent.click(
+ screen.getByRole("button", { name: "Close active modal" }),
+ );
+ expect(onClose).toHaveBeenCalledOnce();
+ });
+});
diff --git a/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/ActionPayloadModal.tsx b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/ActionPayloadModal.tsx
new file mode 100644
index 000000000..e13a4fead
--- /dev/null
+++ b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/ActionPayloadModal.tsx
@@ -0,0 +1,50 @@
+import type { ActionResult } from "@canonical/jujulib/dist/api/facades/action/ActionV7";
+import {
+ Button,
+ CodeSnippet,
+ CodeSnippetBlockAppearance,
+ Modal,
+} from "@canonical/react-components";
+
+import { copyToClipboard } from "components/utils";
+
+import { Label } from "./types";
+
+type Props = {
+ payload: ActionResult["output"] | null;
+ onClose: () => void;
+};
+
+const ActionPayloadModal = ({
+ payload,
+ onClose,
+}: Props): JSX.Element | null => {
+ if (!payload) {
+ return null;
+ }
+ const json = JSON.stringify(payload, null, 2);
+ return (
+ copyToClipboard(json)}>
+ {Label.COPY}
+
+ }
+ data-testid="action-payload-modal"
+ >
+
+
+ );
+};
+
+export default ActionPayloadModal;
diff --git a/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/index.ts b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/index.ts
new file mode 100644
index 000000000..57168aafe
--- /dev/null
+++ b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/index.ts
@@ -0,0 +1 @@
+export { default } from "./ActionPayloadModal";
diff --git a/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/types.ts b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/types.ts
new file mode 100644
index 000000000..2bc3ccff0
--- /dev/null
+++ b/src/pages/EntityDetails/Model/Logs/ActionLogs/ActionPayloadModal/types.ts
@@ -0,0 +1,4 @@
+export enum Label {
+ TITLE = "Action result payload",
+ COPY = "Copy to clipboard",
+}
diff --git a/src/pages/EntityDetails/Model/Logs/ActionLogs/types.ts b/src/pages/EntityDetails/Model/Logs/ActionLogs/types.ts
index 8a2bcc959..b9b2a47ff 100644
--- a/src/pages/EntityDetails/Model/Logs/ActionLogs/types.ts
+++ b/src/pages/EntityDetails/Model/Logs/ActionLogs/types.ts
@@ -1,6 +1,5 @@
export enum Label {
OUTPUT = "Output",
- COPY = "Copy to clipboard",
FETCH_ERROR = "Error while trying to fetch data.",
}