Make uploads via upload dialog work
closes CNVS-28647 Test Plan: - Enable course images feature flag - Go to the course settings - Open the change image modal - Click/Use keyboard to activate the "browse your computer" link - A file upload dialog should appear. - Uploading a file should close the modal and update the image. - Save the course settings and the image should persist Change-Id: I3bd701ad282ef7c891201fe0ddee8037591a85f0 Reviewed-on: https://gerrit.instructure.com/77196 Reviewed-by: Matt Zabriskie <mzabriskie@instructure.com> Tested-by: Jenkins QA-Review: Pierce Arner <pierce@instructure.com> Product-Review: Colleen Palmer <colleen@instructure.com>
This commit is contained in:
parent
540ec880ac
commit
0668a4ea52
|
@ -82,8 +82,8 @@ define ([
|
|||
uploadFile (event, courseId, ajaxLib = axios) {
|
||||
event.preventDefault();
|
||||
return (dispatch, getState) => {
|
||||
const type = event.dataTransfer.files[0].type;
|
||||
const file = event.dataTransfer.files[0];
|
||||
const { type, file } = Helpers.extractInfoFromEvent(event);
|
||||
|
||||
if (Helpers.isValidImageType(type)) {
|
||||
const data = {
|
||||
name: file.name,
|
||||
|
@ -111,8 +111,8 @@ define ([
|
|||
$.flashWarning(I18n.t("'%{type}' is not a valid image type (try jpg, png, or gif)", {type}));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
return Actions;
|
||||
|
|
|
@ -81,7 +81,9 @@ define([
|
|||
</div>
|
||||
</div>
|
||||
<div className="CourseImagePicker__Content">
|
||||
<UploadArea />
|
||||
<UploadArea
|
||||
courseId={this.props.courseId}
|
||||
handleFileUpload={this.props.handleFileUpload}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -6,6 +6,21 @@ define([
|
|||
'./CourseImagePicker'
|
||||
], (React, Modal, I18n, Actions, CourseImagePicker) => {
|
||||
|
||||
const modalOverrides = {
|
||||
content : {
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
left: '0',
|
||||
right: '0',
|
||||
bottom: '0',
|
||||
border: 'none',
|
||||
padding: '12px',
|
||||
maxWidth: '1420px',
|
||||
borderRadius: '0',
|
||||
background: '#ffffff'
|
||||
}
|
||||
};
|
||||
|
||||
class CourseImageSelector extends React.Component {
|
||||
|
||||
constructor (props) {
|
||||
|
@ -56,9 +71,9 @@ define([
|
|||
</button>
|
||||
</div>
|
||||
<Modal
|
||||
className="CourseImageSelector__Modal"
|
||||
isOpen={this.state.showModal}
|
||||
onRequestClose={this.handleModalClose}
|
||||
style={modalOverrides}
|
||||
>
|
||||
<CourseImagePicker
|
||||
courseId={this.props.courseId}
|
||||
|
|
|
@ -1,11 +1,30 @@
|
|||
define([
|
||||
'react',
|
||||
'i18n!course_images',
|
||||
'classnames'
|
||||
], (React, I18n, classnames) => {
|
||||
'classnames',
|
||||
'str/htmlEscape'
|
||||
], (React, I18n, classnames, htmlEscape) => {
|
||||
|
||||
class UploadArea extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
|
||||
this.handleFileChange = this.handleFileChange.bind(this);
|
||||
this.uploadFile = this.uploadFile.bind(this);
|
||||
}
|
||||
|
||||
handleFileChange (e) {
|
||||
this.props.handleFileUpload(e, this.props.courseId);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
uploadFile () {
|
||||
this.refs.courseImagefileUpload.click();
|
||||
}
|
||||
|
||||
render () {
|
||||
|
||||
return (
|
||||
<div className="UploadArea">
|
||||
<div className="UploadArea__Content">
|
||||
|
@ -13,10 +32,25 @@ define([
|
|||
<i className="icon-upload" />
|
||||
</div>
|
||||
<div className="UploadArea__Instructions">
|
||||
<strong>{I18n.t('Drag and drop your image here or browse your computer.')}</strong>
|
||||
<div className="UploadArea__FileTypes">
|
||||
{I18n.t('jpg, png, or gif files')}
|
||||
</div>
|
||||
<strong>
|
||||
{I18n.t('Drag and drop your image here or ')}
|
||||
<a tabIndex="0" role="button" href="#" onClick={this.uploadFile}>
|
||||
<span className="screenreader-only">{I18n.t('Browse your computer for a course image')}</span>
|
||||
<span aria-hidden="true">{I18n.t('browse your computer')}</span>
|
||||
</a>
|
||||
</strong>
|
||||
<input
|
||||
type="file"
|
||||
ref="courseImagefileUpload"
|
||||
name="fileUpload"
|
||||
className="FileUpload__Input"
|
||||
accept=".jpg, .jpeg, .gif, .png, image/png, image/jpeg, image/gif"
|
||||
aria-hidden="true"
|
||||
onChange={this.handleFileChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="UploadArea__FileTypes">
|
||||
{I18n.t('jpg, png, or gif files')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -20,6 +20,20 @@ define ([], () => {
|
|||
formData.append(key, uploadParams[key]);
|
||||
});
|
||||
return formData;
|
||||
},
|
||||
|
||||
extractInfoFromEvent (event) {
|
||||
let file = '';
|
||||
let type = '';
|
||||
if (event.type === 'change') {
|
||||
file = event.target.files[0];
|
||||
type = file.type;
|
||||
} else {
|
||||
type = event.dataTransfer.files[0].type;
|
||||
file = event.dataTransfer.files[0];
|
||||
}
|
||||
|
||||
return {file, type};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -11,14 +11,8 @@
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
.CourseImageSelector__Modal {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: $ic-color-light;
|
||||
max-width: $max_main_width + $right_side_width;
|
||||
.CourseImagePicker {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.CourseImagePicker__Header {
|
||||
|
@ -98,4 +92,8 @@
|
|||
font-style: italic;
|
||||
margin-top: $ic-sp;
|
||||
font-size: $ic-font-size--xsmall;
|
||||
}
|
||||
|
||||
.FileUpload__Input {
|
||||
display: none;
|
||||
}
|
|
@ -121,7 +121,6 @@ define([
|
|||
type: 'image/tiff'
|
||||
}]
|
||||
},
|
||||
//placeholder method to avoid failure when calling uploadFile
|
||||
preventDefault: () => {}
|
||||
};
|
||||
|
||||
|
@ -175,7 +174,6 @@ define([
|
|||
type: 'image/jpeg'
|
||||
}]
|
||||
},
|
||||
//placeholder method to avoid failure when calling uploadFile
|
||||
preventDefault: () => {}
|
||||
};
|
||||
|
||||
|
|
|
@ -15,4 +15,19 @@ define([
|
|||
ok(component);
|
||||
});
|
||||
|
||||
test('calls the handleFileUpload prop when change occurs on the file input', () => {
|
||||
let called = false;
|
||||
const handleFileUploadFunc = () => called = true;
|
||||
const component = TestUtils.renderIntoDocument(
|
||||
<UploadArea
|
||||
courseId="101"
|
||||
handleFileUpload={handleFileUploadFunc}
|
||||
/>
|
||||
);
|
||||
|
||||
const input = TestUtils.findRenderedDOMComponentWithClass(component, 'FileUpload__Input');
|
||||
TestUtils.Simulate.change(input);
|
||||
ok(called, 'handleFileUpload was called');
|
||||
});
|
||||
|
||||
});
|
|
@ -22,4 +22,43 @@ define([
|
|||
ok(formData instanceof FormData, 'created a FormData object');
|
||||
});
|
||||
|
||||
test('extractInfoFromEvent', () => {
|
||||
const changeEvent = {
|
||||
type: 'change',
|
||||
target: {
|
||||
files: [{type: 'image/jpeg'}]
|
||||
}
|
||||
};
|
||||
|
||||
const dragEvent = {
|
||||
type: 'drop',
|
||||
dataTransfer: {
|
||||
files: [{
|
||||
name: 'test',
|
||||
type: 'image/jpeg'
|
||||
}]
|
||||
},
|
||||
};
|
||||
|
||||
const changeResults = Helpers.extractInfoFromEvent(changeEvent);
|
||||
const expectedChangeResults = {
|
||||
file: {
|
||||
type: 'image/jpeg'
|
||||
},
|
||||
type: 'image/jpeg'
|
||||
};
|
||||
|
||||
const dragResults = Helpers.extractInfoFromEvent(dragEvent);
|
||||
const expectedDragResults = {
|
||||
file: {
|
||||
name: 'test',
|
||||
type: 'image/jpeg'
|
||||
},
|
||||
type: 'image/jpeg'
|
||||
};
|
||||
|
||||
deepEqual(changeResults, expectedChangeResults, 'creates the proper info from change events');
|
||||
deepEqual(dragResults, expectedDragResults, 'creates the proper info from drag events');
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue