Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/components/ui/RadioCards/context/RadioCardsContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createContext } from 'react';

export const RadioCardsContext = createContext({
defaultChecked: null,

rootClass: '',
onChange: null

});
4 changes: 2 additions & 2 deletions src/components/ui/RadioCards/fragments/RadioCardsItem.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useContext } from 'react';
import RadioGroupPrimitive from '~/core/primitives/RadioGroup/RadioGroupPrimitive';
import RadioGroupPrimitive, { RadioGroupPrimitiveTypes } from '~/core/primitives/RadioGroup/RadioGroupPrimitive';

Check warning on line 2 in src/components/ui/RadioCards/fragments/RadioCardsItem.tsx

View workflow job for this annotation

GitHub Actions / lint

'RadioGroupPrimitiveTypes' is defined but never used
import { RadioCardsContext } from '../context/RadioCardsContext';

import clsx from 'clsx';

const RadioCardsItem = ({ children, className = '', ...props }: { children: React.ReactNode } & RadioGroupPrimitive.ItemProps) => {
const RadioCardsItem = ({ children, className = '', ...props }: { children: React.ReactNode } & RadioGroupPrimitiveTypes.Item) => {
const { rootClass } = useContext(RadioCardsContext);
return <RadioGroupPrimitive.Item className={clsx(`${rootClass}-item`, className)} {...props}>{children}</RadioGroupPrimitive.Item>;
};
Expand Down
20 changes: 13 additions & 7 deletions src/components/ui/RadioCards/fragments/RadioCardsRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React from 'react';
import RadioGroupPrimitive from '~/core/primitives/RadioGroup/RadioGroupPrimitive';
import RadioGroupPrimitive, { RadioGroupPrimitiveTypes } from '~/core/primitives/RadioGroup/RadioGroupPrimitive';

import clsx from 'clsx';
import { customClassSwitcher } from '~/core';
import { useCreateDataAttribute, useComposeAttributes, useCreateDataAccentColorAttribute } from '~/core/hooks/createDataAttribute';

import { RadioCardsContext } from '../context/RadioCardsContext';

Expand All @@ -11,16 +12,21 @@ const COMPONENT_NAME = 'RadioCards';
type RadioCardsRootProps = {
children: React.ReactNode;
className?: string;
defaultChecked?: string | null;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
customRootClass?: string;
};
size?: string;
color?: string;
variant?: string;
}& RadioGroupPrimitiveTypes.Root

const RadioCardsRoot = ({ children, className = '', defaultChecked = null, onChange = null, customRootClass = '' }: RadioCardsRootProps) => {
const RadioCardsRoot = ({ children, className = '', customRootClass = '', size = '', color = '', variant = '', ...props }: RadioCardsRootProps) => {
const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME);

return <RadioCardsContext.Provider value={{ defaultChecked, rootClass, onChange }}>
<RadioGroupPrimitive.Root className={clsx(rootClass, className)} customRootClass={customRootClass}>{children}</RadioGroupPrimitive.Root>
const dataAttributes = useCreateDataAttribute('button', { variant, size });
const accentAttributes = useCreateDataAccentColorAttribute(color);
const composedAttributes = useComposeAttributes(dataAttributes(), accentAttributes());

return <RadioCardsContext.Provider value={{ rootClass }}>
<RadioGroupPrimitive.Root className={clsx(rootClass, className)} {...composedAttributes()} {...props}>{children}</RadioGroupPrimitive.Root>
</RadioCardsContext.Provider>;
};

Expand Down
12 changes: 7 additions & 5 deletions src/components/ui/RadioGroup/context/RadioGroupContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { createContext } from 'react';
import { createContext, RefObject } from 'react';

Check warning on line 1 in src/components/ui/RadioGroup/context/RadioGroupContext.tsx

View workflow job for this annotation

GitHub Actions / lint

'RefObject' is defined but never used

export const RadioGroupContext = createContext({
defaultChecked: null,
customRootClass: null,
onChange: null
interface RadioGroupContextType {
rootClass: string;
}

export const RadioGroupContext = createContext<RadioGroupContextType>({
rootClass: ''
});
10 changes: 7 additions & 3 deletions src/components/ui/RadioGroup/fragments/RadioGroupItem.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import React, { useContext } from 'react';
import RadioGroupPrimitive from '~/core/primitives/RadioGroup/RadioGroupPrimitive';
import RadioGroupPrimitive, { RadioGroupPrimitiveTypes } from '~/core/primitives/RadioGroup/RadioGroupPrimitive';
import { RadioGroupContext } from '../context/RadioGroupContext';
import clsx from 'clsx';

const RadioGroupItem = ({ children, className = '', ...props }: { children: React.ReactNode } & RadioGroupPrimitive.ItemProps) => {
export type RadioGroupItemProps ={

} & RadioGroupPrimitiveTypes.Item;

const RadioGroupItem = ({ children, className = '', value, ...props }:RadioGroupItemProps) => {
const { rootClass } = useContext(RadioGroupContext);
return <RadioGroupPrimitive.Item className={clsx(`${rootClass}-item`, className)} {...props}>{children}</RadioGroupPrimitive.Item>;
return <RadioGroupPrimitive.Item className={clsx(`${rootClass}-item`, className)} value={value} {...props}>{children}</RadioGroupPrimitive.Item>;
};

export default RadioGroupItem;
27 changes: 16 additions & 11 deletions src/components/ui/RadioGroup/fragments/RadioGroupRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
import React from 'react';
import RadioGroupPrimitive from '~/core/primitives/RadioGroup/RadioGroupPrimitive';
import RadioGroupPrimitive, { RadioGroupPrimitiveTypes } from '~/core/primitives/RadioGroup/RadioGroupPrimitive';

import clsx from 'clsx';
import { customClassSwitcher } from '~/core';

import { RadioGroupContext } from '../context/RadioGroupContext';
import { useCreateDataAttribute, useComposeAttributes, useCreateDataAccentColorAttribute } from '~/core/hooks/createDataAttribute';

const COMPONENT_NAME = 'RadioGroup';

type RadioGroupRootProps = {
children: React.ReactNode;
className?: string;
defaultChecked?: string | null;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
customRootClass?: string;
};
export type RadioGroupRootProps = {
customRootClass?: string;
children: React.ReactNode;
variant?: string;
color?: string;
size?: string;
} & RadioGroupPrimitiveTypes.Root

const RadioGroupRoot = ({ children, className = '', defaultChecked = null, onChange = null, customRootClass = '' }: RadioGroupRootProps) => {
const RadioGroupRoot = ({ children, className = '', customRootClass = '', variant = '', color = '', size = '', ...props }: RadioGroupRootProps) => {
const rootClass = customClassSwitcher(customRootClass, COMPONENT_NAME);

return <RadioGroupContext.Provider value={{ defaultChecked, rootClass, onChange }}>
<RadioGroupPrimitive.Root className={clsx(rootClass, className)} customRootClass={customRootClass}>{children}</RadioGroupPrimitive.Root>
const dataAttributes = useCreateDataAttribute('button', { variant, size });
const accentAttributes = useCreateDataAccentColorAttribute(color);
const composedAttributes = useComposeAttributes(dataAttributes(), accentAttributes());

return <RadioGroupContext.Provider value={{ rootClass }}>
<RadioGroupPrimitive.Root className={clsx(rootClass, className)} {...composedAttributes()} {...props}>{children}</RadioGroupPrimitive.Root>
</RadioGroupContext.Provider>;
};

Expand Down
16 changes: 5 additions & 11 deletions src/components/ui/RadioGroup/stories/RadioGroup.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
import RadioGroup from '../RadioGroup';
import React from 'react';
import SandboxEditor from '~/components/tools/SandboxEditor/SandboxEditor';
import { useState } from 'react';

const RadioButton = (args) => {
const RadioButton = () => {
const options = [
{ id: 'html', value: 'html', label: 'HTML' },
{ id: 'css', value: 'css', label: 'CSS' },
{ id: 'javascript', value: 'javascript', label: 'JavaScript' }];

const [language, setLanguage] = useState('css');

const handleChange = (e) => {
setLanguage(e.target.value);
};
return (
<SandboxEditor>
<RadioGroup.Root defaultChecked={language} items={options} onChange={handleChange} >
<RadioGroup.Root>
{options.map((option) => (
<RadioGroup.Item key={option.id} value={option.value}>
<RadioGroup.Item key={option.id} value={option.value} >
{option.label}
</RadioGroup.Item>
))}
Expand All @@ -29,8 +24,7 @@ const RadioButton = (args) => {
export default {
title: 'WIP/RadioGroup',
component: RadioGroup,
render: (args) => <RadioButton {...args}/>
render: () => <RadioButton />
};

export const All = {};
All.args = {};
25 changes: 25 additions & 0 deletions src/core/primitives/Radio/tests/Radio.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ describe('RadioPrimitive', () => {
expect(radio).toHaveAttribute('id', 'radio1');
});

it('applies checked, required, and disabled props', () => {
render(
<RadioPrimitive {...baseProps} checked required disabled />
);
const radio = screen.getByRole('radio');
expect(radio).toBeChecked();
expect(radio).toBeRequired();
expect(radio).toBeDisabled();
expect(radio).toHaveAttribute('aria-disabled', 'true');
expect(radio).toHaveAttribute('aria-required', 'true');
});
it('applies checked, required, and disabled props', () => {
render(
<RadioPrimitive {...baseProps} checked required disabled />
Expand All @@ -30,6 +41,15 @@ describe('RadioPrimitive', () => {
expect(radio).toHaveAttribute('aria-required', 'true');
});

it('calls onChange when clicked', () => {
const handleChange = jest.fn();
render(
<RadioPrimitive {...baseProps} onChange={handleChange} />
);
const radio = screen.getByRole('radio');
fireEvent.click(radio);
expect(handleChange).toHaveBeenCalled();
});
it('calls onChange when clicked', () => {
const handleChange = jest.fn();
render(
Expand All @@ -45,4 +65,9 @@ describe('RadioPrimitive', () => {
const radio = screen.getByRole('radio');
expect(radio).toBeInTheDocument();
});
it('supports asChild prop (renders without error)', () => {
render(<RadioPrimitive {...baseProps} asChild />);
const radio = screen.getByRole('radio');
expect(radio).toBeInTheDocument();
});
});
9 changes: 7 additions & 2 deletions src/core/primitives/RadioGroup/RadioGroupPrimitive.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import React, { DetailedHTMLProps, InputHTMLAttributes, PropsWithChildren } from 'react';

import RadioGroupPrimitiveRoot from './fragments/RadioGroupPrimitiveRoot';
import RadioGroupPrimitiveItem from './fragments/RadioGroupPrimitiveItem';
import RadioGroupPrimitiveRoot, { RadioGroupPrimitiveRootProps } from './fragments/RadioGroupPrimitiveRoot';
import RadioGroupPrimitiveItem, { RadioGroupPrimitiveItemProps } from './fragments/RadioGroupPrimitiveItem';

export type RadioGroupProps = {
children?: React.ReactNode;

} & DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> & PropsWithChildren

export namespace RadioGroupPrimitiveTypes {
export type Root = RadioGroupPrimitiveRootProps;
export type Item = RadioGroupPrimitiveItemProps;
}

const RadioGroupPrimitive = () => {
console.warn('Direct usage of RadioGroup is not supported. Please use RadioGroup.Root, RadioGroup.Item, etc. instead.');
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import RadioGroupContext from '../context/RadioGroupContext';
import RovingFocusGroup from '~/core/utils/RovingFocusGroup';
import RadioPrimitive from '~/core/primitives/Radio';

type RadioGroupPrimitiveItemProps = PropsWithChildren<{
export type RadioGroupPrimitiveItemProps = PropsWithChildren<{
value: string;
disabled?: boolean
children?: React.ReactNode;
required?: boolean
className?: string
}>;

const RadioGroupPrimitiveItem = ({ value, children, disabled, required = false, ...props }: RadioGroupPrimitiveItemProps) => {
const RadioGroupPrimitiveItem = ({ value, children, disabled, required = false, className = '', ...props }: RadioGroupPrimitiveItemProps) => {
const context = useContext(RadioGroupContext);
if (!context) {
throw new Error('RadioGroup.Item must be used within a RadioGroup.Root');
Expand All @@ -19,7 +20,7 @@ const RadioGroupPrimitiveItem = ({ value, children, disabled, required = false,

return (
<RovingFocusGroup.Item role='radio'>
<div {...props} onFocus={() => setSelectedValue(value)}>
<div {...props} onFocus={() => setSelectedValue(value)} className={className} data-checked={selectedValue === value}>

<RadioPrimitive
id={value}
Expand All @@ -28,6 +29,7 @@ const RadioGroupPrimitiveItem = ({ value, children, disabled, required = false,
checked = {selectedValue === value}
disabled={groupDisabled || disabled}
required={required}
data-checked={selectedValue === value}
onChange={() => setSelectedValue(value)}
/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ import RadioGroupContext from '../context/RadioGroupContext';
import RovingFocusGroup from '~/core/utils/RovingFocusGroup';
import useControllableState from '~/core/hooks/useControllableState';

type RadioGroupPrimitiveRootProps = PropsWithChildren<{
export type RadioGroupPrimitiveRootProps = PropsWithChildren<{
className?: string;
customRootClass?: string;
value?: string;
defaultValue?: string;
onValueChange?: (value: string) => void;
Expand All @@ -18,7 +17,7 @@ type RadioGroupPrimitiveRootProps = PropsWithChildren<{
dir?: 'ltr' | 'rtl';
}>;

const RadioGroupPrimitiveRoot = ({ value, defaultValue = '', onValueChange, children, disabled: groupDisabled = false, required = false, name = '', orientation = 'horizontal', loop = false, dir = 'ltr', ...props }: RadioGroupPrimitiveRootProps) => {
const RadioGroupPrimitiveRoot = ({ value, defaultValue = '', onValueChange, children, disabled: groupDisabled = false, required = false, name = '', orientation = 'both', loop = true, dir = 'ltr', className = '', ...props }: RadioGroupPrimitiveRootProps) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the breaking changes in default prop values

The default values for orientation and loop have been changed:

  • orientation: 'horizontal''both'
  • loop: falsetrue

These changes could potentially break existing implementations that rely on the previous defaults.

Please verify that these default value changes are intentional and documented, and ensure backward compatibility is maintained:


🏁 Script executed:

#!/bin/bash
# Description: Search for RadioGroup usage to assess impact of default prop changes
# Expected: Find all RadioGroup usages to verify if they explicitly set these props

# Search for RadioGroup component usage patterns
rg -A 5 -B 2 "RadioGroup" --type tsx --type ts

# Search for any existing tests that might rely on the old defaults
rg -A 10 -B 2 "(orientation|loop)" --type tsx --type ts | grep -i test

# Look for any documentation that mentions these defaults
fd -e md -e mdx | xargs rg -l "RadioGroup" | head -5 | xargs rg -A 5 -B 5 "(orientation|loop)"

Length of output: 95216


Document and Communicate Updated RadioGroupPrimitive Defaults

Verified that the defaults for orientation ('horizontal''both') and loop (falsetrue) in RadioGroupPrimitiveRoot are intentional and no internal code or tests depend on the old values. To ensure users aren’t caught off-guard, please:

  • Update the JSDoc/API comments in
    src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx
    • Reflect defaults: orientation = 'both', loop = true
  • Add or update the public API documentation (e.g. under docs/app/docs/components/primitives/RadioGroup or your docs directory) to call out the new defaults
  • Record this breaking change in the CHANGELOG or release notes
  • (Optional) Add a basic interaction test for the default focus behavior (no props passed)
🤖 Prompt for AI Agents
In src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx at line
20, update the JSDoc/API comments to reflect the new default values orientation
= 'both' and loop = true. Additionally, update the public API documentation in
the docs directory to clearly state these new defaults. Record this change as a
breaking change in the CHANGELOG or release notes. Optionally, add a basic
interaction test verifying the default focus behavior when no props are passed.

const [selectedValue, setSelectedValue] = useControllableState(
value,
defaultValue,
Expand All @@ -33,10 +32,10 @@ const RadioGroupPrimitiveRoot = ({ value, defaultValue = '', onValueChange, chil
};

return (
<RovingFocusGroup.Root>
<RovingFocusGroup.Root dir={dir} orientation={orientation} loop={loop}>
<RadioGroupContext.Provider value={sendItems}>
<RovingFocusGroup.Group>
<Primitive.div {...props} aria-required={required} role='radiogroup' aria-disabled={groupDisabled}>
<Primitive.div {...props} aria-required={required} role='radiogroup' aria-disabled={groupDisabled} className={className}>

{children}

Expand Down
6 changes: 2 additions & 4 deletions styles/themes/components/radio-group.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@
gap: 24px;
color: var(--rad-ui-color-accent-900);

.rad-ui-radio-group-item{
.rad-ui-radio-group-item input{
display: flex;
align-items: center;
gap: 12px;

input{
width: 20px;
height: 20px;
position:relative;
Expand Down Expand Up @@ -46,7 +44,7 @@
border-radius: 50%;
background-color: var(--rad-ui-color-accent-400);
}
}


}
}
1 change: 0 additions & 1 deletion styles/themes/components/radio.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,3 @@
background-color: var(--rad-ui-color-accent-400);
}
}

7 changes: 1 addition & 6 deletions styles/themes/components/radiocards.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,6 @@
&[data-checked="true"] {
border: 1px solid var(--rad-ui-color-accent-800);
background-color: var(--rad-ui-color-accent-100);
}

&:focus-within {
border: 1px solid var(--rad-ui-color-accent-800);
background-color: var(--rad-ui-color-accent-100);
}
}
}
}
Loading