Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/pluggableWidgets/file-uploader-web/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Added

- We added configuration options to execute actions after both successful and unsuccessful file uploads.

## [2.3.0] - 2025-08-15

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion packages/pluggableWidgets/file-uploader-web/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@mendix/file-uploader-web",
"widgetName": "FileUploader",
"version": "2.3.0",
"version": "2.4.0",
"description": "Upload files via drag-and-drop or file dialog. Supports multiple file uploads and image preview thumbnails.",
"copyright": "© Mendix Technology BV 2025. All rights reserved.",
"license": "Apache-2.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,23 @@
<caption>Object creation timeout</caption>
<description>Consider uploads unsuccessful if the Action to create new files/images does not create new objects within the configured amount of seconds.</description>
</property>
<property key="onUploadSuccessFile" type="action" dataSource="associatedFiles">
<caption>On upload success</caption>
<description>The action to be called if file content uploaded successfully.</description>
</property>
<property key="onUploadSuccessImage" type="action" dataSource="associatedImages">
<caption>On upload success</caption>
<description>The action to be called if image content uploaded successfully.</description>
</property>
<property key="onUploadFailureFile" type="action" dataSource="associatedFiles">
<caption>On upload failure</caption>
<description>The action to be called if file content upload was not successful.</description>
</property>

<property key="onUploadFailureImage" type="action" dataSource="associatedImages">
<caption>On upload failure</caption>
<description>The action to be called if image content upload was not successful.</description>
</property>
<property key="enableCustomButtons" type="boolean" defaultValue="false">
<caption>Enable custom buttons</caption>
<description />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<package xmlns="http://www.mendix.com/package/1.0/">
<clientModule name="FileUploader" version="2.3.0" xmlns="http://www.mendix.com/clientModule/1.0/">
<clientModule name="FileUploader" version="2.4.0" xmlns="http://www.mendix.com/clientModule/1.0/">
<widgetFiles>
<widgetFile path="FileUploader.xml" />
</widgetFiles>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,30 @@ export class FileStore {
this.fileStatus = "uploading";
});

// create object
try {
// request object item
this._objectItem = await this._rootStore.objectCreationHelper.request();
} catch (_e: unknown) {
runInAction(() => {
this.fileStatus = "uploadingError";
this._rootStore.objectCreationHelper.reportCreationFailure();
});
return;
}

// upload content to object
try {
await saveFile(this._objectItem, this._file!);
await this.fetchMxObject();

runInAction(() => {
this.fileStatus = "done";
this._rootStore.objectCreationHelper.reportUploadSuccess(this._objectItem!);
});
} catch (_e: unknown) {
runInAction(() => {
this.fileStatus = "uploadingError";
this._rootStore.objectCreationHelper.reportUploadFailure(this._objectItem!);
});
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DynamicValue, ListValue, ObjectItem } from "mendix";
import { DynamicValue, ObjectItem } from "mendix";
import { FileUploaderContainerProps, UploadModeEnum } from "../../typings/FileUploaderProps";
import { action, computed, makeObservable, observable } from "mobx";
import { Big } from "big.js";
Expand Down Expand Up @@ -26,7 +26,6 @@ export class FileUploaderStore {
_uploadMode: UploadModeEnum;
_maxFileSizeMiB = 0;
_maxFileSize = 0;
_ds?: ListValue;
_maxFilesPerUpload: DynamicValue<Big>;

errorMessage?: string = undefined;
Expand Down Expand Up @@ -90,19 +89,15 @@ export class FileUploaderStore {
}

updateProps(props: FileUploaderContainerProps): void {
if (props.uploadMode === "files") {
this.objectCreationHelper.updateProps(props.createFileAction);
this._ds = props.associatedFiles;
} else {
this.objectCreationHelper.updateProps(props.createImageAction);
this._ds = props.associatedImages;
}
this.objectCreationHelper.updateProps(props);

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

this.translations.updateProps(props);
this.updateProcessor.processUpdate(this._ds);
this.updateProcessor.processUpdate(
props.uploadMode === "files" ? props.associatedFiles : props.associatedImages
);
}

processExistingFileItem(item: ObjectItem): void {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
import { ActionValue, ObjectItem } from "mendix";
import { ActionValue, ListActionValue, ObjectItem } from "mendix";
import { executeAction } from "@mendix/widget-plugin-platform/framework/execute-action";
import { FileUploaderContainerProps } from "../../typings/FileUploaderProps";

type UpdateProps = Pick<
FileUploaderContainerProps,
| "uploadMode"
| "createFileAction"
| "createImageAction"
| "onUploadFailureFile"
| "onUploadSuccessFile"
| "onUploadFailureImage"
| "onUploadSuccessImage"
>;

export class ObjectCreationHelper {
private objectCreationAction?: ActionValue;
private onUploadFailure?: ListActionValue;
private onUploadSuccess?: ListActionValue;
private requestingEnabled = false;
private currentWaiting: Array<[(v: ObjectItem) => void, (e: Error) => void]> = [];
private itemCreationTimer?: number = undefined;
Expand All @@ -25,8 +40,16 @@ export class ObjectCreationHelper {
}
}

updateProps(createAction?: ActionValue): void {
this.objectCreationAction = createAction;
updateProps(props: UpdateProps): void {
if (props.uploadMode === "files") {
this.objectCreationAction = props.createFileAction;
this.onUploadFailure = props.onUploadFailureFile;
this.onUploadSuccess = props.onUploadSuccessFile;
} else {
this.objectCreationAction = props.createImageAction;
this.onUploadFailure = props.onUploadFailureImage;
this.onUploadSuccess = props.onUploadSuccessImage;
}
}

request(): Promise<ObjectItem> {
Expand Down Expand Up @@ -55,6 +78,20 @@ export class ObjectCreationHelper {
this.executeCreation();
}

reportCreationFailure(): void {
console.warn(`File object creation has failed.`);
}

reportUploadFailure(item: ObjectItem): void {
console.warn(`Uploading file content to ${item.id} has failed.`);
executeAction(this.onUploadFailure?.get(item));
}

reportUploadSuccess(item: ObjectItem): void {
console.warn(`Uploading file content to ${item.id} has succeeded.`);
executeAction(this.onUploadSuccess?.get(item));
}

private executeCreation(): void {
if (!this.requestingEnabled) {
// we are not yet able to create objects, wait till next run
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe("ObjectCreationHelper", () => {
jest.useFakeTimers();
const createAction = actionValue(true, false);
const helper = new ObjectCreationHelper("grid1", 10);
helper.updateProps(createAction);
helper.updateProps({ uploadMode: "files", createFileAction: createAction });
helper.enable();
const req1 = helper.request();
const req2 = helper.request();
Expand Down Expand Up @@ -34,7 +34,7 @@ describe("ObjectCreationHelper", () => {
jest.useFakeTimers();
const helper = new ObjectCreationHelper("grid1", 10);
const createAction = actionValue(true, false);
helper.updateProps(createAction);
helper.updateProps({ uploadMode: "files", createFileAction: createAction });
helper.enable();
const req1 = helper.request();
const req2 = helper.request();
Expand All @@ -48,7 +48,7 @@ describe("ObjectCreationHelper", () => {
jest.useFakeTimers();
const helper = new ObjectCreationHelper("grid1", 10);
const createAction = actionValue(true, false);
helper.updateProps(createAction);
helper.updateProps({ uploadMode: "files", createFileAction: createAction });
const req1 = helper.request();
const req2 = helper.request();
jest.advanceTimersByTime(1000);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ export interface FileUploaderContainerProps {
removeSuccessMessage: DynamicValue<string>;
removeErrorMessage: DynamicValue<string>;
objectCreationTimeout: number;
onUploadSuccessFile?: ListActionValue;
onUploadSuccessImage?: ListActionValue;
onUploadFailureFile?: ListActionValue;
onUploadFailureImage?: ListActionValue;
enableCustomButtons: boolean;
customButtons: CustomButtonsType[];
}
Expand Down Expand Up @@ -115,6 +119,10 @@ export interface FileUploaderPreviewProps {
removeSuccessMessage: string;
removeErrorMessage: string;
objectCreationTimeout: number | null;
onUploadSuccessFile: {} | null;
onUploadSuccessImage: {} | null;
onUploadFailureFile: {} | null;
onUploadFailureImage: {} | null;
enableCustomButtons: boolean;
customButtons: CustomButtonsPreviewType[];
}
Loading