Skip to content

Commit fafff24

Browse files
authored
Chat FU: attach files which fail validation should block send button (#31592)
1 parent 4c7e741 commit fafff24

File tree

5 files changed

+118
-5
lines changed

5 files changed

+118
-5
lines changed

packages/devextreme/js/__internal/ui/chat/message_box/chat_text_area.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type { OptionChanged } from '@ts/core/widget/types';
2020
import type { SupportedKeys } from '@ts/core/widget/widget';
2121
import Widget from '@ts/core/widget/widget';
2222
import FileUploader from '@ts/ui/file_uploader/file_uploader';
23-
import type { CancelButtonClickEvent, Properties as FileUploaderProperties } from '@ts/ui/file_uploader/file_uploader.types';
23+
import type { CancelButtonClickEvent, FileValidationErrorEvent, Properties as FileUploaderProperties } from '@ts/ui/file_uploader/file_uploader.types';
2424
import Informer from '@ts/ui/informer/informer';
2525
import type { TextAreaProperties } from '@ts/ui/m_text_area';
2626
import TextArea from '@ts/ui/m_text_area';
@@ -328,6 +328,7 @@ class ChatTextArea extends TextArea<Properties> {
328328
onUploaded: (e) => this._fileUploaderOnUploaded(e),
329329
onCancelButtonClick: (e) => this._fileUploaderOnCancelButtonClick(e),
330330
onFileLimitReached: () => this._fileUploaderFileLimitReached(),
331+
onFileValidationError: (e) => this._fileUploaderFileValidationError(e),
331332
};
332333
}
333334

@@ -340,17 +341,21 @@ class ChatTextArea extends TextArea<Properties> {
340341
fileUploaderOptions.onValueChanged?.(e);
341342
}
342343

343-
_fileUploaderOnUploadStarted(e: UploadStartedEvent): void {
344-
const { file } = e;
345-
const { fileUploaderOptions = {} } = this.option();
346-
344+
_addFileToMap(file: File): void {
347345
this._filesToSend?.set(file, {
348346
readyToSend: false,
349347
name: file.name,
350348
size: file.size,
351349
});
352350
this._toggleButtonDisableState();
351+
}
352+
353+
_fileUploaderOnUploadStarted(e: UploadStartedEvent): void {
354+
const { file } = e;
355+
356+
this._addFileToMap(file);
353357

358+
const { fileUploaderOptions = {} } = this.option();
354359
fileUploaderOptions.onUploadStarted?.(e);
355360
}
356361

@@ -386,6 +391,12 @@ class ChatTextArea extends TextArea<Properties> {
386391
this._updateInputHeight();
387392
}
388393

394+
_fileUploaderFileValidationError(e: FileValidationErrorEvent): void {
395+
const { file } = e;
396+
397+
this._addFileToMap(file);
398+
}
399+
389400
_toggleButtonDisableState(state?: boolean): void {
390401
const shouldDisable = state ?? !this._isMessageCanBeSent();
391402
this._sendButton?.option('disabled', shouldDisable);

packages/devextreme/js/__internal/ui/file_uploader/file_uploader.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ class FileUploader extends Editor<FileUploaderProperties> {
140140

141141
_filesUploadedAction?: (event?: Record<string, unknown>) => void;
142142

143+
_fileValidationErrorAction?: (event?: Record<string, unknown>) => void;
144+
143145
_uploadedAction?: (event?: Record<string, unknown>) => void;
144146

145147
_beforeSendAction?: (event?: Record<string, unknown>) => void;
@@ -209,6 +211,7 @@ class FileUploader extends Editor<FileUploaderProperties> {
209211
onUploadStarted: null,
210212
onUploaded: null,
211213
onFilesUploaded: null,
214+
onFileValidationError: null,
212215
onProgress: null,
213216
onUploadError: null,
214217
onUploadAborted: null,
@@ -314,6 +317,7 @@ class FileUploader extends Editor<FileUploaderProperties> {
314317
this._createUploadStartedAction();
315318
this._createUploadedAction();
316319
this._createFilesUploadedAction();
320+
this._createFileValidationErrorAction();
317321
this._createProgressAction();
318322
this._createUploadErrorAction();
319323
this._createUploadAbortedAction();
@@ -619,6 +623,10 @@ class FileUploader extends Editor<FileUploaderProperties> {
619623
this._filesUploadedAction = this._createActionByOption('onFilesUploaded', { excludeValidators: ['readOnly'] });
620624
}
621625

626+
_createFileValidationErrorAction(): void {
627+
this._fileValidationErrorAction = this._createActionByOption('onFileValidationError', { excludeValidators: ['readOnly'] });
628+
}
629+
622630
_createProgressAction(): void {
623631
this._progressAction = this._createActionByOption('onProgress', { excludeValidators: ['readOnly'] });
624632
}
@@ -769,6 +777,8 @@ class FileUploader extends Editor<FileUploaderProperties> {
769777
if (!file.isValidMinSize) {
770778
file.$statusMessage.append(this._createValidationElement('invalidMinFileSizeMessage'));
771779
}
780+
781+
this._fileValidationErrorAction?.({ file: file.value });
772782
$fileContainer.addClass(FILEUPLOADER_INVALID_CLASS);
773783
}
774784
}
@@ -1716,6 +1726,9 @@ class FileUploader extends Editor<FileUploaderProperties> {
17161726
case 'onFilesUploaded':
17171727
this._createFilesUploadedAction();
17181728
break;
1729+
case 'onFileValidationError':
1730+
this._createFileValidationErrorAction();
1731+
break;
17191732
case 'onProgress':
17201733
this._createProgressAction();
17211734
break;

packages/devextreme/js/__internal/ui/file_uploader/file_uploader.types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ export interface FileUploaderChunkUploadResponse {
114114
error?: string;
115115
}
116116

117+
export type FileValidationErrorEvent = NativeEventInfo<FileUploader> & {
118+
readonly file: File;
119+
};
120+
117121
export type CancelButtonClickEvent = NativeEventInfo<InteractionEvent> & {
118122
readonly file?: File;
119123
};
@@ -146,6 +150,8 @@ export interface Properties extends PublicProperties {
146150
onCancelButtonClick?: (e: CancelButtonClickEvent) => void;
147151

148152
onFileLimitReached?: (e: FileLimitReachedEvent) => void;
153+
154+
onFileValidationError?: (e: FileValidationErrorEvent) => void;
149155
}
150156

151157
export interface FileUploaderProperties extends Properties,

packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/fileUploader.tests.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3670,6 +3670,51 @@ QUnit.module('uploading events', moduleConfig, () => {
36703670

36713671
$cancelButton.trigger('dxclick');
36723672
});
3673+
3674+
QUnit.test('onFileValidationError event should rise if file has not allowed extension', function(assert) {
3675+
assert.expect(2);
3676+
3677+
const $element = $('#fileuploader').dxFileUploader({
3678+
uploadMode: 'instantly',
3679+
allowedFileExtensions: ['.pdf'],
3680+
onFileValidationError: ({ file }) => {
3681+
assert.ok(true, 'onFileValidationError is called');
3682+
assert.strictEqual(file, fakeFile, 'file is passed');
3683+
},
3684+
});
3685+
3686+
simulateFileChoose($element, fakeFile);
3687+
});
3688+
3689+
QUnit.test('onFileValidationError event should rise if file has size bigger than maxFileSize', function(assert) {
3690+
assert.expect(2);
3691+
3692+
const $element = $('#fileuploader').dxFileUploader({
3693+
uploadMode: 'instantly',
3694+
maxFileSize: 1000,
3695+
onFileValidationError: ({ file }) => {
3696+
assert.ok(true, 'onFileValidationError is called');
3697+
assert.strictEqual(file, fakeFile2, 'file is passed');
3698+
},
3699+
});
3700+
3701+
simulateFileChoose($element, fakeFile2);
3702+
});
3703+
3704+
QUnit.test('onFileValidationError event should rise if file has size less than minFileSize', function(assert) {
3705+
assert.expect(2);
3706+
3707+
const $element = $('#fileuploader').dxFileUploader({
3708+
uploadMode: 'instantly',
3709+
minFileSize: 5000,
3710+
onFileValidationError: ({ file }) => {
3711+
assert.ok(true, 'onFileValidationError is called');
3712+
assert.strictEqual(file, fakeFile2, 'file is passed');
3713+
},
3714+
});
3715+
3716+
simulateFileChoose($element, fakeFile2);
3717+
});
36733718
});
36743719

36753720
QUnit.module('keyboard navigation', moduleConfig, () => {

packages/devextreme/testing/tests/DevExpress.ui.widgets/chatParts/chatTextArea.tests.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,44 @@ QUnit.module('ChatTextArea', moduleConfig, () => {
526526

527527
assert.strictEqual(this.sendButton.option('disabled'), true);
528528
});
529+
530+
QUnit.test('send button should be disabled after adding files and some of them fail validation', function(assert) {
531+
this.reinit({
532+
fileUploaderOptions: {
533+
uploadFile: () => {},
534+
allowedFileExtensions: ['.png'],
535+
},
536+
});
537+
const fileUploader = this.getFileUploader();
538+
fileUploader.option('value', [fakeFile, { name: 'img.jpg', size: 1 }]);
539+
fileUploader.upload();
540+
541+
this.clock.tick();
542+
543+
assert.strictEqual(this.sendButton.option('disabled'), true, 'send button is disabled after adding file that fail validation');
544+
});
545+
546+
QUnit.test('send button should not be disabled after removing file that fail validation', function(assert) {
547+
this.reinit({
548+
fileUploaderOptions: {
549+
uploadFile: () => {},
550+
allowedFileExtensions: ['.jpg'],
551+
},
552+
});
553+
const fileUploader = this.getFileUploader();
554+
fileUploader.option('value', [fakeFile]);
555+
fileUploader.upload();
556+
557+
this.clock.tick();
558+
559+
assert.strictEqual(this.sendButton.option('disabled'), true, 'send button is disabled');
560+
561+
const $cancelButton = this.$element.find(`.${FILEUPLOADER_CANCEL_BUTTON_CLASS}`);
562+
563+
$cancelButton.trigger('dxclick');
564+
565+
assert.strictEqual(this.sendButton.option('disabled'), true, 'send button is not disabled');
566+
});
529567
});
530568

531569
QUnit.module('Informer integration', {

0 commit comments

Comments
 (0)