fix: storybook font rendering and shared fonts module #1

Merged
ilia merged 74 commits from feat/portfolio-setup into main 2026-05-18 18:45:22 +00:00
2 changed files with 22 additions and 7 deletions
Showing only changes of commit ac9ee0eb4e - Show all commits
@@ -6,6 +6,7 @@ const DEFAULT_PROPS = {
year: '2024', year: '2024',
description: 'A cool project description', description: 'A cool project description',
tags: ['React', 'Node'], tags: ['React', 'Node'],
url: 'https://example.com',
}; };
describe('ProjectCard', () => { describe('ProjectCard', () => {
@@ -33,7 +34,17 @@ describe('ProjectCard', () => {
it('renders the View Project button', () => { it('renders the View Project button', () => {
render(<ProjectCard {...DEFAULT_PROPS} />); render(<ProjectCard {...DEFAULT_PROPS} />);
expect(screen.getByRole('button', { name: /view project/i })).toBeInTheDocument(); expect(screen.getByRole('link', { name: /view project/i })).toBeInTheDocument();
});
it('View Project link points to the project url', () => {
render(<ProjectCard {...DEFAULT_PROPS} />);
expect(screen.getByRole('link', { name: /view project/i })).toHaveAttribute('href', 'https://example.com');
});
it('View Project link opens in a new tab', () => {
render(<ProjectCard {...DEFAULT_PROPS} />);
expect(screen.getByRole('link', { name: /view project/i })).toHaveAttribute('target', '_blank');
}); });
}); });
@@ -51,7 +62,7 @@ describe('ProjectCard', () => {
it('View Project button is inside the sidebar column', () => { it('View Project button is inside the sidebar column', () => {
render(<ProjectCard {...DEFAULT_PROPS} />); render(<ProjectCard {...DEFAULT_PROPS} />);
const btn = screen.getByRole('button', { name: /view project/i }); const btn = screen.getByRole('link', { name: /view project/i });
expect(btn.closest('.brutal-border-sidebar')).toBeInTheDocument(); expect(btn.closest('.brutal-border-sidebar')).toBeInTheDocument();
}); });
@@ -86,7 +97,7 @@ describe('ProjectCard', () => {
it('View Project button uses sm size', () => { it('View Project button uses sm size', () => {
render(<ProjectCard {...DEFAULT_PROPS} />); render(<ProjectCard {...DEFAULT_PROPS} />);
const btn = screen.getByRole('button', { name: /view project/i }); const btn = screen.getByRole('link', { name: /view project/i });
expect(btn).toHaveClass('px-4', 'py-2', 'text-sm'); expect(btn).toHaveClass('px-4', 'py-2', 'text-sm');
}); });
@@ -1,6 +1,6 @@
import Image from 'next/image'; import Image from 'next/image';
import { cn } from '$shared/lib'; import { cn } from '$shared/lib';
import { Badge, Button, Card, CardSidebar, CardTitle } from '$shared/ui'; import { Badge, Button, Card, CardSidebar, CardTitle, RichText } from '$shared/ui';
type Props = { type Props = {
/** /**
@@ -19,6 +19,10 @@ type Props = {
* Technology or category tags * Technology or category tags
*/ */
tags: string[]; tags: string[];
/**
* Project's URL
*/
url: string;
/** /**
* Optional preview image URL * Optional preview image URL
*/ */
@@ -30,7 +34,7 @@ type Props = {
* Sidebar: year badge, stack tags, View Project button. * Sidebar: year badge, stack tags, View Project button.
* Main: title, optional image, description. * Main: title, optional image, description.
*/ */
export function ProjectCard({ title, year, description, tags, imageUrl }: Props) { export function ProjectCard({ title, year, description, tags, url, imageUrl }: Props) {
return ( return (
<Card className={cn('group hover:shadow-brutal-xl transition-shadow duration-300')}> <Card className={cn('group hover:shadow-brutal-xl transition-shadow duration-300')}>
<CardSidebar <CardSidebar
@@ -46,7 +50,7 @@ export function ProjectCard({ title, year, description, tags, imageUrl }: Props)
))} ))}
</div> </div>
)} )}
<Button variant="primary" size="sm" className="w-full"> <Button href={url} variant="primary" size="sm" className="w-full">
View Project View Project
</Button> </Button>
</div> </div>
@@ -59,7 +63,7 @@ export function ProjectCard({ title, year, description, tags, imageUrl }: Props)
<Image src={imageUrl} alt={title} fill className="object-cover" /> <Image src={imageUrl} alt={title} fill className="object-cover" />
</div> </div>
)} )}
<p className="opacity-80">{description}</p> <RichText html={description} />
</div> </div>
</CardSidebar> </CardSidebar>
</Card> </Card>