Panel
Right-side detail drawer used for master-detail workflows, editors, and deep object inspection.
Overview
Panel is the shared-ui right-side drawer primitive used throughout the brand dashboard for detail views, editors, and workflow configuration.
It is a compound component exported from @fsai/shared-ui and built on top of Headless UI Dialog plus shared layout primitives. In practice it supports two main patterns:
- A global stack-managed panel shell, where the app opens one
Paneland individual screens return onlyPanel.Content - A feature-owned panel, where the feature renders the full
Paneltree itself
For most detail experiences in the dashboard, Panel is the standard container.
Basic Usage
Use the full Panel tree when the feature owns its own open/close state:
import { Panel } from '@fsai/shared-ui';
<Panel isOpen={isOpen} handleClose={() => setIsOpen(false)}>
<Panel.Backdrop />
<Panel.Popup size="lg">
<Panel.Content>
<Panel.Topbar>
<Panel.Topbar.Left>
<Panel.Close />
</Panel.Topbar.Left>
</Panel.Topbar>
<Panel.Header>
<Panel.Header.Left>
<Panel.Header.LeftContent>
<Panel.Header.Title>Edit Workflow Node</Panel.Header.Title>
<Panel.Header.Description>
Configure how this step behaves.
</Panel.Header.Description>
</Panel.Header.LeftContent>
</Panel.Header.Left>
</Panel.Header>
<Panel.Body>
<Panel.Section>{/* panel content */}</Panel.Section>
</Panel.Body>
</Panel.Content>
</Panel.Popup>
</Panel>Stack-Managed Usage
In the brand dashboard, many entity panels are rendered inside a shared global shell owned by PanelStack. In that case, the panel content component should return Panel.Content and related subcomponents only.
import { Panel, Breadcrumb } from '@fsai/shared-ui';
function LeadPanel() {
return (
<Panel.Content>
<Panel.Topbar>
<Panel.Topbar.Left>
<Panel.Close />
<Breadcrumb items={items} />
</Panel.Topbar.Left>
</Panel.Topbar>
<Panel.Header>
<Panel.Header.Left>
<Panel.Header.Avatar name={lead.name} icon="User" />
<Panel.Header.LeftContent>
<Panel.Header.Title>{lead.name}</Panel.Header.Title>
<Panel.Header.Description leftAdornment="PinLocation">
{lead.city}
</Panel.Header.Description>
</Panel.Header.LeftContent>
</Panel.Header.Left>
</Panel.Header>
<Panel.Body>
<Panel.Section>{/* tabs, details, activity */}</Panel.Section>
</Panel.Body>
</Panel.Content>
);
}Do not nest a second Panel inside PanelStack.
Sizes And Behavior
Panel.Popup size | Desktop target width | Typical usage |
|---|---|---|
md | 672px | Focused forms, node configuration, compact editors |
lg | 896px | Standard entity detail panels |
xl | 1152px | Richer detail panels with more horizontal layout |
full | viewport-driven | Large workflows, project views, immersive editing |
Behavior:
- Desktop panels slide in from the right and are resizable from the left edge.
- Mobile panels switch to a full-viewport presentation.
fullis not literally the whole desktop viewport; it uses the available viewport width with layout offsets.Panel.Topbar stickykeeps the top bar pinned while the body scrolls.
Compound API
Root
| Part | Purpose |
|---|---|
Panel | Root dialog container. Requires isOpen and handleClose. |
Panel.Backdrop | Shared backdrop overlay. |
Panel.Popup | Animated drawer shell. Accepts size and className. |
Top Bar
| Part | Purpose |
|---|---|
Panel.Topbar | Top row for close, breadcrumb, actions, and context controls. |
Panel.Topbar.Left | Left-aligned controls, usually Panel.Close and Breadcrumb. |
Panel.Topbar.Right | Right-aligned actions such as menus or share controls. |
Panel.Close | Standard close button wired to panel context. |
Panel.CopyLink | Copies the current URL or a provided link. |
Panel.Menu | Standard overflow action menu. |
Panel.Menu.Item | Small menu item sized for panel actions. |
Panel.Menu.ItemWithConfirmation | Destructive or high-risk action with confirmation. |
Panel.ToggleFavorite | Favorite toggle action button. |
Content Layout
| Part | Purpose |
|---|---|
Panel.Content | Primary vertical layout wrapper for panel internals. |
Panel.Header | Main entity header block below the top bar. |
Panel.Header.Left | Left side of the entity header. |
Panel.Header.LeftContent | Title and description stack. |
Panel.Header.Right | Right-side controls or metadata. |
Panel.Header.Title | Primary panel title. Supports loading skeleton state. |
Panel.Header.Description | Secondary description line, optionally with leftAdornment. |
Panel.Header.Avatar | Standard entity avatar block. |
Panel.Header.Meta | Compact metadata row. |
Panel.Body | Scrollable main body region. |
Panel.Section | Horizontal content padding block. |
Panel.ErrorGuard | Panel-tuned ErrorGuard with centered error-state spacing. |
Props
Panel
| Prop | Type | Default | Description |
|---|---|---|---|
isOpen | boolean | — | Required. Controls whether the panel is mounted and visible. |
handleClose | () => void | — | Required. Called when the panel should close. |
children | ReactNode | — | Required. Usually Panel.Backdrop and Panel.Popup. |
Panel.Popup
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'md' | 'lg' | 'xl' | 'full' | — | Required. Width preset for the panel shell. |
className | string | — | Additional layout or spacing classes. |
children | ReactNode | — | Required. Usually Panel.Content or a custom layout. |
Panel.Topbar
| Prop | Type | Default | Description |
|---|---|---|---|
sticky | boolean | false | Keeps the top bar pinned while content scrolls. |
className | string | — | Additional layout or spacing classes. |
children | ReactNode | — | Required. Top bar content. |
Common Composition Patterns
Entity Detail Panel
Common in lead, location, task, franchisee, and finder-result panels:
Panel.TopbarwithPanel.Close, breadcrumb,Panel.CopyLink, andPanel.MenuPanel.Headerwith avatar, title, description, and right-side metadataPanel.Sectionfor summary cards, tags, or detailsTabsor scrollable content below
Workflow Or Editor Panel
Common in workflow builders and editor-style experiences:
- Feature owns the full
Paneltree Panel.Popup size="md"orsize="lg"- top bar actions are usually menu-driven
- body content may skip
Panel.Headerentirely if the screen uses a custom layout
Full-Width Operational Panel
Used when the panel is effectively a secondary workspace:
Panel.Popup size="full"Panel.Content className="overflow-auto"orPanel.Bodyfor controlled scrolling- Often used for complex editing or deep operational workflows
Error Recovery In Panels
When panel content fails to load, prefer Panel.ErrorGuard over hand-rolling spacing around ErrorState.
Panel.ErrorGuard standardizes the most common panel fallback layout:
min-h-full justify-center px-6 py-12- centered messaging within
Panel.Body - shared
ErrorGuardpreset behavior
In stack-managed panels, the typical recovery actions are Back and Close Panel, not a generic full-page reload.
Brand Dashboard Usage Patterns
Representative usages that define the expected patterns:
| Pattern | Path | Notes |
|---|---|---|
| Global shell | fsai/apps/brand-dashboard/src/components/PanelStack/PanelStack.tsx | Owns the main Panel, backdrop, size mapping, and active panel rendering. |
| Content-only entity panel | fsai/apps/brand-dashboard/src/pages/Marketing/MarketingLeadPanel.tsx | Shows the standard topbar + header + section + tabs composition. |
| Content-only operational panel | fsai/apps/brand-dashboard/src/pages/Workflows/WorkflowRunPanel.tsx | Uses Panel.Content, Panel.Topbar, and Panel.Body inside the global shell. |
| Feature-owned local panel | fsai/apps/brand-dashboard/src/pages/Workflows/components/config-panel/NodeConfigPanel.tsx | Renders the full Panel tree for a workflow configuration drawer. |
| Full-size editor panel | fsai/apps/brand-dashboard/src/pages/AdminPanel/Helpdesk/ArticleManagementPage.tsx | Uses a large panel as an editor workspace. |
Important Conventions
- Use
Panelfor right-side detail and editing workflows, not for small confirmations or simple overlays. UseModalfor centered dialogs. - If a view is rendered inside
PanelStack, returnPanel.Contentand related subcomponents only. Do not create a nestedPanel. Panel.Closeand other context-dependent helpers require a realPanelroot above them.- Prefer
Panel.ErrorGuardwhen the panel body needs an error fallback. Panelmay sit below aModal, but aPanelmust never open on top of an activeModal.- Anchored floating components such as
Popover,Menu,Picker, andTooltipcan still open inside the currentPanelorModal; this rule only applies to major overlay surfaces. - Prefer
Panel.Bodyfor the main scroll container rather than letting the entire drawer scroll unpredictably. - Use
Panel.Sectionto keep horizontal padding consistent. - Use the size presets instead of hardcoding widths.
Naming Collision
There is also a local file at fsai/apps/brand-dashboard/src/modules/library/components/FileViewer/Panel.tsx that defines a different Panel layout component. It is unrelated to @fsai/shared-ui's drawer primitive.
When documenting or generating code, distinguish between:
import { Panel } from '@fsai/shared-ui'import { Panel } from './Panel'
Guidelines
- Do use
Panelas the default container for master-detail views in the dashboard - Do use
Panel.Topbarfor close, breadcrumbs, and high-level actions - Do use
Panel.Headerfor entity identity and top-level metadata - Do use
Panel.ErrorGuardfor centered panel failure states - Do use
Panel.Bodywhen content should scroll independently of the shell - Do choose
size="full"only when the panel needs workspace-like breadth - Don't nest
Panelinside the globalPanelStack - Don't open a
Panelon top of an activeModal - Don't use
Panelfor simple confirmations or short forms that should be a modal - Don't bypass the shared close/menu/header primitives unless the feature genuinely needs a custom layout