import { describe, it, expect, vi } from 'vitest' import { fireEvent, render, screen, within } from '@testing-library/react' import { CategoryRowStacked } from './category-row-stacked' const COLORS = ['rgb(0, 200, 0)', 'rgb(0, 100, 0)'] describe('', () => { it('renders displayName ?? name on the left of the header', () => { render( String(n)} selected={false} onToggle={vi.fn()} />, ) expect(screen.getByText('Cat')).toBeTruthy() expect(screen.queryByText('cat-key')).toBeNull() }) it('falls back to the raw name when displayName is omitted', () => { render( String(n)} selected={false} onToggle={vi.fn()} />, ) expect(screen.getByText('Plain')).toBeTruthy() }) it('renders formatter(sum) on the right of the header', () => { render( `${n}K`} selected={false} onToggle={vi.fn()} />, ) expect(screen.getByText('77K')).toBeTruthy() }) it('renders one breakdown entry per series, including zero values', () => { render( String(n)} selected={false} onToggle={vi.fn()} />, ) expect(screen.getByText('Women: 45')).toBeTruthy() expect(screen.getByText('Other: 0')).toBeTruthy() expect(screen.getByText('Men: 32')).toBeTruthy() }) it('falls back to "Series N" for missing series names (1-indexed)', () => { render( String(n)} selected={false} onToggle={vi.fn()} />, ) expect(screen.getByText('Series 1: 45')).toBeTruthy() expect(screen.getByText('Series 2: 32')).toBeTruthy() }) it('per-row normalization: bar fills 100% with segments proportional to the row sum', () => { // values [30, 70] sum to 100 → segments at 30% and 70%, cumulative // offsets 0% and 30%. The bar saturates regardless of any // hypothetical global max — that's the point of stacked mode. const { container } = render( String(n)} selected={false} onToggle={vi.fn()} />, ) const fills = container.querySelectorAll( '[data-bar-fill="true"]', ) expect(fills.length).toBe(2) expect(fills[0]!.style.left).toBe('0%') expect(fills[0]!.style.width).toBe('30%') expect(fills[1]!.style.left).toBe('30%') expect(fills[1]!.style.width).toBe('70%') }) it('per-row normalization: bar still fills 100% for an arbitrary row total (45 + 32 → 77)', () => { const { container } = render( String(n)} selected={false} onToggle={vi.fn()} />, ) const fills = container.querySelectorAll( '[data-bar-fill="true"]', ) // 45/77 ≈ 58.44…, 32/77 ≈ 41.56… const w0 = parseFloat(fills[0]!.style.width) const w1 = parseFloat(fills[1]!.style.width) expect(w0 + w1).toBeCloseTo(100, 1) expect(w0).toBeGreaterThan(58) expect(w0).toBeLessThan(59) expect(w1).toBeGreaterThan(41) expect(w1).toBeLessThan(42) }) it('renders the stacked bar with one data-bar-fill per non-zero series', () => { const { container } = render( String(n)} selected={false} onToggle={vi.fn()} />, ) const fills = container.querySelectorAll('[data-bar-fill="true"]') // Zero series is skipped in the bar, present in the breakdown. expect(fills).toHaveLength(2) }) it('clicking anywhere in the row toggles by raw name', () => { const onToggle = vi.fn() render( String(n)} selected={false} onToggle={onToggle} />, ) fireEvent.click(screen.getByRole('button')) expect(onToggle).toHaveBeenCalledWith('raw-key') }) it('toggles via keyboard Enter and Space, preventing default on space', () => { const onToggle = vi.fn() render( String(n)} selected={false} onToggle={onToggle} />, ) const row = screen.getByRole('button') fireEvent.keyDown(row, { key: 'Enter' }) expect(onToggle).toHaveBeenCalledWith('K') onToggle.mockClear() fireEvent.keyDown(row, { key: ' ' }) expect(onToggle).toHaveBeenCalledWith('K') }) it('reflects selection via aria-pressed', () => { const { rerender } = render( String(n)} selected={false} onToggle={vi.fn()} />, ) expect(screen.getByRole('button').getAttribute('aria-pressed')).toBe( 'false', ) rerender( String(n)} selected={true} onToggle={vi.fn()} />, ) expect(screen.getByRole('button').getAttribute('aria-pressed')).toBe('true') }) it('when dim colors are passed in, every segment paints with the dim color (breakdown text unchanged)', () => { const DIM = 'rgba(0, 0, 0, 0.26)' render( String(n)} selected={false} onToggle={vi.fn()} />, ) const row = screen.getByRole('button') const fills = row.querySelectorAll('[data-bar-fill="true"]') expect(fills.length).toBe(2) fills.forEach((f) => expect(f.style.backgroundColor).toBe(DIM)) // Breakdown text remains visible and unchanged. expect(within(row).getByText('Women: 45')).toBeTruthy() expect(within(row).getByText('Men: 32')).toBeTruthy() }) })