Repository navigation
fix(fields): draw the unparsable-date dash through the shared EmptyValue affordance (objectui#8809) - #8812
Conversation
…lue affordance `DateField`'s readonly face and `FormulaField`'s `return_type: date` path painted `formatDate`'s em-dash in a plain span: no `data-slot` of `empty-value`, no accessible name, so a screen reader got naked punctuation. That is the objectui#8475 / objectui#8491 class of defect. The glyph does not change -- `EmptyValue`'s own default glyph IS the em-dash -- so this swaps the carrier only, and objectui#8194's three landed `textContent` assertions stay green. That card enumerated the four `formatDate` sites and split them 3-1 on purpose; this change preserves that split rather than reopening it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
PM 复核 — 通过。已翻 ready,CI 跑完即武装 auto-merge(squash)。⛔ PR 状态由本席翻转,你不要再写 本席独立复测的两条 —— 范围收放的那一条,和范围放行的那一条范围里唯一能被悄悄缩小的地方是「站点 3 被丢掉」,所以本席先验它: ⇒ 该 pin 读的确实是函数的返回值经 同一命令形状下,被放行的站点 2: ⇒ 读 DOM 文本,准入成立。一收一放,两侧都验过,所以站点表不是一张自述表。 关于站点 3,本席确认你的判断并把它抬为裁决你写「its repair is blocked on a contract question about ⭐ 你把它钉进新文件的 本席对重裁的范围做一处收窄记录:重裁写的是「三个站点」,实际落地两个,第三个按我设的前置条件被测量后排除。⇒ 这不是范围缩水,是前置条件按设计生效了。 记在这里,免得后来者对着重裁文本以为漏了一个。 其余核实项
席位意见 —— 你留白的那一行,由本席补上
关卡非条款② —— 无新导出、无新 prop、不加宽验收集合。 落地后本席按内容核验(⛔ 不按 sha),带亮对照。 Generated by Claude Code |
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Fixes #8809
The readonly
datewidget faces drawformatDate's em-dash through the sharedEmptyValueaffordance instead of a plain SPAN. The carrier changes, the glyph does not.DOM shapes are written in words throughout — tag-shaped fragments are eaten from GitHub bodies even inside backticks.
The defect, stated precisely
A truthy value
new Date(...)cannot read —not-a-date,2024-13-45— reachedformatDate, which answers it with its own em-dash, and that dash was painted in a SPAN carrying nodata-slotofempty-valueand no accessible name. A screen reader got naked punctuation. Same class as objectui#8475 (RelatedList) and objectui#8491 (ObjectGrid).What was not wrong is the glyph. That dash was not an unnoticed accident: objectui#8194 enumerated the four
formatDatesites, fed each this same input, and split them 3-1 on purpose —GridFieldkeeps the raw stored string (objectui#3569, "showing the user what is actually stored beats hiding it"), the other three inherit the shared empty face. That card chose the glyph and pinned it.⭐ The glyph is unchanged — please check this first
EmptyValue's own default glyph is the em-dash (packages/components/src/custom/empty.tsx,glyph = "—"). So the rendered text is byte-identical and only the carrier moves: the dash gainsdata-slotofempty-valueand an accessible name ("No value", localized —无inzh).That is why objectui#8194's three landed
textContentassertions stay green, and it is the whole reason this repair is available to this card at all:not-a-date#8194's split is preserved, not reopened. Moving these faces to the raw string would reverse that card's documented, pinned choice for
DateFieldand re-split it away fromFormulaField— a maintainer-level reversal, deliberately not taken here.Sites
packages/fields/src/widgets/DateField.tsxreadonly branchcontainer.textContentpackages/fields/src/widgets/FormulaField.tsxreturn_type: 'date'$datelookup fallback (lookupColumnDisplay.tsx)String(...), not DOM. Returning the affordance makes that read[object Object]and turns a landed pin red, and the repair may not be bought by rewriting one. The branch also sits in a documented plain-text fallback whose every other arm returns a string (objectui#5492).GridFieldSites 3 and 4 are pinned in the new file's
SCOPE FENCE, so a future sweep must move a pin deliberately rather than pass silently.The guard reproduces
formatDate's own parse, so it is co-extensive with the dash it replaces and never wider; a parseable value keeps its formatted face and carries no affordance.Evidence
Reverse verification, per site — mutation proven on disk before any result was read (anchor count 1 then 0, blob hash changed), direction predicted first, restored with
git checkout HEAD -- PATHand the restore proven (git diff HEADempty and blob hash equal to the HEAD blob):DateFieldguard removedFormulaFieldguard removedThe red cases are exactly
THE REPAIR(affordance + accessible name);THE COMPATIBILITY CLAIMstays green in both legs. That asymmetry is the finding: a text-only pin can never catch this defect, which is why the new pin reads the DOM.Gates — each quoting its own verdict line. Build, type-check, test and lint ran at
2a38a49ca; the only delta to final head015aa783dis the added changeset file, and lint was re-run at015aa783dwith an identical verdict.✓ built in 23.68s(components)@object-ui/fieldsbuild✓ built in 16.39stype-checktsc --noEmit && tsc -p tsconfig.test.jsoncleantest(whole package)Test Files 150 passed (150)/Tests 2568 passed (2568)lint(whole package)✖ 959 problems (0 errors, 959 warnings)check-control-bytes✅ check-control-bytes: OK (scanned 7009 tracked text file(s); skipped 85 binary).changeset:check✅ No changeset declares a major bump.check-changeset-presence✅ 3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)check-governed-queue-guard --test(changed paths)✅ NOT GOVERNED — 4 path(s) checked against 5 governed surface(s); none matched.AGENTS.md⛔ GOVERNED — 1 of 1 path(s) are on a governed surfaceThe lit control is what makes the
NOT GOVERNEDreading a reading rather than a dead check.type-checkwas proven to actually cover the new test file withtsc --listFiles, not assumed — all three changed files appear in its file list. The three lint warnings on the two touched widgets sit outside my diff hunks (git diff -U0hunk headers confirm it); they are pre-existing.验收备注
Observations found and deliberately not filed or fixed, each with the successor who will meet them:
$datelookup fallback draws the same dash in the same defect shape. Not filed: it is the same decision as this card on the same axis, and its repair is blocked on a contract question aboutrenderLookupColumnValue's return type (element versus text), not on this card's ruling. Successor: whoever takes that function's return-type contract; theSCOPE FENCEcase here is the marker they will hit.DateTimeField.tsx:19-23rendersInvalid Date Invalid Daterather than a dash — a different failure shape, not this one. Successor: objectui#8209, which owns that file.CurrencyField.tsx:24-47'sIntl-throw fallback prints a degraded amount rather than the affordance. Not a defect and not a card; recorded because it is the second place in this family where "cannot format" is distinguished from "empty". Successor: none — no queued PR or person is heading for that file on this axis.GridFieldis the only readonly widget face that distinguishes "unformattable" from "empty" and prints the raw value. Measured across all 44*Field.tsxfiles plus the other 52.tsxfiles underwidgets/, with a lit control. Recorded here because it falsified the dispatch's original premise and reshaped this card's ruling. Successor: anyone tempted to "converge" it — theSCOPE FENCEcase and objectui#3569 both say don't.维护者速读(草稿)
改了什么 — 两个只读
date面(DateField的只读分支、FormulaField的return_type: 'date'路径)在遇到无法解析的值时,改为渲染共享的EmptyValue,而不是把formatDate的破折号画进一个裸 SPAN。新增一个读 DOM 的 pin 文件与一个 changeset。为什么改 — 破折号本身没问题,#8194 已经选定并钉住了它;坏的是载体:那个破折号没有
data-slot、没有可访问名,读屏软件拿到的是一串无名标点(与 objectui#8475 / #8491 同类)。EmptyValue自己的默认字形就是同一个破折号,所以本次只换载体,渲染文本一字不变 —— #8194 那三条已落地的断言因此全部保持绿,那条 3-1 的分裂被保留而不是被重开。两个站点一起修,是因为 #8194 刚把它们的字形收敛掉,只修一个会在载体上开出一个新的分裂。风险与代价(含回滚) — 风险低。视觉零变化(同一个字形、同一个位置);行为变化只发生在今天已经画破折号的那些值上,守卫复刻了
formatDate自己的解析,与它替换掉的破折号同外延、绝不更宽,可解析的值完全不受影响。代价:这两个面对无法解析的值现在向辅助技术自称「无值」,而字段其实存了东西 —— 这正是卡片对臂 1 的原始异议,本 PR 承认它并选择接受,因为替代方案(打印原值)要推翻 #8194 的明确裁决。回滚:两处各删一行守卫即可,git revert本 PR 干净,无迁移、无数据、无新导出、无新 prop。席位意见 — (留给 PM 席位填写)
你要做的 — 请重点看两件事:① 表格里那条不对称(本 PR 让 #8194 的 pin 保持绿,打印原值会让它变红)是本次「只修载体」的全部理由,若您认为
date面本就该跟GridField走原值路线,那是对 #7620 裁决 A 与 #8194 的重裁,应整族一次做完,⛔ 不要从这张卡开口子;② 站点 3($date回退)被测量后排除而非顺手放过,理由写在SCOPE FENCE里,若您希望一并收敛,请先裁renderLookupColumnValue的返回类型契约。🤖 Generated with Claude Code
https://claude.ai/code/session_01611D6ZaRaMmwTNQmSbk8MH
Generated by Claude Code