Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

MetricWidget / MetricCard spread ...props onto the DOM, emitting a schema="[object Object]" attribute on every KPI card #4357

Closed

Description

@yinlianghui

Observation-class finding, recorded while writing the #4032 render pins. Nothing a user sees; filed so the next agent asserting not.toContain('[object Object]') on a dashboard container knows why it fails and does not "fix" it by widening the wrong assertion.

What happens

MetricWidget (packages/plugin-dashboard/src/MetricWidget.tsx) ends its prop list with ...props and spreads it onto the Shadcn Card:

export const MetricWidget = ({ label, value, /* … */ ...props }: MetricWidgetProps) => {
  …
  <Card className={…} onClick={…} {...props}>

When the widget is reached through SchemaRenderer (every dashboard KPI tile), the renderer hands it the widget schema, so schema lands in ...props and React — which passes unknown lowercase attributes straight through — writes it to the DOM stringified:

<div class="rounded-lg border bg-card …" schema="[object Object]" data-obj-type="metric">

MetricCard has the same {...props} spread and the same exposure.

Why it is worth writing down even though it is invisible

It is a trap for exactly the assertion this area attracts. #4163 pinned expect(container.innerHTML).not.toContain('[object Object]') on DashboardGridLayout, and that is the natural thing to write for any I18nLabel render site. On the metric path that assertion fails for a reason unrelated to labels, in both directions:

  • before an i18n fix, it fails and looks like it caught the label bug;
  • after the fix, it still fails, and the tempting repair is to delete or loosen the assertion — which throws away the real pin.

#4032's suite therefore asserts on the card heading element rather than the container, with a comment. That works, but it is a workaround for a defect that should just not be there.

Fix shape

Destructure the schema-shaped props out before the spread (or allow-list what reaches the DOM), the way a component that is both an SDUI block and a React component normally does. Low risk, but it touches the public prop contract of two exported components, so it is not a drive-by.

No pm:queue — nothing a user hits today.


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 12, 2026
  2. yinlianghui commented on Aug 12, 2026

    @yinlianghui
    ContributorAuthor

    CLAIM + RULING — PM seat session_017Qqyix2QcnpUC9XeYVDzx3, branch claude/issue-4357-metric-dom-spread, one dev agent, one PR, Fixes #4357. finding → pm:queue.

    Ruling (delegated, veto window open): destructure the schema-shaped props out before the spread in both MetricWidget and MetricCard (measure at the SchemaRenderer call site exactly which non-DOM props arrive — schema is the known one; take what the measurement shows, allow-list only if destructuring can't enumerate). The spread stays for genuine DOM/aria passthrough. Acceptance: zero rendered-output change except the bogus attribute disappearing, and the assertion this trap blocks becomes writable — the pin IS not.toContain('[object Object]') on the metric container, red pre-fix by the card's own mechanism (#4118), replacing #4032's heading-element workaround with a comment pointing here. Changeset graded by whether the exported props interface actually changes (destructuring alone doesn't narrow it → patch; a type change → the #4403 precedent), never major.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions