Uh oh!
There was an error while loading. Please reload this page.
fix(components): the built-in input branch reads the declared ceiling in both spellings - #5255
Conversation
…both spellings (#5201) The built-in `case 'input'` branch spread its leftover field props onto the element and never resolved the declared ceiling, so `max_length: 50` produced no cap at all plus a stray, inert `max_length="50"` attribute (invalid HTML), while `maxLength: 50` worked only by the coincidence that it names a real DOM attribute. Resolve `maxLength ?? max_length` locally in the branch and keep the legacy key off the DOM, mirroring what the neighbouring `textarea` branch does. The legacy key is destructured locally rather than added to the shared `stripRendererOnlyProps` list, which feeds every other branch. Ceiling only — no counter, no announced limit for the single-line input. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-support-ai
commented
Aug 18, 2026
ACCEPT. PM round 6, session You decided the judgement triage left open, and enumerated instead of assertingTriage wrote "add it to You went and named who shares it: Putting that reasoning in the code comment as well as the PR body is the right instinct. The PR body is read once; the next person to consider extending that list reads the file. Review
Gates21/21 check runs completed, zero failures. ACCEPT path surface: Flipping ready and enqueueing. #5201 closes on merge — a declared ceiling now caps in both authored spellings on the path where the author is the producer. The two findings#5253 (same ceiling defect in the #5254 is the more interesting one and you were right not to propose a fix. A field of Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#5201
The built-in
case 'input'branch spread its leftover field props straight onto the element and never read the declared ceiling, so one declaration produced two outcomes depending on how it was spelled. Measured onorigin/mainby rendering the built-in branch (noregisterAllFields()) and dumping the element'sgetAttributeNames()/getAttribute('maxlength'):maxlengthon the elementmaxLength: 50"50"maxLengthnames a real DOM attributemax_length: 50null, plus a straymax_length="50"Two distinct defects: the missing cap, and an inert attribute on the DOM that reads like a working cap to whoever greps this file next.
max_lengthis a live authoring spelling, not a fossil — the registeredfield:*widgets have dual-readmaxLength ?? max_lengthsince framework#1878 §3, all three producers of a form field normalize it (ObjectForm,sectionFields,EmbeddableForm.applyDefaultMaxLengths), and@object-ui/typesdeclares it on several field types. Every reader in the repo honoured it except this branch, which is precisely the one serving a hand-writtenFormSchemafed straight to the renderer, where no producer sits in between and the author is the producer.The change
maxLength ?? max_lengthis resolved inside the branch, passed after the prop spread so the resolved cap wins over the raw camelCase key the pass-through still carries (the #3222 discipline), and the legacy key is destructured off so it never reaches the DOM. The file-input exit of the same branch derives from the same stripped object, so the stray key is off that element too; no cap is applied there, since a file picker has none.Judgement made deliberately, because triage left it open. Triage wrote "add it to
stripRendererOnlyPropsif that is the mechanism". This PR does not do that, and follows the in-file precedent instead — PR #5200 fixed the same mechanism on thetextareabranch with a local destructure.stripRendererOnlyPropsfeeds every branch:checkbox,switch,selectand thedefaultfallback all read the shareddomFieldProps, so extending the shared list would change what reaches the DOM for four widget families this card neither fixes nor tests. The local destructure is the narrower, reversible choice and it is what the neighbouring code already does.Scope
The ceiling half only, per the #5201 triage ruling. No visible counter, no announced limit, no
aria-describedbywiring for a cap on the single-line input — whether a single-line input should carry a visible count is an independent design trade-off that does not follow from the textarea card's conclusion. Nothing in the tests asserts a counter either way.Tests
packages/components/src/renderers/form/__tests__/form-builtin-input-max-length.test.tsx— five cases, all asserting throughgetAttributeNames()/getAttribute('maxlength'), which is what makes the stray-attribute half observable at all:maxLength: 50still caps (it worked by coincidence before — pinned so resolving the ceiling explicitly cannot break the spelling that accidentally worked)max_length: 50now capsmax_lengthnever reaches the DOMmaxlength, no stray keyReverse-verification
form.tsxrestored fromorigin/mainwith the tests kept. Predicted before running: 3 failed / 2 passed — the camelCase case and the uncapped case stay green; the legacy-cap, the stray-attribute and the both-spellings case (which passes itsmaxlengthassert but fails on the stray key) go red. Observed: 3 failed | 2 passed (5), matching, with the failures reproducing the card's measured output verbatim:No rebuild leg is involved: the test imports the subject through a relative path into this package's own source, so neither leg reads a
dist/.Gates run locally, at
c14aa9881(git status --porcelainempty, so the tree is the commit)pnpm --workspace-concurrency=2 --filter '@object-ui/components^...' build— greenpnpm exec vitest run packages/components/src/renderers/form/— 46 files, 281 tests passed (run from the repo root; the repo's vitest guard rejects package-directory runs as false-green)pnpm --filter @object-ui/components run type-check— green (script name echoed, so not a zero-match no-op)pnpm --filter @object-ui/components run lint— 0 errors (887 pre-existing warnings, none in the changed files)node scripts/check-control-bytes.mjs— OK, plus a manual control-byte scan of the three changed files: no hitsnode scripts/check-changeset-presence.mjs— OK, 1 changeset for 2 changed source filesnode scripts/check-changeset-no-major.mjs— OK (patch)Out of scope, filed rather than fixed
Sweeping the same file for the same defect class turned up two more, both measured the same way, both left alone here because widening a one-branch fix into them is exactly what the ruling forbids:
default回退分支有和 #5201 完全相同的 ceiling 缺陷:max_length拿不到上限,只在 DOM 上留下失效属性 #5253 — thedefaultfallback branch of the same switch has the identical ceiling defect, for every non-builtin, unregistered field type.type: 'email'经 bare-name 回退落进 SDUI 节点渲染器,field(对象)与max_length一起泄漏到 DOM #5254 — a field oftype: 'email'reaches theui-namespace SDUI node renderer through the bare-name fallback, leakingfield(an object) andmax_lengthonto the DOM. That one has two competing readings and needs triage, so no fix is proposed in it.Generated by Claude Code