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)}`
+ );
+ }
+};