Skip to content

Commit

Permalink
Merge pull request #696 from bounswe/fix/frontend/bookmarks-page
Browse files Browse the repository at this point in the history
[Frontend] Fix Bookmarks Page
  • Loading branch information
mmtftr authored Dec 16, 2024
2 parents 1e84295 + 5a522b4 commit e639be8
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 163 deletions.
12 changes: 6 additions & 6 deletions frontend/src/components/TagType.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import { Loader2 } from "lucide-react";
import ErrorAlert from "@/components/ErrorAlert";
import InfiniteScroll from "@/components/InfiniteScroll";
import { TagCard } from "@/components/TagCard";
import { useSearchTags } from "@/services/api/programmingForumComponents";
import { TagDetails } from "@/services/api/programmingForumSchemas";
import ErrorAlert from "@/components/ErrorAlert";
import InfiniteScroll from "@/components/InfiniteScroll";
import { Loader2 } from "lucide-react";
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";

export default function SubtypePage() {
const { typeId } = useParams<{ typeId: string }>();
Expand Down Expand Up @@ -151,7 +151,7 @@ export default function SubtypePage() {

{/* Tags in this type */}
<h2 className="mb-4 text-2xl font-semibold text-gray-800">
Tags in This Category:
Tags in Category
</h2>

{/* Infinite Scroll for displaying Related Tags */}
Expand Down
211 changes: 112 additions & 99 deletions frontend/src/routes/bookmarks.test.tsx
Original file line number Diff line number Diff line change
@@ -1,107 +1,120 @@
import {
GetBookmarkedQuestionsError,
useGetBookmarkedQuestions,
} from "@/services/api/programmingForumComponents";
import { QuestionDetails } from "@/services/api/programmingForumSchemas";
import { testAccessibility } from "@/utils/test-accessibility";
import { QueryObserverSuccessResult } from "@tanstack/react-query";
import { render, screen } from "@testing-library/react";
import {
createMemoryRouter,
MemoryRouter,
Route,
RouterProvider,
Routes,
} from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { routeConfig } from ".";
import { BookmarkedQuestions } from "./bookmarks";

// Mock the useGetBookmarkedQuestions hook
vi.mock("@/services/api/programmingForumComponents", () => ({
useGetBookmarkedQuestions: vi.fn(),
}));

const mockQuestions: QuestionDetails[] = [
{
GetBookmarkedQuestionsError,
useGetBookmarkedQuestions,
} from "@/services/api/programmingForumComponents";
import { QuestionDetails } from "@/services/api/programmingForumSchemas";
import { testAccessibility } from "@/utils/test-accessibility";
import { QueryObserverSuccessResult } from "@tanstack/react-query";
import { render, screen } from "@testing-library/react";
import {
createMemoryRouter,
MemoryRouter,
Route,
RouterProvider,
Routes,
} from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { routeConfig } from ".";
import { BookmarkedQuestions } from "./bookmarks";

// Mock the useGetBookmarkedQuestions hook
vi.mock("@/services/api/programmingForumComponents", () => ({
useGetBookmarkedQuestions: vi.fn(),
}));

const mockQuestions: QuestionDetails[] = [
{
id: 1,
title: "How to implement a binary tree in Python?",
content: "I'm struggling to understand the structure...",
author: {
id: 1,
title: "How to implement a binary tree in Python?",
content: "I'm struggling to understand the structure...",
author: { id: 1, name: "John Doe", username: "user1", profilePicture: "p", reputationPoints: 50},
createdAt: "2024-12-01T12:00:00Z",
updatedAt: "2024-12-01T12:30:00Z",
tags: [{ id: "1", name: "Python" }],
likeCount: 10,
dislikeCount: 2,
commentCount: 4,
viewCount: 50,
bookmarked: true,
selfVoted: 1,
difficulty: "MEDIUM",
selfDifficultyVote: "MEDIUM",
easyCount: 5,
mediumCount: 10,
hardCount: 3,
name: "John Doe",
username: "user1",
profilePicture: "p",
reputationPoints: 50,
},
{
createdAt: "2024-12-01T12:00:00Z",
updatedAt: "2024-12-01T12:30:00Z",
tags: [{ id: "1", name: "Python" }],
likeCount: 10,
dislikeCount: 2,
commentCount: 4,
viewCount: 50,
bookmarked: true,
selfVoted: 1,
difficulty: "MEDIUM",
selfDifficultyVote: "MEDIUM",
easyCount: 5,
mediumCount: 10,
hardCount: 3,
},
{
id: 2,
title: "What are closures in JavaScript?",
content: "Can someone explain closures with an example?",
author: {
id: 2,
title: "What are closures in JavaScript?",
content: "Can someone explain closures with an example?",
author: { id: 2, name: "Jane Smith", username: "user2", profilePicture: "p", reputationPoints: 50},
createdAt: "2024-12-02T10:00:00Z",
updatedAt: "2024-12-02T10:20:00Z",
tags: [{ id: "2", name: "JavaScript" }],
likeCount: 15,
dislikeCount: 1,
commentCount: 5,
viewCount: 70,
bookmarked: true,
selfVoted: 0,
difficulty: "EASY",
selfDifficultyVote: "EASY",
easyCount: 8,
mediumCount: 6,
hardCount: 1,
name: "Jane Smith",
username: "user2",
profilePicture: "p",
reputationPoints: 50,
},
];

describe("BookmarkedQuestions component", () => {
beforeEach(() => {
vi.mocked(useGetBookmarkedQuestions).mockReset();
});

it("should have no accessibility violations", async () => {
const router = createMemoryRouter(routeConfig, {
initialEntries: ["/bookmarks"],
});

await testAccessibility(<RouterProvider router={router} />);
createdAt: "2024-12-02T10:00:00Z",
updatedAt: "2024-12-02T10:20:00Z",
tags: [{ id: "2", name: "JavaScript" }],
likeCount: 15,
dislikeCount: 1,
commentCount: 5,
viewCount: 70,
bookmarked: true,
selfVoted: 0,
difficulty: "EASY",
selfDifficultyVote: "EASY",
easyCount: 8,
mediumCount: 6,
hardCount: 1,
},
];

describe("BookmarkedQuestions component", () => {
beforeEach(() => {
vi.mocked(useGetBookmarkedQuestions).mockReset();
});

it("should have no accessibility violations", async () => {
const router = createMemoryRouter(routeConfig, {
initialEntries: ["/bookmarks"],
});

it("renders bookmarked questions correctly", () => {
vi.mocked(useGetBookmarkedQuestions).mockReturnValue({
isLoading: false,
error: null,
data: {
data: { items: mockQuestions, totalItems: mockQuestions.length },
},
} as QueryObserverSuccessResult<unknown, GetBookmarkedQuestionsError>);

render(
<MemoryRouter initialEntries={["/bookmarks"]}>
<Routes>
<Route path="/bookmarks" element={<BookmarkedQuestions />} />
</Routes>
</MemoryRouter>,
);

expect(
screen.getByText(`You have ${mockQuestions.length} bookmarked questions.`),
).toBeInTheDocument();

mockQuestions.forEach((question) => {
expect(screen.getByText(question.title)).toBeInTheDocument();
});

await testAccessibility(<RouterProvider router={router} />);
});

it("renders bookmarked questions correctly", () => {
vi.mocked(useGetBookmarkedQuestions).mockReturnValue({
isLoading: false,
error: null,
data: {
data: mockQuestions,
},
} as QueryObserverSuccessResult<unknown, GetBookmarkedQuestionsError>);

render(
<MemoryRouter initialEntries={["/bookmarks"]}>
<Routes>
<Route path="/bookmarks" element={<BookmarkedQuestions />} />
</Routes>
</MemoryRouter>,
);

expect(
screen.getByText(
`You have ${mockQuestions.length} bookmarked questions.`,
),
).toBeInTheDocument();

mockQuestions.forEach((question) => {
expect(screen.getByText(question.title)).toBeInTheDocument();
});
});
});
80 changes: 25 additions & 55 deletions frontend/src/routes/bookmarks.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,14 @@
import { useGetBookmarkedQuestions } from "@/services/api/programmingForumComponents";
import {
QuestionSummary,
} from "@/services/api/programmingForumSchemas";
import { QuestionSummary } from "@/services/api/programmingForumSchemas";
import { Loader2 } from "lucide-react";
import { useEffect, useState } from "react";
import ErrorAlert from "../components/ErrorAlert";
import InfiniteScroll from "../components/InfiniteScroll";
import { QuestionCard } from "../components/QuestionCard";

export const BookmarkedQuestions = () => {
const [pageSize, setPageSize] = useState(20);
const [previousData, setPreviousData] = useState<{
items: QuestionSummary[];
totalItems: number;
}>({
items: [],
totalItems: 0,
});
const [previousData, setPreviousData] = useState<QuestionSummary[]>([]);

const {
data: resultList,
isLoading,
error,
} = useGetBookmarkedQuestions({});
const { data: resultList, isLoading, error } = useGetBookmarkedQuestions({});

useEffect(() => {
if (resultList?.data && !isLoading) {
Expand All @@ -36,56 +22,40 @@ export const BookmarkedQuestions = () => {

const resultListData =
(resultList?.data as typeof previousData) || previousData;
const questions = resultListData.items || [];

const next = () => {
setPageSize(pageSize + 20);
};
const questions = resultListData || [];

return (
<div className="container flex flex-col gap-2 py-8">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold ">
{questions.length
? `You have ${resultListData.totalItems} bookmarked questions.`
? `You have ${resultListData.length} bookmarked questions.`
: "You haven't bookmarked any questions."}
</h1>
</div>
{!questions.length && (
<p>Bookmark questions to view them here.</p>
)}
{!questions.length && <p>Bookmark questions to view them here.</p>}

<div className="mt-4">
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-4">
<InfiniteScroll
next={next}
hasMore={
resultListData.totalItems
? resultListData.totalItems > pageSize
: false
}
isLoading={isLoading}
>
{questions.map((question) => (
<QuestionCard
difficulty={question.difficulty}
key={question.id}
id={question.id}
title={question.title}
content={question.content ?? ""}
votes={
((question as unknown as { upvoteCount: number })
.upvoteCount ?? 0) -
((question as unknown as { downvoteCount: number })
.downvoteCount ?? 0)
}
answerCount={
(question as unknown as { answerCount: number })
.answerCount ?? 0
}
/>
))}
</InfiniteScroll>
{questions.map((question) => (
<QuestionCard
difficulty={question.difficulty}
key={question.id}
id={question.id}
title={question.title}
content={question.content ?? ""}
votes={
((question as unknown as { upvoteCount: number }).upvoteCount ??
0) -
((question as unknown as { downvoteCount: number })
.downvoteCount ?? 0)
}
answerCount={
(question as unknown as { answerCount: number }).answerCount ??
0
}
/>
))}
</div>
{isLoading && (
<div className="col-span-3 flex w-full items-center justify-center">
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/services/temporaryMocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const temporaryMocks = {
id: 2,
username: "john_doe",
reputationPoints: 100,
profilePicture: "frontend\src\assets\placeholder_profile.png",
profilePicture: "frontend\\src\\assets\\placeholder_profile.png",
name: "John Doe",
experienceLevel: "INTERMEDIATE",
},
Expand Down Expand Up @@ -62,7 +62,7 @@ export const temporaryMocks = {
id: 1,
username: "jane_doe",
reputationPoints: 150,
profilePicture: "frontend\src\assets\placeholder_profile.png",
profilePicture: "frontend\\src\\assets\\placeholder_profile.png",
name: "Jane Doe",
experienceLevel: "EXPERT",
},
Expand All @@ -83,7 +83,7 @@ export const temporaryMocks = {
id: 2,
username: "john_doe",
reputationPoints: 100,
profilePicture: "frontend\src\assets\placeholder_profile.png",
profilePicture: "frontend\\src\\assets\\placeholder_profile.png",
name: "John Doe",
experienceLevel: "INTERMEDIATE",
},
Expand Down

0 comments on commit e639be8

Please sign in to comment.