Skip to content

Commit

Permalink
feat: CheckboxGroup can now be used as a compound component (#4188)
Browse files Browse the repository at this point in the history
* feat: `CheckboxGroup` can now be used as a compound component

`<CheckboxGroup>` -> `<Checkbox.Group>`

* Create thick-masks-compete.md
  • Loading branch information
sebald authored Oct 2, 2024
1 parent 05996e6 commit b8cd92a
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 39 deletions.
9 changes: 9 additions & 0 deletions .changeset/thick-masks-compete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@marigold/docs": patch
"@marigold/components": patch
---

feat: `CheckboxGroup` can now be used as a compound component

- Refactored the `CheckboxGroup` to be a compound component and align it with other components: `<CheckboxGroup>` -> `<Checkbox.Group>`
- Adjusted the Checkbox appearance demo a bit
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { CheckboxGroup, CheckboxGroupProps } from '@marigold/components';
import { Checkbox } from '@marigold/components';
import type { CheckboxProps } from '@marigold/components';
import { Checkbox, FieldGroup } from '@marigold/components';

export default (props: CheckboxGroupProps) => (
<CheckboxGroup {...props}>
<Checkbox value="ham">Ham</Checkbox>
<Checkbox value="cucumber">Cucumber</Checkbox>
<Checkbox value="onions">Onions</Checkbox>
</CheckboxGroup>
export default (props: CheckboxProps) => (
<FieldGroup labelWidth="0px">
<Checkbox {...props}>
I agree to the Terms of Service and Privacy Policy
</Checkbox>
</FieldGroup>
);
6 changes: 3 additions & 3 deletions docs/content/components/form/checkbox/checkbox-group.demo.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useState } from 'react';
import { Checkbox, CheckboxGroup } from '@marigold/components';
import { Checkbox } from '@marigold/components';

export default () => {
const [selected, setSelected] = useState<string[]>([]);
return (
<>
<CheckboxGroup
<Checkbox.Group
label="Choose your event activities:"
onChange={setSelected}
description="Select the activities you'd like to participate in"
Expand All @@ -18,7 +18,7 @@ export default () => {
<Checkbox value="dining">🍽️ Dining Experiences</Checkbox>
<Checkbox value="exhibitions">🖼️ Art Exhibitions</Checkbox>
<Checkbox value="sports">⚽ Sports Events</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
<hr />
<pre>Selected values: {selected.join(', ')}</pre>
</>
Expand Down
22 changes: 19 additions & 3 deletions packages/components/src/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import type { ReactNode } from 'react';
import type {
ForwardRefExoticComponent,
ReactNode,
RefAttributes,
} from 'react';
import { forwardRef } from 'react';
import type RAC from 'react-aria-components';
import { Checkbox } from 'react-aria-components';
import { StateAttrProps, cn, useClassNames } from '@marigold/system';
import { useFieldGroupContext } from '../FieldBase';
import { CheckboxField } from './CheckBoxField';
import { CheckboxGroup } from './CheckboxGroup';
import { useCheckboxGroupContext } from './Context';

// SVG Icon
Expand Down Expand Up @@ -105,9 +110,18 @@ export interface CheckboxProps extends Omit<RAC.CheckboxProps, RemovedProps> {
children?: ReactNode;
}

export interface CheckboxComponent
extends ForwardRefExoticComponent<
CheckboxProps & RefAttributes<HTMLLabelElement>
> {
/**
* Group for checkboxes.
*/
Group: typeof CheckboxGroup;
}

// Component
// --------------

const _Checkbox = forwardRef<HTMLLabelElement, CheckboxProps>(
(
{
Expand Down Expand Up @@ -175,6 +189,8 @@ const _Checkbox = forwardRef<HTMLLabelElement, CheckboxProps>(
component
);
}
);
) as CheckboxComponent;

_Checkbox.Group = CheckboxGroup;

export { _Checkbox as Checkbox };
49 changes: 24 additions & 25 deletions packages/components/src/Checkbox/CheckboxGroup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Theme, cva } from '@marigold/system';
import { FieldGroup } from '../FieldBase';
import { setup } from '../test.utils';
import { Checkbox } from './Checkbox';
import { CheckboxGroup } from './CheckboxGroup';

const theme: Theme = {
name: 'checkbox group testing',
Expand Down Expand Up @@ -42,11 +41,11 @@ const { render } = setup({ theme });

test('renders label and group of checkboxes', () => {
render(
<CheckboxGroup label="Group of Checkboxes">
<Checkbox.Group label="Group of Checkboxes">
<Checkbox value="one">one</Checkbox>
<Checkbox value="two">two</Checkbox>
<Checkbox value="three">three</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);

expect(screen.getByText('Group of Checkboxes')).toBeInTheDocument();
Expand All @@ -57,11 +56,11 @@ test('renders label and group of checkboxes', () => {

test('label is optional (can use aria-label instead)', () => {
render(
<CheckboxGroup aria-label="Aria Label">
<Checkbox.Group aria-label="Aria Label">
<Checkbox value="one">one</Checkbox>
<Checkbox value="two">two</Checkbox>
<Checkbox value="three">three</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);

expect(screen.queryByText('Group of Checkboxes')).not.toBeInTheDocument();
Expand All @@ -72,11 +71,11 @@ test('label is optional (can use aria-label instead)', () => {

test('applies group styles from theme', () => {
render(
<CheckboxGroup aria-label="With Label">
<Checkbox.Group aria-label="With Label">
<Checkbox value="one">one</Checkbox>
<Checkbox value="two">two</Checkbox>
<Checkbox value="three">three</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);

const group = screen.getByRole('group');
Expand All @@ -85,7 +84,7 @@ test('applies group styles from theme', () => {

test('passes down "disabled" to checkboxes', () => {
render(
<CheckboxGroup label="Group of Checkboxes" disabled>
<Checkbox.Group label="Group of Checkboxes" disabled>
<Checkbox value="one" data-testid="one">
one
</Checkbox>
Expand All @@ -95,7 +94,7 @@ test('passes down "disabled" to checkboxes', () => {
<Checkbox value="three" data-testid="three">
three
</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);

// Bug in `react-aria-components` props are spread on input AND label...
Expand All @@ -106,7 +105,7 @@ test('passes down "disabled" to checkboxes', () => {

test('passes down "read-only" to checkboxes', () => {
render(
<CheckboxGroup label="Group of Checkboxes" readOnly>
<Checkbox.Group label="Group of Checkboxes" readOnly>
<Checkbox value="one" data-testid="one">
one
</Checkbox>
Expand All @@ -116,7 +115,7 @@ test('passes down "read-only" to checkboxes', () => {
<Checkbox value="three" data-testid="three">
three
</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);

// Bug in `react-aria-components` props are spread on input AND label...
Expand All @@ -136,7 +135,7 @@ test('passes down "read-only" to checkboxes', () => {

test('passes down "error" to checkboxes', () => {
render(
<CheckboxGroup label="Group of Checkboxes" error>
<Checkbox.Group label="Group of Checkboxes" error>
<Checkbox value="one" data-testid="one">
one
</Checkbox>
Expand All @@ -146,7 +145,7 @@ test('passes down "error" to checkboxes', () => {
<Checkbox value="three" data-testid="three">
three
</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);

// Bug in `react-aria-components` props are spread on input AND label...
Expand All @@ -167,7 +166,7 @@ test('passes down "error" to checkboxes', () => {
test('controlled', () => {
const onChange = jest.fn();
render(
<CheckboxGroup label="Group of Checkboxes" onChange={onChange}>
<Checkbox.Group label="Group of Checkboxes" onChange={onChange}>
<Checkbox value="one" data-testid="one">
one
</Checkbox>
Expand All @@ -177,7 +176,7 @@ test('controlled', () => {
<Checkbox value="three" data-testid="three">
three
</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);

fireEvent.click(screen.getAllByTestId('one')[1]);
Expand All @@ -195,7 +194,7 @@ test('controlled', () => {

test('accepts description', () => {
render(
<CheckboxGroup label="Group of Checkboxes" description="My description">
<Checkbox.Group label="Group of Checkboxes" description="My description">
<Checkbox value="one" data-testid="one">
one
</Checkbox>
Expand All @@ -205,15 +204,15 @@ test('accepts description', () => {
<Checkbox value="three" data-testid="three">
three
</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);

expect(screen.getByText('My description')).toBeInTheDocument();
});

test('accepts error message', () => {
render(
<CheckboxGroup label="Group of Checkboxes" error errorMessage="My Error">
<Checkbox.Group label="Group of Checkboxes" error errorMessage="My Error">
<Checkbox value="one" data-testid="one">
one
</Checkbox>
Expand All @@ -223,7 +222,7 @@ test('accepts error message', () => {
<Checkbox value="three" data-testid="three">
three
</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);

expect(screen.getByText('My Error')).toBeInTheDocument();
Expand All @@ -232,11 +231,11 @@ test('accepts error message', () => {
test('works with a <FieldGroup>', () => {
render(
<FieldGroup labelWidth="100px">
<CheckboxGroup label="Group of Checkboxes">
<Checkbox.Group label="Group of Checkboxes">
<Checkbox value="one">one</Checkbox>
<Checkbox value="two">two</Checkbox>
<Checkbox value="three">three</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
</FieldGroup>
);

Expand All @@ -248,11 +247,11 @@ test('works with a <FieldGroup>', () => {

test('horiziontal orientation style', () => {
render(
<CheckboxGroup label="Group of Checkboxes" orientation="horizontal">
<Checkbox.Group label="Group of Checkboxes" orientation="horizontal">
<Checkbox value="one">one</Checkbox>
<Checkbox value="two">two</Checkbox>
<Checkbox value="three">three</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);
const presentation = screen
.getAllByRole('presentation')
Expand All @@ -265,11 +264,11 @@ test('horiziontal orientation style', () => {

test('pass down variant and size to <Checkbox>', () => {
render(
<CheckboxGroup label="Group of Checkboxes" size="small">
<Checkbox.Group label="Group of Checkboxes" size="small">
<Checkbox value="one" data-testid="one">
one
</Checkbox>
</CheckboxGroup>
</Checkbox.Group>
);

const one = screen
Expand Down

0 comments on commit b8cd92a

Please sign in to comment.