Skip to content

finding(plugin-dashboard): MetricWidgetProps / MetricCardProps declare no DOM passthrough, so the id / role / aria-* the spread accepts at runtime are a type error for a TS consumer #4426

Description

@yinlianghui

Observation-class finding, measured while implementing #4357. Pre-existing on origin/main and unchanged by that PR; filed rather than widened into it, because closing it changes the exported props interface of two components.

What happens

Both components end their prop list with a ...props spread onto the Shadcn Card. #4357 keeps the spread deliberately — it is the components' genuine DOM/aria passthrough, and removing it would delete the only way a host can put id, role or an aria-* on a KPI card. But neither MetricWidgetProps nor MetricCardProps extends React.HTMLAttributes, and neither declares those keys, so what the runtime accepts the type refuses.

Measured against the built dist/index.d.ts from a consumer package:

probe.tsx(23,52): error TS2322: Type '{ label: string; value: number; id: string; role: string; "aria-label": string; }'
  is not assignable to type 'IntrinsicAttributes & MetricWidgetProps & SchemaHostProps'.

The same source compiles and behaves correctly at runtime — the attributes reach the card. A JS consumer, or any SDUI author going through SchemaRenderer (which is untyped at that boundary), gets the passthrough; a TypeScript consumer importing the component directly cannot use it without a cast.

Why it is worth writing down even though nobody is blocked

It is the mirror image of #4357. That issue was renderer metadata reaching the DOM because the spread is open; this is DOM attributes being unreachable because the type is closed. Both come from the same unstated question — what may a dashboard widget legitimately receive? — and answering it once, in the type, is what makes "declared = enforced" true here in both directions instead of one.

Fix shape

Declare the passthrough: extend the interfaces with the subset of React.HTMLAttributes a KPI card should accept, or an explicit block mirroring what FieldWidgetDomProps does for field widgets (objectui#3221). Either way it widens the exported props interface, which by this repo's grading is a minor with the semantics in the changeset body (the objectui#4403 precedent), so it is not a drive-by rider on a patch fix. It should also be decided together with #4425 — if plugin widgets move to a toDomProps-style whitelist, the declared DOM set is exactly that whitelist and the two are one change.

No pm:queue — nothing a user hits today.


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