Enable disable move - TNL-6063
This commit is contained in:
@@ -735,6 +735,7 @@ class TestDuplicateItem(ItemTest, DuplicateHelper):
|
||||
verify_name(self.seq_usage_key, self.chapter_usage_key, "customized name", display_name="customized name")
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestMoveItem(ItemTest):
|
||||
"""
|
||||
Tests for move item.
|
||||
@@ -744,7 +745,16 @@ class TestMoveItem(ItemTest):
|
||||
Creates the test course structure to build course outline tree.
|
||||
"""
|
||||
super(TestMoveItem, self).setUp()
|
||||
self.setup_course()
|
||||
|
||||
def setup_course(self, default_store=None):
|
||||
"""
|
||||
Helper method to create the course.
|
||||
"""
|
||||
if not default_store:
|
||||
default_store = self.store.default_modulestore.get_modulestore_type()
|
||||
|
||||
self.course = CourseFactory.create(default_store=default_store)
|
||||
# Create a parent chapter
|
||||
chap1 = self.create_xblock(parent_usage_key=self.course.location, display_name='chapter1', category='chapter')
|
||||
self.chapter_usage_key = self.response_usage_key(chap1)
|
||||
@@ -821,10 +831,15 @@ class TestMoveItem(ItemTest):
|
||||
self.assertEqual(new_parent_loc, target_usage_key)
|
||||
self.assertNotEqual(parent_loc, new_parent_loc)
|
||||
|
||||
def test_move_component(self):
|
||||
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
|
||||
def test_move_component(self, store_type):
|
||||
"""
|
||||
Test move component with different xblock types.
|
||||
|
||||
Arguments:
|
||||
store_type (ModuleStoreEnum.Type): Type of modulestore to create test course in.
|
||||
"""
|
||||
self.setup_course(default_store=store_type)
|
||||
for source_usage_key, target_usage_key in [
|
||||
(self.html_usage_key, self.vert2_usage_key),
|
||||
(self.vert_usage_key, self.seq2_usage_key),
|
||||
|
||||
@@ -59,10 +59,10 @@
|
||||
success: callback
|
||||
});
|
||||
};
|
||||
$.postJSON = function(url, data, callback) {
|
||||
$.postJSON = function(url, data, callback) { // eslint-disable-line no-param-reassign
|
||||
return sendJSON(url, data, callback, 'POST');
|
||||
};
|
||||
$.patchJSON = function(url, data, callback) {
|
||||
$.patchJSON = function(url, data, callback) { // eslint-disable-line no-param-reassign
|
||||
return sendJSON(url, data, callback, 'PATCH');
|
||||
};
|
||||
return domReady(function() {
|
||||
|
||||
@@ -1,24 +1,8 @@
|
||||
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', '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) {
|
||||
'js/views/modals/move_xblock_modal', 'js/models/xblock_info'],
|
||||
function($, _, AjaxHelpers, TemplateHelpers, ViewHelpers, MoveXBlockModal, 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,
|
||||
@@ -33,6 +17,11 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
display_name: DISPLAY_NAME,
|
||||
category: 'html'
|
||||
}),
|
||||
sourceParentXBlockInfo: new XBlockInfo({
|
||||
id: 'PARENT_ID',
|
||||
display_name: 'VERT 101',
|
||||
category: 'vertical'
|
||||
}),
|
||||
XBlockURLRoot: '/xblock',
|
||||
outlineURL: OUTLINE_URL,
|
||||
XBlockAncestorInfoURL: ANCESTORS_URL
|
||||
@@ -58,7 +47,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: ' + sourceDisplayName);
|
||||
).toEqual('Move: ' + DISPLAY_NAME);
|
||||
expect(
|
||||
modal.$el.find('.modal-sr-title').text().trim()
|
||||
).toEqual('Choose a location to move your component to');
|
||||
@@ -72,7 +61,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', outlineUrl);
|
||||
AjaxHelpers.expectRequest(requests, 'GET', OUTLINE_URL);
|
||||
AjaxHelpers.respondWithJson(requests, {});
|
||||
AjaxHelpers.expectRequest(requests, 'GET', ANCESTORS_URL);
|
||||
AjaxHelpers.respondWithJson(requests, {});
|
||||
@@ -88,204 +77,4 @@ 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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
|
||||
'common/js/spec_helpers/template_helpers', 'js/views/move_xblock_list',
|
||||
'js/views/move_xblock_breadcrumb', 'js/models/xblock_info'],
|
||||
function($, _, AjaxHelpers, TemplateHelpers, MoveXBlockListView, MoveXBlockBreadcrumbView,
|
||||
XBlockInfoModel) {
|
||||
'common/js/spec_helpers/template_helpers', 'common/js/spec_helpers/view_helpers',
|
||||
'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';
|
||||
describe('MoveXBlock', function() {
|
||||
var renderViews, createXBlockInfo, createCourseOutline, moveXBlockBreadcrumbView,
|
||||
moveXBlockListView, parentChildMap, categoryMap, createChildXBlockInfo,
|
||||
var modal, showModal, renderViews, createXBlockInfo, createCourseOutline, courseOutlineOptions,
|
||||
parentChildMap, categoryMap, createChildXBlockInfo, xblockAncestorInfo, courseOutline,
|
||||
verifyBreadcrumbViewInfo, verifyListViewInfo, getDisplayedInfo, clickForwardButton,
|
||||
clickBreadcrumbButton, verifyXBlockInfo, nextCategory;
|
||||
clickBreadcrumbButton, verifyXBlockInfo, nextCategory, verifyMoveEnabled, getSentRequests,
|
||||
verifyNotificationStatus, sendMoveXBlockRequest, moveXBlockWithSuccess,
|
||||
verifyConfirmationFeedbackTitleHtml, verifyConfirmationFeedbackRedirectLinkHtml,
|
||||
verifyUndoConfirmationFeedbackTitleHtml, verifyConfirmationFeedbackUndoMoveActionHtml,
|
||||
sourceDisplayName = 'component_display_name_0',
|
||||
sourceLocator = 'component_ID_0',
|
||||
sourceParentLocator = 'unit_ID_0';
|
||||
|
||||
parentChildMap = {
|
||||
course: 'section',
|
||||
@@ -24,21 +30,71 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
component: 'component'
|
||||
};
|
||||
|
||||
courseOutlineOptions = {
|
||||
section: 2,
|
||||
subsection: 2,
|
||||
unit: 2,
|
||||
component: 2
|
||||
};
|
||||
|
||||
xblockAncestorInfo = {
|
||||
ancestors: [
|
||||
{
|
||||
category: 'vertical',
|
||||
display_name: 'unit_display_name_0',
|
||||
id: 'unit_ID_0'
|
||||
},
|
||||
{
|
||||
category: 'sequential',
|
||||
display_name: 'subsection_display_name_0',
|
||||
id: 'subsection_ID_0'
|
||||
},
|
||||
{
|
||||
category: 'chapter',
|
||||
display_name: 'section_display_name_0',
|
||||
id: 'section_ID_0'
|
||||
},
|
||||
{
|
||||
category: 'course',
|
||||
display_name: 'Demo Course',
|
||||
id: 'COURSE_ID_101'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
setFixtures(
|
||||
"<div class='breadcrumb-container'></div><div class='xblock-list-container'></div>"
|
||||
);
|
||||
setFixtures("<div id='page-alert'></div>");
|
||||
TemplateHelpers.installTemplates([
|
||||
'move-xblock-list',
|
||||
'move-xblock-breadcrumb'
|
||||
'basic-modal',
|
||||
'modal-button',
|
||||
'move-xblock-modal'
|
||||
]);
|
||||
courseOutline = createCourseOutline(courseOutlineOptions);
|
||||
showModal();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
moveXBlockBreadcrumbView.remove();
|
||||
moveXBlockListView.remove();
|
||||
modal.hide();
|
||||
courseOutline = null;
|
||||
});
|
||||
|
||||
showModal = function() {
|
||||
modal = new MoveXBlockModal({
|
||||
sourceXBlockInfo: new XBlockInfo({
|
||||
id: sourceLocator,
|
||||
display_name: sourceDisplayName,
|
||||
category: 'component'
|
||||
}),
|
||||
sourceParentXBlockInfo: new XBlockInfo({
|
||||
id: sourceParentLocator,
|
||||
display_name: 'unit_display_name_0',
|
||||
category: 'vertical'
|
||||
}),
|
||||
XBlockUrlRoot: '/xblock'
|
||||
});
|
||||
modal.show();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create child XBlock info.
|
||||
*
|
||||
@@ -51,7 +107,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
var childInfo = {
|
||||
category: categoryMap[category],
|
||||
display_name: category + '_display_name_' + xblockIndex,
|
||||
id: category + '_ID'
|
||||
id: category + '_ID_' + xblockIndex
|
||||
};
|
||||
return createXBlockInfo(parentChildMap[category], outlineOptions, childInfo);
|
||||
};
|
||||
@@ -93,12 +149,12 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
* @returns {Object}
|
||||
*/
|
||||
createCourseOutline = function(outlineOptions) {
|
||||
var courseOutline = {
|
||||
var courseXBlockInfo = {
|
||||
category: 'course',
|
||||
display_name: 'Demo Course',
|
||||
id: 'COURSE_ID_101'
|
||||
};
|
||||
return createXBlockInfo('section', outlineOptions, courseOutline);
|
||||
return createXBlockInfo('section', outlineOptions, courseXBlockInfo);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -108,13 +164,8 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
* @param {any} ancestorInfo ancestors info
|
||||
*/
|
||||
renderViews = function(courseOutlineInfo, ancestorInfo) {
|
||||
moveXBlockBreadcrumbView = new MoveXBlockBreadcrumbView({});
|
||||
moveXBlockListView = new MoveXBlockListView(
|
||||
{
|
||||
model: new XBlockInfoModel(courseOutlineInfo, {parse: true}),
|
||||
ancestorInfo: ancestorInfo || {ancestors: []}
|
||||
}
|
||||
);
|
||||
var ancestorInfo = ancestorInfo || {ancestors: []}; // eslint-disable-line no-redeclare
|
||||
modal.renderViews(courseOutlineInfo, ancestorInfo);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -123,7 +174,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
* @returns {Object}
|
||||
*/
|
||||
getDisplayedInfo = function() {
|
||||
var viewEl = moveXBlockListView.$el;
|
||||
var viewEl = modal.moveXBlockListView.$el;
|
||||
return {
|
||||
categoryText: viewEl.find('.category-text').text().trim(),
|
||||
currentLocationText: viewEl.find('.current-location').text().trim(),
|
||||
@@ -147,7 +198,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
*/
|
||||
verifyListViewInfo = function(category, expectedXBlocksCount, hasCurrentLocation) {
|
||||
var displayedInfo = getDisplayedInfo();
|
||||
expect(displayedInfo.categoryText).toEqual(moveXBlockListView.categoriesText[category] + ':');
|
||||
expect(displayedInfo.categoryText).toEqual(modal.moveXBlockListView.categoriesText[category] + ':');
|
||||
expect(displayedInfo.xblockCount).toEqual(expectedXBlocksCount);
|
||||
expect(displayedInfo.xblockDisplayNames).toEqual(
|
||||
_.map(_.range(expectedXBlocksCount), function(xblockIndex) {
|
||||
@@ -174,7 +225,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
* @param {any} xblockIndex XBlock index
|
||||
*/
|
||||
verifyBreadcrumbViewInfo = function(category, xblockIndex) {
|
||||
var displayedBreadcrumbs = moveXBlockBreadcrumbView.$el.find('.breadcrumbs .bc-container').map(
|
||||
var displayedBreadcrumbs = modal.moveXBlockBreadcrumbView.$el.find('.breadcrumbs .bc-container').map(
|
||||
function() { return $(this).text().trim(); }
|
||||
).get(),
|
||||
categories = _.keys(parentChildMap).concat(['component']),
|
||||
@@ -195,14 +246,14 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
*/
|
||||
clickForwardButton = function(buttonIndex) {
|
||||
buttonIndex = buttonIndex || 0; // eslint-disable-line no-param-reassign
|
||||
moveXBlockListView.$el.find('[data-item-index="' + buttonIndex + '"] button').click();
|
||||
modal.moveXBlockListView.$el.find('[data-item-index="' + buttonIndex + '"] button').click();
|
||||
};
|
||||
|
||||
/**
|
||||
* Click on last clickable breadcrumb button.
|
||||
*/
|
||||
clickBreadcrumbButton = function() {
|
||||
moveXBlockBreadcrumbView.$el.find('.bc-container button').last().click();
|
||||
modal.moveXBlockBreadcrumbView.$el.find('.bc-container button').last().click();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -231,6 +282,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
|
||||
verifyListViewInfo(category, expectedXBlocksCount, hasCurrentLocation);
|
||||
verifyBreadcrumbViewInfo(category, buttonIndex);
|
||||
verifyMoveEnabled(category, hasCurrentLocation);
|
||||
|
||||
if (direction === 'forward') {
|
||||
if (category === 'component') {
|
||||
@@ -248,13 +300,162 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
verifyXBlockInfo(outlineOptions, category, buttonIndex, direction, hasCurrentLocation);
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify move button is enabled.
|
||||
*
|
||||
* @param {String} category XBlock category
|
||||
* @param {String} hasCurrentLocation do we need to check current location
|
||||
*/
|
||||
verifyMoveEnabled = function(category, hasCurrentLocation) {
|
||||
var isMoveEnabled = !modal.$el.find('.modal-actions .action-move').hasClass('is-disabled');
|
||||
if (category === 'component' && !hasCurrentLocation) {
|
||||
expect(isMoveEnabled).toBeTruthy();
|
||||
} else {
|
||||
expect(isMoveEnabled).toBeFalsy();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify notification status.
|
||||
*
|
||||
* @param {Object} requests requests object
|
||||
* @param {Object} notificationSpy notification spy
|
||||
* @param {String} notificationText notification text to be verified
|
||||
* @param {Integer} sourceIndex source index of the xblock
|
||||
*/
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send move xblock request.
|
||||
*
|
||||
* @param {Object} requests requests object
|
||||
* @param {Object} xblockLocator Xblock id location
|
||||
* @param {Integer} targetIndex target index of the xblock
|
||||
* @param {Integer} sourceIndex source index of the xblock
|
||||
*/
|
||||
sendMoveXBlockRequest = function(requests, xblockLocator, targetIndex, sourceIndex) {
|
||||
var responseData,
|
||||
expectedData,
|
||||
sourceIndex = sourceIndex || 0; // eslint-disable-line no-redeclare
|
||||
|
||||
responseData = expectedData = {
|
||||
move_source_locator: xblockLocator,
|
||||
parent_locator: modal.targetParentXBlockInfo.id
|
||||
};
|
||||
|
||||
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
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Move xblock with success.
|
||||
*
|
||||
* @param {Object} requests requests object
|
||||
*/
|
||||
moveXBlockWithSuccess = function(requests) {
|
||||
// select a target item and click
|
||||
renderViews(courseOutline);
|
||||
_.each(_.range(3), function() {
|
||||
clickForwardButton(1);
|
||||
});
|
||||
modal.$el.find('.modal-actions .action-move').click();
|
||||
sendMoveXBlockRequest(requests, sourceLocator);
|
||||
expect(modal.movedAlertView).toBeDefined();
|
||||
verifyConfirmationFeedbackTitleHtml(sourceDisplayName);
|
||||
verifyConfirmationFeedbackRedirectLinkHtml();
|
||||
verifyConfirmationFeedbackUndoMoveActionHtml();
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify success banner message html has correct title html.
|
||||
*
|
||||
* @param {String} displayName XBlock display name
|
||||
*/
|
||||
verifyConfirmationFeedbackTitleHtml = function(displayName) {
|
||||
expect(modal.movedAlertView.$el.find('.title').html().trim())
|
||||
.toEqual(StringUtils.interpolate('Success! "{displayName}" has been moved.',
|
||||
{
|
||||
displayName: displayName
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify undo success banner message html has correct title html.
|
||||
*
|
||||
* @param {String} displayName XBlock display name
|
||||
*/
|
||||
verifyUndoConfirmationFeedbackTitleHtml = function(displayName) {
|
||||
expect(modal.movedAlertView.$el.find('.title').html()).toEqual(
|
||||
StringUtils.interpolate(
|
||||
'Move cancelled. "{sourceDisplayName}" has been moved back to its original location.',
|
||||
{
|
||||
sourceDisplayName: displayName
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify success banner message html has correct redirect link html.
|
||||
*/
|
||||
verifyConfirmationFeedbackRedirectLinkHtml = function() {
|
||||
expect(modal.movedAlertView.$el.find('.copy').html().indexOf(
|
||||
HtmlUtils.HTML(
|
||||
'<button class="action-secondary action-cancel">Take me to the new location</button>'
|
||||
) !== -1
|
||||
)).toBeTruthy();
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify success banner message html has correct undo move button html.
|
||||
*/
|
||||
verifyConfirmationFeedbackUndoMoveActionHtml = function() {
|
||||
expect(modal.movedAlertView.$el.find('.copy').html().indexOf(
|
||||
HtmlUtils.HTML(
|
||||
'<button class="action-primary action-save">Undo Move</button>'
|
||||
) !== -1
|
||||
)).toBeTruthy();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get sent requests.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
getSentRequests = function() {
|
||||
return jasmine.Ajax.requests.filter(function(request) {
|
||||
return request.readyState > 0;
|
||||
});
|
||||
};
|
||||
|
||||
it('renders views with correct information', function() {
|
||||
var outlineOptions = {section: 1, subsection: 1, unit: 1, component: 1},
|
||||
outline = createCourseOutline(outlineOptions);
|
||||
|
||||
renderViews(outline);
|
||||
verifyXBlockInfo(outlineOptions, 'section', 0, 'forward', false);
|
||||
verifyXBlockInfo(outlineOptions, 'component', 0, 'backward', false);
|
||||
renderViews(outline, xblockAncestorInfo);
|
||||
verifyXBlockInfo(outlineOptions, 'section', 0, 'forward', true);
|
||||
verifyXBlockInfo(outlineOptions, 'component', 0, 'backward', true);
|
||||
});
|
||||
|
||||
it('shows correct behavior on breadcrumb navigation', function() {
|
||||
@@ -268,43 +469,18 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
_.each(['component', 'unit', 'subsection', 'section'], function(category) {
|
||||
verifyListViewInfo(category, 1);
|
||||
if (category !== 'section') {
|
||||
moveXBlockBreadcrumbView.$el.find('.bc-container button').last().click();
|
||||
modal.moveXBlockBreadcrumbView.$el.find('.bc-container button').last().click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the correct current location', function() {
|
||||
var outlineOptions = {section: 2, subsection: 2, unit: 2, component: 2},
|
||||
outline = createCourseOutline(outlineOptions),
|
||||
ancestorInfo = {
|
||||
ancestors: [
|
||||
{
|
||||
category: 'vertical',
|
||||
display_name: 'unit_display_name_0',
|
||||
id: 'unit_ID'
|
||||
},
|
||||
{
|
||||
category: 'sequential',
|
||||
display_name: 'subsection_display_name_0',
|
||||
id: 'subsection_ID'
|
||||
},
|
||||
{
|
||||
category: 'chapter',
|
||||
display_name: 'section_display_name_0',
|
||||
id: 'section_ID'
|
||||
},
|
||||
{
|
||||
category: 'course',
|
||||
display_name: 'Demo Course',
|
||||
id: 'COURSE_ID_101'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
renderViews(outline, ancestorInfo);
|
||||
outline = createCourseOutline(outlineOptions);
|
||||
renderViews(outline, xblockAncestorInfo);
|
||||
verifyXBlockInfo(outlineOptions, 'section', 0, 'forward', true);
|
||||
// click the outline breadcrumb to render sections
|
||||
moveXBlockBreadcrumbView.$el.find('.bc-container button').first().click();
|
||||
modal.moveXBlockBreadcrumbView.$el.find('.bc-container button').first().click();
|
||||
verifyXBlockInfo(outlineOptions, 'section', 1, 'forward', false);
|
||||
});
|
||||
|
||||
@@ -336,9 +512,114 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
|
||||
_.each(_.range(info.forwardClicks), function() {
|
||||
clickForwardButton();
|
||||
});
|
||||
expect(moveXBlockListView.$el.find('.xblock-no-child-message').text().trim()).toEqual(info.message);
|
||||
moveXBlockListView.undelegateEvents();
|
||||
moveXBlockBreadcrumbView.undelegateEvents();
|
||||
expect(modal.moveXBlockListView.$el.find('.xblock-no-child-message').text().trim())
|
||||
.toEqual(info.message);
|
||||
modal.moveXBlockListView.undelegateEvents();
|
||||
modal.moveXBlockBreadcrumbView.undelegateEvents();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Move an xblock', function() {
|
||||
it('can not move in a disabled state', function() {
|
||||
verifyMoveEnabled(false);
|
||||
modal.$el.find('.modal-actions .action-move').click();
|
||||
expect(modal.movedAlertView).toBeNull();
|
||||
expect(getSentRequests().length).toEqual(0);
|
||||
});
|
||||
|
||||
it('move button is disabled when navigating to same parent', function() {
|
||||
// select a target parent as the same as source parent and click
|
||||
renderViews(courseOutline);
|
||||
_.each(_.range(3), function() {
|
||||
clickForwardButton(0);
|
||||
});
|
||||
verifyMoveEnabled('component', true);
|
||||
});
|
||||
|
||||
it('move button is enabled when navigating to different parent', function() {
|
||||
// select a target parent as the different as source parent and click
|
||||
renderViews(courseOutline);
|
||||
_.each(_.range(3), function() {
|
||||
clickForwardButton(1);
|
||||
});
|
||||
verifyMoveEnabled('component', false);
|
||||
});
|
||||
|
||||
it('verify move state while navigating', function() {
|
||||
renderViews(courseOutline, xblockAncestorInfo);
|
||||
verifyXBlockInfo(courseOutlineOptions, 'section', 0, 'forward', true);
|
||||
// start from course outline again
|
||||
modal.moveXBlockBreadcrumbView.$el.find('.bc-container button').first().click();
|
||||
verifyXBlockInfo(courseOutlineOptions, 'section', 1, 'forward', false);
|
||||
});
|
||||
|
||||
it('move an xblock when move button is clicked', function() {
|
||||
var requests = AjaxHelpers.requests(this);
|
||||
moveXBlockWithSuccess(requests);
|
||||
});
|
||||
|
||||
it('do not move an xblock when cancel button is clicked', function() {
|
||||
modal.$el.find('.modal-actions .action-cancel').click();
|
||||
expect(modal.movedAlertView).toBeNull();
|
||||
expect(getSentRequests().length).toEqual(0);
|
||||
});
|
||||
|
||||
it('undo move an xblock when undo move link is clicked', function() {
|
||||
var sourceIndex = 0,
|
||||
requests = AjaxHelpers.requests(this);
|
||||
moveXBlockWithSuccess(requests);
|
||||
modal.movedAlertView.$el.find('.action-save').click();
|
||||
AjaxHelpers.respondWithJson(requests, {
|
||||
move_source_locator: sourceLocator,
|
||||
parent_locator: sourceParentLocator,
|
||||
target_index: sourceIndex
|
||||
});
|
||||
verifyUndoConfirmationFeedbackTitleHtml(sourceDisplayName);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shows a notification', function() {
|
||||
it('mini operation message when moving an xblock', function() {
|
||||
var requests = AjaxHelpers.requests(this),
|
||||
notificationSpy = ViewHelpers.createNotificationSpy();
|
||||
// navigate to a target parent and click
|
||||
renderViews(courseOutline);
|
||||
_.each(_.range(3), function() {
|
||||
clickForwardButton(1);
|
||||
});
|
||||
modal.$el.find('.modal-actions .action-move').click();
|
||||
verifyNotificationStatus(requests, notificationSpy, 'Moving');
|
||||
});
|
||||
|
||||
it('mini operation message when undo moving an xblock', function() {
|
||||
var notificationSpy,
|
||||
requests = AjaxHelpers.requests(this);
|
||||
moveXBlockWithSuccess(requests);
|
||||
notificationSpy = ViewHelpers.createNotificationSpy();
|
||||
modal.movedAlertView.$el.find('.action-save').click();
|
||||
verifyNotificationStatus(requests, notificationSpy, 'Undo moving');
|
||||
});
|
||||
|
||||
it('error message when move request fails', function() {
|
||||
var requests = AjaxHelpers.requests(this),
|
||||
notificationSpy = ViewHelpers.createNotificationSpy('Error');
|
||||
// select a target item and click
|
||||
renderViews(courseOutline);
|
||||
_.each(_.range(3), function() {
|
||||
clickForwardButton(1);
|
||||
});
|
||||
modal.$el.find('.modal-actions .action-move').click();
|
||||
AjaxHelpers.respondWithError(requests);
|
||||
ViewHelpers.verifyNotificationShowing(notificationSpy, "Studio's having trouble saving your work");
|
||||
});
|
||||
|
||||
it('error message when undo move request fails', function() {
|
||||
var requests = AjaxHelpers.requests(this),
|
||||
notificationSpy = ViewHelpers.createNotificationSpy('Error');
|
||||
moveXBlockWithSuccess(requests);
|
||||
modal.movedAlertView.$el.find('.action-save').click();
|
||||
AjaxHelpers.respondWithError(requests);
|
||||
ViewHelpers.verifyNotificationShowing(notificationSpy, "Studio's having trouble saving your work");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,25 +2,29 @@
|
||||
* The MoveXblockModal to move XBlocks in course.
|
||||
*/
|
||||
define([
|
||||
'jquery', 'backbone', 'underscore', 'gettext',
|
||||
'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',
|
||||
'jquery',
|
||||
'backbone',
|
||||
'underscore',
|
||||
'gettext',
|
||||
'js/views/baseview',
|
||||
'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',
|
||||
'common/js/components/views/feedback',
|
||||
'js/models/xblock_info',
|
||||
'js/views/modals/base_modal',
|
||||
'js/views/move_xblock_list',
|
||||
'js/views/move_xblock_breadcrumb',
|
||||
'text!templates/move-xblock-modal.underscore'
|
||||
],
|
||||
function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlockListView, MoveXBlockBreadcrumbView,
|
||||
Feedback, XBlockViewUtils, MoveXBlockUtils, HtmlUtils, StringUtils, MoveXblockModalTemplate) {
|
||||
function($, Backbone, _, gettext, BaseView, XBlockViewUtils, MoveXBlockUtils, HtmlUtils, StringUtils, Feedback,
|
||||
XBlockInfoModel, BaseModal, MoveXBlockListView, MoveXBlockBreadcrumbView, 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'
|
||||
'click .action-move:not(.is-disabled)': 'moveXBlock'
|
||||
}),
|
||||
|
||||
options: $.extend({}, BaseModal.prototype.options, {
|
||||
@@ -40,6 +44,7 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
|
||||
this.listenTo(Backbone, 'move:breadcrumbRendered', this.focusModal);
|
||||
this.sourceXBlockInfo = this.options.sourceXBlockInfo;
|
||||
this.sourceParentXBlockInfo = this.options.sourceParentXBlockInfo;
|
||||
this.targetParentXBlockInfo = null;
|
||||
this.XBlockURLRoot = this.options.XBlockURLRoot;
|
||||
this.XBlockAncestorInfoURL = StringUtils.interpolate(
|
||||
'{urlRoot}/{usageId}?fields=ancestorInfo',
|
||||
@@ -52,10 +57,9 @@ 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;
|
||||
this.isValidMove = false;
|
||||
this.listenTo(Backbone, 'move:enableMoveOperation', this.enableMoveOperation);
|
||||
},
|
||||
|
||||
getTitle: function() {
|
||||
@@ -71,7 +75,8 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
|
||||
|
||||
show: function() {
|
||||
BaseModal.prototype.show.apply(this, [false]);
|
||||
Feedback.prototype.inFocus.apply(this, [this.options.modalWindowClass]);
|
||||
this.updateMoveState(false);
|
||||
MoveXBlockUtils.hideMovedNotification();
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
@@ -122,50 +127,51 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
|
||||
);
|
||||
},
|
||||
|
||||
updateMoveState: function(isValidMove) {
|
||||
var $moveButton = this.$el.find('.action-move');
|
||||
if (isValidMove) {
|
||||
$moveButton.removeClass('is-disabled');
|
||||
} else {
|
||||
$moveButton.addClass('is-disabled');
|
||||
}
|
||||
},
|
||||
|
||||
enableMoveOperation: function(targetParentXBlockInfo) {
|
||||
var isValidMove = false,
|
||||
sourceParentType = this.sourceParentXBlockInfo.get('category'),
|
||||
targetParentType = targetParentXBlockInfo.get('category');
|
||||
|
||||
if (targetParentType === sourceParentType && this.sourceParentXBlockInfo.id !== targetParentXBlockInfo.id) {
|
||||
isValidMove = true;
|
||||
this.targetParentXBlockInfo = targetParentXBlockInfo;
|
||||
}
|
||||
this.updateMoveState(isValidMove);
|
||||
},
|
||||
|
||||
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();
|
||||
XBlockViewUtils.moveXBlock(self.sourceXBlockInfo.id, self.targetParentXBlockInfo.id)
|
||||
.done(function(response) {
|
||||
// hide modal
|
||||
self.hide();
|
||||
// hide xblock element
|
||||
$("li.studio-xblock-wrapper[data-locator='" + self.sourceXBlockInfo.id + "']").hide();
|
||||
self.movedAlertView = MoveXBlockUtils.showMovedNotification(
|
||||
StringUtils.interpolate(
|
||||
gettext('Success! "{displayName}" has been moved.'),
|
||||
{
|
||||
displayName: self.sourceXBlockInfo.get('display_name')
|
||||
}
|
||||
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')
|
||||
}
|
||||
)
|
||||
);
|
||||
),
|
||||
{
|
||||
sourceDisplayName: self.sourceXBlockInfo.get('display_name'),
|
||||
sourceLocator: self.sourceXBlockInfo.id,
|
||||
sourceParentLocator: self.sourceParentXBlockInfo.id,
|
||||
targetParentLocator: response.parent_locator,
|
||||
targetIndex: response.source_index
|
||||
}
|
||||
});
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ function($, Backbone, _, gettext, HtmlUtils, StringUtils, XBlockUtils, MoveXBloc
|
||||
)
|
||||
);
|
||||
Backbone.trigger('move:childrenRendered', this.breadcrumbInfo());
|
||||
Backbone.trigger('move:enableMoveOperation', this.parentInfo.parent);
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ define(['jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'common/j
|
||||
modal = new MoveXBlockModal({
|
||||
sourceXBlockInfo: XBlockUtils.findXBlockInfo(xblockElement, this.model),
|
||||
sourceParentXBlockInfo: XBlockUtils.findXBlockInfo(parentXBlockElement, this.model),
|
||||
XBlockUrlRoot: this.getURLRoot(),
|
||||
XBlockURLRoot: this.getURLRoot(),
|
||||
outlineURL: this.options.outlineURL
|
||||
});
|
||||
|
||||
|
||||
@@ -1,69 +1,106 @@
|
||||
/**
|
||||
* 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;
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/components/views/feedback',
|
||||
'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($, _, Feedback, AlertView, XBlockViewUtils, MoveXBlockUtils, StringUtils) {
|
||||
'use strict';
|
||||
var redirectLink, undoMoveXBlock, showMovedNotification, hideMovedNotification;
|
||||
|
||||
MovedAlertView = AlertView.Confirmation.extend({
|
||||
events: _.extend({}, AlertView.Confirmation.prototype.events, {
|
||||
'click .action-undo-move': 'undoMoveXBlock'
|
||||
}),
|
||||
redirectLink = function(link) {
|
||||
window.location.href = link;
|
||||
};
|
||||
|
||||
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();
|
||||
undoMoveXBlock = function(data) {
|
||||
XBlockViewUtils.moveXBlock(data.sourceLocator, data.sourceParentLocator, data.targetIndex)
|
||||
.done(function(response) {
|
||||
// show XBlock element
|
||||
$('.studio-xblock-wrapper[data-locator="' + response.move_source_locator + '"]').show();
|
||||
showMovedNotification(
|
||||
StringUtils.interpolate(
|
||||
gettext('Move cancelled. "{sourceDisplayName}" has been moved back to its original location.'),
|
||||
{
|
||||
sourceDisplayName: data.sourceDisplayName
|
||||
}
|
||||
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({
|
||||
showMovedNotification = function(title, data) {
|
||||
var movedAlertView;
|
||||
// data is provided when we click undo move button.
|
||||
if (data) {
|
||||
movedAlertView = new AlertView.Confirmation({
|
||||
title: title,
|
||||
titleHtml: titleHtml,
|
||||
messageHtml: messageHtml,
|
||||
maxShown: 10000
|
||||
actions: {
|
||||
primary: {
|
||||
text: gettext('Undo move'),
|
||||
class: 'action-save',
|
||||
data: JSON.stringify({
|
||||
sourceDisplayName: data.sourceDisplayName,
|
||||
sourceLocator: data.sourceLocator,
|
||||
sourceParentLocator: data.sourceParentLocator,
|
||||
targetIndex: data.targetIndex
|
||||
}),
|
||||
click: function() {
|
||||
undoMoveXBlock(
|
||||
{
|
||||
sourceDisplayName: data.sourceDisplayName,
|
||||
sourceLocator: data.sourceLocator,
|
||||
sourceParentLocator: data.sourceParentLocator,
|
||||
targetIndex: data.targetIndex
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
secondary: [
|
||||
{
|
||||
text: gettext('Take me to the new location'),
|
||||
class: 'action-cancel',
|
||||
data: JSON.stringify({
|
||||
targetParentLocator: data.targetParentLocator
|
||||
}),
|
||||
click: function() {
|
||||
redirectLink('/container/' + data.targetParentLocator);
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
movedAlertView.show();
|
||||
// scroll to top
|
||||
$.smoothScroll({
|
||||
offset: 0,
|
||||
easing: 'swing',
|
||||
speed: 1000
|
||||
} else {
|
||||
movedAlertView = new AlertView.Confirmation({
|
||||
title: title
|
||||
});
|
||||
return movedAlertView;
|
||||
};
|
||||
}
|
||||
movedAlertView.show();
|
||||
// scroll to top
|
||||
$.smoothScroll({
|
||||
offset: 0,
|
||||
easing: 'swing',
|
||||
speed: 1000
|
||||
});
|
||||
movedAlertView.$('.wrapper').first().focus();
|
||||
return movedAlertView;
|
||||
};
|
||||
|
||||
return {
|
||||
showMovedNotification: showMovedNotification
|
||||
};
|
||||
});
|
||||
hideMovedNotification = function() {
|
||||
var movedAlertView = Feedback.active_alert;
|
||||
if (movedAlertView) {
|
||||
AlertView.prototype.hide.apply(movedAlertView);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
redirectLink: redirectLink,
|
||||
showMovedNotification: showMovedNotification,
|
||||
hideMovedNotification: hideMovedNotification
|
||||
};
|
||||
});
|
||||
|
||||
@@ -94,10 +94,10 @@ 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,
|
||||
* @param {String} sourceLocator Locator of xblock element to be moved.
|
||||
* @param {String} targetParentLocator Locator of the target parent xblock, moved xblock would be placed
|
||||
* under this xblock.
|
||||
* @param {Integer} 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.
|
||||
*/
|
||||
@@ -110,8 +110,8 @@ define(['jquery', 'underscore', 'gettext', 'common/js/components/utils/view_util
|
||||
move_source_locator: sourceLocator,
|
||||
parent_locator: targetParentLocator,
|
||||
target_index: targetIndex
|
||||
}, function(data) {
|
||||
moveOperation.resolve(data);
|
||||
}, function(response) {
|
||||
moveOperation.resolve(response);
|
||||
})
|
||||
.fail(function() {
|
||||
moveOperation.reject();
|
||||
|
||||
@@ -297,6 +297,11 @@
|
||||
.ui-loading {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.modal-actions .action-move.is-disabled {
|
||||
border: 1px solid $gray-l1 !important;
|
||||
background: $gray-l1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// upload modal
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
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?
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* 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,9 +15,8 @@
|
||||
<% } %>
|
||||
|
||||
<div class="copy">
|
||||
<h2 class="title title-3" id="<%= type %>-<%= intent %>-title"><%- title %><% if(titleHtml) { %> <%= titleHtml %> <% } %></h2>
|
||||
<h2 class="title title-3" id="<%= type %>-<%= intent %>-title"><%- title %></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