Move and undo move XBlock
- TNL-6062 - TNL-6229
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
'common/js/components/views/feedback_notification', 'coffee/src/ajax_prefix',
|
||||
'jquery.cookie'],
|
||||
function(domReady, $, str, Backbone, gettext, NotificationView) {
|
||||
var main;
|
||||
var main, sendJSON;
|
||||
main = function() {
|
||||
AjaxPrefix.addAjaxPrefix(jQuery, function() {
|
||||
return $("meta[name='path_prefix']").attr('content');
|
||||
@@ -45,20 +45,26 @@
|
||||
});
|
||||
return msg.show();
|
||||
});
|
||||
$.postJSON = function(url, data, callback) {
|
||||
sendJSON = function(url, data, callback, type) { // eslint-disable-line no-param-reassign
|
||||
if ($.isFunction(data)) {
|
||||
callback = data;
|
||||
data = undefined;
|
||||
}
|
||||
return $.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
type: type,
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify(data),
|
||||
success: callback
|
||||
});
|
||||
};
|
||||
$.postJSON = function(url, data, callback) {
|
||||
return sendJSON(url, data, callback, 'POST');
|
||||
};
|
||||
$.patchJSON = function(url, data, callback) {
|
||||
return sendJSON(url, data, callback, 'PATCH');
|
||||
};
|
||||
return domReady(function() {
|
||||
if (window.onTouchBasedDevice()) {
|
||||
return $('body').addClass('touch-based-device');
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
|
||||
'common/js/spec_helpers/template_helpers', 'common/js/spec_helpers/view_helpers',
|
||||
'js/views/modals/move_xblock_modal', 'js/models/xblock_info'],
|
||||
function($, _, AjaxHelpers, TemplateHelpers, ViewHelpers, MoveXBlockModal, XBlockInfo) {
|
||||
'js/views/modals/move_xblock_modal', 'edx-ui-toolkit/js/utils/html-utils',
|
||||
'edx-ui-toolkit/js/utils/string-utils', 'js/models/xblock_info'],
|
||||
function($, _, AjaxHelpers, TemplateHelpers, ViewHelpers, MoveXBlockModal, HtmlUtils, StringUtils, XBlockInfo) {
|
||||
'use strict';
|
||||
|
||||
var modal,
|
||||
showModal,
|
||||
verifyNotificationStatus,
|
||||
selectTargetParent,
|
||||
getConfirmationFeedbackTitle,
|
||||
getUndoConfirmationFeedbackTitle,
|
||||
getConfirmationFeedbackTitleHtml,
|
||||
getConfirmationFeedbackMessageHtml,
|
||||
sourceDisplayName = 'HTML 101',
|
||||
outlineUrl = '/course/cid?formats=concise',
|
||||
sourceLocator = 'source-xblock-locator',
|
||||
targetParentLocator = 'target-parent-xblock-locator',
|
||||
sourceParentLocator = 'source-parent-xblock-locator';
|
||||
|
||||
describe('MoveXBlockModal', function() {
|
||||
var modal,
|
||||
showModal,
|
||||
@@ -42,7 +58,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
showModal();
|
||||
expect(
|
||||
modal.$el.find('.modal-header .title').contents().get(0).nodeValue.trim()
|
||||
).toEqual('Move: ' + DISPLAY_NAME);
|
||||
).toEqual('Move: ' + sourceDisplayName);
|
||||
expect(
|
||||
modal.$el.find('.modal-sr-title').text().trim()
|
||||
).toEqual('Choose a location to move your component to');
|
||||
@@ -56,7 +72,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
expect(modal.$el.find('.ui-loading.is-hidden')).not.toExist();
|
||||
renderViewsSpy = spyOn(modal, 'renderViews');
|
||||
expect(requests.length).toEqual(2);
|
||||
AjaxHelpers.expectRequest(requests, 'GET', OUTLINE_URL);
|
||||
AjaxHelpers.expectRequest(requests, 'GET', outlineUrl);
|
||||
AjaxHelpers.respondWithJson(requests, {});
|
||||
AjaxHelpers.expectRequest(requests, 'GET', ANCESTORS_URL);
|
||||
AjaxHelpers.respondWithJson(requests, {});
|
||||
@@ -72,4 +88,204 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
ViewHelpers.verifyNotificationShowing(notificationSpy, "Studio's having trouble saving your work");
|
||||
});
|
||||
});
|
||||
|
||||
showModal = function() {
|
||||
modal = new MoveXBlockModal({
|
||||
sourceXBlockInfo: new XBlockInfo({
|
||||
id: sourceLocator,
|
||||
display_name: sourceDisplayName,
|
||||
category: 'html'
|
||||
}),
|
||||
sourceParentXBlockInfo: new XBlockInfo({
|
||||
id: sourceParentLocator,
|
||||
display_name: 'VERT 101',
|
||||
category: 'vertical'
|
||||
}),
|
||||
XBlockUrlRoot: '/xblock',
|
||||
outlineURL: outlineUrl
|
||||
});
|
||||
modal.show();
|
||||
};
|
||||
|
||||
selectTargetParent = function(parentLocator) {
|
||||
modal.moveXBlockListView = {
|
||||
parent_info: {
|
||||
parent: {
|
||||
id: parentLocator
|
||||
}
|
||||
},
|
||||
remove: function() {} // attach a fake remove method
|
||||
};
|
||||
};
|
||||
|
||||
getConfirmationFeedbackTitle = function(displayName) {
|
||||
return StringUtils.interpolate(
|
||||
'Success! "{displayName}" has been moved.',
|
||||
{
|
||||
displayName: displayName
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
getUndoConfirmationFeedbackTitle = function(displayName) {
|
||||
return StringUtils.interpolate(
|
||||
'Move cancelled. "{sourceDisplayName}" has been moved back to its original location.',
|
||||
{
|
||||
sourceDisplayName: displayName
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
getConfirmationFeedbackTitleHtml = function(parentLocator) {
|
||||
return StringUtils.interpolate(
|
||||
'{link_start}Take me to the new location{link_end}',
|
||||
{
|
||||
link_start: HtmlUtils.HTML('<a href="/container/' + parentLocator + '">'),
|
||||
link_end: HtmlUtils.HTML('</a>')
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
getConfirmationFeedbackMessageHtml = function(displayName, locator, parentLocator, sourceIndex) {
|
||||
return HtmlUtils.interpolateHtml(
|
||||
HtmlUtils.HTML(
|
||||
'<a class="action-undo-move" href="#" data-source-display-name="{displayName}" ' +
|
||||
'data-source-locator="{sourceLocator}" data-source-parent-locator="{parentSourceLocator}" ' +
|
||||
'data-target-index="{targetIndex}">{undoMove}</a>'),
|
||||
{
|
||||
displayName: displayName,
|
||||
sourceLocator: locator,
|
||||
parentSourceLocator: parentLocator,
|
||||
targetIndex: sourceIndex,
|
||||
undoMove: gettext('Undo move')
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
verifyNotificationStatus = function(requests, notificationSpy, notificationText, sourceIndex) {
|
||||
var sourceIndex = sourceIndex || 0; // eslint-disable-line no-redeclare
|
||||
ViewHelpers.verifyNotificationShowing(notificationSpy, notificationText);
|
||||
AjaxHelpers.respondWithJson(requests, {
|
||||
move_source_locator: sourceLocator,
|
||||
parent_locator: sourceParentLocator,
|
||||
target_index: sourceIndex
|
||||
});
|
||||
ViewHelpers.verifyNotificationHidden(notificationSpy);
|
||||
};
|
||||
|
||||
describe('Move an xblock', function() {
|
||||
var sendMoveXBlockRequest,
|
||||
moveXBlockWithSuccess;
|
||||
|
||||
beforeEach(function() {
|
||||
TemplateHelpers.installTemplates([
|
||||
'basic-modal',
|
||||
'modal-button',
|
||||
'move-xblock-modal'
|
||||
]);
|
||||
showModal();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
modal.hide();
|
||||
});
|
||||
|
||||
sendMoveXBlockRequest = function(requests, xblockLocator, parentLocator, targetIndex, sourceIndex) {
|
||||
var responseData,
|
||||
expectedData,
|
||||
sourceIndex = sourceIndex || 0, // eslint-disable-line no-redeclare
|
||||
moveButton = modal.$el.find('.modal-actions .action-move')[sourceIndex];
|
||||
|
||||
// select a target item and click
|
||||
selectTargetParent(parentLocator);
|
||||
moveButton.click();
|
||||
|
||||
responseData = expectedData = {
|
||||
move_source_locator: xblockLocator,
|
||||
parent_locator: parentLocator
|
||||
};
|
||||
|
||||
if (targetIndex !== undefined) {
|
||||
expectedData = _.extend(expectedData, {
|
||||
targetIndex: targetIndex
|
||||
});
|
||||
}
|
||||
|
||||
// verify content of request
|
||||
AjaxHelpers.expectJsonRequest(requests, 'PATCH', '/xblock/', expectedData);
|
||||
|
||||
// send the response
|
||||
AjaxHelpers.respondWithJson(requests, _.extend(responseData, {
|
||||
source_index: sourceIndex
|
||||
}));
|
||||
};
|
||||
|
||||
moveXBlockWithSuccess = function(requests) {
|
||||
var sourceIndex = 0;
|
||||
sendMoveXBlockRequest(requests, sourceLocator, targetParentLocator);
|
||||
expect(modal.movedAlertView).toBeDefined();
|
||||
expect(modal.movedAlertView.options.title).toEqual(getConfirmationFeedbackTitle(sourceDisplayName));
|
||||
expect(modal.movedAlertView.options.titleHtml).toEqual(
|
||||
getConfirmationFeedbackTitleHtml(targetParentLocator)
|
||||
);
|
||||
expect(modal.movedAlertView.options.messageHtml).toEqual(
|
||||
getConfirmationFeedbackMessageHtml(
|
||||
sourceDisplayName,
|
||||
sourceLocator,
|
||||
sourceParentLocator,
|
||||
sourceIndex
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
it('moves an xblock when move button is clicked', function() {
|
||||
var requests = AjaxHelpers.requests(this);
|
||||
moveXBlockWithSuccess(requests);
|
||||
});
|
||||
|
||||
it('undo move an xblock when undo move button is clicked', function() {
|
||||
var sourceIndex = 0,
|
||||
requests = AjaxHelpers.requests(this);
|
||||
moveXBlockWithSuccess(requests);
|
||||
modal.movedAlertView.undoMoveXBlock({
|
||||
target: $(modal.movedAlertView.options.messageHtml.text)
|
||||
});
|
||||
AjaxHelpers.respondWithJson(requests, {
|
||||
move_source_locator: sourceLocator,
|
||||
parent_locator: sourceParentLocator,
|
||||
target_index: sourceIndex
|
||||
});
|
||||
expect(modal.movedAlertView.movedAlertView.options.title).toEqual(
|
||||
getUndoConfirmationFeedbackTitle(sourceDisplayName)
|
||||
);
|
||||
});
|
||||
|
||||
it('does not move an xblock when cancel button is clicked', function() {
|
||||
var sourceIndex = 0;
|
||||
// select a target parent and click cancel button
|
||||
selectTargetParent(targetParentLocator);
|
||||
modal.$el.find('.modal-actions .action-cancel')[sourceIndex].click();
|
||||
expect(modal.movedAlertView).toBeNull();
|
||||
});
|
||||
|
||||
it('shows a notification when moving', function() {
|
||||
var requests = AjaxHelpers.requests(this),
|
||||
notificationSpy = ViewHelpers.createNotificationSpy();
|
||||
// select a target item and click on move
|
||||
selectTargetParent(targetParentLocator);
|
||||
modal.$el.find('.modal-actions .action-move').click();
|
||||
verifyNotificationStatus(requests, notificationSpy, 'Moving');
|
||||
});
|
||||
|
||||
it('shows a notification when undo moving', function() {
|
||||
var notificationSpy,
|
||||
requests = AjaxHelpers.requests(this);
|
||||
moveXBlockWithSuccess(requests);
|
||||
notificationSpy = ViewHelpers.createNotificationSpy();
|
||||
modal.movedAlertView.undoMoveXBlock({
|
||||
target: $(modal.movedAlertView.options.messageHtml.text)
|
||||
});
|
||||
verifyNotificationStatus(requests, notificationSpy, 'Undo moving');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,14 +6,23 @@ define([
|
||||
'js/views/baseview', 'js/views/modals/base_modal',
|
||||
'js/models/xblock_info', 'js/views/move_xblock_list', 'js/views/move_xblock_breadcrumb',
|
||||
'common/js/components/views/feedback',
|
||||
'js/views/utils/xblock_utils',
|
||||
'js/views/utils/move_xblock_utils',
|
||||
'edx-ui-toolkit/js/utils/html-utils',
|
||||
'edx-ui-toolkit/js/utils/string-utils',
|
||||
'text!templates/move-xblock-modal.underscore'
|
||||
],
|
||||
function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlockListView, MoveXBlockBreadcrumbView,
|
||||
Feedback, StringUtils, MoveXblockModalTemplate) {
|
||||
Feedback, XBlockViewUtils, MoveXBlockUtils, HtmlUtils, StringUtils, MoveXblockModalTemplate) {
|
||||
'use strict';
|
||||
|
||||
var MoveXblockModal = BaseModal.extend({
|
||||
modalSRTitle: gettext('Choose a location to move your component to'),
|
||||
|
||||
events: _.extend({}, BaseModal.prototype.events, {
|
||||
'click .action-move': 'moveXBlock'
|
||||
}),
|
||||
|
||||
options: $.extend({}, BaseModal.prototype.options, {
|
||||
modalName: 'move-xblock',
|
||||
modalSize: 'lg',
|
||||
@@ -30,6 +39,7 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
|
||||
BaseModal.prototype.initialize.call(this);
|
||||
this.listenTo(Backbone, 'move:breadcrumbRendered', this.focusModal);
|
||||
this.sourceXBlockInfo = this.options.sourceXBlockInfo;
|
||||
this.sourceParentXBlockInfo = this.options.sourceParentXBlockInfo;
|
||||
this.XBlockURLRoot = this.options.XBlockURLRoot;
|
||||
this.XBlockAncestorInfoURL = StringUtils.interpolate(
|
||||
'{urlRoot}/{usageId}?fields=ancestorInfo',
|
||||
@@ -42,12 +52,16 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
|
||||
$('.breadcrumb-container').removeClass('is-hidden');
|
||||
self.renderViews(courseOutlineInfo, ancestorInfo);
|
||||
});
|
||||
this.targetParentXBlockInfo = null;
|
||||
this.movedAlertView = null;
|
||||
this.moveXBlockBreadcrumbView = null;
|
||||
this.moveXBlockListView = null;
|
||||
},
|
||||
|
||||
getTitle: function() {
|
||||
return StringUtils.interpolate(
|
||||
gettext('Move: {display_name}'),
|
||||
{display_name: this.sourceXBlockInfo.get('display_name')}
|
||||
gettext('Move: {displayName}'),
|
||||
{displayName: this.sourceXBlockInfo.get('display_name')}
|
||||
);
|
||||
},
|
||||
|
||||
@@ -57,6 +71,7 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
|
||||
|
||||
show: function() {
|
||||
BaseModal.prototype.show.apply(this, [false]);
|
||||
Feedback.prototype.inFocus.apply(this, [this.options.modalWindowClass]);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
@@ -105,6 +120,52 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
|
||||
ancestorInfo: ancestorInfo
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
moveXBlock: function() {
|
||||
var self = this;
|
||||
XBlockViewUtils.moveXBlock(self.sourceXBlockInfo.id, self.moveXBlockListView.parent_info.parent.id)
|
||||
.done(function(response) {
|
||||
if (response.move_source_locator) {
|
||||
// hide modal
|
||||
self.hide();
|
||||
// hide xblock element
|
||||
$("li.studio-xblock-wrapper[data-locator='" + self.sourceXBlockInfo.id + "']").hide();
|
||||
if (self.movedAlertView) {
|
||||
self.movedAlertView.hide();
|
||||
}
|
||||
self.movedAlertView = MoveXBlockUtils.showMovedNotification(
|
||||
StringUtils.interpolate(
|
||||
gettext('Success! "{displayName}" has been moved.'),
|
||||
{
|
||||
displayName: self.sourceXBlockInfo.get('display_name')
|
||||
}
|
||||
),
|
||||
StringUtils.interpolate(
|
||||
gettext('{link_start}Take me to the new location{link_end}'),
|
||||
{
|
||||
link_start: HtmlUtils.HTML('<a href="/container/' + response.parent_locator + '">'),
|
||||
link_end: HtmlUtils.HTML('</a>')
|
||||
}
|
||||
),
|
||||
HtmlUtils.interpolateHtml(
|
||||
HtmlUtils.HTML(
|
||||
'<a class="action-undo-move" href="#" data-source-display-name="{displayName}" ' +
|
||||
'data-source-locator="{sourceLocator}" ' +
|
||||
'data-source-parent-locator="{sourceParentLocator}" ' +
|
||||
'data-target-index="{targetIndex}">{undoMove}</a>'
|
||||
),
|
||||
{
|
||||
displayName: self.sourceXBlockInfo.get('display_name'),
|
||||
sourceLocator: self.sourceXBlockInfo.id,
|
||||
sourceParentLocator: self.sourceParentXBlockInfo.id,
|
||||
targetIndex: response.source_index,
|
||||
undoMove: gettext('Undo move')
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -194,9 +194,11 @@ define(['jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'common/j
|
||||
|
||||
showMoveXBlockModal: function(event) {
|
||||
var xblockElement = this.findXBlockElement(event.target),
|
||||
parentXBlockElement = xblockElement.parents('.studio-xblock-wrapper'),
|
||||
modal = new MoveXBlockModal({
|
||||
sourceXBlockInfo: XBlockUtils.findXBlockInfo(xblockElement, this.model),
|
||||
XBlockURLRoot: this.getURLRoot(),
|
||||
sourceParentXBlockInfo: XBlockUtils.findXBlockInfo(parentXBlockElement, this.model),
|
||||
XBlockUrlRoot: this.getURLRoot(),
|
||||
outlineURL: this.options.outlineURL
|
||||
});
|
||||
|
||||
|
||||
69
cms/static/js/views/utils/move_xblock_utils.js
Normal file
69
cms/static/js/views/utils/move_xblock_utils.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Provides utilities for move xblock.
|
||||
*/
|
||||
define(['jquery', 'underscore', 'common/js/components/views/feedback_alert', 'js/views/utils/xblock_utils',
|
||||
'js/views/utils/move_xblock_utils', 'edx-ui-toolkit/js/utils/string-utils'],
|
||||
function($, _, AlertView, XBlockViewUtils, MoveXBlockUtils, StringUtils) {
|
||||
'use strict';
|
||||
var MovedAlertView, showMovedNotification;
|
||||
|
||||
MovedAlertView = AlertView.Confirmation.extend({
|
||||
events: _.extend({}, AlertView.Confirmation.prototype.events, {
|
||||
'click .action-undo-move': 'undoMoveXBlock'
|
||||
}),
|
||||
|
||||
options: $.extend({}, AlertView.Confirmation.prototype.options),
|
||||
|
||||
initialize: function() {
|
||||
AlertView.prototype.initialize.apply(this, arguments);
|
||||
this.movedAlertView = null;
|
||||
},
|
||||
|
||||
undoMoveXBlock: function(event) {
|
||||
var self = this,
|
||||
$moveButton = $(event.target),
|
||||
sourceLocator = $moveButton.data('source-locator'),
|
||||
sourceDisplayName = $moveButton.data('source-display-name'),
|
||||
sourceParentLocator = $moveButton.data('source-parent-locator'),
|
||||
targetIndex = $moveButton.data('target-index');
|
||||
XBlockViewUtils.moveXBlock(sourceLocator, sourceParentLocator, targetIndex)
|
||||
.done(function(response) {
|
||||
// show XBlock element
|
||||
$('.studio-xblock-wrapper[data-locator="' + response.move_source_locator + '"]').show();
|
||||
if (self.movedAlertView) {
|
||||
self.movedAlertView.hide();
|
||||
}
|
||||
self.movedAlertView = showMovedNotification(
|
||||
StringUtils.interpolate(
|
||||
gettext('Move cancelled. "{sourceDisplayName}" has been moved back to its original ' +
|
||||
'location.'),
|
||||
{
|
||||
sourceDisplayName: sourceDisplayName
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
showMovedNotification = function(title, titleHtml, messageHtml) {
|
||||
var movedAlertView = new MovedAlertView({
|
||||
title: title,
|
||||
titleHtml: titleHtml,
|
||||
messageHtml: messageHtml,
|
||||
maxShown: 10000
|
||||
});
|
||||
movedAlertView.show();
|
||||
// scroll to top
|
||||
$.smoothScroll({
|
||||
offset: 0,
|
||||
easing: 'swing',
|
||||
speed: 1000
|
||||
});
|
||||
return movedAlertView;
|
||||
};
|
||||
|
||||
return {
|
||||
showMovedNotification: showMovedNotification
|
||||
};
|
||||
});
|
||||
@@ -6,7 +6,8 @@ define(['jquery', 'underscore', 'gettext', 'common/js/components/utils/view_util
|
||||
function($, _, gettext, ViewUtils, ModuleUtils, XBlockInfo, StringUtils) {
|
||||
'use strict';
|
||||
var addXBlock, duplicateXBlock, deleteXBlock, createUpdateRequestData, updateXBlockField, VisibilityState,
|
||||
getXBlockVisibilityClass, getXBlockListTypeClass, updateXBlockFields, getXBlockType, findXBlockInfo;
|
||||
getXBlockVisibilityClass, getXBlockListTypeClass, updateXBlockFields, getXBlockType, findXBlockInfo,
|
||||
moveXBlock;
|
||||
|
||||
/**
|
||||
* Represents the possible visibility states for an xblock:
|
||||
@@ -91,6 +92,34 @@ define(['jquery', 'underscore', 'gettext', 'common/js/components/utils/view_util
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves the specified xblock in a new parent xblock.
|
||||
* @param {String} sourceLocator The xblock element to be moved.
|
||||
* @param {String} targetParentLocator Target parent xblock locator of the xblock to be moved,
|
||||
* new moved xblock would be placed under this xblock.
|
||||
* @param {String} targetIndex Intended index position of the xblock in parent xblock. If provided,
|
||||
* xblock would be placed at the particular index in the parent xblock.
|
||||
* @returns {jQuery promise} A promise representing the moving of the xblock.
|
||||
*/
|
||||
moveXBlock = function(sourceLocator, targetParentLocator, targetIndex) {
|
||||
var moveOperation = $.Deferred(),
|
||||
operationText = targetIndex !== undefined ? gettext('Undo moving') : gettext('Moving');
|
||||
return ViewUtils.runOperationShowingMessage(operationText,
|
||||
function() {
|
||||
$.patchJSON(ModuleUtils.getUpdateUrl(), {
|
||||
move_source_locator: sourceLocator,
|
||||
parent_locator: targetParentLocator,
|
||||
target_index: targetIndex
|
||||
}, function(data) {
|
||||
moveOperation.resolve(data);
|
||||
})
|
||||
.fail(function() {
|
||||
moveOperation.reject();
|
||||
});
|
||||
return moveOperation.promise();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes the specified xblock.
|
||||
* @param xblockInfo The model for the xblock to be deleted.
|
||||
@@ -267,6 +296,7 @@ define(['jquery', 'underscore', 'gettext', 'common/js/components/utils/view_util
|
||||
return {
|
||||
VisibilityState: VisibilityState,
|
||||
addXBlock: addXBlock,
|
||||
moveXBlock: moveXBlock,
|
||||
duplicateXBlock: duplicateXBlock,
|
||||
deleteXBlock: deleteXBlock,
|
||||
updateXBlockField: updateXBlockField,
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
options: {
|
||||
title: '',
|
||||
message: '',
|
||||
titleHtml: '', // an optional html that comes after the title.
|
||||
messageHtml: '', // an optional html that comes after the message.
|
||||
intent: null, // "warning", "confirmation", "error", "announcement", "step-required", etc
|
||||
type: null, // "alert", "notification", or "prompt": set by subclass
|
||||
shown: true, // is this view currently being shown?
|
||||
|
||||
49
common/static/common/js/components/views/feedback_move.js
Normal file
49
common/static/common/js/components/views/feedback_move.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* The MovedAlertView to show confirmation message when moving XBlocks.
|
||||
*/
|
||||
(function(define) {
|
||||
'use strict';
|
||||
define(['jquery', 'underscore', 'common/js/components/views/feedback_alert', 'js/views/utils/xblock_utils',
|
||||
'js/views/utils/move_xblock_utils', 'edx-ui-toolkit/js/utils/string-utils'],
|
||||
function($, _, AlertView, XBlockViewUtils, MoveXBlockUtils, StringUtils) {
|
||||
var MovedAlertView = AlertView.Confirmation.extend({
|
||||
events: _.extend({}, AlertView.Confirmation.prototype.events, {
|
||||
'click .action-undo-move': 'undoMoveXBlock'
|
||||
}),
|
||||
|
||||
options: $.extend({}, AlertView.Confirmation.prototype.options),
|
||||
|
||||
initialize: function() {
|
||||
AlertView.prototype.initialize.apply(this, arguments);
|
||||
this.movedAlertView = null;
|
||||
},
|
||||
|
||||
undoMoveXBlock: function(event) {
|
||||
var self = this,
|
||||
$moveButton = $(event.target),
|
||||
sourceLocator = $moveButton.data('source-locator'),
|
||||
sourceDisplayName = $moveButton.data('source-display-name'),
|
||||
sourceParentLocator = $moveButton.data('source-parent-locator'),
|
||||
targetIndex = $moveButton.data('target-index');
|
||||
XBlockViewUtils.moveXBlock(sourceLocator, sourceParentLocator, targetIndex)
|
||||
.done(function(response) {
|
||||
// show XBlock element
|
||||
$('.studio-xblock-wrapper[data-locator="' + response.move_source_locator + '"]').show();
|
||||
if (self.movedAlertView) {
|
||||
self.movedAlertView.hide();
|
||||
}
|
||||
self.movedAlertView = MoveXBlockUtils.showMovedNotification(
|
||||
StringUtils.interpolate(
|
||||
gettext('Move cancelled. "{sourceDisplayName}" has been moved back to its original ' +
|
||||
'location.'),
|
||||
{
|
||||
sourceDisplayName: sourceDisplayName
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
return MovedAlertView;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -15,8 +15,9 @@
|
||||
<% } %>
|
||||
|
||||
<div class="copy">
|
||||
<h2 class="title title-3" id="<%= type %>-<%= intent %>-title"><%- title %></h2>
|
||||
<h2 class="title title-3" id="<%= type %>-<%= intent %>-title"><%- title %><% if(titleHtml) { %> <%= titleHtml %> <% } %></h2>
|
||||
<% if(obj.message) { %><p class="message" id="<%= type %>-<%= intent %>-description"><%- message %></p><% } %>
|
||||
<% if(messageHtml) { %> <%= messageHtml %> <% } %>
|
||||
</div>
|
||||
|
||||
<% if(obj.actions) { %>
|
||||
|
||||
Reference in New Issue
Block a user