Repository: marigold-ui/marigold (ts)

Issue:
Clicking on a leading icon should focus the `<Input>`
## Description

The whole input area should make focus the `<input>`, currently clicking on a leading icon does not do that. E.g. in below image, clicking on the search icon will not focus the field.

![image](https://user-images.githubusercontent.com/985701/221583985-f89eb5cd-49a1-4af6-a22d-5f0fc27f624f.png)

## Proposal

- onClick listener or use CSS padding on the input?


Pull request description:
fix: clicking on icon and focus input
- removed wrapper for input

closes #2825 

Patch:
diff --git a/.changeset/calm-mugs-sneeze.md b/.changeset/calm-mugs-sneeze.md
new file mode 100644
index 000000000..c35149434
--- /dev/null
+++ b/.changeset/calm-mugs-sneeze.md
@@ -0,0 +1,10 @@
+---
+"@marigold/docs": minor
+"@marigold/components": minor
+"@marigold/system": minor
+"@marigold/theme-b2b": minor
+"@marigold/theme-core": minor
+"@marigold/theme-unicorn": minor
+---
+
+refa: Improved `<Input>` with icons/actions
diff --git a/docs/src/demos/components/Label/label-input.demo.tsx b/docs/src/demos/components/Label/label-input.demo.tsx
index 07843c7df..801a33e2c 100644
--- a/docs/src/demos/components/Label/label-input.demo.tsx
+++ b/docs/src/demos/components/Label/label-input.demo.tsx
@@ -3,8 +3,6 @@ import { Label, Input } from '@marigold/components';
 export const LabelInput = () => (
   <Label htmlFor="input">
     Type in your Firstname
-    <Input>
-      <Input.Field id="input" type="text" placeholder="Firstname" />
-    </Input>
+    <Input type="text" placeholder="Firstname" />
   </Label>
 );
diff --git a/packages/components/src/Autocomplete/Autocomplete.tsx b/packages/components/src/Autocomplete/Autocomplete.tsx
index 2286aacdc..2bb214985 100644
--- a/packages/components/src/Autocomplete/Autocomplete.tsx
+++ b/packages/components/src/Autocomplete/Autocomplete.tsx
@@ -133,17 +133,24 @@ export const Autocomplete = ({
         disabled={disabled}
         width={width}
       >
-        <Input>
-          <SearchIcon />
-          <Input.Field {...inputProps} ref={inputRef} />
-          {state.inputValue !== '' && (
-            <ClearButton
-              preventFocus
-              disabled={isDisabled}
-              {...restClearButtonProps}
-            />
-          )}
-        </Input>
+        <Input
+          /**
+           * We use `size` for styles which is a string, not like
+           * the regular HTML attribute, which is a number
+           */
+          {...(inputProps as any)}
+          ref={inputRef}
+          icon={<SearchIcon />}
+          action={
+            state.inputValue !== '' ? (
+              <ClearButton
+                preventFocus
+                disabled={isDisabled}
+                {...restClearButtonProps}
+              />
+            ) : undefined
+          }
+        />
       </FieldBase>
       <Popover
         state={state}
diff --git a/packages/components/src/Input/Input.stories.tsx b/packages/components/src/Input/Input.stories.tsx
index 1b09663eb..7edaf9fcd 100644
--- a/packages/components/src/Input/Input.stories.tsx
+++ b/packages/components/src/Input/Input.stories.tsx
@@ -1,10 +1,9 @@
 import React from 'react';
 import type { Meta, StoryObj } from '@storybook/react';
-import { Input } from './Input';
 import isChromatic from 'chromatic';
 import { Search, Delete } from '@marigold/icons';
 import { Button } from '../Button';
-import { InputField } from './InputField';
+import { Input } from './Input';
 const meta = {
   title: 'Components/Input',
   argTypes: {
@@ -36,17 +35,16 @@ const meta = {
       description: 'Pattern for the input',
     },
   },
-} satisfies Meta<typeof InputField>;
+  args: {
+    type: 'text',
+  },
+} satisfies Meta<typeof Input>;
 
 export default meta;
 type Story = StoryObj<typeof meta>;
 
 export const Basic: Story = {
-  render: args => (
-    <Input>
-      <Input.Field placeholder="Placeholder..." {...args} />
-    </Input>
-  ),
+  render: args => <Input placeholder="Placeholder..." {...args} />,
 };
 
 Basic.parameters = {
@@ -54,14 +52,33 @@ Basic.parameters = {
   theme: isChromatic() ? 'b2b' : 'stacked',
 };
 
+export const WithLeadingIcons: Story = {
+  render: () => <Input placeholder="Placeholder..." icon={<Search />} />,
+};
+
+export const WithAction: Story = {
+  render: () => (
+    <Input
+      placeholder="Placeholder..."
+      action={
+        <Button size="small" variant="text">
+          <Delete />
+        </Button>
+      }
+    />
+  ),
+};
+
 export const WithIcons: Story = {
-  render: args => (
-    <Input>
-      <Search />
-      <Input.Field placeholder="Placeholder..." />
-      <Button size="small" variant="text">
-        <Delete />
-      </Button>
-    </Input>
+  render: () => (
+    <Input
+      placeholder="Placeholder..."
+      icon={<Search />}
+      action={
+        <Button size="small" variant="text">
+          <Delete />
+        </Button>
+      }
+    />
   ),
 };
diff --git a/packages/components/src/Input/Input.tsx b/packages/components/src/Input/Input.tsx
old mode 100644
new mode 100755
index 9e147e1f3..6d0adf13f
--- a/packages/components/src/Input/Input.tsx
+++ b/packages/components/src/Input/Input.tsx
@@ -1,55 +1,88 @@
+import React, { forwardRef, ReactElement } from 'react';
+import { HtmlProps } from '@marigold/types';
 import {
   Box,
-  ResponsiveStyleValue,
-  ThemeExtension,
+  ThemeExtensionsWithParts,
   useComponentStyles,
+  useStateProps,
 } from '@marigold/system';
-import { HtmlProps } from '@marigold/types';
-import React, { ReactNode } from 'react';
-import { InputField } from './InputField';
 
 // Theme Extension
 // ---------------
-export interface InputThemeExtension extends ThemeExtension<'Input'> {}
+export interface InputThemeExtension
+  extends ThemeExtensionsWithParts<'Input', ['input', 'icon', 'container']> {}
 
 // Props
 // ---------------
-export interface InputProps extends Omit<HtmlProps<'div'>, 'size'> {
-  children: ReactNode;
-  space?: ResponsiveStyleValue<string>;
+export interface InputOwnProps extends Omit<HtmlProps<'input'>, 'size'> {
+  icon?: ReactElement;
+  action?: ReactElement;
   variant?: string;
   size?: string;
 }
 
+export interface InputProps
+  extends Omit<React.ComponentPropsWithRef<'input'>, 'size'>,
+    InputOwnProps {}
+
 // Component
 // ---------------
-export const Input = ({
-  space = 'xsmall',
-  children,
-  variant,
-  size,
-  ...props
-}: InputProps) => {
-  const [leading, input, trailing] = React.Children.toArray(children);
-
-  const styles = useComponentStyles('Input', { variant, size });
+export const Input = forwardRef<HTMLInputElement, InputOwnProps>(
+  (
+    { type = 'text', icon, action, variant, size, ...props }: InputOwnProps,
+    ref
+  ) => {
+    const styles = useComponentStyles(
+      'Input',
+      { variant, size },
+      { parts: ['input', 'icon', 'container'] }
+    );
 
-  return (
-    <Box
-      __baseCSS={{
-        display: 'flex',
-        alignItems: 'center',
-        width: '100%',
-        gap: space,
-      }}
-      css={styles}
-      {...props}
-    >
-      {leading}
-      {input}
-      {trailing}
-    </Box>
-  );
-};
+    const stateProps = useStateProps({
+      hasIcon: icon ? true : false,
+    });
 
-Input.Field = InputField;
+    return (
+      <Box
+        __baseCSS={{
+          position: 'relative',
+          display: 'flex',
+          alignItems: 'center',
+          width: '100%',
+        }}
+        css={styles.container}
+      >
+        <Box
+          __baseCSS={{ border: 0, width: '100%', outline: 'none', pl: 16 }}
+          {...props}
+          {...stateProps}
+          as="input"
+          ref={ref}
+          css={styles.input}
+          type={type}
+        />
+        {icon && (
+          <Box
+            __baseCSS={{
+              position: 'absolute',
+              pointerEvents: 'none',
+              left: 8,
+            }}
+            css={styles.icon}
+          >
+            {icon}
+          </Box>
+        )}
+        <Box
+          __baseCSS={{
+            display: 'inline-flex',
+            position: 'absolute',
+            right: 8,
+          }}
+        >
+          {action}
+        </Box>
+      </Box>
+    );
+  }
+);
diff --git a/packages/components/src/Input/InputField.tsx b/packages/components/src/Input/InputField.tsx
deleted file mode 100755
index 3bb409ddb..000000000
--- a/packages/components/src/Input/InputField.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React, { forwardRef } from 'react';
-import { HtmlProps } from '@marigold/types';
-import { Box } from '@marigold/system';
-
-// Props
-// ---------------
-export interface InputFieldOwnProps extends Omit<HtmlProps<'input'>, 'size'> {}
-
-export interface InputFieldProps
-  extends Omit<React.ComponentPropsWithRef<'input'>, 'size'>,
-    InputFieldOwnProps {}
-
-// Component
-// ---------------
-export const InputField = forwardRef<HTMLInputElement, InputFieldOwnProps>(
-  ({ type = 'text', ...props }: InputFieldOwnProps, ref) => {
-    return (
-      <Box
-        __baseCSS={{ border: 0, width: '100%', outline: 'none' }}
-        {...props}
-        ref={ref}
-        as="input"
-        type={type}
-      />
-    );
-  }
-);
diff --git a/packages/components/src/Input/index.ts b/packages/components/src/Input/index.ts
index bb7a7b126..ba9fe7ebc 100644
--- a/packages/components/src/Input/index.ts
+++ b/packages/components/src/Input/index.ts
@@ -1,2 +1,1 @@
-export * from './InputField';
 export * from './Input';
diff --git a/packages/components/src/NumberField/NumberField.tsx b/packages/components/src/NumberField/NumberField.tsx
index 10c07b403..45144bf84 100644
--- a/packages/components/src/NumberField/NumberField.tsx
+++ b/packages/components/src/NumberField/NumberField.tsx
@@ -151,17 +151,17 @@ export const NumberField = forwardRef<HTMLInputElement, NumberFieldProps>(
               {...decrementButtonProps}
             />
           )}
-          <Input variant={variant} size={size} {...stateProps}>
-            <Input.Field
-              ref={inputRef}
-              /**
-               * We use `size` for styles which is a string, not like
-               * the regular HTML attribute, which is a number
-               */
-              {...(inputProps as any)}
-              {...stateProps}
-            />
-          </Input>
+          <Input
+            ref={inputRef}
+            variant={variant}
+            size={size}
+            /**
+             * We use `size` for styles which is a string, not like
+             * the regular HTML attribute, which is a number
+             */
+            {...(inputProps as any)}
+            {...stateProps}
+          />
           {showStepper && (
             <StepButton
               direction="up"
diff --git a/packages/components/src/TextField/TextField.tsx b/packages/components/src/TextField/TextField.tsx
index 6b2f6da6a..edcd8d606 100644
--- a/packages/components/src/TextField/TextField.tsx
+++ b/packages/components/src/TextField/TextField.tsx
@@ -92,21 +92,19 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
         size={size}
         width={width}
       >
-        <Input {...stateProps}>
-          <Input.Field
-            ref={inputRef}
-            variant={variant}
-            size={size}
-            /**
-             * We use `size` for styles which is a string, not like
-             * the regular HTML attribute, which is a number
-             */
-            {...(inputProps as any)}
-            {...focusProps}
-            {...hoverProps}
-            {...stateProps}
-          />
-        </Input>
+        <Input
+          ref={inputRef}
+          variant={variant}
+          size={size}
+          /**
+           * We use `size` for styles which is a string, not like
+           * the regular HTML attribute, which is a number
+           */
+          {...(inputProps as any)}
+          {...focusProps}
+          {...hoverProps}
+          {...stateProps}
+        />
       </FieldBase>
     );
   }
diff --git a/packages/system/src/components/Box/selector.ts b/packages/system/src/components/Box/selector.ts
index 05b2182e5..e702f6e38 100644
--- a/packages/system/src/components/Box/selector.ts
+++ b/packages/system/src/components/Box/selector.ts
@@ -32,6 +32,7 @@ const state = {
   error: [':invalid', '[aria-invalid=true]', '[data-error]'],
   expanded: ['[aria-expanded=true]', '[data-expanded]'],
   required: [':required', '[aria-required]'],
+  hasIcon: ['[data-has-icon]'],
 };
 
 const pseudos = {
@@ -47,6 +48,7 @@ const pseudos = {
   '&:error': createteSelector([selector.self], state.error),
   '&:expanded': createteSelector([selector.self], state.expanded),
   '&:required': createteSelector([selector.self], state.required),
+  '&:has-icon': createteSelector([selector.self], state.hasIcon),
 
   // Selector for elements that are part of a group
   '&:in-group': createteSelector(selector.grouped, state.none, selector.self),
diff --git a/packages/system/src/hooks/useStateProps.ts b/packages/system/src/hooks/useStateProps.ts
index 34fc00a88..c3f3ef860 100644
--- a/packages/system/src/hooks/useStateProps.ts
+++ b/packages/system/src/hooks/useStateProps.ts
@@ -14,7 +14,8 @@ export type ComponentState =
   | 'selected'
   | 'indeterminate'
   | 'expanded'
-  | 'error';
+  | 'error'
+  | 'hasIcon';
 
 export type StateAttrKeyProps = `data-${KebabCase<ComponentState>}`;
 
diff --git a/themes/theme-b2b/src/components/Input.style.ts b/themes/theme-b2b/src/components/Input.style.ts
index 7297c143a..f35975a19 100644
--- a/themes/theme-b2b/src/components/Input.style.ts
+++ b/themes/theme-b2b/src/components/Input.style.ts
@@ -3,41 +3,50 @@ import { colors } from '../colors';
 
 export const Input: Theme['components']['Input'] = {
   base: {
-    color: 'text',
-    lineHeight: 'large',
-
-    bg: 'gray00',
-    border: '1px solid',
-    borderColor: 'gray40',
-    borderRadius: 'small',
-    outline: 'none',
-
-    py: 'none',
-    px: 'xsmall',
-
-    '&:hover': {
-      borderColor: 'gray50',
-    },
-
-    '&:focus': {
-      borderColor: 'orange60',
-      boxShadow: `0 0 0 1px ${colors.orange60}`,
+    input: {
+      color: 'text',
+      lineHeight: 'large',
+
+      bg: 'gray00',
+      border: '1px solid',
+      borderColor: 'gray40',
+      borderRadius: 'small',
+      outline: 'none',
+
+      pl: 'xsmall',
+      pr: 'xsmall',
+
+      '&:hover': {
+        borderColor: 'gray50',
+      },
+
+      '&:focus': {
+        borderColor: 'orange60',
+        outline: `1px solid ${colors.orange60}`,
+      },
+
+      '&:disabled': {
+        bg: 'gray20',
+        color: 'gray40',
+        cursor: 'not-allowed',
+      },
+
+      '&:error': {
+        borderColor: 'red60',
+        outline: `1px solid ${colors.red60}`,
+      },
+
+      '&:in-group': {
+        border: 'none',
+        boxShadow: 'none',
+      },
+
+      '&:has-icon': {
+        pl: 'large',
+      },
     },
-
-    '&:disabled': {
-      bg: 'gray20',
-      color: 'gray40',
-      cursor: 'not-allowed',
-    },
-
-    '&:error': {
-      borderColor: 'red60',
-      boxShadow: `0 0 0 1px ${colors.red60}`,
-    },
-
-    '&:in-group': {
-      border: 'none',
-      boxShadow: 'none',
+    icon: {
+      left: 'xxsmall',
     },
   },
 };
diff --git a/themes/theme-core/src/components/Input.style.ts b/themes/theme-core/src/components/Input.style.ts
index f45f2fb5c..b845d4620 100644
--- a/themes/theme-core/src/components/Input.style.ts
+++ b/themes/theme-core/src/components/Input.style.ts
@@ -2,11 +2,20 @@ import type { Theme } from '@marigold/components';
 
 export const Input: Theme['components']['Input'] = {
   base: {
-    border: '1px solid',
-    borderColor: '#aaa',
-    borderRadius: 'small',
-    lineHeight: 'medium',
-    py: '0',
-    px: 'xxsmall',
+    input: {
+      border: '1px solid',
+      borderColor: '#aaa',
+      borderRadius: 'small',
+      lineHeight: 'medium',
+      pl: 'xsmall',
+      pr: 'xsmall',
+
+      '&:has-icon': {
+        pl: 'large',
+      },
+    },
+    icon: {
+      left: 'xxsmall',
+    },
   },
 };
diff --git a/themes/theme-unicorn/src/components/Input.style.ts b/themes/theme-unicorn/src/components/Input.style.ts
index f96748609..9c901b1f3 100644
--- a/themes/theme-unicorn/src/components/Input.style.ts
+++ b/themes/theme-unicorn/src/components/Input.style.ts
@@ -3,40 +3,47 @@ import { colors } from '../colors';
 
 export const Input: Theme['components']['Input'] = {
   base: {
-    color: 'text',
-    lineHeight: 'large',
-
-    border: '1px solid',
-    borderColor: 'gray40',
-    borderRadius: 'small',
-    outline: 'none',
-
-    py: 'none',
-    px: 'xsmall',
-
-    '&:hover': {
-      borderColor: 'purple40',
-    },
-
-    '&:focus': {
-      borderColor: 'purple60',
-      boxShadow: `0 0 0 1px ${colors.purple60}`,
+    input: {
+      color: 'text',
+      lineHeight: 'large',
+
+      border: '1px solid',
+      borderColor: 'gray40',
+      borderRadius: 'small',
+      outline: 'none',
+      pl: 'xsmall',
+      pr: 'xsmall',
+
+      '&:hover': {
+        borderColor: 'purple40',
+      },
+
+      '&:focus': {
+        borderColor: 'purple60',
+        outline: `1px solid ${colors.purple60}`,
+      },
+
+      '&:disabled': {
+        bg: 'gray20',
+        color: 'gray40',
+        cursor: 'not-allowed',
+      },
+
+      '&:error': {
+        borderColor: 'red60',
+        outline: `1px solid ${colors.red60}`,
+      },
+
+      '&:in-group': {
+        border: 'none',
+        boxShadow: 'none',
+      },
+      '&:has-icon': {
+        pl: 'large',
+      },
     },
-
-    '&:disabled': {
-      bg: 'gray20',
-      color: 'gray40',
-      cursor: 'not-allowed',
-    },
-
-    '&:error': {
-      borderColor: 'red60',
-      boxShadow: `0 0 0 1px ${colors.red60}`,
-    },
-
-    '&:in-group': {
-      border: 'none',
-      boxShadow: 'none',
+    icon: {
+      left: 'xxsmall',
     },
   },
 };


Test patch:
diff --git a/packages/components/src/Input/Input.test.tsx b/packages/components/src/Input/Input.test.tsx
index d2c15a777..2498866e8 100644
--- a/packages/components/src/Input/Input.test.tsx
+++ b/packages/components/src/Input/Input.test.tsx
@@ -1,8 +1,10 @@
+/* eslint-disable testing-library/no-node-access */
 import React from 'react';
 import { render, screen } from '@testing-library/react';
 import { ThemeProvider } from '@marigold/system';
 import { Input } from './Input';
 import { Delete, Search } from '@marigold/icons';
+import { Button } from '../Button';
 
 const theme = {
   fonts: {
@@ -12,11 +14,15 @@ const theme = {
   components: {
     Input: {
       base: {
-        fontFamily: 'body',
+        input: {
+          fontFamily: 'body',
+        },
       },
       variant: {
         robo: {
-          fontFamily: 'forms',
+          input: {
+            fontFamily: 'forms',
+          },
         },
       },
     },
@@ -26,12 +32,11 @@ const theme = {
 test('supports default variant and themeSection', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Input data-testid="input">
-        <Input.Field title="input" />
-      </Input>
+      <Input title="input" />
     </ThemeProvider>
   );
-  const input = screen.getByTestId('input');
+
+  const input = screen.getByTitle('input');
 
   expect(input).toHaveStyle(`font-family: Inter`);
 });
@@ -39,12 +44,10 @@ test('supports default variant and themeSection', () => {
 test('accepts other variant than default', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Input data-testid="input" variant="robo">
-        <Input.Field title="input" />
-      </Input>
+      <Input data-testid="input" variant="robo" title="input" />
     </ThemeProvider>
   );
-  const input = screen.getByTestId('input');
+  const input = screen.getByTitle('input');
 
   expect(input).toHaveStyle(`font-family: Roboto`);
 });
@@ -52,9 +55,7 @@ test('accepts other variant than default', () => {
 test('renders correct HTML element', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Input data-testid="input">
-        <Input.Field title="input" />
-      </Input>
+      <Input title="input" />
     </ThemeProvider>
   );
   const input = screen.getByTitle('input');
@@ -65,9 +66,7 @@ test('renders correct HTML element', () => {
 test('supports custom prop', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Input data-testid="input">
-        <Input.Field title="input" placeholder="placeholder" />
-      </Input>
+      <Input title="input" placeholder="placeholder" />
     </ThemeProvider>
   );
   const input = screen.getByTitle('input');
@@ -75,43 +74,32 @@ test('supports custom prop', () => {
   expect(input).toHaveAttribute('placeholder');
 });
 
-test('renders correct HTML Div element', () => {
+test('renders correct HTML Div parentelement', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Input data-testid="input">
-        <Input.Field title="input" />
-      </Input>
+      <Input title="input" />
     </ThemeProvider>
   );
-  const input = screen.getByTestId('input');
-
-  expect(input instanceof HTMLDivElement).toBeTruthy();
-});
-
-test('renders space prop', () => {
-  render(
-    <ThemeProvider theme={theme}>
-      <Input data-testid="input" space="4px">
-        <Input.Field title="input" />
-      </Input>
-    </ThemeProvider>
-  );
-  const input = screen.getByTestId('input');
+  const input = screen.getByTitle('input');
 
-  expect(input).toHaveStyle('gap: 4px');
+  expect(input.parentElement instanceof HTMLDivElement).toBeTruthy();
 });
 
 test('renders icons within', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Input data-testid="input">
-        <Search />
-        <Input.Field title="input" />
-        <Delete />
-      </Input>
+      <Input
+        title="input"
+        icon={<Search />}
+        action={
+          <Button>
+            <Delete />
+          </Button>
+        }
+      />
     </ThemeProvider>
   );
-  const input = screen.getByTestId('input');
+  const input = screen.getByTitle('input');
 
   // eslint-disable-next-line testing-library/no-node-access
   expect(input.children).toBeTruthy();
diff --git a/packages/components/src/NumberField/NumberField.test.tsx b/packages/components/src/NumberField/NumberField.test.tsx
index 99c2c016e..5093e5589 100644
--- a/packages/components/src/NumberField/NumberField.test.tsx
+++ b/packages/components/src/NumberField/NumberField.test.tsx
@@ -135,7 +135,7 @@ test('input can be styled via "Input" styles', () => {
   );
   const numberField = screen.getByTestId('number-field');
   // eslint-disable-next-line testing-library/no-node-access
-  expect(numberField.parentElement).toHaveStyle(
+  expect(numberField.parentElement?.parentElement).toHaveStyle(
     `border-color: ${theme.colors.blue}`
   );
 });
diff --git a/packages/components/src/TextField/TextField.test.tsx b/packages/components/src/TextField/TextField.test.tsx
index e4cb590f8..e9b41494a 100644
--- a/packages/components/src/TextField/TextField.test.tsx
+++ b/packages/components/src/TextField/TextField.test.tsx
@@ -49,7 +49,9 @@ const theme = {
     },
     Input: {
       base: {
-        borderColor: 'blue',
+        input: {
+          borderColor: 'blue',
+        },
       },
     },
   },
@@ -80,9 +82,7 @@ test('input can be styled via "Input" styles', () => {
     </ThemeProvider>
   );
   const textField = screen.getByTestId('text-field');
-  expect(textField.parentElement).toHaveStyle(
-    `border-color: ${theme.colors.blue}`
-  );
+  expect(textField).toHaveStyle(`border-color: ${theme.colors.blue}`);
 });
 
 test('takes full width by default', () => {
diff --git a/packages/system/src/components/Box/selector.test.ts b/packages/system/src/components/Box/selector.test.ts
index 97f9ca721..a73174e6f 100644
--- a/packages/system/src/components/Box/selector.test.ts
+++ b/packages/system/src/components/Box/selector.test.ts
@@ -165,6 +165,15 @@ describe('transform states', () => {
           {},
       },
     },
+    {
+      name: ':has-icon',
+      input: {
+        '&:has-icon': {},
+      },
+      expected: {
+        '&[data-has-icon]': {},
+      },
+    },
   ])('tramsform "$name" to selector', ({ input, expected }) => {
     expect(transformPseudos(input)).toEqual(expected);
   });