Skip to content
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
8 changes: 7 additions & 1 deletion frontend/__tests__/unit/components/RecentIssues.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,13 @@ describe('<RecentIssues />', () => {
})

it('renders with missing props gracefully', () => {
render(<RecentIssues data={[{} as Issue]} showAvatar={false} />)
const minimalIssue: Issue = {
createdAt: 1704067200000,
title: '',
url: '',
objectID: 'minimal-issue',
}
render(<RecentIssues data={[minimalIssue]} showAvatar={false} />)
expect(screen.getByText('Recent Issues')).toBeInTheDocument()
})

Expand Down
5 changes: 3 additions & 2 deletions frontend/__tests__/unit/pages/ChapterDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ describe('chapterDetailsPage Component', () => {
...mockChapterDetailsData,
topContributors: [
{
name: 'Contributor 1',
login: 'contributor1',
name: '',
avatarUrl: 'https://example.com/avatar1.jpg',
},
],
Expand All @@ -116,7 +117,7 @@ describe('chapterDetailsPage Component', () => {
render(<ChapterDetailsPage />)

await waitFor(() => {
expect(screen.getByText('Contributor 1')).toBeInTheDocument()
expect(screen.getByText('Contributor1')).toBeInTheDocument()
})
})
test('renders chapter sponsor block correctly', async () => {
Expand Down
8 changes: 6 additions & 2 deletions frontend/__tests__/unit/pages/Header.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,12 @@ jest.mock('components/NavDropDown', () => {
return (
<div data-testid="nav-dropdown">
{link.text}
{link.submenu?.map((sub: { href: string; text: string }, i: number) => (
<a key={i} href={sub.href} className={pathname === sub.href ? 'active' : ''}>
{link.submenu?.map((sub: { href: string; text: string }) => (
<a
key={`${sub.text}-${sub.href}`}
href={sub.href}
className={pathname === sub.href ? 'active' : ''}
>
{sub.text}
</a>
))}
Expand Down
54 changes: 32 additions & 22 deletions frontend/src/app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,26 +51,24 @@ const leaders = {
const projectKey = 'nest'

const About = () => {
const {
data: projectMetadataResponse,
error: projectMetadataRequestError,
loading: projectMetadataLoading,
} = useQuery(GetProjectMetadataDocument, {
variables: { key: projectKey },
})
const { data: projectMetadataResponse, error: projectMetadataRequestError } = useQuery(
GetProjectMetadataDocument,
{
variables: { key: projectKey },
}
)

const {
data: topContributorsResponse,
error: topContributorsRequestError,
loading: topContributorsLoading,
} = useQuery(GetTopContributorsDocument, {
variables: {
excludedUsernames: Object.keys(leaders),
hasFullName: true,
key: projectKey,
limit: 24,
},
})
const { data: topContributorsResponse, error: topContributorsRequestError } = useQuery(
GetTopContributorsDocument,
{
variables: {
excludedUsernames: Object.keys(leaders),
hasFullName: true,
key: projectKey,
limit: 24,
},
}
)

const { leadersData, isLoading: leadersLoading } = useLeadersData()

Expand All @@ -97,7 +95,12 @@ const About = () => {
}
}, [topContributorsResponse, topContributorsRequestError])

const isLoading = projectMetadataLoading || topContributorsLoading || leadersLoading
const isLoading =
!projectMetadataResponse ||
!topContributorsResponse ||
(projectMetadataRequestError && !projectMetadata) ||
(topContributorsRequestError && !topContributors) ||
leadersLoading

if (isLoading) {
return <LoadingSpinner />
Expand Down Expand Up @@ -251,8 +254,15 @@ const About = () => {
</SecondaryCard>
)}
<SecondaryCard icon={faScroll} title={<AnchorTitle title="Our Story" />}>
{projectStory.map((text, index) => (
<div key={`story-${index}`} className="mb-4">
{projectStory.map((text) => (
<div
key={text
.slice(0, 40)
.trim()
.replaceAll(' ', '-')
.replaceAll(/[^\w-]/g, '')}
className="mb-4"
>
<div>
<Markdown content={text} />
</div>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/members/[memberKey]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ const UserDetailsPage: React.FC = () => {
const username = mentionMatch[1]
const punctuation = mentionMatch[2] || ''
return (
<React.Fragment key={index}>
<React.Fragment key={`mention-${username}-${index}`}>
<Link
href={`https://github.com/${username}`}
target="_blank"
Expand All @@ -96,7 +96,7 @@ const UserDetailsPage: React.FC = () => {
</React.Fragment>
)
}
return <span key={index}>{word} </span>
return <span key={`word-${word}-${index}`}>{word} </span>
})

if (isLoading) {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -357,8 +357,8 @@ export default function Home() {
</div>
</SecondaryCard>
<div className="grid gap-6 lg:grid-cols-5">
{counterData.map((stat, index) => (
<div key={index}>
{counterData.map((stat) => (
<div key={stat.label}>
<SecondaryCard className="text-center">
<div className="mb-2 text-3xl font-bold text-blue-400">
<AnimatedCounter end={stat.value} duration={2} />+
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/CardDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,8 @@ const DetailsCard = ({
title={<AnchorTitle title="Statistics" />}
className="md:col-span-2"
>
{stats.map((stat, index) => (
<div key={index}>
{stats.map((stat) => (
<div key={`${stat.unit}-${stat.value}`}>
<InfoBlock
className="pb-1"
icon={stat.icon}
Expand Down Expand Up @@ -313,9 +313,9 @@ export const SocialLinks = ({ urls }) => {
<div>
<strong>Social Links</strong>
<div className="mt-2 flex flex-wrap gap-3">
{urls.map((url, index) => (
{urls.map((url) => (
<a
key={index}
key={url}
href={url}
target="_blank"
rel="noopener noreferrer"
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ export default function Footer() {
openSection === section.title ? 'max-h-96' : 'max-h-0 lg:max-h-full'
}`}
>
{section.links.map((link, index) => (
<div key={index} className="py-1">
{section.links.map((link) => (
<div key={link.href || `span-${link.text}`} className="py-1">
{link.isSpan ? (
<span className="text-slate-600 dark:text-slate-400">{link.text}</span>
) : (
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthE
}
return true
})
.map((link, i) => {
.map((link) => {
return link.submenu ? (
<NavDropdown link={link} pathname={pathname} key={i} />
<NavDropdown link={link} pathname={pathname} key={`${link.text}-${link.href}`} />
) : (
<Link
key={link.text}
Expand Down Expand Up @@ -193,9 +193,9 @@ export default function Header({ isGitHubAuthEnabled }: { readonly isGitHubAuthE
{link.text}
</div>
<div className="ml-4">
{link.submenu.map((sub, i) => (
{link.submenu.map((sub) => (
<Link
key={i}
key={`${sub.text}-${sub.href}`}
href={sub.href || '/'}
className={cn(
'block w-full px-4 py-3 text-left text-sm text-slate-700 transition duration-150 ease-in-out first:rounded-t-md last:rounded-b-md hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-700 dark:hover:text-white',
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/components/ItemCardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ const ItemCardList = ({
className={`grid ${showSingleColumn ? 'grid-cols-1' : 'gap-4 gap-y-0 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3'}`}
>
{data.map((item, index) => (
<div key={index} className="mb-4 w-full rounded-lg bg-gray-200 p-4 dark:bg-gray-700">
<div
key={item.objectID || `${item.repositoryName}-${item.title || item.name}-${item.url}`}
className="mb-4 w-full rounded-lg bg-gray-200 p-4 dark:bg-gray-700"
>
<div className="flex w-full flex-col justify-between">
<div className="flex w-full items-center">
{showAvatar && (
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/MultiSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ const MultiSearchBar: React.FC<MultiSearchBarProps> = ({
<ul>
{suggestion.hits.map((hit, subIndex) => (
<li
key={subIndex}
key={`${hit.key || hit.login || hit.url}-${subIndex}`}
className={`flex items-center px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 ${
highlightedIndex &&
highlightedIndex.index === index &&
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/NavDropDown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export default function NavDropdown({ link, pathname }: NavDropDownProps) {
>
{link.submenu.map((submenu, idx) => (
<Link
key={idx}
key={`${submenu.href}-${idx}`}
href={submenu.href || '/'}
className={cn(
'block w-full px-4 py-2 text-left text-sm transition-colors',
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const Pagination: React.FC<PaginationProps> = ({
Prev
</Button>
{pageNumbers.map((number, index) => (
<React.Fragment key={index}>
<React.Fragment key={`${number}-${index}`}>
{number === '...' ? (
<span
className="flex h-10 w-10 items-center justify-center text-gray-600 dark:text-gray-400"
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/RecentReleases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ const RecentReleases: React.FC<RecentReleasesProps> = ({
<div
className={`grid ${showSingleColumn ? 'grid-cols-1' : 'gap-4 gap-y-0 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3'}`}
>
{data.map((item, index) => (
<Release key={index} release={item} showAvatar={showAvatar} index={index} />
{data.map((item) => (
<Release
key={`${item.repositoryName}-${item.tagName}`}
release={item}
showAvatar={showAvatar}
/>
))}
</div>
) : (
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/RepositoryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const RepositoryCard: React.FC<RepositoryCardListProps> = ({
return (
<div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
{displayedRepositories.map((repository, index) => {
return <RepositoryItem key={index} details={repository} />
{displayedRepositories.map((repository) => {
return <RepositoryItem key={repository.key} details={repository} />
})}
</div>
{repositories.length > maxInitialDisplay && <ShowMoreButton onToggle={toggleRepositories} />}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/SkeletonsBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function userCardRender() {
return (
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
{Array.from({ length: cardCount }).map((_, index) => (
<UserCardSkeleton key={index} />
<UserCardSkeleton key={`user-skeleton-${index}`} />
))}
</div>
)
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/ToggleableList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ const ToggleableList = ({
</div>
</h2>
<div className="flex flex-wrap gap-2">
{(showAll ? items : items.slice(0, limit)).map((item, index) => (
{(showAll ? items : items.slice(0, limit)).map((item) => (
<button
key={index}
key={item}
className="rounded-lg border border-gray-400 px-3 py-1 text-sm hover:bg-gray-200 dark:border-gray-300 dark:hover:bg-gray-700"
onClick={() => !isDisabled && handleButtonClick({ item })}
>
Expand Down
7 changes: 5 additions & 2 deletions frontend/src/components/TopContributorsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ const TopContributorsList = ({
}
>
<div className="grid gap-4 sm:grid-cols-1 md:grid-cols-3 lg:grid-cols-4">
{displayContributors.map((item, index) => (
<div key={index} className="overflow-hidden rounded-lg bg-gray-200 p-4 dark:bg-gray-700">
{displayContributors.map((item) => (
<div
key={item.login}
className="overflow-hidden rounded-lg bg-gray-200 p-4 dark:bg-gray-700"
>
<div className="flex w-full items-center gap-2">
<Image
alt={item?.name || ''}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/skeletons/ApiKeySkelton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function ApiKeysSkeleton() {
</tr>
</thead>
<tbody>
{/* # NOSONAR As safe to use index as key - static skeleton items with fixed length */}
{[...Array(totalRows)].map((_, i) => (
<tr key={i} className="border-b-1 border-b-gray-200 dark:border-b-gray-700">
<td className="py-3">
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/skeletons/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const CardSkeleton: React.FC<CardSkeletonProps> = ({

{showIcons && (
<div className="flex min-w-[30%] grow flex-row items-center justify-start gap-2 overflow-auto">
{/* # NOSONAR As safe to use index as key - static skeleton items with fixed length */}
{Array.from({ length: numIcons }).map((_, i) => (
<Skeleton key={i} className="h-8 w-16" />
))}
Expand All @@ -56,6 +57,7 @@ const CardSkeleton: React.FC<CardSkeletonProps> = ({
<div className="flex flex-col justify-start gap-2">
{showContributors && (
<div className="mt-3 flex w-full flex-wrap items-center gap-2">
{/* # NOSONAR As safe to use index as key - static skeleton items with fixed length */}
{[...Array(NUM_CONTRIBUTORS)].map((_, i) => (
<Skeleton key={i} className="border-background h-8 w-8 rounded-full border-2" />
))}
Expand Down