Upload field Property Editor: Fix resetting value to undefined when empty (#20134)

* set value to undefined when empty

* fix nullable checks

* ensure promise rejection when validation fails

* avoid js error when detailStore is not present

* implement editor as form control

* remove unused

---------

Co-authored-by: Mads Rasmussen <madsr@hey.com>
This commit is contained in:
Niels Lyngsø
2025-09-24 11:29:42 +02:00
committed by GitHub
parent a6c92d8a91
commit 6d7c722ec3
8 changed files with 55 additions and 23 deletions

View File

@@ -821,7 +821,7 @@ export abstract class UmbContentDetailWorkspaceContextBase<
* Request a submit of the workspace, in the case of Document Workspaces the validation does not need to be valid for this to be submitted.
* @returns {Promise<void>} a promise which resolves once it has been completed.
*/
public override requestSubmit() {
public override requestSubmit(): Promise<void> {
return this._handleSubmit();
}
@@ -831,7 +831,7 @@ export abstract class UmbContentDetailWorkspaceContextBase<
/**
* Request a save of the workspace, in the case of Document Workspaces the validation does not need to be valid for this to be saved.
* @returns {Promise<void>} a promise which resolves once it has been completed.
* @returns {Promise<void>} A promise which resolves once it has been completed.
*/
public requestSave() {
return this._handleSave();
@@ -847,11 +847,11 @@ export abstract class UmbContentDetailWorkspaceContextBase<
return this._data.constructData(variantIds);
}
protected async _handleSubmit() {
protected async _handleSubmit(): Promise<void> {
await this._handleSave();
this._closeModal();
}
protected async _handleSave() {
protected async _handleSave(): Promise<void> {
const data = this.getData();
if (!data) {
throw new Error('Data is missing');
@@ -877,7 +877,9 @@ export abstract class UmbContentDetailWorkspaceContextBase<
value: { selection: selected },
}).catch(() => undefined);
if (!result?.selection.length) return;
if (!result?.selection.length) {
return Promise.reject('Cannot save without selecting at least one variant.');
}
variantIds = result?.selection.map((x) => UmbVariantId.FromString(x)) ?? [];
} else {
@@ -897,7 +899,9 @@ export abstract class UmbContentDetailWorkspaceContextBase<
() => false,
);
if (valid || this.#ignoreValidationResultOnSubmit) {
return this.performCreateOrUpdate(variantIds, saveData);
await this.performCreateOrUpdate(variantIds, saveData);
} else {
return Promise.reject('Validation issues prevent saving');
}
} else {
await this.performCreateOrUpdate(variantIds, saveData);

View File

@@ -263,6 +263,14 @@ export function UmbFormControlMixin<
* @returns {void}
*/
protected addFormControlElement(element: UmbNativeFormControlElement) {
if (!element) {
throw new Error('Element is null or undefined');
}
if (!element.validity) {
console.log(element);
throw new Error('Element is not a Form Control');
}
if (this.#formCtrlElements.includes(element)) return;
this.#formCtrlElements.push(element);
element.addEventListener(UmbValidationInvalidEvent.TYPE, this.#runValidatorsCallback);
element.addEventListener(UmbValidationValidEvent.TYPE, this.#runValidatorsCallback);

View File

@@ -49,6 +49,6 @@ export class UmbSaveWorkspaceAction<
override async execute() {
await this._retrieveWorkspaceContext;
return await this._workspaceContext?.requestSave();
await this._workspaceContext?.requestSave();
}
}

View File

@@ -16,19 +16,26 @@ import type {
} from '@umbraco-cms/backoffice/dropzone';
import type { UmbTemporaryFileModel } from '@umbraco-cms/backoffice/temporary-file';
import { UMB_SERVER_CONTEXT } from '@umbraco-cms/backoffice/server';
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
@customElement('umb-input-upload-field')
export class UmbInputUploadFieldElement extends UmbLitElement {
export class UmbInputUploadFieldElement extends UmbFormControlMixin<UmbMediaValueType, typeof UmbLitElement>(
UmbLitElement,
) {
@property({ type: Object, attribute: false })
set value(value: UmbMediaValueType) {
override set value(value: UmbMediaValueType | undefined) {
super.value = value;
this.#src = value?.src ?? '';
this.#setPreviewAlias();
}
get value(): UmbMediaValueType {
return {
src: this.#src,
temporaryFileId: this.temporaryFile?.temporaryUnique,
};
override get value(): UmbMediaValueType | undefined {
if (this.#src || this.temporaryFile?.temporaryUnique) {
return {
src: this.#src,
temporaryFileId: this.temporaryFile?.temporaryUnique,
};
}
return undefined;
}
#src = '';
@@ -86,7 +93,7 @@ export class UmbInputUploadFieldElement extends UmbLitElement {
}
async #getPreviewElementAlias() {
if (!this.value.src) return;
if (!this.value?.src) return;
const manifests = await this.#getManifests();
const fallbackAlias = manifests.find((manifest) =>
stringOrStringArrayContains(manifest.forMimeTypes, '*/*'),
@@ -158,7 +165,7 @@ export class UmbInputUploadFieldElement extends UmbLitElement {
}
override render() {
if (!this.temporaryFile && !this.value.src) {
if (!this.temporaryFile && !this.value?.src) {
return this.#renderDropzone();
}

View File

@@ -1,21 +1,24 @@
import type { UmbInputUploadFieldElement } from '../../components/input-upload-field/input-upload-field.element.js';
import type { UmbMediaValueType } from './types.js';
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { html, customElement, state } from '@umbraco-cms/backoffice/external/lit';
import type {
UmbPropertyEditorUiElement,
UmbPropertyEditorConfigCollection,
} from '@umbraco-cms/backoffice/property-editor';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
import '../../components/input-upload-field/input-upload-field.element.js';
/**
* @element umb-property-editor-ui-upload-field
*/
@customElement('umb-property-editor-ui-upload-field')
export class UmbPropertyEditorUIUploadFieldElement extends UmbLitElement implements UmbPropertyEditorUiElement {
@property({ type: Object })
value: UmbMediaValueType = {};
export class UmbPropertyEditorUIUploadFieldElement
extends UmbFormControlMixin<UmbMediaValueType, typeof UmbLitElement>(UmbLitElement)
implements UmbPropertyEditorUiElement
{
@state()
private _fileExtensions?: Array<string>;
@@ -29,6 +32,10 @@ export class UmbPropertyEditorUIUploadFieldElement extends UmbLitElement impleme
}
}
override firstUpdated() {
this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-upload-field')!);
}
#onChange(event: CustomEvent) {
this.value = (event.target as UmbInputUploadFieldElement).value;
this.dispatchEvent(new UmbChangeEvent());

View File

@@ -49,6 +49,9 @@ export class UmbMediaValidationServerDataSource {
MediaService.postMediaValidate({
body,
}),
{
disableNotifications: true,
},
);
if (data && typeof data === 'string') {
@@ -86,6 +89,9 @@ export class UmbMediaValidationServerDataSource {
path: { id: model.unique },
body,
}),
{
disableNotifications: true,
},
);
if (data && typeof data === 'string') {

View File

@@ -19,7 +19,7 @@ export class UmbMemberCollectionRepository extends UmbMemberRepositoryBase imple
const { data, error } = await this.#collectionSource.getCollection(filter);
if (data) {
this.detailStore!.appendItems(data.items);
this.detailStore?.appendItems(data.items);
}
return { data, error, asObservable: () => this.detailStore!.all() };

View File

@@ -19,7 +19,7 @@ export class UmbUserCollectionRepository extends UmbUserRepositoryBase implement
const { data, error } = await this.#collectionSource.getCollection(filter);
if (data) {
this.detailStore!.appendItems(data.items);
this.detailStore?.appendItems(data.items);
}
return { data, error, asObservable: () => this.detailStore!.all() };