Skip to content

Commit 6830121

Browse files
os-zhuangclaude
andauthored
fix(dashboard): auto-flow layout-less widgets in the positioned grid (#1925)
DashboardRenderer placed any widget WITHOUT a `layout` at a single grid column in the positioned (explicit-columns) grid. A dashboard authored in the Studio designer — which adds widgets without a layout and relies on auto-flow — therefore crammed every widget into one row (charts squeezed to a few px wide and effectively invisible). Give layout-less widgets a sensible default span in the positioned grid (KPIs quarter-width w:3/h:2, charts & tables half-width w:6/h:4), clamped to the column count, mirroring the editable DashboardGridLayout's auto-placement. The responsive flow layout (no explicit columns) is unchanged — it already sizes each widget as one cell. `hasExplicitColumns` is hoisted so renderWidget can read it. Found dogfooding the Studio dashboard designer; pairs with the framework change making DashboardWidget.layout optional. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 47c6e25 commit 6830121

1 file changed

Lines changed: 20 additions & 5 deletions

File tree

packages/plugin-dashboard/src/DashboardRenderer.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ export const DashboardRenderer = forwardRef<HTMLDivElement, DashboardRendererPro
225225
})();
226226
const columns = inferredColumns;
227227
const gap = schema.gap || 4;
228+
// Positioned (explicit-columns) grid vs responsive auto-flow grid.
229+
// Defined here (not just above desktopBody) so renderWidget can give
230+
// layout-less widgets a sensible default span in the positioned grid.
231+
const hasExplicitColumns = schema.columns != null || inferredColumns !== 4;
228232
const [refreshing, setRefreshing] = useState(false);
229233
const [isMobile, setIsMobile] = useState(false);
230234
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
@@ -400,9 +404,22 @@ export const DashboardRenderer = forwardRef<HTMLDivElement, DashboardRendererPro
400404
);
401405

402406
const renderWidget = (widget: DashboardWidgetSchema, index: number, forceMobileFullWidth?: boolean) => {
403-
// Clamp widget span to grid columns to prevent overflow
404-
const clampedLayout = widget.layout
405-
? { ...widget.layout, w: Math.min(widget.layout.w, columns) }
407+
// Clamp widget span to grid columns to prevent overflow. A widget
408+
// with NO layout (e.g. authored in the Studio designer, which omits
409+
// it) would otherwise get a single column in the positioned grid and
410+
// cram the whole dashboard into one row (charts squeezed to a few px).
411+
// Give layout-less widgets a sensible default span so they auto-flow
412+
// into a readable grid — KPIs quarter-width, charts/tables half-width —
413+
// matching the editable DashboardGridLayout's auto-placement. Only the
414+
// positioned grid needs this; the responsive flow layout sizes each
415+
// widget as one cell.
416+
const isMetricSpan = widget.type === 'metric' || METRIC_LIKE_TYPES.has(widget.type || '');
417+
const fallbackSpan = hasExplicitColumns
418+
? { w: Math.min(isMetricSpan ? 3 : 6, columns), h: isMetricSpan ? 2 : 4 }
419+
: undefined;
420+
const effectiveLayout = widget.layout ?? fallbackSpan;
421+
const clampedLayout = effectiveLayout
422+
? { ...effectiveLayout, w: Math.min(effectiveLayout.w, columns) }
406423
: undefined;
407424

408425
// ADR-0021 — a widget bound to a semantic-layer dataset renders through
@@ -1003,8 +1020,6 @@ export const DashboardRenderer = forwardRef<HTMLDivElement, DashboardRendererPro
10031020
) : mobileBody;
10041021
}
10051022

1006-
const hasExplicitColumns = schema.columns != null || inferredColumns !== 4;
1007-
10081023
const desktopBody = (
10091024
<div
10101025
ref={ref}

0 commit comments

Comments
 (0)