diff --git a/packages/main/cypress/specs/DateRangePicker.cy.tsx b/packages/main/cypress/specs/DateRangePicker.cy.tsx
index 340c2efc9929..7ea4ea872d98 100644
--- a/packages/main/cypress/specs/DateRangePicker.cy.tsx
+++ b/packages/main/cypress/specs/DateRangePicker.cy.tsx
@@ -970,11 +970,52 @@ describe("Validation inside a form", () => {
});
});
+describe("DateRangePicker rejects relative dates", () => {
+ const relativeKeywords = ["today", "tomorrow", "yesterday"];
+
+ relativeKeywords.forEach(keyword => {
+ it(`typing '${keyword}' sets error state`, () => {
+ cy.mount();
+
+ cy.get("[ui5-daterange-picker]")
+ .as("dateRangePicker")
+ .shadow()
+ .find("[ui5-datetime-input]")
+ .realClick()
+ .should("be.focused");
+
+ cy.realType(keyword);
+ cy.realPress("Enter");
+
+ cy.get("@dateRangePicker")
+ .should("have.value", keyword)
+ .should("have.attr", "value-state", "Negative");
+ });
+ });
+
+ it("valid concrete date range does not set error state", () => {
+ cy.mount();
+
+ cy.get("[ui5-daterange-picker]")
+ .as("dateRangePicker")
+ .shadow()
+ .find("[ui5-datetime-input]")
+ .realClick()
+ .should("be.focused");
+
+ cy.realType("09/09/2020 - 10/10/2020");
+ cy.realPress("Enter");
+
+ cy.get("@dateRangePicker")
+ .should("have.attr", "value-state", "None");
+ });
+});
+
describe("DateRangePicker - Two Calendars Feature", () => {
describe("Basic Two Calendars Display", () => {
it("should display two calendars when showTwoMonths is true", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -993,7 +1034,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
it("should display one calendar when showTwoMonths is false", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1015,7 +1056,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
it("should show consecutive months in two calendars mode", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1040,7 +1081,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
it("should dynamically toggle showTwoMonths after initial render", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1143,7 +1184,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
it("should highlight selection across both calendars", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1161,7 +1202,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
it("should update value when selecting new range", () => {
cy.mount(
-
+
);
const changeSpy = cy.spy().as("changeSpy");
@@ -1186,8 +1227,8 @@ describe("DateRangePicker - Two Calendars Feature", () => {
it("should respect min/max date constraints with two calendars", () => {
cy.mount(
- {
describe("Navigation in Two Calendars Mode", () => {
it("should navigate both calendars forward", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1245,7 +1286,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
it("should navigate both calendars backward", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1268,7 +1309,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
describe("Picker Overlays", () => {
it("should show month picker overlay when clicking month button", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1297,7 +1338,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
it("should show year picker overlay when clicking year button", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1319,7 +1360,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
it("should return to day pickers after selecting from month picker", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1356,7 +1397,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
describe("Keyboard Navigation", () => {
it("should allow keyboard navigation through header buttons", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1413,7 +1454,7 @@ describe("DateRangePicker - Two Calendars Feature", () => {
describe("Edge Cases", () => {
it("should handle year boundary correctly", () => {
cy.mount(
-
+
);
cy.get("[ui5-daterange-picker]")
@@ -1447,5 +1488,3 @@ describe("DateRangePicker - Two Calendars Feature", () => {
});
});
});
-
-
diff --git a/packages/main/src/DateRangePicker.ts b/packages/main/src/DateRangePicker.ts
index 397a032e2c05..6e7d2fa97b75 100644
--- a/packages/main/src/DateRangePicker.ts
+++ b/packages/main/src/DateRangePicker.ts
@@ -6,6 +6,7 @@ import CalendarDate from "@ui5/webcomponents-localization/dist/dates/CalendarDat
import UI5Date from "@ui5/webcomponents-localization/dist/dates/UI5Date.js";
import modifyDateBy from "@ui5/webcomponents-localization/dist/dates/modifyDateBy.js";
import getTodayUTCTimestamp from "@ui5/webcomponents-localization/dist/dates/getTodayUTCTimestamp.js";
+import type DateFormat from "@ui5/webcomponents-localization/dist/DateFormat.js";
import {
DATERANGE_DESCRIPTION,
DATERANGEPICKER_POPOVER_ACCESSIBLE_NAME,
@@ -42,7 +43,10 @@ const DEFAULT_DELIMITER = "-";
* ### Usage
* The user can enter a date by:
* Using the calendar that opens in a popup or typing it in directly in the input field (not available for mobile devices).
- * For the `ui5-daterange-picker`
+ * For the `ui5-daterange-picker`:
+ *
+ * **Note:** Relative date values such as "today", "yesterday", or "tomorrow" are not supported.
+ * Entering a relative date sets the component to an error state.
* ### ES6 Module Import
*
* `import "@ui5/webcomponents/dist/DateRangePicker.js";`
@@ -155,6 +159,27 @@ class DateRangePicker extends DatePicker implements IFormInputElement {
this._prevDelimiter = null;
}
+ /**
+ * Checks if a date string is a relative date (e.g. "today", "tomorrow")
+ * that would be resolved by DateFormat.parseRelative().
+ * Relative dates are not supported in DateRangePicker.
+ * @private
+ */
+ _isRelativeValue(dateString: string, format: DateFormat): boolean {
+ const trimmed = dateString.trim();
+ if (!trimmed) {
+ return false;
+ }
+
+ const parsed = format.parse(trimmed);
+ if (!parsed) {
+ return false;
+ }
+
+ const formatted = format.format(parsed);
+ return formatted !== trimmed;
+ }
+
/**
* **Note:** The getter method is inherited and not supported. If called it will return an empty value.
* @public
@@ -329,6 +354,10 @@ class DateRangePicker extends DatePicker implements IFormInputElement {
isValid(value: string): boolean {
const parts = this._splitValueByDelimiter(value).filter(str => str.trim() !== "");
+ if (parts.some(dateString => this._isRelativeValue(dateString, this.getFormat()))) {
+ return false;
+ }
+
return parts.length <= 2 && parts.every(dateString => super.isValid(dateString)); // must be at most 2 dates and each must be valid
}
@@ -340,6 +369,10 @@ class DateRangePicker extends DatePicker implements IFormInputElement {
isValidValue(value: string): boolean {
const parts = this._splitValueByDelimiter(value).filter(str => str.trim() !== "");
+ if (parts.some(dateString => this._isRelativeValue(dateString, this.getValueFormat()))) {
+ return false;
+ }
+
return parts.length <= 2 && parts.every(dateString => super.isValidValue(dateString)); // must be at most 2 dates and each must be valid
}
@@ -351,6 +384,10 @@ class DateRangePicker extends DatePicker implements IFormInputElement {
isValidDisplayValue(value: string): boolean {
const parts = this._splitValueByDelimiter(value).filter(str => str.trim() !== "");
+ if (parts.some(dateString => this._isRelativeValue(dateString, this.getDisplayFormat()))) {
+ return false;
+ }
+
return parts.length <= 2 && parts.every(dateString => super.isValidDisplayValue(dateString)); // must be at most 2 dates and each must be valid
}