diff --git a/frontend/__tests__/unit/components/ModuleList.test.tsx b/frontend/__tests__/unit/components/ModuleList.test.tsx
deleted file mode 100644
index ded2d57e51..0000000000
--- a/frontend/__tests__/unit/components/ModuleList.test.tsx
+++ /dev/null
@@ -1,316 +0,0 @@
-import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'
-import { fireEvent, render, screen } from '@testing-library/react'
-import React from 'react'
-import ModuleList from 'components/ModuleList'
-
-// Mock FontAwesome icons
-jest.mock('@fortawesome/react-fontawesome', () => ({
- FontAwesomeIcon: ({ icon, className }: { icon: unknown; className?: string }) => {
- let iconName = 'unknown'
-
- if (icon === faChevronDown) {
- iconName = 'chevron-down'
- } else if (icon === faChevronUp) {
- iconName = 'chevron-up'
- }
-
- return
- },
-}))
-
-// Mock HeroUI Button component
-jest.mock('@heroui/button', () => ({
- Button: ({
- children,
- onPress,
- className,
- 'aria-label': ariaLabel,
- disableAnimation: _disableAnimation,
- ..._props
- }: {
- children: React.ReactNode
- onPress: () => void
- className?: string
- 'aria-label'?: string
- disableAnimation?: boolean
- [key: string]: unknown
- }) => (
-
- ),
-}))
-
-describe('ModuleList', () => {
- describe('Empty and Null Cases', () => {
- it('returns null when modules array is empty', () => {
- const { container } = render()
- expect(container.firstChild).toBeNull()
- })
-
- it('returns null when modules is undefined', () => {
- const { container } = render()
- expect(container.firstChild).toBeNull()
- })
-
- it('returns null when modules is null', () => {
- const { container } = render()
- expect(container.firstChild).toBeNull()
- })
- })
-
- describe('Rendering with Different Module Counts', () => {
- it('renders all modules when count is less than 5', () => {
- const modules = ['Module 1', 'Module 2', 'Module 3']
- render()
-
- expect(screen.getByText('Module 1')).toBeInTheDocument()
- expect(screen.getByText('Module 2')).toBeInTheDocument()
- expect(screen.getByText('Module 3')).toBeInTheDocument()
-
- // Should not show "Show more" button
- expect(screen.queryByText('Show more')).not.toBeInTheDocument()
- })
-
- it('renders all modules when count is exactly 5', () => {
- const modules = ['Module 1', 'Module 2', 'Module 3', 'Module 4', 'Module 5']
- render()
-
- for (const myModule of modules) {
- expect(screen.getByText(myModule)).toBeInTheDocument()
- }
-
- // Should not show "Show more" button
- expect(screen.queryByText('Show more')).not.toBeInTheDocument()
- })
-
- it('renders only first 5 modules when count is greater than 5', () => {
- const modules = [
- 'Module 1',
- 'Module 2',
- 'Module 3',
- 'Module 4',
- 'Module 5',
- 'Module 6',
- 'Module 7',
- ]
- render()
-
- // First 5 should be visible
- expect(screen.getByText('Module 1')).toBeInTheDocument()
- expect(screen.getByText('Module 2')).toBeInTheDocument()
- expect(screen.getByText('Module 3')).toBeInTheDocument()
- expect(screen.getByText('Module 4')).toBeInTheDocument()
- expect(screen.getByText('Module 5')).toBeInTheDocument()
-
- // Last 2 should not be visible initially
- expect(screen.queryByText('Module 6')).not.toBeInTheDocument()
- expect(screen.queryByText('Module 7')).not.toBeInTheDocument()
-
- // Should show "Show more" button
- expect(screen.getByText('Show more')).toBeInTheDocument()
- })
- })
-
- describe('Show More/Less Functionality', () => {
- const manyModules = Array.from({ length: 8 }, (_, i) => `Module ${i + 1}`)
-
- it('shows "Show more" button with correct aria-label initially', () => {
- render()
-
- const button = screen.getByRole('button', { name: 'Show more modules' })
- expect(button).toBeInTheDocument()
- expect(screen.getByText('Show more')).toBeInTheDocument()
- expect(screen.getByTestId('icon-chevron-down')).toBeInTheDocument()
- })
-
- it('expands to show all modules when "Show more" is clicked', () => {
- render()
-
- // Initially only first 5 are visible
- expect(screen.getByText('Module 1')).toBeInTheDocument()
- expect(screen.getByText('Module 5')).toBeInTheDocument()
- expect(screen.queryByText('Module 6')).not.toBeInTheDocument()
-
- // Click "Show more"
- const showMoreButton = screen.getByText('Show more')
- fireEvent.click(showMoreButton)
-
- // Now all modules should be visible
- expect(screen.getByText('Module 6')).toBeInTheDocument()
- expect(screen.getByText('Module 7')).toBeInTheDocument()
- expect(screen.getByText('Module 8')).toBeInTheDocument()
- })
-
- it('changes to "Show less" button after expanding', () => {
- render()
-
- const showMoreButton = screen.getByText('Show more')
- fireEvent.click(showMoreButton)
-
- // Button should change to "Show less"
- expect(screen.getByText('Show less')).toBeInTheDocument()
- expect(screen.getByRole('button', { name: 'Show fewer modules' })).toBeInTheDocument()
- expect(screen.getByTestId('icon-chevron-up')).toBeInTheDocument()
- expect(screen.queryByText('Show more')).not.toBeInTheDocument()
- })
-
- it('collapses back to 5 modules when "Show less" is clicked', () => {
- render()
-
- // Expand first
- const showMoreButton = screen.getByText('Show more')
- fireEvent.click(showMoreButton)
-
- // Verify all are visible
- expect(screen.getByText('Module 8')).toBeInTheDocument()
-
- // Click "Show less"
- const showLessButton = screen.getByText('Show less')
- fireEvent.click(showLessButton)
-
- // Should be back to first 5 only
- expect(screen.getByText('Module 5')).toBeInTheDocument()
- expect(screen.queryByText('Module 6')).not.toBeInTheDocument()
- expect(screen.queryByText('Module 8')).not.toBeInTheDocument()
-
- // Button should be back to "Show more"
- expect(screen.getByText('Show more')).toBeInTheDocument()
- })
- })
-
- describe('Module Text Truncation', () => {
- it('truncates module names longer than 50 characters', () => {
- const longModuleName =
- 'This is a very long module name that exceeds fifty characters and should be truncated'
- const modules = [longModuleName]
-
- render()
-
- const truncatedButton = screen.getByRole('button', {
- name: `Module: ${longModuleName}`,
- })
- expect(truncatedButton).toHaveAttribute('title', longModuleName)
- })
-
- it('does not truncate module names 50 characters or shorter', () => {
- const exactlyFiftyChars = 'A'.repeat(50)
- const modules = [exactlyFiftyChars, 'Short']
-
- render()
-
- expect(screen.getByText(exactlyFiftyChars)).toBeInTheDocument()
- expect(screen.getByText('Short')).toBeInTheDocument()
- })
-
- it('adds title attribute for truncated modules', () => {
- const longModuleName =
- 'This is a very long module name that exceeds fifty characters and should be truncated'
- const modules = [longModuleName]
-
- render()
-
- const truncatedButton = screen.getByRole('button', {
- name: /This is a very long module name that exceeds/,
- })
- expect(truncatedButton).toHaveAttribute('title', longModuleName)
- })
-
- it('does not add title attribute for non-truncated modules', () => {
- const shortModuleName = 'Short Module'
- const modules = [shortModuleName]
-
- render()
-
- const button = screen.getByRole('button', { name: `Module: ${shortModuleName}` })
- expect(button).not.toHaveAttribute('title')
- })
- })
-
- describe('Module Button Properties', () => {
- it('renders module buttons with correct classes', () => {
- const modules = ['Test Module']
- render()
-
- const button = screen.getByRole('button', { name: 'Module: Test Module' })
- expect(button).toHaveClass(
- 'rounded-lg',
- 'border',
- 'border-gray-400',
- 'px-3',
- 'py-1',
- 'text-sm',
- 'transition-all',
- 'duration-200',
- 'ease-in-out',
- 'hover:scale-105',
- 'hover:bg-gray-200',
- 'dark:border-gray-300',
- 'dark:hover:bg-gray-700'
- )
- })
-
- it('sets correct button type', () => {
- const modules = ['Test Module']
- render()
-
- const button = screen.getByRole('button', { name: 'Module: Test Module' })
- expect(button).toHaveAttribute('type', 'button')
- })
-
- it('generates unique keys for modules with same name', () => {
- const modules = ['Same Name', 'Same Name', 'Different Name']
- render()
-
- const sameNameButtons = screen.getAllByText('Same Name')
- expect(sameNameButtons).toHaveLength(2)
- expect(screen.getByText('Different Name')).toBeInTheDocument()
- })
- })
-
- describe('Container Structure', () => {
- it('renders with correct container classes', () => {
- const modules = ['Module 1']
- const { container } = render()
-
- const outerDiv = container.firstChild as HTMLElement
- expect(outerDiv).toHaveClass('mt-3')
-
- const innerDiv = outerDiv.firstChild as HTMLElement
- expect(innerDiv).toHaveClass('flex', 'flex-wrap', 'items-center', 'gap-2')
- })
- })
-
- describe('Edge Cases', () => {
- it('handles modules with empty strings', () => {
- const modules = ['', 'Valid Module', '']
- render()
-
- const buttons = screen.getAllByRole('button')
- // Should render 3 buttons (including empty string ones)
- expect(buttons).toHaveLength(3)
- expect(screen.getByText('Valid Module')).toBeInTheDocument()
- })
-
- it('handles exactly 6 modules (edge case for show more)', () => {
- const modules = Array.from({ length: 6 }, (_, i) => `Module ${i + 1}`)
- render()
-
- // First 5 should be visible
- expect(screen.getByText('Module 5')).toBeInTheDocument()
- expect(screen.queryByText('Module 6')).not.toBeInTheDocument()
-
- // Should show "Show more" button
- expect(screen.getByText('Show more')).toBeInTheDocument()
-
- // Click to expand
- fireEvent.click(screen.getByText('Show more'))
- expect(screen.getByText('Module 6')).toBeInTheDocument()
- })
- })
-})
diff --git a/frontend/src/components/Card.tsx b/frontend/src/components/Card.tsx
index 61dcc91886..26d785a1d5 100644
--- a/frontend/src/components/Card.tsx
+++ b/frontend/src/components/Card.tsx
@@ -11,7 +11,6 @@ import ActionButton from 'components/ActionButton'
import ContributorAvatar from 'components/ContributorAvatar'
import DisplayIcon from 'components/DisplayIcon'
import Markdown from 'components/MarkdownWrapper'
-import ModuleList from 'components/ModuleList'
const Card = ({
title,
@@ -23,7 +22,6 @@ const Card = ({
button,
projectName,
projectLink,
- modules,
social,
tooltipLabel,
timeline,
@@ -103,8 +101,6 @@ const Card = ({
{/* Project summary */}
- {/* Modules section (if available) */}
-
{/* Social icons section */}
{social && social.length > 0 && (
diff --git a/frontend/src/components/ModuleList.tsx b/frontend/src/components/ModuleList.tsx
deleted file mode 100644
index 0745199c23..0000000000
--- a/frontend/src/components/ModuleList.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
-import { Button } from '@heroui/button'
-import React, { useState } from 'react'
-
-interface ModuleListProps {
- modules: string[]
-}
-
-const ModuleList: React.FC
= ({ modules }) => {
- const [showAll, setShowAll] = useState(false)
-
- if (!modules || modules.length === 0) return null
-
- const displayedModules = showAll ? modules : modules.slice(0, 5)
-
- return (
-
-
- {displayedModules.map((module, index) => {
- const displayText = module.length > 50 ? `${module.slice(0, 50)}...` : module
- return (
-
- )
- })}
-
- {modules.length > 5 && (
-
- )}
-
-
- )
-}
-
-export default ModuleList
diff --git a/frontend/src/types/card.ts b/frontend/src/types/card.ts
index 482761b65c..67c276df11 100644
--- a/frontend/src/types/card.ts
+++ b/frontend/src/types/card.ts
@@ -22,7 +22,6 @@ export type CardProps = {
level?: Level
projectLink?: string
projectName?: string
- modules?: string[]
social?: { title: string; icon: string; url: string }[]
summary: string
title: string