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

[fields] GridField 子表 date 列不回显 ISO 形状的值:String(val) 直喂 input[type=date],同 #3127 的同族缺口 #3566

Closed

Description

@baozhoutao

现象(读码确认,尚未真机复现)

packages/fields/src/widgets/GridField.tsx:763 内嵌子表的单元格输入:

type={c.type === 'date' ? 'date' : isNumeric(c.type) ? 'number' : 'text'}
...
value={val != null ? String(val) : ''}

当列类型是 date 而值是 ISO 形状(2026-06-17T00:00:00.000Z)时,String(val) 原样喂进 <input type="date">。该控件只接受 YYYY-MM-DD,其余形状静默拒收:属性照样落进 DOM,input.value 读回空串,单元格画成空占位。

即 #3127 在子表面上的同一个缺口。#3127 修的是 DateTimeField / DateField 两个独立 widget,GridField 自己手搓 cell input,不走那条路径,所以没被覆盖到。

预期

子表 date 列应与 DateField 一致:显示存量日历日,且不因时区把日期左移一天。

建议改法

复用 #3127 落地的 packages/fields/src/widgets/nativeDateValue.ts:

value={c.type === 'date' ? toDateInputValue(val) : (val != null ? String(val) : '')}

toDateInputValue 对开头是 YYYY-MM-DD 的串原样透传(避免 date-only 被按 UTC 午夜解析后整体差一天),其余形状按本地日历分量渲染。

写回侧这里不需要配对转换 —— onChange 交出的就是控件自己的 YYYY-MM-DD,与 DateField 的约定一致。

待办

  • 先真机确认:造一条子表 date 列存 ISO 值的记录,看单元格是否为空(本条目前只有读码依据)
  • 若确认,按上面改法修 + 补单测

出处

排查 #3127 时顺带扫到的同族面,按范围纪律未并进那一单(PR #3565)。同批已排除的干净面:plugin-gantt 内联日期编辑(GanttView.tsx:3559 用 toLocaleDateString('en-CA') 生成 YYYY-MM-DD)、UserFilters / FilterUI / action-param-dialog / RecordPickerDialog(初值本来就是空的,不适用)。

Blocked-by: #3565


Generated by Claude Code

Activity

  1. self-assigned this
    on Aug 7, 2026
  2. claude commented on Aug 7, 2026

    @claude
    Contributor

    Triage: pm:queue + pm:blocked, Blocked-by: objectstack-ai/objectui#3565 appended to the body. No domain:* — objectui is a whole-repo seat and routes by repo:*, not by domain lanes. Kept in objectui: the landing site is one objectui file, no packages/spec surface, not cross-repo.

    Premise verified on objectui origin/main@7dfbeb7 — the reported code is present verbatim:

    760:  type={c.type === 'date' ? 'date' : isNumeric(c.type) ? 'number' : 'text'}
    763:  value={val != null ? String(val) : ''}
    

    so an ISO-shaped value does reach <input type="date"> unconverted, and that control silently rejects anything that is not YYYY-MM-DD — the attribute lands in the DOM, input.value reads back empty, the cell paints blank. The code reading is conclusive on its own; the body's open "confirm on a real machine" checkbox is worth doing but is not what gates dispatch.

    ⚠️ Why pm:blocked and not plain pm:queue — this is a real blocker, not caution. The proposed fix reuses toDateInputValue from packages/fields/src/widgets/nativeDateValue.ts, and that file does not exist on main yet (git log origin/main -- …/nativeDateValue.ts → empty). It arrives with PR #3565, which is open and implements #3127 (pm:queue, target:v17). Dispatching before #3565 merges means either waiting on it mid-task or hand-rolling a second date-normalisation helper — and a second helper is exactly the divergence this issue exists to close. Return it to the queue when #3565 merges; the unlock sweep greps the Blocked-by: line for that.

    Dedup. Not a duplicate of #3127: that issue covers the DateTimeField / DateField widgets, and GridField hand-rolls its own cell input rather than routing through them, so #3565's fix does not reach this surface. Same family, disjoint files, sequenced rather than merged — which is the correct shape. The body also records the surfaces already cleared in the same sweep (plugin-gantt inline date editing uses toLocaleDateString('en-CA'); UserFilters / FilterUI / action-param-dialog / RecordPickerDialog start empty), so a dev need not re-scan them.

    Not on the release board. objectui rides the console bundle and target:<major> applies there, but this is held behind #3565 and the parent defect #3127 already carries target:v17 — the boarded item is the one that can actually be worked. If #3566 is still open when the RC is cut, re-evaluate on its own.

    ⚠️ Process note, no action implied. This issue carried an assignee before any triage label existed and has no claim comment — same pattern as objectstack#6257 today. Flagging only because "an unlabelled issue must not be claimed" is the lane protocol's single mechanical guard. This seat never assigns and has not touched the assignee.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. baozhoutao commented on Aug 7, 2026

    @baozhoutao
    ContributorAuthor

    Claim: PM loop round 1
    Branch: claude/issue-3566-gridfield-date-input
    Worktree: objectui-issue-3566


    前提复核(独立于分诊评论,亲自核对 origin/main@7dfbeb7)

    packages/fields/src/widgets/GridField.tsx 现状确认,与 issue 描述逐字一致:

    760:  type={c.type === 'date' ? 'date' : isNumeric(c.type) ? 'number' : 'text'}
    763:  value={val != null ? String(val) : ''}
    

    packages/fields/src/widgets/nativeDateValue.ts 不在 origin/main 上(git ls-tree -r origin/main 无匹配),它随 PR #3565 到达。toDateInputValue 已在该分支上导出,签名与本 issue 建议改法完全吻合,且其 doc comment 已经把「<input type=date> 静默拒收非 YYYY-MM-DD」这一机制写死。所以 Blocked-by 是真实的。

    决策:改「等 #3565 合并」为「叠在 #3565 上开工」(PM 判断,排序/依赖属非升级类)

    #3565 当前状态:非 draft、mergeable: MERGEABLE、mergeStateStatus: CLEAN、18 项检查全绿。也就是说它的内容已定型,阻塞只剩合并时机,不是设计未决。

    因此本单不空等,而是栈式开发:

    这样既拿到了依赖,又绝不会出现 issue 正文最担心的那件事:再手搓第二个日期归一化 helper(那正是本单要消除的分歧)。开发 agent 被明确禁止碰 #3565 的分支本身。

    验证要求:必须真机,夹具已现成

    按本仓约定,objectui 的修复不以单测绿为完成条件,须在 framework showcase 里跑出现象。showcase 已有天然夹具,无需新造:

    examples/app-showcase/src/data/objects/expense-report.object.ts 的 expense_line
    → expense_report: Field.masterDetail(..., { inlineEdit: 'grid' })(正是 GridField 渲染的内嵌子表)
    → incurred_on: Field.date({ label: 'Incurred On' })(正是出问题的 date 列)
    → data/seed/index.ts 里 12 条 incurred_on: cel\daysAgo(N)`` 的存量记录。

    可证伪判据:打开 EXP-2001 的 Edit 表单,Expense Lines 网格的 Incurred On 单元格 —— 修复前应为空、修复后应显示存量日历日且不左移一天。

    不需要 changeset

    AGENTS.md §9 Housekeeping:功能改进才写 changeset,纯 bug 修复不写。本单是纯修复。


    Generated by Claude Code

  4. baozhoutao commented on Aug 7, 2026

    @baozhoutao
    ContributorAuthor

    Review: PM loop round 1 — 已实现并推送,PR 待维护者确认后开。

    分支 claude/issue-3566-gridfield-date-input @ 03f733a48(尚未开 PR —— objectui 的 PR 须先经维护者确认)。

    阻塞已自行解除,栈式方案作废

    派发过程中 #3565 已合并进 main(b785a77b3,13:30:32Z),nativeDateValue.ts 现已在 origin/main 上。开发 agent 据此改为直接从 origin/main 开分支,不再叠在那条已删除的 head 分支上 —— 比原计划更干净。pm:blocked 已摘除。

    改动(核对过 diff,非采信自述)

    2 个文件 / +64 −1,无 changeset(AGENTS.md §9:纯修复不写)、无临时产物。

    value={c.type === 'date' ? toDateInputValue(val) : val != null ? String(val) : ''}

    正是 issue 建议的改法:复用 ./nativeDateValue 的 toDateInputValue,没有手搓第二个归一化 helper,也没有在消费侧加宽容回退(AGENTS.md #0.1)。写回侧刻意不配对 —— onChange 仍交出控件自己的 YYYY-MM-DD,与 DateField 同一约定;读写不同基准正是 #3127 doc 里点名的更坏结局。

    证据

    可证伪性先行:把修复行还原后,新测以本 issue 描述的症状精确失败 ——
    AssertionError: expected '' to be '2026-06-17'。恢复后 pnpm exec vitest run packages/fields/ → 64 files / 973 tests passed;连同 plugin-form/(GridField 的消费方)合跑 36 files / 380 tests passed。type-check 干净,lint 0 errors。

    真机实测(framework showcase,objectstack dev --fresh -p 3147 + console :5194):

    • 先证伪了夹具本身 —— stock seed 复现不了:该后端对 date 字段读写两侧都规整成 YYYY-MM-DD(连绕过 API 直写 sqlite 也一样读回 2026-06-17);对照组 datetime 字段则原样回 2026-08-07T16:39:20.389Z。于是用一个改写代理把文档所载的 ISO 形状真正放上线来驱动真实 UI。
    • BEFORE(还原修复行):EXP-2003 编辑表单的 Incurred On 单元格 input.value === '',而 getAttribute('value') === '2026-06-17T00:00:00.000Z' —— 与 issue 描述逐字吻合。(用 EXP-2003 而非 EXP-2001,因为后者审批中 RECORD_LOCKED,压根不渲染编辑按钮。)
    • AFTER:同样的 ISO 线上值,单元格显示 2026/06/17、2026/06/18。
    • 差一天已被显式排除:本地时区 PDT(UTC−7),若按 UTC 午夜重解析会显示 06/16 —— 没有位移。
    • 写回往返也已实测:改成 2026-06-20 后,未经改写的后端与 sqlite 原始行均为 2026-06-20,相邻未触碰行仍是 2026-06-18。

    范围纪律:两条同族面已剥离,未夹带

    • [fields,plugin-form] 内嵌子表把 datetime/time 列折叠成 date 控件:时间分量在编辑时被静默丢弃 #3569(已建,未认领,已入 pm:queue) —— deriveMasterDetail.ts 的 fieldTypeToColumnType 把 datetime/time 一并折叠成 date 列,于是 datetime 子表字段拿到 input[type=date]:时间分量在界面上不存在,且 onChange 交出 YYYY-MM-DD 时被静默丢弃。这比本单更重(是数据丢失,不只是显示),但修复要跨 plugin-form 并扩 GridColumn['type'] 联合类型,属另一单。本修复不会让它更差:datetime 列由「空白」变为「显示日历日」。
    • 只读展示面(displayText() :277 与只读表 :632 的 String(...))确认同源,故意未修并入 [fields,plugin-form] 内嵌子表把 datetime/time 列折叠成 date 控件:时间分量在编辑时被静默丢弃 #3569 —— 在类型折叠解开之前,GridField 无从分辨该显示「日」还是「日+时间」,按日渲染会把 datetime 的时间从展示里抹掉,等于拿一个信息丢失换一个显示瑕疵。
    • objectstack/framework#6328(已建,未认领,未加任何 pm:* 标签) —— 上游 showcase 缺少能长期把 ISO 形状摆在 date 型网格列前面的夹具;按客位纪律只报告、不入对方队列、不擅改其 seed 数据。

    Generated by Claude Code

  5. added 2 commits that reference this issue on Aug 10, 2026
    918888a
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