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+ } ;
0 commit comments