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

Implemented skeleton from chakra ui #611

Merged
merged 9 commits into from
Jan 28, 2025
12 changes: 6 additions & 6 deletions frontend/__tests__/src/pages/Chapters.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ describe('ChaptersPage Component', () => {
jest.clearAllMocks()
})

test('renders loading spinner initially', async () => {
test('renders skeleton initially', async () => {
render(<ChaptersPage />)
const loadingSpinner = screen.getAllByAltText('Loading indicator')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
const skeletonLoaders = screen.getAllByRole('status')
expect(skeletonLoaders.length).toBeGreaterThan(0)
})
})

Expand Down Expand Up @@ -86,9 +86,9 @@ describe('ChaptersPage Component', () => {
})
render(<ChaptersPage />)

const loadingSpinner = screen.getAllByAltText('Loading indicator')
const skeletonLoaders = screen.getAllByRole('status')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
expect(skeletonLoaders.length).toBeGreaterThan(0)
expect(screen.queryByText('Next Page')).not.toBeInTheDocument()
})
await waitFor(() => {
Expand All @@ -97,7 +97,7 @@ describe('ChaptersPage Component', () => {
expect(screen.getByText('Next Page')).toBeInTheDocument()
})

expect(screen.queryByAltText('Loading indicator')).not.toBeInTheDocument()
expect(screen.queryByTestId('status')).not.toBeInTheDocument()
})
test('opens window on View Details button click', async () => {
const navigateMock = jest.fn()
Expand Down
6 changes: 3 additions & 3 deletions frontend/__tests__/src/pages/CommitteeDetails.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ describe('Committees Component', () => {
jest.clearAllMocks()
})

test('renders loading spinner initially', async () => {
test('renders skeleton initially', async () => {
render(<CommitteeDetailsPage />)
const loadingSpinner = screen.getAllByAltText('Loading indicator')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
const skeletonLoaders = screen.getAllByRole('status')
expect(skeletonLoaders.length).toBeGreaterThan(0)
})
})

Expand Down
13 changes: 5 additions & 8 deletions frontend/__tests__/src/pages/Committees.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ describe('Committees Component', () => {
jest.clearAllMocks()
})

test('renders loading spinner initially', async () => {
test('renders skeleton initially', async () => {
render(<CommitteesPage />)
const loadingSpinner = screen.getAllByAltText('Loading indicator')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
const skeletonLoaders = screen.getAllByRole('status')
expect(skeletonLoaders.length).toBeGreaterThan(0)
})
})

Expand All @@ -53,19 +53,16 @@ describe('Committees Component', () => {

render(<CommitteesPage />)

const loadingSpinner = screen.getAllByAltText('Loading indicator')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)

expect(screen.queryByText('Next Page')).not.toBeInTheDocument()
expect(screen.queryByRole('status')).not.toBeInTheDocument()
})

await waitFor(() => {
expect(screen.getByPlaceholderText('Search for OWASP committees...')).toBeInTheDocument()
expect(screen.getByText('Committee 1')).toBeInTheDocument()
expect(screen.getByText('Next Page')).toBeInTheDocument()
})
expect(screen.queryByAltText('Loading indicator')).not.toBeInTheDocument()
expect(screen.queryByRole('status')).not.toBeInTheDocument()
})

test('renders committee data correctly', async () => {
Expand Down
6 changes: 3 additions & 3 deletions frontend/__tests__/src/pages/Contribute.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ describe('Contribute Component', () => {
jest.clearAllMocks()
})

test('renders loading spinner initially', async () => {
test('renders skeleton initially', async () => {
render(<ContributePage />)
const loadingSpinner = screen.getAllByAltText('Loading indicator')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
const skeletonLoaders = screen.getAllByRole('status')
expect(skeletonLoaders.length).toBeGreaterThan(0)
})
})

Expand Down
12 changes: 6 additions & 6 deletions frontend/__tests__/src/pages/Projects.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ describe('ProjectPage Component', () => {
jest.clearAllMocks()
})

test('renders loading spinner initially', async () => {
test('renders skeleton initially', async () => {
render(<ProjectsPage />)
const loadingSpinner = screen.getAllByAltText('Loading indicator')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
const skeletonLoaders = screen.getAllByRole('status')
expect(skeletonLoaders.length).toBeGreaterThan(0)
})
})

Expand All @@ -52,9 +52,9 @@ describe('ProjectPage Component', () => {

render(<ProjectsPage />)

const loadingSpinner = screen.getAllByAltText('Loading indicator')
const skeletonLoaders = screen.getAllByRole('status')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
expect(skeletonLoaders.length).toBeGreaterThan(0)
expect(screen.queryByText('Next Page')).not.toBeInTheDocument()
})
await waitFor(() => {
Expand All @@ -63,7 +63,7 @@ describe('ProjectPage Component', () => {
expect(screen.getByText('Next Page')).toBeInTheDocument()
})

expect(screen.queryByAltText('Loading indicator')).not.toBeInTheDocument()
expect(screen.queryByTestId('status')).not.toBeInTheDocument()
})

test('renders project data correctly', async () => {
Expand Down
12 changes: 6 additions & 6 deletions frontend/__tests__/src/pages/Users.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ describe('UsersPage Component', () => {
jest.clearAllMocks()
})

test('renders loading spinner initially', async () => {
test('renders skeleton initially', async () => {
render(<UsersPage />)
const loadingSpinner = screen.getAllByAltText('Loading indicator')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
const skeletonLoaders = screen.getAllByRole('status')
expect(skeletonLoaders.length).toBeGreaterThan(0)
})
})

Expand All @@ -52,9 +52,9 @@ describe('UsersPage Component', () => {
render(<UsersPage />)

// Check loading state
const loadingSpinner = screen.getAllByAltText('Loading indicator')
const skeletonLoaders = screen.getAllByRole('status')
await waitFor(() => {
expect(loadingSpinner.length).toBeGreaterThan(0)
expect(skeletonLoaders.length).toBeGreaterThan(0)
expect(screen.queryByText('Next Page')).not.toBeInTheDocument()
})

Expand All @@ -65,7 +65,7 @@ describe('UsersPage Component', () => {
expect(screen.getByText('Next Page')).toBeInTheDocument()
})

expect(screen.queryByAltText('Loading indicator')).not.toBeInTheDocument()
expect(screen.queryByTestId('status')).not.toBeInTheDocument()
})

test('renders user cards correctly', async () => {
Expand Down
6 changes: 2 additions & 4 deletions frontend/src/components/SearchPageLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react'
import LoadingSpinner from 'components/LoadingSpinner'
import Pagination from 'components/Pagination'
import SearchBar from 'components/Search'
import SkeletonBase from 'components/SkeletonsBase'

interface SearchPageLayoutProps {
isLoaded: boolean
Expand Down Expand Up @@ -52,9 +52,7 @@ const SearchPageLayout = ({
/>
</div>
{!isSearchBarReady || !isLoaded ? (
<div className="mt-20 flex h-64 w-full items-center justify-center">
<LoadingSpinner imageUrl={loadingImageUrl} />
</div>
<SkeletonBase indexName={indexName} loadingImageUrl={loadingImageUrl} />
) : (
<>
<div>
Expand Down
53 changes: 53 additions & 0 deletions frontend/src/components/SkeletonsBase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import LoadingSpinner from 'components/LoadingSpinner'
import CardSkeleton from 'components/skeletons/Card'
import UserCardSkeleton from 'components/skeletons/UserCard'
import { Skeleton } from 'components/ui/Skeleton'

function userCardRender() {
const cardCount = 12
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} />
))}
</div>
)
}

const SkeletonBase = ({ indexName, loadingImageUrl }) => {
let Component
switch (indexName) {
case 'chapters':
Component = () => <CardSkeleton showLevel={false} showIcons={1} showLink={false} />
break
case 'issues':
Component = () => (
<CardSkeleton showLevel={false} showIcons={2} showContributors={false} showSocial={false} />
)
break
case 'projects':
Component = () => <CardSkeleton showLink={false} showSocial={false} />
break
case 'committees':
Component = () => <CardSkeleton showLink={false} showLevel={false} showIcons={1} />
break
case 'users':
return userCardRender()
default:
return <LoadingSpinner imageUrl={loadingImageUrl} />
}
return (
<div className="flex w-full flex-col items-center justify-center">
{indexName == 'chapters' ? (
<Skeleton className="mb-2 w-full max-w-6xl" h={400} />
) : (
<Component />
)}
<Component />
<Component />
<Component />
</div>
)
}

export default SkeletonBase
82 changes: 82 additions & 0 deletions frontend/src/components/skeletons/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Box, Flex } from '@chakra-ui/react'
import type React from 'react'
import { CardSkeletonProps } from 'types/skeleton'
import { Skeleton, SkeletonCircle, SkeletonText } from 'components/ui/Skeleton'

const CardSkeleton: React.FC<CardSkeletonProps> = ({
showLevel = true,
showIcons = 4,
showProjectName = true,
showSummary = true,
showLink = true,
showContributors = true,
showSocial = true,
showActionButton = true,
}) => {
const NUM_CONTRIBUTORS = 8

return (
<div role="status" className="flex w-full justify-center">
<Box className="mb-6 w-full rounded-lg border border-border bg-card p-6 transition-colors duration-300 ease-linear hover:bg-accent/10 md:max-w-6xl">
<Flex direction="column" className="flex flex-col sm:flex-row" gap={6}>
{/* Header Section */}
<Flex className="flex w-full flex-col items-start justify-between gap-4 sm:flex-row">
<Flex className="items-center gap-4">
{showLevel && <SkeletonCircle className="h-10 w-10" />}
<Flex direction="column" gap={2}>
{showProjectName && <Skeleton className="h-8 w-[180px] sm:w-[250px]" />}
</Flex>
</Flex>

{showIcons && (
<Flex className="flex min-w-[30%] flex-grow flex-row items-center justify-end gap-2 overflow-auto">
{Array.from({ length: showIcons }).map((_, i) => (
<Skeleton key={i} className="h-8 w-16" />
))}
<Skeleton />
</Flex>
)}
</Flex>

{/* Link Section */}
{showLink && <SkeletonText className="w-[180px] md:w-[350px]" noOfLines={1} />}

{/* Description Section */}
{showSummary && <SkeletonText className="space-y-3" noOfLines={4} />}

{/* Footer Section */}
<Flex className="items-center justify-between gap-4 pt-3">
<div className="flex flex-col justify-start gap-2">
{showContributors && (
<Flex className="items-center space-x-2">
{[...Array(NUM_CONTRIBUTORS)].map((_, i) => (
<SkeletonCircle
key={i}
className="h-[30px] w-[30px] border-2 border-background"
/>
))}
</Flex>
)}
{showSocial && (
<Flex className="space-x-2">
<SkeletonCircle className="h-5 w-5" />
<SkeletonCircle className="h-5 w-5" />
<SkeletonCircle className="h-5 w-5" />
<SkeletonCircle className="h-5 w-5" />
<SkeletonCircle className="h-5 w-5" />
<SkeletonCircle className="h-5 w-5" />
</Flex>
)}
</div>

<Flex gap={4} className="ml-auto items-center">
{showActionButton && <Skeleton className="h-9 w-[100px]" />}
</Flex>
</Flex>
</Flex>
</Box>
</div>
)
}

export default CardSkeleton
37 changes: 37 additions & 0 deletions frontend/src/components/skeletons/UserCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Box, Flex } from '@chakra-ui/react'
import type React from 'react'
import { UserCardSkeletonProps } from 'types/skeleton'
import { Skeleton, SkeletonCircle } from 'components/ui/Skeleton'

const UserCardSkeleton: React.FC<UserCardSkeletonProps> = ({
showAvatar = true,
showName = true,
showViewProfile = true,
}) => {
return (
<Box
role="status"
className="group flex h-64 w-80 flex-col items-center rounded-lg bg-white p-6 text-left shadow-lg dark:bg-gray-800 dark:shadow-gray-900/30"
>
<Flex direction="column" className="w-full items-center space-y-4">
{showAvatar && (
<Box className="relative h-20 w-20 overflow-hidden rounded-full ring-2 ring-gray-100 dark:ring-gray-700">
<SkeletonCircle className="h-full w-full" />
</Box>
)}

<Flex direction="column" className="w-full items-center space-y-2">
{showName && <Skeleton className="h-7 w-40" />}
</Flex>
</Flex>

{showViewProfile && (
<Flex className="mt-auto items-center justify-center">
<Skeleton className="h-5 w-24" />
</Flex>
)}
</Box>
)
}

export default UserCardSkeleton
Loading