Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions .changeset/7443-datetime-compact-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,22 @@ The cell now reads `field.format` (it destructured `value` only, so a
`datetime` field could not reach the style vocabulary a `date` field has) and
renders through the shared function, and `data-table`'s `formatCellValue`
calls `formatDateTime` instead of a third, independently authored option bag.
Every existing cell renders byte-identically; `'compact'` is today's face
named and rehoused, not a new one.
Every existing cell without an authored `format`, and every cell authoring
`'compact'`, renders byte-identically; `'compact'` is today's face named and
rehoused, not a new one. A `datetime` field that authors any OTHER non-empty
`format` does change: the cell previously ignored `field` altogether and always
painted the compact face, and now anything other than `'compact'` selects the
verbose `formatDateTime` default — measured as `Jul 4, 2024, 07:00 AM` in
`en-US` for the instant whose compact face is `7/4/2024 7:00 am`. An
unrecognised value is neither rejected nor passed through; it silently lands on
that verbose face. No `datetime` field in this repository authors a `format`, so
no cell here moves — a consumer that authored one is the case this sentence is
for. Note that `format` has no declared value vocabulary to check a value
against: `@object-ui/types` types it `format?: string`, and `@objectstack/spec`
carries one free-form `format?: string` on its shared field schema, described
"Format string (e.g. email, phone)" and accepting any string. `'compact'` is
therefore the only value with a defined `datetime` meaning, and every other
value means "the verbose face" by fallthrough rather than by design.

Additive, no signature change: `formatDateTime(value, options?)` is unchanged
and `formatDateTime(v, { locale })` keeps meaning what it meant.
Expand Down
19 changes: 19 additions & 0 deletions .changeset/7747-datetime-format-cast-narrowing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
---

Narrow the `DateTimeCellRenderer` style cast from `as any` to the subtype that
actually declares the property (objectui#7747).

`const style = (field as any)?.format || 'compact'` becomes
`(field as DateTimeFieldMetadata | undefined)?.format`. Some cast is
load-bearing — `FieldMetadata` is a 37-member union and `BaseFieldMetadata`
carries no `format`, so the bare read is `TS2339` — but `as any` was wider than
the job: `DateTimeFieldMetadata` is exported from `@object-ui/types`, which this
file already imports from, so the narrow cast was available at zero cost. The
difference that buys: `as any` would also have silenced a typo in the property
name, and the narrow cast will not.

Type-level only; no package is released by this change. The emitted expression
is identical, so every cell renders exactly what it rendered before —
`'compact'` and an absent or empty `format` keep the compact face, and any
other value keeps selecting the verbose `formatDateTime` default.
8 changes: 6 additions & 2 deletions packages/fields/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import React from 'react';
import type { FieldMetadata, SelectOptionMetadata } from '@object-ui/types';
import type { DateTimeFieldMetadata, FieldMetadata, SelectOptionMetadata } from '@object-ui/types';
import { ComponentRegistry, percentDisplayValue, getRecordDisplayName, humanizeLabel, isMissingForRequired, formatDate, formatDateTime, formatDateTimeCompactParts, formatRelativeDate, type ComponentMeta, type DateDisplayOptions } from '@object-ui/core';
// The platform's own value-shape contract, asked rather than restated
// (objectui#6744). See `locationStoredValueSchemaFor` below for why this is a
Expand Down Expand Up @@ -882,7 +882,11 @@ export function DateTimeCellRenderer({ value, field }: CellRendererProps): React
// (objectui#7443). `||`, not `??`, matches the `date` cell and keeps an
// authored empty string on the compact face rather than dropping it into
// the verbose default.
const style = (field as any)?.format || 'compact';
// `FieldMetadata` is a 37-member union and `BaseFieldMetadata` carries no
// `format`, so the bare property read is `TS2339` — SOME cast is load-bearing.
// `DateTimeFieldMetadata` is the narrowest one that carries it (objectui#7747);
// `as any` would also silence a typo in the property name, this does not.
const style = (field as DateTimeFieldMetadata | undefined)?.format || 'compact';

// The compact face is painted in two halves — the time is muted and offset
// — so this branch asks the shared module for the halves rather than the
Expand Down
Loading