Repository: marigold-ui/marigold
Pull request #2688 (2022-12-14 14:50:35)

feat: add <Fieldbase> to <RadioGroup> and <CheckboxGroup>

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: 542761eb951420a9dc932286f523baf1420e6b71

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/marigold-ui/marigold/new/add-fieldbase-to-radio-and-checkbox?filename=.changeset/curly-tables-unite.md&value=---%0A%22%40marigold%2Fdocs%22%3A%20patch%0A%22%40marigold%2Fcomponents%22%3A%20patch%0A%22%40marigold%2Ftheme-b2b%22%3A%20patch%0A%22%40marigold%2Ftheme-unicorn%22%3A%20patch%0A---%0A%0Afeat%3A%20add%20%3CFieldbase%3E%20to%20%3CRadio%3E%20and%20%3CCheckbox%3E%0A)



Patch:
diff --git a/.changeset/real-papayas-leave.md b/.changeset/real-papayas-leave.md
new file mode 100644
index 000000000..53107cbeb
--- /dev/null
+++ b/.changeset/real-papayas-leave.md
@@ -0,0 +1,8 @@
+---
+"@marigold/docs": minor
+"@marigold/components": minor
+"@marigold/theme-b2b": minor
+"@marigold/theme-unicorn": minor
+---
+
+feat: add <Fieldbase> to <RadioGroup> and <CheckboxGroup> to support errorMessage and helptext
diff --git a/docs/src/demos/components/Checkbox/checkbox-group.demo.tsx b/docs/src/demos/components/Checkbox/checkbox-group.demo.tsx
index e0c803cad..6c23d2233 100644
--- a/docs/src/demos/components/Checkbox/checkbox-group.demo.tsx
+++ b/docs/src/demos/components/Checkbox/checkbox-group.demo.tsx
@@ -5,7 +5,11 @@ export const CheckboxGroupoDemo = () => {
   const [selected, setSelected] = useState<string[]>([]);
   return (
     <>
-      <CheckboxGroup label="Choose your toppings:" onChange={setSelected}>
+      <CheckboxGroup
+        label="Choose your toppings:"
+        onChange={setSelected}
+        description="Just click on the options"
+      >
         <Checkbox value="ham">🐖 Ham</Checkbox>
         <Checkbox value="beef" disabled>
           🐄 Beef (out of stock)
diff --git a/docs/src/demos/components/Radio/index.ts b/docs/src/demos/components/Radio/index.ts
index 8eb8e8adc..dc1438c93 100644
--- a/docs/src/demos/components/Radio/index.ts
+++ b/docs/src/demos/components/Radio/index.ts
@@ -1,3 +1,4 @@
 export * from './radio.demo';
 export * from './radio-disabled.demo';
 export * from './radio-required.demo';
+export * from './radio-error.demo';
diff --git a/docs/src/demos/components/Radio/radio-error.demo.tsx b/docs/src/demos/components/Radio/radio-error.demo.tsx
new file mode 100644
index 000000000..283813130
--- /dev/null
+++ b/docs/src/demos/components/Radio/radio-error.demo.tsx
@@ -0,0 +1,16 @@
+import { Radio } from '@marigold/components';
+
+export const ErrorRadio = () => (
+  <Radio.Group
+    label="Radio Group"
+    error
+    errorMessage="Oh no! An error occured."
+  >
+    <Radio value="1">Option 1</Radio>
+    <Radio value="2">Option 2</Radio>
+    <Radio value="3" disabled>
+      Option 3
+    </Radio>
+    <Radio value="4">Option 4</Radio>
+  </Radio.Group>
+);
diff --git a/docs/src/pages/components/checkbox.mdx b/docs/src/pages/components/checkbox.mdx
index 88094a541..d83df8fd5 100644
--- a/docs/src/pages/components/checkbox.mdx
+++ b/docs/src/pages/components/checkbox.mdx
@@ -150,6 +150,18 @@ import { Checkbox, CheckboxGroup } from '@marigold/components';
       type: '(value: string[]) => void',
       description: 'Handler that is called when the checkbox value changes.',
       default: 'none',
+    },
+    {
+      property: 'description',
+      type: 'string',
+      description: 'Sets a helptext for the group.',
+      default: 'none',
+    },
+    {
+      property: 'errorMessage',
+      type: 'string',
+      description: 'Sets a error message for the group if a error occured.',
+      default: 'none',
     }
 
 ]}
@@ -175,7 +187,7 @@ Use indeterminate state when it represents both selected and not selected values
 
 ### Group checkboxes
 
-In this example the `<Checkbox>` are wrapped within the `<CheckboxGroup>`. You see that the `onChange` property is also set.
+In this example the `<Checkbox>` are wrapped within the `<CheckboxGroup>`. You see that the `onChange` property is also set. It has also a helptext within which you can set with the `description` property for the `<CheckboxGroup>`.
 
 ```tsx preview file=components/Checkbox/checkbox-group.demo.tsx
 
diff --git a/docs/src/pages/components/radio.mdx b/docs/src/pages/components/radio.mdx
index a0ef712fb..482767bd7 100644
--- a/docs/src/pages/components/radio.mdx
+++ b/docs/src/pages/components/radio.mdx
@@ -117,6 +117,18 @@ import { Radio } from '@marigold/components';
       description: 'Set the radio group disabled.',
       default: 'false',
     },
+    {
+      property: 'description',
+      type: 'string',
+      description: 'Set the radio group helptext.',
+      default: 'none',
+    },
+    {
+      property: 'errorMessage',
+      type: 'string',
+      description: 'Set the radio group error message if an error occures.',
+      default: 'none',
+    },
   ]}
 />
 
@@ -138,10 +150,18 @@ Here you can see a complete disabled `<Radio.Group>`. You can also just disable
 
 ```
 
-### Required radio with horizontal orientation
+### Required Radio with horizontal orientation
 
 In this example the `<Radio.Group>` is required and the `orientation` is set to `horizontal`. With these props you can determine the direction of the `<Radio>` options.
 
 ```tsx preview file=components/Radio/radio-required.demo.tsx
 
 ```
+
+### Error Radio
+
+In this example you can see how an `errorMessage` would look on a `<RadioGroup>`.
+
+```tsx preview file=components/Radio/radio-error.demo.tsx
+
+```
diff --git a/packages/components/src/Checkbox/Checkbox.tsx b/packages/components/src/Checkbox/Checkbox.tsx
index 235441eaf..45347090e 100644
--- a/packages/components/src/Checkbox/Checkbox.tsx
+++ b/packages/components/src/Checkbox/Checkbox.tsx
@@ -173,8 +173,8 @@ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
     const styles = useComponentStyles(
       'Checkbox',
       {
-        variant: groupState?.variant || variant,
-        size: groupState?.size || size,
+        variant,
+        size,
       },
       { parts: ['container', 'label', 'checkbox'] }
     );
diff --git a/packages/components/src/Checkbox/CheckboxGroup.stories.tsx b/packages/components/src/Checkbox/CheckboxGroup.stories.tsx
index c984e287a..0a5e05d68 100644
--- a/packages/components/src/Checkbox/CheckboxGroup.stories.tsx
+++ b/packages/components/src/Checkbox/CheckboxGroup.stories.tsx
@@ -49,7 +49,37 @@ export const Basic: ComponentStory<typeof CheckboxGroup> = args => {
   const [selected, setSelected] = React.useState<string[]>([]);
   return (
     <>
-      <CheckboxGroup {...args} onChange={setSelected}>
+      <CheckboxGroup
+        {...args}
+        onChange={setSelected}
+        description="Choose your Options"
+        errorMessage="Oh no"
+      >
+        <Checkbox value="ham">Ham</Checkbox>
+        <Checkbox value="salami" disabled>
+          Salami
+        </Checkbox>
+        <Checkbox value="cheese">Cheese</Checkbox>
+        <Checkbox value="tomato">Tomate</Checkbox>
+        <Checkbox value="cucumber">Cucumber</Checkbox>
+        <Checkbox value="onions">Onions</Checkbox>
+      </CheckboxGroup>
+      <hr />
+      <pre>Selected values: {selected.join(', ')}</pre>
+    </>
+  );
+};
+
+export const Error: ComponentStory<typeof CheckboxGroup> = args => {
+  const [selected, setSelected] = React.useState<string[]>([]);
+  return (
+    <>
+      <CheckboxGroup
+        onChange={setSelected}
+        error
+        errorMessage="This is an error"
+        {...args}
+      >
         <Checkbox value="ham">Ham</Checkbox>
         <Checkbox value="salami" disabled>
           Salami
diff --git a/packages/components/src/Checkbox/CheckboxGroup.tsx b/packages/components/src/Checkbox/CheckboxGroup.tsx
index 6bf28925b..647573676 100644
--- a/packages/components/src/Checkbox/CheckboxGroup.tsx
+++ b/packages/components/src/Checkbox/CheckboxGroup.tsx
@@ -6,21 +6,15 @@ import {
 } from '@react-stately/checkbox';
 import { AriaCheckboxGroupProps } from '@react-types/checkbox';
 
-import {
-  Box,
-  ThemeExtensionsWithParts,
-  useComponentStyles,
-} from '@marigold/system';
+import { Box, useStateProps } from '@marigold/system';
 import { ComponentProps } from '@marigold/types';
 
-import { Label } from '../Label';
+import { FieldBase } from '../FieldBase';
 
 // Context
 // ---------------
 export interface CheckboxGroupContextProps extends CheckboxGroupState {
   error?: boolean;
-  variant?: string;
-  size?: string;
 }
 
 /**
@@ -32,11 +26,6 @@ export const CheckboxGroupContext = createContext<CheckboxGroupContextProps>(
 );
 export const useCheckboxGroupContext = () => useContext(CheckboxGroupContext);
 
-// Theme Extension
-// ---------------
-export interface CheckboxGroupThemeExtension
-  extends ThemeExtensionsWithParts<'CheckboxGroup', ['container', 'group']> {}
-
 // Props
 // ---------------
 interface CheckboxGroupProps
@@ -59,8 +48,6 @@ interface CheckboxGroupProps
 // ---------------
 export const CheckboxGroup = ({
   children,
-  variant,
-  size,
   required,
   disabled,
   readOnly,
@@ -76,21 +63,29 @@ export const CheckboxGroup = ({
   } as const;
 
   const state = useCheckboxGroupState(props);
-  const { groupProps, labelProps } = useCheckboxGroup(props, state);
+  const { groupProps, labelProps, descriptionProps, errorMessageProps } =
+    useCheckboxGroup(props, state);
 
-  const styles = useComponentStyles(
-    'CheckboxGroup',
-    { variant, size },
-    { parts: ['container', 'group'] }
-  );
+  const stateProps = useStateProps({
+    disabled,
+    readOnly,
+    error,
+  });
 
   return (
-    <Box {...groupProps} css={styles.container}>
-      {props.label && (
-        <Label as="span" required={required} {...labelProps}>
-          {props.label}
-        </Label>
-      )}
+    <FieldBase
+      label={props.label}
+      labelProps={{ as: 'span', ...labelProps }}
+      description={props.description}
+      descriptionProps={descriptionProps}
+      error={error}
+      errorMessage={props.errorMessage}
+      errorMessageProps={errorMessageProps}
+      disabled={disabled}
+      stateProps={stateProps}
+      required={required}
+      {...groupProps}
+    >
       <Box
         role="presentation"
         __baseCSS={{
@@ -98,14 +93,11 @@ export const CheckboxGroup = ({
           flexDirection: 'column',
           alignItems: 'start',
         }}
-        css={styles.group}
       >
-        <CheckboxGroupContext.Provider
-          value={{ variant, size, error, ...state }}
-        >
+        <CheckboxGroupContext.Provider value={{ error, ...state }}>
           {children}
         </CheckboxGroupContext.Provider>
       </Box>
-    </Box>
+    </FieldBase>
   );
 };
diff --git a/packages/components/src/Radio/Radio.stories.tsx b/packages/components/src/Radio/Radio.stories.tsx
index 333547321..ef8c0da11 100644
--- a/packages/components/src/Radio/Radio.stories.tsx
+++ b/packages/components/src/Radio/Radio.stories.tsx
@@ -51,7 +51,18 @@ export default {
 } as Meta;
 
 export const Basic: ComponentStory<typeof Radio.Group> = args => (
-  <Radio.Group {...args}>
+  <Radio.Group {...args} description="Hier steht ein HelpText">
+    <Radio value="1">Option 1</Radio>
+    <Radio value="2">Option 2</Radio>
+    <Radio value="3" disabled>
+      Option 3
+    </Radio>
+    <Radio value="4">Option 4</Radio>
+  </Radio.Group>
+);
+
+export const Error: ComponentStory<typeof Radio.Group> = args => (
+  <Radio.Group errorMessage="Das ist ein Error" error {...args}>
     <Radio value="1">Option 1</Radio>
     <Radio value="2">Option 2</Radio>
     <Radio value="3" disabled>
diff --git a/packages/components/src/Radio/RadioGroup.tsx b/packages/components/src/Radio/RadioGroup.tsx
index abc0d3811..e8a400d38 100644
--- a/packages/components/src/Radio/RadioGroup.tsx
+++ b/packages/components/src/Radio/RadioGroup.tsx
@@ -3,19 +3,10 @@ import { useRadioGroup } from '@react-aria/radio';
 import { useRadioGroupState } from '@react-stately/radio';
 import { AriaRadioGroupProps } from '@react-types/radio';
 
-import {
-  Box,
-  ThemeExtensionsWithParts,
-  useComponentStyles,
-} from '@marigold/system';
+import { Box, useStateProps } from '@marigold/system';
 
-import { Label } from '../Label';
 import { RadioGroupContext } from './Context';
-
-// Theme Extension
-// ---------------
-export interface RadioGroupThemeExtension
-  extends ThemeExtensionsWithParts<'RadioGroup', ['container', 'group']> {}
+import { FieldBase } from '../FieldBase';
 
 // Props
 // ---------------
@@ -25,8 +16,6 @@ export interface RadioGroupProps
     'isDisabled' | 'isRquired' | 'isReadOnly ' | 'validationState'
   > {
   children: ReactNode[];
-  variant?: string;
-  size?: string;
   width?: string;
   required?: boolean;
   disabled?: boolean;
@@ -39,8 +28,6 @@ export interface RadioGroupProps
 export const RadioGroup = ({
   children,
   orientation = 'vertical',
-  size,
-  variant,
   width,
   required,
   disabled,
@@ -57,21 +44,30 @@ export const RadioGroup = ({
   };
 
   const state = useRadioGroupState(props);
-  const { radioGroupProps, labelProps } = useRadioGroup(props, state);
+  const { radioGroupProps, labelProps, errorMessageProps, descriptionProps } =
+    useRadioGroup(props, state);
 
-  const styles = useComponentStyles(
-    'RadioGroup',
-    { variant, size },
-    { parts: ['container', 'group'] }
-  );
+  const stateProps = useStateProps({
+    disabled,
+    readOnly,
+    error,
+  });
 
   return (
-    <Box {...radioGroupProps} css={styles.container}>
-      {props.label && (
-        <Label as="span" required={required} {...labelProps}>
-          {props.label}
-        </Label>
-      )}
+    <FieldBase
+      width={width}
+      label={props.label}
+      labelProps={{ as: 'span', ...labelProps }}
+      description={props.description}
+      descriptionProps={descriptionProps}
+      error={error}
+      errorMessage={props.errorMessage}
+      errorMessageProps={errorMessageProps}
+      disabled={disabled}
+      stateProps={stateProps}
+      required={required}
+      {...radioGroupProps}
+    >
       <Box
         role="presentation"
         data-orientation={orientation}
@@ -81,14 +77,11 @@ export const RadioGroup = ({
           alignItems: 'start',
           gap: orientation === 'vertical' ? '0.5ch' : '1.5ch',
         }}
-        css={styles.group}
       >
-        <RadioGroupContext.Provider
-          value={{ variant, size, width, error, ...state }}
-        >
+        <RadioGroupContext.Provider value={{ width, error, ...state }}>
           {children}
         </RadioGroupContext.Provider>
       </Box>
-    </Box>
+    </FieldBase>
   );
 };
diff --git a/packages/components/src/Radio/index.ts b/packages/components/src/Radio/index.ts
index 60f8fa6a6..b3bce915c 100644
--- a/packages/components/src/Radio/index.ts
+++ b/packages/components/src/Radio/index.ts
@@ -1,2 +1,2 @@
 export * from './Radio';
-export type { RadioGroupProps, RadioGroupThemeExtension } from './RadioGroup';
+export type { RadioGroupProps } from './RadioGroup';
diff --git a/packages/components/src/theme.ts b/packages/components/src/theme.ts
index 4c04c759d..d2fb87990 100644
--- a/packages/components/src/theme.ts
+++ b/packages/components/src/theme.ts
@@ -7,10 +7,7 @@ import type { BadgeThemeExtension } from './Badge';
 import type { BodyThemeExtension } from './Body';
 import type { ButtonThemeExtension } from './Button';
 import type { CardThemeExtension } from './Card';
-import type {
-  CheckboxThemeExtension,
-  CheckboxGroupThemeExtension,
-} from './Checkbox';
+import type { CheckboxThemeExtension } from './Checkbox';
 import type { DialogThemeExtension } from './Dialog';
 import type { DividerThemeExtension } from './Divider';
 import type { FieldThemeExtension } from './FieldBase';
@@ -28,7 +25,7 @@ import type { MenuThemeExtension } from './Menu';
 import type { MessageThemeExtension } from './Message';
 import type { NumberFieldThemeExtension } from './NumberField';
 import type { UnderlayThemeExtension } from './Overlay';
-import type { RadioThemeExtension, RadioGroupThemeExtension } from './Radio';
+import type { RadioThemeExtension } from './Radio';
 import type { SelectThemeExtension } from './Select';
 import type { SliderThemeExtension } from './Slider';
 import type { SwitchThemeExtension } from './Switch';
@@ -42,7 +39,6 @@ interface ComponentStyles
     ButtonThemeExtension,
     CardThemeExtension,
     CheckboxThemeExtension,
-    CheckboxGroupThemeExtension,
     BodyThemeExtension,
     DialogThemeExtension,
     DividerThemeExtension,
@@ -61,7 +57,6 @@ interface ComponentStyles
     MessageThemeExtension,
     NumberFieldThemeExtension,
     RadioThemeExtension,
-    RadioGroupThemeExtension,
     SelectThemeExtension,
     SliderThemeExtension,
     SwitchThemeExtension,
diff --git a/themes/theme-b2b/src/components/CheckboxGroup.style.ts b/themes/theme-b2b/src/components/CheckboxGroup.style.ts
deleted file mode 100644
index 850d389eb..000000000
--- a/themes/theme-b2b/src/components/CheckboxGroup.style.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import type { Theme } from '@marigold/components';
-
-export const CheckboxGroup: Theme['components']['CheckboxGroup'] = {
-  base: {
-    container: {
-      display: 'flex',
-      flexDirection: 'column',
-      gap: 'xxsmall',
-    },
-  },
-};
diff --git a/themes/theme-b2b/src/components/index.ts b/themes/theme-b2b/src/components/index.ts
index ff6bcf080..61fa2d5e5 100644
--- a/themes/theme-b2b/src/components/index.ts
+++ b/themes/theme-b2b/src/components/index.ts
@@ -2,7 +2,6 @@ export * from './Badge.style';
 export * from './Button.style';
 export * from './Card.style';
 export * from './Checkbox.style';
-export * from './CheckboxGroup.style';
 export * from './Dialog.style';
 export * from './Divider.style';
 export * from './Headline.style';
diff --git a/themes/theme-unicorn/src/components/CheckboxGroup.style.ts b/themes/theme-unicorn/src/components/CheckboxGroup.style.ts
deleted file mode 100644
index 850d389eb..000000000
--- a/themes/theme-unicorn/src/components/CheckboxGroup.style.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import type { Theme } from '@marigold/components';
-
-export const CheckboxGroup: Theme['components']['CheckboxGroup'] = {
-  base: {
-    container: {
-      display: 'flex',
-      flexDirection: 'column',
-      gap: 'xxsmall',
-    },
-  },
-};
diff --git a/themes/theme-unicorn/src/components/index.ts b/themes/theme-unicorn/src/components/index.ts
index 05264d19e..fc91eaf08 100644
--- a/themes/theme-unicorn/src/components/index.ts
+++ b/themes/theme-unicorn/src/components/index.ts
@@ -2,7 +2,6 @@ export * from './Badge.style';
 export * from './Button.style';
 export * from './Card.style';
 export * from './Checkbox.style';
-export * from './CheckboxGroup.style';
 export * from './Dialog.style';
 export * from './Divider.style';
 export * from './Headline.style';


Test patch:
diff --git a/packages/components/src/Checkbox/CheckboxGroup.test.tsx b/packages/components/src/Checkbox/CheckboxGroup.test.tsx
index 953f58bdf..11538a7ef 100644
--- a/packages/components/src/Checkbox/CheckboxGroup.test.tsx
+++ b/packages/components/src/Checkbox/CheckboxGroup.test.tsx
@@ -97,73 +97,6 @@ test('label is optional (can use aria-label instead)', () => {
   expect(screen.getByText('three')).toBeInTheDocument();
 });
 
-test('allows styling container via theme', () => {
-  render(
-    <ThemeProvider theme={theme}>
-      <CheckboxGroup label="Group of Checkboxes">
-        <Checkbox value="one" data-testid="one">
-          one
-        </Checkbox>
-        <Checkbox value="two" data-testid="two">
-          two
-        </Checkbox>
-      </CheckboxGroup>
-    </ThemeProvider>
-  );
-
-  const container = screen.getByRole('group');
-  expect(container).toHaveStyle(`background: ${theme.colors.gray}`);
-
-  const group = screen.getByRole('presentation');
-  expect(group).toHaveStyle(`font-style: italic`);
-});
-
-test('supports styling via variant and size', () => {
-  render(
-    <ThemeProvider theme={theme}>
-      <CheckboxGroup label="Group of Checkboxes" variant="teal" size="large">
-        <Checkbox value="one" data-testid="one">
-          one
-        </Checkbox>
-        <Checkbox value="two" data-testid="two">
-          two
-        </Checkbox>
-      </CheckboxGroup>
-    </ThemeProvider>
-  );
-
-  const container = screen.getByRole('group');
-  expect(container).toHaveStyle(`background: ${theme.colors.teal}`);
-
-  const group = screen.getByRole('presentation');
-  expect(group).toHaveStyle(`font-size: ${theme.fontSizes['large-1']}px`);
-});
-
-test('passed down variant and size to checkboxes', () => {
-  render(
-    <ThemeProvider theme={theme}>
-      <CheckboxGroup label="Group of Checkboxes" variant="teal" size="large">
-        <Checkbox value="one" data-testid="one">
-          one
-        </Checkbox>
-        <Checkbox value="two" data-testid="two">
-          two
-        </Checkbox>
-      </CheckboxGroup>
-    </ThemeProvider>
-  );
-
-  expect(screen.getByText('one')).toHaveStyle(`color: ${theme.colors.teal}`);
-  expect(screen.getByText('one')).toHaveStyle(
-    `font-size: ${theme.fontSizes['small-1']}px`
-  );
-
-  expect(screen.getByText('two')).toHaveStyle(`color: ${theme.colors.teal}`);
-  expect(screen.getByText('two')).toHaveStyle(
-    `font-size: ${theme.fontSizes['small-1']}px`
-  );
-});
-
 test('passes down "disabled" to checkboxes', () => {
   render(
     <CheckboxGroup label="Group of Checkboxes" disabled>
@@ -260,3 +193,39 @@ test('controlled', () => {
   fireEvent.click(screen.getByTestId('three'));
   expect(onChange).toHaveBeenCalledWith(['one', 'two']);
 });
+
+test('accepts description', () => {
+  render(
+    <CheckboxGroup label="Group of Checkboxes" description="My description">
+      <Checkbox value="one" data-testid="one">
+        one
+      </Checkbox>
+      <Checkbox value="two" data-testid="two">
+        two
+      </Checkbox>
+      <Checkbox value="three" data-testid="three">
+        three
+      </Checkbox>
+    </CheckboxGroup>
+  );
+
+  expect(screen.getByText('My description')).toBeInTheDocument();
+});
+
+test('accepts error message', () => {
+  render(
+    <CheckboxGroup label="Group of Checkboxes" error errorMessage="My Error">
+      <Checkbox value="one" data-testid="one">
+        one
+      </Checkbox>
+      <Checkbox value="two" data-testid="two">
+        two
+      </Checkbox>
+      <Checkbox value="three" data-testid="three">
+        three
+      </Checkbox>
+    </CheckboxGroup>
+  );
+
+  expect(screen.getByText('My Error')).toBeInTheDocument();
+});
diff --git a/packages/components/src/Radio/Radio.test.tsx b/packages/components/src/Radio/Radio.test.tsx
index a284aa2c3..3f83edab1 100644
--- a/packages/components/src/Radio/Radio.test.tsx
+++ b/packages/components/src/Radio/Radio.test.tsx
@@ -138,14 +138,14 @@ test('allows styling via theme', () => {
 test('supports styling via variant and size', () => {
   render(
     <ThemeProvider theme={theme}>
-      <Radio.Group label="With Label" variant="green" size="large">
-        <Radio value="1" data-testid="radio-1">
+      <Radio.Group label="With Label">
+        <Radio value="1" data-testid="radio-1" variant="green" size="large">
           Option 1
         </Radio>
-        <Radio value="2" data-testid="radio-2">
+        <Radio value="2" data-testid="radio-2" variant="green" size="large">
           Option 2
         </Radio>
-        <Radio value="3" data-testid="radio-3">
+        <Radio value="3" data-testid="radio-3" variant="green" size="large">
           Option 3
         </Radio>
       </Radio.Group>
@@ -400,3 +400,39 @@ test('forwards ref', () => {
 
   expect(ref.current).toBeInstanceOf(HTMLInputElement);
 });
+
+test('radio accepts helptext', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Radio.Group label="With Label" description="This is my Helptext.">
+        <Radio value="1" data-testid="radio-1">
+          Option 1π
+        </Radio>
+        <Radio value="2" data-testid="radio-2">
+          Option 2
+        </Radio>
+      </Radio.Group>
+    </ThemeProvider>
+  );
+  expect(screen.getByText('This is my Helptext.')).toBeInTheDocument();
+});
+
+test('radio accepts error message', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Radio.Group
+        label="With Label"
+        error
+        errorMessage="This is my error message"
+      >
+        <Radio value="1" data-testid="radio-1">
+          Option 1π
+        </Radio>
+        <Radio value="2" data-testid="radio-2">
+          Option 2
+        </Radio>
+      </Radio.Group>
+    </ThemeProvider>
+  );
+  expect(screen.getByText('This is my error message')).toBeInTheDocument();
+});
diff --git a/packages/components/src/Radio/RadioGroup.test.tsx b/packages/components/src/Radio/RadioGroup.test.tsx
index 5f462ef62..7ef72a21a 100644
--- a/packages/components/src/Radio/RadioGroup.test.tsx
+++ b/packages/components/src/Radio/RadioGroup.test.tsx
@@ -69,34 +69,6 @@ const theme = {
         },
       },
     },
-    RadioGroup: {
-      base: {
-        container: {
-          bg: 'gray',
-        },
-        group: {
-          fontStyle: 'italic',
-
-          '&[data-orientation="horizontal"]': {
-            gap: '3ch',
-          },
-        },
-      },
-      variant: {
-        green: {
-          container: {
-            bg: 'green',
-          },
-        },
-      },
-      size: {
-        large: {
-          group: {
-            fontSize: 'large-1',
-          },
-        },
-      },
-    },
   },
 };
 
@@ -150,54 +122,6 @@ test('label is optional (can use aria-label instead)', () => {
   expect(screen.queryByText('With Label')).not.toBeInTheDocument();
 });
 
-test('allows styling via theme', () => {
-  render(
-    <ThemeProvider theme={theme}>
-      <Radio.Group label="With Label">
-        <Radio value="1" data-testid="radio-1">
-          Option 1
-        </Radio>
-        <Radio value="2" data-testid="radio-2">
-          Option 2
-        </Radio>
-        <Radio value="3" data-testid="radio-3">
-          Option 3
-        </Radio>
-      </Radio.Group>
-    </ThemeProvider>
-  );
-
-  const container = screen.getByRole('radiogroup');
-  expect(container).toHaveStyle(`background: ${theme.colors.gray}`);
-
-  const group = screen.getByRole('presentation');
-  expect(group).toHaveStyle(`font-style: italic`);
-});
-
-test('supports styling via variant and size', () => {
-  render(
-    <ThemeProvider theme={theme}>
-      <Radio.Group label="With Label" variant="green" size="large">
-        <Radio value="1" data-testid="radio-1">
-          Option 1
-        </Radio>
-        <Radio value="2" data-testid="radio-2">
-          Option 2
-        </Radio>
-        <Radio value="3" data-testid="radio-3">
-          Option 3
-        </Radio>
-      </Radio.Group>
-    </ThemeProvider>
-  );
-
-  const container = screen.getByRole('radiogroup');
-  expect(container).toHaveStyle(`background: ${theme.colors.green}`);
-
-  const group = screen.getByRole('presentation');
-  expect(group).toHaveStyle(`font-size: ${theme.fontSizes['large-1']}px`);
-});
-
 test('support vertical orientation by default', () => {
   render(
     <ThemeProvider theme={theme}>
@@ -238,8 +162,6 @@ test('support horizontal orientation', () => {
 
   const group = screen.getByRole('presentation');
   expect(group).toHaveAttribute('data-orientation', 'horizontal');
-
-  expect(group).toHaveStyle(`gap: 3ch;`);
 });
 
 test('supports error styling via theme & passes down error', () => {
@@ -312,5 +234,3 @@ test('controlled', () => {
   expect(onChange).toHaveBeenCalledTimes(1);
   expect(onChange).toHaveBeenCalledWith('1');
 });
-
-// orientation?