diff --git a/CHANGELOG.md b/CHANGELOG.md index 000482d91..153b46b65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +- Add optional `children` property to `RadioCard` and `CheckboxCard` core components displayed only when component is + selected + ### Changed +- Update core components `RadioCard` and `CheckboxCard` to have optional description property and correct alignment + when no description provided - Bump `actions/checkout` from 4.2.0 to 4.2.1 - Update minor and patch NPM dependencies diff --git a/src/core/components/forms/checkboxCard/checkboxCard.stories.tsx b/src/core/components/forms/checkboxCard/checkboxCard.stories.tsx index 9562e7410..ff48ee813 100644 --- a/src/core/components/forms/checkboxCard/checkboxCard.stories.tsx +++ b/src/core/components/forms/checkboxCard/checkboxCard.stories.tsx @@ -21,12 +21,35 @@ type Story = StoryObj; * Default usage of the CheckboxCard component */ export const Default: Story = { - render: (props) => , + args: { + avatar: 'https://assets-global.website-files.com/5e997428d0f2eb13a90aec8c/63f47db62df04b569e4e004e_icon_aragon.svg', + label: 'Checkbox label', + tag: { label: 'Tag', variant: 'info' }, + }, +}; + +/** + * Default usage of the CheckboxCard component + */ +export const WithDescription: Story = { + args: { + avatar: 'https://assets-global.website-files.com/5e997428d0f2eb13a90aec8c/63f47db62df04b569e4e004e_icon_aragon.svg', + label: 'Checkbox label', + description: 'Checkbox description', + tag: { label: 'Tag', variant: 'info' }, + }, +}; + +/** + * Usage of the CheckboxCard component with children when checked + */ +export const WithChildrenWhenChecked: Story = { args: { avatar: 'https://assets-global.website-files.com/5e997428d0f2eb13a90aec8c/63f47db62df04b569e4e004e_icon_aragon.svg', label: 'Checkbox label', description: 'Checkbox description', tag: { label: 'Tag', variant: 'info' }, + children:
Children
, }, }; diff --git a/src/core/components/forms/checkboxCard/checkboxCard.test.tsx b/src/core/components/forms/checkboxCard/checkboxCard.test.tsx index 69c3ad06c..1b273c5f7 100644 --- a/src/core/components/forms/checkboxCard/checkboxCard.test.tsx +++ b/src/core/components/forms/checkboxCard/checkboxCard.test.tsx @@ -62,4 +62,10 @@ describe(' component', () => { render(createTestComponent({ checked })); expect(screen.getByTestId(IconType.CHECKBOX_INDETERMINATE)).toBeVisible(); }); + + it('renders the children when checkbox is checked', () => { + const checked = true; + render(createTestComponent({ checked, children:
})); + expect(screen.getByTestId('children')).toBeVisible(); + }); }); diff --git a/src/core/components/forms/checkboxCard/checkboxCard.tsx b/src/core/components/forms/checkboxCard/checkboxCard.tsx index e42d09bf3..f46c194e9 100644 --- a/src/core/components/forms/checkboxCard/checkboxCard.tsx +++ b/src/core/components/forms/checkboxCard/checkboxCard.tsx @@ -1,6 +1,6 @@ import * as RadixCheckbox from '@radix-ui/react-checkbox'; import classNames from 'classnames'; -import { forwardRef, type ComponentProps } from 'react'; +import { forwardRef, type ComponentProps, type ReactNode } from 'react'; import { useRandomId } from '../../../hooks'; import { Avatar } from '../../avatars'; import { Icon, IconType } from '../../icon'; @@ -19,7 +19,7 @@ export interface ICheckboxCardProps extends ComponentProps<'button'> { /** * Description of the checkbox. */ - description: string; + description?: string; /** * Optional tag for the checkbox. */ @@ -40,10 +40,26 @@ export interface ICheckboxCardProps extends ComponentProps<'button'> { * Id of the checkbox. */ id?: string; + /** + * Additional children to render when the checkbox is checked. + */ + children?: ReactNode; } export const CheckboxCard = forwardRef((props, ref) => { - const { id, avatar, label, description, tag, className, checked, onCheckedChange, disabled, ...otherProps } = props; + const { + id, + avatar, + label, + description, + tag, + className, + checked, + onCheckedChange, + disabled, + children, + ...otherProps + } = props; const randomId = useRandomId(id); const labelId = `${randomId}-label`; @@ -57,7 +73,7 @@ export const CheckboxCard = forwardRef((p onCheckedChange={onCheckedChange} disabled={disabled} className={classNames( - 'group flex h-16 min-w-0 flex-row items-center gap-3 outline-none transition-all md:h-20', // Layout + 'group flex min-w-0 flex-col gap-3 outline-none transition-all', // Layout 'rounded-xl border bg-neutral-0 px-4 py-3 md:gap-4 md:px-6 md:py-4', // Style 'focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-offset', // Focus 'border-primary-400 shadow-primary hover:shadow-primary-md', // Checked/indeterminate & hover @@ -69,37 +85,51 @@ export const CheckboxCard = forwardRef((p )} {...otherProps} > - {avatar && } -
-

+ {avatar && } +

+

+ {label} +

+ {description && ( +

+ {description} +

)} - > - {label} -

-

- {description} -

-
- {tag && } -
+ {tag && }
+ {children &&
{children}
} ); }); diff --git a/src/core/components/forms/radioCard/radioCard.stories.tsx b/src/core/components/forms/radioCard/radioCard.stories.tsx index b520241ff..c133842ee 100644 --- a/src/core/components/forms/radioCard/radioCard.stories.tsx +++ b/src/core/components/forms/radioCard/radioCard.stories.tsx @@ -22,6 +22,41 @@ type Story = StoryObj; * Default usage of the `RadioCard` component */ export const Default: Story = { + render: (props) => ( + + + + ), + args: { + avatar: 'https://assets-global.website-files.com/5e997428d0f2eb13a90aec8c/63f47db62df04b569e4e004e_icon_aragon.svg', + value: '1', + label: 'Option one', + tag: { label: 'Platinum' }, + }, +}; + +/** + * Default usage of the `RadioCard` component with description + */ +export const WithDescription: Story = { + render: (props) => ( + + + + ), + args: { + avatar: 'https://assets-global.website-files.com/5e997428d0f2eb13a90aec8c/63f47db62df04b569e4e004e_icon_aragon.svg', + value: '1', + label: 'Option one', + description: 'The best option ever', + tag: { label: 'Platinum' }, + }, +}; + +/** + * Usage of the `RadioCard` component with children when selected + */ +export const WithChildrenWhenSelected: Story = { render: (props) => ( @@ -33,6 +68,7 @@ export const Default: Story = { label: 'Option one', description: 'The best option ever', tag: { label: 'Platinum' }, + children:
Children
, }, }; diff --git a/src/core/components/forms/radioCard/radioCard.test.tsx b/src/core/components/forms/radioCard/radioCard.test.tsx index eab8e2c80..881cadbb5 100644 --- a/src/core/components/forms/radioCard/radioCard.test.tsx +++ b/src/core/components/forms/radioCard/radioCard.test.tsx @@ -72,4 +72,17 @@ describe(' component', () => { expect(screen.getByRole('radio')).toHaveValue(value); }); + + it('renders children when radio button is checked', async () => { + const user = userEvent.setup(); + + const children = 'test-children'; + render(createTestComponent({ children })); + + const radioButton = screen.getByRole('radio'); + + await user.click(radioButton); + expect(screen.getByText(children)).toBeVisible(); + expect(screen.getByRole('radio')).toBeChecked(); + }); }); diff --git a/src/core/components/forms/radioCard/radioCard.tsx b/src/core/components/forms/radioCard/radioCard.tsx index 23733d7a9..ae0afaf2e 100644 --- a/src/core/components/forms/radioCard/radioCard.tsx +++ b/src/core/components/forms/radioCard/radioCard.tsx @@ -1,6 +1,6 @@ import { RadioGroupIndicator, RadioGroupItem } from '@radix-ui/react-radio-group'; import classNames from 'classnames'; -import { forwardRef, type ComponentProps } from 'react'; +import { forwardRef, type ComponentProps, type ReactNode } from 'react'; import { useRandomId } from '../../../hooks'; import { Avatar } from '../../avatars'; import { Icon, IconType } from '../../icon'; @@ -14,7 +14,7 @@ export interface IRadioCardProps extends ComponentProps<'button'> { /** * Description */ - description: string; + description?: string; /** * Radio label */ @@ -31,16 +31,20 @@ export interface IRadioCardProps extends ComponentProps<'button'> { * Indicates if the radio is disabled. */ disabled?: boolean; + /** + * Additional children to render when the radio is selected. + */ + children?: ReactNode; } export const RadioCard = forwardRef((props, ref) => { - const { value, id, className, tag, avatar, label, description, disabled, ...rest } = props; + const { value, id, className, tag, avatar, label, description, disabled, children, ...rest } = props; const randomId = useRandomId(id); const labelId = `${randomId}-label`; const containerClasses = classNames( - 'group h-16 rounded-xl border border-neutral-100 bg-neutral-0 px-4 py-3 shadow-neutral-sm outline-none transition-all md:h-20 md:rounded-2xl md:px-6 md:py-4', // default + 'group flex w-full flex-col gap-3 rounded-xl border border-neutral-100 bg-neutral-0 px-4 py-3 shadow-neutral-sm outline-none transition-all md:rounded-2xl md:px-6 md:py-4', // default 'data-[state=checked]:border-primary-400 data-[state=checked]:shadow-primary', // checked 'focus:outline-none focus-visible:ring focus-visible:ring-primary focus-visible:ring-offset', // focus 'hover:border-neutral-200 hover:shadow-neutral hover:data-[state=checked]:shadow-primary-md', // hover @@ -67,18 +71,22 @@ export const RadioCard = forwardRef((props, aria-labelledby={labelId} {...rest} > -
+
{avatar && } -
+

{label}

-

{description}

+ {description &&

{description}

}
{tag && }
- + ((props,
+ {children &&
{children}
} ); });