Skip to content

feat: add Toggle component#696

Merged
rohanchkrabrty merged 2 commits intomainfrom
feat-toggle
Mar 17, 2026
Merged

feat: add Toggle component#696
rohanchkrabrty merged 2 commits intomainfrom
feat-toggle

Conversation

@rohanchkrabrty
Copy link
Contributor

@rohanchkrabrty rohanchkrabrty commented Mar 12, 2026

Summary

  • Add Toggle and Toggle.Group components wrapping Base UI's Toggle and ToggleGroup primitives
  • Toggle supports size prop (1–4) via CVA, matching IconButton sizing
  • Toggle.Group provides shared pressed state with single or multiple selection, with grouped styling (border-radius handled via overflow: clip)
  • Add docs page with interactive playground, code demos (group, multiple, size, controlled, disabled), and API reference
  • Add playground examples and register toggle icons in the react-live demo scope

Summary by CodeRabbit

  • New Features

    • Added a Toggle component with sizes, pressed/disabled states, and Toggle.Group (single/multiple selection). Exposed Toggle at package level and added demo/playground examples including alignment and formatting toggles.
  • Documentation

    • Added comprehensive Toggle docs with examples, API reference, and interactive demos.
  • Tests

    • Added a test suite covering rendering, state, keyboard interactions, and accessibility.

@vercel
Copy link

vercel bot commented Mar 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
apsara Error Error Mar 17, 2026 5:25am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 12, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2393233f-c985-448e-91d0-4394ae9fa792

📥 Commits

Reviewing files that changed from the base of the PR and between cd6975e and 38d8cc5.

📒 Files selected for processing (3)
  • apps/www/src/components/demo/demo.tsx
  • apps/www/src/components/playground/index.ts
  • packages/raystack/index.tsx

📝 Walkthrough

Walkthrough

Adds a new Toggle component (with Toggle.Group), styles, tests, docs, demos, and playground examples; exports the component at package and app levels and exposes additional icons for demos.

Changes

Cohort / File(s) Summary
Toggle implementation
packages/raystack/components/toggle/toggle.tsx, packages/raystack/components/toggle/toggle.module.css
New Toggle component and Toggle.Group, forwardRef support, size variants (1–4), class-variance-authority variants, and CSS module with states for hover/pressed/disabled and group layouts.
Package exports
packages/raystack/components/toggle/index.ts, packages/raystack/index.tsx
Re-export Toggle at component package level and add Toggle to the top-level package exports; updated toast export names.
Tests
packages/raystack/components/toggle/__tests__/toggle.test.tsx
Comprehensive test suite for Toggle and Toggle.Group covering rendering, sizes, pressed/controlled states, keyboard and click interactions, disabled behavior, and group selection semantics.
Docs & API props
apps/www/src/content/docs/components/toggle/props.ts, apps/www/src/content/docs/components/toggle/index.mdx
New ToggleProps and ToggleGroupProps interfaces; MDX documentation with usage, examples, API reference, and accessibility notes.
Demos & Playground
apps/www/src/content/docs/components/toggle/demo.ts, apps/www/src/components/playground/toggle-examples.tsx, apps/www/src/components/playground/index.ts
New demo definitions and a playground page showing Toggle variants, groups (single/multiple), controlled examples, sizes, and disabled states; exported toggle-examples from playground index.
Demo icons scope
apps/www/src/components/demo/demo.tsx
Added Radix icon imports and exposed FontBoldIcon, FontItalicIcon, UnderlineIcon, TextAlignLeftIcon/CenterIcon/RightIcon in demo scope for examples.
Minor import change
packages/raystack/components/icon-button/icon-button.tsx
Reordered imports from class-variance-authority (no functional change).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • paanSinghCoder
  • rsbh

Poem

🐰 A tiny toggle, bright and neat,
I hop and press with clicking feet,
Grouped or solo, big or small,
I flip, I switch — I do it all.
Docs and tests and styles in cheer, hooray!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: the addition of a Toggle component to the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-toggle
📝 Coding Plan
  • Generate coding plan for human review comments

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can customize the high-level summary generated by CodeRabbit.

Configure the reviews.high_level_summary_instructions setting to provide custom instructions for generating the high-level summary.

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 (4)
packages/raystack/components/toggle/toggle.tsx (1)

24-28: Redundant size type declaration.

The size?: 1 | 2 | 3 | 4 declaration on line 27 duplicates what VariantProps<typeof toggleVariants> already infers from the CVA definition. This redundancy could lead to maintenance issues if sizes change.

♻️ Suggested simplification
 export interface ToggleProps
   extends TogglePrimitive.Props,
-    VariantProps<typeof toggleVariants> {
-  size?: 1 | 2 | 3 | 4;
-}
+    VariantProps<typeof toggleVariants> {}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/toggle/toggle.tsx` around lines 24 - 28, Remove
the redundant explicit size declaration from the ToggleProps interface: rely on
VariantProps<typeof toggleVariants> to provide the size type instead of the
separate "size?: 1 | 2 | 3 | 4" line; edit the ToggleProps declaration (symbol:
ToggleProps) to extend TogglePrimitive.Props and VariantProps<typeof
toggleVariants> only, remove the "size" property, and run typecheck to ensure no
other code expects the removed literal type and update those callsites if
necessary.
apps/www/src/content/docs/components/toggle/demo.ts (2)

1-9: Consider adding type annotation for props parameter.

Using any for the props parameter loses type safety. A more specific type would improve maintainability.

♻️ Suggested improvement
-const getCode = (props: any) => {
+const getCode = (props: Record<string, unknown>) => {
   return `<Toggle aria-label="Bold"${getPropsString(props)}>
   <FontBoldIcon />
 </Toggle>`;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/toggle/demo.ts` around lines 1 - 9, The
getCode function currently types its props parameter as any; change it to a
specific props type (e.g., React.ComponentProps<typeof Toggle> or a dedicated
ToggleProps/Partial<ToggleProps> type imported from the Toggle component) to
restore type safety, update the function signature for getCode(props: /* new
type */) and adjust any callers or the getPropsString helper signature if
necessary so types align with getPropsString(props) and the <Toggle ...> usage.

68-82: Controlled example's callback signature differs from documentation.

The controlledDemo passes setPressed directly to onPressedChange, but per the documented ToggleProps, the callback signature is (pressed: boolean, event: Event) => void. While React's useState setter will ignore the extra event argument at runtime, this example may confuse users expecting to use the event parameter.

Consider showing the full signature for educational clarity:

📝 Suggested clarification
 export const controlledDemo = {
   type: 'code',
   code: `function ControlledToggle() {
   const [pressed, setPressed] = React.useState(false);
   return (
     <Toggle
       aria-label="Star"
       pressed={pressed}
-      onPressedChange={setPressed}
+      onPressedChange={(newPressed) => setPressed(newPressed)}
     >
       {pressed ? "★" : "☆"}
     </Toggle>
   );
 }`
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/toggle/demo.ts` around lines 68 - 82,
The controlled example passes setPressed directly to onPressedChange which hides
the documented callback signature (pressed: boolean, event: Event) — update the
ControlledToggle example (controlledDemo) to accept both parameters in the
handler: define an onPressedChange wrapper (e.g., (pressed, event) =>
setPressed(pressed)) and use that instead of passing setPressed directly so the
example matches ToggleProps and shows the event parameter; optionally add a
brief param name or type annotation to the handler to make the signature
explicit.
packages/raystack/components/toggle/__tests__/toggle.test.tsx (1)

48-84: Consider adding size={2} test for completeness.

The size variants tests cover sizes 1, 3, and 4 but skip size 2. While unlikely to fail given the pattern, adding it would ensure full coverage.

💚 Suggested addition
     it('applies size 1 class', () => {
       render(
         <Toggle aria-label='Bold' size={1}>
           B
         </Toggle>
       );
       const toggle = screen.getByRole('button', { name: 'Bold' });
       expect(toggle).toHaveClass(styles['size-1']);
     });

+    it('applies size 2 class', () => {
+      render(
+        <Toggle aria-label='Bold' size={2}>
+          B
+        </Toggle>
+      );
+      const toggle = screen.getByRole('button', { name: 'Bold' });
+      expect(toggle).toHaveClass(styles['size-2']);
+    });
+
     it('applies size 3 class', () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/toggle/__tests__/toggle.test.tsx` around lines
48 - 84, Add a missing unit test for the size=2 variant inside the "Size
Variants" describe block by mirroring the existing size tests: render <Toggle
aria-label='Bold' size={2}>B</Toggle>, query the button with
screen.getByRole('button', { name: 'Bold' }) and assert it has the
styles['size-2'] class; place the new it('applies size 2 class', ...) alongside
the other size tests so Toggle and styles['size-2'] are exercised for full
coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/www/src/components/playground/toggle-examples.tsx`:
- Around line 37-48: The four icon-only Toggle examples (Toggle components
containing FontBoldIcon) all use the same aria-label='Bold', making the size
demo indistinguishable to screen readers; update each Toggle instance in
toggle-examples.tsx to provide a unique accessible name (e.g., aria-label='Bold
size 1', 'Bold size 2', etc.) or add per-sample visible size text or
visually-hidden labels next to the FontBoldIcon so each sample is announced
distinctly by assistive technology.

In `@apps/www/src/content/docs/components/toggle/demo.ts`:
- Around line 11-29: The playground controls define size options as strings
which leads to size being emitted as a string prop; update the
playground.controls.size definition in the demo so options are numeric (e.g.,
[1, 2, 3, 4]) and set defaultValue to a number (e.g., 3) so
getCode/getPropsString will generate size={3}; keep the rest of the controls and
getCode reference unchanged.

In `@packages/raystack/components/toggle/toggle.module.css`:
- Around line 14-15: The inner .toggleContent keeps a standalone border-radius
which makes grouped toggles render as rounded pills; update the CSS so
.toggleContent has border-radius: 0 when it lives inside a grouped toggle
container (i.e., when its parent .toggle is part of a group), and re-apply
rounded corners only for the group's first and last items using selectors like
.toggleGroup .toggle:first-child .toggleContent and .toggleGroup
.toggle:last-child .toggleContent (or the existing group modifier class you
use), and apply the same reset to the other occurrences referenced (lines ~31-33
and 75-89) so hover/pressed fills render flush across the segmented surface.
- Around line 27-40: The hover/pressed styles should be limited to enabled
toggles instead of disabling pointer events; update the selectors from
.toggle:hover and .toggle[data-pressed] (and their .toggleContent counterparts)
to guard with :not([data-disabled]) (e.g., .toggle:not([data-disabled]):hover
and .toggle:not([data-disabled])[data-pressed] and likewise for .toggleContent)
and remove pointer-events: none from .toggle[data-disabled] so cursor:
not-allowed remains effective while keeping cursor and opacity.

---

Nitpick comments:
In `@apps/www/src/content/docs/components/toggle/demo.ts`:
- Around line 1-9: The getCode function currently types its props parameter as
any; change it to a specific props type (e.g., React.ComponentProps<typeof
Toggle> or a dedicated ToggleProps/Partial<ToggleProps> type imported from the
Toggle component) to restore type safety, update the function signature for
getCode(props: /* new type */) and adjust any callers or the getPropsString
helper signature if necessary so types align with getPropsString(props) and the
<Toggle ...> usage.
- Around line 68-82: The controlled example passes setPressed directly to
onPressedChange which hides the documented callback signature (pressed: boolean,
event: Event) — update the ControlledToggle example (controlledDemo) to accept
both parameters in the handler: define an onPressedChange wrapper (e.g.,
(pressed, event) => setPressed(pressed)) and use that instead of passing
setPressed directly so the example matches ToggleProps and shows the event
parameter; optionally add a brief param name or type annotation to the handler
to make the signature explicit.

In `@packages/raystack/components/toggle/__tests__/toggle.test.tsx`:
- Around line 48-84: Add a missing unit test for the size=2 variant inside the
"Size Variants" describe block by mirroring the existing size tests: render
<Toggle aria-label='Bold' size={2}>B</Toggle>, query the button with
screen.getByRole('button', { name: 'Bold' }) and assert it has the
styles['size-2'] class; place the new it('applies size 2 class', ...) alongside
the other size tests so Toggle and styles['size-2'] are exercised for full
coverage.

In `@packages/raystack/components/toggle/toggle.tsx`:
- Around line 24-28: Remove the redundant explicit size declaration from the
ToggleProps interface: rely on VariantProps<typeof toggleVariants> to provide
the size type instead of the separate "size?: 1 | 2 | 3 | 4" line; edit the
ToggleProps declaration (symbol: ToggleProps) to extend TogglePrimitive.Props
and VariantProps<typeof toggleVariants> only, remove the "size" property, and
run typecheck to ensure no other code expects the removed literal type and
update those callsites if necessary.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8a75733d-55ee-4e57-a761-c95079cb1ab0

📥 Commits

Reviewing files that changed from the base of the PR and between 14de0ba and cd6975e.

📒 Files selected for processing (12)
  • apps/www/src/components/demo/demo.tsx
  • apps/www/src/components/playground/index.ts
  • apps/www/src/components/playground/toggle-examples.tsx
  • apps/www/src/content/docs/components/toggle/demo.ts
  • apps/www/src/content/docs/components/toggle/index.mdx
  • apps/www/src/content/docs/components/toggle/props.ts
  • packages/raystack/components/icon-button/icon-button.tsx
  • packages/raystack/components/toggle/__tests__/toggle.test.tsx
  • packages/raystack/components/toggle/index.ts
  • packages/raystack/components/toggle/toggle.module.css
  • packages/raystack/components/toggle/toggle.tsx
  • packages/raystack/index.tsx

@rohanchkrabrty rohanchkrabrty enabled auto-merge (squash) March 12, 2026 06:58
@rohanchkrabrty rohanchkrabrty merged commit f1419ff into main Mar 17, 2026
1 of 2 checks passed
@rohanchkrabrty rohanchkrabrty deleted the feat-toggle branch March 17, 2026 05:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants