Skip to content

Commit 1a5a391

Browse files
committed
Docuent CoursePreviewCard Component
1 parent f3345fc commit 1a5a391

7 files changed

Lines changed: 131 additions & 11 deletions

File tree

src/components/landing/TrendingCourses.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useTrendingCourses } from '../../hooks/useCourses.ts';
2-
import CoursePreviewCard from '../ui/CoursePreviewCard.tsx';
2+
import CoursePreviewCard from '../ui/CoursePreviewCard/CoursePreviewCard.tsx';
33
import { useState, useEffect, useMemo } from 'react';
44

55
export function TrendingCourses() {
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import type { Meta, StoryObj } from '@storybook/react-vite';
2+
import CoursePreviewCard from './CoursePreviewCard';
3+
import { BrowserRouter } from 'react-router-dom';
4+
import type { Course } from '../../../types/entities';
5+
6+
/**
7+
* Mock data representing a Course.
8+
* We use 'as unknown as Course' to satisfy strict TypeScript checks
9+
* regarding deep nested interfaces (like Student[], Unit[], etc.)
10+
* without having to mock every single property of the database schema.
11+
*/
12+
const mockCourse = {
13+
id: 'course_123',
14+
name: 'Introduction to React & Modern UI',
15+
description: 'Learn how to build modern interfaces, reusable components, and manage global state with 2024 best practices.',
16+
imageUrl: 'https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=1000&auto=format&fit=crop',
17+
isFree: false,
18+
priceInCents: 2999, // $29.99
19+
status: 'published', // Mandatory field based on your interface
20+
21+
// Nested Objects Mocking
22+
courseType: { id: 'type_1', name: 'Frontend Development' },
23+
24+
// Assuming Professor interface structure based on usage
25+
professor: {
26+
id: 'prof_1',
27+
user: { name: 'Elena', surname: 'Torres' },
28+
institution: { id: 'inst_1', name: 'Tech University' }
29+
},
30+
31+
// Optional root institution
32+
institution: { id: 'inst_1', name: 'Tech University' },
33+
34+
// Arrays
35+
studentsCount: 342,
36+
students: [],
37+
units: [{}, {}, {}, {}, {}], // Dummy units for length count
38+
39+
createdAt: new Date(),
40+
updatedAt: new Date(),
41+
} as unknown as Course;
42+
43+
const meta = {
44+
title: 'Components/ui/CoursePreviewCard',
45+
component: CoursePreviewCard,
46+
parameters: {
47+
layout: 'centered',
48+
},
49+
decorators: [
50+
(Story) => (
51+
<BrowserRouter>
52+
<div className="w-[350px]">
53+
<Story />
54+
</div>
55+
</BrowserRouter>
56+
),
57+
],
58+
tags: ['autodocs'],
59+
argTypes: {
60+
hideButton: { control: 'boolean', description: 'Hides the call-to-action button' },
61+
hideInstructor: { control: 'boolean', description: 'Hides the instructor information' },
62+
course: { control: 'object', description: 'Full course object' },
63+
},
64+
} satisfies Meta<typeof CoursePreviewCard>;
65+
66+
export default meta;
67+
type Story = StoryObj<typeof CoursePreviewCard>
68+
69+
// --- Stories ---
70+
71+
export const Default: Story = {
72+
args: {
73+
course: mockCourse,
74+
},
75+
};
76+
77+
export const FreeCourse: Story = {
78+
args: {
79+
// We spread the mock and cast again to ensure it fits the Course type
80+
course: {
81+
...mockCourse,
82+
name: 'Programming Fundamentals',
83+
isFree: true,
84+
priceInCents: 0,
85+
} as unknown as Course,
86+
},
87+
};
88+
89+
export const CreationPreview: Story = {
90+
args: {
91+
// Individual props mode (No course object passed)
92+
name: 'Draft Course Title',
93+
description: 'This is a description being typed by the professor in real-time to preview the card appearance...',
94+
imageUrl: 'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?q=80&w=1000&auto=format&fit=crop',
95+
isFree: false,
96+
price: 45.50,
97+
courseType: { id: '2', name: 'UX/UI Design' },
98+
},
99+
};
100+
101+
export const NoImage: Story = {
102+
args: {
103+
course: {
104+
...mockCourse,
105+
imageUrl: null as unknown as string, // Force null for visual test
106+
name: 'Course without cover image',
107+
} as unknown as Course,
108+
},
109+
};
110+
111+
export const Minimal: Story = {
112+
args: {
113+
course: mockCourse,
114+
hideButton: true,
115+
hideInstructor: true,
116+
},
117+
};

src/components/ui/CoursePreviewCard.tsx renamed to src/components/ui/CoursePreviewCard/CoursePreviewCard.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import {
66
CardHeader,
77
CardTitle,
88
CardDescription,
9-
} from './Card';
10-
import Badge from './Badge/Badge';
11-
import Button from './Button/Button';
12-
import type { Course, CourseType } from '../../types/entities';
13-
import { cn } from '../../lib/utils';
9+
} from '../Card';
10+
import Badge from '../Badge/Badge';
11+
import Button from '../Button/Button';
12+
import type { Course, CourseType } from '../../../types/entities';
13+
import { cn } from '../../../lib/utils';
1414
import { Link } from 'react-router-dom';
15-
import { formatCurrency } from '../../lib/currency';
15+
import { formatCurrency } from '../../../lib/currency';
1616

1717
interface BasePreviewCardProps extends React.HTMLAttributes<HTMLDivElement> {
1818
hideButton?: boolean;
@@ -42,6 +42,9 @@ interface IndividualPropsPreview extends BasePreviewCardProps {
4242

4343
type CoursePreviewCardProps = CourseObjectProps | IndividualPropsPreview;
4444

45+
/**
46+
*This component displays a preview card for a course. It can accept either a full
47+
*/
4548
const CoursePreviewCard = React.forwardRef<HTMLDivElement, CoursePreviewCardProps>(
4649
({ course, name, description, imageUrl, isFree, price, courseType, hideButton = false, hideInstructor = false, className, ...props }, ref) => {
4750

src/pages/Course/CourseListPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState } from 'react';
22
import { useNavigate, useSearchParams } from 'react-router-dom';
33
import CardList from '../../components/ui/CardList/CardList.tsx';
4-
import CoursePreviewCard from '../../components/ui/CoursePreviewCard.tsx';
4+
import CoursePreviewCard from '../../components/ui/CoursePreviewCard/CoursePreviewCard.tsx';
55
import type { SearchCoursesParams } from '../../types/shared.ts';
66
import { useSearchCourses } from '../../hooks/useCourses.ts';
77
import { useCourseTypes } from '../../hooks/useCourseTypes.ts';

src/pages/Professor/ProfessorAssessmentsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
} from '../../hooks/useAssessments';
88
import Button from '../../components/ui/Button/Button';
99
import { Card } from '../../components/ui/Card';
10-
import Select from '../../components/ui/Select';
10+
import Select from '../../components/ui/Select/Select';
1111
import {
1212
Clock,
1313
FileText,

src/pages/Professor/ProfessorCourseCreation.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Button from "../../components/ui/Button/Button.tsx";
66
import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/Card.tsx";
77
import Input from "../../components/ui/Input/Input.tsx";
88
import Textarea from "../../components/ui/TextArea/TextArea.tsx";
9-
import CoursePreviewCard from "../../components/ui/CoursePreviewCard.tsx";
9+
import CoursePreviewCard from "../../components/ui/CoursePreviewCard/CoursePreviewCard.tsx";
1010
import Switch from "../../components/ui/Swtich/Switch.tsx";
1111
import Label from "../../components/ui/Label/Label.tsx";
1212
import Select from "../../components/ui/Select/Select.tsx";

src/pages/Professor/ProfessorCoursesPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Button from '../../components/ui/Button/Button';
33
import Badge from '../../components/ui/Badge/Badge.tsx';
44
import { Edit, Plus, Star } from 'lucide-react';
55
import { useProfessorCourses } from '../../hooks/useCourses.ts';
6-
import CoursePreviewCard from '../../components/ui/CoursePreviewCard';
6+
import CoursePreviewCard from '../../components/ui/CoursePreviewCard/CoursePreviewCard.tsx';
77

88
const ProfessorCoursesPage = () => {
99
const { data: courses, isLoading, error } = useProfessorCourses();

0 commit comments

Comments
 (0)