diff --git a/packages/apollo-wind/src/components/ui/input.stories.tsx b/packages/apollo-wind/src/components/ui/input.stories.tsx index 5d4f64c4b..0f6086436 100644 --- a/packages/apollo-wind/src/components/ui/input.stories.tsx +++ b/packages/apollo-wind/src/components/ui/input.stories.tsx @@ -19,9 +19,9 @@ const meta = { argTypes: { variant: { control: 'select', - options: ['default', 'ghost'], + options: ['default', 'ghost', 'none'], description: - 'Visual style. Ghost removes the border and sets a surface background, suited for compact panel inputs.', + 'Visual style. Ghost removes the border and sets a surface background, suited for compact panel inputs. None paints no box in any theme, for a field whose className draws its own.', }, size: { control: 'select', @@ -120,6 +120,22 @@ export const Ghost: Story = { }, }; +export const None: Story = { + parameters: { + docs: { + description: { + story: + 'None paints no box, so className draws one that holds in every theme. Under Future, a default Input switches to its own 40px borderless box, while this one keeps 34px with a border.', + }, + }, + }, + args: { + variant: 'none', + placeholder: 'Boxed by className', + className: 'h-[34px] rounded border border-input bg-background px-2 text-sm', + }, +}; + export const CompactSearch: Story = { render: () => (
diff --git a/packages/apollo-wind/src/components/ui/input.test.tsx b/packages/apollo-wind/src/components/ui/input.test.tsx index 12992dd00..09f4c459c 100644 --- a/packages/apollo-wind/src/components/ui/input.test.tsx +++ b/packages/apollo-wind/src/components/ui/input.test.tsx @@ -125,4 +125,23 @@ describe('Input', () => { expect(input).not.toHaveClass('future:focus-visible:ring-primary'); }); }); + + it('applies none variant without any future theme box classes', () => { + render(); + const input = screen.getByRole('textbox'); + expect(input).toHaveClass('border-0', 'bg-transparent'); + expect(input).not.toHaveClass('border-input'); + expect( + [...input.classList].filter( + (name) => name.startsWith('future:') && !name.includes('focus-visible:ring-') + ) + ).toEqual([]); + }); + + it('lets className draw the box for the none variant', () => { + render(); + const input = screen.getByRole('textbox'); + expect(input).toHaveClass('h-[34px]', 'rounded', 'border', 'bg-background'); + expect(input).not.toHaveClass('h-9', 'rounded-md', 'border-0', 'bg-transparent'); + }); }); diff --git a/packages/apollo-wind/src/components/ui/input.tsx b/packages/apollo-wind/src/components/ui/input.tsx index 0a9fbae89..8912b237a 100644 --- a/packages/apollo-wind/src/components/ui/input.tsx +++ b/packages/apollo-wind/src/components/ui/input.tsx @@ -9,7 +9,8 @@ const IN_GROUP_CLASS = 'h-full w-full rounded-none !border-0 !ring-0 bg-transparent p-0 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0 future:h-full future:rounded-none future:border-0 future:bg-transparent future:p-0 future:focus-visible:ring-offset-0'; export interface InputProps extends Omit, 'size'> { - variant?: 'default' | 'ghost'; + /** `none` paints no box in any theme, for a caller whose `className` draws its own. */ + variant?: 'default' | 'ghost' | 'none'; size?: 'default' | 'xs'; /** * Field-specific feedback rendered immediately below the input. @@ -71,6 +72,7 @@ const Input = React.forwardRef( // Variant variant === 'default' && 'border border-input bg-transparent', variant === 'ghost' && 'border-0 bg-surface-overlay', + variant === 'none' && 'border-0 bg-transparent shadow-none', // Future theme overrides apply only to the default variant + default size variant === 'default' && size === 'default' &&