Repository navigation
fix(fields): a numeric formula result reads as a number, and the datetime cell's text separates date and time (objectui#11683) - #11747
Merged
objectstack-fleet[bot] merged 2 commits intoOct 6, 2026
Conversation
…atetime cell's text separates date and time (objectui#11683) FormulaCellRenderer printed every result as raw monospace text. A result declared `returnType: 'number'`, or a JS number from a formula declaring no `returnType`, is now drawn by NumberCellRenderer (formatNumberFieldValue: the locale's grouping and the width resolveFieldScale answers). `summary` shares the renderer. No type is inferred from the expression. DateTimeCellRenderer's compact face separated the date and the time with an `ml-2` margin only, so its text ran the halves together. A text space now sits between them (the compact face's own joiner), with `ml-1` on the time. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
…urnType The quoted sentence is the spec's field-form help text for `returnType`, not its schema describe text. Comment only. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Contributor
✅ 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
|
objectstack-fleet
Bot
deleted the
claude/issue-11683-formula-datetime-cells
branch
October 6, 2026 21:28
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #11683
Clause-②: no
Two cell renderers in
packages/fields/src/index.tsxdrew text that read wrong. Both were found by the 2026-10-06 showcase dogfood (objectui#11672, items 10 and 16). Implemented by sessionsession_01CGZy1BGCjdN5cXqL9cnvB8(seatdomain:ui#3, round 1).What changed
Formula cell (
FormulaCellRenderer). It printed every result as raw monospace text, so Projects' Budget Remaining (a formula with noreturnType) read200000beside a formatted currency column. A numeric result is now drawn byNumberCellRenderer, whoseformatNumberFieldValueis the one call every number face makes. That gives the locale's grouping, and the widthresolveFieldScaleanswers for the field. The spec appliesscaleto aformulafield ("whose computed result is rounded to it"), so a declaredscaleis honoured. The formula cell's guard is unchanged.Which results count as numbers:
returnType: 'number': the spec's declared value type, read as declared. A numeric string is read as a number here, as the number cell reads one.returnType: the value's own JSON type, and only a JS number. A string of digits from an undeclared formula stays text, because nothing says it is a quantity.returnType(text,boolean,date): drawn exactly as before. A declaration is never overridden by the value.summaryis registered to the same renderer, so a numeric roll-up (it has noreturnType) is drawn by the number cell too. Every face that dispatches throughgetCellRendererchanges with it: grid, record detail, related lists, cards, reports, dashboard record fields and the approvals summary card.Datetime cell (
DateTimeCellRenderer). On the compact face, which is the default, the date and the time were two spans separated by anml-2margin alone. So the text a copy takes, a screen reader reads andtextContentreturns ran the halves together:2026/10/6上午1:42in zh-CN,10/6/20261:42 amin en-US. A text space now sits between the two spans. The cell's text is therefore exactly the stringformatDateTime(value, { style: 'compact' })returns, because that function joins the same two halves with one space. The time span keeps its muted colour; its margin narrows fromml-2toml-1beside the space, so the halves stay visibly apart.packages/core/src/utils/date-display.tsis not touched (PR objectui#11729 holds it).No export, prop, language-pack key or spec key is added.
The reading the card asked for: do the formula's inputs give a better type?
Not one this renderer may use. Read on the installed
@objectstack/spec17.6.0:FieldSchema.returnTypeisnumber | text | boolean | date, with no currency value.returnTypethis help text: "Declared value type of the formula, stamped from the inferred CEL type. Consumers read it instead of re-parsing the expression."Budget Remaining's expression subtracts two
currencyfields, so it IS money. But getting "currency" from that means parsing the CEL expression and type-checking it against the object's fields in the renderer. That is the re-parse the contract tells consumers not to do, and it would be a second inference engine beside the authoring one (ObjectFieldInspectorstampsreturnTypefrom the inferred CEL type). So an untyped numeric formula renders as a grouped number, not as money. Showing it as money needs the spec to let a formula declare that, which is a decision for the spec and is left out of this PR (see Acceptance notes).Pins (en-US and zh-CN, measured on
textContent)New:
packages/fields/src/__tests__/formulaDatetimeCells.copiedText-11683.test.tsxformatDateTime(INSTANT, { style: 'compact', locale })and the literals10/6/2026 1:42 am/2026/10/6 上午1:42, and is not the run-together text;format: 'short'reaches the same face; the time span stays muted.200000reads200,000and equals the number cell's face for the same value; no monospace; de-DE reads200.000(en-US and zh-CN group alike, so this is the case that proves the locale reaches the formatter); a fraction keeps its natural precision; asummaryroll-up8900reads8,900;returnType: 'number'(also over a numeric string) is the number face; a declaredscale: 2reads200,000.00. Two controls stay text: a string of digits with noreturnType, andreturnType: 'text'over a number.Updated (each pinned the run-together text as the expected face):
datetime-compact-style-7443.test.tsx(the joined face now carries the space; the offset class pin readsml-1),datetimeCell.formatVocabulary-8853.test.tsx(compactCellTextjoins with a space;9/11/2026 9:30 am),datetime-carriers.impossibleDay-10301.test.tsx(2/28/2026 10:00 am).Measured
The full runs are on
f2fa584. The head isa4a2375, whose only change fromf2fa584rewords one comment inFormulaCellRenderer(4 lines, all comment). Ona4a2375the six formula/datetime pin files were re-run (Test Files 6 passed (6),Tests 191 passed (191): the new file, the three updated ones,emptinessFloorExtensions-8496andcellRenderers.objectLiteral-8596), the type-check was re-run (exit 0), and the light gates and eslint onindex.tsxwere re-run (each exit 0).f2fa584pnpm exec vitest run packages/fields/Test Files 237 passed, 1 skipped (238),Tests 3714 passed, 7 skipped (3721)pnpm exec vitest run --maxWorkers=2over 42 consumer files (below)Test Files 42 passed (42),Tests 522 passed (522)pnpm --workspace-concurrency=2 --filter '@object-ui/fields^...' buildpnpm --filter @object-ui/fields type-checktsc --noEmit && tsc -p tsconfig.test.json, exit 0;--listFilesOnlyon the test program lists all four changed test filespnpm exec eslinton the 5 changed filespnpm check:control-bytes/check:test-path-roots/check:changeset-claims/check:pending-changeset-literals/check:new-line-citations/check:vi-mock-specifiers/check:comment-mask-corpusnode scripts/check-changeset-presence.mjs/-no-major/-fixed/-overwriteConsumer selection: every test file under
packages/plugin-{grid,detail,list,kanban,dashboard,report,tree}andapps/console/srcthat namesDateTimeCellRenderer,FormulaCellRendereror adatetime/formula/summaryfield type (agit grep -lover those trees). That is a declared narrowing: the rest of the farm is CI's. Heavy runs went throughos-verify-lock.sh; the seconds are shared-box seconds.Ablation (through
ablation-replace.mjs, which landed each mutation on disk and proved each restore: blob equals HEAD86e762b7a312,git diff HEADempty), over the new pin file plus the three updated ones:if (returnType === 'number' || …replaced byif (false)):15 failed, 83 passed. Exactly the 15 formula number cases went red; the two text controls and every datetime case stayed green.ml-2):20 failed, 78 passedacross all 4 files. Every datetime text pin went red; every formula case stayed green.Acceptance notes
Read and left alone, outside this card's file surface (reported to the seat, not filed here):
widgets/FormulaField.tsx, the form face) still prints200000(noreturnType) and200000.00(returnType: 'number', atoFixed(2)), monospace, in en-US and zh-CN, where the cell now reads200,000. Measured with a throwaway probe (never committed) onf2fa584. One stored value, two faces.returnTypeofbooleanordate: it printstrueand2026-07-04in monospace, where the widget printsYesandJul 4. Same probe. Which date face a cell should use (the date cell's relative default orformatDate's default) is not pinned anywhere, so it was not guessed here.returnTypetoday (see the reading above).Generated by Claude Code