Skip to content
47 changes: 43 additions & 4 deletions packages/main/cypress/specs/DateRangePicker.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,47 @@ 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(<DateRangePicker></DateRangePicker>);

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(<DateRangePicker displayFormat="dd/MM/yyyy"></DateRangePicker>);

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", () => {
Expand Down Expand Up @@ -1186,8 +1227,8 @@ describe("DateRangePicker - Two Calendars Feature", () => {

it("should respect min/max date constraints with two calendars", () => {
cy.mount(
<DateRangePicker
showTwoMonths={true}
<DateRangePicker
showTwoMonths={true}
formatPattern="dd/MM/yyyy"
minDate="10/01/2024"
maxDate="28/02/2024"
Expand Down Expand Up @@ -1439,5 +1480,3 @@ describe("DateRangePicker - Two Calendars Feature", () => {
});
});
});


39 changes: 38 additions & 1 deletion packages/main/src/DateRangePicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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";`
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
}

Expand Down
Loading