Commit 7c13ba0c by Adam Palay

Disable draft and edit links during ajax calls

STUD-1485
STUD-1499
parent 48e472a0
define ["jquery", "jquery.ui", "gettext", "backbone", define ["jquery", "jquery.ui", "gettext", "backbone",
"js/views/feedback_notification", "js/views/feedback_prompt", "js/views/feedback_notification", "js/views/feedback_prompt",
"coffee/src/views/module_edit", "js/models/module_info"], "coffee/src/views/module_edit", "js/models/module_info",
($, ui, gettext, Backbone, NotificationView, PromptView, ModuleEditView, ModuleModel) -> "js/views/baseview"],
class UnitEditView extends Backbone.View ($, ui, gettext, Backbone, NotificationView, PromptView, ModuleEditView, ModuleModel, BaseView) ->
class UnitEditView extends BaseView
events: events:
'click .new-component .new-component-type a.multiple-templates': 'showComponentTemplates' 'click .new-component .new-component-type a.multiple-templates': 'showComponentTemplates'
'click .new-component .new-component-type a.single-template': 'saveNewComponent' 'click .new-component .new-component-type a.single-template': 'saveNewComponent'
...@@ -212,30 +213,35 @@ define ["jquery", "jquery.ui", "gettext", "backbone", ...@@ -212,30 +213,35 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
) )
createDraft: (event) -> createDraft: (event) ->
@wait(true) self = this
@disableElementWhileRunning($(event.target), ->
$.postJSON(@model.url(), { self.wait(true)
publish: 'create_draft' $.postJSON(self.model.url(), {
}, => publish: 'create_draft'
analytics.track "Created Draft", }, =>
course: course_location_analytics analytics.track "Created Draft",
unit_id: unit_location_analytics course: course_location_analytics
unit_id: unit_location_analytics
@model.set('state', 'draft') self.model.set('state', 'draft')
)
) )
publishDraft: (event) -> publishDraft: (event) ->
@wait(true) self = this
@saveDraft() @disableElementWhileRunning($(event.target), ->
self.wait(true)
$.postJSON(@model.url(), { self.saveDraft()
publish: 'make_public'
}, => $.postJSON(self.model.url(), {
analytics.track "Published Draft", publish: 'make_public'
course: course_location_analytics }, =>
unit_id: unit_location_analytics analytics.track "Published Draft",
course: course_location_analytics
unit_id: unit_location_analytics
@model.set('state', 'public') self.model.set('state', 'public')
)
) )
setVisibility: (event) -> setVisibility: (event) ->
...@@ -259,7 +265,7 @@ define ["jquery", "jquery.ui", "gettext", "backbone", ...@@ -259,7 +265,7 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
@model.set('state', @$('.visibility-select').val()) @model.set('state', @$('.visibility-select').val())
) )
class UnitEditView.NameEdit extends Backbone.View class UnitEditView.NameEdit extends BaseView
events: events:
'change .unit-display-name-input': 'saveName' 'change .unit-display-name-input': 'saveName'
...@@ -293,14 +299,14 @@ define ["jquery", "jquery.ui", "gettext", "backbone", ...@@ -293,14 +299,14 @@ define ["jquery", "jquery.ui", "gettext", "backbone",
display_name: metadata.display_name display_name: metadata.display_name
class UnitEditView.LocationState extends Backbone.View class UnitEditView.LocationState extends BaseView
initialize: => initialize: =>
@model.on('change:state', @render) @model.on('change:state', @render)
render: => render: =>
@$el.toggleClass("#{@model.previous('state')}-item #{@model.get('state')}-item") @$el.toggleClass("#{@model.previous('state')}-item #{@model.get('state')}-item")
class UnitEditView.Visibility extends Backbone.View class UnitEditView.Visibility extends BaseView
initialize: => initialize: =>
@model.on('change:state', @render) @model.on('change:state', @render)
@render() @render()
......
...@@ -76,5 +76,24 @@ define(["jquery", "underscore", "js/views/baseview", "js/utils/handle_iframe_bin ...@@ -76,5 +76,24 @@ define(["jquery", "underscore", "js/views/baseview", "js/utils/handle_iframe_bin
expect(view.$('.is-collapsible')).not.toHaveClass('collapsed'); expect(view.$('.is-collapsible')).not.toHaveClass('collapsed');
}); });
}); });
describe("disabled element while running", function() {
it("adds 'is-disabled' class to element while action is running and removes it after", function() {
var viewWithLink,
link,
deferred = new $.Deferred(),
promise = deferred.promise(),
view = new BaseView();
setFixtures("<a href='#' id='link'>ripe apples drop about my head</a>");
link = $("#link");
expect(link).not.toHaveClass("is-disabled");
view.disableElementWhileRunning(link, function(){return promise});
expect(link).toHaveClass("is-disabled");
deferred.resolve();
expect(link).not.toHaveClass("is-disabled");
});
});
}); });
}); });
...@@ -162,5 +162,79 @@ define(["coffee/src/views/unit", "js/models/module_info", "js/spec_helpers/creat ...@@ -162,5 +162,79 @@ define(["coffee/src/views/unit", "js/models/module_info", "js/spec_helpers/creat
verifyComponents(unit, ['loc_1', 'loc_2']); verifyComponents(unit, ['loc_1', 'loc_2']);
}); });
}); });
describe("Disabled edit/publish links during ajax call", function() {
var unit,
link,
draft_states = [
{
state: "draft",
selector: ".publish-draft"
},
{
state: "public",
selector: ".create-draft"
}
],
editLinkFixture =
'<div class="main-wrapper edit-state-draft" data-locator="unit_locator"> \
<div class="unit-settings window"> \
<h4 class="header">Unit Settings</h4> \
<div class="window-contents"> \
<div class="row published-alert"> \
<p class="edit-draft-message"> \
<a href="#" class="create-draft">edit a draft</a> \
</p> \
<p class="publish-draft-message"> \
<a href="#" class="publish-draft">replace it with this draft</a> \
</p> \
</div> \
</div> \
</div> \
</div>';
function test_link_disabled_during_ajax_call(draft_state) {
beforeEach(function () {
setFixtures(editLinkFixture);
unit = new UnitEditView({
el: $('.main-wrapper'),
model: new ModuleModel({
id: 'unit_locator',
state: draft_state['state']
})
});
// needed to stub out the ajax
window.analytics = jasmine.createSpyObj('analytics', ['track']);
window.course_location_analytics = jasmine.createSpy('course_location_analytics');
window.unit_location_analytics = jasmine.createSpy('unit_location_analytics');
});
it("reenables the " + draft_state['selector'] + " link once the ajax call returns", function() {
runs(function(){
spyOn($, "ajax").andCallThrough();
spyOn($.fn, 'addClass').andCallThrough();
spyOn($.fn, 'removeClass').andCallThrough();
link = $(draft_state['selector']);
link.click();
});
waitsFor(function(){
// wait for "is-disabled" to be removed as a class
return !($(draft_state['selector']).hasClass("is-disabled"));
}, 500);
runs(function(){
// check that the `is-disabled` class was added and removed
expect($.fn.addClass).toHaveBeenCalledWith("is-disabled");
expect($.fn.removeClass).toHaveBeenCalledWith("is-disabled");
// make sure the link finishes without the `is-disabled` class
expect(link).not.toHaveClass("is-disabled");
// affirm that ajax was called
expect($.ajax).toHaveBeenCalled();
});
});
};
for (var i = 0; i < draft_states.length; i++) {
test_link_disabled_during_ajax_call(draft_states[i]);
};
});
} }
); );
define(["jquery", "underscore", "backbone", "js/utils/handle_iframe_binding"], define(["jquery", "underscore", "backbone", "js/utils/handle_iframe_binding"],
function ($, _, Backbone, IframeUtils) { function ($, _, Backbone, IframeUtils) {
/* /*
This view is extended from backbone to provide useful functionality for all Studio views. This view is extended from backbone to provide useful functionality for all Studio views.
This functionality includes: This functionality includes:
- automatic expand and collapse of elements with the 'ui-toggle-expansion' class specified - automatic expand and collapse of elements with the 'ui-toggle-expansion' class specified
- additional control of rendering by overriding 'beforeRender' or 'afterRender' - additional control of rendering by overriding 'beforeRender' or 'afterRender'
Note: the default 'afterRender' function calls a utility function 'iframeBinding' which modifies Note: the default 'afterRender' function calls a utility function 'iframeBinding' which modifies
iframe src urls on a page so that they are rendered as part of the DOM. iframe src urls on a page so that they are rendered as part of the DOM.
*/ */
var BaseView = Backbone.View.extend({ var BaseView = Backbone.View.extend({
...@@ -61,6 +61,20 @@ define(["jquery", "underscore", "backbone", "js/utils/handle_iframe_binding"], ...@@ -61,6 +61,20 @@ define(["jquery", "underscore", "backbone", "js/utils/handle_iframe_binding"],
}, },
/** /**
* Disables a given element when a given operation is running.
* @param {jQuery} element: the element to be disabled.
* @param operation: the operation during whose duration the
* element should be disabled. The operation should return
* a jquery promise.
*/
disableElementWhileRunning: function(element, operation) {
element.addClass("is-disabled");
operation().always(function() {
element.removeClass("is-disabled");
});
},
/**
* Loads the named template from the page, or logs an error if it fails. * Loads the named template from the page, or logs an error if it fails.
* @param name The name of the template. * @param name The name of the template.
* @returns The loaded template. * @returns The loaded template.
......
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