mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
81 lines
3.0 KiB
TypeScript
81 lines
3.0 KiB
TypeScript
/**
|
|
* 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<HTMLInputElement>('input[type="color"]')!;
|
|
const hexField = () => container.querySelector<HTMLInputElement>('input[type="text"]');
|
|
|
|
describe('ColorField', () => {
|
|
test('renders just the swatch by default (no hex field)', () => {
|
|
act(() => root.render(<ColorField value="#112233" label="Fill" onChange={() => {}} />));
|
|
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(<ColorField value="steelblue" label="Fill" onChange={() => {}} />));
|
|
expect(swatch().value).toBe('#000000');
|
|
});
|
|
|
|
test('the picker reports its raw value on change', () => {
|
|
const onChange = vi.fn();
|
|
act(() => root.render(<ColorField value="#112233" label="Fill" onChange={onChange} />));
|
|
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(<ColorField value="#112233" label="Fill" onChange={onChange} hex />));
|
|
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(<ColorField value="#112233" label="Fill" onChange={onChange} hex />));
|
|
act(() => setNativeValue(hexField()!, '#abc'));
|
|
expect(onChange).not.toHaveBeenCalled();
|
|
});
|
|
});
|