Skip to content

Commit

Permalink
WD-11593 - refactor: remove nested components from ActionLogs (#1783)
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimir-cucu authored Jun 21, 2024
1 parent 62ec9c1 commit 524d693
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 67 deletions.
19 changes: 0 additions & 19 deletions src/pages/EntityDetails/Model/Logs/ActionLogs/ActionLogs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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);

Expand Down Expand Up @@ -364,16 +355,6 @@ describe("Action Logs", () => {
expect(modal).not.toBeInTheDocument();
});

it("can copy the action result", async () => {
renderComponent(<ActionLogs />, { 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();
Expand Down
51 changes: 4 additions & 47 deletions src/pages/EntityDetails/Model/Logs/ActionLogs/ActionLogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
Expand All @@ -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[];
Expand Down Expand Up @@ -66,18 +63,6 @@ enum InlineErrors {
FETCH = "fetch",
}

function generateLinkToApp(
appName: string,
userName: string,
modelName: string,
) {
return (
<Link to={urls.model.app.index({ userName, modelName, appName })}>
{appName}
</Link>
);
}

function generateAppIcon(
application: ApplicationData | undefined,
appName: string,
Expand All @@ -91,43 +76,15 @@ function generateAppIcon(
return (
<>
<CharmIcon name={appName} charmId={application.charm} />
{generateLinkToApp(appName, userName, modelName)}
<Link to={urls.model.app.index({ userName, modelName, appName })}>
{appName}
</Link>
</>
);
}
return <>{appName}</>;
}

function ActionPayloadModal(props: {
payload: ActionResult["output"] | null;
onClose: () => void;
}) {
if (!props.payload) return <></>;
const json = JSON.stringify(props.payload, null, 2);
return (
<Modal
close={props.onClose}
title="Action result payload"
buttonRow={
<Button appearance="neutral" onClick={() => copyToClipboard(json)}>
{Label.COPY}
</Button>
}
data-testid="action-payload-modal"
>
<CodeSnippet
blocks={[
{
appearance: CodeSnippetBlockAppearance.NUMBERED,
wrapLines: true,
code: json,
},
]}
/>
</Modal>
);
}

const compare = (a: string | number, b: string | number) =>
a === b ? 0 : a > b ? 1 : -1;

Expand Down
Original file line number Diff line number Diff line change
@@ -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(
<ActionPayloadModal payload={null} onClose={vi.fn()} />,
);
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(
<ActionPayloadModal payload={mockPayload} onClose={vi.fn()} />,
);
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(
<ActionPayloadModal payload={mockPayload} onClose={vi.fn()} />,
);
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(
<ActionPayloadModal payload={mockPayload} onClose={onClose} />,
);
await userEvent.click(
screen.getByRole("button", { name: "Close active modal" }),
);
expect(onClose).toHaveBeenCalledOnce();
});
});
Original file line number Diff line number Diff line change
@@ -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 (
<Modal
close={onClose}
title={Label.TITLE}
buttonRow={
<Button appearance="neutral" onClick={() => copyToClipboard(json)}>
{Label.COPY}
</Button>
}
data-testid="action-payload-modal"
>
<CodeSnippet
blocks={[
{
appearance: CodeSnippetBlockAppearance.NUMBERED,
wrapLines: true,
code: json,
},
]}
/>
</Modal>
);
};

export default ActionPayloadModal;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./ActionPayloadModal";
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum Label {
TITLE = "Action result payload",
COPY = "Copy to clipboard",
}
1 change: 0 additions & 1 deletion src/pages/EntityDetails/Model/Logs/ActionLogs/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export enum Label {
OUTPUT = "Output",
COPY = "Copy to clipboard",
FETCH_ERROR = "Error while trying to fetch data.",
}

Expand Down

0 comments on commit 524d693

Please sign in to comment.