Commit cf5d5666 by Eric Fischer

Merge pull request #9794 from edx/efischer/prevent_doubleclick

TNL 3294 - Prevent double click creating multiple teams
parents 56dae323 02b72abc
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
"common/js/components/views/feedback_prompt"], "common/js/components/views/feedback_prompt"],
function ($, _, gettext, NotificationView, PromptView) { function ($, _, gettext, NotificationView, PromptView) {
var toggleExpandCollapse, showLoadingIndicator, hideLoadingIndicator, confirmThenRunOperation, var toggleExpandCollapse, showLoadingIndicator, hideLoadingIndicator, confirmThenRunOperation,
runOperationShowingMessage, disableElementWhileRunning, getScrollOffset, setScrollOffset, runOperationShowingMessage, withDisabledElement, disableElementWhileRunning,
setScrollTop, redirect, reload, hasChangedAttributes, deleteNotificationHandler, getScrollOffset, setScrollOffset, setScrollTop, redirect, reload, hasChangedAttributes, deleteNotificationHandler,
validateRequiredField, validateURLItemEncoding, validateTotalKeyLength, checkTotalKeyLengthViolations; validateRequiredField, validateURLItemEncoding, validateTotalKeyLength, checkTotalKeyLengthViolations;
// see https://openedx.atlassian.net/browse/TNL-889 for what is it and why it's 65 // see https://openedx.atlassian.net/browse/TNL-889 for what is it and why it's 65
...@@ -88,6 +88,25 @@ ...@@ -88,6 +88,25 @@
}; };
/** /**
* Wraps a Backbone event callback to disable the event's target element.
*
* This paradigm is designed to be used in Backbone event maps where
* multiple events firing simultaneously is not desired.
*
* @param functionName the function to execute, as a string.
* The function must return a jQuery promise and be able to take an event
*/
withDisabledElement = function(functionName) {
return function(event) {
var view = this;
disableElementWhileRunning($(event.currentTarget), function() {
//call view.functionName(event), with view as the current this
return view[functionName].apply(view, [event]);
});
};
};
/**
* Disables a given element when a given operation is running. * Disables a given element when a given operation is running.
* @param {jQuery} element the element to be disabled. * @param {jQuery} element the element to be disabled.
* @param operation the operation during whose duration the * @param operation the operation during whose duration the
...@@ -235,6 +254,7 @@ ...@@ -235,6 +254,7 @@
'hideLoadingIndicator': hideLoadingIndicator, 'hideLoadingIndicator': hideLoadingIndicator,
'confirmThenRunOperation': confirmThenRunOperation, 'confirmThenRunOperation': confirmThenRunOperation,
'runOperationShowingMessage': runOperationShowingMessage, 'runOperationShowingMessage': runOperationShowingMessage,
'withDisabledElement': withDisabledElement,
'disableElementWhileRunning': disableElementWhileRunning, 'disableElementWhileRunning': disableElementWhileRunning,
'deleteNotificationHandler': deleteNotificationHandler, 'deleteNotificationHandler': deleteNotificationHandler,
'setScrollTop': setScrollTop, 'setScrollTop': setScrollTop,
......
...@@ -17,6 +17,29 @@ define(["jquery", "underscore", "common/js/components/utils/view_utils", "common ...@@ -17,6 +17,29 @@ define(["jquery", "underscore", "common/js/components/utils/view_utils", "common
deferred.resolve(); deferred.resolve();
expect(link).not.toHaveClass("is-disabled"); expect(link).not.toHaveClass("is-disabled");
}); });
it("uses withDisabledElement wrapper to disable element while running a Backbone event handler", function() {
var link,
eventCallback,
event,
deferred = new $.Deferred(),
promise = deferred.promise(),
MockView = Backbone.View.extend({
testFunction: function() {
return promise;
}
}),
testView = new MockView();
setFixtures("<a href='#' id='link'>ripe apples drop about my head</a>");
link = $("#link");
expect(link).not.toHaveClass("is-disabled");
eventCallback = ViewUtils.withDisabledElement('testFunction');
event = {currentTarget: link};
eventCallback.apply(testView, [event]);
expect(link).toHaveClass("is-disabled");
deferred.resolve();
expect(link).not.toHaveClass("is-disabled");
});
}); });
describe("progress notification", function() { describe("progress notification", function() {
...@@ -92,4 +115,4 @@ define(["jquery", "underscore", "common/js/components/utils/view_utils", "common ...@@ -92,4 +115,4 @@ define(["jquery", "underscore", "common/js/components/utils/view_utils", "common
}); });
}); });
}); });
}).call(this, define || RequireJS.define); }).call(this, define || RequireJS.define);
\ No newline at end of file
...@@ -6,16 +6,17 @@ ...@@ -6,16 +6,17 @@
'gettext', 'gettext',
'js/views/fields', 'js/views/fields',
'teams/js/models/team', 'teams/js/models/team',
'common/js/components/utils/view_utils',
'text!teams/templates/edit-team.underscore'], 'text!teams/templates/edit-team.underscore'],
function (Backbone, _, gettext, FieldViews, TeamModel, editTeamTemplate) { function (Backbone, _, gettext, FieldViews, TeamModel, ViewUtils, editTeamTemplate) {
return Backbone.View.extend({ return Backbone.View.extend({
maxTeamNameLength: 255, maxTeamNameLength: 255,
maxTeamDescriptionLength: 300, maxTeamDescriptionLength: 300,
events: { events: {
'click .action-primary': 'createOrUpdateTeam', 'click .action-primary': ViewUtils.withDisabledElement('createOrUpdateTeam'),
'submit form': 'createOrUpdateTeam', 'submit form': ViewUtils.withDisabledElement('createOrUpdateTeam'),
'click .action-cancel': 'cancelAndGoBack' 'click .action-cancel': 'cancelAndGoBack'
}, },
...@@ -124,10 +125,9 @@ ...@@ -124,10 +125,9 @@
var validationResult = this.validateTeamData(data); var validationResult = this.validateTeamData(data);
if (validationResult.status === false) { if (validationResult.status === false) {
this.showMessage(validationResult.message, validationResult.srMessage); this.showMessage(validationResult.message, validationResult.srMessage);
return; return $().promise();
} }
return view.teamModel.save(data, saveOptions)
this.teamModel.save(data, saveOptions)
.done(function(result) { .done(function(result) {
view.teamEvents.trigger('teams:update', { view.teamEvents.trigger('teams:update', {
action: view.action, action: view.action,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment