Skip to content

feat(datetime): add additional options for vertical scroll, range selection, and new month/year view#31255

Draft
OS-susmitabhowmik wants to merge 25 commits into
nextfrom
ROU-12867-POC-ion-datetime
Draft

feat(datetime): add additional options for vertical scroll, range selection, and new month/year view#31255
OS-susmitabhowmik wants to merge 25 commits into
nextfrom
ROU-12867-POC-ion-datetime

Conversation

@OS-susmitabhowmik

@OS-susmitabhowmik OS-susmitabhowmik commented Jun 30, 2026

Copy link
Copy Markdown

Issue number: resolves internal


What is the current behavior?

What is the new behavior?

This PR is a proof of concept for adding the following features to the ion-datetime component:

  • Vertical scroll rather than arrows for navigation
  • Date range selection
  • An updated month/year view

Does this introduce a breaking change?

  • Yes
  • No

Other information

OS-susmitabhowmik and others added 8 commits June 30, 2026 10:20
…and monthYearPickerView="grid"

Introduces three new opt-in props to ion-datetime:

selectionMode="range"
- First tap sets the range start; second tap commits the range and
  emits ionChange with a two-element ISO array [start, end]
- Second tap before first auto-swaps if needed so start is always earlier
- Third tap resets and starts a new range
- Deprecates the `multiple` boolean in favour of selectionMode="multiple"
- Range state (isRangeStart, isInRange, isRangeEnd) flows through
  getCalendarDayState() and is exposed as CSS classes on day buttons and
  ::before pseudo-element track on their wrapper cells

monthNavigation="scroll"
- Replaces the horizontal 3-month swipe window with a free-scrolling
  vertical list of ~13 months (+-6 from the working month, clamped to
  min/max)
- Each month card renders its own heading and days-of-week row inline
- A debounced scroll listener updates workingParts and the visually-
  hidden aria-live region as the user scrolls
- Arrow buttons are hidden in this mode (display: none)

monthYearPickerView="grid"
- Replaces the wheel picker overlay with a month name grid (3 col x 4
  row) and a paginated year grid (4 col x 6 row, 24 years per page)
- Year pages are navigated with prev/next arrow buttons; the page resets
  to the one containing the working year each time the picker opens
- Also applied to month/month-year/year presentations which previously
  hardcoded the wheel picker regardless of the prop

Adds unit tests for the new range state logic and E2E test files with
index.html pages for all three features.
Remove destructured { page } from test callbacks in the three new
datetime E2E test files where page was declared but never referenced
in the test body, resolving TypeScript "declared but its value is never
read" errors that were breaking the build.
Fix four issues found via CI:

- selectionMode="range": first click was falling through to confirm()
  and emitting ionChange with a partial { start } range. Add an early
  return so ionChange only fires after the second click when both start
  and end are committed.

- month-navigation scroll mode tests: .calendar-month-year is not
  rendered in scroll mode (minimal header only); switch programmatic
  nextMonth/prevMonth tests to use .calendar-month-year-announce.

- month-year-grid month cell selector: the year grid element carries
  both month-year-grid and month-year-grid-years classes, so
  .month-year-grid .month-year-grid-cell was matching year cells too
  (19 instead of 12). Switch all month-cell locators to
  [aria-label="Select month"] .month-year-grid-cell.

- month-year-grid year cells test: toHaveCount(expect.any(Number)) is
  not valid Playwright API; replace with a plain count assertion.
@vercel

vercel Bot commented Jun 30, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
ionic-framework Ready Ready Preview, Comment Jul 3, 2026 12:44am

Request Review

@github-actions github-actions Bot added package: core @ionic/core package package: angular @ionic/angular package package: vue @ionic/vue package package: react @ionic/react package labels Jun 30, 2026
- Add `titleSelectedDatesFormatter` support in range mode inside
  `getHeaderSelectedDateText()`, with try/catch fallback to default format

- Add `getRangeAriaLabel()` module-level helper and wire it into calendar
  day buttons so range start/end/in-range days carry descriptive aria-labels

- Add `calendar-range-announce` visually-hidden aria-live region that
  prompts screen readers to "Select an end date" after a start is picked

- Add `role="dialog"` / `aria-modal` / `aria-label` on the month/year
  grid overlay when open

- Add `handleGridKeyDown(ev, cols)` for arrow-key navigation within the
  month and year grids (skips disabled cells, respects edges)

- Add `@Watch('showMonthAndYear')` focus management: moves focus into
  the first enabled grid cell on open, returns focus to the toggle
  button on close

- Add `monthYearButtonRef` to capture the toggle button element

- Reset `activeParts` and re-align year page in `reset()` for range +
  grid picker modes

- Expand range/index.html with showDefaultTitle + titleSelectedDatesFormatter
  and highlightedDates test sections

- Expand show-adjacent-days/index.html with range + adjacent-day cases

- Expand month-year-grid/index.html with a monthValues variant

- Remove flaky "grid picker open" visual regression test (md-rtl snapshot
  diff of 391px)
…es and add grid to presentation test page

- renderMonthYearGrid now accepts showMonths/showYears flags so month presentation shows only the month grid and year presentation shows only the year grid
- presentation test page now renders both wheel and grid datetimes side by side, both responding to the presentation selector
…ns and segmented overlay

- Replace the single month-year toggle button with two separate buttons
  in the calendar header when monthYearPickerView="grid": a month name
  button (e.g. "June") and a year button (e.g. "2022"), each opening the
  grid overlay directly to their respective panel

- Add gridPickerActiveTab state ('month' | 'year') to track which panel
  is active; openGridPicker(tab) sets the tab and opens the overlay

- The grid overlay shows only the active panel (month grid or year grid)
  based on which header button was tapped

- Add segmented control to presentation="month-year" with
  monthYearPickerView="grid": segment buttons show the selected month
  name and year, switching between the month and year grids inline
  without closing (inline=true skips toggleMonthAndYearView on cell click)

- Guard @watch('showMonthAndYear') focus management to only fire for
  date/date-time/time-date presentations (overlay path); month/year/
  month-year inline presentations are excluded

- Add getMonthName() helper to format.ts for localized long month name

- Active grid cell styling: color: current-color(base) on both iOS and
  MD, matching the primary-color text style of the wheel picker

- Remove Grid - Month/Year Presentation test case from month-year-grid
  test page
…-day navigation

- Fix scroll position restore to use scrollWindowCenter instead of stale
  workingParts (setWorkingParts is deferred in writeTask so workingParts
  is not yet updated when componentDidRender runs)
- Capture the visual offset into the current month before the DOM re-render
  and re-apply it after, so varying month heights (4/5/6-week rows) no
  longer cause a visible positional jump on window re-centre
- Set scrollTop synchronously in componentDidRender (not via writeTask/rAF)
  so the restore happens before the browser paints
- Skip animateToDate in scroll mode for adjacent-day clicks and processValue
  since the target month is already visible in the continuous list
- Guard scrollWindowCenter updates in processValue to only fire when the
  month/year actually changes, preventing scroll jumps on day/time changes
- Add scroll mode examples to show-adjacent-days and first-day-of-week test pages
- Add 'single' as a valid selectionMode value ('single' | 'multiple' | 'range')
- Add startDateLabel and endDateLabel props for customizable range placeholder text
- Update showDefaultTitle header in range mode: empty state shows "Start date – End date",
  partial state shows "Oct 1 – End date" instead of the formatted working date
- Add range-start, range-end, and range-selection shadow parts to calendar day buttons
- Add --range-background CSS variable for the range track background color
- Disable the confirm button in range mode until both start and end are selected
- Reset partial range activeParts when cancel() is called in range mode
- Fix: single-element array value in range mode is now a valid partial range (start only);
  processValue maps it to { start } instead of falling through to the generic array branch
- Fix: range value warning threshold changed from !== 2 to > 2 so [startDate] is accepted
- Add test cases to test/range/index.html covering all new features
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: react @ionic/react package package: vue @ionic/vue package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants