Skip to content
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

fix(datetime): arrow navigation respects min/max values #25182

Merged
merged 14 commits into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
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
52 changes: 52 additions & 0 deletions core/src/components/datetime/test/minmax/datetime.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';

test.describe('datetime: minmax', () => {

test('calendar arrow navigation should respect min/max values', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/25073'
});

await page.setContent(`
<ion-datetime min="2022-04-22" max="2022-05-21" value="2022-04-22T10:00:00"></ion-datetime>

<script>
const observer = new MutationObserver((mutationRecords) => {
if (mutationRecords) {
window.dispatchEvent(new CustomEvent('datetimeMonthDidChange'));
}
});

const initDatetimeChangeEvent = () => {
observer.observe(document.querySelector('ion-datetime').shadowRoot.querySelector('.calendar-body'), {
subtree: true,
childList: true
});
}
</script>
`);

await page.waitForSelector('.datetime-ready');

const prevButton = page.locator('ion-datetime .calendar-next-prev ion-button:nth-child(1)');
const nextButton = page.locator('ion-datetime .calendar-next-prev ion-button:nth-child(2)');

expect(nextButton).toBeEnabled();
expect(prevButton).toBeDisabled();

await page.evaluate('initDatetimeChangeEvent()');

const monthDidChangeSpy = await page.spyOnEvent('datetimeMonthDidChange');

await nextButton.click();
await page.waitForChanges();

await monthDidChangeSpy.next();

expect(nextButton).toBeDisabled();
expect(prevButton).toBeEnabled();
});

});
4 changes: 2 additions & 2 deletions core/src/components/datetime/utils/comparison.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const isBefore = (baseParts: DatetimeParts, compareParts: DatetimeParts)
(baseParts.year === compareParts.year && baseParts.month < compareParts.month) ||
(baseParts.year === compareParts.year &&
baseParts.month === compareParts.month &&
baseParts.day! < compareParts.day!)
baseParts.day && baseParts.day < compareParts.day!)
);
};

Expand All @@ -31,6 +31,6 @@ export const isAfter = (baseParts: DatetimeParts, compareParts: DatetimeParts) =
(baseParts.year === compareParts.year && baseParts.month > compareParts.month) ||
(baseParts.year === compareParts.year &&
baseParts.month === compareParts.month &&
baseParts.day! > compareParts.day!)
baseParts.day && baseParts.day > compareParts.day!)
);
};
10 changes: 8 additions & 2 deletions core/src/components/datetime/utils/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,10 @@ export const isMonthDisabled = (
* previous navigation button is disabled.
*/
export const isPrevMonthDisabled = (refParts: DatetimeParts, minParts?: DatetimeParts, maxParts?: DatetimeParts) => {
const prevMonth = getPreviousMonth(refParts);
const prevMonth = {
...getPreviousMonth(refParts),
day: null,
};
return isMonthDisabled(prevMonth, {
minParts,
maxParts,
Expand All @@ -152,7 +155,10 @@ export const isPrevMonthDisabled = (refParts: DatetimeParts, minParts?: Datetime
* determine if the next navigation button is disabled.
*/
export const isNextMonthDisabled = (refParts: DatetimeParts, maxParts?: DatetimeParts) => {
const nextMonth = getNextMonth(refParts);
const nextMonth = {
...getNextMonth(refParts),
day: null,
};
return isMonthDisabled(nextMonth, {
maxParts,
});
Expand Down
12 changes: 12 additions & 0 deletions core/src/utils/test/playwright/page/utils/goto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ export const goto = async (page: Page, url: string, testInfo: TestInfo, original

const formattedUrl = `${splitUrl[0]}?ionic:_testing=${ionicTesting}&ionic:mode=${formattedMode}&rtl=${formattedRtl}`;

testInfo.annotations.push({
type: 'mode',
description: formattedMode
});

if (rtl) {
testInfo.annotations.push({
type: 'rtl',
description: 'true'
});
}

const result = await Promise.all([
page.waitForFunction(() => (window as any).testAppLoaded === true, { timeout: 4750 }),
originalFn(formattedUrl),
Expand Down
1 change: 1 addition & 0 deletions core/src/utils/test/playwright/page/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './goto';
export * from './get-snapshot-settings';
export * from './set-ion-viewport';
export * from './spy-on-event';
export * from './set-content';
59 changes: 59 additions & 0 deletions core/src/utils/test/playwright/page/utils/set-content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { Page } from '@playwright/test';

/**
* Overwrites the default Playwright page.setContent method.
*
* Navigates to a blank page, sets the content, and waits for the
* Stencil components to be hydrated before proceeding with the test.
*
* @param page The Playwright page object.
* @param html The HTML content to set on the page.
*/
export const setContent = async (page: Page, html: string) => {
if (page.isClosed()) {
throw new Error('setContent unavailable: page is already closed');
}

const baseUrl = process.env.PLAYWRIGHT_TEST_BASE_URL;

const output = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link href="${baseUrl}/css/ionic.bundle.css" rel="stylesheet" />
<link href="${baseUrl}/scripts/testing/styles.css" rel="stylesheet" />
<script src="${baseUrl}/scripts/testing/scripts.js"></script>
<script type="module" src="${baseUrl}/dist/ionic/ionic.esm.js"></script>

</head>
<body>
${html}
</body>
</html>
`;

if (baseUrl) {
await page.route(baseUrl, route => {
if (route.request().url() === `${baseUrl}/`) {
/**
* Intercepts the empty page request and returns the
* HTML content that was passed in.
*/
route.fulfill({
status: 200,
contentType: 'text/html',
body: output
});
} else {
// Allow all other requests to pass through
route.continue();
}
});

await page.goto(`${baseUrl}#`);
}

await page.waitForFunction(() => (window as any).testAppLoaded === true, { timeout: 4750 });
}
6 changes: 0 additions & 6 deletions core/src/utils/test/playwright/playwright-declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@ export interface E2EPage extends Page {
* we need to wait until the changes have been applied to the DOM.
*/
waitForChanges: (timeoutMs?: number) => Promise<void>;
/**
* Listens on the window for a specific event to be dispatched.
* Will wait a maximum of 5 seconds for the event to be dispatched.
*/
waitForCustomEvent: (eventName: string) => Promise<Page>;

/**
* Creates a new EventSpy and listens
* on the window for an event.
Expand Down
2 changes: 2 additions & 0 deletions core/src/utils/test/playwright/playwright-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { initPageEvents } from './page/event-spy';
import {
getSnapshotSettings,
goto as goToPage,
setContent,
setIonViewport,
spyOnEvent,
waitForChanges,
Expand All @@ -34,6 +35,7 @@ export const test = base.extend<CustomFixtures>({

// Overridden Playwright methods
page.goto = (url: string) => goToPage(page, url, testInfo, originalGoto);
page.setContent = (html: string) => setContent(page, html);
// Custom Ionic methods
page.getSnapshotSettings = () => getSnapshotSettings(page, testInfo);
page.setIonViewport = () => setIonViewport(page);
Expand Down