Skip to content

Conversation

@GoldGroove06
Copy link
Contributor

@GoldGroove06 GoldGroove06 commented Jul 8, 2025

closes #1192 #1193

Summary by CodeRabbit

  • New Features

    • Added support for right-to-left (RTL) layouts and a new "both" orientation mode in radio groups and keyboard navigation.
    • Introduced new stories and improved examples for radio and radio group components, including disabled and form-controlled scenarios.
    • Enhanced radio and radio group components with additional props such as disabled, required, id, and orientation options.
    • Improved accessibility and focus management with roving focus integration in radio groups.
    • Refined primitive input typing for improved type safety.
  • Bug Fixes

    • Improved focus and keyboard navigation to respect text direction and orientation settings.
  • Style

    • Minor formatting and spacing improvements for consistency.
  • Documentation

    • Expanded storybook examples for better demonstration of component usage.
  • Tests

    • Added comprehensive tests for radio group behavior, including controlled/uncontrolled modes, disabling, and error handling.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 8, 2025

Walkthrough

This update expands the Radio and RadioGroup primitives with new props, improved context typing, and enhanced keyboard navigation supporting directionality and flexible orientation. Stories are refactored for controlled usage and new scenarios, and the RovingFocusGroup utilities are updated to support both horizontal and vertical navigation, as well as right-to-left layouts.

Changes

Files/Group Change Summary
src/core/primitives/Radio/RadioPrimitive.stories.tsx Refactored stories for controlled usage, added Disabled story, expanded All story args.
src/core/primitives/Radio/index.tsx Changed RadioPrimitive to stateless, updated props (added id, required, disabled, asChild), removed internal state and onClick.
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx Added new stories for RadioGroupPrimitive, including controlled usage in a form.
src/core/primitives/RadioGroup/RadioGroupPrimitive.tsx Refactored to use explicit interface for RadioGroupPrimitive object, replaced empty object with function component that warns on direct usage.
src/core/primitives/RadioGroup/context/RadioGroupContext.tsx Introduced strongly typed context for radio group state with RadioGroupContextValue interface.
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx Enforced context usage, added disabled and required props, switched to context-driven state and handlers.
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx Added/extended props (disabled, required, name, orientation, loop, dir), refactored for controllable state and roving focus.
src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx Minor formatting and whitespace cleanup only.
src/core/primitives/Select/fragments/SelectPrimitiveItem.tsx Removed extra space in state declaration; no logic changes.
src/core/utils/RovingFocusGroup/context/RovingFocusRootContext.tsx Added dir property, expanded orientation to include 'both'.
src/core/utils/RovingFocusGroup/fragments/RovingFocusItem.tsx Enhanced keyboard navigation to respect dir and support 'both' orientation.
src/core/utils/RovingFocusGroup/fragments/RovingFocusRoot.tsx Added dir prop, expanded orientation prop, adjusted context and ARIA attributes accordingly.
src/components/ui/TabNav/fragments/TabNavLink.tsx Removed unnecessary TypeScript expect-error comment.
src/core/primitives/Primitive/index.tsx Changed PrimitiveProps type to a union distinguishing input elements from others for improved typing.
src/core/primitives/RadioGroup/tests/RadioGroupPrimtive.test.tsx Added comprehensive tests for RadioGroupPrimitive covering controlled/uncontrolled modes, disabling, and error cases.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant RadioGroupPrimitiveRoot
    participant RadioGroupContext
    participant RadioGroupPrimitiveItem
    participant RovingFocusGroup

    User->>RadioGroupPrimitiveRoot: Render with props (name, disabled, orientation, dir, etc.)
    RadioGroupPrimitiveRoot->>RovingFocusGroup: Wrap children for focus management
    RadioGroupPrimitiveRoot->>RadioGroupContext: Provide context (selectedValue, setSelectedValue, groupDisabled, name)
    User->>RadioGroupPrimitiveItem: Interact (click, focus)
    RadioGroupPrimitiveItem->>RadioGroupContext: Consume context, update selected value
    RadioGroupPrimitiveItem->>RovingFocusGroup: Participate in focus navigation (respecting orientation and dir)
Loading

Assessment against linked issues

Objective Addressed Explanation
Add/extend props for RadioGroup Root: asChild, disabled, name, loop, required, orientation, direction (#1192)
Add/extend props for RadioGroup Item: asChild, disabled (#1192)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Possibly related issues

Possibly related PRs

Suggested labels

automerge

Suggested reviewers

  • kotAPI

Poem

In the garden of UI, radios bloom anew,
With context that's typed and directions in view.
Focus now roves, both left and right,
RTL or LTR, it all feels right!
Stories are richer, the code is pristine—
A hop, a click, and radios gleam.
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-07-10T07_33_12_104Z-debug-0.log


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abf7841 and 81928e7.

📒 Files selected for processing (1)
  • src/core/primitives/RadioGroup/RadioGroupPrimitive.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
src/core/primitives/RadioGroup/RadioGroupPrimitive.tsx (1)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
src/core/primitives/RadioGroup/RadioGroupPrimitive.tsx (2)

11-14: LGTM! Clean implementation of the deprecation pattern.

The function correctly implements a deprecation pattern by warning users about direct usage while maintaining backward compatibility. The warning message is clear and provides helpful guidance to use the proper compound component API.


19-19: Minor formatting improvement.

The empty line addition improves code readability by providing better visual separation.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (1)

39-40: Consider completing the RovingFocusGroup implementation.

The RovingFocusGroup code is commented out, which suggests an incomplete implementation. This would provide keyboard navigation support for the radio group.

Would you like me to help implement the RovingFocusGroup integration or create an issue to track this enhancement?

Also applies to: 48-50

src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (2)

6-15: Replace empty interface with type alias.

The empty RadioButtonProps interface should be replaced with a type alias as suggested by static analysis.

Apply this fix:

-interface RadioButtonProps {
-    // Add any props you want to pass to RadioButton here
-}
+type RadioButtonProps = Record<string, never>;

Or if no props are needed, simply use {} in the function signature.


30-30: Consider demonstrating new RadioGroup features.

The story examples should showcase the new props added to RadioGroupPrimitive.Root such as disabled, required, name, etc.

Consider adding stories that demonstrate these new features:

-            <RadioGroupPrimitive.Root  >
+            <RadioGroupPrimitive.Root name="language" required disabled={false} onChange={handleChange}>

This would provide better documentation of the enhanced API.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b9e9699 and 7f027e8.

📒 Files selected for processing (7)
  • src/core/primitives/Radio/RadioPrimitive.stories.tsx (2 hunks)
  • src/core/primitives/Radio/index.tsx (2 hunks)
  • src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (2 hunks)
  • src/core/primitives/RadioGroup/RadioGroupPrimitive.tsx (1 hunks)
  • src/core/primitives/RadioGroup/context/RadioGroupContext.tsx (1 hunks)
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx (2 hunks)
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
src/core/primitives/Radio/index.tsx (2)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
src/core/primitives/RadioGroup/context/RadioGroupContext.tsx (1)
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
src/core/primitives/Radio/RadioPrimitive.stories.tsx (3)
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx (3)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (3)
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (2)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
src/core/primitives/RadioGroup/RadioGroupPrimitive.tsx (1)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
🧬 Code Graph Analysis (2)
src/core/primitives/RadioGroup/context/RadioGroupContext.tsx (1)
src/components/ui/RadioGroup/context/RadioGroupContext.tsx (1)
  • RadioGroupContext (3-7)
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (1)
src/components/ui/RadioGroup/context/RadioGroupContext.tsx (1)
  • RadioGroupContext (3-7)
🪛 Biome (1.9.4)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx

[error] 11-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (18)
src/core/primitives/Radio/index.tsx (2)

10-12: Good addition of new props with proper typing.

The new optional props (disabled, asChild, required) are correctly typed and will enhance the component's functionality.


34-34: Props correctly passed to Primitive.input.

The new props are properly spread to the underlying input element, maintaining the component's API consistency.

src/core/primitives/Radio/RadioPrimitive.stories.tsx (3)

13-16: Good addition of form wrapper for better context.

Wrapping the RadioPrimitive in a <form> element provides a more realistic usage context and helps test form-related behavior.


28-32: Comprehensive story args demonstrating new props.

The story args effectively showcase the new name, value, checked, and required props, providing good documentation and testing coverage.


35-45: Good addition of Disabled story for edge case coverage.

The new Disabled story provides important test coverage for the disabled state functionality.

src/core/primitives/RadioGroup/context/RadioGroupContext.tsx (2)

3-9: Excellent addition of strongly typed context interface.

The RadioGroupContextValue interface provides clear type definitions for all context properties, improving type safety and developer experience.


11-11: Proper context typing with null safety.

The context is correctly typed as RadioGroupContextValue | null, allowing for proper null checks in consuming components.

src/core/primitives/RadioGroup/RadioGroupPrimitive.tsx (2)

12-15: Good interface definition for RadioGroupPrimitive structure.

The RadioGroupPrimitiveType interface clearly defines the expected shape of the RadioGroupPrimitive object, improving type safety.


17-20: Clean refactor to object literal with type safety.

The object literal approach is more concise and maintainable than separate property assignments, while the explicit typing ensures type safety.

src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx (3)

8-8: Good addition of disabled prop to type definition.

The optional disabled prop enhances the component's flexibility while maintaining type safety.


12-16: Excellent runtime context validation.

The context presence check with descriptive error message helps developers catch usage errors during development.


29-29: Correct disabled state logic combining group and item states.

The disabled logic properly combines both the group's disabled state and the individual item's disabled prop using logical OR.

src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (3)

6-17: LGTM! Good accessibility and configuration enhancements.

The new props significantly improve the component's accessibility and configurability. The additions align well with modern React patterns and ARIA best practices.


30-36: LGTM! Context value properly updated.

The context value correctly includes the new groupDisabled and name properties, maintaining the existing API while adding the new functionality.


19-19: Fix parameter destructuring syntax.

The destructuring syntax has a syntax error with the disabled:groupDisabled renaming.

Apply this fix:

-const RadioGroupPrimitiveRoot = ({ children, defaultChecked = '', onChange, asChild=false, disabled:groupDisabled = false, required=false, name='', orientation='vertical', loop=true, ...props }: RadioGroupPrimitiveRootProps) => {
+const RadioGroupPrimitiveRoot = ({ children, defaultChecked = '', onChange, asChild = false, disabled: groupDisabled = false, required = false, name = '', orientation = 'vertical', loop = true, ...props }: RadioGroupPrimitiveRootProps) => {
⛔ Skipped due to learnings
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (3)

17-21: LGTM! Good TypeScript improvements.

The explicit typing of the options array and consistent formatting enhance code readability and type safety.


23-27: LGTM! Proper typing and handler signature.

The state typing and updated handler signature correctly align with the new context API that passes string values directly.


56-66: LGTM! Proper form integration example.

The form template correctly demonstrates controlled usage with proper TypeScript typing and event handling.

Comment on lines 38 to 52
return (
// <RovingFocusGroup.Root orientation={orientation} loop={loop}>
<RadioGroupContext.Provider value={sendItems}>
{/* <RovingFocusGroup.Group > */}
<Primitive.div {...props} aria-required={required} role='radiogroup' aria-disabled={groupDisabled} asChild>

{children}


</Primitive.div>
{/* </RovingFocusGroup.Group> */}
</RadioGroupContext.Provider>
// </RovingFocusGroup.Root>
)
;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix ARIA attribute type and improve formatting.

The implementation has good ARIA support but needs a type fix and formatting improvements.

Apply this fix:

-     return (
-    // <RovingFocusGroup.Root orientation={orientation} loop={loop}>
-    <RadioGroupContext.Provider value={sendItems}>
-        {/* <RovingFocusGroup.Group > */}
-        <Primitive.div {...props} aria-required={required} role='radiogroup' aria-disabled={groupDisabled} asChild>
-            
-                {children}
-                
-            
-            </Primitive.div>
-            {/* </RovingFocusGroup.Group> */}
-    </RadioGroupContext.Provider>
-    // </RovingFocusGroup.Root> 
-     )
-    ;
+    return (
+        // <RovingFocusGroup.Root orientation={orientation} loop={loop}>
+        <RadioGroupContext.Provider value={sendItems}>
+            {/* <RovingFocusGroup.Group > */}
+            <Primitive.div {...props} aria-required={required} role='radiogroup' aria-disabled={groupDisabled ? 'true' : 'false'} asChild={asChild}>
+                {children}
+            </Primitive.div>
+            {/* </RovingFocusGroup.Group> */}
+        </RadioGroupContext.Provider>
+        // </RovingFocusGroup.Root> 
+    );

Key fixes:

  1. aria-disabled should be a string ('true'/'false'), not boolean
  2. asChild prop was missing from the Primitive.div
  3. Improved formatting and indentation
  4. Removed unnecessary empty lines
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
// <RovingFocusGroup.Root orientation={orientation} loop={loop}>
<RadioGroupContext.Provider value={sendItems}>
{/* <RovingFocusGroup.Group > */}
<Primitive.div {...props} aria-required={required} role='radiogroup' aria-disabled={groupDisabled} asChild>
{children}
</Primitive.div>
{/* </RovingFocusGroup.Group> */}
</RadioGroupContext.Provider>
// </RovingFocusGroup.Root>
)
;
return (
// <RovingFocusGroup.Root orientation={orientation} loop={loop}>
<RadioGroupContext.Provider value={sendItems}>
{/* <RovingFocusGroup.Group > */}
<Primitive.div {...props} aria-required={required} role='radiogroup' aria-disabled={groupDisabled ? 'true' : 'false'} asChild={asChild}>
{children}
</Primitive.div>
{/* </RovingFocusGroup.Group> */}
</RadioGroupContext.Provider>
// </RovingFocusGroup.Root>
);
🤖 Prompt for AI Agents
In src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx between
lines 38 and 52, fix the aria-disabled attribute by converting its boolean value
to a string 'true' or 'false'. Ensure the asChild prop is present on
Primitive.div. Also, clean up the formatting by removing unnecessary empty lines
and adjusting indentation for better readability.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (1)

15-38: Connect state to RadioGroup and display selected value.

The story defines state and a change handler but doesn't connect them to the RadioGroup component or display the selected value. This makes the story less useful for demonstrating the component's behavior.

-            <RadioGroupPrimitive.Root orientation='vertical'>
+            <RadioGroupPrimitive.Root orientation='vertical' value={language} onChange={handleChange}>
                 {options.map((option) => (
                     <RadioGroupPrimitive.Item key={option.id} value={option.value}>
                         {option.label}
                     </RadioGroupPrimitive.Item>
                 ))}
             </RadioGroupPrimitive.Root>
+            <p>Selected: {language}</p>
♻️ Duplicate comments (3)
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (2)

38-52: Clean up code formatting and structure.

The return statement has inconsistent formatting with unnecessary empty lines and poor indentation. This affects code readability and maintainability.

- return (
-     <RovingFocusGroup.Root orientation={orientation} loop={loop} >
-         <RadioGroupContext.Provider value={sendItems}>
-
-             <Primitive.div {...props} aria-required={required} role='radiogroup' aria-disabled={groupDisabled} asChild>
-                 <RovingFocusGroup.Group >
-                     {children}
-                 </RovingFocusGroup.Group>
-
-             </Primitive.div>
-
-         </RadioGroupContext.Provider>
-     </RovingFocusGroup.Root>
- )
- ;
+ return (
+     <RovingFocusGroup.Root orientation={orientation} loop={loop}>
+         <RadioGroupContext.Provider value={sendItems}>
+             <Primitive.div {...props} aria-required={required} role='radiogroup' aria-disabled={groupDisabled ? 'true' : 'false'} asChild={asChild}>
+                 <RovingFocusGroup.Group>
+                     {children}
+                 </RovingFocusGroup.Group>
+             </Primitive.div>
+         </RadioGroupContext.Provider>
+     </RovingFocusGroup.Root>
+ );

42-42: Fix ARIA attribute type for accessibility compliance.

The aria-disabled attribute should be a string value ('true'/'false') rather than a boolean to comply with ARIA specifications.

- aria-disabled={groupDisabled}
+ aria-disabled={groupDisabled ? 'true' : 'false'}
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx (1)

31-31: Use context-provided name instead of hardcoded value.

The name prop is hardcoded as 'radio' despite the context providing a name value. This could cause issues with form submission when multiple radio groups exist on the same page.

- <RadioPrimitive name='radio' value={value} checked={checkedItem === value} onChange={handleOnChange} onFocus={handleOnChange} disabled={groupDisabled || disabled} aria-checked={checkedItem === value} required />
+ <RadioPrimitive name={name} value={value} checked={checkedItem === value} onChange={handleOnChange} onFocus={handleOnChange} disabled={groupDisabled || disabled} aria-checked={checkedItem === value} required />
🧹 Nitpick comments (2)
src/core/utils/RovingFocusGroup/context/RovingFocusRootContext.tsx (1)

5-5: Update JSDoc comment to reflect the new 'both' orientation option.

The JSDoc comment should be updated to include the new 'both' value that was added to the orientation type.

- * @property {('horizontal'|'vertical')} orientation - The navigation orientation for arrow keys
+ * @property {('horizontal'|'vertical'|'both')} orientation - The navigation orientation for arrow keys
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (1)

11-13: Replace empty interface with type alias.

An empty interface is equivalent to {} and should be a type alias instead, following TypeScript best practices.

-interface RadioButtonProps {
-    // Add any props you want to pass to RadioButton here
-}
+type RadioButtonProps = {
+    // Add any props you want to pass to RadioButton here
+};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f027e8 and 86f8c0d.

📒 Files selected for processing (11)
  • src/core/primitives/Radio/RadioPrimitive.stories.tsx (2 hunks)
  • src/core/primitives/Radio/index.tsx (2 hunks)
  • src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (2 hunks)
  • src/core/primitives/RadioGroup/RadioGroupPrimitive.tsx (1 hunks)
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx (2 hunks)
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (2 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx (1 hunks)
  • src/core/primitives/Select/fragments/SelectPrimitiveItem.tsx (1 hunks)
  • src/core/utils/RovingFocusGroup/context/RovingFocusRootContext.tsx (1 hunks)
  • src/core/utils/RovingFocusGroup/fragments/RovingFocusItem.tsx (1 hunks)
  • src/core/utils/RovingFocusGroup/fragments/RovingFocusRoot.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/core/primitives/Select/fragments/SelectPrimitiveItem.tsx
  • src/core/primitives/Select/fragments/SelectPrimitiveContent.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/core/primitives/RadioGroup/RadioGroupPrimitive.tsx
  • src/core/primitives/Radio/RadioPrimitive.stories.tsx
  • src/core/primitives/Radio/index.tsx
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
src/core/utils/RovingFocusGroup/fragments/RovingFocusItem.tsx (1)
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
src/core/utils/RovingFocusGroup/fragments/RovingFocusRoot.tsx (1)
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (4)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: krau5
PR: rad-ui/ui#609
File: src/components/ui/Text/Text.tsx:19-22
Timestamp: 2024-12-06T14:30:05.834Z
Learning: When the keys and values for the `as` prop are the same, we should define the type directly using string literals (e.g., `'div' | 'span' | 'p' | 'label'`) and eliminate extra mappings like `tagMap` to simplify the code.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx (3)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (3)
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
🪛 Biome (1.9.4)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx

[error] 10-13: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (14)
src/core/utils/RovingFocusGroup/context/RovingFocusRootContext.tsx (1)

9-9: LGTM! The 'both' orientation addition enhances navigation flexibility.

The addition of 'both' as a valid orientation value allows roving focus groups to handle both horizontal and vertical navigation simultaneously, which is valuable for complex UI patterns.

src/core/utils/RovingFocusGroup/fragments/RovingFocusRoot.tsx (2)

17-17: LGTM! Orientation type extension aligns with context changes.

The addition of "both" to the orientation prop type is consistent with the changes made to the context type and enables flexible navigation behavior.


56-56: LGTM! Proper ARIA attribute handling for 'both' orientation.

The conditional handling of aria-orientation is correct - when orientation is 'both', omitting the attribute is appropriate since there's no single orientation to specify. This follows ARIA best practices.

src/core/utils/RovingFocusGroup/fragments/RovingFocusItem.tsx (4)

185-185: LGTM! Proper keyboard navigation support for 'both' orientation.

The extension of the condition to include 'both' orientation correctly enables vertical navigation (ArrowUp) when the roving focus group supports bidirectional navigation.


192-192: LGTM! Proper keyboard navigation support for 'both' orientation.

The extension of the condition to include 'both' orientation correctly enables horizontal navigation (ArrowLeft) when the roving focus group supports bidirectional navigation.


199-199: LGTM! Proper keyboard navigation support for 'both' orientation.

The extension of the condition to include 'both' orientation correctly enables vertical navigation (ArrowDown) when the roving focus group supports bidirectional navigation.


206-206: LGTM! Proper keyboard navigation support for 'both' orientation.

The extension of the condition to include 'both' orientation correctly enables horizontal navigation (ArrowRight) when the roving focus group supports bidirectional navigation.

src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (2)

6-17: LGTM! Comprehensive prop additions enhance RadioGroup functionality.

The addition of asChild, disabled, required, name, orientation, and loop props significantly enhances the RadioGroup's flexibility and accessibility features. The type definitions are well-structured.


39-40: LGTM! Excellent integration with RovingFocusGroup for keyboard navigation.

The wrapping with RovingFocusGroup.Root and the new orientation and loop props provide proper keyboard navigation support for radio groups, which is essential for accessibility.

src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx (3)

13-16: LGTM! Proper context validation with helpful error message.

The runtime check for context existence with a descriptive error message is excellent for developer experience and prevents silent failures when the component is used incorrectly.


30-33: LGTM! Good integration with RovingFocusGroup for keyboard navigation.

The wrapping with RovingFocusGroup.Item and the role='radio' attribute properly integrates the radio item with the roving focus behavior, enhancing keyboard accessibility.


31-31: LGTM! Comprehensive RadioPrimitive prop usage enhances functionality.

The addition of onFocus, disabled, aria-checked, and required props to the RadioPrimitive component significantly improves the accessibility and user experience of the radio group.

src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (2)

5-9: Well-defined interface for radio options.

The Option interface provides clear typing for radio button options with appropriate string fields.


48-81: Good typing improvements in InFormTemplate.

The component now has proper TypeScript typing for options, state, and event handlers. The form submission logic is correctly implemented.

Note: The existing issue with defaultChecked usage on line 70 has already been addressed in previous review comments.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (1)

70-70: Fix controlled component behavior with defaultChecked.

The current usage of defaultChecked={language} with a dynamic state value is problematic. The defaultChecked prop only sets the initial selection and doesn't update when the language state changes.

Choose one of the following solutions:

Option 1 (Recommended): Use uncontrolled mode with static initial value

-<RadioGroupPrimitive.Root defaultChecked={language} onChange={handleChange} >
+<RadioGroupPrimitive.Root defaultChecked="html" onChange={handleChange} >

Option 2: If controlled mode is needed, extend RadioGroupPrimitiveRoot to support a value prop

-<RadioGroupPrimitive.Root defaultChecked={language} onChange={handleChange} >
+<RadioGroupPrimitive.Root value={language} onChange={handleChange} >

Note: Option 2 requires updating the RadioGroupPrimitiveRoot component to accept a value prop for controlled behavior.

🧹 Nitpick comments (1)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (1)

11-13: Replace empty interface with type alias.

The empty RadioButtonProps interface should be replaced with a type alias as suggested by the static analysis tool.

-interface RadioButtonProps {
-    // Add any props you want to pass to RadioButton here
-}
+type RadioButtonProps = Record<string, never>;

Alternatively, if you expect to add props later, consider using:

-interface RadioButtonProps {
-    // Add any props you want to pass to RadioButton here
-}
+interface RadioButtonProps {
+    // TODO: Add props as needed
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86f8c0d and 9220dee.

📒 Files selected for processing (5)
  • src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (2 hunks)
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (2 hunks)
  • src/core/utils/RovingFocusGroup/context/RovingFocusRootContext.tsx (2 hunks)
  • src/core/utils/RovingFocusGroup/fragments/RovingFocusItem.tsx (2 hunks)
  • src/core/utils/RovingFocusGroup/fragments/RovingFocusRoot.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/core/utils/RovingFocusGroup/fragments/RovingFocusItem.tsx
  • src/core/utils/RovingFocusGroup/fragments/RovingFocusRoot.tsx
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx
  • src/core/utils/RovingFocusGroup/context/RovingFocusRootContext.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (3)
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
🪛 Biome (1.9.4)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx

[error] 10-13: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (6)

1-1: LGTM: Proper TypeScript imports added.

The addition of TypeScript-specific imports for React hooks and event types enhances type safety throughout the component.


5-9: LGTM: Well-defined interface for options.

The Option interface provides clear type definitions for the radio button options, improving code maintainability and IDE support.


15-26: LGTM: Improved TypeScript typing in RadioButton component.

The explicit typing for the component props, options array, state, and event handlers enhances type safety and developer experience.


29-29: LGTM: Enhanced RadioGroupPrimitive with orientation and direction props.

The addition of explicit orientation='horizontal' and dir='rtl' props aligns with the API improvements mentioned in the PR objectives and provides better accessibility support.


43-43: LGTM: Proper TypeScript typing for render function.

The explicit typing of the render function args parameter improves type safety in the Storybook configuration.


49-66: LGTM: Enhanced TypeScript typing in InFormTemplate.

The consistent application of TypeScript typing for the options array, state, and event handlers follows the same pattern as the RadioButton component.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
src/core/primitives/RadioGroup/tests/RadioGroupPrimtive.test.tsx (3)

24-32: Improve radio input selection logic

The getRadioByText helper might be fragile if the DOM structure changes. Consider using more robust selection methods.

  function getRadioByText(label: string): HTMLInputElement {
-   // Find the radio input by traversing from the text node
-   const textNode = screen.getByText(label);
-   const parent = textNode.closest('div');
-   if (!parent) throw new Error('No parent div found for label: ' + label);
-   const radio = parent.querySelector('input[type="radio"]');
-   if (!radio) throw new Error('No radio input found for label: ' + label);
-   return radio as HTMLInputElement;
+   // Find radio by aria-labelledby or use more robust selection
+   const radio = screen.getByRole('radio', { name: label });
+   return radio as HTMLInputElement;
  }

111-120: Enhance keyboard navigation test

The current test for the loop prop is too basic and doesn't actually verify looping behavior. Consider adding more comprehensive keyboard navigation tests.

  it('supports loop prop (keyboard navigation)', () => {
    renderGroup({ loop: true });
-   // Focus the first radio and press ArrowRight to loop
    const radios = options.map(({ label }) => getRadioByText(label));
-   if (radios[0]) (radios[0] as HTMLInputElement).focus();
-   fireEvent.keyDown(radios[0], { key: 'ArrowRight' });
-   // This test is basic, as focus management is handled by RovingFocusGroup
-   // and would require more integration tests for full coverage
-   expect(radios[1]).toBeDefined();
+   
+   // Focus first radio and navigate to second
+   radios[0].focus();
+   fireEvent.keyDown(radios[0], { key: 'ArrowDown' });
+   expect(document.activeElement).toBe(radios[1]);
+   
+   // Navigate to third
+   fireEvent.keyDown(radios[1], { key: 'ArrowDown' });
+   expect(document.activeElement).toBe(radios[2]);
+   
+   // Test looping: from last to first
+   fireEvent.keyDown(radios[2], { key: 'ArrowDown' });
+   expect(document.activeElement).toBe(radios[0]);
  });

100-109: Add verification for the thrown error type

The test verifies that an error is thrown but doesn't check the error message or type, which could help ensure the right error is being thrown.

  it('throws if Item is used outside Root', () => {
    // Suppress error output for this test
    const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
    expect(() =>
      render(
        <RadioGroupPrimitive.Item value="test">Test</RadioGroupPrimitive.Item>
      )
-   ).toThrow();
+   ).toThrow(/RadioGroupPrimitive\.Item must be used within RadioGroupPrimitive\.Root/);
    spy.mockRestore();
  });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9220dee and aac4bd1.

📒 Files selected for processing (3)
  • src/core/primitives/Radio/index.tsx (2 hunks)
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (2 hunks)
  • src/core/primitives/RadioGroup/tests/RadioGroupPrimtive.test.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core/primitives/Radio/index.tsx
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
src/core/primitives/RadioGroup/tests/RadioGroupPrimtive.test.tsx (2)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
🪛 GitHub Actions: Build RAD UI
src/core/primitives/RadioGroup/tests/RadioGroupPrimtive.test.tsx

[error] 51-62: Test failure due to unhandled TypeError: Cannot set properties of undefined (setting 'checked') in RadioGroupPrimitive tests. This causes multiple test failures including 'calls onChange with correct value' and 'only allows one item to be checked at a time'.


[error] 44-44: Test 'checks the defaultChecked value' failed: Expected radio input to be checked but it was not.


[error] 85-85: Test 'passes required and name props to input' failed: Expected input to have attribute name='test-group' but received name='radio'.


[error] 151-151: Test 'works correctly inside a form and submits checked value' failed: Expected submitted value 'js' but received null.

Comment on lines 128 to 152
it('works correctly inside a form and submits checked value', () => {
let submittedValue: FormDataEntryValue | null = null;
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
submittedValue = formData.get('fav');
};
render(
<form onSubmit={handleSubmit}>
<RadioGroupPrimitive.Root name="fav" defaultChecked="css">
{options.map((option) => (
<RadioGroupPrimitive.Item key={option.value} value={option.value}>
{option.label}
</RadioGroupPrimitive.Item>
))}
</RadioGroupPrimitive.Root>
<button type="submit">Submit</button>
</form>
);
// Change selection
const radio = getRadioByText('JavaScript');
fireEvent.click(radio);
fireEvent.click(screen.getByText('Submit'));
expect(submittedValue).toBe('js');
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix form submission test - use correct prop name

The test fails because it uses defaultChecked instead of defaultValue, and the name prop issue also affects form data collection.

  it('works correctly inside a form and submits checked value', () => {
    let submittedValue: FormDataEntryValue | null = null;
    const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
      e.preventDefault();
      const formData = new FormData(e.target as HTMLFormElement);
      submittedValue = formData.get('fav');
    };
    render(
      <form onSubmit={handleSubmit}>
-       <RadioGroupPrimitive.Root name="fav" defaultChecked="css">
+       <RadioGroupPrimitive.Root name="fav" defaultValue="css">
          {options.map((option) => (
            <RadioGroupPrimitive.Item key={option.value} value={option.value}>
              {option.label}
            </RadioGroupPrimitive.Item>
          ))}
        </RadioGroupPrimitive.Root>
        <button type="submit">Submit</button>
      </form>
    );
    // Change selection
    const radio = getRadioByText('JavaScript');
    fireEvent.click(radio);
    fireEvent.click(screen.getByText('Submit'));
    expect(submittedValue).toBe('js');
  });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('works correctly inside a form and submits checked value', () => {
let submittedValue: FormDataEntryValue | null = null;
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
submittedValue = formData.get('fav');
};
render(
<form onSubmit={handleSubmit}>
<RadioGroupPrimitive.Root name="fav" defaultChecked="css">
{options.map((option) => (
<RadioGroupPrimitive.Item key={option.value} value={option.value}>
{option.label}
</RadioGroupPrimitive.Item>
))}
</RadioGroupPrimitive.Root>
<button type="submit">Submit</button>
</form>
);
// Change selection
const radio = getRadioByText('JavaScript');
fireEvent.click(radio);
fireEvent.click(screen.getByText('Submit'));
expect(submittedValue).toBe('js');
});
it('works correctly inside a form and submits checked value', () => {
let submittedValue: FormDataEntryValue | null = null;
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
submittedValue = formData.get('fav');
};
render(
<form onSubmit={handleSubmit}>
<RadioGroupPrimitive.Root name="fav" defaultValue="css">
{options.map((option) => (
<RadioGroupPrimitive.Item key={option.value} value={option.value}>
{option.label}
</RadioGroupPrimitive.Item>
))}
</RadioGroupPrimitive.Root>
<button type="submit">Submit</button>
</form>
);
// Change selection
const radio = getRadioByText('JavaScript');
fireEvent.click(radio);
fireEvent.click(screen.getByText('Submit'));
expect(submittedValue).toBe('js');
});
🧰 Tools
🪛 GitHub Actions: Build RAD UI

[error] 151-151: Test 'works correctly inside a form and submits checked value' failed: Expected submitted value 'js' but received null.

🤖 Prompt for AI Agents
In src/core/primitives/RadioGroup/tests/RadioGroupPrimtive.test.tsx lines 128 to
152, the test uses the incorrect prop name `defaultChecked` on
RadioGroupPrimitive.Root, which causes the form submission to fail capturing the
selected value. Replace `defaultChecked` with `defaultValue` to correctly set
the initial selected radio button. Also verify that the `name` prop is correctly
set on the RadioGroupPrimitive.Root to ensure form data is collected properly.

Comment on lines 81 to 86
it('passes required and name props to input', () => {
renderGroup({ required: true, name: 'test-group' });
const radio = getRadioByText('HTML');
expect(radio).toBeRequired();
expect(radio).toHaveAttribute('name', 'test-group');
});
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

Fix name prop forwarding issue

The test expects name='test-group' but receives name='radio', indicating the name prop isn't being properly forwarded to the radio inputs.

This suggests the RadioGroupPrimitive implementation isn't correctly passing the name prop to individual radio inputs. The implementation should ensure all radio items in a group share the same name attribute.


🏁 Script executed:

#!/bin/bash
# Check how the name prop is handled in RadioGroupPrimitive
echo "Searching for name prop usage in RadioGroup implementation..."
rg -A 5 -B 5 "name.*prop" src/core/primitives/RadioGroup/
echo "Looking for input element creation..."
ast-grep --pattern 'input[type="radio"]'

Length of output: 2643


🏁 Script executed:

#!/bin/bash
# List all RadioGroup primitive fragment files
echo "Fragment files:"
fd -e tsx RadioGroupPrimitive* src/core/primitives/RadioGroup/fragments

# Search for context creation to see what values are provided
echo -e "\ncreateContext occurrences:"
rg -n "createContext" src/core/primitives/RadioGroup/fragments

# Search within provider usage for 'name' to see if it's included
echo -e "\nContext value includes 'name'?:"
rg -n "value\s*=\s*{[^}]*name" -n src/core/primitives/RadioGroup/fragments

# Find where input[type="radio"] is rendered
echo -e "\n<input type=\"radio\" occurrences:"
rg -n "<input[^>]*type=[\"']radio[\"']" -n src/core/primitives/RadioGroup/fragments

Length of output: 837


🏁 Script executed:

#!/bin/bash
# Locate where the RadioGroup context is defined
echo "Searching for createContext in RadioGroup directory..."
rg -n createContext src/core/primitives/RadioGroup

# If a context file is found, display its contents
echo -e "\nContext definition file content (first 200 lines):"
rg -l createContext src/core/primitives/RadioGroup | xargs -I{} sed -n '1,200p' {}

# Show how the Provider is used in the Root component
echo -e "\nProvider usage in RadioGroupPrimitiveRoot.tsx:"
rg -n "Provider" -n src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx
rg -n "value\s*=" -n src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx

# Inspect the Input element in the Item component
echo -e "\n<input type=\"radio\" in RadioGroupPrimitiveItem.tsx:"
rg -n '<input[^>]*type=[\"'"'"']radio[\"'"'"']' -n src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx
sed -n '1,200p' src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx

Length of output: 3216


Update RadioGroupPrimitiveItem to forward context.name

The name prop is currently hard-coded to 'radio' instead of using the name value from context. This causes tests like:

expect(radio).toHaveAttribute('name', 'test-group');

to fail, since inputs all get name="radio".

Locations to fix:

  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx, inside the <RadioPrimitive> invocation.

Proposed change:

- <RadioPrimitive
-   name='radio'
+ <RadioPrimitive
+   name={name}
    value={value}
    checked={checkedItem === value}
    onChange={handleOnChange}
    onFocus={handleOnChange}
    disabled={groupDisabled || disabled}
    aria-checked={checkedItem === value}
    required
/>

This ensures the name passed to each radio input comes from the RadioGroup’s name prop and satisfies the existing test.

🧰 Tools
🪛 GitHub Actions: Build RAD UI

[error] 85-85: Test 'passes required and name props to input' failed: Expected input to have attribute name='test-group' but received name='radio'.

🤖 Prompt for AI Agents
In src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx around
the RadioPrimitive component, the name prop is currently hard-coded to 'radio'
instead of using the name from context. To fix this, update the RadioPrimitive
invocation to forward the name value from the RadioGroup context rather than a
fixed string. This will ensure the input elements receive the correct name prop,
matching the RadioGroup's name and making the test expecting 'test-group' pass.

Comment on lines 47 to 54
it('calls onChange with correct value', () => {
const handleChange = jest.fn();
renderGroup({ onChange: handleChange });
const radio = getRadioByText('JavaScript');
fireEvent.click(radio);
expect(handleChange).toHaveBeenCalledWith('js');
expect(radio).toBeChecked();
});
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

Test may fail due to implementation issues

The pipeline failure indicates a TypeError when setting the 'checked' property. This suggests the underlying RadioGroupPrimitive implementation has bugs in how it manages radio state.


🏁 Script executed:

#!/bin/bash
# Check the RadioGroupPrimitive implementation for potential issues
echo "Searching for RadioGroupPrimitive implementation..."
fd -e tsx -e ts RadioGroupPrimitive | head -10
echo "Looking for how radio inputs are managed..."
rg -A 10 "checked.*=" src/core/primitives/RadioGroup/

Length of output: 6245


🏁 Script executed:

#!/bin/bash
FILE=src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx
echo "==== Top of RadioGroupPrimitiveItem.tsx ===="
head -n20 $FILE
echo
echo "==== handleOnChange snippet ===="
sed -n '1,200p' $FILE | sed -n '/const handleOnChange/,/};/p'
echo
echo "==== Searching for RadioPrimitive definition ===="
rg -R "function RadioPrimitive" -e tsx -e ts || true
rg -R "const RadioPrimitive" -e tsx -e ts || true
rg -R "export default RadioPrimitive" -e tsx -e ts || true

Length of output: 3050


🏁 Script executed:

#!/bin/bash
echo "Locating RadioPrimitive files under src/core/primitives/Radio"
fd -t f -e tsx -e ts src/core/primitives/Radio

echo
echo "Displaying Radio.tsx or index.tsx content if present"
for f in src/core/primitives/Radio.tsx src/core/primitives/Radio/index.tsx src/core/primitives/Radio/index.ts; do
  if [ -f "$f" ]; then
    echo "==== $f ===="
    head -n200 "$f"
    echo
  fi
done

Length of output: 2678


🏁 Script executed:

#!/bin/bash
echo "Locating Primitive component"
fd -t f -e tsx -e ts primitives/Primitive --base-path src/core
echo
echo "Displaying Primitive.tsx content"
FILE=src/core/primitives/Primitive.tsx
if [ -f "$FILE" ]; then
  sed -n '1,200p' "$FILE"
else
  echo "Primitive.tsx not found"
fi

Length of output: 611


Fix onChange forwarding in RadioPrimitive to satisfy RadioGroup handler

The TypeError in your tests stems from a mismatch between RadioPrimitive’s onChange payload and what RadioGroupPrimitiveItem expects. RadioPrimitive currently calls:

onChange({
  value,
  checked: isChecked
});

so in the group item, handleOnChange(event) receives an object without event.target, causing event.target.checked = true to throw.

Please update:

  • src/core/primitives/Radio/index.tsx – forward the native event to consumers instead of a custom object.
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx – you can then rely on event.target.checked or read event.target.value directly.

Suggested patch for RadioPrimitive:

--- a/src/core/primitives/Radio/index.tsx
+++ b/src/core/primitives/Radio/index.tsx
@@ const RadioPrimitive = ({ /*…*/ }: RadioPrimitiveProps) => {
-  const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
-    const isChecked = event.target.checked;
-    console.log(isChecked);
-    if (typeof onChange === 'function') {
-      setIsChecked(isChecked);
-      onChange({
-        value,
-        checked: isChecked
-      });
-    }
-  };
+  const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+    const isChecked = event.target.checked;
+    setIsChecked(isChecked);
+    if (typeof onChange === 'function') {
+      onChange(event);  // forward the original event
+    }
+  };

With that change, in RadioGroupPrimitiveItem.tsx your existing handler can safely do:

const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  // event.target is now defined
  setCheckedItem(value);
  onChange?.(value);
};

This aligns both components and should resolve the TypeError in your test suite.

🤖 Prompt for AI Agents
In src/core/primitives/RadioGroup/tests/RadioGroupPrimtive.test.tsx around lines
47 to 54, the test fails due to RadioPrimitive forwarding a custom object
instead of the native event, causing a TypeError when RadioGroupPrimitiveItem
tries to access event.target.checked. To fix this, update
src/core/primitives/Radio/index.tsx to forward the native change event to the
onChange handler instead of a custom object, and adjust
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx to handle
the native event by accessing event.target.checked and event.target.value
directly. This will align the event handling between components and resolve the
test error.


RadioGroupPrimitive.Root = RadioGroupPrimitiveRoot;
RadioGroupPrimitive.Item = RadioGroupPrimitiveItem;
const RadioGroupPrimitive: RadioGroupPrimitiveType = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

the previous declaration is valid, we use this pattern/style everywhere else, so do other libs

we should make the ts lint work our way, not the other way around

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed, thanks for pointing out

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (1)

15-38: Fix unused handleChange function and missing controlled behavior.

The handleChange function is defined but never used, and the RadioGroupPrimitive.Root doesn't receive the language state or change handler. This creates a disconnect between the component's state and the radio group's behavior.

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

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

     const handleChange = (item: string) => {
         setLanguage(item);
     };
     return (
         <SandboxEditor>
-            <RadioGroupPrimitive.Root orientation='horizontal' dir='rtl' name='test'>
+            <RadioGroupPrimitive.Root 
+                orientation='horizontal' 
+                dir='rtl' 
+                name='test'
+                value={language}
+                onValueChange={handleChange}
+            >
                 {options.map((option) => (
                     <RadioGroupPrimitive.Item key={option.id} value={option.value}>
                         {option.label}
                     </RadioGroupPrimitive.Item>
                 ))}
             </RadioGroupPrimitive.Root>
         </SandboxEditor>
     );
 };
🧹 Nitpick comments (2)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (2)

11-13: Replace empty interface with type alias.

The empty interface RadioButtonProps should be replaced with a type alias for better TypeScript practices.

-interface RadioButtonProps {
-    // Add any props you want to pass to RadioButton here
-}
+type RadioButtonProps = Record<string, never>;

Alternatively, if no props are needed, you can remove the interface entirely and use {} directly in the function parameter.


92-95: Remove unnecessary blank lines.

Multiple consecutive blank lines should be cleaned up for better code consistency.

-

-

-

-
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aac4bd1 and 1ead8c9.

📒 Files selected for processing (5)
  • src/core/primitives/Radio/index.tsx (2 hunks)
  • src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (1 hunks)
  • src/core/primitives/RadioGroup/context/RadioGroupContext.tsx (1 hunks)
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx (1 hunks)
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/core/primitives/RadioGroup/context/RadioGroupContext.tsx
  • src/core/primitives/Radio/index.tsx
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (3)
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
🪛 Biome (1.9.4)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx

[error] 10-13: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (1)

48-90: Good implementation of controlled radio group usage.

The InForm component correctly demonstrates controlled usage with proper state management, form submission handling, and value synchronization.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (6)
src/core/primitives/RadioGroup/tests/RadioGroupPrimtive.test.tsx (5)

10-10: Fix prop name: use defaultValue instead of defaultChecked

Based on the past review comments, the RadioGroupPrimitive uses defaultValue to set the initially selected option, not defaultChecked.


43-43: Fix prop name: use defaultValue instead of defaultChecked

Consistent with other tests, this should use defaultValue instead of defaultChecked.


56-56: Fix prop name: use defaultValue instead of defaultChecked

Consistent with other tests, this should use defaultValue instead of defaultChecked.


69-69: Fix prop name: use defaultValue instead of defaultChecked

Consistent with other tests, this should use defaultValue instead of defaultChecked.


74-74: Verify name prop forwarding implementation

Based on past review comments, the test expects name='my-radio-group' but the implementation might be forwarding name='radio' instead. Ensure the RadioGroupPrimitiveItem component correctly forwards the name from context.

src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (1)

35-48: Fix ARIA attributes and improve RovingFocusGroup integration

Several issues need to be addressed:

  1. aria-disabled should be a string ('true'/'false'), not boolean
  2. Missing asChild prop on Primitive.div
  3. RovingFocusGroup components aren't receiving the orientation, loop, and dir props
  4. Poor formatting

Apply this fix:

-    return (
-        <RovingFocusGroup.Root>
-            <RadioGroupContext.Provider value={sendItems}>
-                <RovingFocusGroup.Group>
-                    <Primitive.div {...props} aria-required={required} role='radiogroup' aria-disabled={groupDisabled}>
-
-                        {children}
-
-                    </Primitive.div>
-                </RovingFocusGroup.Group>
-            </RadioGroupContext.Provider>
-        </RovingFocusGroup.Root>
-    )
-    ;
+    return (
+        <RovingFocusGroup.Root orientation={orientation} loop={loop} dir={dir}>
+            <RadioGroupContext.Provider value={sendItems}>
+                <RovingFocusGroup.Group>
+                    <Primitive.div 
+                        {...props} 
+                        aria-required={required} 
+                        role='radiogroup' 
+                        aria-disabled={groupDisabled ? 'true' : 'false'}
+                        asChild={props.asChild}
+                    >
+                        {children}
+                    </Primitive.div>
+                </RovingFocusGroup.Group>
+            </RadioGroupContext.Provider>
+        </RovingFocusGroup.Root>
+    );
🧹 Nitpick comments (3)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (2)

11-13: Fix empty interface issue

As indicated by static analysis, an empty interface is equivalent to {}. Use a type alias instead.

Apply this fix:

-interface RadioButtonProps {
-    // Add any props you want to pass to RadioButton here
-}
+type RadioButtonProps = {}

22-26: Remove unused state and handler

The language state and handleChange function are defined but never used in the RadioButton component. Clean up by removing unused code.

-    const [language, setLanguage] = useState<string>('css');
-
-    const handleChange = (item: string) => {
-        setLanguage(item);
-    };
src/core/primitives/Radio/index.tsx (1)

21-21: Consider making tabIndex configurable

The hardcoded tabIndex={-1} might not be appropriate for all use cases. Consider making it configurable or removing it to allow natural tab order.

-            tabIndex={-1}
+            tabIndex={props.tabIndex ?? -1}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ead8c9 and abf7841.

📒 Files selected for processing (8)
  • src/components/ui/TabNav/fragments/TabNavLink.tsx (0 hunks)
  • src/core/primitives/Primitive/index.tsx (1 hunks)
  • src/core/primitives/Radio/RadioPrimitive.stories.tsx (2 hunks)
  • src/core/primitives/Radio/index.tsx (1 hunks)
  • src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (1 hunks)
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx (1 hunks)
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (1 hunks)
  • src/core/primitives/RadioGroup/tests/RadioGroupPrimtive.test.tsx (1 hunks)
💤 Files with no reviewable changes (1)
  • src/components/ui/TabNav/fragments/TabNavLink.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/core/primitives/Radio/RadioPrimitive.stories.tsx
  • src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveItem.tsx
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
src/core/primitives/RadioGroup/tests/RadioGroupPrimtive.test.tsx (3)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
src/core/primitives/Primitive/index.tsx (3)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: krau5
PR: rad-ui/ui#609
File: src/components/ui/Text/Text.tsx:19-22
Timestamp: 2024-12-06T14:30:05.834Z
Learning: When the keys and values for the `as` prop are the same, we should define the type directly using string literals (e.g., `'div' | 'span' | 'p' | 'label'`) and eliminate extra mappings like `tagMap` to simplify the code.
Learnt from: krau5
PR: rad-ui/ui#609
File: src/components/ui/Text/Text.tsx:19-22
Timestamp: 2024-12-06T14:30:18.599Z
Learning: When the keys and values for the `as` prop are identical, it's preferable to use a union type directly for the `as` prop and eliminate unnecessary mappings like `tagMap`.
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (3)
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (4)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
Learnt from: krau5
PR: rad-ui/ui#609
File: src/components/ui/Text/Text.tsx:19-22
Timestamp: 2024-12-06T14:30:05.834Z
Learning: When the keys and values for the `as` prop are the same, we should define the type directly using string literals (e.g., `'div' | 'span' | 'p' | 'label'`) and eliminate extra mappings like `tagMap` to simplify the code.
src/core/primitives/Radio/index.tsx (3)
Learnt from: kotAPI
PR: rad-ui/ui#576
File: src/core/primitives/Toggle/index.tsx:15-22
Timestamp: 2024-11-24T06:43:42.194Z
Learning: In the `TogglePrimitive` component (`src/core/primitives/Toggle/index.tsx`), when the component becomes controlled, it's acceptable to not sync the internal `isPressed` state with the external `pressed` prop.
Learnt from: kotAPI
PR: rad-ui/ui#1031
File: src/components/ui/Accordion/fragments/AccordionRoot.tsx:41-44
Timestamp: 2025-04-07T04:38:34.864Z
Learning: The Accordion component in rad-ui/ui supports both controlled and uncontrolled modes through props like `value`, `defaultValue`, and `onValueChange`. When implementing controlled components, remember to: 1) Initialize state from defaultValue, 2) Update internal state when value changes (controlled mode), 3) Call onValueChange callback, and 4) Prevent internal state updates when in controlled mode.
Learnt from: decipher-cs
PR: rad-ui/ui#417
File: src/components/ui/Dropdown/Dropdown.stories.tsx:43-50
Timestamp: 2024-12-12T08:34:33.079Z
Learning: Ensure to verify existing ARIA attributes in components before suggesting additions during code reviews, especially in the `Dropdown.Trigger` component in `src/components/ui/Dropdown/Dropdown.stories.tsx`.
🧬 Code Graph Analysis (1)
src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (2)
src/core/hooks/useControllableState/index.tsx (1)
  • useControllableState (15-78)
src/components/ui/RadioGroup/context/RadioGroupContext.tsx (1)
  • RadioGroupContext (3-7)
🪛 Biome (1.9.4)
src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx

[error] 10-13: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (4)
src/core/primitives/Primitive/index.tsx (1)

8-10: Excellent type safety improvement!

The union type approach correctly differentiates between input elements and other HTML elements, providing better type specificity. This change supports the updated RadioPrimitive component that now passes explicit input-related props to Primitive.input.

src/core/primitives/RadioGroup/fragments/RadioGroupPrimitiveRoot.tsx (1)

21-26: Excellent controlled/uncontrolled state implementation!

The use of useControllableState correctly implements the controlled/uncontrolled pattern following the established learning about initializing from defaultValue, updating internal state when controlled, and calling onValueChange callback.

src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx (1)

73-77: Excellent controlled component example!

The InForm story correctly demonstrates controlled usage with value and onValueChange props, following the established pattern for controlled components.

src/core/primitives/Radio/index.tsx (1)

4-13: Good type definition improvements!

The updated RadioPrimitiveProps type correctly includes all necessary props for a radio input element. The addition of id, required, disabled, and asChild props enhances the component's flexibility.

Comment on lines +29 to +33
<RadioGroupPrimitive.Root
orientation='horizontal' dir='rtl'
name='test'
// defaultValue='css'
>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add defaultValue for uncontrolled mode

The RadioButton story should demonstrate uncontrolled usage with a defaultValue prop, as the commented line suggests.

             <RadioGroupPrimitive.Root
                 orientation='horizontal' dir='rtl'
                 name='test'
-            //  defaultValue='css'
+                defaultValue='css'
             >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<RadioGroupPrimitive.Root
orientation='horizontal' dir='rtl'
name='test'
// defaultValue='css'
>
<RadioGroupPrimitive.Root
orientation='horizontal' dir='rtl'
name='test'
defaultValue='css'
>
🤖 Prompt for AI Agents
In src/core/primitives/RadioGroup/RadioGroupPrimitive.stories.tsx around lines
29 to 33, the RadioGroupPrimitive.Root component is missing the defaultValue
prop needed for uncontrolled mode. Uncomment the defaultValue='css' line to
properly demonstrate uncontrolled usage in the story.

Comment on lines +15 to +33
function RadioPrimitive({ name, value, id, checked, required, onChange, disabled, asChild, ...props }: RadioPrimitiveProps) {
return (
<Primitive.input id={value} type='radio' value={value} name={name} onChange={handleOnChange} {...props} {...dataAttributes}/>
<Primitive.input
type="radio"
checked={checked}
name={name}
tabIndex={-1}
value={value}
onChange={onChange}
id={value}
aria-disabled={disabled}
disabled={disabled}
required={required}
aria-required={required}
asChild={asChild}
{...props}
/>
);
};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix id prop usage and verify onChange signature

Two issues need attention:

  1. The id prop from props is not being used - line 24 sets id={value} instead of id={id}
  2. The onChange signature changed from accepting an event to no arguments - ensure this is intentional and compatible with parent components

Apply this fix:

         <Primitive.input
             type="radio"
             checked={checked}
             name={name}
             tabIndex={-1}
             value={value}
             onChange={onChange}
-            id={value}
+            id={id}
             aria-disabled={disabled}
             disabled={disabled}
             required={required}
             aria-required={required}
             asChild={asChild}
             {...props}
         />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function RadioPrimitive({ name, value, id, checked, required, onChange, disabled, asChild, ...props }: RadioPrimitiveProps) {
return (
<Primitive.input id={value} type='radio' value={value} name={name} onChange={handleOnChange} {...props} {...dataAttributes}/>
<Primitive.input
type="radio"
checked={checked}
name={name}
tabIndex={-1}
value={value}
onChange={onChange}
id={value}
aria-disabled={disabled}
disabled={disabled}
required={required}
aria-required={required}
asChild={asChild}
{...props}
/>
);
};
}
function RadioPrimitive({ name, value, id, checked, required, onChange, disabled, asChild, ...props }: RadioPrimitiveProps) {
return (
<Primitive.input
type="radio"
checked={checked}
name={name}
tabIndex={-1}
value={value}
onChange={onChange}
id={id}
aria-disabled={disabled}
disabled={disabled}
required={required}
aria-required={required}
asChild={asChild}
{...props}
/>
);
}
🤖 Prompt for AI Agents
In src/core/primitives/Radio/index.tsx between lines 15 and 33, the id prop is
incorrectly set to the value prop; update the id attribute to use the id prop
instead of value. Also, verify that the onChange handler's signature matches the
expected usage by parent components—if onChange no longer receives an event
argument, confirm this change is intentional and adjust the handler accordingly
to maintain compatibility.

@kotAPI kotAPI added the automerge A tag that tells kodiak bot to automerge PRs for us when tests and approval conditions are met label Jul 10, 2025
@kodiakhq kodiakhq bot merged commit 631dfe7 into main Jul 10, 2025
6 checks passed
@kodiakhq kodiakhq bot deleted the radiogroup-primitive-api branch July 10, 2025 15:11
This was referenced Sep 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge A tag that tells kodiak bot to automerge PRs for us when tests and approval conditions are met

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[New Feature] Api addition for RadioGroup Primitive

3 participants