Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
419 changes: 150 additions & 269 deletions packages/main/cypress/specs/DynamicDateRange.cy.tsx

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/main/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import "./commands/ColorPalettePopover.commands.js";
import "./commands/ColorPicker.commands.js";
import "./commands/DateTimePicker.commands.js";
import "./commands/DateRangePicker.commands.js";
import "./commands/DynamicDateRange.commands.js";
import "./commands/Dialog.commands.ts";
import "./commands/Popover.commands.ts";
import "./commands/ResponsivePopover.commands.js";
Expand Down
135 changes: 135 additions & 0 deletions packages/main/cypress/support/commands/DynamicDateRange.commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
Cypress.Commands.add("ui5DynamicDateRangeOpen", { prevSubject: true }, (prevSubject) => {
cy.wrap(prevSubject)
.as("ddr")
.shadow()
.find("[ui5-input]")
.as("input");

cy.get("@input")
.shadow()
.find("input")
.as("innerInput");

cy.get("@input")
.find('[ui5-icon]')
.as("icon");

cy.get("@icon")
.realClick();

cy.get("@ddr")
.ui5DynamicDateRangeOpened();

return cy.wrap(prevSubject);
});

Cypress.Commands.add("ui5DynamicDateRangeOpened", { prevSubject: true }, (subject) => {
cy.wrap(subject)
.as("ddr");

cy.get("@ddr")
.should("have.attr", "open");

cy.get("@ddr")
.shadow()
.find("[ui5-responsive-popover]")
.as("popover")
.should("have.attr", "open");

cy.get("@popover")
.find("[ui5-list]")
.as("list")
.should("be.visible");
});

Cypress.Commands.add("ui5DynamicDateRangeGetOptionsList", { prevSubject: true }, (subject) => {
cy.wrap(subject)
.as("ddr");

cy.get("@ddr")
.shadow()
.find("[ui5-responsive-popover]")
.as("popover");

cy.get("@popover")
.should('exist');

cy.get("@popover")
.find("[ui5-list]")
.as("list");

return cy.get('@list')
.find("[ui5-li]");
});

Cypress.Commands.add("ui5DynamicDateRangeSelectOption", { prevSubject: true }, (prevSubject, index?: number) => {
const optionIndex = index ?? 0;

cy.wrap(prevSubject)
.as("ddr");

cy.get("@ddr")
.ui5DynamicDateRangeGetOptionsList()
.eq(optionIndex)
.realClick();

return cy.wrap(prevSubject);
});

Cypress.Commands.add("ui5DynamicDateRangeSetDateTime", { prevSubject: true }, (prevSubject, pickerId: string, dateTimeValue: string) => {
cy.wrap(prevSubject)
.as("ddr");

cy.get("@ddr")
.shadow()
.find("[ui5-responsive-popover]")
.find(`[ui5-datetime-picker]#${pickerId}`)
.as("picker");

cy.get("@picker")
.shadow()
.find("[ui5-datetime-input]")
.as("input");

cy.get("@input")
.shadow()
.find("input")
.as("innerInput");

cy.get("@innerInput")
.clear()
.realType(dateTimeValue)
.realPress("Enter");

return cy.wrap(prevSubject);
});

Cypress.Commands.add("ui5DynamicDateRangeSubmit", { prevSubject: true }, (prevSubject) => {
cy.wrap(prevSubject)
.as("ddr");

cy.get("@ddr")
.shadow()
.find("[ui5-responsive-popover]")
.as("popover");

cy.get("@popover")
.find("[ui5-button][design='Emphasized']")
.as("submitButton");

cy.get("@submitButton")
.realClick();
});

declare global {
namespace Cypress {
interface Chainable {
ui5DynamicDateRangeOpen(): Chainable<JQuery<HTMLElement>>
ui5DynamicDateRangeOpened(): Chainable<void>
ui5DynamicDateRangeGetOptionsList(): Chainable<JQuery<HTMLElement>>
ui5DynamicDateRangeSelectOption(index?: number): Chainable<JQuery<HTMLElement>>
ui5DynamicDateRangeSetDateTime(pickerId: string, dateTimeValue: string): Chainable<JQuery<HTMLElement>>
ui5DynamicDateRangeSubmit(): Chainable<void>
}
}
}
1 change: 1 addition & 0 deletions packages/main/src/DynamicDateRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ interface IDynamicDateRangeOption {
* - "TOMORROW" - Represents the next date. An example value is `{ operator: "TOMORROW"}`. Import: `import "@ui5/webcomponents/dist/dynamic-date-range-options/Tomorrow.js";`
* - "DATE" - Represents a single date. An example value is `{ operator: "DATE", values: [new Date()]}`. Import: `import "@ui5/webcomponents/dist/dynamic-date-range-options/SingleDate.js";`
* - "DATERANGE" - Represents a range of dates. An example value is `{ operator: "DATERANGE", values: [new Date(), new Date()]}`. Import: `import "@ui5/webcomponents/dist/dynamic-date-range-options/DateRange.js";`
* - "DATETIMERANGE" - Represents a range of dates with times. An example value is `{ operator: "DATETIMERANGE", values: [new Date(), new Date()]}`. Import: `import "@ui5/webcomponents/dist/dynamic-date-range-options/DateTimeRange.js";`
* - "FROMDATETIME" - Represents a range from date and time. An example value is `{ operator: "FROMDATETIME", values: [new Date()]}`. Import: `import "@ui5/webcomponents/dist/dynamic-date-range-options/FromDateTime.js";`
* - "TODATETIME" - Represents a range to date and time. An example value is `{ operator: "TODATETIME", values: [new Date()]}`. Import: `import "@ui5/webcomponents/dist/dynamic-date-range-options/ToDateTime.js";`
* - "LASTDAYS" - Represents Last X Days from today. An example value is `{ operator: "LASTDAYS", values: [2]}`. Import: `import "@ui5/webcomponents/dist/dynamic-date-range-options/LastOptions.js";`
Expand Down
1 change: 1 addition & 0 deletions packages/main/src/bundle.esm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import Yesterday from "./dynamic-date-range-options/Yesterday.js";
import Tomorrow from "./dynamic-date-range-options/Tomorrow.js";
import SingleDate from "./dynamic-date-range-options/SingleDate.js";
import DateRange from "./dynamic-date-range-options/DateRange.js";
import DateTimeRange from "./dynamic-date-range-options/DateTimeRange.js";
import FromDateTime from "./dynamic-date-range-options/FromDateTime.js";
import ToDateTime from "./dynamic-date-range-options/ToDateTime.js";
import ExpandableText from "./ExpandableText.js";
Expand Down
112 changes: 112 additions & 0 deletions packages/main/src/dynamic-date-range-options/DateTimeRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import DateTimeRangeTemplate from "./DateTimeRangeTemplate.js";
import type { DynamicDateRangeValue, IDynamicDateRangeOption } from "../DynamicDateRange.js";
import DateFormat from "@ui5/webcomponents-localization/dist/DateFormat.js";
import type { JsxTemplate } from "@ui5/webcomponents-base/dist/index.js";
import {
DYNAMIC_DATE_TIME_RANGE_TEXT,
} from "../generated/i18n/i18n-defaults.js";
import { dateTimeRangeOptionToDates } from "./toDates.js";
import DynamicDateRange from "../DynamicDateRange.js";
import getCachedLocaleDataInstance from "@ui5/webcomponents-localization/dist/getCachedLocaleDataInstance.js";
import getLocale from "@ui5/webcomponents-base/dist/locale/getLocale.js";

const DEFAULT_DELIMITER = "-";

/**
* @class
* @constructor
* @public
* @since 2.16.0
*/

class DateTimeRange implements IDynamicDateRangeOption {
template: JsxTemplate;

constructor() {
this.template = DateTimeRangeTemplate;
}

parse(value: string): DynamicDateRangeValue {
const returnValue = { operator: this.operator, values: [] } as DynamicDateRangeValue;

if (!value) {
return returnValue;
}
const splitValue = value.split(DEFAULT_DELIMITER);
const startDate = this._parseDate(splitValue[0].trim()) as Date;
const endDate = this._parseDate(splitValue[1].trim()) as Date;

returnValue.values = [startDate, endDate];

if (returnValue.values[0].getTime() > returnValue.values[1].getTime()) {
returnValue.values.reverse();
}

return returnValue;
}

_parseDate(value: string): Date | undefined {
return this.getFormat().parse(value) as Date;
}

format(value: DynamicDateRangeValue) {
const valuesArray = value?.values as Array<Date>;

if (!valuesArray || valuesArray.length !== 2 || !valuesArray[0] || !valuesArray[1]) {
return "";
}

const startDate = this._formatDate(valuesArray[0]);
const endDate = this._formatDate(valuesArray[1]);

return `${startDate} ${DEFAULT_DELIMITER} ${endDate}`;
}

_formatDate(date: Date) {
return this.getFormat().format(date);
}

toDates(value: DynamicDateRangeValue): Array<Date> {
return dateTimeRangeOptionToDates(value);
}

get text(): string {
return DynamicDateRange.i18nBundle.getText(DYNAMIC_DATE_TIME_RANGE_TEXT);
}

get operator() {
return "DATETIMERANGE";
}

get icon() {
return "appointment-2";
}

isValidString(value: string): boolean {
const splitValue = value.split(DEFAULT_DELIMITER);
const startDate = this._parseDate(splitValue[0].trim()) as Date;
const endDate = this._parseDate(splitValue[1].trim()) as Date;

if (!startDate || !endDate || Number.isNaN(startDate.getTime()) || Number.isNaN(endDate.getTime())) {
return false;
}

return true;
}

getFormatPattern() {
const localeData = getCachedLocaleDataInstance(getLocale());
return localeData.getCombinedDateTimePattern("medium", "medium");
}

getFormat(): DateFormat {
return DateFormat.getDateInstance({
strictParsing: true,
pattern: this.getFormatPattern(),
});
}
}

DynamicDateRange.register("DATETIMERANGE", DateTimeRange);

export default DateTimeRange;
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import DynamicDateRange from "../DynamicDateRange.js";
import DateTimePicker from "../DateTimePicker.js";
import Label from "../Label.js";
import {
DYNAMIC_DATE_TIME_RANGE_TEXT_TO_LABEL,
DYNAMIC_DATE_TIME_RANGE_TEXT_FROM_LABEL,
} from "../generated/i18n/i18n-defaults.js";

export default function DateTimeRangeTemplate(this: DynamicDateRange) {
const currentOperator = this.currentValue?.operator || "DATETIMERANGE";

const getDateFromValue = (index = 0) => {
if (this.value?.operator === currentOperator && this.value.values && this.value.values.length === 2) {
return this.getOption(this.value.operator)?.format(this.value)?.split("-")[index].trim();
}
return undefined;
};

const handleSelectionChange = () => {
const fromPicker = this.shadowRoot?.querySelector("[ui5-datetime-picker]#from-picker") as DateTimePicker;
const toPicker = this.shadowRoot?.querySelector("[ui5-datetime-picker]#to-picker") as DateTimePicker;

const fromDateValue = fromPicker.dateValue;
const toDateValue = toPicker.dateValue;

// If there are no dates selected, clear the value
if (!(fromDateValue && toDateValue)) {
this.currentValue = {
operator: currentOperator,
values: []
};
return;
}

const newValue = [fromDateValue, toDateValue];

if (newValue[0] && newValue[1] && newValue[0].getTime() > newValue[1].getTime()) {
newValue.reverse();
}

this.currentValue = {
operator: currentOperator,
values: newValue,
};
};
return (
<div class="ui5-last-next-container ui5-last-next-container-padded">
<Label class="ui5-ddr-label">{DynamicDateRange.i18nBundle.getText(DYNAMIC_DATE_TIME_RANGE_TEXT_FROM_LABEL)}</Label>
<DateTimePicker
id="from-picker"
onChange={handleSelectionChange}
value={getDateFromValue()}>
</DateTimePicker>
<Label class="ui5-ddr-label">{DynamicDateRange.i18nBundle.getText(DYNAMIC_DATE_TIME_RANGE_TEXT_TO_LABEL)}</Label>
<DateTimePicker
id="to-picker"
onChange={handleSelectionChange}
value={getDateFromValue(1)}>
</DateTimePicker>
</div>
);
}
20 changes: 20 additions & 0 deletions packages/main/src/dynamic-date-range-options/toDates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import type { DynamicDateRangeValue, IDynamicDateRangeOption } from "../DynamicD
import UI5Date from "@ui5/webcomponents-localization/dist/dates/UI5Date.js";

const dateOptionToDates = (value: DynamicDateRangeValue): Array<Date> => {
if (!value || !value.values || value.values.length !== 1) {
return [];
}

const startDate = value.values ? value.values[0] as Date : UI5Date.getInstance();
const endDate = UI5Date.getInstance(startDate.getTime());

Expand All @@ -12,6 +16,10 @@ const dateOptionToDates = (value: DynamicDateRangeValue): Array<Date> => {
};

const dateRangeOptionToDates = (value: DynamicDateRangeValue): Array<Date> => {
if (!value || !value.values || value.values.length !== 2) {
return [];
}

const startDate = value.values ? value.values[0] as Date : UI5Date.getInstance();
const endDate = value.values ? value.values[1] as Date : UI5Date.getInstance();

Expand All @@ -21,6 +29,17 @@ const dateRangeOptionToDates = (value: DynamicDateRangeValue): Array<Date> => {
return [startDate, endDate];
};

const dateTimeRangeOptionToDates = (value: DynamicDateRangeValue): Array<Date> => {
if (!value || !value.values || value.values.length !== 2) {
return [];
}

const startDate = value.values ? value.values[0] as Date : UI5Date.getInstance();
const endDate = value.values ? value.values[1] as Date : UI5Date.getInstance();

return [startDate, endDate];
};

const todayToDates = (): Array<Date> => {
const startDate = UI5Date.getInstance();
const endDate = UI5Date.getInstance();
Expand Down Expand Up @@ -194,6 +213,7 @@ const dateTimeOptionToDates = (value: DynamicDateRangeValue): Array<Date> => {
export {
dateOptionToDates,
dateRangeOptionToDates,
dateTimeRangeOptionToDates,
todayToDates,
tomorrowToDates,
yesterdayToDates,
Expand Down
6 changes: 6 additions & 0 deletions packages/main/src/i18n/messagebundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,12 @@ DYNAMIC_DATE_RANGE_TO_TEXT=To (Date Time)
#XFLD: Text for the selected date in the DynamicDateRange component.
DYNAMIC_DATE_RANGE_SELECTED_TEXT=Selected

DYNAMIC_DATE_TIME_RANGE_TEXT=From / To (Date and Time)

DYNAMIC_DATE_TIME_RANGE_TEXT_TO_LABEL=To

DYNAMIC_DATE_TIME_RANGE_TEXT_FROM_LABEL=From

#FLD: Text for the selected date section when there's no value in the DynamicDateRange component.
DYNAMIC_DATE_RANGE_EMPTY_SELECTED_TEXT=Choose Dates

Expand Down
Loading
Loading