feat(Card): add optional title prop with uppercase monospace header
When a title string is provided, renders an uppercase monospace h3 header with a subtle border separator above the card body. Children are wrapped in a padded body div when title is present; without title, children render directly as before (no breaking change). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,3 +11,22 @@
|
||||
.accent-warning { border-top: 3px solid var(--warning); }
|
||||
.accent-error { border-top: 3px solid var(--error); }
|
||||
.accent-running { border-top: 3px solid var(--running); }
|
||||
|
||||
.titleHeader {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid var(--border-subtle);
|
||||
}
|
||||
|
||||
.titleText {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
font-family: var(--font-mono);
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
letter-spacing: 0.5px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
49
src/design-system/primitives/Card/Card.test.tsx
Normal file
49
src/design-system/primitives/Card/Card.test.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { Card } from './Card'
|
||||
|
||||
describe('Card', () => {
|
||||
it('renders children', () => {
|
||||
render(<Card>Hello world</Card>)
|
||||
expect(screen.getByText('Hello world')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders title when provided', () => {
|
||||
render(<Card title="Status">Content</Card>)
|
||||
expect(screen.getByText('Status')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not render title header when title is omitted', () => {
|
||||
const { container } = render(<Card>Content</Card>)
|
||||
expect(container.querySelector('h3')).toBeNull()
|
||||
})
|
||||
|
||||
it('wraps children in body div when title is provided', () => {
|
||||
render(<Card title="Status"><span>Content</span></Card>)
|
||||
const content = screen.getByText('Content')
|
||||
expect(content.parentElement).toHaveClass('body')
|
||||
})
|
||||
|
||||
it('renders with accent and title together', () => {
|
||||
const { container } = render(
|
||||
<Card accent="success" title="Health">Content</Card>,
|
||||
)
|
||||
const card = container.firstChild as HTMLElement
|
||||
expect(card).toHaveClass('accent-success')
|
||||
expect(screen.getByText('Health')).toBeInTheDocument()
|
||||
expect(screen.getByText('Content')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('accepts className prop', () => {
|
||||
const { container } = render(<Card className="custom">Content</Card>)
|
||||
const card = container.firstChild as HTMLElement
|
||||
expect(card).toHaveClass('custom')
|
||||
})
|
||||
|
||||
it('renders children directly when no title (no wrapper div)', () => {
|
||||
const { container } = render(<Card><span>Direct child</span></Card>)
|
||||
const card = container.firstChild as HTMLElement
|
||||
const span = screen.getByText('Direct child')
|
||||
expect(span.parentElement).toBe(card)
|
||||
})
|
||||
})
|
||||
@@ -4,15 +4,25 @@ import type { ReactNode } from 'react'
|
||||
interface CardProps {
|
||||
children: ReactNode
|
||||
accent?: 'amber' | 'success' | 'warning' | 'error' | 'running' | 'none'
|
||||
title?: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function Card({ children, accent = 'none', className }: CardProps) {
|
||||
export function Card({ children, accent = 'none', title, className }: CardProps) {
|
||||
const classes = [
|
||||
styles.card,
|
||||
accent !== 'none' ? styles[`accent-${accent}`] : '',
|
||||
className ?? '',
|
||||
].filter(Boolean).join(' ')
|
||||
|
||||
return <div className={classes}>{children}</div>
|
||||
return (
|
||||
<div className={classes}>
|
||||
{title && (
|
||||
<div className={styles.titleHeader}>
|
||||
<h3 className={styles.titleText}>{title}</h3>
|
||||
</div>
|
||||
)}
|
||||
{title ? <div className={styles.body}>{children}</div> : children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user