import * as React from 'react' import { render, screen } from '@testing-library/react' import { describe, it, expect } from 'vitest' import { axe } from 'vitest-axe' import { ColorSwatch, type ColorSwatchProps } from './color-swatch' import { parseColor } from '../../lib/color' function renderSwatch(props: ColorSwatchProps) { return render() } const swatch = () => screen.getByTestId('swatch') describe('ColorSwatch', () => { it('paints an opaque color as a flat fill, with no checkerboard behind it', () => { renderSwatch({ color: '#3b82f6' }) expect(swatch().style.backgroundColor).toBe('rgb(59, 130, 246)') expect(swatch().style.backgroundImage).toBe('') }) it('accepts a Color object as well as a string', () => { renderSwatch({ color: parseColor('hsl(217, 91%, 60%)') }) expect(swatch().style.backgroundColor).toBe('rgb(60, 131, 246)') }) it('draws a translucent color over a checkerboard so the transparency reads', () => { renderSwatch({ color: 'rgba(59, 130, 246, 0.5)' }) expect(swatch().style.backgroundImage).toContain('linear-gradient(rgba(59, 130, 246, 0.5)') expect(swatch().style.backgroundImage).toContain('repeating-conic-gradient') // The tile size has to be its own declaration: `background-image` rejects the // `0 0 / size` shorthand and drops the whole rule. expect(swatch().style.backgroundSize).toBe('auto, 0.3em 0.3em') }) it('drops the checkerboard so a translucent color can blend into the surface', () => { renderSwatch({ color: 'rgba(59, 130, 246, 0.5)', checkerboard: false }) expect(swatch().style.backgroundImage).toBe('') expect(swatch().style.backgroundColor).toBe('rgba(59, 130, 246, 0.5)') }) it('names the color for assistive technology', () => { renderSwatch({ color: '#3b82f6' }) expect(screen.getByRole('img')).toHaveAccessibleName('vivid blue') }) it('describes grays and the extremes without reaching for a hue', () => { const { rerender } = renderSwatch({ color: '#000000' }) expect(swatch()).toHaveAccessibleName('black') rerender() expect(swatch()).toHaveAccessibleName('white') rerender() expect(swatch()).toHaveAccessibleName('gray') rerender() expect(swatch()).toHaveAccessibleName('transparent') }) it('reports the opacity as part of the name', () => { renderSwatch({ color: 'rgba(59, 130, 246, 0.5)' }) expect(swatch()).toHaveAccessibleName('vivid blue, 50% opacity') }) it('lets colorName replace the description and aria-label add context', () => { renderSwatch({ color: '#ff0000', colorName: 'Fire truck red', 'aria-label': 'Background color' }) expect(swatch()).toHaveAccessibleName('Fire truck red, Background color') }) it('sizes from a preset or an exact pixel number', () => { const { rerender } = renderSwatch({ color: '#3b82f6' }) // The box is 1em, so the preset scale and a number both land on font-size. expect(swatch()).toHaveClass('text-[2.5rem]') expect(swatch()).toHaveClass('size-[1em]') rerender() expect(swatch().style.fontSize).toBe('20px') expect(swatch()).not.toHaveClass('text-[2.5rem]') }) it('takes a size class override on the root', () => { renderSwatch({ color: '#3b82f6', className: 'size-10' }) expect(swatch()).toHaveClass('size-10') expect(swatch()).not.toHaveClass('size-[1em]') }) it('rounds the corners by default and goes fully round on request', () => { const { rerender } = renderSwatch({ color: '#3b82f6' }) expect(swatch().className).toContain('rounded-[calc(tan(atan2(var(--radius-md),2.5rem))*100%)]') rerender() expect(swatch()).toHaveClass('rounded-full') }) it('drops the color when disabled, so the flat fill can show', () => { renderSwatch({ color: 'rgba(59, 130, 246, 0.5)', disabled: true }) // An inline background would paint straight over the disabled class. expect(swatch()).toHaveAttribute('data-disabled') expect(swatch().style.backgroundImage).toBe('') expect(swatch().style.backgroundColor).toBe('') }) it('carries the shared border overlay', () => { renderSwatch({ color: '#3b82f6' }) expect(swatch().querySelector('[data-slot="color-swatch-surface"]')).toBeInTheDocument() }) it('renders children over the color', () => { renderSwatch({ color: '#3b82f6', children: ✓ }) expect(screen.getByTestId('check')).toBeInTheDocument() }) it('has no accessibility violations', async () => { const { container } = renderSwatch({ color: 'rgba(59, 130, 246, 0.5)' }) expect(await axe(container)).toHaveNoViolations() }) })