Skip to content

plugin-dashboard: DashboardRenderer's widget grid spreads the whole SDUI node onto its container — 13 non-DOM attributes reach the DOM #4432

Description

@yinlianghui

Measured by the #4425 phase-1 DOM-leak sweep gate (packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx). Recorded in that gate's ledger, not fixed there — phase 1 is measurement only.

The defect

view:dashboard resolves to DashboardRenderer, whose widget-grid container ends in a bare spread (packages/plugin-dashboard/src/DashboardRenderer.tsx:962). Reached through SchemaRenderer — which every dashboard page is — the node's own keys and its props contents land on that container as attributes.

This is the same package PR #4428 fixed for MetricWidget / MetricCard and a different element: #4357 measured and closed the two KPI card components, and the container the cards sit in was not in that card's scope.

Measured, through the real SDUI path

One render under SchemaRendererProvider, node carrying the canary set:

class="grid auto-rows-min grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
style="gap: 1rem;" id="canary-node" name="canary_node" bind="data.revenue"
events="[object Object]" arialabel="Canary label" ariadescribedby="canary-desc"
zzcanary="CANARY-STR" zzcanaryobj="[object Object]" zzcanarynum="42"
zzcanarycamel="CANARY-CAMEL" reference_to="contacts" props="[object Object]"
colorvariant="success" zzcanaryprop="CANARY-PROP" aria-label="Canary label"
aria-describedby="canary-desc" data-obj-id="canary-node" data-obj-type="view:dashboard"

The 13 leaked attribute names:

ariadescribedby  arialabel  bind        colorvariant  events
name             props      reference_to  zzcanary    zzcanaryobj
zzcanarynum      zzcanarycamel           zzcanaryprop

datasource is not among them — DashboardRenderer consumes the adapter, so it never reaches the spread. schema is not either. Everything else does.

As with #4431, arialabel / ariadescribedby duplicate the correctly-resolved aria-label / aria-describedby the renderer emits, under a spelling assistive technology ignores.

Not in scope of the gate that found it

The sweep ledgers this target with this issue as its owner. The assertion is exact set equality, so fixing the leak turns the gate red until the ledger row is deleted in the same change.

Fix shape

Deliberately left to #4425 phase 2, which decides the SDUI widget prop contract generally. Note this package already carries a deny-list for the same class — packages/plugin-dashboard/src/schemaHostProps.ts — so the cheap move is to reuse it here. That would strip the seven named keys and leave the open tail (zzcanary, reference_to, an authored props: { colorVariant }) still on the DOM, which is exactly the limit the sweep measured on metric and metric-card. Worth weighing against the whitelist before reaching for it.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions