Skip to content

feat (sidebar): add accordion and trailingIcon support#721

Open
paanSinghCoder wants to merge 3 commits intofeat/sidebar-design-improvementsfrom
feat/sidebar-accordion
Open

feat (sidebar): add accordion and trailingIcon support#721
paanSinghCoder wants to merge 3 commits intofeat/sidebar-design-improvementsfrom
feat/sidebar-accordion

Conversation

@paanSinghCoder
Copy link
Copy Markdown
Contributor

@paanSinghCoder paanSinghCoder commented Mar 27, 2026

Description

feat: add accordion support for the section
feat: add trailingIcon support

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactor (no functional changes, no bug fixes just code improvements)
  • Chore (changes to the build process or auxiliary tools and libraries such as documentation generation)
  • Style (changes that do not affect the meaning of the code (white-space, formatting, etc))
  • Test (adding missing tests or correcting existing tests)
  • Improvement (Improvements to existing code)
  • Other (please specify)

How Has This Been Tested?

[Describe the tests that you ran to verify your changes]

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (.mdx files)
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works

Screenshots (if appropriate):

[Add screenshots here]

Related Issues

[Link any related issues here using #issue-number]

Summary by CodeRabbit

  • New Features

    • Added Sidebar.More component for nested, collapsible menu items
    • Introduced sidebar variant styling options (plain, floating, inset)
    • Navigation groups now support accordion-style expand/collapse behavior
  • Documentation

    • Updated sidebar component documentation with new features, variants, and usage examples

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 27, 2026

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

Project Deployment Actions Updated (UTC)
apsara Ready Ready Preview, Comment Mar 27, 2026 11:00am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 27, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0909f569-488b-4865-93ba-ee0488d636db

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces Sidebar.More subcomponent for collapsible menu items and variant prop for Sidebar styling variants (plain/floating/inset). Adds accordion support to navigation groups with trailing action buttons. Refactors leading visual rendering into dedicated component. Updates examples page with Radix UI icons and enhanced sidebar structure with nested menus and handlers.

Changes

Cohort / File(s) Summary
Sidebar Core Implementation
packages/raystack/components/sidebar/sidebar.tsx, packages/raystack/components/sidebar/sidebar-root.tsx, packages/raystack/components/sidebar/sidebar-misc.tsx
Extended Sidebar with variant prop for styling variants. Updated context to include position field. Added accordion and trailing icon support to SidebarNavigationGroup. Wired SidebarMore to component API.
Sidebar More Component
packages/raystack/components/sidebar/sidebar-more.tsx
New component for collapsible menu item groups, consuming SidebarContext to render trigger and Menu-based dropdown with cloned child items, tooltip support when collapsed, and accessibility attributes.
Sidebar Leading Visual
packages/raystack/components/sidebar/sidebar-leading-visual.tsx
New component extracted to handle leading icon/fallback avatar rendering for sidebar items, replacing inline logic from SidebarItem.
Sidebar Item Refactor
packages/raystack/components/sidebar/sidebar-item.tsx
Delegated leading visual rendering to new SidebarLeadingVisual component, removing inline icon/avatar logic.
Sidebar Styling
packages/raystack/components/sidebar/sidebar.module.css
Added styles for variant data attributes, "More" trigger/menu items with state-based highlighting, accordion trigger with chevron rotation, and collapsible panel animations.
Sidebar Tests
packages/raystack/components/sidebar/__tests__/sidebar.test.tsx
Added tests for variant support, accordion behavior (toggle/state persistence), trailing icons, and Sidebar.More rendering and accessibility.
Documentation Updates
apps/www/src/content/docs/components/sidebar/index.mdx, apps/www/src/content/docs/components/sidebar/demo.ts, apps/www/src/content/docs/components/sidebar/props.ts
Documented Sidebar.More component, variant options, and corresponding demos. Extended SidebarRootProps and added SidebarMoreProps interface. Added variantDemo and moreDemo showcasing new features.
Examples Page
apps/www/src/app/examples/page.tsx
Replaced Apsara icon imports with Radix UI icons. Updated sidebar usage with variant='plain' and enhanced structure including accordion behavior, nested "More" sections, group-level trailing action buttons, and item-level handlers.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • rohilsurana
  • rsbh
  • rohanchkrabrty

Poem

🐰 The sidebar hops with fancy flair,
A "More" menu here, variants there!
With accordion charm and leading grace,
Our components sparkle in their new place! ✨

🚥 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 captures the main features added: accordion support for sidebar groups and trailingIcon support, which are the primary changes throughout the changeset.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/sidebar-accordion

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.

@paanSinghCoder paanSinghCoder self-assigned this Mar 27, 2026
@paanSinghCoder paanSinghCoder changed the title Feat/sidebar accordion feat (sidebar): add accordion and trailingIcon support Mar 27, 2026
@paanSinghCoder paanSinghCoder marked this pull request as draft March 27, 2026 08:14
Copy link
Copy Markdown
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 (2)
packages/raystack/components/sidebar/sidebar-more.tsx (2)

42-44: Type assertion may accept non-SidebarItem elements.

The filter only checks isValidElement, then casts all results to ReactElement<SidebarItemProps>[]. If a consumer passes other React elements (e.g., <div>), they'll pass the filter but won't have the expected props like as, active, or leadingIcon, potentially causing runtime issues when destructuring at line 91-99.

Consider adding a more specific filter or documenting that only Sidebar.Item children are supported.

♻️ Suggested approach to filter by displayName
+ import { SidebarItem } from './sidebar-item';
+
  const items = Children.toArray(children).filter(
-   isValidElement
- ) as ReactElement<SidebarItemProps>[];
+   (child): child is ReactElement<SidebarItemProps> =>
+     isValidElement(child) &&
+     (child.type as any)?.displayName === SidebarItem.displayName
+ );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/sidebar/sidebar-more.tsx` around lines 42 - 44,
The current creation of items using
Children.toArray(children).filter(isValidElement) and casting to
ReactElement<SidebarItemProps>[] can accept arbitrary valid React elements
(e.g., <div>) and cause runtime errors when you later destructure expected
props; change the filter to explicitly only accept Sidebar.Item elements by
checking the child's type/displayName (e.g., verify (child.type as
any).displayName === 'SidebarItem' or child.type === Sidebar.Item) or implement
a TypeScript runtime type guard like isSidebarItemElement that narrows to
ReactElement<SidebarItemProps>, and use that instead of the blanket cast on the
result from Children.toArray(children) so destructuring of SidebarItemProps is
safe.

136-142: Using array index as fallback key may cause issues.

When item.key is null, the fallback to index can lead to incorrect DOM reconciliation if items are reordered or removed. Consider using a more stable identifier from item props (e.g., href, children text content) if keys aren't provided.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/sidebar/sidebar-more.tsx` around lines 136 -
142, The Menu.Item rendering uses item.key ?? index which can break
reconciliation; update the key resolution in sidebar-more.tsx to use a stable
identifier instead of the array index — e.g., derive a key by using item.key ??
item.href ?? String(item.children) (or item.id if available) and pass that into
<Menu.Item key={...} ... />; ensure the chosen symbol (item.href / item.children
/ item.id) is present or coerce to string so keys are stable across reorders.
🤖 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/app/examples/page.tsx`:
- Around line 180-187: The Sidebar.More trigger is icon-only here (Sidebar.More
with leadingIcon={<DotsHorizontalIcon />}), so screen readers get no name;
update the usage to provide an accessible name by passing a visible label prop
or an explicit aria-label/accessibleName prop to Sidebar.More (or add a
dedicated accessible-name prop to the Sidebar.More component if you prefer
component change), and ensure the component uses that value for the collapsed
trigger aria-label (the code path around label/aria-label in Sidebar.More should
read the new prop when leadingIcon is present).

In `@apps/www/src/content/docs/components/sidebar/props.ts`:
- Around line 92-115: The Sidebar API docs are missing new group props—update
the SidebarGroupProps interface to document the same new API surface as
SidebarMoreProps: add the variant prop (e.g., variant?: string | enum) and the
Sidebar.Group-specific props accordion?: boolean and trailingIcon?: ReactNode so
the docs/API table covers the props used in tests and examples; ensure the names
match existing symbols (SidebarGroupProps, variant, accordion, trailingIcon,
Sidebar.More) and add brief JSDoc comments for each to match the style used in
SidebarMoreProps.

In `@packages/raystack/components/sidebar/sidebar-misc.tsx`:
- Around line 125-129: The Accordion is remounting because of key={isCollapsed ?
'collapsed' : 'expanded'} and uses only defaultValue (groupValue), so
expand/collapse state resets on sidebar toggle; remove the conditional key and
convert AccordionPrimitive.Root to a controlled component by adding a value
state (e.g., accordionValue) and an onValueChange handler (e.g.,
setAccordionValue) wired to AccordionPrimitive.Root's value and onValueChange
props, initializing accordionValue from groupValue if needed so panel state
persists across isCollapsed toggles.

In `@packages/raystack/components/sidebar/sidebar-root.tsx`:
- Around line 61-63: The Tooltip for collapsed sidebar items is hardcoded to
side='right' in SidebarItem (look for Tooltip.Content with side='right' in
packages/raystack/components/sidebar/sidebar-item.tsx), so update that to read
the current position from SidebarContext instead: consume position from
SidebarContext (the same context provided by SidebarContext in sidebar-root.tsx)
and pass it to Tooltip.Content (e.g., side={position} or map position to
Tooltip's side prop) so collapsed item tooltips follow the sidebar position;
ensure fallback to 'right' or an appropriate default if position is undefined.

---

Nitpick comments:
In `@packages/raystack/components/sidebar/sidebar-more.tsx`:
- Around line 42-44: The current creation of items using
Children.toArray(children).filter(isValidElement) and casting to
ReactElement<SidebarItemProps>[] can accept arbitrary valid React elements
(e.g., <div>) and cause runtime errors when you later destructure expected
props; change the filter to explicitly only accept Sidebar.Item elements by
checking the child's type/displayName (e.g., verify (child.type as
any).displayName === 'SidebarItem' or child.type === Sidebar.Item) or implement
a TypeScript runtime type guard like isSidebarItemElement that narrows to
ReactElement<SidebarItemProps>, and use that instead of the blanket cast on the
result from Children.toArray(children) so destructuring of SidebarItemProps is
safe.
- Around line 136-142: The Menu.Item rendering uses item.key ?? index which can
break reconciliation; update the key resolution in sidebar-more.tsx to use a
stable identifier instead of the array index — e.g., derive a key by using
item.key ?? item.href ?? String(item.children) (or item.id if available) and
pass that into <Menu.Item key={...} ... />; ensure the chosen symbol (item.href
/ item.children / item.id) is present or coerce to string so keys are stable
across reorders.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 56ff7da0-75fe-4315-b812-d406cb55393b

📥 Commits

Reviewing files that changed from the base of the PR and between eda5a9e and c68a182.

📒 Files selected for processing (12)
  • apps/www/src/app/examples/page.tsx
  • apps/www/src/content/docs/components/sidebar/demo.ts
  • apps/www/src/content/docs/components/sidebar/index.mdx
  • apps/www/src/content/docs/components/sidebar/props.ts
  • packages/raystack/components/sidebar/__tests__/sidebar.test.tsx
  • packages/raystack/components/sidebar/sidebar-item.tsx
  • packages/raystack/components/sidebar/sidebar-leading-visual.tsx
  • packages/raystack/components/sidebar/sidebar-misc.tsx
  • packages/raystack/components/sidebar/sidebar-more.tsx
  • packages/raystack/components/sidebar/sidebar-root.tsx
  • packages/raystack/components/sidebar/sidebar.module.css
  • packages/raystack/components/sidebar/sidebar.tsx

Comment on lines +180 to +187
<Sidebar.More leadingIcon={<DotsHorizontalIcon />}>
<Sidebar.Item href='#' leadingIcon={<RadixBellIcon />}>
Notifications
</Sidebar.Item>
<Sidebar.Item href='#' leadingIcon={<PersonIcon />} disabled>
Billing
</Sidebar.Item>
</Sidebar.More>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Give this icon-only Sidebar.More trigger an accessible name.

In packages/raystack/components/sidebar/sidebar-more.tsx (Lines 51-67), label is used as the visible trigger text and as the collapsed aria-label. With only leadingIcon here, this control is unnamed for screen readers. Pass a label, or add a dedicated accessible-name prop before showcasing icon-only usage.

Safe immediate fix
-              <Sidebar.More leadingIcon={<DotsHorizontalIcon />}>
+              <Sidebar.More
+                label='More account actions'
+                leadingIcon={<DotsHorizontalIcon />}
+              >
📝 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
<Sidebar.More leadingIcon={<DotsHorizontalIcon />}>
<Sidebar.Item href='#' leadingIcon={<RadixBellIcon />}>
Notifications
</Sidebar.Item>
<Sidebar.Item href='#' leadingIcon={<PersonIcon />} disabled>
Billing
</Sidebar.Item>
</Sidebar.More>
<Sidebar.More
label='More account actions'
leadingIcon={<DotsHorizontalIcon />}
>
<Sidebar.Item href='#' leadingIcon={<RadixBellIcon />}>
Notifications
</Sidebar.Item>
<Sidebar.Item href='#' leadingIcon={<PersonIcon />} disabled>
Billing
</Sidebar.Item>
</Sidebar.More>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/app/examples/page.tsx` around lines 180 - 187, The Sidebar.More
trigger is icon-only here (Sidebar.More with leadingIcon={<DotsHorizontalIcon
/>}), so screen readers get no name; update the usage to provide an accessible
name by passing a visible label prop or an explicit aria-label/accessibleName
prop to Sidebar.More (or add a dedicated accessible-name prop to the
Sidebar.More component if you prefer component change), and ensure the component
uses that value for the collapsed trigger aria-label (the code path around
label/aria-label in Sidebar.More should read the new prop when leadingIcon is
present).

Comment on lines +92 to +115
export interface SidebarMoreProps {
/** String for the more trigger label. */
label?: string;

/** Optional ReactNode for the trigger icon. */
leadingIcon?: ReactNode;

/** Sidebar items rendered inside the menu content. */
children?: ReactNode;

/** Optional class names for customizing parts of the more trigger/menu. */
classNames?: {
/** Class name for the trigger root element. */
root?: string;
/** Class name for the leading icon container. */
leadingIcon?: string;
/** Class name for the text element. */
text?: string;
/** Class name for menu item root elements. */
menuItem?: string;
/** Class name for menu content container. */
menuContent?: string;
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Document the new Sidebar.Group props in the same API table.

This update adds variant and Sidebar.More, but SidebarGroupProps still doesn't mention accordion or trailingIcon, even though both are already exercised in packages/raystack/components/sidebar/__tests__/sidebar.test.tsx (Lines 289-435) and apps/www/src/app/examples/page.tsx (Lines 114-188). The docs will ship without the main group's new API surface.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/sidebar/props.ts` around lines 92 - 115,
The Sidebar API docs are missing new group props—update the SidebarGroupProps
interface to document the same new API surface as SidebarMoreProps: add the
variant prop (e.g., variant?: string | enum) and the Sidebar.Group-specific
props accordion?: boolean and trailingIcon?: ReactNode so the docs/API table
covers the props used in tests and examples; ensure the names match existing
symbols (SidebarGroupProps, variant, accordion, trailingIcon, Sidebar.More) and
add brief JSDoc comments for each to match the style used in SidebarMoreProps.

Comment on lines +61 to +63
<SidebarContext
value={{ isCollapsed: !open, position, hideCollapsedItemTooltip }}
>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Apply position to collapsed item tooltips as well.

position is now in SidebarContext, but packages/raystack/components/sidebar/sidebar-item.tsx (Lines 73-77) still hardcodes Tooltip.Content to side='right'. Right-positioned sidebars will keep showing collapsed item tooltips on the wrong edge.

Suggested follow-up
-  const { isCollapsed, hideCollapsedItemTooltip } = useContext(SidebarContext);
+  const { isCollapsed, hideCollapsedItemTooltip, position } =
+    useContext(SidebarContext);
...
-        <Tooltip.Content side='right'>{children}</Tooltip.Content>
+        <Tooltip.Content side={position === 'left' ? 'right' : 'left'}>
+          {children}
+        </Tooltip.Content>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/raystack/components/sidebar/sidebar-root.tsx` around lines 61 - 63,
The Tooltip for collapsed sidebar items is hardcoded to side='right' in
SidebarItem (look for Tooltip.Content with side='right' in
packages/raystack/components/sidebar/sidebar-item.tsx), so update that to read
the current position from SidebarContext instead: consume position from
SidebarContext (the same context provided by SidebarContext in sidebar-root.tsx)
and pass it to Tooltip.Content (e.g., side={position} or map position to
Tooltip's side prop) so collapsed item tooltips follow the sidebar position;
ensure fallback to 'right' or an appropriate default if position is undefined.

@paanSinghCoder paanSinghCoder changed the base branch from main to feat/sidebar-design-improvements March 27, 2026 09:27
@paanSinghCoder paanSinghCoder marked this pull request as ready for review March 27, 2026 09:28
@paanSinghCoder paanSinghCoder force-pushed the feat/sidebar-accordion branch from c0c7d22 to c68a182 Compare March 27, 2026 10:44
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.

1 participant