Skip to content

Commit

Permalink
Merge pull request #51 from Sillen00/41-create-bookmarkpage
Browse files Browse the repository at this point in the history
41 create bookmarkpage
  • Loading branch information
Edvindjulic committed Dec 8, 2023
2 parents c477adf + 0be7527 commit 9d827ff
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 42 deletions.
16 changes: 16 additions & 0 deletions src/components/BookmarkedMovies/BookmarkedMovies.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import styled from "@emotion/styled";

export const StyleBookmarkedMovie = styled.div`
padding: 1rem;
a {
text-decoration: none;
}
.empty-bookmark-message {
font-size: 3rem;
text-align: center;
margin: 3rem 0;
font-weight: bold;
}
`;
30 changes: 30 additions & 0 deletions src/components/BookmarkedMovies/BookmarkedMovies.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Grid } from "@mantine/core";
import { useContext } from "react";
import { Link } from "react-router-dom";
import { MovieContext } from "../../contexts/MovieContext";
import { titleToSlug } from "../../pages/MovieViewPage";
import MovieCard from "../MovieCard/MovieCard";
import { StyleBookmarkedMovie } from "./BookmarkedMovies.style";

function BookmarkedMovies() {
const { bookmarkedMovies } = useContext(MovieContext);

return (
<StyleBookmarkedMovie>
{bookmarkedMovies.length === 0 && (
<p className='empty-bookmark-message'>You dont have any movies bookmarked.</p>
)}
<Grid gutter={20}>
{bookmarkedMovies.map(movie => (
<Grid.Col className='grid-item' span={{ base: 6, md: 3, lg: 2.4 }} key={movie.title}>
<Link to={`/movie/${titleToSlug(movie.title)}`}>
<MovieCard {...movie} />
</Link>
</Grid.Col>
))}
</Grid>
</StyleBookmarkedMovie>
);
}

export default BookmarkedMovies;
1 change: 0 additions & 1 deletion src/components/Footer/Footer.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ export const StyledFooter = styled.footer`
}
.logo p {
font-size: clamp(1.2rem, 2vw, 1.8rem);
color: white;
font-weight: 700;
Expand Down
1 change: 1 addition & 0 deletions src/components/MovieCard/MovieCard.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ export const StyledMovieCard = styled.div`
.bookmark-box {
position: relative;
left: 4px;
color: white;
}
`;
75 changes: 58 additions & 17 deletions src/components/MovieCard/MovieCard.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,73 @@
import { Box, Image } from "@mantine/core";
import { FaRegBookmark } from "react-icons/fa";
import { Box, Image, Text } from "@mantine/core";
import { useContext } from "react";
import { FaBookmark, FaRegBookmark } from "react-icons/fa";
import { Link } from "react-router-dom";
import { MovieContext } from "../../contexts/MovieContext";
import { titleToSlug } from "../../pages/MovieViewPage";
import { StyledMovieCard } from "./MovieCard.style";

export interface MovieCardProps {
thumbnail: string;
export interface MovieProps {
title: string;
year: number;
rating: string;
title: string;
actors: string[];
genre: string;
synopsis: string;
thumbnail: string;
isTrending?: boolean;
}

function MovieCard({ thumbnail, year, rating, title }: MovieCardProps) {
function MovieCard(movie: MovieProps) {
const { bookmarkedMovies, setBookmarkedMovies } = useContext(MovieContext);

const handleImageError = (event: React.SyntheticEvent<HTMLImageElement, Event>) => {
// Replace the failed image with the placeholder image
event.currentTarget.src = "./404.png";
};

const handleBookmarkClick = (e: React.SyntheticEvent<HTMLDivElement>) => {
e.preventDefault();

// Check if the movie is already bookmarked
const isBookmarked = bookmarkedMovies.some(m => m.title === movie.title);

// If it's bookmarked, remove it; otherwise, add it
const updatedBookmarkedMovies = isBookmarked
? bookmarkedMovies.filter(m => m.title !== movie.title)
: [...bookmarkedMovies, movie];

// Update the bookmarkedMovies state with the new array
setBookmarkedMovies(updatedBookmarkedMovies);
};

return (
<StyledMovieCard data-testid={`id-${title}`}>
<Box className='thumbnail-box'>
<Image className='thumbnail' src={thumbnail} onError={handleImageError} alt={title} />
</Box>
<Box className='movie-card-bottom'>
<Box className='movie-card-text'>
<p>{title}</p>
<p>{year}</p>
<p>{rating}</p>
<StyledMovieCard data-testid={`id-${movie.title}`}>
<Link to={`/movie/${titleToSlug(movie.title)}`}>
<Box className='thumbnail-box'>
<Image
className='thumbnail'
src={movie.thumbnail}
onError={handleImageError}
alt={movie.title}
/>
</Box>
<Box className='bookmark-box'>
<FaRegBookmark size={"30px"} />
</Link>
<Box className='movie-card-bottom'>
<Link to={`/movie/${titleToSlug(movie.title)}`}>
<Box className='movie-card-text'>
<Text>{movie.title}</Text>
<Text>{movie.year}</Text>
<Text>{movie.rating}</Text>
</Box>
</Link>

<Box onClick={handleBookmarkClick} className='bookmark-box'>
{/* If movie is bookmarked, show a text with text "OO" else show FaRegBookmark icon. */}
{bookmarkedMovies.some(m => m.title === movie.title) ? (
<FaBookmark size={"30px"} />
) : (
<FaRegBookmark size={"30px"} />
)}
</Box>
</Box>
</StyledMovieCard>
Expand Down
4 changes: 1 addition & 3 deletions src/components/MovieView/MovieView.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@ export const StyledMovieView = styled.div`
}
.bookmark {
background-color: #fff;
width: 35px;
height: 45px;
color: white;
}
.text {
Expand Down
29 changes: 27 additions & 2 deletions src/components/MovieView/MovieView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useContext } from "react";
import { FaBookmark, FaRegBookmark } from "react-icons/fa";
import { Movie, MovieContext } from "../../contexts/MovieContext";
import { Box, Image, Text, Title } from "@mantine/core";
import { useState } from "react";
import { Movie } from "../../contexts/MovieContext";
import { StyledMovieView } from "./MovieView.style";

interface Props {
Expand All @@ -15,6 +17,23 @@ function MovieView({ movie }: Props) {
};
const { title, genre, synopsis, year, rating, actors } = movie;

const { bookmarkedMovies, setBookmarkedMovies } = useContext(MovieContext);

const handleBookmarkClick = (e: React.SyntheticEvent<HTMLDivElement>) => {
e.preventDefault();

// Check if the movie is already bookmarked
const isBookmarked = bookmarkedMovies.some(m => m.title === movie.title);

// If it's bookmarked, remove it; otherwise, add it
const updatedBookmarkedMovies = isBookmarked
? bookmarkedMovies.filter(m => m.title !== movie.title)
: [...bookmarkedMovies, movie];

// Update the bookmarkedMovies state with the new array
setBookmarkedMovies(updatedBookmarkedMovies);
};

return (
<StyledMovieView>
<Image src={imageSrc} onError={handleImageError} alt={title} />
Expand All @@ -30,7 +49,13 @@ function MovieView({ movie }: Props) {
{rating}
</Title>
</Box>
<Box className='bookmark'></Box>
<Box onClick={handleBookmarkClick} className='bookmark'>
{bookmarkedMovies.some(m => m.title === movie.title) ? (
<FaBookmark size={"30px"} />
) : (
<FaRegBookmark size={"30px"} />
)}
</Box>
</Box>
<Box>
<Title order={1}>{title}</Title>
Expand Down
12 changes: 11 additions & 1 deletion src/contexts/MovieContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export interface Movie {

interface MovieContextValue {
movies: Movie[];
bookmarkedMovies: Movie[];
setBookmarkedMovies: (bookmarkedMovies: Movie[]) => void;
}

interface Props {
Expand All @@ -22,13 +24,21 @@ interface Props {

export const MovieContext = createContext<MovieContextValue>({
movies: [],
bookmarkedMovies: [],
setBookmarkedMovies: () => {},
});

export default function SearchProvider({ children }: Props) {
const [movies, setMovies] = useState<Movie[]>([]);
const [bookmarkedMovies, setBookmarkedMovies] = useState<Movie[]>([]);
useEffect(() => {
setMovies(data);
setBookmarkedMovies([]);
}, []);

return <MovieContext.Provider value={{ movies }}>{children}</MovieContext.Provider>;
return (
<MovieContext.Provider value={{ movies, bookmarkedMovies, setBookmarkedMovies }}>
{children}
</MovieContext.Provider>
);
}
4 changes: 2 additions & 2 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ a {

.App {
position: relative;
min-height: 100vh;
display: flex;
flex-direction: column;
min-height: 100vh;
}

main {
flex: 1;
flex-grow: 1;
}

body {
Expand Down
2 changes: 1 addition & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import App from "./App";
import MovieContextProvider from "./contexts/MovieContext";
import "./index.css";
import { theme } from "./mantineTheme";
import BookMarkedPage from "./pages/BookMarkedPage";
import BookMarkedPage from "./pages/BookmarkedPage";
import CategoryPage from "./pages/CategoryPage";
import MovieViewPage from "./pages/MovieViewPage";
import NotFoundPage from "./pages/NotFoundPage";
Expand Down
12 changes: 9 additions & 3 deletions src/pages/BookMarkedPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
function BookMarkedPage() {
return <div>BookMarkedPage</div>;
import BookmarkedMovies from "../components/BookmarkedMovies/BookmarkedMovies";

function BookmarkedPage() {
return (
<div>
<BookmarkedMovies />
</div>
);
}

export default BookMarkedPage;
export default BookmarkedPage;
45 changes: 33 additions & 12 deletions src/tests/MovieCard.test.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,60 @@
import { MemoryRouter } from "react-router-dom";
import { describe, expect, it } from "vitest";
import MovieCard, { MovieCardProps } from "../components/MovieCard/MovieCard";
import MovieCard from "../components/MovieCard/MovieCard";
import { render, screen, waitFor } from "../utils/test-utils";

interface MovieCardTestProps {
thumbnail: string; // Make it non-optional
interface MovieProps {
title: string;
year: number;
rating: string;
title: string;
actors: string[];
genre: string;
synopsis: string;
thumbnail: string;
isTrending?: boolean;
}

describe("MovieCard", () => {
it("should render the movie title", () => {
const movieProps: MovieCardTestProps = {
thumbnail: "https://example.com/image.jpg",
const movieProps: MovieProps = {
title: "Movie Title",
year: 2022,
rating: "PG-13",
title: "Movie Title",
actors: [""],
genre: "",
synopsis: "",
thumbnail: "https://example.com/image.jpg",
isTrending: false,
};

render(<MovieCard {...movieProps} />);
render(
<MemoryRouter>
<MovieCard {...movieProps} />
</MemoryRouter>
);

expect(screen.getByText("2022")).toBeInTheDocument();
expect(screen.getByText("PG-13")).toBeInTheDocument();
expect(screen.getByAltText("Movie Title")).toBeInTheDocument();
});

it("should show a placeholder image if no thumbnail is provided", async () => {
const movieProps: MovieCardProps = {
thumbnail: "",
const movieProps: MovieProps = {
title: "Movie Title",
year: 2022,
rating: "PG-13",
title: "Movie Title",
actors: [""],
genre: "",
synopsis: "",
thumbnail: "",
isTrending: false,
};

render(<MovieCard {...movieProps} />);
render(
<MemoryRouter>
<MovieCard {...movieProps} />
</MemoryRouter>
);

const image = await screen.findByAltText("Movie Title");
waitFor(() => {
Expand Down

0 comments on commit 9d827ff

Please sign in to comment.