fix(datetime): changing months work if partially visible (#27917)

Issue number: resolves #27913

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

When determining what the changed month is, we grab the element at the
center of the datetime and then grab the nearest calendar month. This
works fine if the datetime is fully in view, but if the center point is
out of the viewport then this will return `null`. As a result, scrolling
in the datetime will break.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- We now check scroll position instead of querying for DOM elements at
coordinates. This allows the view to continue to update even if the
entire calendar body is outside the viewport.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `7.2.2-dev.11690996559.1019674a`
This commit is contained in:
Liam DeBeasi
2023-08-04 14:41:25 -04:00
committed by GitHub
parent a0e6ac6013
commit eb19c289d6
3 changed files with 17 additions and 22 deletions

View File

@ -883,21 +883,18 @@ export class Datetime implements ComponentInterface {
const getChangedMonth = (parts: DatetimeParts): DatetimeParts | undefined => { const getChangedMonth = (parts: DatetimeParts): DatetimeParts | undefined => {
const box = calendarBodyRef.getBoundingClientRect(); const box = calendarBodyRef.getBoundingClientRect();
const root = this.el!.shadowRoot!;
/** /**
* Get the element that is in the center of the calendar body. * If the current scroll position is all the way to the left
* This will be an element inside of the active month. * then we have scrolled to the previous month.
* Otherwise, assume that we have scrolled to the next
* month. We have a tolerance of 2px to account for
* sub pixel rendering.
*
* Check below the next line ensures that we did not
* swipe and abort (i.e. we swiped but we are still on the current month).
*/ */
const elementAtCenter = root.elementFromPoint(box.x + box.width / 2, box.y + box.height / 2); const month = calendarBodyRef.scrollLeft <= 2 ? startMonth : endMonth;
/**
* If there is no element then the
* component may be re-rendering on a slow device.
*/
if (!elementAtCenter) return;
const month = elementAtCenter.closest('.calendar-month');
if (!month) return;
/** /**
* The edge of the month must be lined up with * The edge of the month must be lined up with

View File

@ -301,21 +301,20 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) =>
<ion-datetime <ion-datetime
min="2022-01-15" min="2022-01-15"
value="2022-02-01" value="2022-02-01"
presentation="date" presentation="month-year"
></ion-datetime> ></ion-datetime>
`, `,
config config
); );
const datetime = page.locator('ion-datetime'); const datetime = page.locator('ion-datetime');
const monthYearToggle = page.locator('ion-datetime .calendar-month-year');
const monthColumnItems = page.locator('ion-datetime .month-column .picker-item:not(.picker-item-empty)'); const monthColumnItems = page.locator('ion-datetime .month-column .picker-item:not(.picker-item-empty)');
const ionChange = await page.spyOnEvent('ionChange');
await monthYearToggle.click(); await page.waitForSelector('.datetime-ready');
await page.waitForChanges();
await monthColumnItems.nth(0).click(); // switch to January await monthColumnItems.nth(0).click(); // switch to January
await page.waitForChanges(); await ionChange.next();
await expect(datetime).toHaveJSProperty('value', '2022-01-15T00:00:00'); await expect(datetime).toHaveJSProperty('value', '2022-01-15T00:00:00');
}); });

View File

@ -34,13 +34,14 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
`, `,
config config
); );
await page.waitForSelector('.datetime-ready'); await page.waitForSelector('.datetime-ready');
const datetime = page.locator('ion-datetime'); const datetime = page.locator('ion-datetime');
const activeDayButton = page.locator('.calendar-day-active'); const activeDayButton = page.locator('.calendar-day-active');
const monthYearButton = page.locator('.calendar-month-year'); const monthYearButton = page.locator('.calendar-month-year');
const monthColumn = page.locator('.month-column'); const monthColumn = page.locator('.month-column');
const yearColumn = page.locator('.year-column'); const ionChange = await page.spyOnEvent('ionChange');
await datetime.evaluate((el: HTMLIonDatetimeElement) => (el.value = '2021-10-05')); await datetime.evaluate((el: HTMLIonDatetimeElement) => (el.value = '2021-10-05'));
@ -49,11 +50,9 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
await page.waitForChanges(); await page.waitForChanges();
// Select October 2021 // Select October 2021
// The year will automatically switch to 2021 when selecting 10
await monthColumn.locator('.picker-item[data-value="10"]').click(); await monthColumn.locator('.picker-item[data-value="10"]').click();
await page.waitForChanges(); await ionChange.next();
await yearColumn.locator('.picker-item[data-value="2021"]').click();
await page.waitForChanges();
// Close month/year picker // Close month/year picker
await monthYearButton.click(); await monthYearButton.click();