From 677aea37ba8002bce6c95ea40c5231056aef7338 Mon Sep 17 00:00:00 2001 From: codic-yeeshu Date: Sun, 25 May 2025 18:39:54 +0530 Subject: [PATCH 1/8] improved breadcrumbs by introducing page layout component --- .../unit/components/BreadCrumbs.test.tsx | 65 +++-- .../unit/pages/CommitteeDetails.test.tsx | 3 +- .../unit/pages/OrganizationDetails.test.tsx | 3 +- .../unit/pages/ProjectDetails.test.tsx | 3 +- .../unit/pages/RepositoryDetails.test.tsx | 3 +- .../unit/pages/SnapshotDetails.test.tsx | 6 +- .../__tests__/unit/pages/UserDetails.test.tsx | 8 +- frontend/src/app/about/page.tsx | 253 +++++++++--------- .../src/app/chapters/[chapterKey]/page.tsx | 28 +- frontend/src/app/chapters/page.tsx | 55 ++-- .../app/committees/[committeeKey]/page.tsx | 26 +- frontend/src/app/committees/page.tsx | 29 +- frontend/src/app/contribute/page.tsx | 29 +- frontend/src/app/layout.tsx | 2 - frontend/src/app/members/[memberKey]/page.tsx | 36 ++- frontend/src/app/members/page.tsx | 33 +-- .../organizations/[organizationKey]/page.tsx | 34 ++- .../repositories/[repositoryKey]/page.tsx | 41 ++- frontend/src/app/organizations/page.tsx | 33 +-- .../src/app/projects/[projectKey]/page.tsx | 40 +-- frontend/src/app/projects/page.tsx | 47 ++-- frontend/src/app/snapshots/[id]/page.tsx | 150 ++++++----- frontend/src/app/snapshots/page.tsx | 25 +- frontend/src/components/BreadCrumbs.tsx | 38 ++- frontend/src/components/PageLayout.tsx | 21 ++ 25 files changed, 566 insertions(+), 445 deletions(-) create mode 100644 frontend/src/components/PageLayout.tsx diff --git a/frontend/__tests__/unit/components/BreadCrumbs.test.tsx b/frontend/__tests__/unit/components/BreadCrumbs.test.tsx index 17511e18ad..dd5116fff2 100644 --- a/frontend/__tests__/unit/components/BreadCrumbs.test.tsx +++ b/frontend/__tests__/unit/components/BreadCrumbs.test.tsx @@ -1,28 +1,23 @@ import { render, screen } from '@testing-library/react' -import { usePathname } from 'next/navigation' import BreadCrumbs from 'components/BreadCrumbs' import '@testing-library/jest-dom' -jest.mock('next/navigation', () => ({ - usePathname: jest.fn(), -})) - -describe('BreadCrumb', () => { - afterEach(() => { - jest.clearAllMocks() - }) - - test('does not render on root path "/"', () => { - ;(usePathname as jest.Mock).mockReturnValue('/') - - render() +describe('BreadCrumbs', () => { + test('does not render when bcItems is empty', () => { + render() expect(screen.queryByText('Home')).not.toBeInTheDocument() }) test('renders breadcrumb with multiple segments', () => { - ;(usePathname as jest.Mock).mockReturnValue('/dashboard/users/profile') - - render() + render( + + ) expect(screen.getByText('Home')).toBeInTheDocument() expect(screen.getByText('Dashboard')).toBeInTheDocument() @@ -31,19 +26,30 @@ describe('BreadCrumb', () => { }) test('disables the last segment (non-clickable)', () => { - ;(usePathname as jest.Mock).mockReturnValue('/settings/account') - - render() + render( + + ) const lastSegment = screen.getByText('Account') expect(lastSegment).toBeInTheDocument() - expect(lastSegment).not.toHaveAttribute('href') + expect(lastSegment.closest('a')).toBeNull() }) test('links have correct href attributes', () => { - ;(usePathname as jest.Mock).mockReturnValue('/dashboard/users/profile') - - render() + render( + + ) const homeLink = screen.getByText('Home').closest('a') const dashboardLink = screen.getByText('Dashboard').closest('a') @@ -55,9 +61,14 @@ describe('BreadCrumb', () => { }) test('links have hover styles', () => { - ;(usePathname as jest.Mock).mockReturnValue('/dashboard/users') - - render() + render( + + ) const homeLink = screen.getByText('Home').closest('a') const dashboardLink = screen.getByText('Dashboard').closest('a') diff --git a/frontend/__tests__/unit/pages/CommitteeDetails.test.tsx b/frontend/__tests__/unit/pages/CommitteeDetails.test.tsx index c7c2480ba2..f4d4e933be 100644 --- a/frontend/__tests__/unit/pages/CommitteeDetails.test.tsx +++ b/frontend/__tests__/unit/pages/CommitteeDetails.test.tsx @@ -51,7 +51,8 @@ describe('CommitteeDetailsPage Component', () => { test('renders committee data correctly', async () => { render() await waitFor(() => { - expect(screen.getByText('Test Committee')).toBeInTheDocument() + const title = screen.getByRole('heading', { name: 'Test Committee' }) + expect(title).toBeInTheDocument() }) expect(screen.getByText('This is a test committee summary.')).toBeInTheDocument() expect(screen.getByText('Leader 1')).toBeInTheDocument() diff --git a/frontend/__tests__/unit/pages/OrganizationDetails.test.tsx b/frontend/__tests__/unit/pages/OrganizationDetails.test.tsx index 985c31bf27..059c7be9ba 100644 --- a/frontend/__tests__/unit/pages/OrganizationDetails.test.tsx +++ b/frontend/__tests__/unit/pages/OrganizationDetails.test.tsx @@ -70,7 +70,8 @@ describe('OrganizationDetailsPage', () => { render() await waitFor(() => { - expect(screen.getByText('Test Organization')).toBeInTheDocument() + const title = screen.getByRole('heading', { name: 'Test Organization' }) + expect(title).toBeInTheDocument() }) expect(screen.getByText('@test-org')).toBeInTheDocument() diff --git a/frontend/__tests__/unit/pages/ProjectDetails.test.tsx b/frontend/__tests__/unit/pages/ProjectDetails.test.tsx index 1a191420ee..63d57f3196 100644 --- a/frontend/__tests__/unit/pages/ProjectDetails.test.tsx +++ b/frontend/__tests__/unit/pages/ProjectDetails.test.tsx @@ -68,7 +68,8 @@ describe('ProjectDetailsPage', () => { render() await waitFor(() => { - expect(screen.getByText('Test Project')).toBeInTheDocument() + const title = screen.getByRole('heading', { name: 'Test Project' }) + expect(title).toBeInTheDocument() expect(screen.getByText('Lab')).toBeInTheDocument() }) expect(screen.getByText('2.2K Stars')).toBeInTheDocument() diff --git a/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx b/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx index 4ab52dab27..d2294cd07a 100644 --- a/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx +++ b/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx @@ -68,7 +68,8 @@ describe('RepositoryDetailsPage', () => { render() await waitFor(() => { - expect(screen.getByText('Test Repo')).toBeInTheDocument() + const title = screen.getByRole('heading', { name: 'Test Repo' }) + expect(title).toBeInTheDocument() expect(screen.getByText('MIT')).toBeInTheDocument() }) expect(screen.getByText('50K Stars')).toBeInTheDocument() diff --git a/frontend/__tests__/unit/pages/SnapshotDetails.test.tsx b/frontend/__tests__/unit/pages/SnapshotDetails.test.tsx index e5256e1468..913c23f7a8 100644 --- a/frontend/__tests__/unit/pages/SnapshotDetails.test.tsx +++ b/frontend/__tests__/unit/pages/SnapshotDetails.test.tsx @@ -71,7 +71,8 @@ describe('SnapshotDetailsPage', () => { render() await waitFor(() => { - expect(screen.getByText('New Snapshot')).toBeInTheDocument() + const title = screen.getByRole('heading', { name: 'New Snapshot' }) + expect(title).toBeInTheDocument() }) expect(screen.getByText('New Chapters')).toBeInTheDocument() @@ -145,7 +146,8 @@ describe('SnapshotDetailsPage', () => { render() await waitFor(() => { - expect(screen.getByText('New Snapshot')).toBeInTheDocument() + const title = screen.getByRole('heading', { name: 'New Snapshot' }) + expect(title).toBeInTheDocument() expect(screen.getByText('Latest pre-release')).toBeInTheDocument() }) diff --git a/frontend/__tests__/unit/pages/UserDetails.test.tsx b/frontend/__tests__/unit/pages/UserDetails.test.tsx index 74e641c37c..9a1ab052ee 100644 --- a/frontend/__tests__/unit/pages/UserDetails.test.tsx +++ b/frontend/__tests__/unit/pages/UserDetails.test.tsx @@ -88,7 +88,8 @@ describe('UserDetailsPage', () => { expect(screen.queryByAltText('Loading indicator')).not.toBeInTheDocument() }) - expect(screen.getByText('Test User')).toBeInTheDocument() + const title = screen.getByRole('heading', { name: 'Test User' }) + expect(title).toBeInTheDocument() expect(screen.getByText('Statistics')).toBeInTheDocument() expect(screen.getByText('Contribution Heatmap')).toBeInTheDocument() expect(screen.getByText('Test Company')).toBeInTheDocument() @@ -267,7 +268,7 @@ describe('UserDetailsPage', () => { render() await waitFor(() => { - const userName = screen.getByText('Test User') + const userName = screen.getByRole('heading', { name: 'Test User' }) expect(userName).toBeInTheDocument() }) }) @@ -327,7 +328,8 @@ describe('UserDetailsPage', () => { render() await waitFor(() => { - expect(screen.getByText('Test User')).toBeInTheDocument() + const userName = screen.getByRole('heading', { name: 'Test User' }) + expect(userName).toBeInTheDocument() expect(screen.queryByText('Test @User')).not.toBeInTheDocument() }) }) diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx index 7f105f3067..4a8165e499 100644 --- a/frontend/src/app/about/page.tsx +++ b/frontend/src/app/about/page.tsx @@ -28,6 +28,7 @@ import AnchorTitle from 'components/AnchorTitle' import AnimatedCounter from 'components/AnimatedCounter' import LoadingSpinner from 'components/LoadingSpinner' import Markdown from 'components/MarkdownWrapper' +import PageLayout from 'components/PageLayout' import SecondaryCard from 'components/SecondaryCard' import TopContributors from 'components/TopContributors' import UserCard from 'components/UserCard' @@ -98,143 +99,145 @@ const About = () => { } return ( -
-
-

About

- }> - {aboutText.map((text) => ( -
-
- -
-
- ))} -
- - }> -
- {Object.keys(leaders).map((username) => ( -
- + +
+
+

About

+ }> + {aboutText.map((text) => ( +
+
+ +
))} -
- + - {topContributors && ( - - )} - - }> -
-
- {technologies.map((tech) => ( -
-

{tech.section}

-
    - {Object.entries(tech.tools).map(([name, details]) => ( -
  • - {`${name} - - {name} - -
  • - ))} -
+ }> +
+ {Object.keys(leaders).map((username) => ( +
+
))}
-
- + + + {topContributors && ( + + )} - }> -
- {[...projectMetadata.recentMilestones] - .sort((a, b) => (a.title > b.title ? 1 : -1)) - .map((milestone, index) => ( -
-
- -

- {milestone.title} - 0 - ? 'In Progress' - : 'Not Started' - } - id={`tooltip-state-${index}`} - delay={100} - placement="top" - showArrow - > - - 0 - ? faUserGear - : faClock - } - /> - - -

- -

{milestone.body}

+ }> +
+
+ {technologies.map((tech) => ( +
+

{tech.section}

+
    + {Object.entries(tech.tools).map(([name, details]) => ( +
  • + {`${name} + + {name} + +
  • + ))} +
-
- ))} -
-
+ ))} +
+
+ -
- {[ - { label: 'Forks', value: projectMetadata.forksCount }, - { label: 'Stars', value: projectMetadata.starsCount }, - { label: 'Contributors', value: projectMetadata.contributorsCount }, - { label: 'Open Issues', value: projectMetadata.issuesCount }, - ].map((stat, index) => ( -
- -
- + -
-
{stat.label}
-
+ }> +
+ {[...projectMetadata.recentMilestones] + .sort((a, b) => (a.title > b.title ? 1 : -1)) + .map((milestone, index) => ( +
+
+ +

+ {milestone.title} + 0 + ? 'In Progress' + : 'Not Started' + } + id={`tooltip-state-${index}`} + delay={100} + placement="top" + showArrow + > + + 0 + ? faUserGear + : faClock + } + /> + + +

+ +

{milestone.body}

+
+
+ ))}
- ))} +
+ +
+ {[ + { label: 'Forks', value: projectMetadata.forksCount }, + { label: 'Stars', value: projectMetadata.starsCount }, + { label: 'Contributors', value: projectMetadata.contributorsCount }, + { label: 'Open Issues', value: projectMetadata.issuesCount }, + ].map((stat, index) => ( +
+ +
+ + +
+
{stat.label}
+
+
+ ))} +
-
+ ) } diff --git a/frontend/src/app/chapters/[chapterKey]/page.tsx b/frontend/src/app/chapters/[chapterKey]/page.tsx index 371f31f80a..2b4576a069 100644 --- a/frontend/src/app/chapters/[chapterKey]/page.tsx +++ b/frontend/src/app/chapters/[chapterKey]/page.tsx @@ -10,6 +10,7 @@ import { TopContributorsTypeGraphql } from 'types/contributor' import { formatDate } from 'utils/dateFormatter' import DetailsCard from 'components/CardDetailsPage' import LoadingSpinner from 'components/LoadingSpinner' +import PageLayout from 'components/PageLayout' export default function ChapterDetailsPage() { const { chapterKey } = useParams() @@ -60,15 +61,22 @@ export default function ChapterDetailsPage() { }, ] return ( - + + + ) } diff --git a/frontend/src/app/chapters/page.tsx b/frontend/src/app/chapters/page.tsx index 10118d1413..dc67508085 100644 --- a/frontend/src/app/chapters/page.tsx +++ b/frontend/src/app/chapters/page.tsx @@ -9,6 +9,7 @@ import { ChapterTypeAlgolia } from 'types/chapter' import { getFilteredIcons, handleSocialUrls } from 'utils/utility' import Card from 'components/Card' import ChapterMapWrapper from 'components/ChapterMapWrapper' +import PageLayout from 'components/PageLayout' import SearchPageLayout from 'components/SearchPageLayout' const ChaptersPage = () => { @@ -76,32 +77,34 @@ const ChaptersPage = () => { } return ( - - {chapters.length > 0 && ( - - )} - {chapters && chapters.filter((chapter) => chapter.is_active).map(renderChapterCard)} - + + + {chapters.length > 0 && ( + + )} + {chapters && chapters.filter((chapter) => chapter.is_active).map(renderChapterCard)} + + ) } diff --git a/frontend/src/app/committees/[committeeKey]/page.tsx b/frontend/src/app/committees/[committeeKey]/page.tsx index 9307454d94..8f6335ffd4 100644 --- a/frontend/src/app/committees/[committeeKey]/page.tsx +++ b/frontend/src/app/committees/[committeeKey]/page.tsx @@ -17,6 +17,7 @@ import { TopContributorsTypeGraphql } from 'types/contributor' import { formatDate } from 'utils/dateFormatter' import DetailsCard from 'components/CardDetailsPage' import LoadingSpinner from 'components/LoadingSpinner' +import PageLayout from 'components/PageLayout' export default function CommitteeDetailsPage() { const { committeeKey } = useParams<{ committeeKey: string }>() const [committee, setCommittee] = useState(null) @@ -79,14 +80,21 @@ export default function CommitteeDetailsPage() { ] return ( - + + + ) } diff --git a/frontend/src/app/committees/page.tsx b/frontend/src/app/committees/page.tsx index 0f2dadec6e..df4c79e5f5 100644 --- a/frontend/src/app/committees/page.tsx +++ b/frontend/src/app/committees/page.tsx @@ -5,6 +5,7 @@ import FontAwesomeIconWrapper from 'wrappers/FontAwesomeIconWrapper' import { CommitteeTypeAlgolia } from 'types/committee' import { getFilteredIcons, handleSocialUrls } from 'utils/utility' import Card from 'components/Card' +import PageLayout from 'components/PageLayout' import SearchPageLayout from 'components/SearchPageLayout' const CommitteesPage = () => { @@ -51,19 +52,21 @@ const CommitteesPage = () => { } return ( - - {committees && committees.map(renderCommitteeCard)} - + + + {committees && committees.map(renderCommitteeCard)} + + ) } diff --git a/frontend/src/app/contribute/page.tsx b/frontend/src/app/contribute/page.tsx index b9cee29dd3..0f5a727172 100644 --- a/frontend/src/app/contribute/page.tsx +++ b/frontend/src/app/contribute/page.tsx @@ -9,6 +9,7 @@ import { getFilteredIcons } from 'utils/utility' import Card from 'components/Card' import DialogComp from 'components/Modal' +import PageLayout from 'components/PageLayout' import SearchPageLayout from 'components/SearchPageLayout' const ContributePage = () => { @@ -70,19 +71,21 @@ const ContributePage = () => { } return ( - - {issues && issues.map(renderContributeCard)} - + + + {issues && issues.map(renderContributeCard)} + + ) } diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 5efb2234e4..7d3fccc9f9 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -4,7 +4,6 @@ import { Geist, Geist_Mono } from 'next/font/google' import React from 'react' import { Providers } from 'wrappers/provider' import { GTM_ID } from 'utils/credentials' -import BreadCrumbs from 'components/BreadCrumbs' import Footer from 'components/Footer' import Header from 'components/Header' import ScrollToTop from 'components/ScrollToTop' @@ -68,7 +67,6 @@ export default function RootLayout({ >
- {children}