feat: add maxHeight prop with sectionContent wrapper to SidebarSection

Adds position and maxHeight to SidebarSectionProps and wraps children in a .sectionContent div when the section is open, enabling scrollable constrained height via inline style.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-15 20:57:43 +02:00
parent 7e2fce8b14
commit 57d60bf2ed
2 changed files with 81 additions and 1 deletions

View File

@@ -324,4 +324,73 @@ describe('Sidebar compound component', () => {
const item = screen.getByText('Admin').closest('[role="button"]')! const item = screen.getByText('Admin').closest('[role="button"]')!
expect(item.className).toMatch(/bottomItemActive/) expect(item.className).toMatch(/bottomItemActive/)
}) })
// 17. renders sectionContent wrapper with maxHeight when open
it('renders section content wrapper with maxHeight style when open', () => {
const { container } = render(
<Wrapper>
<Sidebar>
<Sidebar.Section
icon={<span>ic</span>}
label="Apps"
open
onToggle={vi.fn()}
maxHeight="200px"
>
<div>child</div>
</Sidebar.Section>
</Sidebar>
</Wrapper>,
)
const contentWrapper = container.querySelector('.sectionContent')
expect(contentWrapper).toBeInTheDocument()
expect(contentWrapper).toHaveStyle({ maxHeight: '200px' })
expect(screen.getByText('child')).toBeInTheDocument()
})
// 18. renders sectionContent wrapper without maxHeight when not provided
it('renders section content wrapper without inline maxHeight when maxHeight is not provided', () => {
const { container } = render(
<Wrapper>
<Sidebar>
<Sidebar.Section
icon={<span>ic</span>}
label="Apps"
open
onToggle={vi.fn()}
>
<div>child</div>
</Sidebar.Section>
</Sidebar>
</Wrapper>,
)
const contentWrapper = container.querySelector('.sectionContent')
expect(contentWrapper).toBeInTheDocument()
expect(contentWrapper).not.toHaveStyle({ maxHeight: '200px' })
expect(screen.getByText('child')).toBeInTheDocument()
})
// 19. does not render sectionContent wrapper when section is closed
it('does not render section content wrapper when section is closed', () => {
const { container } = render(
<Wrapper>
<Sidebar>
<Sidebar.Section
icon={<span>ic</span>}
label="Apps"
open={false}
onToggle={vi.fn()}
maxHeight="200px"
>
<div>child</div>
</Sidebar.Section>
</Sidebar>
</Wrapper>,
)
const contentWrapper = container.querySelector('.sectionContent')
expect(contentWrapper).not.toBeInTheDocument()
})
}) })

View File

@@ -26,6 +26,8 @@ interface SidebarSectionProps {
active?: boolean active?: boolean
children: ReactNode children: ReactNode
className?: string className?: string
position?: 'top' | 'bottom'
maxHeight?: string
} }
interface SidebarFooterProps { interface SidebarFooterProps {
@@ -83,6 +85,8 @@ function SidebarSection({
active, active,
children, children,
className, className,
position: _position,
maxHeight,
}: SidebarSectionProps) { }: SidebarSectionProps) {
const { collapsed, onCollapseToggle } = useSidebarContext() const { collapsed, onCollapseToggle } = useSidebarContext()
@@ -125,7 +129,14 @@ function SidebarSection({
{icon && <span className={styles.sectionIcon}>{icon}</span>} {icon && <span className={styles.sectionIcon}>{icon}</span>}
<span className={styles.treeSectionLabel}>{label}</span> <span className={styles.treeSectionLabel}>{label}</span>
</div> </div>
{open && children} {open && (
<div
className={styles.sectionContent}
style={maxHeight ? { maxHeight } : undefined}
>
{children}
</div>
)}
</div> </div>
) )
} }