Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/features/courses.feature
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ Feature: Create Course
And I am logged into Studio
When I click the New Course button
And I fill in the new course information
And I press the "Save" button
And I press the "Create" button
Then the Courseware page has loaded in Studio
And I see a link for adding a new section
167 changes: 107 additions & 60 deletions cms/static/js/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -605,80 +605,117 @@ function cancelNewSection(e) {
function addNewCourse(e) {
e.preventDefault();
$('.new-course-button').addClass('is-disabled');
$('.new-course-save').addClass('is-disabled');
var $newCourse = $('.wrapper-create-course').addClass('is-shown');
var $cancelButton = $newCourse.find('.new-course-cancel');
$newCourse.find('.new-course-name').focus().select();
$newCourse.find('form').bind('submit', saveNewCourse);
var $courseName = $('.new-course-name');
$courseName.focus().select();
$('.new-course-save').on('click', saveNewCourse);
$cancelButton.bind('click', cancelNewCourse);
$body.bind('keyup', {
$cancelButton: $cancelButton
}, checkForCancel);
}

function saveNewCourse(e) {
e.preventDefault();

var $newCourseForm = $(this).closest('#create-course-form');
var display_name = $newCourseForm.find('.new-course-name').val();
var org = $newCourseForm.find('.new-course-org').val();
var number = $newCourseForm.find('.new-course-number').val();
var run = $newCourseForm.find('.new-course-run').val();

var required_field_text = gettext('Required field');

var display_name_errMsg = (display_name === '') ? required_field_text : null;
var org_errMsg = (org === '') ? required_field_text : null;
var number_errMsg = (number === '') ? required_field_text : null;
var run_errMsg = (run === '') ? required_field_text : null;

var bInErr = (display_name_errMsg || org_errMsg || number_errMsg || run_errMsg);

// check for suitable encoding
if (!bInErr) {
var encoding_errMsg = gettext('Please do not use any spaces or special characters in this field.');

if (encodeURIComponent(org) != org)
org_errMsg = encoding_errMsg;
if (encodeURIComponent(number) != number)
number_errMsg = encoding_errMsg;
if (encodeURIComponent(run) != run)
run_errMsg = encoding_errMsg;

bInErr = (org_errMsg || number_errMsg || run_errMsg);
// Check that a course (org, number, run) doesn't use any special characters
var validateCourseItemEncoding = function(item) {
var required = validateRequiredField(item);
if(required) {
return required;
}
if(item !== encodeURIComponent(item)) {
return gettext('Please do not use any spaces or special characters in this field.');
}
return '';
}

var header_err_msg = (bInErr) ? gettext('Please correct the fields below.') : null;

var setNewCourseErrMsgs = function(header_err_msg, display_name_errMsg, org_errMsg, number_errMsg, run_errMsg) {
if (header_err_msg) {
$('.wrapper-create-course').addClass('has-errors');
// Ensure that all items are less than 80 characters.
var validateTotalCourseItemsLength = function() {
var totalLength = _.reduce(
['.new-course-name', '.new-course-org', '.new-course-number', '.new-course-run'],
function(sum, ele) {
return sum + $(ele).val().length;
}, 0
);
if(totalLength > 80) {
$('.wrap-error').addClass('is-shown');
$('#course_creation_error').html('<p>' + header_err_msg + '</p>');
} else {
$('#course_creation_error').html('<p>' + gettext('Course fields must have a combined length of no more than 80 characters.') + '</p>');
$('.new-course-save').addClass('is-disabled');
}
else {
$('.wrap-error').removeClass('is-shown');
$('#course_creation_error').html('');
}
}

var setNewCourseFieldInErr = function(el, msg) {
el.children('.tip-error').remove();
if (msg !== null && msg !== '') {
el.addClass('error');
el.append('<span class="tip tip-error">' + msg + '</span>');
} else {
el.removeClass('error');
}
};
// Handle validation asynchronously
_.each(
['.new-course-org', '.new-course-number', '.new-course-run'],
function(ele) {
var $ele = $(ele);
$ele.on('keyup', function(event) {
// Don't bother showing "required field" error when
// the user tabs into a new field; this is distracting
// and unnecessary
if(event.keyCode === 9) {
return;
}
var error = validateCourseItemEncoding($ele.val());
setNewCourseFieldInErr($ele.parent('li'), error);
validateTotalCourseItemsLength();
});
}
);
var $name = $('.new-course-name');
$name.on('keyup', function() {
var error = validateRequiredField($name.val());
setNewCourseFieldInErr($name.parent('li'), error);
validateTotalCourseItemsLength();
});
}

function validateRequiredField(msg) {
return msg.length === 0 ? gettext('Required field.') : '';
}

function setNewCourseFieldInErr(el, msg) {
if(msg) {
el.addClass('error');
el.children('span.tip-error').addClass('is-showing').removeClass('is-hiding').text(msg);
$('.new-course-save').addClass('is-disabled');
}
else {
el.removeClass('error');
el.children('span.tip-error').addClass('is-hiding').removeClass('is-showing');
// One "error" div is always present, but hidden or shown
if($('.error').length === 1) {
$('.new-course-save').removeClass('is-disabled');
}
}
};

setNewCourseFieldInErr($('#field-course-name'), display_name_errMsg);
setNewCourseFieldInErr($('#field-organization'), org_errMsg);
setNewCourseFieldInErr($('#field-course-number'), number_errMsg);
setNewCourseFieldInErr($('#field-course-run'), run_errMsg);
};
function saveNewCourse(e) {
e.preventDefault();

setNewCourseErrMsgs(header_err_msg, display_name_errMsg, org_errMsg, number_errMsg, run_errMsg);
// One final check for empty values
var errors = _.reduce(
['.new-course-name', '.new-course-org', '.new-course-number', '.new-course-run'],
function(acc, ele) {
var $ele = $(ele);
var error = validateRequiredField($ele.val());
setNewCourseFieldInErr($ele.parent('li'), error);
return error ? true : acc;
},
false
);

if (bInErr)
if(errors) {
return;
}

var $newCourseForm = $(this).closest('#create-course-form');
var display_name = $newCourseForm.find('.new-course-name').val();
var org = $newCourseForm.find('.new-course-org').val();
var number = $newCourseForm.find('.new-course-number').val();
var run = $newCourseForm.find('.new-course-run').val();

analytics.track('Created a Course', {
'org': org,
Expand All @@ -697,9 +734,9 @@ function saveNewCourse(e) {
if (data.id !== undefined) {
window.location = '/' + data.id.replace(/.*:\/\//, '');
} else if (data.ErrMsg !== undefined) {
var orgErrMsg = (data.OrgErrMsg !== undefined) ? data.OrgErrMsg : null;
var courseErrMsg = (data.CourseErrMsg !== undefined) ? data.CourseErrMsg : null;
setNewCourseErrMsgs(data.ErrMsg, null, orgErrMsg, courseErrMsg, null);
$('.wrap-error').addClass('is-shown');
$('#course_creation_error').html('<p>' + data.ErrMsg + '</p>');
$('.new-course-save').addClass('is-disabled');
}
}
);
Expand All @@ -709,6 +746,16 @@ function cancelNewCourse(e) {
e.preventDefault();
$('.new-course-button').removeClass('is-disabled');
$('.wrapper-create-course').removeClass('is-shown');
// Clear out existing fields and errors
_.each(
['.new-course-name', '.new-course-org', '.new-course-number', '.new-course-run'],
function(field) {
$(field).val('');
}
);
$('#course_creation_error').html('');
$('.wrap-error').removeClass('is-shown');
$('.new-course-save').off('click');
}

function addNewSubsection(e) {
Expand Down
9 changes: 8 additions & 1 deletion cms/static/sass/elements/_forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -225,8 +225,15 @@ form[class^="create-"] {
color: $red;
}

.is-showing {
@extend .anim-fadeIn;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand why you removed this reference class, but is there a way we could keep the animation and de-couple it from the base .tip-error styling? My recommendation would be to have a class of is-showing (with the @extend .anim-fadeIn associated with it) and a class of is-hiding (@extend .anim-fadeOut associated similarly).

Would you mind adding the management of those two classes to your JS logic and trying it out to preserve the animation in the UI?

}

.is-hiding {
@extend .anim-fadeOut;
}

.tip-error {
@extend .anim-fadeIn;
display: block;
color: $red;
}
Expand Down
6 changes: 5 additions & 1 deletion cms/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -99,31 +99,35 @@ <h3 class="title">${_("Create a New Course")}</h3>
<label for="new-course-name">${_("Course Name")}</label>
<input class="new-course-name" id="new-course-name" type="text" name="new-course-name" aria-required="true" placeholder="${_('e.g. Introduction to Computer Science')}" />
<span class="tip tip-stacked">${_("The public display name for your course.")}</span>
<span class="tip tip-error is-hiding"></span>
</li>
<li class="field field-inline text required" id="field-organization">
<label for="new-course-org">${_("Organization")}</label>
<input class="new-course-org" id="new-course-org" type="text" name="new-course-org" aria-required="true" placeholder="${_('e.g. MITX or IMF')}" />
<span class="tip tip-stacked">${_("The name of the organization sponsoring the course")} - <strong>${_("Note: No spaces or special characters are allowed. This cannot be changed.")}</strong></span>
<span class="tip tip-error is-hiding"></span>
</li>

<li class="field field-inline text required" id="field-course-number">
<label for="new-course-number">${_("Course Number")}</label>
<input class="new-course-number" id="new-course-number" type="text" name="new-course-number" aria-required="true" placeholder="${_('e.g. CS101')}" />
<span class="tip tip-stacked">${_("The unique number that identifies your course within your organization")} - <strong>${_("Note: No spaces or special characters are allowed. This cannot be changed.")}</strong></span>
<span class="tip tip-error is-hiding"></span>
</li>

<li class="field field-inline text required" id="field-course-run">
<label for="new-course-run">${_("Course Run")}</label>
<input class="new-course-run" id="new-course-run" type="text" name="new-course-run" aria-required="true"placeholder="${_('e.g. 2013_Spring')}" />
<span class="tip tip-stacked">${_("The term in which your course will run")} - <strong>${_("Note: No spaces or special characters are allowed. This cannot be changed.")}</strong></span>
<span class="tip tip-error is-hiding"></span>
</li>
</ol>

</fieldset>
</div>

<div class="actions">
<input type="submit" value="${_('Save')}" class="action action-primary new-course-save" />
<input type="submit" value="${_('Create')}" class="action action-primary new-course-save" />
<input type="button" value="${_('Cancel')}" class="action action-secondary action-cancel new-course-cancel" />
</div>
</form>
Expand Down