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

adding feature of anchor links #960

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion frontend/__tests__/e2e/pages/ChapterDetails.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test.describe('Chapter Details Page', () => {
})

test('should have top contributors', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Top Contributors' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Top Contributors' }).first()).toBeVisible()
await expect(page.getByRole('img', { name: 'Contributor 1' })).toBeVisible()
await expect(page.getByText('Contributor 1')).toBeVisible()
await expect(page.getByText('10 Contributions')).toBeVisible()
Expand Down
4 changes: 2 additions & 2 deletions frontend/__tests__/e2e/pages/CommitteeDetails.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ test.describe('Committee Details Page', () => {
})

test('should have committee details block', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Committee Details' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Committee Details' }).first()).toBeVisible()
await expect(page.getByText('Last Updated: Dec 13, 2024')).toBeVisible()
await expect(page.getByText('Leaders: Leader 1, Leader 2')).toBeVisible()
await expect(page.getByRole('link', { name: 'https://owasp.org/test-committee' })).toBeVisible()
})

test('should have top contributors', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Top Contributors' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Top Contributors' }).first()).toBeVisible()
await expect(page.getByRole('img', { name: 'Contributor 1' })).toBeVisible()
await expect(page.getByText('Contributor 1')).toBeVisible()
await expect(page.getByText('2157 Contributions')).toBeVisible()
Expand Down
12 changes: 6 additions & 6 deletions frontend/__tests__/e2e/pages/Home.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,37 +22,37 @@ test.describe('Home Page', () => {
})

test('should have new chapters', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'New Chapters' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'New Chapters' }).first()).toBeVisible()
await expect(page.getByRole('link', { name: 'chapter 1' })).toBeVisible()
await expect(page.getByText('Feb 20,').first()).toBeVisible()
await page.getByRole('link', { name: 'chapter 1' }).click()
expect(page.url()).toContain('chapters/chapter-1')
})
test('should have new projects', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'New Projects' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'New Projects' }).first()).toBeVisible()
await expect(page.getByRole('link', { name: 'Project 1' })).toBeVisible()
await expect(page.getByText('Jan 1,').first()).toBeVisible()
await page.getByRole('link', { name: 'Project 1' }).click()
expect(page.url()).toContain('projects/project-1')
})

test('should have top contributors', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Top Contributors' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Top Contributors' }).first()).toBeVisible()
await expect(page.getByRole('img', { name: 'Contributor 1' })).toBeVisible()
await expect(page.getByRole('link', { name: 'Project 21' })).toBeVisible()
await page.getByText('Contributor 1').click()
expect(page.url()).toContain('community/users/contributor1')
})

test('should have recent issues', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Issues' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Recent Issues' }).first()).toBeVisible()
await expect(page.getByRole('heading', { name: 'Issue 1' })).toBeVisible()
await expect(page.getByText('Feb 24,').first()).toBeVisible()
await expect(page.getByText('5 comments')).toBeVisible()
})

test('should have recent Releases', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Releases' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Recent Releases' }).first()).toBeVisible()
await expect(page.getByRole('heading', { name: 'Release 1' })).toBeVisible()
await expect(page.getByText('Feb 22,')).toBeVisible()
await expect(page.getByText('v1', { exact: true })).toBeVisible()
Expand All @@ -69,7 +69,7 @@ test.describe('Home Page', () => {
})

test('should have upcoming events', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Upcoming Events' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Upcoming Events' }).first()).toBeVisible()
await expect(page.getByRole('link', { name: 'Event 1' })).toBeVisible()
await expect(page.getByText('Feb 27 — 28, 2025')).toBeVisible()
await page.getByRole('link', { name: 'Event 1' }).click()
Expand Down
14 changes: 7 additions & 7 deletions frontend/__tests__/e2e/pages/ProjectDetails.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test.describe('Project Details Page', () => {
})

test('should have project details block', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Project Details' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Project Details' }).first()).toBeVisible()
await expect(page.getByText('Last Updated: Feb 7, 2025')).toBeVisible()
await expect(page.getByText('Level: Lab')).toBeVisible()
await expect(page.getByText('Leaders: alice, bob')).toBeVisible()
Expand All @@ -36,21 +36,21 @@ test.describe('Project Details Page', () => {
})

test('should have project topics', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Topics' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Topics' }).first()).toBeVisible()
await expect(page.getByText('graphql', { exact: true })).toBeVisible()
await expect(page.getByText('django', { exact: true })).toBeVisible()
await expect(page.getByText('backend')).toBeVisible()
})

test('should have project languages', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Languages' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Languages' }).first()).toBeVisible()
await expect(page.getByText('Python', { exact: true })).toBeVisible()
await expect(page.getByText('GraphQL', { exact: true })).toBeVisible()
await expect(page.getByText('JavaScript', { exact: true })).toBeVisible()
})

test('should have top contributors', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Top Contributors' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Top Contributors' }).first()).toBeVisible()
await expect(page.getByRole('img', { name: 'Contributor 1' })).toBeVisible()
await expect(page.getByText('Contributor 1')).toBeVisible()
await expect(page.getByText('30 Contributions')).toBeVisible()
Expand All @@ -68,22 +68,22 @@ test.describe('Project Details Page', () => {
})

test('should have project recent issues', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Issues' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Recent Issues' }).first()).toBeVisible()
await expect(page.getByRole('heading', { name: 'Fix authentication bug' })).toBeVisible()
await expect(page.getByRole('img', { name: 'Dave Debugger' })).toBeVisible()
await expect(page.getByText('Feb 5, 2025')).toBeVisible()
await expect(page.getByText('5 comments')).toBeVisible()
})

test('should have project recent releases', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Releases' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Recent Releases' }).first()).toBeVisible()
await expect(page.getByRole('heading', { name: 'V1.2.0' })).toBeVisible()
await expect(page.getByRole('img', { name: 'Charlie Dev' })).toBeVisible()
await expect(page.getByText('Jan 20, 2025')).toBeVisible()
})

test('should have project repositories', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Repositories' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Repositories' }).first()).toBeVisible()
await expect(page.getByText('Repo One')).toBeVisible()
await expect(page.getByText('Stars95')).toBeVisible()
await expect(page.getByText('Forks12')).toBeVisible()
Expand Down
12 changes: 6 additions & 6 deletions frontend/__tests__/e2e/pages/RepositoryDetails.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ test.describe('Repository Details Page', () => {
})

test('should have repository details block', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Repository Details' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Repository Details' }).first()).toBeVisible()
await expect(page.getByText('Last Updated: Jan 1, 2024')).toBeVisible()
await expect(page.getByText('License: MIT')).toBeVisible()
await expect(page.getByText('Size: 1200 KB')).toBeVisible()
Expand All @@ -34,18 +34,18 @@ test.describe('Repository Details Page', () => {
})

test('should have topics', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Topics' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Topics' }).first()).toBeVisible()
await expect(page.getByText('JavaScript', { exact: true })).toBeVisible()
await expect(page.getByText('TypeScript', { exact: true })).toBeVisible()
})

test('should have languages', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Languages' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Languages' }).first()).toBeVisible()
await expect(page.getByText('web', { exact: true })).toBeVisible()
await expect(page.getByText('security', { exact: true })).toBeVisible()
})
test('should have top contributors', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Top Contributors' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Top Contributors' }).first()).toBeVisible()
await expect(page.getByRole('img', { name: 'Contributor 1' })).toBeVisible()
await expect(page.getByText('Contributor 1')).toBeVisible()
await expect(page.getByText('30 Contributions')).toBeVisible()
Expand All @@ -63,15 +63,15 @@ test.describe('Repository Details Page', () => {
})

test('should have recent issues', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Issues' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Recent Issues' }).first()).toBeVisible()
await expect(page.getByRole('heading', { name: 'Bug fix required' })).toBeVisible()
await expect(page.getByRole('img', { name: 'Test User 1' })).toBeVisible()
await expect(page.getByText('Jan 2, 2024')).toBeVisible()
await expect(page.getByText('4 comments')).toBeVisible()
})

test('should have recent releases', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Releases' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Recent Releases' }).first()).toBeVisible()
await expect(page.getByRole('heading', { name: 'v1.0.0' })).toBeVisible()
await expect(page.getByRole('img', { name: 'Test User 2' })).toBeVisible()
await expect(page.getByText('Jan 1, 2024', { exact: true })).toBeVisible()
Expand Down
4 changes: 2 additions & 2 deletions frontend/__tests__/e2e/pages/UserDetails.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ test.describe('User Details Page', () => {
})

test('should have recent issues', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Issues' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Recent Issues' }).first()).toBeVisible()
await expect(page.getByRole('link', { name: 'Test Issue' })).toBeVisible()
await expect(page.getByText('8/7/2024').first()).toBeVisible()
})

test('should have recent releases', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Recent Releases' })).toBeVisible()
await expect(page.getByRole('heading', { name: 'Recent Releases' }).first()).toBeVisible()
await expect(page.getByRole('link', { name: 'v1.0.0' })).toBeVisible()
await expect(page.getByRole('link', { name: 'testuser/test-repo' }).nth(1)).toBeVisible()
await expect(page.getByText('8/7/2024').first()).toBeVisible()
Expand Down
2 changes: 1 addition & 1 deletion frontend/__tests__/unit/pages/ProjectDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ describe('ProjectDetailsPage', () => {

const contributorsSection = screen
.getByRole('heading', { name: /Top Contributors/i })
.closest('div')
.closest('div.mb-8') as HTMLElement
const showMoreButton = within(contributorsSection!).getByRole('button', { name: /Show more/i })
fireEvent.click(showMoreButton)

Expand Down
2 changes: 1 addition & 1 deletion frontend/__tests__/unit/pages/RepositoryDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ describe('RepositoryDetailsPage', () => {

const contributorsSection = screen
.getByRole('heading', { name: /Top Contributors/i })
.closest('div')
.closest('div.mb-8') as HTMLElement
const showMoreButton = within(contributorsSection!).getByRole('button', { name: /Show more/i })
fireEvent.click(showMoreButton)

Expand Down
37 changes: 37 additions & 0 deletions frontend/src/components/AnchorTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { faLink } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import React from 'react'

interface AnchorTitleProps {
href: string
title: string
}

const AnchorTitle: React.FC<AnchorTitleProps> = ({ href, title }) => {
const id = href.replace('#', '')

const handleClick = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
const element = document.getElementById(id)
if (element) {
event.preventDefault()
element.scrollIntoView({ behavior: 'smooth' })
}
}

return (
<div id={id} className="relative scroll-mt-20">
<div className="items-top group relative flex">
<h2 className="mb-4 text-2xl font-semibold">{title}</h2>
<a
href={href}
className="inherit-color ml-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100"
onClick={handleClick}
>
<FontAwesomeIcon icon={faLink} className="custom-icon h-7 w-5" />
</a>
</div>
</div>
)
}

export default AnchorTitle
38 changes: 30 additions & 8 deletions frontend/src/components/CardDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { DetailsCardProps } from 'types/card'
import { formatDate } from 'utils/dateFormatter'
import { getSocialIcon } from 'utils/urlIconMappings'
import AnchorTitle from 'components/AnchorTitle'
import ChapterMap from 'components/ChapterMap'
import InfoBlock from 'components/InfoBlock'
import ItemCardList from 'components/ItemCardList'
Expand Down Expand Up @@ -38,12 +39,17 @@ const DetailsCard = ({
{!is_active && (
<span className="ml-2 rounded bg-red-200 px-2 py-1 text-sm text-red-800">Inactive</span>
)}
<SecondaryCard title="Summary">
<SecondaryCard title={<AnchorTitle href="#summary" title="Summary" />}>
<p>{summary}</p>
</SecondaryCard>
<div className="grid grid-cols-1 gap-6 md:grid-cols-7">
<SecondaryCard
title={`${type[0].toUpperCase() + type.slice(1)} Details`}
title={
<AnchorTitle
href={`#${type}-details`}
title={`${type[0].toUpperCase() + type.slice(1)} Details`}
/>
}
className={`${type !== 'chapter' ? 'md:col-span-5' : 'md:col-span-3'} gap-2`}
>
{details &&
Expand All @@ -57,7 +63,10 @@ const DetailsCard = ({
)}
</SecondaryCard>
{(type === 'project' || type === 'repository' || type === 'committee') && (
<SecondaryCard title="Statistics" className="md:col-span-2">
<SecondaryCard
title={<AnchorTitle href="#statistics" title="Statistics" />}
className="md:col-span-2"
>
{stats.map((stat, index) => (
<InfoBlock key={index} className="pb-1" icon={stat.icon} value={stat.value} />
))}
Expand All @@ -82,15 +91,25 @@ const DetailsCard = ({
<div
className={`mb-8 grid grid-cols-1 gap-6 ${topics.length === 0 || languages.length === 0 ? 'md:col-span-1' : 'md:grid-cols-2'}`}
>
{languages.length !== 0 && <ToggleableList items={languages} label="Languages" />}
{topics.length !== 0 && <ToggleableList items={topics} label="Topics" />}
{languages.length !== 0 && (
<ToggleableList
items={languages}
label={<AnchorTitle href="#languages" title="Languages" />}
/>
)}
{topics.length !== 0 && (
<ToggleableList
items={topics}
label={<AnchorTitle href="#topics" title="Topics" />}
/>
)}
</div>
)}
<TopContributors contributors={topContributors} maxInitialDisplay={6} />
{(type === 'project' || type === 'repository') && (
<>
<ItemCardList
title="Recent Issues"
title={<AnchorTitle href="#recent-issues" title="Recent Issues" />}
data={recentIssues}
renderDetails={(item) => (
<div className="mt-2 flex items-center text-sm text-gray-600 dark:text-gray-400">
Expand All @@ -106,7 +125,7 @@ const DetailsCard = ({
)}
/>
<ItemCardList
title="Recent Releases"
title={<AnchorTitle href="#recent-releases" title="Recent Releases" />}
data={recentReleases}
renderDetails={(item) => (
<div className="mt-2 flex items-center text-sm text-gray-600 dark:text-gray-400">
Expand All @@ -120,7 +139,10 @@ const DetailsCard = ({
</>
)}
{type === 'project' && repositories.length > 0 && (
<SecondaryCard title="Repositories" className="mt-6">
<SecondaryCard
title={<AnchorTitle href="#repositories" title="Repositories" />}
className="mt-6"
>
<RepositoriesCard repositories={repositories} />
</SecondaryCard>
)}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/ItemCardList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { JSX } from 'react'
import React, { JSX } from 'react'
import { ProjectIssuesType, ProjectReleaseType } from 'types/project'
import SecondaryCard from './SecondaryCard'

Expand All @@ -7,7 +7,7 @@ const ItemCardList = ({
data,
renderDetails,
}: {
title: string
title: React.ReactNode
data: ProjectReleaseType[] | ProjectIssuesType[]
renderDetails: (item: {
createdAt: string
Expand Down Expand Up @@ -49,7 +49,7 @@ const ItemCardList = ({
))}
</div>
) : (
<p>No {title.toLowerCase()}.</p>
<p> Nothing to display.</p>
)}
</SecondaryCard>
)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/SecondaryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const SecondaryCard = ({
children = null,
className = '',
}: {
title?: string
title?: React.ReactNode
children?: React.ReactNode
className?: string
} = {}) => (
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/ToggleContributors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { TopContributorsTypeGraphql } from 'types/contributor'
import AnchorTitle from 'components/AnchorTitle'

const TopContributors = ({
contributors,
label = 'Top Contributors',
Expand All @@ -29,7 +31,7 @@ const TopContributors = ({
}
return (
<div className={`mb-8 rounded-lg bg-gray-100 p-6 shadow-md dark:bg-gray-800 ${className}`}>
<h2 className="mb-4 text-2xl font-semibold">{label}</h2>
<AnchorTitle href="#top-contributors" title={label} />
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3">
{displayContributors.map((contributor, index) => (
<div
Expand Down
Loading