Skip to content

Commit

Permalink
refactor: add delete button to draft gallery (#462)
Browse files Browse the repository at this point in the history
  • Loading branch information
crnkovic authored Nov 17, 2023
1 parent 3f033d2 commit 4326aa2
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,83 @@
import React from "react";
import { GalleryActionToolbar } from "./GalleryActionToolbar";
import { type GalleryDraft } from "@/Pages/Galleries/hooks/useWalletDraftGalleries";
import { render, screen } from "@/Tests/testing-library";
import { allBreakpoints } from "@/Tests/utils";

interface IndexedDBMockResponse {
add: (draft: GalleryDraft) => Promise<number>;
getAll: () => Promise<GalleryDraft[]>;
update: (draft: GalleryDraft) => Promise<GalleryDraft>;
deleteRecord: (id: number) => Promise<void>;
getByID: (id: number | null) => Promise<GalleryDraft | undefined>;
openCursor: () => void;
getByIndex: () => void;
clear: () => void;
}

const defaultGalleryDraft = {
id: 1,
title: "",
cover: null,
coverType: null,
coverFileName: null,
nfts: [],
walletAddress: "mockedAddress",
value: "test",
collectionsCount: 1,
updatedAt: new Date().getTime(),
};

const expiredGalleryDraft = {
id: 10,
title: "",
cover: null,
coverType: null,
coverFileName: null,
nfts: [],
walletAddress: "mockedAddress",
value: "test",
collectionsCount: 1,
updatedAt: 169901639000,
};

const useIndexedDBMock = (): IndexedDBMockResponse => {
const drafts: GalleryDraft[] = [defaultGalleryDraft, expiredGalleryDraft];

return {
add: async (draft: GalleryDraft): Promise<number> => {
const id = drafts.length + 1;
drafts.push({ ...draft, id });
return await Promise.resolve(id);
},
getAll: async (): Promise<GalleryDraft[]> => await Promise.resolve(drafts),
update: async (draft: GalleryDraft): Promise<GalleryDraft> => {
const index = drafts.findIndex((savedDraft) => savedDraft.id === draft.id);
drafts.splice(index, 1, draft);

return await Promise.resolve(draft);
},
deleteRecord: async (id: number): Promise<void> => {
const index = drafts.findIndex((savedDraft) => savedDraft.id === id);
drafts.splice(index, 0);

await Promise.resolve();
},
getByID: async (id: number | null) => await Promise.resolve(drafts.find((draft) => draft.id === id)),
openCursor: vi.fn(),
getByIndex: vi.fn(),
clear: vi.fn(),
};
};

const mocks = vi.hoisted(() => ({
useIndexedDB: () => useIndexedDBMock(),
}));

vi.mock("react-indexed-db-hook", () => ({
useIndexedDB: mocks.useIndexedDB,
}));

describe("GalleryActionToolbar", () => {
it.each(allBreakpoints)("should render in %s screen", (breakpoint) => {
render(<GalleryActionToolbar />, { breakpoint });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type FormEvent, type MouseEvent, useLayoutEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { GalleryDraftDeleteButton } from "./GalleryDraftDeleteButton";
import { Button, IconButton } from "@/Components/Buttons";
import { GalleryDraftStatus } from "@/Components/Galleries/GalleryPage/GalleryActionToolbar/GalleryDraftStatus";
import { Icon } from "@/Components/Icon";
Expand Down Expand Up @@ -201,6 +202,8 @@ export const GalleryActionToolbar = ({
draftId={draftId}
/>

{draftId !== undefined && <GalleryDraftDeleteButton draftId={draftId} />}

{showDelete && (
<IconButton
data-testid="GalleryActionToolbar__delete"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { router } from "@inertiajs/react";
import { GalleryDraftDeleteButton } from "./GalleryDraftDeleteButton";
import * as ToastsHook from "@/Hooks/useToasts";
import { type GalleryDraft, useWalletDraftGalleries } from "@/Pages/Galleries/hooks/useWalletDraftGalleries";
import { render, renderHook, screen, userEvent, waitFor } from "@/Tests/testing-library";

interface IndexedDBMockResponse {
add: (draft: GalleryDraft) => Promise<number>;
getAll: () => Promise<GalleryDraft[]>;
update: (draft: GalleryDraft) => Promise<GalleryDraft>;
deleteRecord: (id: number) => Promise<void>;
getByID: (id: number | null) => Promise<GalleryDraft | undefined>;
openCursor: () => void;
getByIndex: () => void;
clear: () => void;
}

const defaultGalleryDraft = {
id: 1,
title: "",
cover: null,
coverType: null,
coverFileName: null,
nfts: [],
walletAddress: "mockedAddress",
value: "test",
collectionsCount: 1,
updatedAt: new Date().getTime(),
};

const expiredGalleryDraft = {
id: 10,
title: "",
cover: null,
coverType: null,
coverFileName: null,
nfts: [],
walletAddress: "mockedAddress",
value: "test",
collectionsCount: 1,
updatedAt: 169901639000,
};

const useIndexedDBMock = (): IndexedDBMockResponse => {
const drafts: GalleryDraft[] = [defaultGalleryDraft, expiredGalleryDraft];

return {
add: async (draft: GalleryDraft): Promise<number> => {
const id = drafts.length + 1;
drafts.push({ ...draft, id });
return await Promise.resolve(id);
},
getAll: async (): Promise<GalleryDraft[]> => await Promise.resolve(drafts),
update: async (draft: GalleryDraft): Promise<GalleryDraft> => {
const index = drafts.findIndex((savedDraft) => savedDraft.id === draft.id);
drafts.splice(index, 1, draft);

return await Promise.resolve(draft);
},
deleteRecord: async (id: number): Promise<void> => {
const index = drafts.findIndex((savedDraft) => savedDraft.id === id);
drafts.splice(index, 0);

await Promise.resolve();
},
getByID: async (id: number | null) => await Promise.resolve(drafts.find((draft) => draft.id === id)),
openCursor: vi.fn(),
getByIndex: vi.fn(),
clear: vi.fn(),
};
};

const mocks = vi.hoisted(() => ({
useIndexedDB: () => useIndexedDBMock(),
}));

vi.mock("react-indexed-db-hook", () => ({
useIndexedDB: mocks.useIndexedDB,
}));

describe("GalleryDraftDeleteButton", () => {
it("opens the confirmation dialog when delete button is pressed", async () => {
render(<GalleryDraftDeleteButton draftId={1} />);

expect(screen.getByTestId("GalleryActionToolbar__draftDelete")).toBeInTheDocument();

await userEvent.click(screen.getByTestId("GalleryActionToolbar__draftDelete"));

expect(screen.getByTestId("ConfirmationDialog__confirm")).toBeInTheDocument();

await userEvent.click(screen.getByTestId("ConfirmationDialog__close"));

expect(screen.queryByTestId("ConfirmationDialog__confirm")).not.toBeInTheDocument();
});

it("removes from draft when submitted", async () => {
const { result } = renderHook(() => useWalletDraftGalleries({ address: "mockedAddress" }));

await waitFor(() => {
expect(result.current.drafts).toHaveLength(1);
});

const routerSpy = vi.spyOn(router, "visit").mockImplementation((_, options) => {
(options as { onFinish: () => void }).onFinish();
});

const showToastMock = vi.fn();

vi.spyOn(ToastsHook, "useToasts").mockImplementation(() => ({
showToast: showToastMock,
clear: vi.fn(),
}));

render(<GalleryDraftDeleteButton draftId={1} />);

await userEvent.click(screen.getByTestId("GalleryActionToolbar__draftDelete"));

await userEvent.click(screen.getByTestId("ConfirmationDialog__confirm"));

expect(routerSpy).toBeCalledTimes(1);
expect(showToastMock).toBeCalledTimes(1);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { router } from "@inertiajs/react";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { IconButton } from "@/Components/Buttons";
import { ConfirmDeletionDialog } from "@/Components/ConfirmDeletionDialog";
import { useAuth } from "@/Contexts/AuthContext";
import { useToasts } from "@/Hooks/useToasts";
import { useWalletDraftGalleries } from "@/Pages/Galleries/hooks/useWalletDraftGalleries";
import { assertWallet } from "@/Utils/assertions";

export const GalleryDraftDeleteButton = ({ draftId }: { draftId: number }): JSX.Element => {
const { t } = useTranslation();
const [open, setOpen] = useState(false);
const { showToast } = useToasts();

const { wallet } = useAuth();

assertWallet(wallet);

const { remove } = useWalletDraftGalleries({ address: wallet.address });

const deleteDraft = (): void => {
void remove(draftId);

router.visit(
route("my-galleries", {
draft: true,
}),
{
onFinish: () => {
showToast({
message: t("pages.galleries.my_galleries.draft_succesfully_deleted"),
});
},
},
);
};

return (
<>
<IconButton
data-testid="GalleryActionToolbar__draftDelete"
icon="Trash"
className="flex sm:hidden lg:flex"
onClick={() => {
setOpen(true);
}}
/>

<ConfirmDeletionDialog
title={t("pages.galleries.my_galleries.delete_modal.title")}
isOpen={open}
onClose={() => {
setOpen(false);
}}
onConfirm={deleteDraft}
requiresConfirmation={false}
confirmationButtonVariant="danger"
>
{t("pages.galleries.my_galleries.delete_modal.text")}
</ConfirmDeletionDialog>
</>
);
};

0 comments on commit 4326aa2

Please sign in to comment.