Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.

Commit 08616c9

Browse files
committed
Merge pull request #710 from appirio-tech/windows-submission-bug-fix
Fix windows and IE bugs
2 parents 0c8776b + de82801 commit 08616c9

File tree

4 files changed

+277
-228
lines changed

4 files changed

+277
-228
lines changed

app/directives/tc-file-input/tc-file-input.directive.js

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import _ from 'lodash'
44
(function() {
55
'use strict'
66

7-
angular.module('tcUIComponents').directive('tcFileInput', tcFileInput)
7+
angular.module('tcUIComponents').directive('tcFileInput', ['$timeout', tcFileInput])
88

9-
function tcFileInput() {
9+
function tcFileInput($timeout) {
1010
return {
1111
restrict: 'E',
1212
require: '^form',
@@ -26,6 +26,14 @@ import _ from 'lodash'
2626
scope.selectFile = selectFile
2727
var fileTypes = scope.fileType.split(',')
2828

29+
// Add extra checks for Windows zip file types
30+
var hasZip = _.some(fileTypes, _.matches('zip'))
31+
32+
if (hasZip) {
33+
fileTypes = angular.copy(fileTypes)
34+
fileTypes.push('x-zip', 'x-zip-compressed')
35+
}
36+
2937
// fieldId is not set on element at this point, so grabbing with class .none
3038
// which exists on the element right away
3139
var fileInput = $(element[0]).find('.none')
@@ -46,33 +54,36 @@ import _ from 'lodash'
4654
var selectedFileType = file.type.slice(file.type.lastIndexOf('/') + 1)
4755
var isAllowedFileFormat = _.some(fileTypes, _.matches(selectedFileType))
4856

49-
scope.$apply(function(){
50-
// Set the file name as the value of the disabled input
51-
fileNameInput[0].value = file.name
52-
var clickedFileInput = formController[attrs.fieldId]
53-
54-
if (!isAllowedFileFormat) {
55-
// Manually setting is required since Angular doesn't support file inputs
56-
clickedFileInput.$setTouched()
57-
clickedFileInput.$setValidity('required', false)
58-
59-
} else {
60-
clickedFileInput.$setValidity('required', true)
61-
}
62-
63-
if (!isAllowedFileSize) {
64-
// Manually setting is required since Angular doesn't support file inputs
65-
clickedFileInput.$setTouched()
66-
clickedFileInput.$setValidity('filesize', false)
67-
68-
} else {
69-
clickedFileInput.$setValidity('filesize', true)
70-
}
71-
72-
if (isAllowedFileFormat && isAllowedFileSize) {
73-
// Pass file object up through callback into controller
74-
scope.setFileReference({file: file, fieldId: scope.fieldId})
75-
}
57+
// Timeout needed for fixing IE bug ($apply already in progress)
58+
$timeout(function() {
59+
scope.$apply(function(){
60+
// Set the file name as the value of the disabled input
61+
fileNameInput[0].value = file.name
62+
var clickedFileInput = formController[attrs.fieldId]
63+
64+
if (!isAllowedFileFormat) {
65+
// Manually setting is required since Angular doesn't support file inputs
66+
clickedFileInput.$setTouched()
67+
clickedFileInput.$setValidity('required', false)
68+
69+
} else {
70+
clickedFileInput.$setValidity('required', true)
71+
}
72+
73+
if (!isAllowedFileSize) {
74+
// Manually setting is required since Angular doesn't support file inputs
75+
clickedFileInput.$setTouched()
76+
clickedFileInput.$setValidity('filesize', false)
77+
78+
} else {
79+
clickedFileInput.$setValidity('filesize', true)
80+
}
81+
82+
if (isAllowedFileFormat && isAllowedFileSize) {
83+
// Pass file object up through callback into controller
84+
scope.setFileReference({file: file, fieldId: scope.fieldId})
85+
}
86+
})
7687
})
7788
})
7889

Lines changed: 113 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,148 +1,186 @@
1+
import angular from 'angular'
2+
13
/* jshint -W117, -W030 */
24
describe('Topcoder File Input Directive', function() {
3-
var scope, element, isolateScope, fileInput;
5+
var scope, element, isolateScope, fileInput
46

57
beforeEach(function() {
6-
bard.appModule('topcoder');
7-
bard.inject(this, '$compile', '$rootScope');
8-
scope = $rootScope.$new();
8+
bard.appModule('topcoder')
9+
bard.inject(this, '$compile', '$rootScope', '$timeout')
10+
scope = $rootScope.$new()
911

1012
var html = '' +
1113
'<form>' +
1214
'<tc-file-input ' +
13-
'label-text="Preview Image"' +
14-
'field-id="DESIGN_COVER"' +
15+
'label-text="Submission"' +
16+
'field-id="SUBMISSION_ZIP"' +
1517
'button-text="Add File"' +
16-
'file-type="jpg,jpeg,png"' +
17-
'placeholder="Image file as .jpg or .png"' +
18+
'file-type="zip"' +
19+
'placeholder="Attach all visible files as a single .zip file"' +
1820
'mandatory="true"' +
1921
'set-file-reference="vm.setFileReference(file, fieldId)"' +
2022
'ng-model="vm.submissionForm.submissionZip"' +
2123
' />' +
22-
'</form>';
23-
var form = angular.element(html);
24-
element = form.find('tc-file-input');
25-
var formElement = $compile(form)(scope);
26-
scope.$digest();
24+
'</form>'
25+
var form = angular.element(html)
26+
element = form.find('tc-file-input')
27+
var formElement = $compile(form)(scope)
28+
scope.$digest()
2729

28-
isolateScope = element.isolateScope();
29-
});
30+
isolateScope = element.isolateScope()
31+
})
3032

3133
beforeEach(function() {
32-
fileInput = $(element).find('.none')[0];
33-
});
34+
fileInput = $(element).find('.none')[0]
35+
})
3436

3537
afterEach(function() {
36-
scope.$destroy();
37-
fileInput = undefined;
38-
});
38+
scope.$destroy()
39+
fileInput = undefined
40+
})
3941

40-
bard.verifyNoOutstandingHttpRequests();
42+
bard.verifyNoOutstandingHttpRequests()
4143

4244
describe('selectFile', function() {
4345
it('triggers a click on the file input', function() {
44-
var mockClick = sinon.spy(fileInput, 'click');
46+
var mockClick = sinon.spy(fileInput, 'click')
4547

46-
isolateScope.selectFile();
47-
scope.$digest();
48+
isolateScope.selectFile()
49+
scope.$digest()
4850

49-
expect(mockClick).calledOnce;
50-
});
51-
});
51+
expect(mockClick).calledOnce
52+
})
53+
})
5254

5355
describe('a change event on the file input', function() {
54-
var fileNameInput, fileList, mockSetFileReference;
56+
var fileNameInput, fileList, mockSetFileReference
5557

5658
beforeEach(function() {
57-
fileNameInput = $(element).find('input[type=text]')[0];
59+
fileNameInput = $(element).find('input[type=text]')[0]
5860
fileList = {
5961
0: {
60-
name: 'test.png',
62+
name: 'test.zip',
6163
size: 50,
62-
type: 'image/png'
64+
type: 'application/zip'
6365
},
6466
length: 1,
65-
item: function (index) { return file; }
66-
};
67+
item: function (index) { return file }
68+
}
6769

68-
mockSetFileReference = sinon.spy(isolateScope, 'setFileReference');
69-
});
70+
mockSetFileReference = sinon.spy(isolateScope, 'setFileReference')
71+
})
7072

7173
afterEach(function() {
72-
fileNameInput = undefined;
73-
fileList = undefined;
74-
mockSetFileReference = undefined;
75-
});
74+
fileNameInput = undefined
75+
fileList = undefined
76+
mockSetFileReference = undefined
77+
})
7678

7779
it('sets the value of the fileNameInput with the name of the file', function() {
7880
$(fileInput).triggerHandler({
7981
type: 'change',
8082
target: { files: fileList }
81-
});
83+
})
84+
85+
$timeout.flush()
8286

83-
expect(fileNameInput.value).to.equal('test.png');
84-
});
87+
expect(fileNameInput.value).to.equal('test.zip')
88+
})
8589

8690
describe('with a valid file', function() {
8791
beforeEach(function() {
8892
$(fileInput).triggerHandler({
8993
type: 'change',
9094
target: { files: fileList }
91-
});
92-
});
95+
})
96+
$timeout.flush()
97+
})
9398

9499
it('calls setFileReference', function() {
95-
expect(mockSetFileReference).calledOnce;
96-
});
100+
expect(mockSetFileReference).calledOnce
101+
})
97102

98103
it('has ng-valid-filesize class', function() {
99-
expect($(fileInput).hasClass('ng-valid-filesize')).to.be.true;
100-
});
104+
expect($(fileInput).hasClass('ng-valid-filesize')).to.be.true
105+
})
101106

102107
it('has ng-valid-required class', function() {
103-
expect($(fileInput).hasClass('ng-valid-required')).to.be.true;
104-
});
105-
});
108+
expect($(fileInput).hasClass('ng-valid-required')).to.be.true
109+
})
106110

107-
describe('with a file that\'s greater than 500MB', function() {
108-
beforeEach(function() {
109-
fileList[0].size = 500000001;
111+
it('works with Windows file type application/x-zip', function(){
112+
fileList[0].type = 'application/x-zip'
110113

111114
$(fileInput).triggerHandler({
112115
type: 'change',
113116
target: { files: fileList }
114-
});
115-
});
117+
})
116118

117-
it('does not call setFileReference', function() {
118-
expect(mockSetFileReference).not.calledOnce;
119-
});
119+
$timeout.flush()
120120

121-
it('has ng-touched and ng-invalid-filesize classes', function() {
122-
expect($(fileInput).hasClass('ng-invalid-filesize')).to.be.true;
123-
expect($(fileInput).hasClass('ng-touched')).to.be.true;
124-
});
125-
});
121+
expect(mockSetFileReference).called
122+
expect($(fileInput).hasClass('ng-valid-filesize')).to.be.true
123+
expect($(fileInput).hasClass('ng-valid-required')).to.be.true
124+
})
125+
126+
it('works with Windows file type application/x-zip-compressed', function(){
127+
fileList[0].type = 'application/x-zip-compressed'
128+
129+
$(fileInput).triggerHandler({
130+
type: 'change',
131+
target: { files: fileList }
132+
})
133+
134+
$timeout.flush()
135+
136+
expect(mockSetFileReference).called
137+
expect($(fileInput).hasClass('ng-valid-filesize')).to.be.true
138+
expect($(fileInput).hasClass('ng-valid-required')).to.be.true
139+
})
140+
})
126141

127142
describe('with a file type that\'s not in the list of fileTypes given to the directive', function() {
128143
beforeEach(function() {
129-
fileList[0].type = 'application/zip';
144+
fileList[0].type = 'image/png'
130145

131146
$(fileInput).triggerHandler({
132147
type: 'change',
133148
target: { files: fileList }
134-
});
135-
});
149+
})
150+
151+
$timeout.flush()
152+
})
136153

137154
it('does not call setFileReference', function() {
138-
expect(mockSetFileReference).not.calledOnce;
139-
});
155+
expect(mockSetFileReference).not.calledOnce
156+
})
140157

141158
it('has ng-touched and ng-invalid-required classes', function() {
142-
expect($(fileInput).hasClass('ng-invalid-required')).to.be.true;
143-
expect($(fileInput).hasClass('ng-touched')).to.be.true;
144-
});
145-
});
159+
expect($(fileInput).hasClass('ng-invalid-required')).to.be.true
160+
expect($(fileInput).hasClass('ng-touched')).to.be.true
161+
})
162+
})
163+
164+
describe('with a file that\'s greater than 500MB', function() {
165+
beforeEach(function() {
166+
fileList[0].size = 500000001
167+
168+
$(fileInput).triggerHandler({
169+
type: 'change',
170+
target: { files: fileList }
171+
})
172+
173+
$timeout.flush()
174+
})
146175

147-
});
148-
});
176+
it('does not call setFileReference', function() {
177+
expect(mockSetFileReference).not.calledOnce
178+
})
179+
180+
it('has ng-touched and ng-invalid-filesize classes', function() {
181+
expect($(fileInput).hasClass('ng-invalid-filesize')).to.be.true
182+
expect($(fileInput).hasClass('ng-touched')).to.be.true
183+
})
184+
})
185+
})
186+
})

0 commit comments

Comments
 (0)