feat(datetime): add header parts (#30945)

Co-authored-by: Brandy Smith <brandyscarney@users.noreply.github.com>
This commit is contained in:
Maria Hutt
2026-02-20 13:36:39 -08:00
committed by GitHub
parent 23e998b731
commit 6ea186d96d
4 changed files with 267 additions and 7 deletions

View File

@@ -566,7 +566,15 @@ ion-datetime,part,calendar-day
ion-datetime,part,calendar-day active
ion-datetime,part,calendar-day disabled
ion-datetime,part,calendar-day today
ion-datetime,part,calendar-days-of-week
ion-datetime,part,calendar-header
ion-datetime,part,datetime-header
ion-datetime,part,datetime-selected-date
ion-datetime,part,datetime-title
ion-datetime,part,month-year-button
ion-datetime,part,navigation-button
ion-datetime,part,next-button
ion-datetime,part,previous-button
ion-datetime,part,time-button
ion-datetime,part,time-button active
ion-datetime,part,wheel

View File

@@ -88,14 +88,23 @@ import { checkForPresentationFormatMismatch, warnIfTimeZoneProvided } from './ut
* layout with `presentation="date-time"` or `"time-date"`.
* @part time-button active - The time picker button when the picker is open.
*
* @part calendar-header - The calendar header manages the date navigation controls (month/year picker and previous/next buttons) and the days of the week when using a grid style layout.
* @part month-year-button - The button that opens the month/year picker when
* using a grid style layout.
* @part navigation-button - The buttons used to navigate to the next or previous month when using a grid style layout.
* @part previous-button - The button used to navigate to the previous month when using a grid style layout.
* @part next-button - The button used to navigate to the next month when using a grid style layout.
* @part calendar-days-of-week - The container for the day-of-the-week header (both weekdays and weekends) when using a grid style layout.
*
* @part calendar-day - The individual buttons that display a day inside of the datetime
* calendar.
* @part calendar-day active - The currently selected calendar day.
* @part calendar-day today - The calendar day that contains the current day.
* @part calendar-day disabled - The calendar day that is disabled.
*
* @part datetime-header - The datetime header contains the content for the `title` slot and the selected date.
* @part datetime-title - The element that contains the `title` slot content.
* @part datetime-selected-date - The element that contains the selected date.
*/
@Component({
tag: 'ion-datetime',
@@ -2166,7 +2175,7 @@ export class Datetime implements ComponentInterface {
const hostDir = this.el.getAttribute('dir') || undefined;
return (
<div class="calendar-header">
<div class="calendar-header" part="calendar-header">
<div class="calendar-action-buttons">
<div class="calendar-month-year">
<button
@@ -2195,7 +2204,12 @@ export class Datetime implements ComponentInterface {
<div class="calendar-next-prev">
<ion-buttons>
<ion-button aria-label="Previous month" disabled={prevMonthDisabled} onClick={() => this.prevMonth()}>
<ion-button
aria-label="Previous month"
disabled={prevMonthDisabled}
onClick={() => this.prevMonth()}
part="navigation-button previous-button"
>
<ion-icon
dir={hostDir}
aria-hidden="true"
@@ -2205,7 +2219,12 @@ export class Datetime implements ComponentInterface {
flipRtl
></ion-icon>
</ion-button>
<ion-button aria-label="Next month" disabled={nextMonthDisabled} onClick={() => this.nextMonth()}>
<ion-button
aria-label="Next month"
disabled={nextMonthDisabled}
onClick={() => this.nextMonth()}
part="navigation-button next-button"
>
<ion-icon
dir={hostDir}
aria-hidden="true"
@@ -2218,7 +2237,7 @@ export class Datetime implements ComponentInterface {
</ion-buttons>
</div>
</div>
<div class="calendar-days-of-week" aria-hidden="true">
<div class="calendar-days-of-week" aria-hidden="true" part="calendar-days-of-week">
{getDaysOfWeek(this.locale, mode, this.firstDayOfWeek % 7).map((d) => {
return <div class="day-of-week">{d}</div>;
})}
@@ -2571,11 +2590,15 @@ export class Datetime implements ComponentInterface {
}
return (
<div class="datetime-header">
<div class="datetime-title">
<div class="datetime-header" part="datetime-header">
<div class="datetime-title" part="datetime-title">
<slot name="title">Select Date</slot>
</div>
{showExpandedHeader && <div class="datetime-selected-date">{this.getHeaderSelectedDateText()}</div>}
{showExpandedHeader && (
<div class="datetime-selected-date" part="datetime-selected-date">
{this.getHeaderSelectedDateText()}
</div>
)}
</div>
);
}

View File

@@ -175,6 +175,190 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
expect(backgroundColor).toBe('rgb(0, 0, 255)');
});
test('should be able to customize datetime header parts', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30083',
});
await page.setContent(
`
<style>
ion-datetime::part(datetime-header) {
background-color: orange;
}
ion-datetime::part(datetime-title) {
background-color: pink;
}
ion-datetime::part(datetime-selected-date) {
background-color: violet;
}
</style>
<ion-datetime value="2020-03-14T14:23:00.000Z">
<span slot="title">Select Date</span>
</ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const header = datetime.locator('.datetime-header');
const title = datetime.locator('.datetime-title');
const selectedDate = datetime.locator('.datetime-selected-date');
const headerBackgroundColor = await header.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
const titleBackgroundColor = await title.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
const selectedDateBackgroundColor = await selectedDate.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(headerBackgroundColor).toBe('rgb(255, 165, 0)');
expect(titleBackgroundColor).toBe('rgb(255, 192, 203)');
expect(selectedDateBackgroundColor).toBe('rgb(238, 130, 238)');
});
test('should be able to customize calendar header part', async ({ page }) => {
await page.setContent(
`
<style>
ion-datetime::part(calendar-header) {
background-color: orange;
}
</style>
<ion-datetime value="2020-03-14T14:23:00.000Z"></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const header = datetime.locator('.calendar-header');
const backgroundColor = await header.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe('rgb(255, 165, 0)');
});
test('should be able to customize month/year picker part', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/26596',
});
await page.setContent(
`
<style>
ion-datetime::part(month-year-button) {
background-color: lightblue;
}
</style>
<ion-datetime value="2020-03-14T14:23:00.000Z"></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const monthYearButton = datetime.locator('.calendar-month-year-toggle');
const backgroundColor = await monthYearButton.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe('rgb(173, 216, 230)');
});
test('should be able to customize navigation button parts', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30830',
});
await page.setContent(
`
<style>
ion-datetime::part(navigation-button) {
background-color: firebrick;
}
ion-datetime::part(previous-button) {
color: blue;
}
ion-datetime::part(next-button) {
color: green;
}
</style>
<ion-datetime value="2020-03-14T14:23:00.000Z"></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const prevButton = datetime.locator('.calendar-next-prev ion-button').first();
const nextButton = datetime.locator('.calendar-next-prev ion-button').last();
const prevBackgroundColor = await prevButton.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
const prevColor = await prevButton.evaluate((el) => {
return window.getComputedStyle(el).color;
});
const nextBackgroundColor = await nextButton.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
const nextColor = await nextButton.evaluate((el) => {
return window.getComputedStyle(el).color;
});
// Verify the navigation-button part applies the styles
expect(prevBackgroundColor).toBe('rgb(178, 34, 34)');
expect(nextBackgroundColor).toBe('rgb(178, 34, 34)');
// Verify the previous-button part applies the styles
expect(prevColor).toBe('rgb(0, 0, 255)');
// Verify the next-button part applies the styles
expect(nextColor).toBe('rgb(0, 128, 0)');
});
test('should be able to customize days of the week part', async ({ page }, testInfo) => {
testInfo.annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/30830',
});
await page.setContent(
`
<style>
ion-datetime::part(calendar-days-of-week) {
background-color: green;
}
</style>
<ion-datetime value="2020-03-14T14:23:00.000Z"></ion-datetime>
`,
config
);
const datetime = page.locator('ion-datetime');
const daysOfWeek = datetime.locator('.calendar-days-of-week');
const backgroundColor = await daysOfWeek.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(backgroundColor).toBe('rgb(0, 128, 0)');
});
});
});

View File

@@ -134,6 +134,46 @@
background-color: rgb(154 209 98 / 0.2);
color: #9ad162;
}
/*
* Custom Datetime Header Parts
* -------------------------------------------
*/
#custom-grid::part(calendar-header),
#custom-title::part(datetime-header) {
background-color: orange;
}
#custom-grid::part(month-year-button) {
background-color: lightblue;
color: rgb(128, 30, 171);
}
#custom-grid::part(navigation-button) {
background-color: firebrick;
}
#custom-grid::part(previous-button) {
color: white;
}
#custom-grid::part(next-button) {
color: black;
}
#custom-grid::part(calendar-days-of-week) {
background-color: #9ad162;
color: white;
}
#custom-title::part(datetime-title) {
background-color: pink;
}
#custom-title::part(datetime-selected-date) {
background-color: violet;
}
</style>
</head>
@@ -163,6 +203,11 @@
<h2>Grid Style</h2>
<ion-datetime id="custom-calendar-days" value="2023-06-15" presentation="date"></ion-datetime>
</div>
<div class="grid-item">
<ion-datetime id="custom-title" presentation="date">
<span slot="title">Select Date</span>
</ion-datetime>
</div>
</div>
</ion-content>
</ion-app>