feat(datetime): add parts for calendar day, active, and today (#27641)
Issue number: resolves #25340 --------- - Exposes the following parts for a calendar day: `calendar-day`, `today`, and `active` - Combines the `calendar-day-highlight` element with the `calendar-day` element so developers don't have to know to style two different elements & we don't have to expose them as separate parts - Improves height parity of the calendar day across browsers - Updates the `custom` e2e test to include an example of styling days using the newly exposed CSS parts - Adds tests for the focus states of the calendar day
@ -428,6 +428,9 @@ ion-datetime,css-prop,--background-rgb
|
||||
ion-datetime,css-prop,--title-color
|
||||
ion-datetime,css-prop,--wheel-fade-background-rgb
|
||||
ion-datetime,css-prop,--wheel-highlight-background
|
||||
ion-datetime,part,calendar-day
|
||||
ion-datetime,part,calendar-day active
|
||||
ion-datetime,part,calendar-day today
|
||||
ion-datetime,part,month-year-button
|
||||
ion-datetime,part,time-button
|
||||
ion-datetime,part,time-button active
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
@ -85,27 +85,32 @@
|
||||
*/
|
||||
@include padding($datetime-ios-padding * 0.5, $datetime-ios-padding * 0.5, $datetime-ios-padding * 0.5, $datetime-ios-padding * 0.5);
|
||||
|
||||
align-items: center;
|
||||
|
||||
height: calc(100% - #{$datetime-ios-padding});
|
||||
}
|
||||
|
||||
:host .calendar-day {
|
||||
font-size: 20px;
|
||||
}
|
||||
:host .calendar-day-wrapper {
|
||||
@include padding(4px);
|
||||
|
||||
// This is required so that the calendar day wrapper
|
||||
// will collapse instead of expanding to fill the button
|
||||
height: 0;
|
||||
|
||||
.calendar-day:focus .calendar-day-highlight,
|
||||
.calendar-day.calendar-day-active .calendar-day-highlight {
|
||||
opacity: 0.2;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
.calendar-day.calendar-day-active .calendar-day-highlight {
|
||||
background: current-color(base);
|
||||
:host .calendar-day {
|
||||
width: $datetime-ios-day-width;
|
||||
min-width: $datetime-ios-day-width;
|
||||
|
||||
height: $datetime-ios-day-height;
|
||||
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
// !important is needed here to overwrite custom highlight background, which is inline.
|
||||
// Does not apply to the active state because highlights aren't applied at all there.
|
||||
.calendar-day:focus .calendar-day-highlight {
|
||||
/* stylelint-disable-next-line declaration-no-important */
|
||||
background: current-color(base) !important;
|
||||
.calendar-day.calendar-day-active {
|
||||
background: current-color(base, 0.2);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,12 +140,6 @@
|
||||
color: current-color(contrast);
|
||||
}
|
||||
|
||||
.calendar-day.calendar-day-today.calendar-day-active .calendar-day-highlight {
|
||||
background: current-color(base);
|
||||
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// Time / Header
|
||||
// -----------------------------------
|
||||
:host .datetime-time {
|
||||
|
||||
@ -15,3 +15,9 @@ $datetime-ios-time-width: 68px !default;
|
||||
|
||||
/// @prop - Border radius of the time picker
|
||||
$datetime-ios-time-border-radius: 8px !default;
|
||||
|
||||
/// @prop - Width of the calendar day
|
||||
$datetime-ios-day-width: 40px !default;
|
||||
|
||||
/// @prop - Height of the calendar day
|
||||
$datetime-ios-day-height: $datetime-ios-day-width !default;
|
||||
|
||||
@ -69,15 +69,12 @@
|
||||
|
||||
// Individual day button in month
|
||||
:host .calendar-day {
|
||||
@include padding(13px, 0, 13px, 0px);
|
||||
width: $datetime-md-day-width;
|
||||
min-width: $datetime-md-day-width;
|
||||
|
||||
font-size: $datetime-md-calendar-item-font-size;
|
||||
}
|
||||
|
||||
.calendar-day:focus .calendar-day-highlight {
|
||||
background: current-color(base, 0.2);
|
||||
height: $datetime-md-day-height;
|
||||
|
||||
box-shadow: 0px 0px 0px 4px current-color(base, 0.2);
|
||||
font-size: $datetime-md-calendar-item-font-size;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,11 +82,9 @@
|
||||
* should have ion-color for text color.
|
||||
*/
|
||||
:host .calendar-day.calendar-day-today {
|
||||
color: current-color(base);
|
||||
}
|
||||
|
||||
.calendar-day.calendar-day-today .calendar-day-highlight {
|
||||
border: 1px solid current-color(base);
|
||||
|
||||
color: current-color(base);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,7 +96,7 @@
|
||||
color: current-color(contrast);
|
||||
}
|
||||
|
||||
.calendar-day.calendar-day-active .calendar-day-highlight {
|
||||
.calendar-day.calendar-day-active {
|
||||
border: 1px solid current-color(base);
|
||||
|
||||
background: current-color(base);
|
||||
|
||||
@ -15,3 +15,9 @@ $datetime-md-header-padding: 20px !default;
|
||||
|
||||
/// @prop - Padding for content
|
||||
$datetime-md-padding: 16px !default;
|
||||
|
||||
/// @prop - Width of the calendar day
|
||||
$datetime-md-day-width: 42px !default;
|
||||
|
||||
/// @prop - Height of the calendar day
|
||||
$datetime-md-day-height: $datetime-md-day-width !default;
|
||||
|
||||
@ -290,6 +290,10 @@ ion-picker-column-internal {
|
||||
}
|
||||
|
||||
:host .calendar-body .calendar-month {
|
||||
display: flex;
|
||||
|
||||
flex-flow: column;
|
||||
|
||||
/**
|
||||
* Swiping should snap to at
|
||||
* most one month at a time.
|
||||
@ -325,13 +329,31 @@ ion-picker-column-internal {
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
}
|
||||
|
||||
:host .calendar-day-wrapper {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
// Adding a min width and min height allows
|
||||
// it to shrink smaller than its content
|
||||
// which keeps the calendar day highlight
|
||||
// larger while letting the grid items shrink
|
||||
min-width: 0;
|
||||
|
||||
min-height: 0;
|
||||
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Center the day text vertically
|
||||
* and horizontally within its grid cell.
|
||||
*/
|
||||
:host .calendar-day {
|
||||
@include padding(0px, 0px, 0px, 0px);
|
||||
@include margin(0px, 0px, 0px, 0px);
|
||||
@include border-radius(50%);
|
||||
@include padding(0px);
|
||||
@include margin(0px);
|
||||
|
||||
display: flex;
|
||||
|
||||
@ -362,16 +384,10 @@ ion-picker-column-internal {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.calendar-day-highlight {
|
||||
@include border-radius(32px, 32px, 32px, 32px);
|
||||
@include padding(4px, 4px, 4px, 4px);
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
.calendar-day:focus {
|
||||
background: current-color(base, 0.2);
|
||||
|
||||
z-index: -1;
|
||||
box-shadow: 0px 0px 0px 4px current-color(base, 0.2);
|
||||
}
|
||||
|
||||
// Time / Header
|
||||
|
||||
@ -85,6 +85,11 @@ import {
|
||||
*
|
||||
* @part month-year-button - The button that opens the month/year picker 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.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-datetime',
|
||||
@ -756,7 +761,7 @@ export class Datetime implements ComponentInterface {
|
||||
/**
|
||||
* Get the number of padding days so
|
||||
* we know how much to offset our next selector by
|
||||
* to grab the correct calenday-day element.
|
||||
* to grab the correct calendar-day element.
|
||||
*/
|
||||
const padding = currentMonth.querySelectorAll('.calendar-day-padding');
|
||||
const { day } = this.workingParts;
|
||||
@ -770,7 +775,7 @@ export class Datetime implements ComponentInterface {
|
||||
* and focus it.
|
||||
*/
|
||||
const dayEl = currentMonth.querySelector(
|
||||
`.calendar-day:nth-of-type(${padding.length + day})`
|
||||
`.calendar-day-wrapper:nth-of-type(${padding.length + day}) .calendar-day`
|
||||
) as HTMLElement | null;
|
||||
if (dayEl) {
|
||||
dayEl.focus();
|
||||
@ -2116,69 +2121,85 @@ export class Datetime implements ComponentInterface {
|
||||
dateStyle = getHighlightStyles(highlightedDates, dateIsoString, el);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
tabindex="-1"
|
||||
data-day={day}
|
||||
data-month={month}
|
||||
data-year={year}
|
||||
data-index={index}
|
||||
data-day-of-week={dayOfWeek}
|
||||
disabled={isCalDayDisabled}
|
||||
class={{
|
||||
'calendar-day-padding': isCalendarPadding,
|
||||
'calendar-day': true,
|
||||
'calendar-day-active': isActive,
|
||||
'calendar-day-today': isToday,
|
||||
}}
|
||||
style={
|
||||
dateStyle && {
|
||||
color: dateStyle.textColor,
|
||||
}
|
||||
}
|
||||
aria-hidden={isCalendarPadding ? 'true' : null}
|
||||
aria-selected={ariaSelected}
|
||||
aria-label={ariaLabel}
|
||||
onClick={() => {
|
||||
if (isCalendarPadding) {
|
||||
return;
|
||||
}
|
||||
let dateParts = undefined;
|
||||
|
||||
this.setWorkingParts({
|
||||
...this.workingParts,
|
||||
month,
|
||||
day,
|
||||
year,
|
||||
});
|
||||
|
||||
// multiple only needs date info, so we can wipe out other fields like time
|
||||
if (multiple) {
|
||||
this.setActiveParts(
|
||||
{
|
||||
month,
|
||||
day,
|
||||
year,
|
||||
},
|
||||
isActive
|
||||
);
|
||||
} else {
|
||||
this.setActiveParts({
|
||||
...activePart,
|
||||
// "Filler days" at the beginning of the grid should not get the calendar day
|
||||
// CSS parts added to them
|
||||
if (!isCalendarPadding) {
|
||||
dateParts = `calendar-day${isActive ? ' active' : ''}${isToday ? ' today' : ''}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="calendar-day-wrapper">
|
||||
<button
|
||||
// We need to use !important for the inline styles here because
|
||||
// otherwise the CSS shadow parts will override these styles.
|
||||
// See https://github.com/WICG/webcomponents/issues/847
|
||||
// Both the CSS shadow parts and highlightedDates styles are
|
||||
// provided by the developer, but highlightedDates styles should
|
||||
// always take priority.
|
||||
ref={(el) => {
|
||||
if (el) {
|
||||
el.style.setProperty('color', `${dateStyle ? dateStyle.textColor : ''}`, 'important');
|
||||
el.style.setProperty(
|
||||
'background-color',
|
||||
`${dateStyle ? dateStyle.backgroundColor : ''}`,
|
||||
'important'
|
||||
);
|
||||
}
|
||||
}}
|
||||
tabindex="-1"
|
||||
data-day={day}
|
||||
data-month={month}
|
||||
data-year={year}
|
||||
data-index={index}
|
||||
data-day-of-week={dayOfWeek}
|
||||
disabled={isCalDayDisabled}
|
||||
class={{
|
||||
'calendar-day-padding': isCalendarPadding,
|
||||
'calendar-day': true,
|
||||
'calendar-day-active': isActive,
|
||||
'calendar-day-today': isToday,
|
||||
}}
|
||||
part={dateParts}
|
||||
aria-hidden={isCalendarPadding ? 'true' : null}
|
||||
aria-selected={ariaSelected}
|
||||
aria-label={ariaLabel}
|
||||
onClick={() => {
|
||||
if (isCalendarPadding) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setWorkingParts({
|
||||
...this.workingParts,
|
||||
month,
|
||||
day,
|
||||
year,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="calendar-day-highlight"
|
||||
style={{
|
||||
backgroundColor: dateStyle?.backgroundColor,
|
||||
|
||||
// multiple only needs date info, so we can wipe out other fields like time
|
||||
if (multiple) {
|
||||
this.setActiveParts(
|
||||
{
|
||||
month,
|
||||
day,
|
||||
year,
|
||||
},
|
||||
isActive
|
||||
);
|
||||
} else {
|
||||
this.setActiveParts({
|
||||
...activePart,
|
||||
month,
|
||||
day,
|
||||
year,
|
||||
});
|
||||
}
|
||||
}}
|
||||
></div>
|
||||
{text}
|
||||
</button>
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@ -490,3 +490,40 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This behavior does not differ across
|
||||
* directions.
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('datetime: focus'), () => {
|
||||
test('should focus the selected day and then the day after', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime value="2023-08-01"></ion-datetime>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
const datetime = page.locator('ion-datetime');
|
||||
|
||||
const day = datetime.locator(`.calendar-day[data-day='1'][data-month='8']`);
|
||||
|
||||
await day.focus();
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(day).toBeFocused();
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-focus-selected-calendar-day`));
|
||||
|
||||
await page.keyboard.press('ArrowRight');
|
||||
await page.waitForChanges();
|
||||
|
||||
const nextDay = datetime.locator(`.calendar-day[data-day='2'][data-month='8']`);
|
||||
|
||||
await expect(nextDay).toBeFocused();
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-focus-calendar-day`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@ -9,7 +9,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto('/src/components/datetime/test/color', config);
|
||||
|
||||
const datetime = page.locator('ion-datetime');
|
||||
const datetime = page.locator('#color-datetime');
|
||||
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-color`));
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
@ -9,11 +9,18 @@
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
ion-checkbox,
|
||||
ion-select {
|
||||
margin-inline-start: 10px;
|
||||
margin-inline-end: 10px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(250px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, 250px);
|
||||
grid-gap: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
@ -29,10 +36,14 @@
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
#color-name::first-letter {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dark Colors
|
||||
* -------------------------------------------
|
||||
*/
|
||||
* Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
body.dark {
|
||||
--ion-color-primary: #428cff;
|
||||
@ -100,9 +111,9 @@
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body.dark {
|
||||
--ion-background-color: #000000;
|
||||
@ -144,9 +155,9 @@
|
||||
}
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body.dark {
|
||||
--ion-background-color: #121212;
|
||||
@ -191,30 +202,23 @@
|
||||
<ion-app>
|
||||
<ion-header translucent="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>Datetime - Color</ion-title>
|
||||
<ion-item lines="none" slot="end">
|
||||
<ion-label>Dark Mode</ion-label>
|
||||
<ion-checkbox slot="end"></ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-item lines="none" slot="end">
|
||||
<ion-label>Color</ion-label>
|
||||
<ion-select value="danger">
|
||||
<ion-select-option value="primary">Primary</ion-select-option>
|
||||
<ion-select-option value="secondary">Secondary</ion-select-option>
|
||||
<ion-select-option value="tertiary">Tertiary</ion-select-option>
|
||||
<ion-select-option value="success">Success</ion-select-option>
|
||||
<ion-select-option value="warning">Warning</ion-select-option>
|
||||
<ion-select-option value="danger">Danger</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
<ion-checkbox>Dark Mode</ion-checkbox>
|
||||
<ion-select label="Color" justify="end" slot="end" value="danger">
|
||||
<ion-select-option value="primary">Primary</ion-select-option>
|
||||
<ion-select-option value="secondary">Secondary</ion-select-option>
|
||||
<ion-select-option value="tertiary">Tertiary</ion-select-option>
|
||||
<ion-select-option value="success">Success</ion-select-option>
|
||||
<ion-select-option value="warning">Warning</ion-select-option>
|
||||
<ion-select-option value="danger">Danger</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Default</h2>
|
||||
<h2 id="color-name">Danger</h2>
|
||||
<ion-datetime
|
||||
id="color-datetime"
|
||||
color="danger"
|
||||
value="2022-05-03"
|
||||
show-default-title="true"
|
||||
@ -224,14 +228,14 @@
|
||||
</div>
|
||||
</ion-content>
|
||||
<script>
|
||||
const colorDatetime = document.querySelector('#color-datetime');
|
||||
const colorName = document.querySelector('#color-name');
|
||||
const colorSelect = document.querySelector('ion-select');
|
||||
const darkModeToggle = document.querySelector('ion-checkbox');
|
||||
const datetimes = document.querySelectorAll('ion-datetime');
|
||||
|
||||
colorSelect.addEventListener('ionChange', (ev) => {
|
||||
datetimes.forEach((datetime) => {
|
||||
datetime.color = ev.detail.value;
|
||||
});
|
||||
colorDatetime.color = ev.detail.value;
|
||||
colorName.innerHTML = ev.detail.value;
|
||||
});
|
||||
|
||||
darkModeToggle.addEventListener('ionChange', (ev) => {
|
||||
|
||||
@ -14,7 +14,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
});
|
||||
|
||||
test('should allow styling time picker in grid style datetimes', async ({ page }) => {
|
||||
const timeButton = page.locator('ion-datetime .time-body');
|
||||
const timeButton = page.locator('#custom-grid .time-body');
|
||||
const popover = page.locator('.popover-viewport');
|
||||
const ionPopoverDidPresent = await page.spyOnEvent('ionPopoverDidPresent');
|
||||
|
||||
@ -26,5 +26,41 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
await expect(popover).toHaveScreenshot(screenshot(`datetime-custom-time-picker`));
|
||||
await expect(timeButton).toHaveScreenshot(screenshot(`datetime-custom-time-button-active`));
|
||||
});
|
||||
|
||||
test('should allow styling calendar days in grid style datetimes', async ({ page }) => {
|
||||
const datetime = page.locator('#custom-calendar-days');
|
||||
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-custom-calendar-days`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* This behavior does not differ across
|
||||
* directions.
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('datetime: custom focus'), () => {
|
||||
test('should focus the selected day and then the day after', async ({ page }) => {
|
||||
await page.goto(`/src/components/datetime/test/custom`, config);
|
||||
|
||||
const datetime = page.locator('#custom-calendar-days');
|
||||
|
||||
const day = datetime.locator(`.calendar-day[data-day='15'][data-month='6']`);
|
||||
|
||||
await day.focus();
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(day).toBeFocused();
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-custom-focus-selected-calendar-day`));
|
||||
|
||||
await page.keyboard.press('ArrowRight');
|
||||
await page.waitForChanges();
|
||||
|
||||
const nextDay = datetime.locator(`.calendar-day[data-day='16'][data-month='6']`);
|
||||
|
||||
await expect(nextDay).toBeFocused();
|
||||
await expect(datetime).toHaveScreenshot(screenshot(`datetime-custom-focus-calendar-day`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 23 KiB |
@ -33,43 +33,97 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom Datetime Time / Wheel Parts
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.custom-grid-wheel {
|
||||
--background: rgb(245, 235, 247);
|
||||
--background-rgb: 245, 235, 247;
|
||||
}
|
||||
|
||||
.custom-grid-wheel::part(time-button) {
|
||||
color: rgb(128, 30, 171);
|
||||
}
|
||||
|
||||
.custom-grid-wheel::part(time-button active) {
|
||||
background-color: rgb(248, 215, 255);
|
||||
}
|
||||
|
||||
/*
|
||||
The second selectors that target ion-picker(-column)-internal
|
||||
directly are for styling the time picker. This is currently
|
||||
undocumented usage.
|
||||
*/
|
||||
|
||||
ion-datetime,
|
||||
.custom-grid-wheel,
|
||||
ion-picker-internal {
|
||||
--wheel-highlight-background: rgb(218, 216, 255);
|
||||
--wheel-fade-background-rgb: 245, 235, 247;
|
||||
}
|
||||
|
||||
ion-datetime {
|
||||
--background: rgb(245, 235, 247);
|
||||
--background-rgb: 245, 235, 247;
|
||||
}
|
||||
|
||||
ion-picker-internal {
|
||||
background-color: rgb(245, 235, 247);
|
||||
}
|
||||
|
||||
ion-datetime::part(wheel-item),
|
||||
.custom-grid-wheel::part(wheel-item),
|
||||
ion-picker-column-internal::part(wheel-item) {
|
||||
color: rgb(255, 134, 154);
|
||||
}
|
||||
|
||||
ion-datetime::part(wheel-item active),
|
||||
.custom-grid-wheel::part(wheel-item active),
|
||||
ion-picker-column-internal::part(wheel-item active) {
|
||||
color: rgb(128, 30, 171);
|
||||
}
|
||||
|
||||
ion-datetime::part(time-button) {
|
||||
color: rgb(128, 30, 171);
|
||||
/*
|
||||
* Custom Datetime Day Parts
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
#custom-calendar-days::part(calendar-day) {
|
||||
background-color: #ffe2e6;
|
||||
|
||||
color: #da5296;
|
||||
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
ion-datetime::part(time-button active) {
|
||||
background-color: rgb(248, 215, 255);
|
||||
#custom-calendar-days::part(calendar-day today) {
|
||||
color: #8462d1;
|
||||
}
|
||||
|
||||
#custom-calendar-days::part(calendar-day):focus {
|
||||
background-color: rgb(154 209 98 / 0.2);
|
||||
box-shadow: 0px 0px 0px 4px rgb(154 209 98 / 0.2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom Material Design Datetime Day Parts
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
#custom-calendar-days.md::part(calendar-day active),
|
||||
#custom-calendar-days.md::part(calendar-day active):focus {
|
||||
background-color: #9ad162;
|
||||
border-color: #9ad162;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#custom-calendar-days.md::part(calendar-day today) {
|
||||
border-color: #8462d1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom iOS Datetime Day Parts
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
#custom-calendar-days.ios::part(calendar-day active),
|
||||
#custom-calendar-days.ios::part(calendar-day active):focus {
|
||||
background-color: rgb(154 209 98 / 0.2);
|
||||
color: #9ad162;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@ -85,14 +139,57 @@
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Grid Style</h2>
|
||||
<ion-datetime id="custom-grid" value="2020-03-14T14:23:00.000Z"></ion-datetime>
|
||||
<ion-datetime id="custom-grid" value="2020-03-14T14:23:00.000Z" class="custom-grid-wheel"></ion-datetime>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<h2>Wheel Style</h2>
|
||||
<ion-datetime id="custom-wheel" prefer-wheel="true" value="2020-03-14T14:23:00.000Z"></ion-datetime>
|
||||
<ion-datetime
|
||||
id="custom-wheel"
|
||||
prefer-wheel="true"
|
||||
value="2020-03-14T14:23:00.000Z"
|
||||
class="custom-grid-wheel"
|
||||
></ion-datetime>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<h2>Grid Style</h2>
|
||||
<ion-datetime id="custom-calendar-days" value="2023-06-15" presentation="date"></ion-datetime>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
const customDatetime = document.querySelector('#custom-calendar-days');
|
||||
|
||||
// Mock the current day to always have the same screenshots
|
||||
const mockToday = '2023-06-10T16:22';
|
||||
Date = class extends Date {
|
||||
constructor(...args) {
|
||||
if (args.length === 0) {
|
||||
super(mockToday);
|
||||
} else {
|
||||
super(...args);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
customDatetime.highlightedDates = [
|
||||
{
|
||||
date: '2023-06-02',
|
||||
textColor: 'purple',
|
||||
backgroundColor: 'pink',
|
||||
},
|
||||
{
|
||||
date: '2023-06-04',
|
||||
textColor: 'firebrick',
|
||||
backgroundColor: 'salmon',
|
||||
},
|
||||
{
|
||||
date: '2023-06-06',
|
||||
textColor: 'blue',
|
||||
backgroundColor: 'lightblue',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |