Skip to content

finding: CurrencyField overrides a host onBlur, dropping a declared DOM pass-through key #6802

Description

@claude

Found while implementing #6780 (PR #6801), which added an onBlur arm to four
type="number" widgets. Filed rather than folded in: repairing it is a
behaviour change outside that card's ruling.

What

CurrencyField writes onBlur={handleBlur} AFTER the {...toDomProps(props)}
spread, so it OVERRIDES any onBlur a host supplied.

onBlur is a declared DOM pass-through key — it is named in
FieldWidgetDomProps (packages/fields/src/widgets/types.ts) and in
SDUI_DOM_PASS_THROUGH_KEYS (packages/core/src/utils/dom-props.ts), and
toDomProps forwards it. So this is the DECLARED-BUT-NOT-DELIVERED class
#3290 / #3222 — a key the contract promises that never reaches the DOM.

TagsField has the same shape (onBlur={() => addTag(draft)} after its spread).

Why it is LATENT today, not an active defect

Measured on d06059f24: no host in this repo passes onBlur to a field
widget.
The form renderers do not, and the data-table inline editor
deliberately uses a document-level pointerdown listener instead, with a
comment saying the injected widgets have no blur handler
(packages/components/src/renderers/complex/data-table.tsx). So nothing is
being dropped right now — it would bite the first host that relies on the
declared key, e.g. react-hook-form's touched/onBlur validation mode.

Why PR #6801 did not just repair it

The three widgets that gained an onBlur there (Percent, Number, Geolocation)
DO compose the host handler, because for them a bare handler would have been a
new regression. CurrencyField is different: composing changes behaviour that has
been in place since long before that card, and could start firing onBlur-mode
validation on currency fields that never saw it. That is an unmeasured change
outside the ruling, so it was left exactly as it was and written down here.

Suggested shape

The same one PR #6801 used for the other three:

const domProps = toDomProps(props);
const handleBlur = (e) => { ...existing...; domProps.onBlur?.(e); };

plus a pin that a host onBlur survives — PR #6801 has one for the other three
in NumberInputWidgets.badInputAnnounce.test.tsx.


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions