feat: add Label primitive

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-18 11:50:24 +01:00
parent ce3203c842
commit 10a9ccfd42
3 changed files with 65 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
.label {
font-family: var(--font-body);
font-size: 12px;
color: var(--text-primary);
font-weight: 500;
}
.asterisk {
color: var(--error);
margin-left: 2px;
}

View File

@@ -0,0 +1,34 @@
import { describe, it, expect } from 'vitest'
import { render, screen } from '@testing-library/react'
import { Label } from './Label'
describe('Label', () => {
it('renders label text', () => {
render(<Label>Email address</Label>)
expect(screen.getByText('Email address')).toBeInTheDocument()
})
it('does not show asterisk when required is not set', () => {
render(<Label>Username</Label>)
expect(screen.queryByText('*')).not.toBeInTheDocument()
})
it('shows asterisk when required', () => {
render(<Label required>Password</Label>)
expect(screen.getByText('*')).toBeInTheDocument()
})
it('passes htmlFor to the label element', () => {
render(<Label htmlFor="email-input">Email</Label>)
const label = screen.getByText('Email')
expect(label).toHaveAttribute('for', 'email-input')
})
it('forwards ref to the label element', () => {
let ref: HTMLLabelElement | null = null
render(
<Label ref={(el) => { ref = el }}>Ref test</Label>
)
expect(ref).toBeInstanceOf(HTMLLabelElement)
})
})

View File

@@ -0,0 +1,20 @@
import styles from './Label.module.css'
import { forwardRef, type LabelHTMLAttributes, type ReactNode } from 'react'
interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {
required?: boolean
children?: ReactNode
className?: string
}
export const Label = forwardRef<HTMLLabelElement, LabelProps>(
({ required, children, className, ...rest }, ref) => {
return (
<label ref={ref} className={`${styles.label} ${className ?? ''}`} {...rest}>
{children}
{required && <span className={styles.asterisk}>*</span>}
</label>
)
},
)
Label.displayName = 'Label'