Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

363-as-a-dev-i-want-to-refactor-the-context-menu-for-making-action-on-uploaded-file-with-button-2 #389

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 32 additions & 38 deletions src/app/__tests__/HomePage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable react/display-name */
/* eslint-disable react-hooks/rules-of-hooks */
import useUploadedFilesStore from "@/stores/fileStore";
import { fireEvent, render, screen } from "@testing-library/react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { useTranslation } from "react-i18next";
import { Response } from "whatwg-fetch";
Expand All @@ -27,28 +27,6 @@ jest.mock("react-i18next", () => ({
}));
const { t } = useTranslation("homePage");

// Mock the FileElement component
jest.mock(
"../../components/FileElement",
() =>
({
fileName,
fileUrl,
handleDelete,
}: {
fileName: string;
fileUrl: string;
handleDelete: (url: string) => void;
}) => (
<div data-testid="file-element">
<span data-testid="file-name">{fileName}</span>
<button data-testid="delete" onClick={() => handleDelete(fileUrl)}>
Delete
</button>
</div>
),
);

global.URL.createObjectURL = jest
.fn()
.mockImplementation(() => "blob:example/image.png");
Expand Down Expand Up @@ -98,18 +76,24 @@ describe("HomePage Component", () => {
userEvent.upload(input, file);

// Check that the file was uploaded and appears in the list.
const fileElement = await screen.findByTestId("file-element");
const fileElement = await screen.findByTestId("file-element-hello.png");
expect(fileElement).toBeInTheDocument();

const fileName = await screen.findByTestId("file-name");
expect(fileName).toHaveTextContent("hello.png");

fireEvent.mouseEnter(fileElement);

await waitFor(() => {

// Find and click the delete button
const deleteButton = screen.getByTestId("delete");
const deleteButton = screen.getByTestId("delete-hello.png");
fireEvent.click(deleteButton);
});


// Check that the file was removed
expect(screen.queryByTestId("file-element")).not.toBeInTheDocument();
expect(screen.queryByTestId("file-element-hello.png")).not.toBeInTheDocument();
});

it("should allow file uploads via drag and drop", async () => {
Expand Down Expand Up @@ -159,21 +143,26 @@ describe("HomePage Component", () => {
});

// Check that the file was uploaded and appears in the list.
const fileElement = await screen.findByTestId("file-element");
const fileElement = await screen.findByTestId("file-element-hello.png");
expect(fileElement).toBeInTheDocument();

const fileName = await screen.findByTestId("file-name");
expect(fileName).toHaveTextContent("hello.png");

fireEvent.mouseEnter(fileElement);

await waitFor(() => {

// Find and click the delete button
const deleteButton = screen.getByTestId("delete");
const deleteButton = screen.getByTestId("delete-hello.png");
fireEvent.click(deleteButton);
});

// Check that the file was removed
expect(screen.queryByTestId("file-element")).not.toBeInTheDocument();
expect(screen.queryByTestId("file-element-hello.png")).not.toBeInTheDocument();
});

it("should allow file upload via input", async () => {
it("should allow file upload via input and keep hover effect until delete button is clicked", async () => {
render(<HomePage />);

// Mock file
Expand All @@ -184,18 +173,23 @@ describe("HomePage Component", () => {
userEvent.upload(input, file);

// Check that the file was uploaded and appears in the list.
const fileElement = await screen.findByTestId("file-element");
const fileElement = await screen.findByTestId("file-element-hello.png");
expect(fileElement).toBeInTheDocument();

const fileName = await screen.findByTestId("file-name");
expect(fileName).toHaveTextContent("hello.png");

fireEvent.mouseEnter(fileElement);

await waitFor(() => {

// Find and click the delete button
const deleteButton = screen.getByTestId("delete");
const deleteButton = screen.getByTestId("delete-hello.png");
fireEvent.click(deleteButton);
});

// Check that the file was removed
expect(screen.queryByTestId("file-element")).not.toBeInTheDocument();
expect(screen.queryByTestId("file-element-hello.png")).not.toBeInTheDocument();
});

it("The button submit should be visible when a file is uploaded", async () => {
Expand All @@ -209,7 +203,7 @@ describe("HomePage Component", () => {
userEvent.upload(input, file);

// Check that the file was uploaded and appears in the list.
const fileElement = await screen.findByTestId("file-element");
const fileElement = await screen.findByTestId("file-element-hello.png");
expect(fileElement).toBeInTheDocument();

const fileName = await screen.findByTestId("file-name");
Expand All @@ -230,7 +224,7 @@ describe("HomePage Component", () => {
userEvent.upload(input, file);

// Check that the file was uploaded and appears in the list.
const fileElement = await screen.findByTestId("file-element");
const fileElement = await screen.findByTestId("file-element-hello.png");
expect(fileElement).toBeInTheDocument();

const fileName = await screen.findByTestId("file-name");
Expand All @@ -254,11 +248,11 @@ describe("HomePage Component", () => {
userEvent.upload(input, [file, file2]);

// Check that the file was uploaded and appears in the list.
const fileElement = await screen.findByText("hello.png");
const fileElement = await screen.findByTestId("file-element-hello.png");
expect(fileElement).toBeInTheDocument();

// Check that the file was uploaded and appears in the list.
const fileElement2 = await screen.findByText("hello2.png");
const fileElement2 = await screen.findByTestId("file-element-hello2.png");
expect(fileElement2).toBeInTheDocument();

// Check that the file count is displayed
Expand Down Expand Up @@ -301,7 +295,7 @@ describe("HomePage Component", () => {
const input = screen.getByLabelText(t("dropzone.browseFile"));
userEvent.upload(input, file);

const fileElement = await screen.findByTestId("file-element");
const fileElement = await screen.findByTestId("file-element-hello.png");
expect(fileElement).toBeInTheDocument();

const submitButton = screen.getByTestId("submit-button");
Expand Down
2 changes: 1 addition & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function HomePage() {
>
<span className="flex justify-center w-full">
<Button
className="xs:w-[90%] md:w-[100%] min-w-[133.44px] max-h-[40px]"
className={`xs:w-[90%] md:w-[100%] min-w-[133.44px] max-h-[40px] md:max-w-[470px]`} // do not modify md:max-w-[470px] so that the button keeps the same width as the FileList
data-testid="submit-button"
variant="contained"
color="secondary"
Expand Down
131 changes: 117 additions & 14 deletions src/components/FileElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import {
Divider,
Grid2,
IconButton,
TextField,
Typography,
useTheme,
} from "@mui/material";
import DeleteIcon from "@mui/icons-material/Delete";
import CreateIcon from "@mui/icons-material/Create";
import CheckIcon from "@mui/icons-material/Check";
import { DropzoneState } from "@/types/types";
import Image from "next/image";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import useUploadedFilesStore from "@/stores/fileStore";

/**
* FileElementProps interface to define the props for the FileElement component
Expand All @@ -30,19 +34,28 @@ interface FileElementProps {
*
* @returns
*/
const FileElement: React.FC<
FileElementProps & { handleDelete: (fileUrl: string) => void }
> = ({ setDropzoneState, fileName, fileUrl, handleDelete }) => {
const FileElement: React.FC<FileElementProps> = ({ setDropzoneState, fileName, fileUrl }) => {
const theme = useTheme();
const { t } = useTranslation("homePage");
const [hovered, setHovered] = useState(false);
const { removeUploadedFile, renameUploadedFile } = useUploadedFilesStore();
const [isRenaming, setIsRenaming] = useState(false);
const extension = fileName.split(".").pop() || "";
const baseName = fileName.replace(`.${extension}`, "");
const [newName, setNewName] = useState(baseName);

const isValidObjectURL = (url: string) => {
const pattern =
/^(blob:+http:\/\/|https:\/\/)[a-zA-Z0-9\-_.]+(?:\.[a-zA-Z0-9\-_.]+)*(?::\d+)?\/[a-zA-Z0-9\-_.]+$/;
return pattern.test(url);
};

const handleRenameSubmit = (event: React.KeyboardEvent) => {
if (event.key === "Enter" && newName.trim() !== "") {
renameUploadedFile(fileUrl, `${newName.trim()}.${extension}`);
}
};

return (
<>
<Grid2
Expand All @@ -55,7 +68,8 @@ const FileElement: React.FC<
setDropzoneState({ visible: false, imageUrl: "" });
}}
className="relative h-full w-full min-h-[90px] flex items-center
justify-center overflow-hidden rounded border-2 border-neutral-600 bg-neutral-200"
justify-center overflow-hidden rounded border-2 border-neutral-600 bg-neutral-200"
data-testid={`file-element-${fileName}`}
>
<Grid2 size={20} className="relative flex justify-center items-center">
{isValidObjectURL(fileUrl) && (
Expand All @@ -77,31 +91,120 @@ const FileElement: React.FC<
color={theme.palette.primary.dark}
sx={{ borderRightWidth: 3 }} // className="border-r-2" dont work
/>
<Grid2 size={80} className="relative flex items">
<Grid2 size={80} className="relative flex items-center justify-between">
{isRenaming ? (
<div className="flex items-center w-full">
<TextField
value={newName}
onChange={(e) => setNewName(e.target.value)}
onKeyPress={handleRenameSubmit}
autoFocus
placeholder="Enter file name"
inputProps={{ autoComplete: "off" }}
style={{
marginLeft: "5px",
marginRight: "5px",
width: "calc(100% - 45px)",
}}
data-testid="rename-input"
/>
<Typography
variant="body1"
color={theme.palette.text.primary}
style={{
whiteSpace: "nowrap",
marginRight: "5px",
flexShrink: 0,
width: "auto",
}}
>
.{extension}
</Typography>
<IconButton
edge="end"
size="small"
aria-label={t("fileElement.altText.renameFileAlt")}
sx={{
color: "black",
backgroundColor: "#D3D3D3",
borderRadius: "5px",
marginRight: "0.5rem",
"&:hover": {
backgroundColor: "#A9A9A9",
},
}}
onClick={() =>{
setIsRenaming(false);
renameUploadedFile(fileUrl, `${newName.trim()}.${extension}`)
}}
data-testid={`rename-submit`}
>
<CheckIcon style={{ fontSize: "1.7rem" }} />
</IconButton>
</div>
) : (
<Typography
variant="h6"
color={theme.palette.text.primary}
className="overflow-hidden text-ellipsis whitespace-nowrap text-start pl-2"
sx={{ maxWidth: { xs: "80%", md: "calc(100% - 75px)" } }}
data-testid="file-name"
>
{fileName}
</Typography>
</Grid2>
{hovered && (
)}
</Grid2>
{!isRenaming && hovered && (
<>
<IconButton
edge="end"
aria-label={t("fileElement.altText.deleteFileAlt")}
style={{
size="small"
sx={{
alignSelf: "center",
display: "flex",
maxHeight: "50%",
color: "black",
position: "absolute",
top: "-5px",
right: 5,
borderRadius: "5px",
right: { xs: 5, sm: 5 },
top: { xs: 0, sm: "0" },
bottom: { xs: "auto", sm: 10 },
"&:hover": {
backgroundColor: "#A9A9A9",
},
}}
onClick={() => handleDelete(fileUrl)}
onClick={() => {removeUploadedFile(fileUrl); setDropzoneState({ visible: false, imageUrl: "" });}}
data-testid={`delete-${fileName}`}
>
<DeleteIcon data-testid="delete" style={{ fontSize: "1.7rem" }} />
<DeleteIcon data-testid="delete-icon" style={{ fontSize: "1.7rem" }} />
</IconButton>
)}
</Grid2>
<IconButton
edge="end"
size="small"
aria-label={t("fileElement.altText.renameFileAlt")}
sx={{
alignSelf: "center",
display: "flex",
maxHeight: "50%",
color: "black",
position: "absolute",
borderRadius: "5px",
right: { xs: 6, sm: 45 },
top: { xs: 50, sm: "0" },
bottom: { xs: "auto", sm: 10 },
"&:hover": {
backgroundColor: "#A9A9A9",
},
}}
onClick={() => setIsRenaming(true)}
data-testid={`rename-${fileName}`}
>
<CreateIcon data-testid={`rename-icon`} style={{ fontSize: "1.7rem" }} />
</IconButton>
</>
)}
</Grid2>
</>
);
};
Expand Down
Loading
Loading