Skip to content

fix(datetime): time picker display matches dynamically set value #25010

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Apr 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9ac60f4
fix(datetime): time picker display matches dynamically set value
sean-perkins Mar 28, 2022
376be2e
Merge remote-tracking branch 'origin/main' into FW-1003-part-2
sean-perkins Mar 31, 2022
86f8cf8
test(datetime): migrate test to playwright
sean-perkins Apr 1, 2022
276edb4
chore(): lint
sean-perkins Apr 1, 2022
49cda5f
chore(): remove unneeded test
sean-perkins Apr 1, 2022
70df503
test(datetime): playwright utils
sean-perkins Apr 1, 2022
164bccc
Merge remote-tracking branch 'origin/main' into FW-1003-part-2
sean-perkins Apr 1, 2022
c616b84
chore(): lint
sean-perkins Apr 1, 2022
48ffd73
chore(): add updated snapshots
Ionitron Apr 1, 2022
0809c29
test(datetime): initial values for screenshot tests
sean-perkins Apr 1, 2022
0d40faf
Merge remote-tracking branch 'origin/FW-1003-part-2' into FW-1003-part-2
sean-perkins Apr 1, 2022
ea56022
test(): use text content
sean-perkins Apr 1, 2022
4d16418
chore(): add updated snapshots
Ionitron Apr 1, 2022
b58bc8c
chore: trigger rebuild
sean-perkins Apr 1, 2022
7194888
Merge remote-tracking branch 'origin/FW-1003-part-2' into FW-1003-part-2
sean-perkins Apr 1, 2022
44ba5ff
chore(): add delay for intersection observer behavior
sean-perkins Apr 1, 2022
e4c16ef
test(): waitForSelector datetime ready
sean-perkins Apr 1, 2022
56eab26
chore(): adjust wait for selectors
sean-perkins Apr 1, 2022
220beb9
Merge remote-tracking branch 'origin/main' into FW-1003-part-2
sean-perkins Apr 5, 2022
5e6cd2e
fix(): post merge clean-up
sean-perkins Apr 5, 2022
ad0ced0
fix(): datetime behavior + playwright test
sean-perkins Apr 6, 2022
44aa2a2
chore(): delete reference screenshots
sean-perkins Apr 6, 2022
65b8639
chore(): add updated snapshots
Ionitron Apr 6, 2022
aeef96f
Merge remote-tracking branch 'origin/main' into FW-1003-part-2
sean-perkins Apr 11, 2022
72b3f6c
fix(): set-value test file
sean-perkins Apr 11, 2022
48e667e
fix(): update to using event spy
sean-perkins Apr 11, 2022
fe7f83d
test(datetime): update test to run per presentation type
sean-perkins Apr 12, 2022
b837c6a
chore(): delete reference screenshots
sean-perkins Apr 12, 2022
d918f77
chore(): add updated snapshots
Ionitron Apr 12, 2022
5dfd068
Merge remote-tracking branch 'origin/main' into FW-1003-part-2
sean-perkins Apr 13, 2022
9c1683c
test(ripple-effect): update type from IonicPage to E2EPage
sean-perkins Apr 13, 2022
716100d
chore(): lint
sean-perkins Apr 13, 2022
e88b7bc
Merge remote-tracking branch 'origin/main' into FW-1003-part-2
sean-perkins Apr 15, 2022
2a3085f
chore(): remove unused wait for custom event util
sean-perkins Apr 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions core/src/components/datetime/datetime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -317,14 +317,26 @@ 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,
day,
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.`);
}
Expand Down Expand Up @@ -1049,15 +1061,15 @@ 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,
hour,
minute,
tzOffset,
ampm: hour >= 12 ? 'pm' : 'am',
};
});

this.activeParts = {
month,
Expand Down Expand Up @@ -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,
Expand Down
112 changes: 112 additions & 0 deletions core/src/components/datetime/test/presentation/datetime.e2e.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
46 changes: 0 additions & 46 deletions core/src/components/datetime/test/presentation/e2e.ts

This file was deleted.

76 changes: 24 additions & 52 deletions core/src/components/datetime/test/presentation/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,73 +9,45 @@
<script src="../../../../../scripts/testing/scripts.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(250px, 1fr));
grid-gap: 60px 20px;
}
h2 {
font-size: 12px;
font-weight: normal;

color: #6f7378;

margin-top: 10px;
margin-left: 5px;
}

@media screen and (max-width: 800px) {
.grid {
grid-template-columns: 1fr;
padding: 0;
}
}

ion-datetime {
box-shadow: 0px 16px 32px rgba(0, 0, 0, 0.25), 0px 8px 16px rgba(0, 0, 0, 0.25);
border-radius: 8px;
}
</style>
</head>

<body>
<ion-app>
<ion-header translucent="true">
<ion-toolbar>
<ion-title>Datetime - Presentation</ion-title>
</ion-toolbar>
<ion-toolbar>
<select class="ion-margin-end" slot="end">
<option value="date-time">date-time</option>
<option value="time-date">time-date</option>
<option value="time">time</option>
<option value="date">date</option>
<option value="month-year">month-year</option>
<option value="month">month</option>
<option value="year">year</option>
</select>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<div class="grid">
<div class="grid-item">
<h2>date-time</h2>
<ion-datetime presentation="date-time"></ion-datetime>
</div>
<div class="grid-item">
<h2>time-date</h2>
<ion-datetime presentation="time-date"></ion-datetime>
</div>
<div class="grid-item">
<h2>time</h2>
<ion-datetime presentation="time"></ion-datetime>
</div>
<div class="grid-item">
<h2>date</h2>
<ion-datetime presentation="date"></ion-datetime>
</div>
<div class="grid-item">
<h2>month-year</h2>
<ion-datetime presentation="month-year"></ion-datetime>
</div>
<div class="grid-item">
<h2>month</h2>
<ion-datetime presentation="month"></ion-datetime>
</div>
<div class="grid-item">
<h2>year</h2>
<ion-datetime presentation="year"></ion-datetime>
</div>
</div>
<ion-datetime presentation="date-time"></ion-datetime>
</ion-content>
</ion-app>
<script>
const datetime = document.querySelector('ion-datetime');
const select = document.querySelector('select');

datetime.value = '2022-03-10T13:00:00';

select.addEventListener('change', (ev) => {
const presentation = ev.target.value;
datetime.presentation = presentation;
});
</script>
</body>
</html>
13 changes: 7 additions & 6 deletions core/src/components/datetime/test/set-value/e2e.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import type { E2EPage } from '@stencil/core/testing';
import { newE2EPage } from '@stencil/core/testing';

describe('datetime: setting the value', () => {
it('should update the active date', async () => {
const page = await newE2EPage({
let page: E2EPage;

beforeEach(async () => {
page = await newE2EPage({
url: '/src/components/datetime/test/set-value?ionic:_testing=true',
});
});

it('should update the active date', async () => {
await page.$eval('ion-datetime', (elm: any) => {
elm.value = '2021-11-25T12:40:00.000Z';
});
Expand All @@ -18,10 +23,6 @@ describe('datetime: setting the value', () => {
});

it('should update the active time', async () => {
const page = await newE2EPage({
url: '/src/components/datetime/test/set-value?ionic:_testing=true',
});

await page.$eval('ion-datetime', (elm: any) => {
elm.value = '2021-11-25T12:40:00.000Z';
});
Expand Down
3 changes: 1 addition & 2 deletions core/src/components/popover/test/basic/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const DIRECTORY = 'basic';
* to wait for the requestAnimationFrame to fire.
*/
const expectActiveElementTextToEqual = async (page: E2EPage, textValue: string) => {
await page.evaluate((text) => document.activeElement!.textContent === text, textValue)
await page.evaluate((text) => document.activeElement!.textContent === text, textValue);
};

const getActiveElementSelectionStart = (page: E2EPage) => {
Expand Down Expand Up @@ -104,7 +104,6 @@ test('popover: htmlAttributes', async () => {
});

describe('popover: focus trap', () => {

it('should focus the first ion-item on ArrowDown', async () => {
const page = await newE2EPage({ url: '/src/components/popover/test/basic?ionic:_testing=true' });

Expand Down
Loading