/** * ColorField — the shared color-input primitive (swatch + optional hex field). * Covers the render shapes and the hex-entry commit/normalize behavior; the * Theme Builder and Chart Builder integrations are exercised in their own tests. */ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { act } from 'react'; import { createRoot, type Root } from 'react-dom/client'; import { ColorField } from './ColorField'; (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; let container: HTMLDivElement; let root: Root; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); act(() => { root = createRoot(container); }); }); afterEach(() => { act(() => root.unmount()); container.remove(); }); /** Drive an input's value through the native setter so React's onChange fires. */ function setNativeValue(el: HTMLInputElement, value: string) { // eslint-disable-next-line @typescript-eslint/unbound-method -- invoked immediately via .call const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')!.set!; setter.call(el, value); el.dispatchEvent(new Event('input', { bubbles: true })); } const swatch = () => container.querySelector('input[type="color"]')!; const hexField = () => container.querySelector('input[type="text"]'); describe('ColorField', () => { test('renders just the swatch by default (no hex field)', () => { act(() => root.render( {}} />)); expect(swatch().getAttribute('aria-label')).toBe('Fill'); expect(swatch().value).toBe('#112233'); expect(hexField()).toBeNull(); }); test('falls back to #000000 in the picker for a non-hex value', () => { act(() => root.render( {}} />)); expect(swatch().value).toBe('#000000'); }); test('the picker reports its raw value on change', () => { const onChange = vi.fn(); act(() => root.render()); act(() => setNativeValue(swatch(), '#ff0000')); expect(onChange).toHaveBeenCalledWith('#ff0000'); }); test('hex mode commits a valid hex and normalizes an unprefixed one', () => { const onChange = vi.fn(); act(() => root.render()); const hex = hexField()!; expect(hex.getAttribute('aria-label')).toBe('Fill hex value'); act(() => setNativeValue(hex, '#abcdef')); expect(onChange).toHaveBeenLastCalledWith('#abcdef'); act(() => setNativeValue(hex, 'ABCDEF')); expect(onChange).toHaveBeenLastCalledWith('#abcdef'); }); test('hex mode does not commit an incomplete value', () => { const onChange = vi.fn(); act(() => root.render()); act(() => setNativeValue(hexField()!, '#abc')); expect(onChange).not.toHaveBeenCalled(); }); });