feat: add Badge component to shared/ui
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export { Badge } from './ui/Badge'
|
||||
@@ -0,0 +1,52 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { Badge } from './Badge'
|
||||
|
||||
describe('Badge', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders children', () => {
|
||||
render(<Badge>React</Badge>)
|
||||
expect(screen.getByText('React')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders as inline span', () => {
|
||||
render(<Badge>Tag</Badge>)
|
||||
expect(screen.getByText('Tag').tagName).toBe('SPAN')
|
||||
})
|
||||
})
|
||||
|
||||
describe('variants', () => {
|
||||
it('applies default variant classes', () => {
|
||||
render(<Badge variant="default">Tag</Badge>)
|
||||
const el = screen.getByText('Tag')
|
||||
expect(el).toHaveClass('bg-carbon-black', 'text-ochre-clay')
|
||||
})
|
||||
|
||||
it('applies primary variant classes', () => {
|
||||
render(<Badge variant="primary">Tag</Badge>)
|
||||
expect(screen.getByText('Tag')).toHaveClass('bg-burnt-oxide')
|
||||
})
|
||||
|
||||
it('applies secondary variant classes', () => {
|
||||
render(<Badge variant="secondary">Tag</Badge>)
|
||||
expect(screen.getByText('Tag')).toHaveClass('bg-slate-indigo')
|
||||
})
|
||||
|
||||
it('applies outline variant classes', () => {
|
||||
render(<Badge variant="outline">Tag</Badge>)
|
||||
expect(screen.getByText('Tag')).toHaveClass('bg-transparent')
|
||||
})
|
||||
|
||||
it('defaults to default variant when unspecified', () => {
|
||||
render(<Badge>Tag</Badge>)
|
||||
expect(screen.getByText('Tag')).toHaveClass('bg-carbon-black')
|
||||
})
|
||||
})
|
||||
|
||||
describe('className passthrough', () => {
|
||||
it('merges custom className', () => {
|
||||
render(<Badge className="mt-4">Tag</Badge>)
|
||||
expect(screen.getByText('Tag')).toHaveClass('mt-4')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { cn } from '$shared/lib'
|
||||
|
||||
type BadgeVariant = 'default' | 'primary' | 'secondary' | 'outline'
|
||||
|
||||
interface Props {
|
||||
/**
|
||||
* Badge content
|
||||
*/
|
||||
children: ReactNode
|
||||
/**
|
||||
* Visual variant
|
||||
* @default 'default'
|
||||
*/
|
||||
variant?: BadgeVariant
|
||||
/**
|
||||
* Additional CSS classes
|
||||
*/
|
||||
className?: string
|
||||
}
|
||||
|
||||
const VARIANTS: Record<BadgeVariant, string> = {
|
||||
default: 'brutal-border bg-carbon-black text-ochre-clay',
|
||||
primary: 'brutal-border bg-burnt-oxide text-ochre-clay',
|
||||
secondary: 'brutal-border bg-slate-indigo text-ochre-clay',
|
||||
outline: 'brutal-border bg-transparent text-carbon-black',
|
||||
}
|
||||
|
||||
/**
|
||||
* Small label for categorization or status.
|
||||
*/
|
||||
export function Badge({ children, variant = 'default', className }: Props) {
|
||||
return (
|
||||
<span className={cn('inline-block px-3 py-1 text-xs uppercase tracking-wider', VARIANTS[variant], className)}>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user