diff --git a/packages/emcn/src/components/chip-input/chip-input.test.tsx b/packages/emcn/src/components/chip-input/chip-input.test.tsx new file mode 100644 index 00000000000..ed7c1a54f9f --- /dev/null +++ b/packages/emcn/src/components/chip-input/chip-input.test.tsx @@ -0,0 +1,38 @@ +/** + * @vitest-environment jsdom + */ +import { act } from 'react' +import { createRoot, type Root } from 'react-dom/client' +import { afterEach, describe, expect, it } from 'vitest' +import { ChipInput } from './chip-input' + +let root: Root | null = null +let container: HTMLDivElement | null = null + +function mount(): HTMLInputElement { + ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true + container = document.createElement('div') + document.body.appendChild(container) + root = createRoot(container) + act(() => root?.render()) + + const input = container.querySelector('input') + if (!input) throw new Error('ChipInput did not render an input') + return input +} + +afterEach(() => { + if (root) act(() => root?.unmount()) + container?.remove() + root = null + container = null +}) + +describe('ChipInput', () => { + it('reserves paintable clearance for a leading glyph without shifting its alignment', () => { + const input = mount() + + expect(input.className).toContain('-ml-1') + expect(input.className).toContain('indent-1') + }) +}) diff --git a/packages/emcn/src/components/chip-input/chip-input.tsx b/packages/emcn/src/components/chip-input/chip-input.tsx index 1266a9aa5b9..3f668691bd7 100644 --- a/packages/emcn/src/components/chip-input/chip-input.tsx +++ b/packages/emcn/src/components/chip-input/chip-input.tsx @@ -9,10 +9,11 @@ * * The chrome lives on the wrapper so a leading `icon` and a trailing * `endAdornment` (reveal / copy buttons) sit flush next to a transparent inner - * ``. The leading icon uses the same 1.5 gap as `Chip`. It shares the - * chip-field chrome with {@link ChipTextarea}, shows no focus ring — keep the - * surface calm and rely on the caret for focus. Pass `error` to swap the border - * to the error token. + * ``. Matching negative margin and text indent give leading glyphs paint + * clearance without moving their visual alignment. The leading icon uses the + * same 1.5 gap as `Chip`. It shares the chip-field chrome with + * {@link ChipTextarea}, shows no focus ring — keep the surface calm and rely on + * the caret for focus. Pass `error` to swap the border to the error token. * * @example * ```tsx @@ -77,7 +78,7 @@ export const ChipInput = React.forwardRef( type={type} disabled={disabled} className={cn( - 'h-full w-full bg-transparent disabled:cursor-not-allowed', + '-ml-1 h-full w-full bg-transparent indent-1 disabled:cursor-not-allowed', chipFieldTextClass, inputClassName )}