@@ -85,7 +87,7 @@
class="c-file-upload__prompt qa-file-upload-prompt"
[class.c-file-upload__prompt--hidden]="hideButton"
[theme]="theme"
- [isDisabled]="dragInProgress"
+ [isDisabled]="dragInProgress || isDisabled"
(clicked)="promptForFiles()"
>
{{ buttonMessage }}
diff --git a/terminus-ui/file-upload/src/file-upload.component.scss b/terminus-ui/file-upload/src/file-upload.component.scss
index ec315d5fa..6de8d9279 100644
--- a/terminus-ui/file-upload/src/file-upload.component.scss
+++ b/terminus-ui/file-upload/src/file-upload.component.scss
@@ -21,7 +21,9 @@ $drag-bg: lighten(color(primary, xlight), 50%);
// Top level styles belong here
.c-file-upload {
- cursor: cursor(pointer);
+ &:not(.c-file-upload--disabled) {
+ cursor: cursor(pointer);
+ }
}
// If followed directly by another instance, add vertical spacing
@@ -39,7 +41,9 @@ $drag-bg: lighten(color(primary, xlight), 50%);
&:hover,
&:focus {
- border-color: color(primary, xlight);
+ &:not(.c-file-upload--disabled) {
+ border-color: color(primary, xlight);
+ }
}
// Class added when dragging over
@@ -63,6 +67,12 @@ $drag-bg: lighten(color(primary, xlight), 50%);
border-color: color(warn);
}
+ &.c-file-upload--disabled {
+ .c-file-upload__empty {
+ color: color(utility, light);
+ }
+ }
+
// container for the selected file
.c-file-upload__file {
@include typography(hint);
diff --git a/terminus-ui/file-upload/src/file-upload.component.spec.ts b/terminus-ui/file-upload/src/file-upload.component.spec.ts
index 0f40d4b7a..4e6bf501a 100644
--- a/terminus-ui/file-upload/src/file-upload.component.spec.ts
+++ b/terminus-ui/file-upload/src/file-upload.component.spec.ts
@@ -7,7 +7,9 @@ import {
TestBed,
TestModuleMetadata,
} from '@angular/core/testing';
+import { FormControl } from '@angular/forms';
import { By } from '@angular/platform-browser';
+import { KEYS } from '@terminus/ngx-tools/keycodes';
import {
configureTestBedWithoutReset,
createFakeEvent,
@@ -17,13 +19,12 @@ import {
} from '@terminus/ngx-tools/testing';
import { TsStyleThemeTypes } from '@terminus/ui/utilities';
-import { FormControl } from '@angular/forms';
-import { KEYS } from '@terminus/ngx-tools/keycodes';
import { TsFileUploadComponent } from './file-upload.component';
import { TsFileUploadModule } from './file-upload.module';
import { TsFileImageDimensionConstraints } from './image-dimension-constraints';
import {
- TS_ACCEPTED_MIME_TYPES, TsFileAcceptedMimeTypes,
+ TS_ACCEPTED_MIME_TYPES,
+ TsFileAcceptedMimeTypes,
} from './mime-types';
import { TsSelectedFile } from './selected-file';
@@ -36,8 +37,8 @@ const FILE_BLOB = new Blob(
[fileContentsMock],
{ type: 'image/png' },
);
-FILE_BLOB.lastModifiedDate = new Date();
-FILE_BLOB.name = 'foo';
+FILE_BLOB['lastModifiedDate'] = new Date();
+FILE_BLOB['name'] = 'foo';
jest.spyOn(FILE_BLOB, 'size', 'get').mockReturnValue(3 * 1024);
const FILE_MOCK = FILE_BLOB as File;
@@ -45,25 +46,27 @@ const FILE_MOCK = FILE_BLOB as File;
@Component({
template: `
`,
})
class TestHostComponent {
+ public isDisabled = false;
public mimeTypes: TsFileAcceptedMimeTypes | TsFileAcceptedMimeTypes[] | undefined = ['image/png', 'image/jpg'];
public maxKb: number | undefined;
public multiple = false;
@@ -86,19 +89,13 @@ class TestHostComponent {
}
-
-
describe(`TsFileUploadComponent`, function() {
let fixture: ComponentFixture;
let hostComponent: TestHostComponent;
let component: TsFileUploadComponent;
const moduleDefinition: TestModuleMetadata = {
- imports: [
- TsFileUploadModule,
- ],
- declarations: [
- TestHostComponent,
- ],
+ imports: [TsFileUploadModule],
+ declarations: [TestHostComponent],
};
configureTestBedWithoutReset(moduleDefinition);
@@ -121,7 +118,7 @@ describe(`TsFileUploadComponent`, function() {
class DummyFileReader {
public addEventListener = jest.fn();
public readAsDataURL = jest.fn().mockImplementation(function(this: FileReader) {
- this.onload({} as Event);
+ this.onload!({} as ProgressEvent);
});
// eslint-disable-next-line max-len
public result = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABIAQMAAABvIyEEAAAAA1BMVEXXbFn0Q9OUAAAADklEQVR4AWMYRmAUjAIAAtAAAaW+yXMAAAAASUVORK5CYII=';
@@ -129,7 +126,6 @@ describe(`TsFileUploadComponent`, function() {
// Not sure why any is needed
(window as any).FileReader = jest.fn(() => new DummyFileReader);
-
class DummyImage {
public _onload = () => {};
public set onload(fn) {
@@ -155,6 +151,28 @@ describe(`TsFileUploadComponent`, function() {
component = hostComponent.component;
});
+ describe(`isDisabled`, () => {
+
+ test(`should disable the file upload`, () => {
+ component.ratioConstraints = ['2:1'];
+ fixture.detectChanges();
+ const el = component['elementRef'].nativeElement;
+ const wrapper = el.querySelector('.c-file-upload');
+ const button = el.querySelector('ts-button').querySelector('button');
+
+ expect(component.isDisabled).toEqual(false);
+ expect(wrapper.classList).not.toContain('c-file-upload--disabled');
+ expect(button.getAttribute('disabled')).toEqual(null);
+
+ hostComponent.isDisabled = true;
+ fixture.detectChanges();
+
+ expect(component.isDisabled).toEqual(true);
+ expect(wrapper.classList).toContain('c-file-upload--disabled');
+ expect(button.getAttribute('disabled')).toEqual('true');
+ });
+
+ });
describe(`accept`, () => {
@@ -171,7 +189,6 @@ describe(`TsFileUploadComponent`, function() {
});
-
describe(`seedFile`, () => {
test(`should seed the file and trigger all process'`, () => {
@@ -188,11 +205,10 @@ describe(`TsFileUploadComponent`, function() {
});
-
describe(`buttonMessage`, () => {
test(`should set the correct drop vs select message pluralized if neeeded`, () => {
- const el = component.elementRef.nativeElement;
+ const el = component['elementRef'].nativeElement;
hostComponent.multiple = true;
dispatchMouseEvent(el, 'dragover');
fixture.detectChanges();
@@ -222,7 +238,6 @@ describe(`TsFileUploadComponent`, function() {
});
-
describe(`hideButton`, () => {
test(`should hide the button from view`, () => {
@@ -238,7 +253,6 @@ describe(`TsFileUploadComponent`, function() {
});
-
describe(`validation messages`, () => {
test(`should show size validation message`, () => {
@@ -253,7 +267,6 @@ describe(`TsFileUploadComponent`, function() {
expect(uploadDiv.classes['c-file-upload--error']).toBeTruthy();
});
-
test(`should show MIME type validation message`, () => {
hostComponent.mimeTypes = 'text/csv';
fixture.detectChanges();
@@ -266,7 +279,6 @@ describe(`TsFileUploadComponent`, function() {
expect(uploadDiv.classes['c-file-upload--error']).toBeTruthy();
});
-
test(`should show image dimension validation message`, () => {
hostComponent.constraints = [{
height: {
@@ -288,7 +300,6 @@ describe(`TsFileUploadComponent`, function() {
expect(uploadDiv.classes['c-file-upload--error']).toBeTruthy();
});
-
test(`should show image ratio validation message`, () => {
hostComponent.ratioConstraints = ['2:1'];
fixture.detectChanges();
@@ -303,7 +314,6 @@ describe(`TsFileUploadComponent`, function() {
});
-
describe(`hints`, () => {
test(`should set image hints`, () => {
@@ -349,7 +359,6 @@ describe(`TsFileUploadComponent`, function() {
expect(hints[2].nativeElement.textContent).toContain('Must be under 100kb');
});
-
test(`should not set dimensions hint if constraints were not passed in`, () => {
hostComponent.mimeTypes = ['image/jpg', 'image/png'];
hostComponent.maxKb = 100;
@@ -361,7 +370,6 @@ describe(`TsFileUploadComponent`, function() {
expect(hints[1].nativeElement.textContent).toContain('Must be under 100kb');
});
-
test(`should set csv hints`, () => {
hostComponent.mimeTypes = 'text/csv';
hostComponent.maxKb = 100;
@@ -381,7 +389,6 @@ describe(`TsFileUploadComponent`, function() {
});
});
-
describe(`removeFile`, () => {
test(`should clear the file, clear validations and emit an event`, () => {
@@ -401,135 +408,128 @@ describe(`TsFileUploadComponent`, function() {
expect(component.file).toBeFalsy();
});
-
- test(`should stop event propogation`, () => {
+ test(`should stop event propagation`, () => {
component.seedFile = FILE_MOCK;
fixture.detectChanges();
- component.preventAndStopEventPropagation = jest.fn();
const mouseEvent = createMouseEvent('click');
+ Object.defineProperty(mouseEvent, 'preventDefault', { value: jest.fn() });
+ Object.defineProperty(mouseEvent, 'stopPropagation', { value: jest.fn() });
+
component.removeFile(mouseEvent);
- expect(component.preventAndStopEventPropagation).toHaveBeenCalledWith(mouseEvent);
+ expect(mouseEvent.preventDefault).toHaveBeenCalled();
+ expect(mouseEvent.stopPropagation).toHaveBeenCalled();
});
});
-
describe(`setUpNewFile`, () => {
test(`should not continue if no file is passed in`, () => {
- component.setValidationMessages = jest.fn();
- component.setUpNewFile(undefined as any);
+ component['setValidationMessages'] = jest.fn();
+ component['setUpNewFile'](undefined as any);
- expect(component.setValidationMessages).not.toHaveBeenCalled();
+ expect(component['setValidationMessages']).not.toHaveBeenCalled();
});
});
-
describe(`setValidationMessages`, () => {
test(`should do nothing if no file was passed in`, () => {
component.formControl.setErrors = jest.fn();
- component.setValidationMessages(undefined);
+ component['setValidationMessages'](undefined);
expect(component.formControl.setErrors).not.toHaveBeenCalled();
});
});
-
describe(`updateVirtualFileInputAttrs`, () => {
test(`should add and remove the multiple attr`, () => {
- expect(component.virtualFileInput.getAttribute('multiple')).toBeFalsy();
+ expect(component['virtualFileInput'].getAttribute('multiple')).toBeFalsy();
hostComponent.multiple = true;
fixture.detectChanges();
- expect(component.virtualFileInput.getAttribute('multiple')).toBeTruthy();
+ expect(component['virtualFileInput'].getAttribute('multiple')).toBeTruthy();
hostComponent.multiple = false;
fixture.detectChanges();
- expect(component.virtualFileInput.getAttribute('multiple')).toBeFalsy();
+ expect(component['virtualFileInput'].getAttribute('multiple')).toBeFalsy();
});
-
test(`should add and remove the accept attr`, () => {
- expect(component.virtualFileInput.getAttribute('accept')).toBeFalsy();
+ expect(component['virtualFileInput'].getAttribute('accept')).toBeFalsy();
hostComponent.mimeTypes = 'text/csv';
fixture.detectChanges();
- expect(component.virtualFileInput.getAttribute('accept')).toEqual('text/csv');
+ expect(component['virtualFileInput'].getAttribute('accept')).toEqual('text/csv');
hostComponent.mimeTypes = undefined;
fixture.detectChanges();
- expect(component.virtualFileInput.getAttribute('accept'))
+ expect(component['virtualFileInput'].getAttribute('accept'))
.toEqual('text/csv,image/jpeg,image/jpg,image/png,image/gif,video/mp4,video/x-flv,video/webm,video/quicktime,video/mpeg');
});
});
-
describe(`HostListeners`, () => {
test(`should handle dragover`, () => {
- component.preventAndStopEventPropagation = jest.fn();
- dispatchMouseEvent(component.elementRef.nativeElement, 'dragover');
+ component['preventAndStopEventPropagation'] = jest.fn();
+ dispatchMouseEvent(component['elementRef'].nativeElement, 'dragover');
fixture.detectChanges();
const foundClass = fixture.debugElement.query(By.css('.c-file-upload--drag'));
expect(foundClass).toBeTruthy();
- expect(component.preventAndStopEventPropagation).toHaveBeenCalledWith(expect.any(Event));
+ expect(component['preventAndStopEventPropagation']).toHaveBeenCalledWith(expect.any(Event));
expect(hostComponent.userDragBegin).toHaveBeenCalled();
});
-
test(`should handle dragleave`, () => {
- component.preventAndStopEventPropagation = jest.fn();
- dispatchMouseEvent(component.elementRef.nativeElement, 'dragleave');
+ component['preventAndStopEventPropagation'] = jest.fn();
+ dispatchMouseEvent(component['elementRef'].nativeElement, 'dragleave');
fixture.detectChanges();
const foundClass = fixture.debugElement.query(By.css('.c-file-upload--drag'));
expect(foundClass).toBeFalsy();
- expect(component.preventAndStopEventPropagation).toHaveBeenCalledWith(expect.any(Event));
+ expect(component['preventAndStopEventPropagation']).toHaveBeenCalledWith(expect.any(Event));
expect(hostComponent.userDragEnd).toHaveBeenCalled();
});
-
test(`should handle drop`, () => {
component.dragInProgress = true;
- component.preventAndStopEventPropagation = jest.fn();
- component.collectFilesFromEvent = jest.fn();
+ component['preventAndStopEventPropagation'] = jest.fn();
+ component['collectFilesFromEvent'] = jest.fn();
const event = createFakeEvent('drop');
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('.c-file-upload--drag'))).toBeTruthy();
- component.elementRef.nativeElement.dispatchEvent(event);
+ component['elementRef'].nativeElement.dispatchEvent(event);
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('.c-file-upload--drag'))).toBeFalsy();
- expect(component.preventAndStopEventPropagation).toHaveBeenCalled();
- expect(component.collectFilesFromEvent).toHaveBeenCalled();
+ expect(component['preventAndStopEventPropagation']).toHaveBeenCalled();
+ expect(component['collectFilesFromEvent']).toHaveBeenCalled();
});
-
test(`should handle click`, () => {
- component.virtualFileInput.click = jest.fn();
- dispatchMouseEvent(component.elementRef.nativeElement, 'click');
+ component['virtualFileInput'].click = jest.fn();
+ dispatchMouseEvent(component['elementRef'].nativeElement, 'click');
- expect(component.virtualFileInput.click).toHaveBeenCalled();
+ expect(component['virtualFileInput'].click).toHaveBeenCalled();
});
-
describe(`keypress`, () => {
let el: HTMLElement;
beforeEach(() => {
- el = component.elementRef.nativeElement;
+ el = component['elementRef'].nativeElement;
component.promptForFiles = jest.fn();
el.blur = jest.fn();
});
@@ -541,7 +541,6 @@ describe(`TsFileUploadComponent`, function() {
expect(el.blur).toHaveBeenCalled();
});
-
test(`should do nothing if the key pressed was not Enter`, () => {
dispatchKeyboardEvent(el, 'keydown', KEYS.A);
@@ -552,20 +551,22 @@ describe(`TsFileUploadComponent`, function() {
});
-
describe(`collectFilesFromEvent`, () => {
+ beforeEach(() => {
+ component['setUpNewFile'] = jest.fn();
+ });
+
test(`should throw an error if no files exist in the dataTransfer object`, () => {
const event = createFakeEvent('DragEvent') as DragEvent;
const dataTransfer = { files: [] };
Object.defineProperty(event, 'dataTransfer', { value: dataTransfer });
- component.setUpNewFile = jest.fn();
expect(() => {
- component.collectFilesFromEvent(event);
+ component['collectFilesFromEvent'](event);
}).toThrowError();
fixture.detectChanges();
- expect(component.setUpNewFile).not.toHaveBeenCalled();
+ expect(component['setUpNewFile']).not.toHaveBeenCalled();
expect(hostComponent.handleFile).not.toHaveBeenCalled();
});
@@ -574,57 +575,50 @@ describe(`TsFileUploadComponent`, function() {
const event = createFakeEvent('Event');
const input = document.createElement('input');
Object.defineProperty(event, 'target', { value: input });
- component.setUpNewFile = jest.fn();
expect(() => {
- component.collectFilesFromEvent(event);
+ component['collectFilesFromEvent'](event);
}).toThrowError();
fixture.detectChanges();
- expect(component.setUpNewFile).not.toHaveBeenCalled();
+ expect(component['setUpNewFile']).not.toHaveBeenCalled();
expect(hostComponent.handleFile).not.toHaveBeenCalled();
});
-
test(`should collect a file from a drag/drop event`, () => {
const event = createFakeEvent('DragEvent') as DragEvent;
const dataTransfer = { files: [FILE_MOCK] };
Object.defineProperty(event, 'dataTransfer', { value: dataTransfer });
- component.setUpNewFile = jest.fn();
fixture.detectChanges();
- component.collectFilesFromEvent(event);
+ component['collectFilesFromEvent'](event);
fixture.detectChanges();
- expect(component.setUpNewFile).toHaveBeenCalledWith(expect.any(TsSelectedFile));
+ expect(component['setUpNewFile']).toHaveBeenCalledWith(expect.any(TsSelectedFile));
expect(hostComponent.handleFile).toHaveBeenCalledWith(expect.any(TsSelectedFile));
expect(hostComponent.formControl.value).toEqual(FILE_MOCK);
});
-
test(`should collect a file from an input change (manual selection)`, () => {
const event = createFakeEvent('Event');
const input = document.createElement('input');
Object.defineProperty(input, 'files', { value: [FILE_MOCK] });
Object.defineProperty(event, 'target', { value: input });
- component.setUpNewFile = jest.fn();
- component.collectFilesFromEvent(event);
+ component['collectFilesFromEvent'](event);
fixture.detectChanges();
- expect(component.setUpNewFile).toHaveBeenCalledWith(expect.any(TsSelectedFile));
+ expect(component['setUpNewFile']).toHaveBeenCalledWith(expect.any(TsSelectedFile));
expect(hostComponent.handleFile).toHaveBeenCalledWith(expect.any(TsSelectedFile));
});
-
test(`should collect emit when multiple files are selected`, () => {
const event = createFakeEvent('DragEvent') as DragEvent;
const dataTransfer = { files: [FILE_MOCK, FILE_MOCK] };
Object.defineProperty(event, 'dataTransfer', { value: dataTransfer });
- component.setUpNewFile = jest.fn();
- component.collectFilesFromEvent(event);
+ component['collectFilesFromEvent'](event);
fixture.detectChanges();
expect(hostComponent.handleMultipleFiles).toHaveBeenCalled();
expect(hostComponent.handleFile).not.toHaveBeenCalled();
- expect(component.setUpNewFile).not.toHaveBeenCalled();
+ expect(component['setUpNewFile']).not.toHaveBeenCalled();
});
});
@@ -633,44 +627,42 @@ describe(`TsFileUploadComponent`, function() {
describe(`ngOnDestroy`, () => {
test(`should remove the event listener`, () => {
- component.onVirtualInputElementChange = jest.fn();
- component.dropProtectionService.remove = jest.fn();
+ component['onVirtualInputElementChange'] = jest.fn();
+ component['dropProtectionService'].remove = jest.fn();
component.ngOnDestroy();
const event = createFakeEvent('change');
- component.virtualFileInput.dispatchEvent(event);
+ component['virtualFileInput'].dispatchEvent(event);
- expect(component.onVirtualInputElementChange).not.toHaveBeenCalled();
- expect(component.dropProtectionService.remove).toHaveBeenCalled();
+ expect(component['onVirtualInputElementChange']).not.toHaveBeenCalled();
+ expect(component['dropProtectionService'].remove).toHaveBeenCalled();
});
});
-
describe(`virtualFileInput.change`, () => {
test(`should trigger the file handler`, () => {
- component.collectFilesFromEvent = jest.fn();
+ component['collectFilesFromEvent'] = jest.fn();
// Wire up bindings
component.ngAfterContentInit();
const event = createFakeEvent('change');
- component.virtualFileInput.dispatchEvent(event);
+ component['virtualFileInput'].dispatchEvent(event);
- expect(component.collectFilesFromEvent).toHaveBeenCalled();
- expect(component.virtualFileInput.value).toEqual('');
+ expect(component['collectFilesFromEvent']).toHaveBeenCalled();
+ expect(component['virtualFileInput'].value).toEqual('');
});
});
-
describe(`preventAndStopEventPropagation`, () => {
- test(`should both prevent and stop event propogation`, () => {
+ test(`should both prevent and stop event propagation`, () => {
const event = createFakeEvent('fake');
Object.defineProperties(event, {
preventDefault: { value: jest.fn() },
stopPropagation: { value: jest.fn() },
});
- component.preventAndStopEventPropagation(event);
+ component['preventAndStopEventPropagation'](event);
expect(event.preventDefault).toHaveBeenCalled();
expect(event.stopPropagation).toHaveBeenCalled();
@@ -678,19 +670,17 @@ describe(`TsFileUploadComponent`, function() {
});
-
describe(`ngOnInit`, () => {
test(`should enable dropProtectionService`, () => {
- component.dropProtectionService.add = jest.fn();
+ component['dropProtectionService'].add = jest.fn();
component.ngOnInit();
- expect(component.dropProtectionService.add).toHaveBeenCalled();
+ expect(component['dropProtectionService'].add).toHaveBeenCalled();
});
});
-
describe(`theme`, () => {
test(`should set the theme`, () => {
@@ -702,17 +692,46 @@ describe(`TsFileUploadComponent`, function() {
});
+ describe(`formControl`, () => {
+
+ test(`should fall back to internal control if one is not passed in`, () => {
+ component.formControl = undefined as any;
+ expect(component.formControl).toBeTruthy();
+ });
+
+ });
+
+ describe(`id`, () => {
+
+ test(`should support a custom ID and fall back to UID`, () => {
+ component.id = 'foo';
+ fixture.detectChanges();
+ const wrapper = component['elementRef'].nativeElement.querySelector('#foo');
+
+ expect(component.id).toEqual('foo');
+ expect(wrapper).toBeTruthy();
+
+ component.id = undefined as any;
+ fixture.detectChanges();
+ expect(component.id).toEqual(expect.stringContaining('ts-file-upload-'));
+ });
+
+ });
+
+ // NOTE: Currently this must be our last test - something may not be getting reset properly
describe(`ratioConstraint format`, () => {
- test(`should throw error if ratioContraint is not in right format`, () => {
+
+ test(`should throw error if ratio constraint is not in right format`, () => {
expect(() => {
try {
- hostComponent.ratioConstraints = '5' as any;
+ hostComponent.ratioConstraints = '5:v' as any;
fixture.detectChanges();
} catch (e) {
throw new Error(e);
}
}).toThrowError();
});
+
});
});
diff --git a/terminus-ui/file-upload/src/file-upload.component.ts b/terminus-ui/file-upload/src/file-upload.component.ts
index a9f604b89..5b8dd896f 100644
--- a/terminus-ui/file-upload/src/file-upload.component.ts
+++ b/terminus-ui/file-upload/src/file-upload.component.ts
@@ -5,7 +5,6 @@ import {
Component,
ElementRef,
EventEmitter,
- HostBinding,
HostListener,
Input,
isDevMode,
@@ -125,7 +124,7 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
/**
* Define the default component ID
*/
- protected _uid = `ts-file-upload-${nextUniqueId++}`;
+ protected uid = `ts-file-upload-${nextUniqueId++}`;
/**
* A flag that represents an in-progress drag movement
@@ -145,13 +144,7 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
/**
* Store reference to the generated file input
*/
- private virtualFileInput: HTMLInputElement;
-
- /**
- * Reflect the ID back to the DOM
- */
- @HostBinding('attr.id')
- public publicID: string = this.id;
+ private readonly virtualFileInput: HTMLInputElement;
/**
* Provide access to the file preview element
@@ -163,11 +156,7 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
* Get the file select button text
*/
public get buttonMessage(): string {
- if (this.dragInProgress) {
- return `Drop File${this.multiple ? 's' : ''}`;
- }
- return `Select File${this.multiple ? 's' : ''}`;
-
+ return this.dragInProgress ? `Drop File${this.multiple ? 's' : ''}` : `Select File${this.multiple ? 's' : ''}`;
}
/**
@@ -265,8 +254,6 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
/**
* Define if the 'select files' button should be visible. DO NOT USE.
- *
- * TODO: This should be removed once UX/Product decide if they want the button.
*/
@Input()
public hideButton = false;
@@ -276,23 +263,25 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
*/
@Input()
public set id(value: string) {
- this._id = value || this._uid;
+ this._id = value || this.uid;
}
public get id(): string {
return this._id;
}
- protected _id!: string;
+ private _id: string = this.uid;
+
+ /**
+ * Define if the component is disabled
+ */
+ @Input()
+ public isDisabled = false;
/**
* Define the maximum file size in kilobytes
*/
@Input()
public set maximumKilobytesPerFile(value: number) {
- if (!value) {
- return;
- }
-
- this._maximumKilobytesPerFile = value;
+ this._maximumKilobytesPerFile = value || MAXIMUM_KILOBYTES_PER_FILE;
}
public get maximumKilobytesPerFile(): number {
return this._maximumKilobytesPerFile;
@@ -308,7 +297,7 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
for (const value of values) {
const v = value.split(':');
const minPartsForValidRatio = 2;
- if ((v.length !== minPartsForValidRatio) || (!isNumber(v[0]) && !isNumber(v[1]))) {
+ if ((v.length !== minPartsForValidRatio) || (!isNumber(v[0]) || !isNumber(v[1]))) {
throw new Error('TsFileUploadComponent: An array of image ratios should be formatted as ["1:2", "3:4"]');
}
}
@@ -320,7 +309,6 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
}
private _ratioConstraints: Array | undefined;
-
/**
* Define if multiple files may be uploaded
*/
@@ -393,59 +381,71 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
* Event emitted when the user's cursor enters the field while dragging a file
*/
@Output()
- public readonly enter: EventEmitter = new EventEmitter();
+ public readonly enter = new EventEmitter();
/**
* Event emitted when the user's cursor exits the field while dragging a file
*/
@Output()
- public readonly exit: EventEmitter = new EventEmitter();
+ public readonly exit = new EventEmitter();
/**
* Event emitted when the user drops or selects a file
*/
@Output()
- public readonly selected: EventEmitter = new EventEmitter();
+ public readonly selected = new EventEmitter();
/**
* Event emitted when the user drops or selects multiple files
*/
@Output()
- public readonly selectedMultiple: EventEmitter = new EventEmitter();
+ public readonly selectedMultiple = new EventEmitter();
/**
* Event emitted when the user clears a loaded file
*/
@Output()
- public readonly cleared: EventEmitter = new EventEmitter();
+ public readonly cleared = new EventEmitter();
/**
* HostListeners
*/
@HostListener('dragover', ['$event'])
public handleDragover(event: TsFileUploadDragEvent) {
- this.preventAndStopEventPropagation(event);
- this.enter.emit(true);
- this.dragInProgress = true;
+ // istanbul ignore else
+ if (!this.isDisabled) {
+ this.preventAndStopEventPropagation(event);
+ this.enter.emit(true);
+ this.dragInProgress = true;
+ }
}
@HostListener('dragleave', ['$event'])
public handleDragleave(event: TsFileUploadDragEvent) {
- this.preventAndStopEventPropagation(event);
- this.exit.emit(true);
- this.dragInProgress = false;
+ // istanbul ignore else
+ if (!this.isDisabled) {
+ this.preventAndStopEventPropagation(event);
+ this.exit.emit(true);
+ this.dragInProgress = false;
+ }
}
@HostListener('drop', ['$event'])
public handleDrop(event: TsFileUploadDragEvent) {
- this.preventAndStopEventPropagation(event);
- this.dragInProgress = false;
- this.collectFilesFromEvent(event);
+ // istanbul ignore else
+ if (!this.isDisabled) {
+ this.preventAndStopEventPropagation(event);
+ this.dragInProgress = false;
+ this.collectFilesFromEvent(event);
+ }
}
@HostListener('click')
public handleClick() {
- this.promptForFiles();
+ // istanbul ignore else
+ if (!this.isDisabled) {
+ this.promptForFiles();
+ }
}
@@ -457,9 +457,6 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
) {
super();
this.virtualFileInput = this.createFileInput();
-
- // Force setter to be called in case the ID was not specified.
- this.id = this.id;
}
/**
@@ -500,7 +497,7 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
/**
- * Update the virtual file imput when the change event is fired
+ * Update the virtual file input when the change event is fired
*/
public ngAfterContentInit(): void {
this.virtualFileInput.addEventListener('change', this.onVirtualInputElementChange.bind(this));
@@ -669,13 +666,18 @@ export class TsFileUploadComponent extends TsReactiveFormBaseComponent implement
* @param event - The event
*/
private onVirtualInputElementChange(event: Event): void {
- this.collectFilesFromEvent(event);
- this.virtualFileInput.value = '';
+ // istanbul ignore else
+ if (!this.isDisabled) {
+ this.collectFilesFromEvent(event);
+ this.virtualFileInput.value = '';
+ }
}
/*
- * Stops event propogation
+ * Stops event propagation
+ *
+ * NOTE: Making this static seems to break our tests.
*/
private preventAndStopEventPropagation(event: Event): void {
event.preventDefault();
diff --git a/terminus-ui/file-upload/src/image-dimension-constraints.ts b/terminus-ui/file-upload/src/image-dimension-constraints.ts
index 5ac8e6fc4..546419487 100644
--- a/terminus-ui/file-upload/src/image-dimension-constraints.ts
+++ b/terminus-ui/file-upload/src/image-dimension-constraints.ts
@@ -1,6 +1,6 @@
/**
- * An indiviual size constraint
+ * An individual size constraint
*/
export interface TsFileImageDimensionContraint {
height: {
diff --git a/terminus-ui/file-upload/src/selected-file.spec.ts b/terminus-ui/file-upload/src/selected-file.spec.ts
index 13d79c681..e76614a71 100644
--- a/terminus-ui/file-upload/src/selected-file.spec.ts
+++ b/terminus-ui/file-upload/src/selected-file.spec.ts
@@ -1,4 +1,3 @@
-
import { TsFileImageDimensionConstraints } from './image-dimension-constraints';
import { TsFileAcceptedMimeTypes } from './mime-types';
import { TsSelectedFile } from './selected-file';
@@ -33,8 +32,8 @@ const FILE_BLOB = new Blob(
['data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABIAQMAAABvIyEEAAAAA1BMVEXXbFn0Q9OUAAAADklEQVR4AWMYRmAUjAIAAtAAAaW+yXMAAAAASUVORK5CYII='],
{ type: 'image/png' },
);
-FILE_BLOB.lastModifiedDate = new Date();
-FILE_BLOB.name = 'foo';
+FILE_BLOB['lastModifiedDate'] = new Date();
+FILE_BLOB['name'] = 'foo';
jest.spyOn(FILE_BLOB, 'size', 'get').mockReturnValue(3 * 1024);
const FILE_MOCK = FILE_BLOB as File;
@@ -43,8 +42,8 @@ const FILE_CSV_BLOB = new Blob(
['my csv value'],
{ type: 'text/csv' },
);
-FILE_CSV_BLOB.lastModifiedDate = new Date();
-FILE_CSV_BLOB.name = 'myCSV';
+FILE_CSV_BLOB['lastModifiedDate'] = new Date();
+FILE_CSV_BLOB['name'] = 'myCSV';
jest.spyOn(FILE_CSV_BLOB, 'size', 'get').mockReturnValue(3 * 1024);
const FILE_CSV_MOCK = FILE_CSV_BLOB as File;
@@ -53,14 +52,12 @@ const FILE_VIDEO_BLOB = new Blob(
['my video value'],
{ type: 'video/mp4' },
);
-FILE_VIDEO_BLOB.lastModifiedDate = new Date();
-FILE_VIDEO_BLOB.name = 'myVideo';
+FILE_VIDEO_BLOB['lastModifiedDate'] = new Date();
+FILE_VIDEO_BLOB['name'] = 'myVideo';
jest.spyOn(FILE_VIDEO_BLOB, 'size', 'get').mockReturnValue(3 * 1024);
const FILE_VIDEO_MOCK = FILE_VIDEO_BLOB as File;
-
-
describe(`TsSelectedFile`, function() {
const createFile = (
file = FILE_MOCK,
@@ -79,7 +76,6 @@ describe(`TsSelectedFile`, function() {
ratio,
);
-
// Mock `FileReader` and `Image`:
beforeEach(() => {
// Mock FileReader
@@ -114,10 +110,8 @@ describe(`TsSelectedFile`, function() {
}
}
(window as any).Image = jest.fn(() => new DummyImage());
-
});
-
describe(`constructor`, () => {
test(`should set top-level items and validations`, () => {
@@ -135,7 +129,6 @@ describe(`TsSelectedFile`, function() {
expect.assertions(6);
});
-
test(`should set top-level items and validations for videos`, done => {
const file = createFile(FILE_VIDEO_MOCK, undefined, ['video/mp4']);
file.fileLoaded$.subscribe(f => {
@@ -154,7 +147,6 @@ describe(`TsSelectedFile`, function() {
});
-
describe(`width`, () => {
test(`should return the width or zero`, () => {
@@ -167,7 +159,6 @@ describe(`TsSelectedFile`, function() {
});
-
describe(`height`, () => {
test(`should return the height or zero`, () => {
@@ -180,7 +171,6 @@ describe(`TsSelectedFile`, function() {
});
-
describe(`isCSV`, () => {
test(`should return true is the file is a CSV`, () => {
@@ -195,7 +185,6 @@ describe(`TsSelectedFile`, function() {
});
-
describe(`isImage`, () => {
test(`should return true is the file is an image`, () => {
@@ -209,7 +198,6 @@ describe(`TsSelectedFile`, function() {
});
-
describe(`isVideo`, () => {
test(`should return true is the file is a video`, () => {
@@ -224,7 +212,6 @@ describe(`TsSelectedFile`, function() {
});
-
describe(`fileContents`, () => {
test(`should return the FileReader result`, () => {
@@ -257,7 +244,7 @@ describe(`TsSelectedFile`, function() {
public onload = jest.fn();
public addEventListener = jest.fn();
public readAsDataURL = jest.fn().mockImplementation(function(this: FileReader) {
- this.onload({} as Event);
+ this.onload!({} as ProgressEvent);
});
public result = str2ab(FILE_BLOB);
}
@@ -277,7 +264,7 @@ describe(`TsSelectedFile`, function() {
public onload = jest.fn();
public addEventListener = jest.fn();
public readAsDataURL = jest.fn().mockImplementation(function(this: FileReader) {
- this.onload({} as Event);
+ this.onload!({} as ProgressEvent);
});
public result = str2ab(FILE_CSV_BLOB);
}
@@ -293,11 +280,8 @@ describe(`TsSelectedFile`, function() {
});
});
-
-
});
-
describe(`isValid`, () => {
const file = createFile();
@@ -334,7 +318,6 @@ describe(`TsSelectedFile`, function() {
});
-
describe(`determineImageDimensions`, () => {
test(`should set validation to true and exit if the file is not an image`, done => {
@@ -351,7 +334,6 @@ describe(`TsSelectedFile`, function() {
expect.assertions(4);
});
-
test(`should still seed the FileReader for non-image files`, done => {
const file = createFile(FILE_CSV_MOCK);
file.fileLoaded$.subscribe(f => {
@@ -363,7 +345,6 @@ describe(`TsSelectedFile`, function() {
expect.assertions(1);
});
-
test(`should set dimensions and call callback`, () => {
createFile().fileLoaded$.subscribe(f => {
if (f) {
@@ -378,28 +359,25 @@ describe(`TsSelectedFile`, function() {
});
-
describe(`validateImageRatio`, () => {
test(`should return true if no constraints exist`, () => {
const file = createFile();
- expect(file.validateImageRatio(undefined)).toEqual(true);
+ expect(file['validateImageRatio'](undefined)).toEqual(true);
});
-
test(`should return true if ratio are valid`, () => {
const file = createFile();
- const result = file.validateImageRatio([{
+ const result = file['validateImageRatio']([{
widthRatio: 1,
heightRatio: 1,
}]);
expect(result).toEqual(true);
});
-
test(`should return false if ratio are not valid`, () => {
const file = createFile();
- const result = file.validateImageRatio([{
+ const result = file['validateImageRatio']([{
widthRatio: 2,
heightRatio: 1,
}]);
@@ -412,17 +390,15 @@ describe(`TsSelectedFile`, function() {
test(`should return true if no constraints exist`, () => {
const file = createFile();
- expect(file.validateImageDimensions(undefined)).toEqual(true);
+ expect(file['validateImageDimensions'](undefined)).toEqual(true);
});
-
test(`should return true if dimensions are valid`, () => {
const file = createFile();
- const result = file.validateImageDimensions(CONSTRAINTS_MOCK);
+ const result = file['validateImageDimensions'](CONSTRAINTS_MOCK);
expect(result).toEqual(true);
});
-
test(`should return false if dimensions are not valid`, () => {
const file = createFile();
const constraints = [
@@ -447,7 +423,7 @@ describe(`TsSelectedFile`, function() {
},
},
];
- const result = file.validateImageDimensions(constraints);
+ const result = file['validateImageDimensions'](constraints);
expect(result).toEqual(false);
});
diff --git a/terminus-ui/file-upload/src/selected-file.ts b/terminus-ui/file-upload/src/selected-file.ts
index 919926b1b..0dfdb2480 100644
--- a/terminus-ui/file-upload/src/selected-file.ts
+++ b/terminus-ui/file-upload/src/selected-file.ts
@@ -273,10 +273,7 @@ export class TsSelectedFile {
*/
private isSame(number1: number, number2: number): boolean {
const minimumAmountToConsiderMatch = .001;
- if (Math.abs((number1 - number2) / number1) < minimumAmountToConsiderMatch) {
- return true;
- }
- return false;
+ return Math.abs((number1 - number2) / number1) < minimumAmountToConsiderMatch;
}
}