Skip to content

Calendar extension proposal: compare-mode, presets, time picker, formatted trigger labels (DatePicker scope) #236

@murdore

Description

@murdore

Context

Lighthouse currently ships a 600+ line project datePicker component used at 30 callsites. The library Calendar is the natural target for migration, but Calendar today supports only mode='single' and mode='range'. To migrate cleanly we need feature parity on four axes that Calendar lacks:

  1. Compare-mode — dual-range selection where the user picks a primary range AND a comparison range (e.g. "Last 7 days vs previous 7 days"). Drives all analytics filter bars.
  2. Presets — a sidebar/dropdown of common ranges (Today, Yesterday, Last 7/30/90 days, This/Last month, Custom) with the active preset visually pinned. Some products also need comparison-presets ("Previous period", "Same period last year").
  3. Time picker — when the consumer wants the range to include hours (e.g. abandoned-checkout cohort selection), a HH:MM AM/PM input that binds to the same value.
  4. Descriptive labels — formatted output for the trigger button ("Jun 1 – Jun 30, 2026", "Compared to: May 1 – May 30") the consumer can render in the closed-state UI.

Question for maintainer

I want to scope this against GUIDELINES.md before authoring. Two possible shapes — preference?

Shape A — extend Calendar itself with optional props:

  • presets?: Array<{ label: string, getRange(): { start: Date, end: Date } }> — consumer-supplied, library renders a sidebar
  • compareMode?: boolean + a second compareRangeStart/End bindable pair, OR a new mode='compare'
  • time?: { start: string, end: string } + ontimechange event when mode allows
  • triggerLabel?: Snippet<[{ start, end, compareStart?, compareEnd?, presetKey? }]> for the formatted label

Shape B — a new DatePicker primitive that composes Calendar + adds the popover/trigger/preset surface, leaving Calendar slim:

  • DatePicker owns the trigger button, popover positioning, preset list, time inputs
  • Internally renders the existing Calendar for the grid
  • The trigger label snippet is part of DatePicker's API, not Calendar's

I'd lean towards (B) — it keeps Calendar philosophically clean (it's a grid, not a popover) and matches the Button + Popover + Calendar composition pattern the library already validates with RangeSelect (recipe ships in #235). But (A) keeps the API surface smaller.

Which shape would you accept, and is there anything about the preset / time / compare-mode semantics you'd want shaped differently before I open the PR?

Why this matters

This is the single biggest blocker to Lighthouse fully migrating off project-owned UI primitives. 30 callsites × 4 missing features is the gap between "library extension PR" and "rewrite each callsite to compose smaller primitives", and the first is dramatically less risky if the library is willing to absorb the feature surface.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions