Skip to content

fix(fields): 内嵌子表 date 列回显存量 ISO 值 (#3566) - #3718

Draft
baozhoutao wants to merge 1 commit into
mainfrom
claude/issue-3566-gridfield-date-input
Draft

fix(fields): 内嵌子表 date 列回显存量 ISO 值 (#3566)#3718
baozhoutao wants to merge 1 commit into
mainfrom
claude/issue-3566-gridfield-date-input

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #3566

#3127 的同族缺口,落在子表面上。

缺陷

GridField 自己手搓单元格输入,不走 DateField,所以 #3127 的修复够不着它。date 列拿到 API 的 ISO 形状时,String(val) 把它原样喂进 <input type="date">

el.getAttribute('value')  // "2026-06-17T00:00:00.000Z"  ← 记录里的值
el.value                  // ""                          ← 用户看到的

该控件只接受 YYYY-MM-DD,其余形状静默拒收 —— 属性照样落进 DOM,input.value 读回空串,于是有值的行画出空占位

改法

复用 #3127 落地的 toDateInputValue不新增第二个日期归一化 helper

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

它对开头是 YYYY-MM-DD 的串原样透传,避免 date-only 被 new Date() 按 UTC 午夜解析后、再读本地日期分量而在格林威治以西整体差一天。

写回侧刻意不配对onChange 仍交出控件自己的 plain YYYY-MM-DD —— 与 DateField 同一约定,读写保持同一基准。

验证

单测(先证伪):把修复行还原后,新测以本缺陷的精确症状失败 ——

FAIL packages/fields/src/widgets/GridField.test.tsx > date columns echo the stored value (#3566)
AssertionError: expected '' to be '2026-06-17'

恢复后 pnpm exec vitest run packages/fields/64 files / 973 tests passed;连同消费方 plugin-form/ 合跑 36 files / 380 tests passed。新增 4 条:ISO 与裸日期两种形状都渲染 2026-06-17、空值保持空、写回仍是 plain YYYY-MM-DD

真机(framework showcase + 本分支 console dev,对象 showcase_expense_lineincurred_on,走 inlineEdit: 'grid' 的内嵌子表):

EXP-2003 编辑表单的 Incurred On 单元格 attr .value
还原修复行(对照组) 2026-06-17T00:00:00.000Z "" ← 复现缺陷
本分支 2026-06-17 2026/06/17
  • 差一天已显式排除:本地时区 PDT(UTC−7),若按 UTC 午夜重解析会显示 06/16 —— 没有位移。
  • 写回往返:单元格改成 2026-06-20 保存 → 后端与 sqlite 原始行均为 2026-06-20,相邻未触碰行仍是 2026-06-18(未被重新提交或改坏)。

取证说明:该后端对 date 字段读写两侧都会规整成 YYYY-MM-DD(绕过 API 直写 sqlite 亦然),stock seed 复现不了本缺陷;因此用一个改写代理把文档所载的 ISO 形状真正放上线来驱动真实 UI。对照组 datetime 字段接口原样回 2026-08-07T16:39:20.389Z。上游夹具缺口已报 objectstack-ai/objectstack#6328(未擅改其 seed 数据)。

范围

无 changeset(AGENTS.md §9:纯 bug 修复不写)。

同族但故意未修、已拆出 #3569deriveMasterDetail.tsdatetime/time 折叠成 date 列,导致 datetime 子表字段的时间分量在写回时被静默丢弃;只读展示面(displayText()、只读表的 String(...))同源。两者的修复都要先解开类型折叠并跨 plugin-form,在此之前 GridField 无从分辨该显示「日」还是「日+时间」,按日渲染会把 datetime 的时间从展示里抹掉。本 PR 不会让 #3569 更差:datetime 列由「空白」变为「显示日历日」。

🤖 Generated with Claude Code

GridField hand-rolls its own cell input rather than going through
DateField, so objectui#3127's fix did not reach it: a `date` column whose
value carries the API's ISO shape (`2026-06-17T00:00:00.000Z`) fed that
string straight into `<input type="date">`, which accepts only
`YYYY-MM-DD` and rejects everything else SILENTLY — the attribute lands
in the DOM while `input.value` reads back `''`, so the cell painted its
empty placeholder on a row that has a value.

Reuse `toDateInputValue` from #3127 instead of adding a second date
normaliser. It passes a leading `YYYY-MM-DD` through verbatim, so a
date-only value is not re-parsed as UTC midnight and shifted a day
earlier west of Greenwich.

The write side is deliberately unpaired: `onChange` still emits the
control's own plain `YYYY-MM-DD`, which is DateField's contract too, so
read and write stay on one basis.

Fixes #3566

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 8, 2026 2:51am

Request Review

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-BukWa__X.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.66KB 3.13KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 26.07KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.65KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 480.72KB 105.64KB
core (index.js) 2.96KB 1.13KB
create-plugin (index.js) 9.08KB 2.92KB
data-objectstack (index.js) 137.51KB 35.11KB
fields (index.js) 230.90KB 56.84KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.65KB 1.06KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 9.48KB 3.27KB
i18n (useObjectLabel.js) 26.14KB 6.07KB
i18n (useSafeTranslation.js) 4.52KB 1.96KB
layout (index.js) 38.53KB 10.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.98KB 12.37KB
plugin-charts (index.js) 61.04KB 17.31KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 115.50KB 29.96KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 232.79KB 57.42KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 112.10KB 27.10KB
plugin-gantt (index.js) 162.55KB 39.57KB
plugin-grid (index.js) 186.61KB 49.34KB
plugin-kanban (index.js) 48.30KB 13.28KB
plugin-list (index.js) 105.12KB 25.48KB
plugin-map (index.js) 16.81KB 5.24KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.58KB 10.58KB
plugin-timeline (index.js) 25.76KB 7.33KB
plugin-tree (index.js) 8.50KB 2.88KB
plugin-view (index.js) 84.03KB 20.55KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.71KB 1.34KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 2.59KB 1.31KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant