# Sidebar Detail Panel (`sidebar-detail-panel`) - SignalOS widget

> Master-detail right-side sheet with badges, metrics, assignee, and tabbed sections. Opens via a built-in trigger or a custom one passed in `trigger`.

- **Version:** 0.4.0
- **Kind:** widget · **Category:** data-display
- **Install:** `npx shadcn@latest add @signalos/sidebar-detail-panel`
- **Registry dependencies (pulled automatically):** @signalos/tokens, @signalos/utils, @signalos/button, @signalos/sheet, @signalos/tabs, @signalos/skeleton
- **npm dependencies:** lucide-react@^1.7.0, class-variance-authority@^0.7.1
- **Files installed:** `src/components/widgets/sidebar-detail-panel/DetailPanel.utils.ts`, `src/components/widgets/sidebar-detail-panel/DetailPanel.variants.ts`, `src/components/widgets/sidebar-detail-panel/DetailPanel.tsx`, `src/components/widgets/sidebar-detail-panel/DetailPanel.types.ts`, `src/components/widgets/sidebar-detail-panel/DetailPanel.skeleton.tsx`, `src/components/widgets/sidebar-detail-panel/DetailPanel.constants.ts`

## Access

This is a private registry: pulling source requires a `SIGNALOS_REGISTRY_TOKEN`
(GitHub fine-grained PAT with read access to the signal-widgets repo) and an
`@signalos` entry in components.json `"registries"`. Previews and this document are public.

## Usage

```tsx
import { DetailPanel } from "@/components/widgets/sidebar-detail-panel/DetailPanel"
```

## Example

```tsx
// Example: opening the panel via its built-in trigger, no parent-owned state.
import { DetailPanel } from "./DetailPanel"

export default function Example() {
  return (
    <DetailPanel
      title="Revenue anomaly detected in EU checkout funnel"
      description="Created 2 hours ago · Slot #a1b2"
      metrics={[
        {
          id: "confidence",
          label: "Confidence",
          value: "82%",
          tone: "primary",
        },
        { id: "cagg", label: "CAgg", value: "0.842", mono: true },
      ]}
      assignee={{
        role: "Analyst",
        name: "Jane Doe",
        email: "jane@example.com",
      }}
      sections={[
        { id: "signals", label: "Signals", content: <p>Signal details.</p> },
        {
          id: "timeline",
          label: "Timeline",
          content: <p>Timeline details.</p>,
        },
      ]}
    />
  )
}
```

## Props

### `DetailPanelMetric`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `id` | `string` | yes | - |  |
| `label` | `ReactNode` | yes | - |  |
| `value` | `ReactNode` | yes | - |  |
| `tone` | `"default" \| "error" \| "primary" \| undefined` | no | - |  |
| `mono` | `boolean \| undefined` | no | - |  |

### `DetailPanelSection`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `id` | `string` | yes | - |  |
| `label` | `ReactNode` | yes | - |  |
| `icon` | `ReactNode` | no | - |  |
| `content` | `ReactNode` | yes | - |  |

### `DetailPanelAssignee`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `name` | `ReactNode` | no | - |  |
| `email` | `ReactNode` | no | - |  |
| `role` | `ReactNode` | no | - |  |
| `icon` | `ReactNode` | no | - |  |

### `DetailPanelProps`

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `open` | `boolean \| undefined` | no | - |  |
| `onOpenChange` | `((open: boolean) => void) \| undefined` | no | - |  |
| `trigger` | `ReactNode` | no | - |  |
| `badges` | `readonly ReactNode[] \| undefined` | no | - |  |
| `title` | `ReactNode` | yes | - |  |
| `description` | `ReactNode` | no | - |  |
| `progress` | `ReactNode` | no | - |  |
| `metrics` | `readonly DetailPanelMetric[] \| undefined` | no | - |  |
| `assignee` | `DetailPanelAssignee \| undefined` | no | - |  |
| `assigneeEmptyLabel` | `ReactNode` | no | - |  |
| `sections` | `readonly DetailPanelSection[] \| undefined` | no | - |  |
| `defaultSectionId` | `string \| undefined` | no | - |  |
| `errorMessage` | `ReactNode` | no | - |  |
| `children` | `ReactNode` | no | - |  |
| `widthClassName` | `string \| undefined` | no | - |  |
| `className` | `string \| undefined` | no | - |  |
| `data-testid` | `string \| undefined` | no | - |  |

## Changelog

# sidebar-detail-panel

## 0.4.0

- A controlled panel (one given `open`) no longer renders the built-in
  "Details" trigger button. It drives its own visibility, so the default
  trigger was a stray, unwanted button on the page — consumers were commenting
  the `SheetTrigger` out downstream to get rid of it. Uncontrolled panels are
  unchanged, and an explicitly-passed `trigger` still renders in both modes.

## 0.3.1

- Content sync fix to bring source in line with published registry payload.


## 0.3.0

- Fixed tabbed sections: wired up `Tabs`/`TabsList`/`TabsTrigger`/`TabsContent` from `@signalos/tabs`, previously commented out and undefined.

## 0.2.0

- `open`/`onOpenChange` are now optional — the panel manages its own visibility when omitted.
- Added `trigger` prop: pass any element to open the panel; defaults to a built-in icon button.

## 0.1.1

- Prior release.
