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.", }