mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-08 23:58:13 +08:00
fix(datetime): switching month and year accounts for day (#25996)
resolves #25585
This commit is contained in:
@ -45,6 +45,7 @@ import {
|
||||
getPreviousWeek,
|
||||
getPreviousYear,
|
||||
getStartOfWeek,
|
||||
validateParts,
|
||||
} from './utils/manipulation';
|
||||
import {
|
||||
clampDate,
|
||||
@ -583,6 +584,19 @@ export class Datetime implements ComponentInterface {
|
||||
private setActiveParts = (parts: DatetimeParts, removeDate = false) => {
|
||||
const { multiple, activePartsClone, highlightActiveParts } = this;
|
||||
|
||||
/**
|
||||
* When setting the active parts, it is possible
|
||||
* to set invalid data. For example,
|
||||
* when updating January 31 to February,
|
||||
* February 31 does not exist. As a result
|
||||
* we need to validate the active parts and
|
||||
* ensure that we are only setting valid dates.
|
||||
* Additionally, we need to update the working parts
|
||||
* too in the event that the validated parts are different.
|
||||
*/
|
||||
const validatedParts = validateParts(parts);
|
||||
this.setWorkingParts(validatedParts);
|
||||
|
||||
if (multiple) {
|
||||
/**
|
||||
* We read from activePartsClone here because valueChanged() only updates that,
|
||||
@ -595,20 +609,20 @@ export class Datetime implements ComponentInterface {
|
||||
*/
|
||||
const activePartsArray = Array.isArray(activePartsClone) ? activePartsClone : [activePartsClone];
|
||||
if (removeDate) {
|
||||
this.activeParts = activePartsArray.filter((p) => !isSameDay(p, parts));
|
||||
this.activeParts = activePartsArray.filter((p) => !isSameDay(p, validatedParts));
|
||||
} else if (highlightActiveParts) {
|
||||
this.activeParts = [...activePartsArray, parts];
|
||||
this.activeParts = [...activePartsArray, validatedParts];
|
||||
} else {
|
||||
/**
|
||||
* If highlightActiveParts is false, that means we just have a
|
||||
* default value of today in activeParts; we need to replace that
|
||||
* rather than adding to it since it's just a placeholder.
|
||||
*/
|
||||
this.activeParts = [parts];
|
||||
this.activeParts = [validatedParts];
|
||||
}
|
||||
} else {
|
||||
this.activeParts = {
|
||||
...parts,
|
||||
...validatedParts,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
48
core/src/components/datetime/test/datetime.e2e.ts
Normal file
48
core/src/components/datetime/test/datetime.e2e.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('datetime: switching months with different number of days', () => {
|
||||
test.beforeEach(async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
skip.mode('ios');
|
||||
|
||||
await page.setContent(`
|
||||
<ion-datetime locale="en-US" presentation="date" value="2022-01-31"></ion-datetime>
|
||||
`);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
});
|
||||
|
||||
test('should switch the calendar header when moving to a month with a different number of days', async ({ page }) => {
|
||||
const monthYearToggle = page.locator('ion-datetime .calendar-month-year');
|
||||
const monthColumnItems = page.locator('ion-datetime .month-column .picker-item:not(.picker-item-empty)');
|
||||
|
||||
await expect(monthYearToggle).toContainText('January 2022');
|
||||
|
||||
await monthYearToggle.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
// February
|
||||
await monthColumnItems.nth(1).click();
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(monthYearToggle).toContainText('February 2022');
|
||||
});
|
||||
|
||||
test('should adjust the selected day when moving to a month with a different number of days', async ({ page }) => {
|
||||
const monthYearToggle = page.locator('ion-datetime .calendar-month-year');
|
||||
const monthColumnItems = page.locator('ion-datetime .month-column .picker-item:not(.picker-item-empty)');
|
||||
const datetime = page.locator('ion-datetime');
|
||||
const ionChange = await page.spyOnEvent('ionChange');
|
||||
|
||||
await monthYearToggle.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
// February
|
||||
await monthColumnItems.nth(1).click();
|
||||
|
||||
await ionChange.next();
|
||||
await expect(ionChange).toHaveReceivedEventTimes(1);
|
||||
await expect(datetime).toHaveJSProperty('value', '2022-02-28');
|
||||
});
|
||||
});
|
||||
@ -339,3 +339,27 @@ export const calculateHourFromAMPM = (currentParts: DatetimeParts, newAMPM: 'am'
|
||||
|
||||
return newHour;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates parts to ensure that month and day
|
||||
* values are valid. For days that do not exist,
|
||||
* the closest valid day is used.
|
||||
*/
|
||||
export const validateParts = (parts: DatetimeParts): DatetimeParts => {
|
||||
const { month, day, year } = parts;
|
||||
const partsCopy = { ...parts };
|
||||
|
||||
const numDays = getNumDaysInMonth(month, year);
|
||||
|
||||
/**
|
||||
* If the max number of days
|
||||
* is greater than the day we want
|
||||
* to set, update the DatetimeParts
|
||||
* day field to be the max days.
|
||||
*/
|
||||
if (day !== null && numDays < day) {
|
||||
partsCopy.day = numDays;
|
||||
}
|
||||
|
||||
return partsCopy;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user