Skip to content

feat: add DateInput, TimeInput, and DateTimeInput components - #617

Closed
mrholek wants to merge 13 commits into
mainfrom
feat/date-input
Closed

feat: add DateInput, TimeInput, and DateTimeInput components#617
mrholek wants to merge 13 commits into
mainfrom
feat/date-input

Conversation

@mrholek

@mrholek mrholek commented Jul 19, 2026

Copy link
Copy Markdown
Member

What

Three new form components with section-based input masks for dates and times, covering the native date, time, and datetime-local inputs with full control over the format — modeled on the MUI X field components (DateField/TimeField/DateTimeField):

  • DateInput (data-coreui-date-input)
  • TimeInput (data-coreui-time-input)
  • DateTimeInput (data-coreui-date-time-input)

Each field is split into sections (day/month/year/hour/minute/second/AM-PM) rendered as role="spinbutton" elements — the accessible DOM structure MUI adopted in v8, mirroring the native date input internals.

Architecture

  • SectionInput — shared, unregistered base class with all interaction logic; the three components are thin subclasses providing defaults and locale-derived layouts. Designed for reuse as the input field of the pickers in a follow-up iteration.
  • util/date-sections.js — pure, DOM-free section engine (format resolution, digit/letter entry state machines, increments, composition to/from Date).

Features

  • Typing UX: digits accumulate while ambiguous and auto-advance when complete (month: 2 → done; 1+3 → restart at 03); arrows increment with wrapping; Backspace/Delete clear; Home/End/arrows navigate; Ctrl+A selects all (then Ctrl+C copies the formatted value, Ctrl+X cuts, typing restarts from the first section); paste fills all sections, including month names and day periods
  • Formats: tokens compatible with dayjs/moment and date-fns/luxon (dd/DD, MM/MMM/MMMM, yy/yyyy, H/HH, h/hh, mm, ss, A/a); text month sections match names typed by prefix (m → March, may → May); inputDateParse plugs in an external date library for pasted text
  • Locale-derived layouts via Intl: section order, separators, hour cycle (h12/h23), month names in the grammatical form of a date context (overridable via monthNames), localized day periods
  • Correctness: day section bounds follow the selected month/year (Feb caps at 28/29, changing the month clamps the day immediately); two-digit years expand with smart century assignment
  • Forms: renders as .form-control (sizing, validation states, and input groups work out of the box), hidden input for submission, valid/invalid options, and .was-validated submit integration (the hidden input is excluded from native constraint validation)
  • Floating labels: supported via a :focus-within/filled-class re-binding (a div never matches :placeholder-shown)

Fixes (separate commits)

  • EventHandler: beforeinput, copy, cut, and paste added to native events — namespaced listeners for them never fired before
  • Calendar util: date-only ISO strings (2026-07-14) parsed as local midnight instead of UTC, fixing an off-by-one-day shift in negative-offset timezones (also affected minDate/maxDate of the pickers)

Testing

  • 76 new unit tests (components + engine + calendar regression); full suite green in four timezones: Europe/Warsaw, America/New_York, Pacific/Auckland, America/Sao_Paulo (including historic midnight-DST transitions)
  • lib build, bundlewatch (budgets adjusted for the new components: ~2.9 kB min gzip for all three), and docs-build pass

Docs

Three new pages under Forms (aligned structure, live examples incl. dayjs integration, keyboard tables, a11y notes) + sidebar entries.

Update: the data API uses bare attributes (data-coreui-date-input etc.) instead of data-coreui-toggle — the chip-input/chip-set convention and the Bootstrap v6 direction for in-place widgets.

mrholek added 4 commits July 19, 2026 02:03
…events

Namespaced listeners for these events (e.g. `paste.coreui.date-input`) were
registered as literal custom event types and never fired; components had to
fall back to un-namespaced names, which dispose() could not clean up.
Date.parse treats "2026-07-14" as UTC midnight, shifting the date one day
back in negative-offset timezones (e.g. `minDate: '2026-07-01'` resolving to
June 30 in New York). Append a time part so date-only ISO strings parse in
the local timezone, consistently with every other supported date format.
Section-based masked fields for dates and times, covering the native date,
time, and datetime-local inputs with full control over the format — modeled
on the MUI X field components.

- Shared SectionInput base (spinbutton per section, roving tabindex, letter
  and digit entry, arrow stepping, select all/copy/cut, paste, validation,
  hidden input for forms) and a pure section engine in util/date-sections.js
- Format tokens compatible with dayjs, moment, date-fns, and luxon (dd/DD,
  MM/MMM/MMMM, yy/yyyy, H/HH, h/hh, mm, ss, A/a), locale-derived layouts via
  Intl (section order, separators, hour cycle, month and day period names),
  and inputDateParse for external date libraries
- Day section bounds follow the selected month and year; text month sections
  match names typed by prefix; two-digit years expand with smart centuries
- Renders as .form-control, so sizing, validation states, and input groups
  work; floating labels supported via a :focus-within/filled re-binding
- Docs pages with live examples, sidebar entries, and full test coverage
In-place widgets auto-init from a bare attribute (data-coreui-date-input,
data-coreui-time-input, data-coreui-date-time-input) instead of
data-coreui-toggle — there is no trigger and nothing to toggle. Matches the
chip-input/chip-set convention and the Bootstrap v6 direction
([data-bs-otp], [data-bs-chips]), which keeps data-bs-toggle for
trigger/overlay components only.
@coveralls

coveralls commented Jul 19, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 29708635725

Coverage decreased (-0.3%) to 94.331%

Details

  • Coverage decreased (-0.3%) from the base build.
  • Patch coverage: 49 uncovered changes across 5 files (604 of 653 lines covered, 92.5%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
js/src/section-input.js 370 335 90.54%
js/src/date-input.js 20 16 80.0%
js/src/date-time-input.js 29 25 86.21%
js/src/time-input.js 30 26 86.67%
js/src/util/date-sections.js 201 199 99.0%
Total (6 files) 653 604 92.5%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 8185
Covered Lines: 7858
Line Coverage: 96.0%
Relevant Branches: 3458
Covered Branches: 3125
Branch Coverage: 90.37%
Branches in Coverage %: Yes
Coverage Strength: 737.64 hits per line

💛 - Coveralls

mrholek added 9 commits July 19, 2026 02:39
Arrow keys on an empty section started at the bounds, which is useless for
the year (ArrowUp gave 0001, ArrowDown 9999). Start every empty section from
the current date and time instead, MUI DateField-style; further arrows step
from there.
Formats without every section work like the native month input — MM.yyyy or
MMMM yyyy reports the first day of the month (missing sections default to
1970-01-01 midnight). The initial date is normalized through the mask too, so
a config date carrying parts the mask doesn't show (day, time) no longer
leaks into getDate() and change events.
Review feedback: boundary starts (ArrowUp -> min, ArrowDown -> max) are
predictable for day, month, and time sections; only the year benefits from
starting at the current year instead of 0001/9999.
Empty sections showed the muted placeholder color, but the separators
between them kept the regular text color, so the DD.MM.YYYY mask rendered
with dark dots. Separators inherit the placeholder color until the field
has a value, matching the native date input.
The docs examples used bare divs, so until the load-event init added the
form-control and form-date-time classes the element rendered unstyled and
then jumped into shape. The examples now carry both classes in the markup,
and .form-date-time holds min-height (with sm/lg overrides), so an
uninitialized container keeps the final size and look.
MUI DateField onError parity: consumers previously only saw date: null with
no way to tell why. The components now emit errorChange whenever the
validation state transitions, with error being 'incomplete', 'minDate',
'maxDate', 'disabledDate', or null when the value is valid or the field is
empty. 'incomplete' does not toggle .is-invalid, matching the existing
behavior.
Focuses the first section right after initialization, like the native
autofocus attribute on an input.
@mrholek

mrholek commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

Shipping in v6 rather than 5.x.

All 13 commits are already on v6-dev — merged there with a real merge commit (8aeb3be31), not cherry-picked, so history stays reconcilable. The components are documented on the preview docs pages and the v6 migration guide records the timepicker: true replacement.

Closing this in favour of the v6 line. The feat/date-input branch stays as the merge-base ancestor of v6-dev.

@mrholek mrholek closed this Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants