From ed88274ecd06afaafa563208f9b70c2210640e4a Mon Sep 17 00:00:00 2001 From: Henry Wilkinson Date: Tue, 16 Jun 2026 17:30:40 -0400 Subject: [PATCH 1/2] Adds no-value rendering to format-date --- .../src/content/components/format-date.mdx | 32 +++++++++++++------ .../components/format-date/format-date.tsx | 30 +++++++++++++---- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/packages/demo/src/content/components/format-date.mdx b/packages/demo/src/content/components/format-date.mdx index f82eb53..c654c97 100644 --- a/packages/demo/src/content/components/format-date.mdx +++ b/packages/demo/src/content/components/format-date.mdx @@ -18,6 +18,8 @@ The FormatDate component renders a date as either **relative** time When showing relative time, a tooltip reveals the absolute time on hover or keyboard focus. +If no date is provided (`null`, `undefined`, or an empty string), the component renders a `---` placeholder instead. + ## Time zones Absolute time is formatted in **UTC by default**. To render in a different zone, pass a `timeZone` (e.g. `"America/New_York"`). Relative time reads the same everywhere. @@ -108,14 +110,26 @@ For a short numeric date like `2026-06-09`, use numeric options. Digit _order_ i /> ``` +## Empty state + +When `date` is missing — `null`, `undefined`, or an empty string — the component renders a `---` placeholder with an accessible "No date" label. This is distinct from passing a present-but-unparseable value (e.g. `"not-a-date"`), which renders `Invalid date`. + + + +```tsx + + + +``` + ## Props -| Name | Description | Type | Default | Required | -| ----------------- | ----------------------------------------------------------------------- | ---------------------------- | ---------- | -------- | -| `date` | The date to display | `string`, `number`, `Date` | — | ✅ | -| `displayAs` | Render relative or absolute time | `relative`, `absolute` | `absolute` | ❌ | -| `timeZone` | Time zone used for absolute formatting | `string` | `UTC` | ❌ | -| `locale` | BCP 47 locale used for formatting | `string` | `en-US` | ❌ | -| `tooltip` | When relative, show a tooltip with the absolute time on hover/focus | `boolean` | `true` | ❌ | -| `live` | When relative, re-render on an interval so the value stays current | `boolean` | `true` | ❌ | -| `absoluteOptions` | Override the `Intl.DateTimeFormat` options used for absolute formatting | `Intl.DateTimeFormatOptions` | — | ❌ | +| Name | Description | Type | Default | Required | +| ----------------- | ----------------------------------------------------------------------- | ---------------------------------- | ---------- | -------- | +| `date` | The date to display | `string`, `number`, `Date`, `null` | — | ✅ | +| `displayAs` | Render relative or absolute time | `relative`, `absolute` | `absolute` | ❌ | +| `timeZone` | Time zone used for absolute formatting | `string` | `UTC` | ❌ | +| `locale` | BCP 47 locale used for formatting | `string` | `en-US` | ❌ | +| `tooltip` | When relative, show a tooltip with the absolute time on hover/focus | `boolean` | `true` | ❌ | +| `live` | When relative, re-render on an interval so the value stays current | `boolean` | `true` | ❌ | +| `absoluteOptions` | Override the `Intl.DateTimeFormat` options used for absolute formatting | `Intl.DateTimeFormatOptions` | — | ❌ | diff --git a/packages/ui/src/components/format-date/format-date.tsx b/packages/ui/src/components/format-date/format-date.tsx index 0b286ab..d5296d1 100644 --- a/packages/ui/src/components/format-date/format-date.tsx +++ b/packages/ui/src/components/format-date/format-date.tsx @@ -15,8 +15,8 @@ export interface FormatDateProps extends Omit< React.TimeHTMLAttributes, 'dateTime' | 'children' > { - /** The date to display. Accepts an ISO 8601 string, epoch milliseconds, or a Date. */ - date: string | number | Date; + /** The date to display. Accepts an ISO 8601 string, epoch milliseconds, or a Date. A missing value (null, undefined, or empty string) renders a placeholder. */ + date: string | number | Date | null | undefined; /** Render relative ("2 weeks ago") or absolute ("Jun 9 2026, 18:42:03 UTC") time. */ displayAs?: FormatDateDisplayMode; /** Time zone used for absolute formatting. Defaults to "UTC". */ @@ -58,7 +58,16 @@ const RELATIVE_DIVISIONS: { amount: number; unit: Intl.RelativeTimeFormatUnit }[ // Re-render relative time so values like "Just now" stay accurate without busy-looping const LIVE_INTERVAL_MS = 30_000; -// Parse any date-like input into a Date, or null if missing/invalid +// Shown when no date is provided, as distinct from a date that fails to parse +const NO_DATE_PLACEHOLDER = '---'; + +// A missing value (null, undefined, or empty/whitespace string) is "no date", +// as opposed to a present-but-unparseable value, which is "Invalid date" +function isMissing(value: string | number | Date | null | undefined): value is null | undefined { + return value == null || (typeof value === 'string' && value.trim() === ''); +} + +// Parse any date-like input into a Date, or null if invalid function toDate(value: string | number | Date): Date | null { const date = value instanceof Date ? value : new Date(value); return Number.isNaN(date.getTime()) ? null : date; @@ -98,7 +107,8 @@ function FormatDate({ className, ...props }: FormatDateProps) { - const parsed = React.useMemo(() => toDate(date), [date]); + const missing = isMissing(date); + const parsed = React.useMemo(() => (isMissing(date) ? null : toDate(date)), [date]); const [now, setNow] = React.useState(() => new Date()); const [mounted, setMounted] = React.useState(false); @@ -112,11 +122,19 @@ function FormatDate({ return () => clearInterval(id); }, [isRelative, live]); + if (missing) { + return ( + + {NO_DATE_PLACEHOLDER} + + ); + } + if (!parsed) { return ( - + ); } From b4af28224ecb254c3758f9c2b40903f544d2e8f8 Mon Sep 17 00:00:00 2001 From: Henry Wilkinson Date: Tue, 16 Jun 2026 17:31:57 -0400 Subject: [PATCH 2/2] Update package.json --- packages/ui/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ui/package.json b/packages/ui/package.json index 611a454..2c35996 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -2,7 +2,7 @@ "name": "@eqtylab/equality", "description": "EQTYLab's component and token-based design system", "homepage": "https://equality.eqtylab.io/", - "version": "2.1.2", + "version": "2.1.3", "license": "Apache-2.0", "keywords": [ "component library",