Skip to content

Commit

Permalink
fix(file): handle multiple boolean on droparea (#858)
Browse files Browse the repository at this point in the history
ref: MANAGER-15187
Signed-off-by: Maxime Bajeux <[email protected]>
  • Loading branch information
MaximeBajeux authored Sep 10, 2024
1 parent d5e240b commit 46430ff
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export const DragDropArea = forModule(moduleName).createElement(
disabled="$ctrl.disabled"
maxsize="150000"
model="$ctrl.model"
multiple
droparea>
</oui-file>`,
{
Expand All @@ -106,13 +107,33 @@ export const DragDropArea = forModule(moduleName).createElement(

DragDropArea.storyName = 'Drag & Drop area';

export const DragDropAreaSingle = forModule(moduleName).createElement(
() => compileTemplate(
`
<oui-file
disabled="$ctrl.disabled"
maxsize="150000"
model="$ctrl.model"
droparea>
</oui-file>`,
{
$ctrl: {
disabled: boolean('Disabled state', false),
},
},
),
);

DragDropAreaSingle.storyName = 'Drag & Drop area single file';

export const DragDropWithPreview = forModule(moduleName).createElement(
() => compileTemplate(
`
<oui-file
disabled="$ctrl.disabled"
maxsize="150000"
model="$ctrl.model"
multiple
droparea
preview>
</oui-file>`,
Expand Down
53 changes: 47 additions & 6 deletions packages/components/file/src/js/file.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default class {
this.units = orderBy(this.$filter('orderBy')(ouiFileConfiguration.units, '-size'), 'size', 'desc');
}

checkFileValidity(_file_) {
checkFileValidity(_file_, errors) {
const file = _file_;

if (file) {
Expand Down Expand Up @@ -58,9 +58,19 @@ export default class {
if (file.errors.type) {
this.form[this.name].$setValidity('type', false);
}
if (errors?.notSingle) {
this.form[this.name].$setValidity('not-single', false);
}
this.form[this.name].$setDirty();
}

if (!isEmpty(errors)) {
file.errors = {
...file.errors,
...errors,
};
}

// Clean errors
if (isEmpty(file.errors)) {
delete file.errors;
Expand Down Expand Up @@ -108,12 +118,22 @@ export default class {
this.model = [];
}

const errors = {};
if ((!this.multiple && this.droparea) && (files.length + this.model?.length || 0) > 1) {
errors.notSingle = true;
this.model.forEach((_item_) => {
const item = _item_;
if (!item.errors) item.errors = {};
item.errors.notSingle = true;
});
}

if (angular.isArray(files)) {
files.forEach((file) => {
// Check for duplicate before adding
if (!find(this.model, (item) => file.name === item.name)) {
this.getFileInfos(file);
this.checkFileValidity(file);
this.checkFileValidity(file, errors);
this.loadFilePreview(file);
this.model.push(file);
}
Expand All @@ -127,15 +147,35 @@ export default class {
removeFile(file) {
if (angular.isArray(this.model)) {
remove(this.model, (item) => item === file);
if (file.errors && this.form && this.form[this.name]) {
if (file.errors) {
let hasMaxsizeErrors = false;
let hasTypeErrors = false;
this.model.forEach((item) => {
let hasNotSingleError = false;

if (!this.multiple && this.droparea) {
hasNotSingleError = (this.model?.length || 0) > 1;
}

this.model.forEach((_item_) => {
const item = _item_;
hasMaxsizeErrors = hasMaxsizeErrors || item.errors?.maxsize;
hasTypeErrors = hasTypeErrors || item.errors?.type;
if (hasNotSingleError) {
if (!item.errors) item.errors = {};
item.errors.notSingle = true;
} else if (item.errors?.notSingle) {
delete item.errors.notSingle;
}
if (isEmpty(item.errors)) {
delete item.errors;
}
});
this.form[this.name].$setValidity('maxsize', !hasMaxsizeErrors);
this.form[this.name].$setValidity('type', !hasTypeErrors);

if (this.form && this.form[this.name]) {
this.form[this.name].$setValidity('maxsize', !hasMaxsizeErrors);
this.form[this.name].$setValidity('type', !hasTypeErrors);
this.form[this.name].$setValidity('not-single', !hasNotSingleError);
}
}
this.onRemove({ modelValue: this.model });
}
Expand All @@ -149,6 +189,7 @@ export default class {
if (this.form && this.form[this.name]) {
this.form[this.name].$setValidity('maxsize', true);
this.form[this.name].$setValidity('type', true);
this.form[this.name].$setValidity('not-single', true);
}
}

Expand Down
7 changes: 6 additions & 1 deletion packages/components/file/src/js/file.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@
<span class="oui-icon oui-icon-file" aria-hidden="true"></span>
</div>
<div class="oui-file-droparea__text" >
<span ng-bind="::$ctrl.translations.dropArea"></span>
<span ng-if="$ctrl.multiple" ng-bind="::$ctrl.translations.dropArea"></span>
<span ng-if="!$ctrl.multiple" ng-bind="::$ctrl.translations.dropAreaSingle"></span>
<button class="oui-link"
type="button"
ng-bind="::$ctrl.translations.dropAreaSelector"
Expand Down Expand Up @@ -144,6 +145,10 @@
ng-if="file.errors && file.errors.maxsize"
ng-bind="::$ctrl.translations.maxsizeError">
</span>
<span class="oui-file-attachments__error"
ng-if="file.errors && file.errors.notSingle"
ng-bind="::$ctrl.translations.notSingleError">
</span>
</span>
<button class="oui-file-attachments__remove oui-icon oui-icon-close"
type="button"
Expand Down
2 changes: 2 additions & 0 deletions packages/components/file/src/js/file.provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ export default class {
this.translations = {
attachmentsHeading: 'Attachment(s)',
dropArea: 'Attach document(s) by drap and drop or',
dropAreaSingle: 'Attach a document by drap and drop or',
dropAreaSelector: 'select a file',
filePlaceholder: 'Select a file...',
fileSelector: 'Select file',
filesSelector: 'Select file(s)...',
maxsizeError: 'This file exceeds the size limit',
notSingleError: 'You can only add one file',
removeFile: 'Remove file from selector',
};

Expand Down
70 changes: 70 additions & 0 deletions packages/components/file/src/js/file.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import find from 'lodash/find';
import isEmpty from 'lodash/isEmpty';

describe('ouiFile', () => {
let $timeout;
Expand All @@ -17,13 +18,20 @@ describe('ouiFile', () => {
type: 'image/png',
};

const mockFile2 = {
name: 'test2.png',
size: 150000,
type: 'image/png',
};

const invalidMockFile = {
name: 'test_invalid.png',
size: 500000,
type: 'image/jpeg',
};

const mockFiles = [mockFile];
const mockMultipleFiles = [mockFile, mockFile2];

beforeEach(angular.mock.module('oui.file'));
beforeEach(angular.mock.module('oui.file.configuration'));
Expand Down Expand Up @@ -192,6 +200,68 @@ describe('ouiFile', () => {
});
});

describe('Drop area without multiple', () => {
let element;
let controller;
let inputFile;
let selector;
let attachments;

beforeEach(() => {
element = TestUtils.compileTemplate('<oui-file model="$ctrl.model" droparea></oui-file>');
controller = element.controller('ouiFile');

$timeout.flush();

inputFile = getInputFile(element);
selector = getDropArea(element);
attachments = getAttachments(element);
});

it('should have input file multiple', () => {
expect(inputFile.multiple).toBeTruthy();
});

it('should show drop area and attachments', () => {
expect(selector).toBeTruthy();
expect(attachments).toBeTruthy();
});

it('should update classname with drag & drop events', () => {
controller.fileDroparea.triggerHandler('dragenter');
expect(controller.fileDroparea.hasClass('oui-file-droparea_dragover')).toBeTruthy();

controller.fileDroparea.triggerHandler('dragleave');
expect(controller.fileDroparea.hasClass('oui-file-droparea_dragover')).toBeFalsy();
});

it('should add files when dropped', () => {
controller.fileDroparea.triggerHandler({
type: 'drop',
dataTransfer: {
files: mockMultipleFiles,
},
});
});

it('should have error because multiple files have been loaded', () => {
controller.addFiles(mockMultipleFiles);
expect(controller.model.reduce(
(acc, item) => acc && !isEmpty(item.errors),
true,
)).toBeTruthy();
});

it('should have not error because we remove one of the two files loaded', () => {
controller.addFiles(mockMultipleFiles);
controller.removeFile(mockFile);
expect(controller.model.reduce(
(acc, item) => acc && !isEmpty(item.errors),
true,
)).toBeFalsy();
});
});

describe('File management', () => {
let element;
let controller;
Expand Down

0 comments on commit 46430ff

Please sign in to comment.