Skip to content

components: element:text_input's label / placeholder / description are the INVERSE of #5637 — the renderer already resolves the locale map, the declaration still says 'string', so the gate warns on a legal write #5717

Description

@os-zhuang

Found while implementing #5637 (element:record_picker's label / placeholder). Out of that card's fence — filed, not fixed there.

This is the same family, opposite direction. #5590 and #5637 are "the contract admits the map, the renderer drops it, so the declaration correctly stays narrow until the render site catches up". element:text_input is the case where the render site already caught up and the declaration never moved.

Measured

packages/components/src/renderers/basic/text-input.tsx resolves all three keys at their read sites:

 96: const label = pickLocalized(props.label, language);
97: const placeholder = pickLocalized(props.placeholder, language);
98: const description = pickLocalized(props.description, language);

The contract for all three is the I18nLabel union — measured on the installed @objectstack/spec17.1.0 pin, ElementTextInputPropsSchema:

label union [string | record] | map safeParse: OK
placeholder union [string | record] | map safeParse: OK
description union [string | record] | map safeParse: OK

But the ComponentMeta entries still declare one arm each (text-input.tsx:146, :147, :183):

{name: 'label',type: 'string',label: 'Label'},{name: 'placeholder',type: 'string',label: 'Placeholder'},{name: 'description',type: 'string',label: 'Description'},

Driven through the same manifestFromConfigs + validateTree path the console specimen test uses, with { en: 'Owner', 'zh-CN': '负责人' }:

PROBE label declaredArms="string" codes=["type-mismatch :: <element:text_input> prop \"label\" expected a string"]
PROBE placeholder declaredArms="string" codes=["type-mismatch :: <element:text_input> prop \"placeholder\" expected a string"]
PROBE description declaredArms="string" codes=["type-mismatch :: <element:text_input> prop \"description\" expected a string"]

Why this is the defect and not a harmless narrowing

ComponentInput.type in packages/types/src/base.ts states the rule in both directions: declare an arm the renderer drops and you advertise a shape that never reaches the screen; withhold an arm the renderer resolves and the manifest gate reports type-mismatch on a legal write. Its own words for the second half — "one platform authority contradicting itself on the write it just recommended, at warning severity — which is worse than it sounds, because noise on legal writes trains authors (AI authors included) to dismiss the unknown-prop and type-mismatch reports that ARE real."

Here the author writes a map, the renderer renders it correctly in the viewer's language, and the gate warns anyway. That is precisely the direction #5590's changeset called "the false declaration in the other direction".

Not a duplicate of the defaultValue specimen

apps/console/src/__tests__/component-input-union-specimens.test.ts:214 asserts that an I18N_MAP on element:text_input.defaultValuedoes report type-mismatch, and that assertion is correct and must stay: defaultValue's spec type is string | number, a key whose contract has no object arm. It is the control that keeps the widening per-key. The three keys above are a different set with a different contract, and none of them is pinned anywhere today.

What a fix would decide

Mechanically it looks like the emptyText move run backwards: widen the three inputs arms to ['string', 'object'] and rewrite the descriptions from what the renderer does. Two things worth a triage look rather than assuming:

  1. Whether all three travel together, or description is separable — it has a different destination than the two label-ish keys.
  2. Whether the console specimen file should gain a pin for this block, since the specimens are where "declared arms == contract arms" is currently guarded, and a fix with no pin can silently regress the same way.

Filed unassigned and unqueued.

Metadata

Metadata

Assignees

Labels

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

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions