Skip to content
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

fix(datetime): switching month and year accounts for day #25996

Merged
merged 6 commits into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions core/src/components/datetime/datetime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
getPreviousWeek,
getPreviousYear,
getStartOfWeek,
validateParts,
} from './utils/manipulation';
import {
clampDate,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
};
}

Expand Down
48 changes: 48 additions & 0 deletions core/src/components/datetime/test/datetime.e2e.ts
Original file line number Diff line number Diff line change
@@ -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');
});
});
24 changes: 24 additions & 0 deletions core/src/components/datetime/utils/manipulation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};