Skip to content

feat(datetime): enable range selection - #31581

Open
OS-susmitabhowmik wants to merge 104 commits into
nextfrom
ROU-12704-datepicker-range
Open

OS-susmitabhowmik wants to merge 104 commits into
nextfrom
ROU-12704-datepicker-range

Conversation

@OS-susmitabhowmik

@OS-susmitabhowmik OS-susmitabhowmik commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Issue number: resolves internal


What is the current behavior?

ion-datetime supports selecting a single date or multiple individual dates (via the multiple boolean prop). There is no way to select a contiguous range of dates with a start and end date.

What is the new behavior?

  • Adds a new selectionMode prop with "range" and "multiple" options. Single-date selection remains the default when unset.
  • Range mode (selectionMode="range") allows users to select a start and end date. The first tap sets the start, the second tap sets the end. Tapping again resets the range.
  • Range selection is visualized with pill-shaped start/end days and a colored track connecting them, consistent across ionic, iOS, and MD themes.
  • Today's date retains its blue text color when it falls within a range.
  • Adds startDateLabel and endDateLabel props for customizing header text and screen reader announcements.
  • Adds --range-background CSS custom property for theming the range track color.
  • Adds range-start, range-end, range-selection, and range-track CSS shadow parts.
  • The confirm button is disabled until both start and end dates are selected.
  • ion-datetime-button displays the range as "startDate - endDate".
  • Readonly ranges display normally (not faded); disabled ranges fade the track and pills equally.
  • Deprecates the multiple boolean prop in favor of selectionMode="multiple".
  • Range selection only works with presentation="date" and preferWheel="false".

Does this introduce a breaking change?

  • Yes
  • No

The multiple prop continues to work but is deprecated. A console warning is logged when used.

Other information

OS-susmitabhowmik and others added 13 commits October 13, 2026 18:18
Introduces the internal type that represents a date range as a start/end
pair, used by activeParts when selectionMode="range" is enabled.
Adds the selectionMode property with 'multiple' and 'range' values,
along with startDateLabel and endDateLabel props for range header
placeholders. Deprecates the multiple boolean in favor of
selectionMode="multiple" with backward compatibility. Includes
validation warnings for misconfigured range/multiple usage.
Extends getCalendarDayState with an optional rangeParts parameter that
returns isRangeStart, isInRange, and isRangeEnd flags. These drive the
CSS class names and shadow parts applied to each calendar day wrapper.
Adds the core range selection behavior: setActivePartsRange handles
start/end date picking with disabled-date validation, confirm suppresses
emission until both dates are set, cancel clears partial ranges, reset
clears and optionally navigates, and processValue normalizes programmatic
range values (reversed pairs, >2 entries). Updates type narrowing across
the component to accommodate the DatetimeRangeParts state shape.
Adds range CSS classes (calendar-day-wrapper-range-start, -in-range,
-range-end) and shadow parts (range-start, range-end, range-selection,
range-track) to the calendar day wrappers and buttons. Range styling
takes priority over highlightedDates. Adds screen reader announcements
for start/end dates using startDateLabel and endDateLabel props.
Adds ::before pseudo-element on .calendar-day-wrapper to draw the
colored background track between range start and end. Introduces the
--range-background CSS variable for customization. Adds range day
styling across all three themes (ios, md, ionic) with appropriate
colors for start/end buttons and in-range days.
Updates the datetime header to show range text (e.g. "Oct 1 - Oct 5")
using startDateLabel/endDateLabel as placeholders for empty slots.
Supports titleSelectedDatesFormatter in range mode. Updates
ion-datetime-button to display "startDate - endDate" format when
selectionMode="range" is active.
The confirm button is now disabled while the range selection is
incomplete (missing an end date), preventing users from committing
a half-finished range.
Adds two interactive test pages for manual visual testing of the date
range feature across all themes and modes:

- range/index.html: covers basic range, programmatic values, single-day
  range, header, custom labels, buttons, disabled dates, min/max,
  highlighted dates, custom formatter, adjacent days, and readonly.
- range-styling/index.html: covers --range-background CSS variable,
  range-start/range-end/range-track shadow parts, custom track height,
  color prop, and a range spanning today.
Updates the range highlight track to match the Figma reference:
- Track height matches the day button height (vertically centered)
  instead of filling the full wrapper
- In-range days no longer show a circle background, only the track
  behind them is visible
- Adds --range-track-height CSS variable per theme (ios: 40px,
  md: 42px, ionic: scale-1200)
- Range start/end keep their selected day circle appearance
- Fix range track not visible by adding z-index: 0 stacking context on
  calendar-day-wrapper so the ::before track renders above the host background
- Fix press/hover states on range days being overridden by the in-range
  background: none rule due to CSS specificity
- Fix --focus-ring-width using wrong token ($ion-border-radius-025 →
  $ion-border-size-050) to match Figma border-size/050 spec
- Add disabled overlay (rgba white 60%) for range days to match Figma
- Remove invalid ::part(range-track)::before test case from range-styling
- Align range test pages to match existing test page conventions
Add selectionMode, startDateLabel, and endDateLabel props to the
public API manifest, TypeScript definitions, and Angular/Vue proxies.
@vercel

vercel Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ionic-framework Ready Ready Preview Nov 3, 2026 2:43am UTC

Request Review

@github-actions github-actions Bot added package: core @ionic/core package package: angular @ionic/angular package package: vue @ionic/vue package labels Oct 16, 2026
Remove redundant `activeParts.start` check in confirm() since the type
guard already guarantees start exists (strict-boolean-expressions).
Add isRangeStart, isRangeEnd, isInRange to state.spec.ts expectations
to match the new return shape from getCalendarDayState().
The toolbar-basic-text-icon-buttons ionic/md/rtl/light Chrome snapshot
was inadvertently changed and no longer matches what CI renders.
Reorder properties to satisfy stylelint order/properties-order rule
and replace bare border-radius with the border-radius mixin in the
disabled range overlay.
Ionitron and others added 2 commits October 16, 2026 17:16
Move z-index after min-width/min-height/overflow in calendar-day-wrapper.
Move @include border-radius before declarations in disabled range overlay.
Check that day10 lost range-end (not range-start) to properly verify
the range extension was blocked by the disabled date.
Check that day3 became range-start and day18 lost range-end to
properly verify the old range was cleared when clicking before start.
Lock in the weekday-free header format by matching the exact string
instead of loose regex patterns.
The visual regression block at the top already runs both directions,
so the separate RTL test was taking the same screenshot under a
different name. Remove the test and its snapshots.
Move the event detail assertion into "clicking two days should select
a range" and remove the duplicate standalone test.
Verify that a range spanning June to July correctly marks days in
both months, catching date comparison bugs across month boundaries.
Only show the range track and wrapper-range-start class when the
range has an end date, matching MD2 where the first tap shows only
the circle and the track appears once the end is picked.
Add test that confirm button is disabled with only a start date
and becomes enabled once the end date is selected.
Add test that reset('2022-12-01') clears the range selection and
navigates the calendar to December 2022.
Add aria-live region that announces "select end date" after picking a
start date, and add "in range" to the aria-label of days between the
start and end. Both per the design doc's accessibility requirements.
Add aria-live region that announces "select end date" after picking a
start date, derived from rangeParts in the render method. Add "in range"
to the aria-label of days between the start and end. Uses inline styles
for the visually-hidden live region.
Remove implementation detail (::before) from the part description
to keep it as documented API rather than internal detail.
… element

After navigating to March, Jan 25 is no longer in the DOM. Assert
only on the visible Mar 5 element instead.
Use .calendar-month:nth-child(2) to target the visible June 27
element, avoiding strict mode violation when the same day exists
in multiple rendered months.
Check the month-year toggle text instead of element visibility to
avoid flaky element-not-found errors in CI.
When processValue handles a complete range, return early without
navigating. The workingParts comparison was unreliable due to
animation timing, causing the calendar to snap back to the start
month in CI.
…etion

Add rangeCompletedInteractively flag set in setActivePartsRange and
consumed in processValue. This reliably distinguishes interactive
range completion (stay on current month) from programmatic value
changes (navigate to start month), avoiding animation timing issues.
Use the fixture helper to set the value as an array instead of a
string attribute, ensuring the calendar opens on the correct month
in range mode.
Use RANGE_VALUE to open on June 2022, then click before the start
to begin a new range across months. Avoids partial range completion
on first click and clock pinning issues.

This branch was successfully deployed

1 active deployment
Preview — 9a5de97e Deployed Nov 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: angular @ionic/angular package package: core @ionic/core package package: vue @ionic/vue package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants