Skip to content

RFE: Sectioned Widget Layouts [draft] #18

@nicolethoen

Description

@nicolethoen

Summary

Add support for grouping widgets into named sections within a dashboard layout.
Each section has an editable label and contains its own grid of widgets. Widgets
can be dragged between sections.

Motivation

Current dashboards use a single flat grid for all widgets. Users have expressed
the need to organize widgets into logical groups (e.g., "Performance",
"Security", "Inventory") with visible section headers. This improves scannability
and allows dashboard authors to create meaningful visual hierarchy.

Proposed Data Model

New Types

interface SectionConfig {
  /** Unique identifier for the section */
  id: string;
  /** Display label (editable by users) */
  label: string;
  /** Sort order of the section within the dashboard */
  order: number;
  /** Whether the section is collapsed */
  isCollapsed?: boolean;
}

interface SectionedTemplateConfig {
  /** Section metadata */
  sections: SectionConfig[];
  /** Layouts keyed by section ID, then by breakpoint */
  layouts: Record<string, ExtendedTemplateConfig>;
}

Example

const template: SectionedTemplateConfig = {
  sections: [
    { id: 'performance', label: 'Performance', order: 0 },
    { id: 'security', label: 'Security', order: 1 },
  ],
  layouts: {
    performance: {
      xl: [
        { i: 'cpu-widget#1', x: 0, y: 0, w: 2, h: 3, widgetType: 'cpu-widget',
title: 'CPU Usage' },
        { i: 'memory-widget#1', x: 2, y: 0, w: 2, h: 3, widgetType:
'memory-widget', title: 'Memory' },
      ],
      // lg, md, sm...
    },
    security: {
      xl: [
        { i: 'alerts-widget#1', x: 0, y: 0, w: 2, h: 4, widgetType:
'alerts-widget', title: 'Active Alerts' },
      ],
      // lg, md, sm...
    },
  },
};

Proposed Component API

SectionedLayout

A new top-level component that renders multiple GridLayout instances, one per
section.

interface SectionedLayoutProps {
  /** Widget mapping definition (shared across all sections) */
  widgetMapping: WidgetMapping;
  /** Sectioned template configuration */
  template: SectionedTemplateConfig;
  /** Callback when template changes (layout moves, section edits, cross-section
drags) */
  onTemplateChange?: (template: SectionedTemplateConfig) => void;
  /** Whether the layout is locked */
  isLayoutLocked?: boolean;
  /** Whether section labels are editable */
  isSectionLabelEditable?: boolean;
  /** Whether sections can be collapsed */
  isSectionCollapsible?: boolean;
  /** Whether widgets can be dragged between sections */
  allowCrossSectionDrag?: boolean;
  /** Analytics tracker */
  analytics?: AnalyticsTracker;
}

SectionHeader

An internal component (also exported for standalone use) rendering the editable
section label.

interface SectionHeaderProps {
  /** Section configuration */
  section: SectionConfig;
  /** Callback when label is edited */
  onLabelChange?: (sectionId: string, newLabel: string) => void;
  /** Callback when collapse is toggled */
  onCollapseToggle?: (sectionId: string) => void;
  /** Whether the label is editable */
  isEditable?: boolean;
  /** Whether the section is collapsible */
  isCollapsible?: boolean;
}

Usage

<SectionedLayout
  widgetMapping={widgetMapping}
  template={sectionedTemplate}
  onTemplateChange={setSectionedTemplate}
  isSectionLabelEditable
  isSectionCollapsible
  allowCrossSectionDrag
/>

Implementation Considerations

Cross-Section Drag and Drop

- Each section contains its own GridLayout (and thus its own react-grid-layout
instance)
- Cross-section drag requires coordinating between two separate grid instances
- Approach: when a widget is dragged out of one section, remove it from the
source section's layout and add it to the target section's layout via drop
handlers
- react-grid-layout supports external drops via onDrop / isDroppable, which can
be leveraged here

Backwards Compatibility

- SectionedLayout is a new component  the existing GridLayout and WidgetLayout
remain unchanged
- Consumers using the flat ExtendedTemplateConfig are unaffected
- A utility function flatToSectioned(template, sectionId?) could help migrate
existing templates

Section Management

- Adding/removing/reordering sections should be handled via onTemplateChange
- Consider providing helper utilities: addSection, removeSection, reorderSections
- Empty sections could show a drop zone placeholder

Drawer Integration

- The WidgetDrawer should work with SectionedLayout  dragging a widget from the
drawer into a specific section
- The drawer's filtering (hiding already-used widgets) needs to account for
widgets across all sections

Acceptance Criteria

- New SectionedTemplateConfig type with sections and per-section layouts
- SectionedLayout component renders multiple grids with section headers
- Section labels are inline-editable when isSectionLabelEditable is true
- Sections can be collapsed/expanded when isSectionCollapsible is true
- Widgets can be dragged between sections when allowCrossSectionDrag is true
- WidgetDrawer integrates with sectioned layouts (drop into specific section)
- Existing GridLayout / WidgetLayout APIs remain unchanged
- Unit tests for section CRUD operations and cross-section drag
- Documentation example demonstrating sectioned layout

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions