-
Notifications
You must be signed in to change notification settings - Fork 13.4k
fix(datetime-button): respect datetime constraints in initial text #31218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
f82ea68
ba0ebeb
7d95269
4824122
ef4c1cd
41952e6
be319d6
9d784dd
05d81cb
7d6c32f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,7 +7,6 @@ import { createColorClasses } from '@utils/theme'; | |||||||||||||||||||||||||||||||
| import { getIonMode } from '../../global/ionic-global'; | ||||||||||||||||||||||||||||||||
| import type { Color } from '../../interface'; | ||||||||||||||||||||||||||||||||
| import type { DatetimePresentation } from '../datetime/datetime-interface'; | ||||||||||||||||||||||||||||||||
| import { getToday } from '../datetime/utils/data'; | ||||||||||||||||||||||||||||||||
| import { getLocalizedDateTime, getLocalizedTime } from '../datetime/utils/format'; | ||||||||||||||||||||||||||||||||
| import { getHourCycle } from '../datetime/utils/helpers'; | ||||||||||||||||||||||||||||||||
| import { parseDate } from '../datetime/utils/parse'; | ||||||||||||||||||||||||||||||||
|
|
@@ -125,7 +124,7 @@ export class DatetimeButton implements ComponentInterface { | |||||||||||||||||||||||||||||||
| overlayEl.classList.add('ion-datetime-button-overlay'); | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| componentOnReady(datetimeEl, () => { | ||||||||||||||||||||||||||||||||
| componentOnReady(datetimeEl, async () => { | ||||||||||||||||||||||||||||||||
| const datetimePresentation = (this.datetimePresentation = datetimeEl.presentation || 'date-time'); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||
|
|
@@ -138,7 +137,7 @@ export class DatetimeButton implements ComponentInterface { | |||||||||||||||||||||||||||||||
| * to re-render the displayed | ||||||||||||||||||||||||||||||||
| * text in the buttons. | ||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
| this.setDateTimeText(); | ||||||||||||||||||||||||||||||||
| await this.setDateTimeText(); | ||||||||||||||||||||||||||||||||
|
thetaPC marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||
| addEventListener(datetimeEl, 'ionValueChange', this.setDateTimeText); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||
|
|
@@ -189,7 +188,7 @@ export class DatetimeButton implements ComponentInterface { | |||||||||||||||||||||||||||||||
| * ion-datetime and then format it according | ||||||||||||||||||||||||||||||||
| * to the locale specified on ion-datetime. | ||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
| private setDateTimeText = () => { | ||||||||||||||||||||||||||||||||
| private setDateTimeText = async () => { | ||||||||||||||||||||||||||||||||
|
thetaPC marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||
| const { datetimeEl, datetimePresentation } = this; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (!datetimeEl) { | ||||||||||||||||||||||||||||||||
|
|
@@ -204,7 +203,8 @@ export class DatetimeButton implements ComponentInterface { | |||||||||||||||||||||||||||||||
| * Both ion-datetime and ion-datetime-button default | ||||||||||||||||||||||||||||||||
| * to today's date and time if no value is set. | ||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
| const parsedDatetimes = parseDate(parsedValues.length > 0 ? parsedValues : [getToday()]); | ||||||||||||||||||||||||||||||||
| const defaultDatetime = [(await datetimeEl.getClosestDate(new Date())).toISOString()]; | ||||||||||||||||||||||||||||||||
| const parsedDatetimes = parseDate(parsedValues.length > 0 ? parsedValues : defaultDatetime); | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| if (!parsedDatetimes) { | ||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -35,7 +35,13 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getTimeColumnsData, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getCombinedDateColumnData, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from './utils/data'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { formatValue, getLocalizedDateTime, getLocalizedTime, getMonthAndYear } from './utils/format'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| formatValue, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getLocalizedDateTime, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getLocalizedTime, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getMonthAndYear, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| removeDateTzOffset, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from './utils/format'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { isLocaleDayPeriodRTL, isMonthFirstLocale, getNumDaysInMonth, getHourCycle } from './utils/helpers'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| calculateHourFromAMPM, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -604,6 +610,45 @@ export class Datetime implements ComponentInterface { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Get the closest valid DatetimeParts according to the restrictions on this Datetime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param parts The DatetimeParts to find the closest valid value for | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private getClosestDatetimeParts(parts: DatetimeParts) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This helper can go away. It only exists because the earlier |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hourValues = (this.parsedHourValues = convertToArrayOfNumbers(this.hourValues)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const minuteValues = (this.parsedMinuteValues = convertToArrayOfNumbers(this.minuteValues)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const monthValues = (this.parsedMonthValues = convertToArrayOfNumbers(this.monthValues)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const yearValues = (this.parsedYearValues = convertToArrayOfNumbers(this.yearValues)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dayValues = (this.parsedDayValues = convertToArrayOfNumbers(this.dayValues)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return getClosestValidDate({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refParts: parts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| monthValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dayValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| yearValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hourValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minuteValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minParts: this.minParts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxParts: this.maxParts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Get the closest valid Date according to the restrictions on this Datetime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param date The Date to find the closest valid value for | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Method() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async getClosestDate(date: Date) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const closest = this.getClosestDatetimeParts({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| month: date.getMonth(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| day: date.getDay(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You can reproduce it with a plain no-value date picker (no constraints needed): the calendar lands on Jun 17, 2026 but the button reads "May 3, 2026". Both of these can be seen with: <ion-item>
<ion-label>Start Date</ion-label>
<ion-datetime-button slot="end" datetime="no-value-date"></ion-datetime-button>
</ion-item>
<!-- keep-contents-mounted makes the datetime (and the button text) compute on load, so the mismatch shows immediately -->
<ion-modal keep-contents-mounted="true">
<ion-datetime locale="en-US" presentation="date" id="no-value-date"></ion-datetime>
</ion-modal>Fix:
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| year: date.getFullYear(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dayOfWeek: date.getDay(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hour: date.getHours(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minute: date.getMinutes(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return removeDateTzOffset(new Date(convertDataToISO(closest))); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+607
to
+617
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd recommend an
Making it
Suggested change
The /**
* Both ion-datetime and ion-datetime-button default to today's date and
* time if no value is set. We read the datetime's computed default so the
* button respects the same constraints (min, max, minuteValues, etc.) that
* the datetime applies to its own fallback, instead of using a raw "now".
*/
const parsedDatetimes =
parsedValues.length > 0 ? parseDate(parsedValues) : [await datetimeEl.getDefaultPart()]; |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private warnIfIncorrectValueUsage = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { multiple, value } = this; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!multiple && Array.isArray(value)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1495,27 +1540,12 @@ export class Datetime implements ComponentInterface { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| warnIfTimeZoneProvided(el, formatOptions); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hourValues = (this.parsedHourValues = convertToArrayOfNumbers(this.hourValues)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in https://github.com/ionic-team/ionic-framework/pull/31218/changes#r3463042864, I would revert this. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const minuteValues = (this.parsedMinuteValues = convertToArrayOfNumbers(this.minuteValues)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const monthValues = (this.parsedMonthValues = convertToArrayOfNumbers(this.monthValues)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const yearValues = (this.parsedYearValues = convertToArrayOfNumbers(this.yearValues)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const dayValues = (this.parsedDayValues = convertToArrayOfNumbers(this.dayValues)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const todayParts = (this.todayParts = parseDate(getToday())!); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.processMinParts(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.processMaxParts(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.defaultParts = getClosestValidDate({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in https://github.com/ionic-team/ionic-framework/pull/31218/changes#r3463042864, I would revert this. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refParts: todayParts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| monthValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dayValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| yearValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hourValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minuteValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minParts: this.minParts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxParts: this.maxParts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.defaultParts = this.getClosestDatetimeParts(todayParts); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.processValue(this.value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.