feat(datetime): add animation to adjacent days selection (#30298)

Issue number: internal

---------

<!-- 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 new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
Now when an adjacent day is selected the component will scroll to the
pretended month, as it does when a month change via arrow buttons.

## Changes:
- add styles for active adjacent day;
- scroll animation when adjacentDay is selected;

## 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

- [md
preview](https://ionic-framework-git-rou-11744-ionic1.vercel.app/src/components/datetime/test/show-adjacent-days?ionic:mode=md)
- [ios
preview](https://ionic-framework-git-rou-11744-ionic1.vercel.app/src/components/datetime/test/show-adjacent-days?ionic:mode=ios
)
<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

---------

Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
Co-authored-by: ionitron <hi@ionicframework.com>
This commit is contained in:
João Ferreira
2025-03-28 17:19:42 +00:00
committed by GitHub
parent b67259edae
commit e140b9010f
9 changed files with 123 additions and 44 deletions

View File

@ -251,7 +251,8 @@
* is selected should have ion-color for * is selected should have ion-color for
* text color and be bolder. * text color and be bolder.
*/ */
:host .calendar-day.calendar-day-active { :host .calendar-day.calendar-day-active,
:host .calendar-day.calendar-day-adjacent-day.calendar-day-active {
color: current-color(base); color: current-color(base);
font-weight: 600; font-weight: 600;

View File

@ -117,7 +117,8 @@
* is selected should have ion-color for * is selected should have ion-color for
* text color and be bolder. * text color and be bolder.
*/ */
:host .calendar-day.calendar-day-active { :host .calendar-day.calendar-day-active,
:host .calendar-day.calendar-day-adjacent-day.calendar-day-active {
color: current-color(contrast); color: current-color(contrast);
} }

View File

@ -2383,36 +2383,25 @@ export class Datetime implements ComponentInterface {
if (isAdjacentDay) { if (isAdjacentDay) {
// The user selected a day outside the current month. Ignore this button, as the month will be re-rendered. // The user selected a day outside the current month. Ignore this button, as the month will be re-rendered.
this.el.blur(); this.el.blur();
} this.activeParts = { ...activePart, ...referenceParts };
this.animateToDate(referenceParts);
this.confirm();
} else {
this.setWorkingParts({ this.setWorkingParts({
...this.workingParts, ...this.workingParts,
month: _month, ...referenceParts,
day,
year: _year,
isAdjacentDay,
}); });
// multiple only needs date info, so we can wipe out other fields like time // Multiple only needs date info so we can wipe out other fields like time.
if (multiple) { if (multiple) {
this.setActiveParts( this.setActiveParts(referenceParts, isActive);
{
month: _month,
day,
year: _year,
isAdjacentDay,
},
isActive
);
} else { } else {
this.setActiveParts({ this.setActiveParts({
...activePart, ...activePart,
month: _month, ...referenceParts,
day,
year: _year,
isAdjacentDay,
}); });
} }
}
}} }}
> >
{text} {text}

View File

@ -47,5 +47,75 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
const datetime = page.locator('#display'); const datetime = page.locator('#display');
await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days-display`)); await expect(datetime).toHaveScreenshot(screenshot(`datetime-show-adjacent-days-display`));
}); });
test('should return the same date format on current month days and on adjacent days', async ({ page }) => {
await page.setContent(
`
<ion-datetime show-adjacent-days="true" value="2022-10-14T16:22:00.000Z" presentation="date"></ion-datetime>
`,
config
);
// Wait for the datetime to be ready.
await page.locator('.datetime-ready').waitFor();
const ionChange = await page.spyOnEvent('ionChange');
const calendarMonthYear = page.locator('ion-datetime .calendar-month-year');
/**
* Make sure to exclude adjacent days from the query since
* the previous/next month is rendered hidden. This causes
* the query to possibly return different results: one for
* the current month and one from the hidden previous/next
* month.
*/
const october20Button = page.locator(
'[data-month="10"][data-year="2022"][data-day="20"]:not(.calendar-day-adjacent-day)'
);
await october20Button.click();
await ionChange.next();
await expect(ionChange).toHaveReceivedEventDetail({
value: '2022-10-20T16:22:00',
});
const november1Button = page.locator(
'.calendar-day-adjacent-day[data-month="11"][data-year="2022"][data-day="1"]'
);
await november1Button.click();
// Wait for the datetime to change the month since an adjacent day
// was clicked.
await page.waitForChanges();
// Wait for the title to update to the new month since it changes
// after the month animation finishes.
await expect(calendarMonthYear).toHaveText('November 2022');
await ionChange.next();
await expect(ionChange).toHaveReceivedEventDetail({
value: '2022-11-01T16:22:00',
});
/**
* Make sure to exclude adjacent days from the query since
* the previous/next month is rendered hidden. This causes
* the query to possibly return different results: one for
* the current month and one from the hidden previous/next
* month.
*/
const november22Button = page.locator(
'[data-month="11"][data-year="2022"][data-day="22"]:not(.calendar-day-adjacent-day)'
);
await november22Button.click();
await ionChange.next();
await expect(ionChange).toHaveReceivedEventDetail({
value: '2022-11-22T16:22:00',
});
});
}); });
}); });

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -161,8 +161,20 @@
<span>FirstDayOfWeek: <span id="start-of-week">1</span></span> <span>FirstDayOfWeek: <span id="start-of-week">1</span></span>
</div> </div>
</div> </div>
<div class="grid-item">
<h2>DateTime format with IonChange Event (console)</h2>
<ion-datetime
id="default2"
show-adjacent-days="true"
locale="en-US"
value="2022-10-14T16:22:00.000Z"
presentation="date"
></ion-datetime>
</div> </div>
<div> </div>
<div class="grid">
<div class="grid-item">
<label for="presentation">Presentation</label> <label for="presentation">Presentation</label>
<select id="presentation" onchange="changePresentation(event)"> <select id="presentation" onchange="changePresentation(event)">
<option value="date-time" selected>date-time</option> <option value="date-time" selected>date-time</option>
@ -178,6 +190,7 @@
<br /><br /> <br /><br />
<ion-datetime show-adjacent-days="true" id="display" value="2022-02-22T16:30:00"></ion-datetime> <ion-datetime show-adjacent-days="true" id="display" value="2022-02-22T16:30:00"></ion-datetime>
</div> </div>
</div>
</ion-content> </ion-content>
</ion-app> </ion-app>
@ -305,6 +318,11 @@
}; };
initCalendarMonthChangeObserver(); initCalendarMonthChangeObserver();
const datetimeDefault = document.querySelector('#default2');
datetimeDefault.addEventListener('ionChange', (ev) => {
console.log(ev.target.value);
});
</script> </script>
</body> </body>
</html> </html>