Skip to content

Commit

Permalink
Bugfix: Date Picker with only "time" does not display correctly (#2115)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
iOvergaard committed Jul 16, 2024
1 parent c58daf0 commit 9a8141f
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
import type { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
import { html, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbInputDateElement } from '@umbraco-cms/backoffice/components';
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
Expand Down Expand Up @@ -42,13 +42,16 @@ export class UmbPropertyEditorUIDatePickerElement extends UmbLitElement implemen
@property()
value?: string;

@state()
private _inputValue?: string;

public set config(config: UmbPropertyEditorConfigCollection | undefined) {
if (!config) return;

// Format string prevalue/config
const format = config.getValueByAlias<string>('format');
const hasTime = format?.includes('H') || format?.includes('m');
const hasSeconds = format?.includes('s');
const hasTime = (format?.includes('H') || format?.includes('m')) ?? false;
const hasSeconds = format?.includes('s') ?? false;
this._inputType = hasTime ? 'datetime-local' : 'date';

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

#onChange(event: CustomEvent & { target: UmbInputDateElement }) {
this.#formatValue(event.target.value.toString());
let value = event.target.value.toString();

switch (this._inputType) {
case 'time':
value = `0001-01-01 ${value}`;
break;
case 'date':
value = `${value} 00:00:00`;
break;
case 'datetime-local':
value = value.replace('T', ' ');
break;
}

this.#syncValue(value);
}

/**
* Formats the value depending on the input type.
*/
#formatValue(value: string) {
// Check that the value is a valid date
const valueToDate = new Date(value);
if (isNaN(valueToDate.getTime())) {
console.warn('[Umbraco.DatePicker] The value is not a valid date.', value);
this._inputValue = undefined;

if (isNaN(new Date(value).getTime())) {
console.warn(`[UmbDatePicker] Invalid date: ${value}`);
return;
}

// Replace the potential time demoninator 'T' with a whitespace for backwards compatibility
value = value.replace('T', ' ');

// If the inputType is 'date', we need to make sure the value doesn't have a time
if (this._inputType === 'date' && value.includes(' ')) {
value = value.split(' ')[0];
const dateSplit = value.split(' ');
if (dateSplit.length !== 2) {
console.warn(`[UmbDatePicker] Invalid date: ${value}`);
return;
}

// If the inputType is 'time', we need to remove the date part of the value
if (this._inputType === 'time' && value.includes(' ')) {
value = value.split(' ')[1];
switch (this._inputType) {
case 'time':
this._inputValue = dateSplit[1];
break;
case 'date':
this._inputValue = dateSplit[0];
break;
default:
this._inputValue = dateSplit.join('T');
break;
}
}

#syncValue(value: string) {
const valueHasChanged = this.value !== value;
if (valueHasChanged) {
this.value = value;
Expand All @@ -107,7 +131,7 @@ export class UmbPropertyEditorUIDatePickerElement extends UmbLitElement implemen
override render() {
return html`
<umb-input-date
value="${ifDefined(this.value)}"
.value=${this._inputValue}
.min=${this._min}
.max=${this._max}
.step=${this._step}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,60 @@ describe('UmbPropertyEditorUIDatePickerElement', () => {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}

describe('input', () => {
it('should format the value to a datetime-local', async () => {
element.value = '2024-05-03 10:44:00';
element.config = new UmbPropertyEditorConfigCollection([{ alias: 'format', value: 'YYYY-MM-dd HH:mm:ss' }]);
await element.updateComplete;
expect((element as any)._inputValue).to.equal('2024-05-03T10:44:00');
});

it('should format the value to a date', async () => {
element.value = '2024-05-03 10:44:00';
element.config = new UmbPropertyEditorConfigCollection([{ alias: 'format', value: 'YYYY-MM-dd' }]);
await element.updateComplete;
expect((element as any)._inputValue).to.equal('2024-05-03');
});

it('should format the value to a time', async () => {
element.value = '2024-05-03 10:44:00';
element.config = new UmbPropertyEditorConfigCollection([{ alias: 'format', value: 'HH:mm' }]);
await element.updateComplete;
expect((element as any)._inputValue).to.equal('10:44:00');
});

it('should disregard a non-datetime value', async () => {
element.value = '03/05/2024 10:44:00';
await element.updateComplete;
expect((element as any)._inputValue).to.be.undefined;
});
});

describe('output', () => {
it('should format the value to a datetime-local', async () => {
inputElement.value = '2024-05-03T10:44:00';
inputElement.dispatchEvent(new CustomEvent('change'));
await element.updateComplete;
expect(element.value).to.equal('2024-05-03 10:44:00');
});

it('should format the value to a date', async () => {
element.config = new UmbPropertyEditorConfigCollection([{ alias: 'format', value: 'YYYY-MM-dd' }]);
await element.updateComplete;
inputElement.value = '2024-05-03';
inputElement.dispatchEvent(new CustomEvent('change'));
await element.updateComplete;
expect(element.value).to.equal('2024-05-03 00:00:00');
});

it('should format the value to a time', async () => {
element.config = new UmbPropertyEditorConfigCollection([{ alias: 'format', value: 'HH:mm' }]);
await element.updateComplete;
inputElement.value = '10:44:00';
inputElement.dispatchEvent(new CustomEvent('change'));
await element.updateComplete;
expect(element.value).to.equal('0001-01-01 10:44:00');
});
});
});

0 comments on commit 9a8141f

Please sign in to comment.