Skip to content

Commit 2acdbcc

Browse files
feat(ui5-date-picker): add valueFormat and displayFormat (#11751)
Add support for separate value and display formatting in DatePicker component. Add valueFormat property to control internal value representation Add displayFormat property to control user-visible date format Maintain backward compatibility with existing formatPattern usage Enable decoupling of data format from presentation format This addresses the limitation where value and display formats were tightly coupled, allowing developers to store dates in standardized formats (e.g., ISO 8601) while presenting them in user-friendly formats (e.g., "Jan 1st, 2020"). Closes #1957
1 parent b9aaf78 commit 2acdbcc

File tree

23 files changed

+612
-37
lines changed

23 files changed

+612
-37
lines changed

packages/main/cypress/specs/DatePicker.cy.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,28 @@ describe("Date Picker Tests", () => {
7878
.should("have.attr", "value-state", "None");
7979
});
8080

81+
it("custom formatting", () => {
82+
cy.mount(<DatePicker displayFormat="yyyy, dd/MM" valueFormat="yyyy-MM-dd"></DatePicker>);
83+
84+
cy.get("[ui5-date-picker]")
85+
.as("datePicker");
86+
87+
cy.get<DatePicker>("@datePicker")
88+
.ui5DatePickerGetInnerInput()
89+
.realClick()
90+
.should("be.focused")
91+
.realType("2018, 05/05")
92+
.realPress("Enter");
93+
94+
cy.get("@datePicker")
95+
.shadow()
96+
.find("ui5-datetime-input")
97+
.should("have.attr", "value", "2018, 05/05");
98+
99+
cy.get("@datePicker")
100+
.should("have.attr", "value", "2018-05-05");
101+
});
102+
81103
it("value state", () => {
82104
cy.mount(<DatePicker></DatePicker>);
83105
cy.get("[ui5-date-picker]")

packages/main/cypress/specs/DateRangePicker.cy.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,28 @@ describe("DateRangePicker general interaction", () => {
6464
.should("have.attr", "value-state", "None");
6565
});
6666

67+
it("custom formatting", () => {
68+
cy.mount(<DateRangePicker displayFormat="dd/MM/yyyy" valueFormat="yyyy-MM-dd"></DateRangePicker>);
69+
70+
cy.get("[ui5-daterange-picker]")
71+
.as("dateRangePicker");
72+
73+
cy.get<DateRangePicker>("@dateRangePicker")
74+
.ui5DatePickerGetInnerInput()
75+
.realClick()
76+
.should("be.focused")
77+
.realType("05/05/2018 - 06/06/2018")
78+
.realPress("Enter");
79+
80+
cy.get("@dateRangePicker")
81+
.shadow()
82+
.find("ui5-datetime-input")
83+
.should("have.attr", "value", "05/05/2018 - 06/06/2018");
84+
85+
cy.get("@dateRangePicker")
86+
.should("have.attr", "value", "2018-05-05 - 2018-06-06");
87+
});
88+
6789
it("Selected dates are updated after value update in the input field", () => {
6890
cy.mount(<DateRangePickerTemplate formatPattern="dd/MM/yyyy" />);
6991

packages/main/cypress/specs/DateTimePicker.cy.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,28 @@ describe("DateTimePicker general interaction", () => {
418418
.and("have.attr", "value", "");
419419
});
420420

421+
it("custom formatting", () => {
422+
cy.mount(<DateTimePicker displayFormat="yyyy, dd/MM hh:mm:ss" valueFormat="yyyy-MM-dd hh:mm:ss"></DateTimePicker>);
423+
424+
cy.get("[ui5-datetime-picker]")
425+
.as("dateTimePicker");
426+
427+
cy.get<DateTimePicker>("@dateTimePicker")
428+
.ui5DatePickerGetInnerInput()
429+
.realClick()
430+
.should("be.focused")
431+
.realType("2018, 05/05 12:00:00")
432+
.realPress("Enter");
433+
434+
cy.get("@dateTimePicker")
435+
.shadow()
436+
.find("ui5-datetime-input")
437+
.should("have.attr", "value", "2018, 05/05 12:00:00");
438+
439+
cy.get("@dateTimePicker")
440+
.should("have.attr", "value", "2018-05-05 12:00:00");
441+
});
442+
421443
it("Min and max dates are set, with no format pattern provided, using valid ISO format", () => {
422444
cy.mount(
423445
<DateTimePickerTemplate minDate="2023-05-01" maxDate="2023-05-31" />

packages/main/src/DateComponentBase.ts

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,30 @@ class DateComponentBase extends UI5Element {
5555
/**
5656
* Determines the format, displayed in the input field.
5757
* @default undefined
58+
* @deprecated
5859
* @public
5960
*/
6061
@property()
6162
formatPattern?: string;
6263

64+
/**
65+
* Determines the format, displayed in the input field.
66+
* @default undefined
67+
* @since 2.14.0
68+
* @public
69+
*/
70+
@property()
71+
displayFormat?: string;
72+
73+
/**
74+
* Determines the format, used for the value attribute.
75+
* @default undefined
76+
* @since 2.14.0
77+
* @public
78+
*/
79+
@property()
80+
valueFormat?: string;
81+
6382
/**
6483
* Determines the minimum date available for selection.
6584
*
@@ -143,6 +162,14 @@ class DateComponentBase extends UI5Element {
143162
return this._formatPattern !== "medium" && this._formatPattern !== "short" && this._formatPattern !== "long";
144163
}
145164

165+
get _isValueFormatPattern() {
166+
return this._valueFormat !== "medium" && this._valueFormat !== "short" && this._valueFormat !== "long";
167+
}
168+
169+
get _isDisplayFormatPattern() {
170+
return this._displayFormat !== "medium" && this._displayFormat !== "short" && this._displayFormat !== "long";
171+
}
172+
146173
get hasSecondaryCalendarType() {
147174
return !!this.secondaryCalendarType && this.secondaryCalendarType !== this.primaryCalendarType;
148175
}
@@ -159,7 +186,14 @@ class DateComponentBase extends UI5Element {
159186
}
160187

161188
_getCalendarDateFromString(value: string) {
162-
const jsDate = this.getFormat().parse(value) as Date;
189+
const jsDate = this.getValueFormat().parse(value) as Date;
190+
if (jsDate) {
191+
return CalendarDate.fromLocalJSDate(jsDate, this._primaryCalendarType);
192+
}
193+
}
194+
195+
_getCalendarDateFromStringDisplayValue(value: string) {
196+
const jsDate = this.getDisplayFormat().parse(value) as Date;
163197
if (jsDate) {
164198
return CalendarDate.fromLocalJSDate(jsDate, this._primaryCalendarType);
165199
}
@@ -173,10 +207,32 @@ class DateComponentBase extends UI5Element {
173207
}
174208

175209
_getStringFromTimestamp(timestamp: number) {
210+
if (!timestamp) {
211+
return "";
212+
}
213+
176214
const localDate = UI5Date.getInstance(timestamp);
177215
return this.getFormat().format(localDate, true);
178216
}
179217

218+
_getDisplayStringFromTimestamp(timestamp: number) {
219+
if (!timestamp) {
220+
return "";
221+
}
222+
223+
const localDate = UI5Date.getInstance(timestamp);
224+
return this.getDisplayFormat().format(localDate, true);
225+
}
226+
227+
_getValueStringFromTimestamp(timestamp: number) {
228+
if (!timestamp) {
229+
return "";
230+
}
231+
232+
const localDate = UI5Date.getInstance(timestamp);
233+
return this.getValueFormat().format(localDate, true);
234+
}
235+
180236
getFormat() {
181237
return this._isPattern
182238
? DateFormat.getDateInstance({
@@ -191,6 +247,58 @@ class DateComponentBase extends UI5Element {
191247
});
192248
}
193249

250+
get _displayFormat() {
251+
if (this.displayFormat) {
252+
return this.displayFormat;
253+
}
254+
255+
return this._formatPattern;
256+
}
257+
258+
get _valueFormat() {
259+
if (this.valueFormat) {
260+
return this.valueFormat;
261+
}
262+
263+
if (this._formatPattern) {
264+
return this._formatPattern;
265+
}
266+
267+
return "";
268+
}
269+
270+
getDisplayFormat() {
271+
return this._isDisplayFormatPattern
272+
? DateFormat.getDateInstance({
273+
strictParsing: true,
274+
pattern: this._displayFormat,
275+
calendarType: this._primaryCalendarType,
276+
})
277+
: DateFormat.getDateInstance({
278+
strictParsing: true,
279+
style: this._displayFormat,
280+
calendarType: this._primaryCalendarType,
281+
});
282+
}
283+
284+
getValueFormat() {
285+
if (!this._valueFormat) {
286+
return this.getISOFormat();
287+
}
288+
289+
return this._isValueFormatPattern
290+
? DateFormat.getDateInstance({
291+
strictParsing: true,
292+
pattern: this._valueFormat,
293+
calendarType: this._primaryCalendarType,
294+
})
295+
: DateFormat.getDateInstance({
296+
strictParsing: true,
297+
style: this._valueFormat,
298+
calendarType: this._primaryCalendarType,
299+
});
300+
}
301+
194302
getISOFormat() {
195303
if (!this._isoFormatInstance) {
196304
this._isoFormatInstance = DateFormat.getDateInstance({

0 commit comments

Comments
 (0)