Skip to content

Commit 9a8141f

Browse files
committed
Bugfix: Date Picker with only "time" does not display correctly (#2115)
* fix: takes care of a case where the server and/or input element could send various datetime strings regardless of the configuration of the property editor, for example the "time" configuration could still be complete datetime string * test: adds tests for all the various input and output cases to ensure the format is 100% matching * fix: add `_inputValue` to differ between the input and output values, because the server always expects datetimes whereas the client expects differentiated strings
1 parent c58daf0 commit 9a8141f

File tree

2 files changed

+98
-18
lines changed

2 files changed

+98
-18
lines changed

src/packages/property-editors/date-picker/property-editor-ui-date-picker.element.ts

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
22
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
3-
import { html, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
3+
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
44
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
55
import type { UmbInputDateElement } from '@umbraco-cms/backoffice/components';
66
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
@@ -42,13 +42,16 @@ export class UmbPropertyEditorUIDatePickerElement extends UmbLitElement implemen
4242
@property()
4343
value?: string;
4444

45+
@state()
46+
private _inputValue?: string;
47+
4548
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
4649
if (!config) return;
4750

4851
// Format string prevalue/config
4952
const format = config.getValueByAlias<string>('format');
50-
const hasTime = format?.includes('H') || format?.includes('m');
51-
const hasSeconds = format?.includes('s');
53+
const hasTime = (format?.includes('H') || format?.includes('m')) ?? false;
54+
const hasSeconds = format?.includes('s') ?? false;
5255
this._inputType = hasTime ? 'datetime-local' : 'date';
5356

5457
// Based on the type of format string change the UUI-input type
@@ -70,33 +73,54 @@ export class UmbPropertyEditorUIDatePickerElement extends UmbLitElement implemen
7073
}
7174

7275
#onChange(event: CustomEvent & { target: UmbInputDateElement }) {
73-
this.#formatValue(event.target.value.toString());
76+
let value = event.target.value.toString();
77+
78+
switch (this._inputType) {
79+
case 'time':
80+
value = `0001-01-01 ${value}`;
81+
break;
82+
case 'date':
83+
value = `${value} 00:00:00`;
84+
break;
85+
case 'datetime-local':
86+
value = value.replace('T', ' ');
87+
break;
88+
}
89+
90+
this.#syncValue(value);
7491
}
7592

7693
/**
7794
* Formats the value depending on the input type.
7895
*/
7996
#formatValue(value: string) {
80-
// Check that the value is a valid date
81-
const valueToDate = new Date(value);
82-
if (isNaN(valueToDate.getTime())) {
83-
console.warn('[Umbraco.DatePicker] The value is not a valid date.', value);
97+
this._inputValue = undefined;
98+
99+
if (isNaN(new Date(value).getTime())) {
100+
console.warn(`[UmbDatePicker] Invalid date: ${value}`);
84101
return;
85102
}
86103

87-
// Replace the potential time demoninator 'T' with a whitespace for backwards compatibility
88-
value = value.replace('T', ' ');
89-
90-
// If the inputType is 'date', we need to make sure the value doesn't have a time
91-
if (this._inputType === 'date' && value.includes(' ')) {
92-
value = value.split(' ')[0];
104+
const dateSplit = value.split(' ');
105+
if (dateSplit.length !== 2) {
106+
console.warn(`[UmbDatePicker] Invalid date: ${value}`);
107+
return;
93108
}
94109

95-
// If the inputType is 'time', we need to remove the date part of the value
96-
if (this._inputType === 'time' && value.includes(' ')) {
97-
value = value.split(' ')[1];
110+
switch (this._inputType) {
111+
case 'time':
112+
this._inputValue = dateSplit[1];
113+
break;
114+
case 'date':
115+
this._inputValue = dateSplit[0];
116+
break;
117+
default:
118+
this._inputValue = dateSplit.join('T');
119+
break;
98120
}
121+
}
99122

123+
#syncValue(value: string) {
100124
const valueHasChanged = this.value !== value;
101125
if (valueHasChanged) {
102126
this.value = value;
@@ -107,7 +131,7 @@ export class UmbPropertyEditorUIDatePickerElement extends UmbLitElement implemen
107131
override render() {
108132
return html`
109133
<umb-input-date
110-
value="${ifDefined(this.value)}"
134+
.value=${this._inputValue}
111135
.min=${this._min}
112136
.max=${this._max}
113137
.step=${this._step}

src/packages/property-editors/date-picker/property-editor-ui-date-picker.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,60 @@ describe('UmbPropertyEditorUIDatePickerElement', () => {
4343
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
4444
});
4545
}
46+
47+
describe('input', () => {
48+
it('should format the value to a datetime-local', async () => {
49+
element.value = '2024-05-03 10:44:00';
50+
element.config = new UmbPropertyEditorConfigCollection([{ alias: 'format', value: 'YYYY-MM-dd HH:mm:ss' }]);
51+
await element.updateComplete;
52+
expect((element as any)._inputValue).to.equal('2024-05-03T10:44:00');
53+
});
54+
55+
it('should format the value to a date', async () => {
56+
element.value = '2024-05-03 10:44:00';
57+
element.config = new UmbPropertyEditorConfigCollection([{ alias: 'format', value: 'YYYY-MM-dd' }]);
58+
await element.updateComplete;
59+
expect((element as any)._inputValue).to.equal('2024-05-03');
60+
});
61+
62+
it('should format the value to a time', async () => {
63+
element.value = '2024-05-03 10:44:00';
64+
element.config = new UmbPropertyEditorConfigCollection([{ alias: 'format', value: 'HH:mm' }]);
65+
await element.updateComplete;
66+
expect((element as any)._inputValue).to.equal('10:44:00');
67+
});
68+
69+
it('should disregard a non-datetime value', async () => {
70+
element.value = '03/05/2024 10:44:00';
71+
await element.updateComplete;
72+
expect((element as any)._inputValue).to.be.undefined;
73+
});
74+
});
75+
76+
describe('output', () => {
77+
it('should format the value to a datetime-local', async () => {
78+
inputElement.value = '2024-05-03T10:44:00';
79+
inputElement.dispatchEvent(new CustomEvent('change'));
80+
await element.updateComplete;
81+
expect(element.value).to.equal('2024-05-03 10:44:00');
82+
});
83+
84+
it('should format the value to a date', async () => {
85+
element.config = new UmbPropertyEditorConfigCollection([{ alias: 'format', value: 'YYYY-MM-dd' }]);
86+
await element.updateComplete;
87+
inputElement.value = '2024-05-03';
88+
inputElement.dispatchEvent(new CustomEvent('change'));
89+
await element.updateComplete;
90+
expect(element.value).to.equal('2024-05-03 00:00:00');
91+
});
92+
93+
it('should format the value to a time', async () => {
94+
element.config = new UmbPropertyEditorConfigCollection([{ alias: 'format', value: 'HH:mm' }]);
95+
await element.updateComplete;
96+
inputElement.value = '10:44:00';
97+
inputElement.dispatchEvent(new CustomEvent('change'));
98+
await element.updateComplete;
99+
expect(element.value).to.equal('0001-01-01 10:44:00');
100+
});
101+
});
46102
});

0 commit comments

Comments
 (0)