LogisBaseLogisBase

ContentPanel

<ContentPanel> is the canonical collapsible section used to organize forms, settings, and detail views. Supports titles, status badges, action buttons, and permission gating.

<ContentPanel>

<ContentPanel> is the standard collapsible panel used to organize a page into discrete sections. Forms, settings groups, detail views, drawers — they all use ContentPanel.

It's collapsible by default. Pass @open={{true}} to render expanded.

Basic Usage

<ContentPanel @title='Driver Details' @open={{true}}>
  <InputGroup @name='Name' @value={{this.driver.name}} />
  <InputGroup @name='Phone'>
    <PhoneInput @value={{this.driver.phone}} />
  </InputGroup>
</ContentPanel>

Collapsible by Default

Without @open={{true}}, the panel renders collapsed:

<ContentPanel @title='Advanced Settings'>
  {{! This body is hidden until the user clicks the header }}
  <InputGroup @name='Webhook URL' @value={{this.webhookUrl}} />
</ContentPanel>

With a Status Badge

@titleStatus renders a <Badge> next to the title:

<ContentPanel
  @title='Order #FB-1234'
  @titleStatus={{this.order.status}}
  @open={{true}}
>
  {{! ... }}
</ContentPanel>

With a Subtitle

<ContentPanel
  @title='Payment Settings'
  @subtitle='Configure how customers pay you'
  @open={{true}}
>
  {{! ... }}
</ContentPanel>

With Action Buttons

The actions named block places action buttons in the panel header (right side):

<ContentPanel @title='Locations' @open={{true}}>
  <:actions>
    <Button
      @type='primary'
      @icon='plus'
      @text='Add Location'
      @onClick={{this.addLocation}}
    />
  </:actions>

  <:default>
    {{! Locations list }}
  </:default>
</ContentPanel>

Arguments

Title & Subtitle

ArgumentTypeDescription
@titlestringPanel title
@subtitlestringSmaller text rendered beneath the title
@prefixTitlestringSmall text shown above the title
@titleIconstringFontAwesome icon next to the title
@titleIconPrefixstringIcon prefix (fas, far, fab)
@titleIconSizestringDefault xs
@titleIconClassstringExtra classes on the icon
@titleStatusstringStatus string — renders a <Badge> next to the title
@disableTitleStatusHumanizebooleanPass through to the <Badge>
@hideStatusDotbooleanPass through to the <Badge>
@titleComponentstringRender a custom component instead of the default title block
@titleComponentContextanyContext passed to @titleComponent
@titleStatusContainerClassstringExtra classes around the status badge
@titleStatusClassstringExtra classes on the status badge

Open / Toggle Behavior

ArgumentTypeDefaultDescription
@openbooleanfalseWhether the panel starts open
@toggleOnCaretOnlybooleanfalseOnly toggle when clicking the caret icon — clicking the title text does nothing
@hideCaretbooleanfalseHide the chevron toggle
@caretIconstringchevronThe icon name (suffixed with -down/-up based on state)
@caretLeftbooleanfalseRender the caret on the left of the title instead of the right

Loading & Disabled

ArgumentTypeDefaultDescription
@isLoadingbooleanfalseShow a spinner inside the title row and dim the panel
@disabledbooleanfalseVisually disable the toggle (still clickable, but dimmed)
@permissionstringIf the current user lacks this ability, the panel auto-disables and shows an "Unauthorized" tooltip

Tooltip

ArgumentTypeDefaultDescription
@helpTextstringTooltip on the title
@exampleTextstringOptional example beneath the help text
@tooltipPlacementstringrightTooltip placement

Action Buttons

@actionButtons accepts an array of action descriptors — each is rendered as a <Button> (or a custom component) in the panel header:

<ContentPanel
  @title='Settings'
  @open={{true}}
  @actionButtons={{this.headerActions}}
>
  {{! ... }}
</ContentPanel>
// in your component class
get headerActions() {
  return [
    { type: 'primary', icon: 'plus', text: 'Add', onClick: this.add },
    {
      icon: 'ellipsis',
      items: [   // dropdown menu
        { text: 'Export', onClick: this.export },
        { text: 'Import', onClick: this.import },
      ],
    },
  ];
}

For more control, use the actions named block instead.

Layout Class Hooks

A long tail of *Class arguments lets you target every internal element:

@wrapperClass, @containerClass, @panelClass, @panelHeaderClass, @panelHeaderLeftClass, @panelHeaderRightClass, @panelTitleClass, @panelTitleInlineClass, @panelTitleWrapperClass, @panelSubtitleInlineClass, @titleContainerClass, @prefixTitleClass, @prefixTitleContainerClass, @titleIconWrapperClass, @pad.

Callbacks

ArgumentSignatureDescription
@onInsert(api)Called when the panel is inserted. Receives { toggle, open, close }
@onToggle(isOpen)Called whenever the panel toggles
@onClick(api)Called on any click in the header
@onClickCaret(api)Called when the caret specifically is clicked
@onClickPanelTitle(api)Called when the title (not the caret) is clicked

The api object contains { toggle, open, close } so you can imperatively control the panel from outside.

Imperative Control

Capture the API via @onInsert to control the panel from outside:

<ContentPanel @title='Filters' @onInsert={{this.captureApi}}>
  {{! ... }}
</ContentPanel>

<Button @text='Open filters' @onClick={{fn this.api.open}} />
<Button @text='Close filters' @onClick={{fn this.api.close}} />
@tracked api = null;

@action captureApi(api) {
  this.api = api;  // { toggle, open, close }
}

Yielded Blocks

BlockPurpose
(default)The panel body
:titleCustom content next to the title (replaces the default title rendering)
:actionsAction buttons / dropdown in the header right

Real-World Examples

{{! Settings group with status badge }}
<ContentPanel
  @title='Stripe Gateway'
  @titleStatus={{if this.gateway.is_sandbox 'sandbox' 'live'}}
  @open={{true}}
>
  <InputGroup @name='Publishable Key' @value={{this.gateway.publishable_key}} />
  <InputGroup
    @name='Secret Key'
    @value={{this.gateway.secret_key}}
    @type='password'
  />
</ContentPanel>

{{! Permission-gated panel }}
<ContentPanel
  @title='Danger Zone'
  @permission='platform manage organization'
  @helpText='Only org admins can change these settings'
>
  <Button @type='danger' @text='Delete Organization' @onClick={{this.delete}} />
</ContentPanel>

{{! With actions block }}
<ContentPanel @title='Drivers' @open={{true}}>
  <:actions>
    <Button
      @type='primary'
      @icon='plus'
      @text='New Driver'
      @onClick={{this.addDriver}}
    />
    <Button @icon='filter' @onClick={{this.openFilters}} />
  </:actions>

  <:default>
    {{! Drivers list }}
  </:default>
</ContentPanel>

{{! Loading state }}
<ContentPanel
  @title='Order Details'
  @isLoading={{this.isLoading}}
  @open={{true}}
>
  {{#unless this.isLoading}}
    {{! Order body }}
  {{/unless}}
</ContentPanel>

Source

ContentPanel | LogisBase