Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions src/components/products/ProductCard/ProductCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,14 @@ describe('ProductCard', () => {
expect(screen.getByText('Feature 2')).toBeInTheDocument()
})

it('renders link to product page', () => {
it('renders entire card as clickable link to product page', () => {
render(<ProductCard {...props} />)
expect(screen.getByRole('link', { name: /learn more/i })).toHaveAttribute(
'href',
'/products/vibes',
)
// Link contains all card content, so its accessible name includes "Vibes"
const link = screen.getByRole('link', { name: /vibes/i })
expect(link).toHaveAttribute('href', '/products/vibes')
// Verify the card content is inside the link (screen readers will announce this)
expect(link).toContainElement(screen.getByText('Vibes'))
expect(link).toContainElement(screen.getByText('Remote control for Claude Code'))
expect(link).toContainElement(screen.getByText('Learn More →'))
})
})
61 changes: 30 additions & 31 deletions src/components/products/ProductCard/ProductCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,36 @@ export function ProductCard({
className,
}: ProductCardProps) {
return (
<Card variant="interactive" className={cn('overflow-hidden', className)}>
{image && (
<div className="aspect-video bg-muted/50 border-b border-border">
<img src={image} alt={`${name} screenshot`} className="w-full h-full object-cover" />
</div>
)}
<CardContent className="space-y-4">
<div className="flex items-center gap-3">
<StatusBadge status={status} />
</div>
<div>
<Heading level={3} size="lg" className="mb-1">
{name}
</Heading>
<Text variant="muted">{tagline}</Text>
</div>
<ul className="space-y-2">
{features.map((feature) => (
<li key={feature} className="flex items-start gap-2">
<span className="text-accent mt-0.5">•</span>
<Text size="sm">{feature}</Text>
</li>
))}
</ul>
<Link
to={href}
className="inline-flex items-center text-accent hover:text-accent/80 font-medium transition-colors"
>
Learn More →
</Link>
</CardContent>
<Card asChild variant="interactive" className={cn('group overflow-hidden', className)}>
<Link to={href}>
{image && (
<div className="aspect-video bg-muted/50 border-b border-border">
<img src={image} alt={`${name} screenshot`} className="w-full h-full object-cover" />
</div>
)}
<CardContent className="space-y-4">
<div className="flex items-center gap-3">
<StatusBadge status={status} />
</div>
<div>
<Heading level={3} size="lg" className="mb-1">
{name}
</Heading>
<Text variant="muted">{tagline}</Text>
</div>
<ul className="space-y-2">
{features.map((feature) => (
<li key={feature} className="flex items-start gap-2">
<span className="text-accent mt-0.5">•</span>
<Text size="sm">{feature}</Text>
</li>
))}
</ul>
<span className="inline-flex items-center text-accent font-medium transition-colors group-hover:text-accent/80">
Learn More →
</span>
</CardContent>
</Link>
</Card>
)
}
Loading