Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
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 || item.name || item.avatarUrl}
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