Skip to content

Commit a8a33f6

Browse files
committed
feat: implement grid layout for FeaturedProjects component with TDD
- Add responsive 3-column grid (mobile: 1, tablet: 2, desktop: 3) - Update gap spacing from gap-6 to gap-8 - Increase margin top from mt-8 to mt-12 - Add props support for projects array with TypeScript interfaces - Update project data structure with new sample projects: * CCPTools Ecosystem (React Native, Node.js, PostgreSQL) * Multi-Tenant Nutrition Platform (Next.js, Prisma, tRPC) * Healthcare Management System (React, Express, MongoDB) - Comprehensive test coverage for grid layout, responsive design, and props - Maintain backward compatibility with default project data - All tests passing (18/18) with no regressions
1 parent 860bbb1 commit a8a33f6

2 files changed

Lines changed: 130 additions & 48 deletions

File tree

src/components/sections/FeaturedProjects.tsx

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,35 @@
11
import { ProjectCard } from '@/components/ui';
22

3-
const projects = [
3+
// Default project data
4+
const defaultProjects = [
45
{
56
title: "CCPTools Ecosystem",
6-
description: "Comprehensive nutrition platform with meal planning, recipe management, and nutritional analysis tools built with React and Node.js.",
7-
technologies: ["React Native", "Node.js", "PostgreSQL", "Express", "TypeScript"]
7+
description: "Comprehensive nutrition platform with meal planning, recipe management, and nutritional analysis tools.",
8+
technologies: ["React Native", "Node.js", "PostgreSQL"]
89
},
910
{
10-
title: "Portfolio Website",
11-
description: "Modern portfolio built with Next.js, TypeScript, and Tailwind CSS featuring responsive design and dark mode support.",
12-
technologies: ["Next.js", "TypeScript", "Tailwind CSS", "React", "Vercel"]
11+
title: "Multi-Tenant Nutrition Platform",
12+
description: "Advanced nutrition platform with multi-tenant architecture and real-time data synchronization.",
13+
technologies: ["Next.js", "Prisma", "tRPC"]
1314
},
1415
{
15-
title: "E-Commerce Platform",
16-
description: "Full-stack e-commerce solution with payment integration, inventory management, and admin dashboard using React and Express.",
17-
technologies: ["React", "Express", "MongoDB", "Stripe", "Redux", "JWT"]
16+
title: "Healthcare Management System",
17+
description: "Complete healthcare management solution with patient records, appointment scheduling, and billing.",
18+
technologies: ["React", "Express", "MongoDB"]
1819
}
1920
];
2021

21-
export function FeaturedProjects() {
22+
export interface Project {
23+
title: string;
24+
description: string;
25+
technologies: string[];
26+
}
27+
28+
export interface FeaturedProjectsProps {
29+
projects?: Project[];
30+
}
31+
32+
export function FeaturedProjects({ projects = defaultProjects }: FeaturedProjectsProps) {
2233
return (
2334
<section
2435
className="py-20 bg-[#ffffff] dark:bg-[#0d1117]"
@@ -33,7 +44,7 @@ export function FeaturedProjects() {
3344
</h2>
3445

3546
<div
36-
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mt-8"
47+
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mt-12"
3748
data-testid="projects-grid"
3849
>
3950
{projects.map((project, index) => (

src/components/sections/__tests__/FeaturedProjects.test.tsx

Lines changed: 108 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
import { render, screen } from "@testing-library/react";
22
import { FeaturedProjects } from "../FeaturedProjects";
33

4+
// Sample project data for testing
5+
const mockProjects = [
6+
{
7+
title: "CCPTools Ecosystem",
8+
description: "Comprehensive nutrition platform with meal planning, recipe management, and nutritional analysis tools.",
9+
technologies: ["React Native", "Node.js", "PostgreSQL"]
10+
},
11+
{
12+
title: "Multi-Tenant Nutrition Platform",
13+
description: "Advanced nutrition platform with multi-tenant architecture and real-time data synchronization.",
14+
technologies: ["Next.js", "Prisma", "tRPC"]
15+
},
16+
{
17+
title: "Healthcare Management System",
18+
description: "Complete healthcare management solution with patient records, appointment scheduling, and billing.",
19+
technologies: ["React", "Express", "MongoDB"]
20+
}
21+
];
22+
423
describe("FeaturedProjects", () => {
524
it("renders section with correct padding", () => {
625
render(<FeaturedProjects />);
@@ -76,50 +95,102 @@ describe("FeaturedProjects", () => {
7695
document.documentElement.classList.remove("dark");
7796
});
7897

79-
it("renders project cards grid", () => {
80-
render(<FeaturedProjects />);
81-
82-
const grid = screen.getByTestId("projects-grid");
83-
expect(grid).toBeInTheDocument();
84-
expect(grid).toHaveClass("grid", "grid-cols-1", "md:grid-cols-2", "lg:grid-cols-3", "gap-6", "mt-8");
85-
});
98+
describe("Grid Layout", () => {
99+
it("renders grid container with correct responsive classes", () => {
100+
render(<FeaturedProjects />);
101+
102+
const grid = screen.getByTestId("projects-grid");
103+
expect(grid).toHaveClass("grid", "grid-cols-1", "md:grid-cols-2", "lg:grid-cols-3");
104+
});
86105

87-
it("renders CCPTools Ecosystem project card", () => {
88-
render(<FeaturedProjects />);
89-
90-
expect(screen.getByText("CCPTools Ecosystem")).toBeInTheDocument();
91-
expect(screen.getByText(/comprehensive nutrition platform/i)).toBeInTheDocument();
106+
it("renders grid with correct gap spacing", () => {
107+
render(<FeaturedProjects />);
108+
109+
const grid = screen.getByTestId("projects-grid");
110+
expect(grid).toHaveClass("gap-8");
111+
});
112+
113+
it("renders grid with correct margin top from heading", () => {
114+
render(<FeaturedProjects />);
115+
116+
const grid = screen.getByTestId("projects-grid");
117+
expect(grid).toHaveClass("mt-12");
118+
});
92119
});
93120

94-
it("renders Portfolio Website project card", () => {
95-
render(<FeaturedProjects />);
96-
97-
expect(screen.getByText("Portfolio Website")).toBeInTheDocument();
98-
expect(screen.getByText(/modern portfolio built with next\.js/i)).toBeInTheDocument();
121+
describe("Project Data", () => {
122+
it("renders all 3 project cards", () => {
123+
render(<FeaturedProjects />);
124+
125+
const projectTitles = screen.getAllByRole("heading", { level: 3 });
126+
expect(projectTitles).toHaveLength(3);
127+
});
128+
129+
it("renders CCPTools Ecosystem project with correct data", () => {
130+
render(<FeaturedProjects />);
131+
132+
expect(screen.getByText("CCPTools Ecosystem")).toBeInTheDocument();
133+
expect(screen.getByText(/comprehensive nutrition platform/i)).toBeInTheDocument();
134+
expect(screen.getByText("React Native")).toBeInTheDocument();
135+
expect(screen.getByText("Node.js")).toBeInTheDocument();
136+
expect(screen.getByText("PostgreSQL")).toBeInTheDocument();
137+
});
138+
139+
it("renders Multi-Tenant Nutrition Platform project with correct data", () => {
140+
render(<FeaturedProjects />);
141+
142+
expect(screen.getByText("Multi-Tenant Nutrition Platform")).toBeInTheDocument();
143+
expect(screen.getByText(/advanced nutrition platform/i)).toBeInTheDocument();
144+
expect(screen.getByText("Next.js")).toBeInTheDocument();
145+
expect(screen.getByText("Prisma")).toBeInTheDocument();
146+
expect(screen.getByText("tRPC")).toBeInTheDocument();
147+
});
148+
149+
it("renders Healthcare Management System project with correct data", () => {
150+
render(<FeaturedProjects />);
151+
152+
expect(screen.getByText("Healthcare Management System")).toBeInTheDocument();
153+
expect(screen.getByText(/complete healthcare management solution/i)).toBeInTheDocument();
154+
expect(screen.getByText("React")).toBeInTheDocument();
155+
expect(screen.getByText("Express")).toBeInTheDocument();
156+
expect(screen.getByText("MongoDB")).toBeInTheDocument();
157+
});
99158
});
100159

101-
it("renders E-Commerce Platform project card", () => {
102-
render(<FeaturedProjects />);
103-
104-
expect(screen.getByText("E-Commerce Platform")).toBeInTheDocument();
105-
expect(screen.getByText(/full-stack e-commerce solution/i)).toBeInTheDocument();
160+
describe("Props Support", () => {
161+
it("accepts projects array as prop and renders them", () => {
162+
render(<FeaturedProjects projects={mockProjects} />);
163+
164+
expect(screen.getByText("CCPTools Ecosystem")).toBeInTheDocument();
165+
expect(screen.getByText("Multi-Tenant Nutrition Platform")).toBeInTheDocument();
166+
expect(screen.getByText("Healthcare Management System")).toBeInTheDocument();
167+
});
168+
169+
it("renders each card with correct props", () => {
170+
render(<FeaturedProjects projects={mockProjects} />);
171+
172+
// Check that each project card receives the correct props
173+
mockProjects.forEach(project => {
174+
expect(screen.getByText(project.title)).toBeInTheDocument();
175+
expect(screen.getByText(project.description)).toBeInTheDocument();
176+
project.technologies.forEach(tech => {
177+
expect(screen.getByText(tech)).toBeInTheDocument();
178+
});
179+
});
180+
});
106181
});
107182

108-
it("renders all project cards with correct structure", () => {
109-
render(<FeaturedProjects />);
110-
111-
// Should have 3 project cards
112-
const projectTitles = screen.getAllByRole("heading", { level: 3 });
113-
expect(projectTitles).toHaveLength(3);
114-
115-
// Each should be a project card with proper styling
116-
projectTitles.forEach(title => {
117-
// Get the parent div that contains the card styling (not the space-y-4 div)
118-
const card = title.closest('[class*="bg-surface"]');
119-
expect(card).toHaveClass('bg-surface');
120-
expect(card).toHaveClass('border');
121-
expect(card).toHaveClass('rounded-lg');
122-
expect(card).toHaveClass('p-6');
183+
describe("Responsive Design", () => {
184+
it("has mobile-first responsive grid classes", () => {
185+
render(<FeaturedProjects />);
186+
187+
const grid = screen.getByTestId("projects-grid");
188+
// Should start with 1 column on mobile
189+
expect(grid).toHaveClass("grid-cols-1");
190+
// Then 2 columns on medium screens
191+
expect(grid).toHaveClass("md:grid-cols-2");
192+
// Then 3 columns on large screens
193+
expect(grid).toHaveClass("lg:grid-cols-3");
123194
});
124195
});
125196
});

0 commit comments

Comments
 (0)