Compare commits

...

2 Commits

Author SHA1 Message Date
hsiegeln
d775df61e4 feat: remove expand toggle from collapsed sidebar, bump v0.1.54
All checks were successful
Build & Publish / publish (push) Successful in 1m49s
SonarQube Analysis / sonarqube (push) Successful in 2m36s
Collapsed sidebar no longer shows an expand button — clicking any
section icon expands the sidebar and opens that section instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:05:10 +02:00
hsiegeln
7c6d383ac9 docs: update CLAUDE.md and COMPONENT_GUIDE.md for chart wrappers and sidebar section props
All checks were successful
Build & Publish / publish (push) Successful in 1m2s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 21:55:22 +02:00
6 changed files with 60 additions and 24 deletions

View File

@@ -37,8 +37,8 @@ Always read `COMPONENT_GUIDE.md` before building any UI feature. It contains dec
### Import Paths
```tsx
import { Button, Input } from '../design-system/primitives'
import { Modal, DataTable, KpiStrip, SplitPane, EntityList, LogViewer } from '../design-system/composites'
import type { Column, KpiItem, LogEntry } from '../design-system/composites'
import { Modal, DataTable, KpiStrip, SplitPane, EntityList, LogViewer, LineChart, AreaChart, BarChart } from '../design-system/composites'
import type { Column, KpiItem, LogEntry, ChartSeries } from '../design-system/composites'
import { AppShell } from '../design-system/layout/AppShell'
import { Sidebar } from '../design-system/layout/Sidebar/Sidebar'
import { SidebarTree } from '../design-system/layout/Sidebar/SidebarTree'
@@ -95,7 +95,7 @@ import { Button, AppShell, ThemeProvider } from '@cameleer/design-system'
```tsx
// All components from single entry
import { Button, Input, Modal, DataTable, KpiStrip, SplitPane, EntityList, LogViewer, StatusText, AppShell } from '@cameleer/design-system'
import { Button, Input, Modal, DataTable, KpiStrip, SplitPane, EntityList, LogViewer, StatusText, AppShell, LineChart, AreaChart, BarChart } from '@cameleer/design-system'
// Sidebar (compound component — compose your own navigation)
import { Sidebar, SidebarTree, useStarred } from '@cameleer/design-system'
@@ -132,7 +132,7 @@ import camelSvg from '@cameleer/design-system/assets/camel-logo.svg' // simp
<!-- gitnexus:start -->
# GitNexus — Code Intelligence
This project is indexed by GitNexus as **design-system** (1479 symbols, 2371 relationships, 24 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
This project is indexed by GitNexus as **design-system** (1536 symbols, 2408 relationships, 23 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.

View File

@@ -54,8 +54,10 @@
### "I need to display data"
- Key metrics → **StatCard** (with optional sparkline/trend)
- Tabular data → **DataTable** (sortable, paginated)
- Time series **ThemedChart** with `<Line>` or `<Area>`
- Categorical comparison **ThemedChart** with `<Bar>`
- Time series (quick) → **LineChart** or **AreaChart** (convenience wrappers with series data)
- Categorical comparison (quick) → **BarChart** (convenience wrapper with series data)
- Time series (custom) → **ThemedChart** with `<Line>` or `<Area>`
- Categorical comparison (custom) → **ThemedChart** with `<Bar>`
- Inline trend → **Sparkline**
- Advanced charts (treemap, radar, heatmap, pie, etc.) → **Recharts** with `rechartsTheme` (see [Charting Strategy](#charting-strategy))
- Event log → **EventFeed**
@@ -109,6 +111,9 @@ Sidebar compound API:
<Sidebar.Section label="str" icon={node} open={bool} onToggle={fn} active={bool}>
<SidebarTree nodes={[...]} selectedPath="..." filterQuery="..." ... />
</Sidebar.Section>
<Sidebar.Section label="str" icon={node} open={bool} onToggle={fn} position="bottom" maxHeight="200px">
<SidebarTree nodes={[...]} ... />
</Sidebar.Section>
<Sidebar.Footer>
<Sidebar.FooterLink icon={node} label="str" onClick={fn} active={bool} />
</Sidebar.Footer>
@@ -119,6 +124,11 @@ Notes:
- Section headers have no chevron — the entire row is clickable to toggle
- The app controls all content — sections, order, tree data, collapse state
- Sidebar provides the frame, search input, and icon-rail collapse mode
- `position="bottom"` stacks sections above the footer; a spacer separates top/bottom groups
- `maxHeight` (CSS string) constrains the content area — section header stays visible, children scroll
- Both groups scroll independently when the viewport is short
- Custom thin scrollbars match the dark sidebar aesthetic
- No expand button when collapsed — clicking any section icon expands the sidebar and opens that section
```
### Data page pattern
@@ -182,9 +192,38 @@ URL-driven progressive filtering: /agents → /agents/:appId → /agents/:appId/
## Charting Strategy
The design system provides a **ThemedChart** wrapper component that applies consistent styling to Recharts charts. Recharts is bundled as a dependency — consumers do not need to install it separately.
The design system provides convenience chart wrappers (**LineChart**, **AreaChart**, **BarChart**) for common use cases, plus a lower-level **ThemedChart** wrapper for full Recharts control. Recharts is bundled as a dependency — consumers do not need to install it separately.
### Usage
### Quick Charts (convenience wrappers)
```tsx
import { LineChart, AreaChart, BarChart } from '@cameleer/design-system'
import type { ChartSeries } from '@cameleer/design-system'
const series: ChartSeries[] = [
{ label: 'CPU', data: [{ x: '10:00', y: 45 }, { x: '10:05', y: 62 }] },
{ label: 'Memory', data: [{ x: '10:00', y: 70 }, { x: '10:05', y: 72 }] },
]
<LineChart series={series} height={200} yLabel="%" />
<AreaChart series={series} height={200} yLabel="%" thresholdValue={85} thresholdLabel="Alert" />
<BarChart series={series} height={200} stacked />
```
| Prop | LineChart | AreaChart | BarChart | Description |
|------|:---------:|:---------:|:--------:|-------------|
| `series` | required | required | required | `ChartSeries[]``{ label, data: { x, y }[], color? }` |
| `height` | optional | optional | optional | Chart height in pixels |
| `width` | optional | optional | optional | Container width in pixels |
| `yLabel` | optional | optional | optional | Y-axis label |
| `xLabel` | optional | optional | optional | X-axis label |
| `className` | optional | optional | optional | Container CSS class |
| `threshold` | `{ value, label }` | — | — | Horizontal reference line |
| `thresholdValue` | — | optional | — | Threshold y-value |
| `thresholdLabel` | — | optional | — | Threshold label |
| `stacked` | — | — | optional | Stack bars instead of grouping |
### Custom Charts (ThemedChart)
```tsx
import { ThemedChart, Line, Area, ReferenceLine, CHART_COLORS } from '@cameleer/design-system'
@@ -231,6 +270,8 @@ For chart types not covered (treemap, radar, pie, sankey), import from `recharts
| Accordion | composite | Multiple collapsible sections, single or multi-open mode |
| Alert | primitive | Page-level attention banner with variant colors |
| AlertDialog | composite | Confirmation dialog for destructive/important actions |
| AreaChart | composite | Convenience area chart wrapper — pass `series` data, get themed chart with fills |
| BarChart | composite | Convenience bar chart wrapper — grouped or `stacked` mode |
| Avatar | primitive | User representation with initials and color |
| AvatarGroup | composite | Stacked overlapping avatars with overflow count |
| Badge | primitive | Labeled status indicator with semantic colors |
@@ -261,7 +302,7 @@ For chart types not covered (treemap, radar, pie, sankey), import from `recharts
| KeyboardHint | primitive | Keyboard shortcut display |
| KpiStrip | composite | Horizontal row of KPI cards with colored left border, trend, subtitle, optional sparkline |
| Label | primitive | Form label with optional required asterisk |
| ThemedChart | composite | Recharts wrapper with themed axes, grid, and tooltip |
| LineChart | composite | Convenience line chart wrapper — pass `series` data, get themed chart with lines |
| LogViewer | composite | Scrollable log output with timestamped, severity-colored monospace entries |
| MenuItem | composite | Sidebar navigation item with health/count |
| Modal | composite | Generic dialog overlay with backdrop |
@@ -298,7 +339,7 @@ For chart types not covered (treemap, radar, pie, sankey), import from `recharts
| Component | Purpose |
|-----------|---------|
| AppShell | Page shell: sidebar + topbar + main + optional detail panel |
| Sidebar | Composable compound sidebar shell with icon-rail collapse mode. Sub-components: `Sidebar.Header`, `Sidebar.Section`, `Sidebar.Footer`, `Sidebar.FooterLink`. The app controls all content via children — the DS provides the frame. |
| Sidebar | Composable compound sidebar shell with icon-rail collapse mode. Sub-components: `Sidebar.Header`, `Sidebar.Section` (supports `position="bottom"` and `maxHeight`), `Sidebar.Footer`, `Sidebar.FooterLink`. The app controls all content via children — the DS provides the frame. |
| SidebarTree | Data-driven tree for sidebar sections. Accepts `nodes: SidebarTreeNode[]` with expand/collapse, starring, keyboard nav, search filter, and path-based selection highlighting. |
| useStarred | Hook for localStorage-backed starred item IDs. Returns `{ starredIds, isStarred, toggleStar }`. |
| TopBar | Header bar with breadcrumb, search trigger, ButtonGroup status filters, time range selector, theme toggle, environment slot (`ReactNode` — pass a string for a static label or a custom dropdown for interactive selection), user avatar |

View File

@@ -1,6 +1,6 @@
{
"name": "@cameleer/design-system",
"version": "0.1.53",
"version": "0.1.54",
"type": "module",
"main": "./dist/index.es.js",
"module": "./dist/index.es.js",

View File

@@ -53,11 +53,6 @@
justify-content: center;
}
.sidebarCollapsed .collapseToggle {
top: 52px;
right: 50%;
transform: translateX(50%);
}
.logoImg {
width: 28px;

View File

@@ -132,8 +132,8 @@ describe('Sidebar compound component', () => {
expect(onCollapseToggle).toHaveBeenCalledTimes(1)
})
// 7. renders expand toggle label when collapsed
it('renders expand toggle when sidebar is collapsed', () => {
// 7. hides collapse toggle when sidebar is collapsed
it('hides collapse toggle when sidebar is collapsed', () => {
render(
<Wrapper>
<Sidebar collapsed onCollapseToggle={vi.fn()}>
@@ -141,7 +141,8 @@ describe('Sidebar compound component', () => {
</Sidebar>
</Wrapper>,
)
expect(screen.getByRole('button', { name: /expand sidebar/i })).toBeInTheDocument()
expect(screen.queryByRole('button', { name: /collapse sidebar/i })).not.toBeInTheDocument()
expect(screen.queryByRole('button', { name: /expand sidebar/i })).not.toBeInTheDocument()
})
// 8. renders search input and calls onSearchChange

View File

@@ -3,7 +3,6 @@ import {
Search,
X,
ChevronsLeft,
ChevronsRight,
} from 'lucide-react'
import styles from './Sidebar.module.css'
import { SidebarContext, useSidebarContext } from './SidebarContext'
@@ -194,14 +193,14 @@ function SidebarRoot({
className ?? '',
].filter(Boolean).join(' ')}
>
{/* Collapse toggle */}
{onCollapseToggle && (
{/* Collapse toggle (hidden when collapsed — sections expand on click) */}
{onCollapseToggle && !collapsed && (
<button
className={styles.collapseToggle}
onClick={onCollapseToggle}
aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
aria-label="Collapse sidebar"
>
{collapsed ? <ChevronsRight size={14} /> : <ChevronsLeft size={14} />}
<ChevronsLeft size={14} />
</button>
)}