Skip to content

adding feature of anchor links #960

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

Merged
merged 47 commits into from
Apr 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
629f0e4
adding feature of anchor links
shining-bluemoon-11 Feb 28, 2025
c8f5865
fix update
shining-bluemoon-11 Feb 28, 2025
858bd9a
resolves conflicts
shining-bluemoon-11 Feb 28, 2025
39af270
Merge branch 'main' into feature/add-anchor-links
kasya Mar 1, 2025
79e372f
Merge branch 'main' into feature/add-anchor-links
kasya Mar 2, 2025
f1de9aa
update asked changes
shining-bluemoon-11 Mar 4, 2025
daf3979
update fix
shining-bluemoon-11 Mar 4, 2025
04dd54c
fix checks
shining-bluemoon-11 Mar 4, 2025
4f40573
remove unwanted file
shining-bluemoon-11 Mar 4, 2025
0127c28
fix checks
shining-bluemoon-11 Mar 4, 2025
e42db29
update fix
shining-bluemoon-11 Mar 4, 2025
63e5c44
fix
shining-bluemoon-11 Mar 4, 2025
b2c8854
update fix
shining-bluemoon-11 Mar 5, 2025
a58ff4e
resolve conversation
shining-bluemoon-11 Mar 6, 2025
b493f9f
Merge branch 'main' into feature/add-anchor-links
shining-bluemoon-11 Mar 6, 2025
9a8e6e1
Merge branch 'main' into feature/add-anchor-links
kasya Mar 7, 2025
4ae3100
resolve updates
shining-bluemoon-11 Mar 9, 2025
ccb5315
resolve
shining-bluemoon-11 Mar 9, 2025
0f22366
fix checks
shining-bluemoon-11 Mar 9, 2025
6c9d866
fix required updates
shining-bluemoon-11 Mar 10, 2025
d8a2f5c
Merge branch 'main' into feature/add-anchor-links
kasya Mar 11, 2025
9142ec3
final fix
shining-bluemoon-11 Mar 12, 2025
57f3ace
resolve fix
shining-bluemoon-11 Mar 12, 2025
719b826
Merge branch 'main' into feature/add-anchor-links
shining-bluemoon-11 Mar 13, 2025
846bf78
fix
shining-bluemoon-11 Mar 13, 2025
dc40bb4
fix conversations update
shining-bluemoon-11 Mar 21, 2025
53ba80b
Merge branch 'main' into feature/add-anchor-links
shining-bluemoon-11 Mar 21, 2025
11f99cd
update fix
shining-bluemoon-11 Mar 23, 2025
b770ccb
update
shining-bluemoon-11 Mar 23, 2025
fb9d1a8
resolve conflict
shining-bluemoon-11 Mar 30, 2025
ccbca51
Merge branch 'main' into feature/add-anchor-links
kasya Mar 31, 2025
3d8b058
fix conversations
shining-bluemoon-11 Mar 31, 2025
3d4e484
fix
shining-bluemoon-11 Mar 31, 2025
cab4472
resolve
shining-bluemoon-11 Mar 31, 2025
3e36d0b
resolve update
shining-bluemoon-11 Apr 5, 2025
627e034
Merge branch 'main' into feature/add-anchor-links
kasya Apr 6, 2025
28684f3
resolve
shining-bluemoon-11 Apr 12, 2025
7b49504
fix
shining-bluemoon-11 Apr 12, 2025
75457c4
fix
shining-bluemoon-11 Apr 12, 2025
599c438
fix e2e test
shining-bluemoon-11 Apr 12, 2025
6fce824
Merge branch 'main' into feature/add-anchor-links
kasya Apr 21, 2025
12291e9
Fix icon/title alignments. Resolve conflicts
kasya Apr 21, 2025
89ea621
Fix tests
kasya Apr 21, 2025
9c8d215
Fix tests
kasya Apr 21, 2025
8529ed9
Update code
arkid15r Apr 21, 2025
ebc6a64
Merge branch 'main' into feature/add-anchor-links
arkid15r Apr 21, 2025
ea80a3a
Merge branch 'main' into feature/add-anchor-links
arkid15r Apr 21, 2025
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
1 change: 1 addition & 0 deletions frontend/__tests__/e2e/pages/Home.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ test.describe('Home Page', () => {
await page.getByRole('link', { name: 'Chapter 1' }).click()
await expect(page).toHaveURL('chapters/chapter_1')
})

test('should have new projects', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'New Projects' })).toBeVisible()
await expect(page.getByRole('link', { name: 'Project 1', exact: true })).toBeVisible()
Expand Down
1 change: 1 addition & 0 deletions frontend/__tests__/e2e/pages/RepositoryDetails.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ test.describe('Repository Details Page', () => {
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('img', { name: 'Contributor 1' })).toBeVisible()
Expand Down
7 changes: 2 additions & 5 deletions frontend/__tests__/unit/pages/About.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,18 +197,15 @@ describe('About Component', () => {
expect(screen.queryByText('Contributor 10')).not.toBeInTheDocument()
})

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

await waitFor(() => {
expect(screen.getByText('Contributor 7')).toBeInTheDocument()
expect(screen.getByText('Contributor 8')).toBeInTheDocument()
})

const showLessButton = within(contributorsSection!).getByRole('button', { name: /Show less/i })
const showLessButton = screen.getByRole('button', { name: /Show less/i })
fireEvent.click(showLessButton)

await waitFor(() => {
Expand Down
7 changes: 2 additions & 5 deletions frontend/__tests__/unit/pages/ProjectDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,15 @@ describe('ProjectDetailsPage', () => {
expect(screen.queryByText('Contributor 10')).not.toBeInTheDocument()
})

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

await waitFor(() => {
expect(screen.getByText('Contributor 7')).toBeInTheDocument()
expect(screen.getByText('Contributor 8')).toBeInTheDocument()
})

const showLessButton = within(contributorsSection!).getByRole('button', { name: /Show less/i })
const showLessButton = screen.getByRole('button', { name: /Show less/i })
fireEvent.click(showLessButton)

await waitFor(() => {
Expand Down
9 changes: 3 additions & 6 deletions frontend/__tests__/unit/pages/RepositoryDetails.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useQuery } from '@apollo/client'
import { addToast } from '@heroui/toast'
import { act, fireEvent, screen, waitFor, within } from '@testing-library/react'
import { act, fireEvent, screen, waitFor } from '@testing-library/react'
import { mockRepositoryData } from '@unit/data/mockRepositoryData'
import { render } from 'wrappers/testUtil'
import RepositoryDetailsPage from 'app/organizations/[organizationKey]/repositories/[repositoryKey]/page'
Expand Down Expand Up @@ -105,18 +105,15 @@ describe('RepositoryDetailsPage', () => {
expect(screen.queryByText('Contributor 10')).not.toBeInTheDocument()
})

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

await waitFor(() => {
expect(screen.getByText('Contributor 7')).toBeInTheDocument()
expect(screen.getByText('Contributor 8')).toBeInTheDocument()
})

const showLessButton = within(contributorsSection!).getByRole('button', { name: /Show less/i })
const showLessButton = screen.getByRole('button', { name: /Show less/i })
fireEvent.click(showLessButton)

await waitFor(() => {
Expand Down
18 changes: 18 additions & 0 deletions frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,24 @@ a {
opacity: 0.6;
}

html {
scroll-behavior: smooth;
}

.inherit-color {
color: inherit;
}

.custom-icon {
display: inline-flex;
align-items: center;
vertical-align: middle;
}

section {
scroll-margin-top: 100px;
}

/* Dropdown container */
.dropdown {
@apply relative;
Expand Down
87 changes: 73 additions & 14 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { EventType } from 'types/event'
import { MainPageData } from 'types/home'
import { capitalize } from 'utils/capitalize'
import { formatDate, formatDateRange } from 'utils/dateFormatter'
import AnchorTitle from 'components/AnchorTitle'
import AnimatedCounter from 'components/AnimatedCounter'
import ChapterMapWrapper from 'components/ChapterMapWrapper'
import LeadersList from 'components/LeadersList'
Expand Down Expand Up @@ -147,7 +148,19 @@ export default function Home() {
/>
</div>
</div>
<SecondaryCard icon={faCalendarAlt} title="Upcoming Events" className="overflow-hidden">
<SecondaryCard
icon={faCalendarAlt}
title={
<div className="flex items-center gap-2">
<AnchorTitle
href="#upcoming-events"
title="Upcoming Events"
className="flex items-center leading-none"
/>
</div>
}
className="overflow-hidden"
>
<div className="grid gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
{data?.upcomingEvents?.map((event: EventType, index: number) => (
<div key={`card-${event.name}`} className="overflow-hidden">
Expand Down Expand Up @@ -185,7 +198,19 @@ export default function Home() {
</div>
</SecondaryCard>
<div className="grid gap-4 md:grid-cols-2">
<SecondaryCard icon={faMapMarkerAlt} title="New Chapters" className="overflow-hidden">
<SecondaryCard
icon={faMapMarkerAlt}
title={
<div className="flex items-center gap-2">
<AnchorTitle
href="#new-chapters"
title="New Chapters"
className="flex items-center leading-none"
/>
</div>
}
className="overflow-hidden"
>
<div className="space-y-4">
{data?.recentChapters?.map((chapter) => (
<div key={chapter.key} className="rounded-lg bg-gray-200 p-4 dark:bg-gray-700">
Expand Down Expand Up @@ -219,7 +244,19 @@ export default function Home() {
))}
</div>
</SecondaryCard>
<SecondaryCard icon={faFolder} title="New Projects" className="overflow-hidden">
<SecondaryCard
icon={faFolder}
title={
<div className="flex items-center gap-2">
<AnchorTitle
href="#new-projects"
title="New Projects"
className="flex items-center leading-none"
/>
</div>
}
className="overflow-hidden"
>
<div className="space-y-4">
{data?.recentProjects?.map((project) => (
<div key={project.key} className="rounded-lg bg-gray-200 p-4 dark:bg-gray-700">
Expand Down Expand Up @@ -254,10 +291,18 @@ export default function Home() {
</SecondaryCard>
</div>
<div className="mb-20">
<h2 className="mb-4 text-2xl font-semibold">
<FontAwesomeIcon icon={faGlobe} className="mr-2 h-5 w-5" />
Chapters Worldwide
</h2>
<div className="mb-4 flex items-center gap-2">
<FontAwesomeIcon
icon={faGlobe}
className="h-5 w-5"
style={{ verticalAlign: 'middle' }}
/>
<AnchorTitle
href="#chapters-worldwide"
title="Chapters Worldwide"
className="flex items-center leading-none"
/>
</div>
<ChapterMapWrapper
geoLocData={geoLocData}
showLocal={false}
Expand All @@ -281,7 +326,19 @@ export default function Home() {
<RecentPullRequests data={data?.recentPullRequests} />
</div>
<RecentReleases data={data?.recentReleases} />
<SecondaryCard icon={faNewspaper} title="News & Opinions" className="overflow-hidden">
<SecondaryCard
icon={faNewspaper}
title={
<div className="flex items-center gap-2">
<AnchorTitle
href="#news-&-opinions"
title="News & Opinions"
className="flex items-center leading-none"
/>
</div>
}
className="overflow-hidden"
>
<div className="grid gap-4 sm:grid-cols-1 md:grid-cols-2">
{data?.recentPosts.map((post) => (
<div
Expand Down Expand Up @@ -314,12 +371,14 @@ export default function Home() {
</SecondaryCard>
<div className="grid gap-6 md:grid-cols-4">
{counterData.map((stat, index) => (
<SecondaryCard key={index} className="text-center">
<div className="mb-2 text-3xl font-bold text-blue-400">
<AnimatedCounter end={parseInt(stat.value)} duration={2} />+
</div>
<div className="text-gray-600 dark:text-gray-400">{stat.label}</div>
</SecondaryCard>
<div key={index}>
<SecondaryCard className="text-center">
<div className="mb-2 text-3xl font-bold text-blue-400">
<AnimatedCounter end={parseInt(stat.value)} duration={2} />+
</div>
<div className="text-gray-600 dark:text-gray-400">{stat.label}</div>
</SecondaryCard>
</div>
))}
</div>

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

interface AnchorTitleProps {
href: string
title: string
className?: string
}

const AnchorTitle: React.FC<AnchorTitleProps> = ({ href, title }) => {
const id = href.replace('#', '') // TODO(arkid15r): refactor get href from title automatically.

const scrollToElement = useCallback(() => {
const element = document.getElementById(id)
if (element) {
const headingHeight =
(element.querySelector('div#anchor-title') as HTMLElement)?.offsetHeight || 0
const yOffset = -headingHeight - 50
const y = element.getBoundingClientRect().top + window.pageYOffset + yOffset
window.scrollTo({ top: y, behavior: 'smooth' })
}
}, [id])

const handleClick = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
event.preventDefault()
scrollToElement()
window.history.pushState(null, '', href)
}

useEffect(() => {
const hash = window.location.hash.replace('#', '')
if (hash === id) {
requestAnimationFrame(() => scrollToElement())
}
}, [id, scrollToElement])

useEffect(() => {
const handlePopState = () => {
const hash = window.location.hash.replace('#', '')
if (hash === id) {
requestAnimationFrame(() => scrollToElement())
}
}
window.addEventListener('popstate', handlePopState)
return () => window.removeEventListener('popstate', handlePopState)
}, [id, scrollToElement])

return (
<div id={id} className="relative">
<div className="group relative flex items-center">
<div className="flex items-center text-2xl font-semibold" id="anchor-title">
{title}
</div>
<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
Loading