* fix: eslint operator-linebreak issue * fix: eslint quotes issue * fix: react jsx indent and props issues * fix: eslint trailing spaces issues * fix: eslint line around directives issue * fix: eslint semi rule * fix: eslint newline per chain rule * fix: eslint space infix ops rule * fix: eslint space-in-parens issue * fix: eslint space before function paren issue * fix: eslint space before blocks issue * fix: eslint arrow body style issue * fix: eslint dot-location issue * fix: eslint quotes issue * fix: eslint quote props issue * fix: eslint operator assignment issue * fix: eslint new line after import issue * fix: indent issues * fix: operator assignment issue * fix: all autofixable eslint issues * fix: all react related fixable issues * fix: autofixable eslint issues * chore: remove all template literals * fix: remaining autofixable issues * chore: apply amnesty on all existing issues * fix: failing xss-lint issues * refactor: apply amnesty on remaining issues * refactor: apply amnesty on new issues * fix: remove file level suppressions * refactor: apply amnesty on new issues
178 lines
7.2 KiB
JavaScript
178 lines
7.2 KiB
JavaScript
define(['edx-ui-toolkit/js/utils/html-utils',
|
|
'js/views/baseview',
|
|
'underscore',
|
|
'jquery',
|
|
'gettext',
|
|
'common/js/components/views/feedback_notification',
|
|
'common/js/components/views/feedback_alert',
|
|
'js/views/baseview',
|
|
'jquery.smoothScroll'],
|
|
function(HtmlUtils, BaseView, _, $, gettext, NotificationView, AlertView) {
|
|
var ValidatingView = BaseView.extend({
|
|
// Intended as an abstract class which catches validation errors on the model and
|
|
// decorates the fields. Needs wiring per class, but this initialization shows how
|
|
// either have your init call this one or copy the contents
|
|
initialize: function() {
|
|
this.listenTo(this.model, 'invalid', this.handleValidationError);
|
|
this.selectorToField = _.invert(this.fieldToSelectorMap);
|
|
},
|
|
|
|
errorTemplate: HtmlUtils.template('<span class="message-error"><%- message %></span>'),
|
|
|
|
save_title: gettext("You've made some changes"),
|
|
save_message: gettext('Your changes will not take effect until you save your progress.'),
|
|
error_title: gettext("You've made some changes, but there are some errors"),
|
|
error_message: gettext('Please address the errors on this page first, and then save your progress.'),
|
|
|
|
events: {
|
|
'change input': 'clearValidationErrors',
|
|
'change textarea': 'clearValidationErrors'
|
|
},
|
|
fieldToSelectorMap: {
|
|
// Your subclass must populate this w/ all of the model keys and dom selectors
|
|
// which may be the subjects of validation errors
|
|
},
|
|
_cacheValidationErrors: [],
|
|
|
|
handleValidationError: function(model, error) {
|
|
this.clearValidationErrors();
|
|
// error is object w/ fields and error strings
|
|
// eslint-disable-next-line guard-for-in
|
|
for (var field in error) {
|
|
var ele = this.$el.find('#' + this.fieldToSelectorMap[field]);
|
|
this._cacheValidationErrors.push(ele);
|
|
this.getInputElements(ele).addClass('error');
|
|
HtmlUtils.append($(ele).parent(), this.errorTemplate({message: error[field]}));
|
|
}
|
|
$('.wrapper-notification-warning').addClass('wrapper-notification-warning-w-errors');
|
|
$('.action-save').addClass('is-disabled');
|
|
// TODO: (pfogg) should this text fade in/out on change?
|
|
$('#notification-warning-title').text(this.error_title);
|
|
$('#notification-warning-description').text(this.error_message);
|
|
},
|
|
|
|
clearValidationErrors: function() {
|
|
// error is object w/ fields and error strings
|
|
while (this._cacheValidationErrors.length > 0) {
|
|
var ele = this._cacheValidationErrors.pop();
|
|
this.getInputElements(ele).removeClass('error');
|
|
$(ele).nextAll('.message-error').remove();
|
|
}
|
|
$('.wrapper-notification-warning').removeClass('wrapper-notification-warning-w-errors');
|
|
$('.action-save').removeClass('is-disabled');
|
|
$('#notification-warning-title').text(this.save_title);
|
|
$('#notification-warning-description').text(this.save_message);
|
|
},
|
|
|
|
setField: function(event) {
|
|
// Set model field and return the new value.
|
|
this.clearValidationErrors();
|
|
var field = this.selectorToField[event.currentTarget.id];
|
|
var newVal = '';
|
|
if (event.currentTarget.type == 'checkbox') {
|
|
newVal = $(event.currentTarget).is(':checked').toString();
|
|
} else {
|
|
newVal = $(event.currentTarget).val();
|
|
}
|
|
this.model.set(field, newVal);
|
|
this.model.isValid();
|
|
return newVal;
|
|
},
|
|
// these should perhaps go into a superclass but lack of event hash inheritance demotivates me
|
|
inputFocus: function(event) {
|
|
$("label[for='" + event.currentTarget.id + "']").addClass('is-focused');
|
|
},
|
|
inputUnfocus: function(event) {
|
|
$("label[for='" + event.currentTarget.id + "']").removeClass('is-focused');
|
|
},
|
|
|
|
getInputElements: function(ele) {
|
|
var inputElements = 'input, textarea';
|
|
if ($(ele).is(inputElements)) {
|
|
return $(ele);
|
|
} else {
|
|
// put error on the contained inputs
|
|
return $(ele).find(inputElements);
|
|
}
|
|
},
|
|
|
|
showNotificationBar: function(message, primaryClick, secondaryClick) {
|
|
// Show a notification with message. primaryClick is called on
|
|
// pressing the save button, and secondaryClick (if it's
|
|
// passed, which it may not be) will be called on
|
|
// cancel. Takes care of hiding the notification bar at the
|
|
// appropriate times.
|
|
if (this.notificationBarShowing) {
|
|
return;
|
|
}
|
|
// If we've already saved something, hide the alert.
|
|
if (this.saved) {
|
|
this.saved.hide();
|
|
}
|
|
var self = this;
|
|
this.confirmation = new NotificationView.Warning({
|
|
title: this.save_title,
|
|
message: message,
|
|
actions: {
|
|
primary: {
|
|
text: gettext('Save Changes'),
|
|
class: 'action-save',
|
|
click: function() {
|
|
primaryClick();
|
|
self.confirmation.hide();
|
|
self.notificationBarShowing = false;
|
|
}
|
|
},
|
|
secondary: [{
|
|
text: gettext('Cancel'),
|
|
class: 'action-cancel',
|
|
click: function() {
|
|
if (secondaryClick) {
|
|
secondaryClick();
|
|
}
|
|
self.model.clear({silent: true});
|
|
self.confirmation.hide();
|
|
self.notificationBarShowing = false;
|
|
}
|
|
}]
|
|
}
|
|
});
|
|
this.notificationBarShowing = true;
|
|
this.confirmation.show();
|
|
// Make sure the bar is in the right state
|
|
this.model.isValid();
|
|
},
|
|
|
|
showSavedBar: function(title, message) {
|
|
var defaultTitle = gettext('Your changes have been saved.');
|
|
this.saved = new AlertView.Confirmation({
|
|
title: title || defaultTitle,
|
|
message: message,
|
|
closeIcon: false
|
|
});
|
|
this.saved.show();
|
|
$.smoothScroll({
|
|
offset: 0,
|
|
easing: 'swing',
|
|
speed: 1000
|
|
});
|
|
},
|
|
|
|
saveView: function() {
|
|
var self = this;
|
|
this.model.save(
|
|
{},
|
|
{
|
|
success: function() {
|
|
self.showSavedBar();
|
|
self.render();
|
|
},
|
|
silent: true
|
|
}
|
|
);
|
|
}
|
|
});
|
|
|
|
return ValidatingView;
|
|
}); // end define()
|