-
Notifications
You must be signed in to change notification settings - Fork 13.4k
fix(datetime-button): fix initial text not obeying datetime constraints #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
base: main
Are you sure you want to change the base?
Changes from 2 commits
f82ea68
ba0ebeb
7d95269
4824122
ef4c1cd
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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
+629
to
+639
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)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| refParts: todayParts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| monthValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dayValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| yearValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hourValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minuteValues, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minParts: this.minParts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| maxParts: this.maxParts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.defaultParts = this.getClosestDatetimeParts(todayParts); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.processValue(this.value); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getClosestDateis awaited on every call, but the result is thrown away wheneverparsedValuesis non-empty (i.e. whenever the datetime has a value). SincegetClosestDateis a Stencil@Method, this is an async cross-component round-trip on everyionValueChange, just to discard the result. Move it into the no-value branch and also update the comment: