diff --git a/packages/plugin-dashboard/src/DashboardRenderer.tsx b/packages/plugin-dashboard/src/DashboardRenderer.tsx index 6cf6b5d27..0e85d454e 100644 --- a/packages/plugin-dashboard/src/DashboardRenderer.tsx +++ b/packages/plugin-dashboard/src/DashboardRenderer.tsx @@ -225,6 +225,10 @@ export const DashboardRenderer = forwardRef | null>(null); @@ -400,9 +404,22 @@ export const DashboardRenderer = forwardRef { - // Clamp widget span to grid columns to prevent overflow - const clampedLayout = widget.layout - ? { ...widget.layout, w: Math.min(widget.layout.w, columns) } + // Clamp widget span to grid columns to prevent overflow. A widget + // with NO layout (e.g. authored in the Studio designer, which omits + // it) would otherwise get a single column in the positioned grid and + // cram the whole dashboard into one row (charts squeezed to a few px). + // Give layout-less widgets a sensible default span so they auto-flow + // into a readable grid — KPIs quarter-width, charts/tables half-width — + // matching the editable DashboardGridLayout's auto-placement. Only the + // positioned grid needs this; the responsive flow layout sizes each + // widget as one cell. + const isMetricSpan = widget.type === 'metric' || METRIC_LIKE_TYPES.has(widget.type || ''); + const fallbackSpan = hasExplicitColumns + ? { w: Math.min(isMetricSpan ? 3 : 6, columns), h: isMetricSpan ? 2 : 4 } + : undefined; + const effectiveLayout = widget.layout ?? fallbackSpan; + const clampedLayout = effectiveLayout + ? { ...effectiveLayout, w: Math.min(effectiveLayout.w, columns) } : undefined; // ADR-0021 — a widget bound to a semantic-layer dataset renders through @@ -1003,8 +1020,6 @@ export const DashboardRenderer = forwardRef