fix(datetime): time picker display matches dynamically set value (#25010)
Resolves #24967
@@ -317,6 +317,8 @@ export class Datetime implements ComponentInterface {
|
||||
const valueDateParts = parseDate(this.value);
|
||||
if (valueDateParts) {
|
||||
const { month, day, year, hour, minute } = valueDateParts;
|
||||
const ampm = hour >= 12 ? 'pm' : 'am';
|
||||
|
||||
this.activePartsClone = {
|
||||
...this.activeParts,
|
||||
month,
|
||||
@@ -324,7 +326,17 @@ export class Datetime implements ComponentInterface {
|
||||
year,
|
||||
hour,
|
||||
minute,
|
||||
ampm,
|
||||
};
|
||||
|
||||
/**
|
||||
* The working parts am/pm value must be updated when the value changes, to
|
||||
* ensure the time picker hour column values are generated correctly.
|
||||
*/
|
||||
this.setWorkingParts({
|
||||
...this.workingParts,
|
||||
ampm,
|
||||
});
|
||||
} else {
|
||||
printIonWarning(`Unable to parse date string: ${this.value}. Please provide a valid ISO 8601 datetime string.`);
|
||||
}
|
||||
@@ -1049,7 +1061,7 @@ export class Datetime implements ComponentInterface {
|
||||
const valueToProcess = value || getToday();
|
||||
const { month, day, year, hour, minute, tzOffset } = parseDate(valueToProcess);
|
||||
|
||||
this.workingParts = {
|
||||
this.setWorkingParts({
|
||||
month,
|
||||
day,
|
||||
year,
|
||||
@@ -1057,7 +1069,7 @@ export class Datetime implements ComponentInterface {
|
||||
minute,
|
||||
tzOffset,
|
||||
ampm: hour >= 12 ? 'pm' : 'am',
|
||||
};
|
||||
});
|
||||
|
||||
this.activeParts = {
|
||||
month,
|
||||
@@ -1628,7 +1640,7 @@ export class Datetime implements ComponentInterface {
|
||||
const timeOnlyPresentation = presentation === 'time';
|
||||
const use24Hour = is24Hour(this.locale, this.hourCycle);
|
||||
const { hours, minutes, am, pm } = generateTime(
|
||||
this.workingParts,
|
||||
workingParts,
|
||||
use24Hour ? 'h23' : 'h12',
|
||||
this.minParts,
|
||||
this.maxParts,
|
||||
|
||||
112
core/src/components/datetime/test/presentation/datetime.e2e.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import type { Locator } from '@playwright/test';
|
||||
import { expect } from '@playwright/test';
|
||||
import type { E2EPage } from '@utils/test/playwright';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('datetime: presentation', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/datetime/test/presentation`);
|
||||
|
||||
await page.setIonViewport();
|
||||
|
||||
const compares = [];
|
||||
const presentations = ['date-time', 'time-date', 'time', 'date', 'month-year', 'month', 'year'];
|
||||
|
||||
for (const presentation of presentations) {
|
||||
await page.locator('select').selectOption(presentation);
|
||||
await page.waitForChanges();
|
||||
compares.push({
|
||||
presentation,
|
||||
screenshot: await page.screenshot({ fullPage: true }),
|
||||
});
|
||||
}
|
||||
|
||||
for (const compare of compares) {
|
||||
expect(compare.screenshot).toMatchSnapshot(
|
||||
`datetime-presentation-${compare.presentation}-diff-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('datetime: presentation: time', () => {
|
||||
let timePickerFixture: TimePickerFixture;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
timePickerFixture = new TimePickerFixture(page);
|
||||
await timePickerFixture.goto();
|
||||
});
|
||||
|
||||
test('changing value from AM to AM should update the text', async () => {
|
||||
await timePickerFixture.setValue('04:20:00');
|
||||
await timePickerFixture.expectTime('4', '20', 'AM');
|
||||
|
||||
await timePickerFixture.setValue('11:03:00');
|
||||
await timePickerFixture.expectTime('11', '03', 'AM');
|
||||
});
|
||||
|
||||
test('changing value from AM to PM should update the text', async () => {
|
||||
await timePickerFixture.setValue('05:30:00');
|
||||
await timePickerFixture.expectTime('5', '30', 'AM');
|
||||
|
||||
await timePickerFixture.setValue('16:40:00');
|
||||
await timePickerFixture.expectTime('4', '40', 'PM');
|
||||
});
|
||||
|
||||
test('changing the value from PM to AM should update the text', async () => {
|
||||
await timePickerFixture.setValue('16:40:00');
|
||||
await timePickerFixture.expectTime('4', '40', 'PM');
|
||||
|
||||
await timePickerFixture.setValue('04:20:00');
|
||||
await timePickerFixture.expectTime('4', '20', 'AM');
|
||||
});
|
||||
|
||||
test('changing the value from PM to PM should update the text', async () => {
|
||||
await timePickerFixture.setValue('16:40:00');
|
||||
await timePickerFixture.expectTime('4', '40', 'PM');
|
||||
|
||||
await timePickerFixture.setValue('19:32:00');
|
||||
await timePickerFixture.expectTime('7', '32', 'PM');
|
||||
});
|
||||
});
|
||||
|
||||
class TimePickerFixture {
|
||||
readonly page: E2EPage;
|
||||
|
||||
private timePicker!: Locator;
|
||||
|
||||
constructor(page: E2EPage) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto(`/src/components/datetime/test/presentation`);
|
||||
await this.page.locator('select').selectOption('time');
|
||||
await this.page.waitForSelector('.datetime-presentation-time');
|
||||
this.timePicker = this.page.locator('ion-datetime');
|
||||
}
|
||||
|
||||
async setValue(value: string) {
|
||||
const ionChange = await this.page.spyOnEvent('ionChange');
|
||||
await this.timePicker.evaluate((el: HTMLIonDatetimeElement, newValue: string) => {
|
||||
el.value = newValue;
|
||||
}, value);
|
||||
|
||||
await ionChange.next();
|
||||
|
||||
// Changing the value can take longer than the default 100ms to repaint
|
||||
await this.page.waitForChanges(300);
|
||||
}
|
||||
|
||||
async expectTime(hour: string, minute: string, ampm: string) {
|
||||
expect(
|
||||
await this.timePicker.locator('ion-picker-column-internal:nth-child(1) .picker-item-active').textContent()
|
||||
).toBe(hour);
|
||||
expect(
|
||||
await this.timePicker.locator('ion-picker-column-internal:nth-child(2) .picker-item-active').textContent()
|
||||
).toBe(minute);
|
||||
expect(
|
||||
await this.timePicker.locator('ion-picker-column-internal:nth-child(3) .picker-item-active').textContent()
|
||||
).toBe(ampm);
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 31 KiB |