Skip to content

Commit 2cfcd33

Browse files
authored
[WC-3157] Add event handlers for file uploader (#1964)
2 parents 8fcc7ed + 28d0f54 commit 2cfcd33

File tree

12 files changed

+103
-21
lines changed

12 files changed

+103
-21
lines changed

packages/pluggableWidgets/file-uploader-web/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- We added configuration options to execute actions after both successful and unsuccessful file uploads.
12+
913
## [2.3.0] - 2025-08-15
1014

1115
### Fixed

packages/pluggableWidgets/file-uploader-web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@mendix/file-uploader-web",
33
"widgetName": "FileUploader",
4-
"version": "2.3.0",
4+
"version": "2.4.0",
55
"description": "Upload files via drag-and-drop or file dialog. Supports multiple file uploads and image preview thumbnails.",
66
"copyright": "© Mendix Technology BV 2025. All rights reserved.",
77
"license": "Apache-2.0",

packages/pluggableWidgets/file-uploader-web/src/FileUploader.editorConfig.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ export function getProperties(
2626
]);
2727
}
2828

29+
if (values.uploadMode === "files") {
30+
hidePropertiesIn(properties, values, ["onUploadSuccessImage", "onUploadFailureImage"]);
31+
} else {
32+
hidePropertiesIn(properties, values, ["onUploadSuccessFile", "onUploadFailureFile"]);
33+
}
34+
2935
if (values.enableCustomButtons) {
3036
values.customButtons.forEach((_button, index) => {
3137
hideNestedPropertiesIn(

packages/pluggableWidgets/file-uploader-web/src/FileUploader.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,24 @@
195195
<caption>Object creation timeout</caption>
196196
<description>Consider uploads unsuccessful if the Action to create new files/images does not create new objects within the configured amount of seconds.</description>
197197
</property>
198+
199+
<property key="onUploadSuccessFile" type="action" dataSource="associatedFiles">
200+
<caption>On upload success</caption>
201+
<description>The action to be called if file content uploaded successfully.</description>
202+
</property>
203+
<property key="onUploadSuccessImage" type="action" dataSource="associatedImages">
204+
<caption>On upload success</caption>
205+
<description>The action to be called if image content uploaded successfully.</description>
206+
</property>
207+
<property key="onUploadFailureFile" type="action" dataSource="associatedFiles" defaultValue="FileUploader.ACT_DeleteUploadedFileDocument" defaultType="CallNanoflow">
208+
<caption>On upload failure</caption>
209+
<description>The action to be called if file content upload was not successful.</description>
210+
</property>
211+
<property key="onUploadFailureImage" type="action" dataSource="associatedImages" defaultValue="FileUploader.ACT_DeleteUploadedImageDocument" defaultType="CallNanoflow">
212+
<caption>On upload failure</caption>
213+
<description>The action to be called if image content upload was not successful.</description>
214+
</property>
215+
198216
<property key="enableCustomButtons" type="boolean" defaultValue="false">
199217
<caption>Enable custom buttons</caption>
200218
<description />

packages/pluggableWidgets/file-uploader-web/src/components/FileEntry.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ function FileEntry(props: FileEntryProps): ReactElement {
8383
return (
8484
<div
8585
className={classNames("file-entry", {
86-
removed: props.fileStatus === "removedFile",
86+
removed: props.fileStatus === "removedFile" || props.fileStatus === "removedAfterError",
8787
invalid: props.fileStatus === "validationError"
8888
})}
8989
title={props.title}

packages/pluggableWidgets/file-uploader-web/src/components/UploadInfo.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export function UploadInfo({ status, error }: UploadInfoProps): ReactElement {
1515
case "done":
1616
return <span className={"upload-status success"}>{translations.get("uploadSuccessMessage")}</span>;
1717
case "uploadingError":
18+
case "removedAfterError":
1819
return <span className={"upload-status error"}>{translations.get("uploadFailureGenericMessage")}</span>;
1920
case "validationError":
2021
return <span className={"upload-status error"}>{error}</span>;

packages/pluggableWidgets/file-uploader-web/src/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8" ?>
22
<package xmlns="http://www.mendix.com/package/1.0/">
3-
<clientModule name="FileUploader" version="2.3.0" xmlns="http://www.mendix.com/clientModule/1.0/">
3+
<clientModule name="FileUploader" version="2.4.0" xmlns="http://www.mendix.com/clientModule/1.0/">
44
<widgetFiles>
55
<widgetFile path="FileUploader.xml" />
66
</widgetFiles>

packages/pluggableWidgets/file-uploader-web/src/stores/FileStore.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export type FileStatus =
2121
| "uploading"
2222
| "done"
2323
| "uploadingError"
24+
| "removedAfterError"
2425
| "removedFile"
2526
| "validationError";
2627

@@ -64,7 +65,8 @@ export class FileStore {
6465
}
6566

6667
markMissing(): void {
67-
this.fileStatus = "missing";
68+
this.fileStatus = this.fileStatus === "uploadingError" ? "removedAfterError" : "missing";
69+
6870
this._mxObject = undefined;
6971
this._objectItem = undefined;
7072
}
@@ -102,18 +104,30 @@ export class FileStore {
102104
this.fileStatus = "uploading";
103105
});
104106

107+
// create object
105108
try {
106-
// request object item
107109
this._objectItem = await this._rootStore.objectCreationHelper.request();
110+
} catch (_e: unknown) {
111+
runInAction(() => {
112+
this.fileStatus = "uploadingError";
113+
this._rootStore.objectCreationHelper.reportCreationFailure();
114+
});
115+
return;
116+
}
117+
118+
// upload content to object
119+
try {
108120
await saveFile(this._objectItem, this._file!);
109121
await this.fetchMxObject();
110122

111123
runInAction(() => {
112124
this.fileStatus = "done";
125+
this._rootStore.objectCreationHelper.reportUploadSuccess(this._objectItem!);
113126
});
114127
} catch (_e: unknown) {
115128
runInAction(() => {
116129
this.fileStatus = "uploadingError";
130+
this._rootStore.objectCreationHelper.reportUploadFailure(this._objectItem!);
117131
});
118132
}
119133
}

packages/pluggableWidgets/file-uploader-web/src/stores/FileUploaderStore.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DynamicValue, ListValue, ObjectItem } from "mendix";
1+
import { DynamicValue, ObjectItem } from "mendix";
22
import { FileUploaderContainerProps, UploadModeEnum } from "../../typings/FileUploaderProps";
33
import { action, computed, makeObservable, observable } from "mobx";
44
import { Big } from "big.js";
@@ -26,7 +26,6 @@ export class FileUploaderStore {
2626
_uploadMode: UploadModeEnum;
2727
_maxFileSizeMiB = 0;
2828
_maxFileSize = 0;
29-
_ds?: ListValue;
3029
_maxFilesPerUpload: DynamicValue<Big>;
3130

3231
errorMessage?: string = undefined;
@@ -90,19 +89,15 @@ export class FileUploaderStore {
9089
}
9190

9291
updateProps(props: FileUploaderContainerProps): void {
93-
if (props.uploadMode === "files") {
94-
this.objectCreationHelper.updateProps(props.createFileAction);
95-
this._ds = props.associatedFiles;
96-
} else {
97-
this.objectCreationHelper.updateProps(props.createImageAction);
98-
this._ds = props.associatedImages;
99-
}
92+
this.objectCreationHelper.updateProps(props);
10093

10194
// Update max files properties
10295
this._maxFilesPerUpload = props.maxFilesPerUpload;
10396

10497
this.translations.updateProps(props);
105-
this.updateProcessor.processUpdate(this._ds);
98+
this.updateProcessor.processUpdate(
99+
props.uploadMode === "files" ? props.associatedFiles : props.associatedImages
100+
);
106101
}
107102

108103
processExistingFileItem(item: ObjectItem): void {

packages/pluggableWidgets/file-uploader-web/src/utils/ObjectCreationHelper.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1-
import { ActionValue, ObjectItem } from "mendix";
1+
import { ActionValue, ListActionValue, ObjectItem } from "mendix";
2+
import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action";
3+
import { FileUploaderContainerProps } from "../../typings/FileUploaderProps";
4+
5+
type UpdateProps = Pick<
6+
FileUploaderContainerProps,
7+
| "uploadMode"
8+
| "createFileAction"
9+
| "createImageAction"
10+
| "onUploadFailureFile"
11+
| "onUploadSuccessFile"
12+
| "onUploadFailureImage"
13+
| "onUploadSuccessImage"
14+
>;
215

316
export class ObjectCreationHelper {
417
private objectCreationAction?: ActionValue;
18+
private onUploadFailure?: ListActionValue;
19+
private onUploadSuccess?: ListActionValue;
520
private requestingEnabled = false;
621
private currentWaiting: Array<[(v: ObjectItem) => void, (e: Error) => void]> = [];
722
private itemCreationTimer?: number = undefined;
@@ -25,8 +40,16 @@ export class ObjectCreationHelper {
2540
}
2641
}
2742

28-
updateProps(createAction?: ActionValue): void {
29-
this.objectCreationAction = createAction;
43+
updateProps(props: UpdateProps): void {
44+
if (props.uploadMode === "files") {
45+
this.objectCreationAction = props.createFileAction;
46+
this.onUploadFailure = props.onUploadFailureFile;
47+
this.onUploadSuccess = props.onUploadSuccessFile;
48+
} else {
49+
this.objectCreationAction = props.createImageAction;
50+
this.onUploadFailure = props.onUploadFailureImage;
51+
this.onUploadSuccess = props.onUploadSuccessImage;
52+
}
3053
}
3154

3255
request(): Promise<ObjectItem> {
@@ -55,6 +78,19 @@ export class ObjectCreationHelper {
5578
this.executeCreation();
5679
}
5780

81+
reportCreationFailure(): void {
82+
console.warn(`File object creation has failed.`);
83+
}
84+
85+
reportUploadFailure(item: ObjectItem): void {
86+
console.warn(`Uploading file content to ${item.id} has failed.`);
87+
executeAction(this.onUploadFailure?.get(item));
88+
}
89+
90+
reportUploadSuccess(item: ObjectItem): void {
91+
executeAction(this.onUploadSuccess?.get(item));
92+
}
93+
5894
private executeCreation(): void {
5995
if (!this.requestingEnabled) {
6096
// we are not yet able to create objects, wait till next run

0 commit comments

Comments
 (0)