From 5dfaf63c6582811b61339a6fa50cf551cd8724d0 Mon Sep 17 00:00:00 2001 From: Amanda Johnston <90629384+amandaejohnston@users.noreply.github.com> Date: Wed, 22 Jun 2022 13:38:32 -0500 Subject: [PATCH] fix(datetime): add dev warnings when setting out of bounds value (#25513) --- core/src/components/datetime/datetime.tsx | 9 +++++- .../datetime/test/minmax/datetime.e2e.ts | 29 +++++++++++++++++++ .../components/datetime/utils/comparison.ts | 13 +++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/core/src/components/datetime/datetime.tsx b/core/src/components/datetime/datetime.tsx index d9e9c518dd..ae07f5819f 100644 --- a/core/src/components/datetime/datetime.tsx +++ b/core/src/components/datetime/datetime.tsx @@ -11,6 +11,7 @@ import { isRTL } from '../../utils/rtl'; import { createColorClasses } from '../../utils/theme'; import type { PickerColumnItem } from '../picker-column-internal/picker-column-internal-interfaces'; +import { warnIfValueOutOfBounds } from './utils/comparison'; import { generateMonths, generateTime, @@ -326,6 +327,8 @@ export class Datetime implements ComponentInterface { */ const valueDateParts = parseDate(this.value); if (valueDateParts) { + warnIfValueOutOfBounds(valueDateParts, this.minParts, this.maxParts); + const { month, day, year, hour, minute } = valueDateParts; const ampm = hour >= 12 ? 'pm' : 'am'; @@ -1084,7 +1087,11 @@ export class Datetime implements ComponentInterface { private processValue = (value?: string | null) => { this.highlightActiveParts = !!value; const valueToProcess = parseDate(value || getToday()); - const { month, day, year, hour, minute, tzOffset } = clampDate(valueToProcess, this.minParts, this.maxParts); + + const { minParts, maxParts } = this; + warnIfValueOutOfBounds(valueToProcess, minParts, maxParts); + + const { month, day, year, hour, minute, tzOffset } = clampDate(valueToProcess, minParts, maxParts); this.setWorkingParts({ month, diff --git a/core/src/components/datetime/test/minmax/datetime.e2e.ts b/core/src/components/datetime/test/minmax/datetime.e2e.ts index ad3996f640..badc50abd4 100644 --- a/core/src/components/datetime/test/minmax/datetime.e2e.ts +++ b/core/src/components/datetime/test/minmax/datetime.e2e.ts @@ -1,4 +1,5 @@ import { expect } from '@playwright/test'; +import type { E2EPage } from '@utils/test/playwright'; import { test } from '@utils/test/playwright'; test.describe('datetime: minmax', () => { @@ -47,6 +48,7 @@ test.describe('datetime: minmax', () => { expect(nextButton).toBeDisabled(); expect(prevButton).toBeEnabled(); }); + test('datetime: minmax months disabled', async ({ page }) => { await page.goto('/src/components/datetime/test/minmax'); const calendarMonths = page.locator('ion-datetime#inside .calendar-month'); @@ -67,6 +69,7 @@ test.describe('datetime: minmax', () => { expect(navButtons.nth(0)).toHaveAttribute('disabled', ''); expect(navButtons.nth(1)).toHaveAttribute('disabled', ''); }); + test('datetime: min including day should not disable month', async ({ page }) => { await page.goto('/src/components/datetime/test/minmax'); await page.waitForSelector('.datetime-ready'); @@ -77,6 +80,7 @@ test.describe('datetime: minmax', () => { expect(calendarMonths.nth(1)).not.toHaveClass(/calendar-month-disabled/); expect(calendarMonths.nth(2)).not.toHaveClass(/calendar-month-disabled/); }); + test.describe('when the datetime does not have a value', () => { test('all time values should be available for selection', async ({ page }) => { /** @@ -105,4 +109,29 @@ test.describe('datetime: minmax', () => { expect(await minutes.count()).toBe(60); }); }); + + test.describe('setting value outside bounds should show in-bounds month', () => { + const testDisplayedMonth = async (page: E2EPage, content: string) => { + await page.setContent(content); + await page.waitForSelector('.datetime-ready'); + + const calendarMonthYear = page.locator('ion-datetime .calendar-month-year'); + expect(calendarMonthYear).toHaveText('June 2021'); + }; + + test('when min is defined', async ({ page }) => { + await testDisplayedMonth(page, ``); + }); + + test('when max is defined', async ({ page }) => { + await testDisplayedMonth(page, ``); + }); + + test('when both min and max are defined', async ({ page }) => { + await testDisplayedMonth( + page, + `` + ); + }); + }); }); diff --git a/core/src/components/datetime/utils/comparison.ts b/core/src/components/datetime/utils/comparison.ts index 2e64fb5776..a70f254d1e 100644 --- a/core/src/components/datetime/utils/comparison.ts +++ b/core/src/components/datetime/utils/comparison.ts @@ -1,3 +1,5 @@ +import { printIonWarning } from '@utils/logging'; + import type { DatetimeParts } from '../datetime-interface'; /** @@ -36,3 +38,14 @@ export const isAfter = (baseParts: DatetimeParts, compareParts: DatetimeParts) = baseParts.day > compareParts.day!) ); }; + +export const warnIfValueOutOfBounds = (value: DatetimeParts, min: DatetimeParts, max: DatetimeParts) => { + if ((min && isBefore(value, min)) || (max && isAfter(value, max))) { + printIonWarning( + 'The value provided to ion-datetime is out of bounds.\n\n' + + `Min: ${JSON.stringify(min)}\n` + + `Max: ${JSON.stringify(max)}\n` + + `Value: ${JSON.stringify(value)}` + ); + } +};