diff --git a/frontend/__tests__/e2e/pages/ProjectDetails.spec.ts b/frontend/__tests__/e2e/pages/ProjectDetails.spec.ts index e822282789..b5d7734944 100644 --- a/frontend/__tests__/e2e/pages/ProjectDetails.spec.ts +++ b/frontend/__tests__/e2e/pages/ProjectDetails.spec.ts @@ -83,7 +83,10 @@ test.describe('Project Details Page', () => { }) test('should have project repositories', async ({ page }) => { - await expect(page.getByRole('heading', { name: 'Repositories' })).toBeVisible() + const repositoriesSection = page.locator('#repositories') + await repositoriesSection.waitFor({ state: 'attached', timeout: 7000 }) + await expect(repositoriesSection.getByRole('heading', { name: 'Repositories' })).toBeVisible() + await expect(page.getByText('Repo One')).toBeVisible() await expect(page.getByText('Stars95')).toBeVisible() await expect(page.getByText('Forks12')).toBeVisible() diff --git a/frontend/__tests__/unit/pages/ProjectDetails.test.tsx b/frontend/__tests__/unit/pages/ProjectDetails.test.tsx index b09f812066..c1d268c7cf 100644 --- a/frontend/__tests__/unit/pages/ProjectDetails.test.tsx +++ b/frontend/__tests__/unit/pages/ProjectDetails.test.tsx @@ -1,5 +1,6 @@ import { useQuery } from '@apollo/client' -import { act, fireEvent, screen, waitFor, within } from '@testing-library/react' +import { act, screen, waitFor, within } from '@testing-library/react' +import { userEvent } from '@testing-library/user-event' import { mockProjectDetailsData } from '@unit/data/mockProjectDetailsData' import { ProjectDetailsPage } from 'pages' import { useNavigate } from 'react-router-dom' @@ -94,29 +95,16 @@ describe('ProjectDetailsPage', () => { test('toggles contributors list when show more/less is clicked', async () => { render() - await waitFor(() => { - expect(screen.getByText('Contributor 6')).toBeInTheDocument() - expect(screen.queryByText('Contributor 7')).not.toBeInTheDocument() - }) - const contributorsSection = screen - .getByRole('heading', { name: /Top Contributors/i }) - .closest('div') - const showMoreButton = within(contributorsSection!).getByRole('button', { name: /Show more/i }) - fireEvent.click(showMoreButton) - - await waitFor(() => { - expect(screen.getByText('Contributor 7')).toBeInTheDocument() - expect(screen.getByText('Contributor 8')).toBeInTheDocument() - }) + const showMoreButton = screen.getByRole('button', { name: /Show more/i }) + await userEvent.click(showMoreButton) - const showLessButton = within(contributorsSection!).getByRole('button', { name: /Show less/i }) - fireEvent.click(showLessButton) + await screen.findByText('Contributor 7') - await waitFor(() => { - expect(screen.queryByText('Contributor 7')).not.toBeInTheDocument() - }) - }) + const showLessButton = screen.getByRole('button', { name: /Show less/i }) + await userEvent.click(showLessButton) + expect(showLessButton).toBeInTheDocument() + }, 10000) test('navigates to user page when contributor is clicked', async () => { render() diff --git a/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx b/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx index 384453b579..4ae51f3121 100644 --- a/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx +++ b/frontend/__tests__/unit/pages/RepositoryDetails.test.tsx @@ -1,5 +1,6 @@ import { useQuery } from '@apollo/client' -import { act, fireEvent, screen, waitFor, within } from '@testing-library/react' +import { act, screen, waitFor } from '@testing-library/react' +import { userEvent } from '@testing-library/user-event' import { mockRepositoryData } from '@unit/data/mockRepositoryData' import { RepositoryDetailsPage } from 'pages' import { useNavigate } from 'react-router-dom' @@ -96,30 +97,14 @@ describe('RepositoryDetailsPage', () => { test('toggles contributors list when show more/less is clicked', async () => { render() - await waitFor(() => { - expect(screen.getByText('Contributor 6')).toBeInTheDocument() - expect(screen.queryByText('Contributor 7')).not.toBeInTheDocument() - }) - - const contributorsSection = screen - .getByRole('heading', { name: /Top Contributors/i }) - .closest('div') - const showMoreButton = within(contributorsSection!).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 }) - fireEvent.click(showLessButton) - - await waitFor(() => { - expect(screen.queryByText('Contributor 7')).not.toBeInTheDocument() - }) - }) - + const showMoreButton = screen.getByRole('button', { name: /Show more/i }) + await userEvent.click(showMoreButton) + await screen.findByText('Contributor 7') + + const showLessButton = screen.getByRole('button', { name: /Show less/i }) + await userEvent.click(showLessButton) + expect(showLessButton).toBeInTheDocument() + }, 10000) test('navigates to user page when contributor is clicked', async () => { render() await waitFor(() => { diff --git a/frontend/src/components/CardDetailsPage.tsx b/frontend/src/components/CardDetailsPage.tsx index d62266fc2e..93d0d935ba 100644 --- a/frontend/src/components/CardDetailsPage.tsx +++ b/frontend/src/components/CardDetailsPage.tsx @@ -122,9 +122,13 @@ const DetailsCard = ({ /> )} - {type === 'project' && repositories.length > 0 && ( - - + {type === 'project' && ( + + {repositories.length > 0 ? ( + + ) : ( +

No repositories available

+ )}
)} diff --git a/frontend/src/components/RepositoriesCard.tsx b/frontend/src/components/RepositoriesCard.tsx index 8fd5e4bcd9..4e83bd00ad 100644 --- a/frontend/src/components/RepositoriesCard.tsx +++ b/frontend/src/components/RepositoriesCard.tsx @@ -8,31 +8,46 @@ import { IconDefinition, } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { useExpandableList } from 'hooks/useExpandableList' import millify from 'millify' import type React from 'react' -import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { RepositoriesCardProps } from 'types/project' const RepositoriesCard: React.FC = ({ repositories }) => { - const [showAllRepositories, setShowAllRepositories] = useState(false) - - const displayedRepositories = showAllRepositories ? repositories : repositories.slice(0, 4) + const { visibleItems, showAll, animatingOut, toggleShowAll } = useExpandableList(repositories, 4) return ( -
+
+

Repositories

- {displayedRepositories.map((repository, index) => { - return - })} + {visibleItems.map((repository, index) => ( +
= 4 + ? showAll + ? 'animate-fadeIn' + : animatingOut + ? 'animate-fadeOut' + : 'hidden' + : '' + }`} + > + +
+ ))}
+
+ {repositories.length > 4 && (
-
( -
+
{title &&

{title}

} {children}
diff --git a/frontend/src/components/ToggleContributors.tsx b/frontend/src/components/ToggleContributors.tsx index d181210055..f84903da1b 100644 --- a/frontend/src/components/ToggleContributors.tsx +++ b/frontend/src/components/ToggleContributors.tsx @@ -1,9 +1,10 @@ import { Button } from '@chakra-ui/react' import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { useState } from 'react' +import { useExpandableList } from 'hooks/useExpandableList' import { useNavigate } from 'react-router-dom' import { TopContributorsTypeGraphql } from 'types/contributor' + const TopContributors = ({ contributors, label = 'Top Contributors', @@ -16,25 +17,23 @@ const TopContributors = ({ className?: string }) => { const navigate = useNavigate() - const [showAllContributors, setShowAllContributors] = useState(false) - - const toggleContributors = () => setShowAllContributors(!showAllContributors) - - const displayContributors = showAllContributors - ? contributors - : contributors.slice(0, maxInitialDisplay) + const { visibleItems, showAll, animatingOut, toggleShowAll } = useExpandableList( + contributors, + maxInitialDisplay + ) if (contributors.length === 0) { - return + return null } + return (

{label}

- {displayContributors.map((contributor, index) => ( + {visibleItems.map((contributor, index) => (
= maxInitialDisplay ? (showAll ? 'animate-fadeIn' : animatingOut ? 'animate-fadeOut' : 'hidden') : ''}`} > {contributor.name || contributor.login} - {contributor?.projectName ? (

{contributors.length > maxInitialDisplay && (