mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-08 07:41:51 +08:00
fix(datetime): set working parts to last selected value (#29610)
Issue number: resolves #29094 --------- <!-- 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 assigning multiple selected dates that span different months, the date time will not set the correct working parts and instead fallback to the default date: May 28, 2021. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> When opening the datetime with multiple dates selected, the calendar will animate to the last value in the array of selected dates. If the datetime is collapsed, body is not visible, is not a grid view or the user has made a selection of a new date, the calendar will not animate and instead will set the working parts to the current value selected (latest/last value). ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. --> ## Other information <!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. --> Internally we discussed setting the month view to the first value in the array. Upon further investigation I determined this is not the expected behavior. When a user interacts with multiple date selection, the most recent selection (their active view) is the **last** value in the array. Animating or updating the working parts to the first value in the array would result in the calendar month jumping after every selection. Using the last index of the array results in no odd jumps in the experience. If a developer wishes to configure this behavior, they can change the order of the values in the value assigned to the datetime (to cause a specific month/date to be the initial view). Dev build: `8.5.8-dev.11748388365.11ad9dfe` --------- Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
This commit is contained in:
@ -1263,21 +1263,20 @@ export class Datetime implements ComponentInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* If there are multiple values, pick an arbitrary one to clamp to. This way,
|
||||
* if the values are across months, we always show at least one of them. Note
|
||||
* that the values don't necessarily have to be in order.
|
||||
* If there are multiple values, clamp to the last one.
|
||||
* This is because the last value is the one that the user
|
||||
* has most recently interacted with.
|
||||
*/
|
||||
const singleValue = Array.isArray(valueToProcess) ? valueToProcess[0] : valueToProcess;
|
||||
const singleValue = Array.isArray(valueToProcess) ? valueToProcess[valueToProcess.length - 1] : valueToProcess;
|
||||
const targetValue = clampDate(singleValue, minParts, maxParts);
|
||||
|
||||
const { month, day, year, hour, minute } = targetValue;
|
||||
const ampm = parseAmPm(hour!);
|
||||
|
||||
/**
|
||||
* Since `activeParts` indicates a value that
|
||||
* been explicitly selected either by the
|
||||
* user or the app, only update `activeParts`
|
||||
* if the `value` property is set.
|
||||
* Since `activeParts` indicates a value that been explicitly selected
|
||||
* either by the user or the app, only update `activeParts` if the
|
||||
* `value` property is set.
|
||||
*/
|
||||
if (hasValue) {
|
||||
if (Array.isArray(valueToProcess)) {
|
||||
@ -1301,53 +1300,29 @@ export class Datetime implements ComponentInterface {
|
||||
this.activeParts = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Only animate if:
|
||||
* 1. We're using grid style (wheel style pickers should just jump to new value)
|
||||
* 2. The month and/or year actually changed, and both are defined (otherwise there's nothing to animate to)
|
||||
* 3. The calendar body is visible (prevents animation when in collapsed datetime-button, for example)
|
||||
* 4. The month/year picker is not open (since you wouldn't see the animation anyway)
|
||||
*/
|
||||
const didChangeMonth =
|
||||
(month !== undefined && month !== workingParts.month) || (year !== undefined && year !== workingParts.year);
|
||||
const bodyIsVisible = el.classList.contains('datetime-ready');
|
||||
const { isGridStyle, showMonthAndYear } = this;
|
||||
|
||||
let areAllSelectedDatesInSameMonth = true;
|
||||
if (Array.isArray(valueToProcess)) {
|
||||
const firstMonth = valueToProcess[0].month;
|
||||
for (const date of valueToProcess) {
|
||||
if (date.month !== firstMonth) {
|
||||
areAllSelectedDatesInSameMonth = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is more than one date selected
|
||||
* and the dates aren't all in the same month,
|
||||
* then we should neither animate to the date
|
||||
* nor update the working parts because we do
|
||||
* not know which date the user wants to view.
|
||||
*/
|
||||
if (areAllSelectedDatesInSameMonth) {
|
||||
if (isGridStyle && didChangeMonth && bodyIsVisible && !showMonthAndYear) {
|
||||
this.animateToDate(targetValue);
|
||||
} else {
|
||||
/**
|
||||
* We only need to do this if we didn't just animate to a new month,
|
||||
* since that calls prevMonth/nextMonth which calls setWorkingParts for us.
|
||||
*/
|
||||
this.setWorkingParts({
|
||||
month,
|
||||
day,
|
||||
year,
|
||||
hour,
|
||||
minute,
|
||||
ampm,
|
||||
});
|
||||
}
|
||||
if (isGridStyle && didChangeMonth && bodyIsVisible && !showMonthAndYear) {
|
||||
/**
|
||||
* Only animate if:
|
||||
* 1. We're using grid style (wheel style pickers should just jump to new value)
|
||||
* 2. The month and/or year actually changed, and both are defined (otherwise there's nothing to animate to)
|
||||
* 3. The calendar body is visible (prevents animation when in collapsed datetime-button, for example)
|
||||
* 4. The month/year picker is not open (since you wouldn't see the animation anyway)
|
||||
*/
|
||||
this.animateToDate(targetValue);
|
||||
} else {
|
||||
this.setWorkingParts({
|
||||
month,
|
||||
day,
|
||||
year,
|
||||
hour,
|
||||
minute,
|
||||
ampm,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -174,18 +174,6 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
await expect(monthYear).toHaveText(/June 2022/);
|
||||
});
|
||||
|
||||
test('should not scroll to new month when value is updated with dates in different months', async ({ page }) => {
|
||||
const datetime = await datetimeFixture.goto(config, MULTIPLE_DATES);
|
||||
await datetime.evaluate((el: HTMLIonDatetimeElement, dates: string[]) => {
|
||||
el.value = dates;
|
||||
}, MULTIPLE_DATES_SEPARATE_MONTHS);
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
const monthYear = datetime.locator('.calendar-month-year');
|
||||
await expect(monthYear).toHaveText(/June 2022/);
|
||||
});
|
||||
|
||||
test('with buttons, should only update value when confirm is called', async ({ page }) => {
|
||||
const datetime = await datetimeFixture.goto(config, SINGLE_DATE, { showDefaultButtons: true });
|
||||
const june2Button = datetime.locator('[data-month="6"][data-day="2"]');
|
||||
@ -311,4 +299,41 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
await expect(header).toHaveText('Mon, Oct 10');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('with selected days in different months', () => {
|
||||
test(`set the active month view to the latest value's month`, async ({ page }, testInfo) => {
|
||||
testInfo.annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/29094',
|
||||
});
|
||||
|
||||
const datetime = await new DatetimeMultipleFixture(page).goto(config, MULTIPLE_DATES_SEPARATE_MONTHS);
|
||||
const calendarMonthYear = datetime.locator('.calendar-month-year');
|
||||
|
||||
await expect(calendarMonthYear).toHaveText(/May 2022/);
|
||||
});
|
||||
|
||||
test('does not change the active month view when selecting a day in a different month', async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
testInfo.annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/29094',
|
||||
});
|
||||
|
||||
const datetime = await new DatetimeMultipleFixture(page).goto(config, MULTIPLE_DATES_SEPARATE_MONTHS);
|
||||
const nextButton = page.locator('.calendar-next-prev ion-button:nth-child(2)');
|
||||
const calendarMonthYear = datetime.locator('.calendar-month-year');
|
||||
|
||||
await nextButton.click();
|
||||
|
||||
await expect(calendarMonthYear).toHaveText(/June 2022/);
|
||||
|
||||
const june8Button = datetime.locator('[data-month="6"][data-day="8"]');
|
||||
|
||||
await june8Button.click();
|
||||
|
||||
await expect(calendarMonthYear).toHaveText(/June 2022/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user