Skip to content

Commit 8976ea1

Browse files
huangyiireneclaude
andauthored
docs(spec): teach DatasetMeasureSchema.format what a date measure can say (#16515)
`format` was silent about date-valued measures while its JSDoc advertised `e.g. "$0,0.00", "0.0%"` — exactly the numeral/date pattern grammar a date measure cannot read. An author with a `min`/`max` over a date field read that line, wrote `format: 'YYYY-MM-DD'`, parsed clean and got the locale default. Carried by a `.describe()` where there was none, so the statement reaches the published JSON Schema and the reference table in `content/docs/references/ui/dataset.mdx`, whose Description cell for `format` was rendering the silence as a blank. The docblock above carries the longer measured record and its objectui read points. Measured at the objectui pin this repo builds against rather than inherited: a date-only value reads `format` as a display STYLE (`short`, `relative`), a datetime value ignores `format` altogether, and no value reads a date PATTERN. Nothing accepts or rejects differently — `format` stays `z.string().optional()`. Generated artifact regenerated with `pnpm --filter @objectstack/spec gen:schema && pnpm --filter @objectstack/spec gen:docs`. Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6538b68 commit 8976ea1

3 files changed

Lines changed: 48 additions & 4 deletions

File tree

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
`DatasetMeasureSchema.format` now documents what a DATE-valued measure can and cannot say, and the numeral-pattern examples no longer stand as the whole story.
6+
7+
The field was silent about date measures while advertising `e.g. "$0,0.00", "0.0%"` — the pattern grammar a date measure is precisely unable to read. An author with a `min` / `max` over a date field read that line, wrote `format: 'YYYY-MM-DD'`, parsed clean, and got the locale default.
8+
9+
The statement is carried by a `.describe()` where there was none, so it reaches the published surfaces an author actually reads: the generated JSON Schema (`json-schema/ui/Dataset.json`, `DatasetMeasure.json`) and the reference table in `content/docs/references/ui/dataset.mdx`, whose Description cell for `format` had been rendering the silence as a blank. The docblock above it carries the longer measured record.
10+
11+
What it now says, measured rather than assumed against the objectui pin this repo builds against: a numeral pattern applies to a numeric measure; a date-valued measure never reads a date PATTERN — a date-only value reads `format` as a display STYLE (`short`, `relative`), and a datetime value ignores `format` altogether.
12+
13+
Nothing accepts or rejects differently: `format` remains `z.string().optional()` and no measure is refused. Documentation over a published schema (objectui#7178 ruled A).

content/docs/references/ui/dataset.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ const result = DatasetSchema.parse(data);
8484
| **aggregate** | `Enum<'count' \| 'sum' \| 'avg' \| 'min' \| 'max' \| 'count_distinct'>` | optional | Aggregation (sum/avg/count/...); omit when `derived` is set |
8585
| **field** | `string` | optional | Aggregated field; optional for count(*) |
8686
| **filter** | `any` | optional | |
87-
| **format** | `string` | optional | |
87+
| **format** | `string` | optional | Numeral pattern for a NUMERIC measure — grouping, decimals, percent; e.g. "0,0.00", "0.0%". An amount takes its symbol from `currency`, not from a "$" in the pattern. A DATE-valued measure never reads a date pattern: `"YYYY-MM-DD"` renders the locale default. A date-only value reads `format` as a display style (`short`, `relative`); a datetime value ignores it. |
8888
| **currency** | `string` | optional | Display currency code (ISO 4217) |
8989
| **derived** | `{ op: Enum<'ratio' \| 'sum' \| 'difference' \| 'product'>; of: string[] }` | optional | |
9090

@@ -125,7 +125,7 @@ const result = DatasetSchema.parse(data);
125125
| **aggregate** | `Enum<'count' \| 'sum' \| 'avg' \| 'min' \| 'max' \| 'count_distinct'>` | optional | Aggregation (sum/avg/count/...); omit when `derived` is set |
126126
| **field** | `string` | optional | Aggregated field; optional for count(*) |
127127
| **filter** | `any` | optional | |
128-
| **format** | `string` | optional | |
128+
| **format** | `string` | optional | Numeral pattern for a NUMERIC measure — grouping, decimals, percent; e.g. "0,0.00", "0.0%". An amount takes its symbol from `currency`, not from a "$" in the pattern. A DATE-valued measure never reads a date pattern: `"YYYY-MM-DD"` renders the locale default. A date-only value reads `format` as a display style (`short`, `relative`); a datetime value ignores it. |
129129
| **currency** | `string` | optional | Display currency code (ISO 4217) |
130130
| **derived** | `{ op: Enum<'ratio' \| 'sum' \| 'difference' \| 'product'>; of: string[] }` | optional | |
131131

packages/spec/src/ui/dataset.zod.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,39 @@ export const DatasetMeasureSchema = lazySchema(() => strictObject({
188188
field: z.string().optional().describe('Aggregated field; optional for count(*)'),
189189
/** Measure-scoped filter (e.g. only won deals for "won_amount"). */
190190
filter: FilterConditionSchema.optional(),
191-
/** Display format, e.g. "$0,0.00", "0.0%". */
192-
format: z.string().optional(),
191+
/**
192+
* Display format — a NUMERAL pattern controlling grouping, decimals and
193+
* percent: `"0,0.00"`, `"0.0%"`. A `$` in the pattern is still honoured as a
194+
* legacy literal, but a real amount takes its symbol from `currency` below,
195+
* never from the pattern — see that field's note.
196+
*
197+
* A DATE-valued measure (`min` / `max` over a date field) never reads a date
198+
* PATTERN here: `"YYYY-MM-DD"` is accepted by this schema, reaches the
199+
* renderer, and produces the locale default. The shared date path takes a
200+
* named STYLE instead, so a date-only value reads `format` as `short`
201+
* (`Jul 4, '24`) or `relative` (`3 days ago` inside a ±7-day window, the
202+
* absolute locale form outside it) — the same two words `DateCellRenderer`
203+
* honours from `field.format` — while a DATETIME value ignores `format`
204+
* altogether.
205+
*
206+
* Measured at the pin this repo builds against (`.objectui-sha` =
207+
* `a472b0716`; re-derived at that pin 2026-09-07) in objectui
208+
* `packages/core/src/utils/dataset-format.ts`: `formatMeasure` routes a
209+
* non-numeric value through `formatMeasureDate` (`:184-197`), whose
210+
* date-only arm threads `format` into the STYLE parameter of `formatDate`
211+
* (`utils/date-display.ts:104-137`, whose `relative` branch falls back to
212+
* the absolute form beyond ±7 days at `:90`), while its datetime arm calls
213+
* `formatDateTime(v, { locale })` with no style at all (`:194`). Teaching
214+
* the shared path a pattern grammar would change every list cell that reads
215+
* it, so the gap is DOCUMENTED here rather than closed (objectui#7178 ruled
216+
* A; the datetime half is objectui#7443).
217+
*/
218+
format: z.string().optional().describe(
219+
'Numeral pattern for a NUMERIC measure — grouping, decimals, percent; e.g. "0,0.00", "0.0%". '
220+
+ 'An amount takes its symbol from `currency`, not from a "$" in the pattern. A DATE-valued '
221+
+ 'measure never reads a date pattern: `"YYYY-MM-DD"` renders the locale default. A date-only '
222+
+ 'value reads `format` as a display style (`short`, `relative`); a datetime value ignores it.',
223+
),
193224
/**
194225
* Display currency (ISO 4217, e.g. "USD", "CNY"). Carried onto the result
195226
* field so presentations render a locale-correct symbol via `Intl` rather

0 commit comments

Comments
 (0)