Commit e9b8e17f by Mushtaq Ali

Enable disable move - TNL-6063

parent e856f07b
...@@ -735,6 +735,7 @@ class TestDuplicateItem(ItemTest, DuplicateHelper): ...@@ -735,6 +735,7 @@ class TestDuplicateItem(ItemTest, DuplicateHelper):
verify_name(self.seq_usage_key, self.chapter_usage_key, "customized name", display_name="customized name") verify_name(self.seq_usage_key, self.chapter_usage_key, "customized name", display_name="customized name")
@ddt.ddt
class TestMoveItem(ItemTest): class TestMoveItem(ItemTest):
""" """
Tests for move item. Tests for move item.
...@@ -744,7 +745,16 @@ class TestMoveItem(ItemTest): ...@@ -744,7 +745,16 @@ class TestMoveItem(ItemTest):
Creates the test course structure to build course outline tree. Creates the test course structure to build course outline tree.
""" """
super(TestMoveItem, self).setUp() 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 # Create a parent chapter
chap1 = self.create_xblock(parent_usage_key=self.course.location, display_name='chapter1', category='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) self.chapter_usage_key = self.response_usage_key(chap1)
...@@ -821,10 +831,15 @@ class TestMoveItem(ItemTest): ...@@ -821,10 +831,15 @@ class TestMoveItem(ItemTest):
self.assertEqual(new_parent_loc, target_usage_key) self.assertEqual(new_parent_loc, target_usage_key)
self.assertNotEqual(parent_loc, new_parent_loc) 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. 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 [ for source_usage_key, target_usage_key in [
(self.html_usage_key, self.vert2_usage_key), (self.html_usage_key, self.vert2_usage_key),
(self.vert_usage_key, self.seq2_usage_key), (self.vert_usage_key, self.seq2_usage_key),
......
...@@ -59,10 +59,10 @@ ...@@ -59,10 +59,10 @@
success: callback success: callback
}); });
}; };
$.postJSON = function(url, data, callback) { $.postJSON = function(url, data, callback) { // eslint-disable-line no-param-reassign
return sendJSON(url, data, callback, 'POST'); 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 sendJSON(url, data, callback, 'PATCH');
}; };
return domReady(function() { return domReady(function() {
......
define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers', 'common/js/spec_helpers/view_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', 'js/views/modals/move_xblock_modal', 'js/models/xblock_info'],
'edx-ui-toolkit/js/utils/string-utils', 'js/models/xblock_info'], function($, _, AjaxHelpers, TemplateHelpers, ViewHelpers, MoveXBlockModal, XBlockInfo) {
function($, _, AjaxHelpers, TemplateHelpers, ViewHelpers, MoveXBlockModal, HtmlUtils, StringUtils, XBlockInfo) {
'use strict'; '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() { describe('MoveXBlockModal', function() {
var modal, var modal,
showModal, showModal,
...@@ -33,6 +17,11 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -33,6 +17,11 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
display_name: DISPLAY_NAME, display_name: DISPLAY_NAME,
category: 'html' category: 'html'
}), }),
sourceParentXBlockInfo: new XBlockInfo({
id: 'PARENT_ID',
display_name: 'VERT 101',
category: 'vertical'
}),
XBlockURLRoot: '/xblock', XBlockURLRoot: '/xblock',
outlineURL: OUTLINE_URL, outlineURL: OUTLINE_URL,
XBlockAncestorInfoURL: ANCESTORS_URL XBlockAncestorInfoURL: ANCESTORS_URL
...@@ -58,7 +47,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -58,7 +47,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
showModal(); showModal();
expect( expect(
modal.$el.find('.modal-header .title').contents().get(0).nodeValue.trim() modal.$el.find('.modal-header .title').contents().get(0).nodeValue.trim()
).toEqual('Move: ' + sourceDisplayName); ).toEqual('Move: ' + DISPLAY_NAME);
expect( expect(
modal.$el.find('.modal-sr-title').text().trim() modal.$el.find('.modal-sr-title').text().trim()
).toEqual('Choose a location to move your component to'); ).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 ...@@ -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(); expect(modal.$el.find('.ui-loading.is-hidden')).not.toExist();
renderViewsSpy = spyOn(modal, 'renderViews'); renderViewsSpy = spyOn(modal, 'renderViews');
expect(requests.length).toEqual(2); expect(requests.length).toEqual(2);
AjaxHelpers.expectRequest(requests, 'GET', outlineUrl); AjaxHelpers.expectRequest(requests, 'GET', OUTLINE_URL);
AjaxHelpers.respondWithJson(requests, {}); AjaxHelpers.respondWithJson(requests, {});
AjaxHelpers.expectRequest(requests, 'GET', ANCESTORS_URL); AjaxHelpers.expectRequest(requests, 'GET', ANCESTORS_URL);
AjaxHelpers.respondWithJson(requests, {}); AjaxHelpers.respondWithJson(requests, {});
...@@ -88,204 +77,4 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -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"); 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');
});
});
}); });
define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'common/js/spec_helpers/template_helpers', 'js/views/move_xblock_list', 'common/js/spec_helpers/template_helpers', 'common/js/spec_helpers/view_helpers',
'js/views/move_xblock_breadcrumb', 'js/models/xblock_info'], 'js/views/modals/move_xblock_modal', 'edx-ui-toolkit/js/utils/html-utils',
function($, _, AjaxHelpers, TemplateHelpers, MoveXBlockListView, MoveXBlockBreadcrumbView, 'edx-ui-toolkit/js/utils/string-utils', 'js/models/xblock_info'],
XBlockInfoModel) { function($, _, AjaxHelpers, TemplateHelpers, ViewHelpers, MoveXBlockModal, HtmlUtils, StringUtils, XBlockInfo) {
'use strict'; 'use strict';
describe('MoveXBlock', function() { describe('MoveXBlock', function() {
var renderViews, createXBlockInfo, createCourseOutline, moveXBlockBreadcrumbView, var modal, showModal, renderViews, createXBlockInfo, createCourseOutline, courseOutlineOptions,
moveXBlockListView, parentChildMap, categoryMap, createChildXBlockInfo, parentChildMap, categoryMap, createChildXBlockInfo, xblockAncestorInfo, courseOutline,
verifyBreadcrumbViewInfo, verifyListViewInfo, getDisplayedInfo, clickForwardButton, 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 = { parentChildMap = {
course: 'section', course: 'section',
...@@ -24,21 +30,71 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -24,21 +30,71 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
component: 'component' 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() { beforeEach(function() {
setFixtures( setFixtures("<div id='page-alert'></div>");
"<div class='breadcrumb-container'></div><div class='xblock-list-container'></div>"
);
TemplateHelpers.installTemplates([ TemplateHelpers.installTemplates([
'move-xblock-list', 'basic-modal',
'move-xblock-breadcrumb' 'modal-button',
'move-xblock-modal'
]); ]);
courseOutline = createCourseOutline(courseOutlineOptions);
showModal();
}); });
afterEach(function() { afterEach(function() {
moveXBlockBreadcrumbView.remove(); modal.hide();
moveXBlockListView.remove(); 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. * Create child XBlock info.
* *
...@@ -51,7 +107,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -51,7 +107,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
var childInfo = { var childInfo = {
category: categoryMap[category], category: categoryMap[category],
display_name: category + '_display_name_' + xblockIndex, display_name: category + '_display_name_' + xblockIndex,
id: category + '_ID' id: category + '_ID_' + xblockIndex
}; };
return createXBlockInfo(parentChildMap[category], outlineOptions, childInfo); return createXBlockInfo(parentChildMap[category], outlineOptions, childInfo);
}; };
...@@ -93,12 +149,12 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -93,12 +149,12 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
* @returns {Object} * @returns {Object}
*/ */
createCourseOutline = function(outlineOptions) { createCourseOutline = function(outlineOptions) {
var courseOutline = { var courseXBlockInfo = {
category: 'course', category: 'course',
display_name: 'Demo Course', display_name: 'Demo Course',
id: 'COURSE_ID_101' 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 ...@@ -108,13 +164,8 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
* @param {any} ancestorInfo ancestors info * @param {any} ancestorInfo ancestors info
*/ */
renderViews = function(courseOutlineInfo, ancestorInfo) { renderViews = function(courseOutlineInfo, ancestorInfo) {
moveXBlockBreadcrumbView = new MoveXBlockBreadcrumbView({}); var ancestorInfo = ancestorInfo || {ancestors: []}; // eslint-disable-line no-redeclare
moveXBlockListView = new MoveXBlockListView( modal.renderViews(courseOutlineInfo, ancestorInfo);
{
model: new XBlockInfoModel(courseOutlineInfo, {parse: true}),
ancestorInfo: ancestorInfo || {ancestors: []}
}
);
}; };
/** /**
...@@ -123,7 +174,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -123,7 +174,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
* @returns {Object} * @returns {Object}
*/ */
getDisplayedInfo = function() { getDisplayedInfo = function() {
var viewEl = moveXBlockListView.$el; var viewEl = modal.moveXBlockListView.$el;
return { return {
categoryText: viewEl.find('.category-text').text().trim(), categoryText: viewEl.find('.category-text').text().trim(),
currentLocationText: viewEl.find('.current-location').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 ...@@ -147,7 +198,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
*/ */
verifyListViewInfo = function(category, expectedXBlocksCount, hasCurrentLocation) { verifyListViewInfo = function(category, expectedXBlocksCount, hasCurrentLocation) {
var displayedInfo = getDisplayedInfo(); 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.xblockCount).toEqual(expectedXBlocksCount);
expect(displayedInfo.xblockDisplayNames).toEqual( expect(displayedInfo.xblockDisplayNames).toEqual(
_.map(_.range(expectedXBlocksCount), function(xblockIndex) { _.map(_.range(expectedXBlocksCount), function(xblockIndex) {
...@@ -174,7 +225,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -174,7 +225,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
* @param {any} xblockIndex XBlock index * @param {any} xblockIndex XBlock index
*/ */
verifyBreadcrumbViewInfo = function(category, xblockIndex) { 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(); } function() { return $(this).text().trim(); }
).get(), ).get(),
categories = _.keys(parentChildMap).concat(['component']), categories = _.keys(parentChildMap).concat(['component']),
...@@ -195,14 +246,14 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -195,14 +246,14 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
*/ */
clickForwardButton = function(buttonIndex) { clickForwardButton = function(buttonIndex) {
buttonIndex = buttonIndex || 0; // eslint-disable-line no-param-reassign 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. * Click on last clickable breadcrumb button.
*/ */
clickBreadcrumbButton = function() { 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 ...@@ -231,6 +282,7 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
verifyListViewInfo(category, expectedXBlocksCount, hasCurrentLocation); verifyListViewInfo(category, expectedXBlocksCount, hasCurrentLocation);
verifyBreadcrumbViewInfo(category, buttonIndex); verifyBreadcrumbViewInfo(category, buttonIndex);
verifyMoveEnabled(category, hasCurrentLocation);
if (direction === 'forward') { if (direction === 'forward') {
if (category === 'component') { if (category === 'component') {
...@@ -248,13 +300,162 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -248,13 +300,162 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
verifyXBlockInfo(outlineOptions, category, buttonIndex, direction, hasCurrentLocation); 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() { it('renders views with correct information', function() {
var outlineOptions = {section: 1, subsection: 1, unit: 1, component: 1}, var outlineOptions = {section: 1, subsection: 1, unit: 1, component: 1},
outline = createCourseOutline(outlineOptions); outline = createCourseOutline(outlineOptions);
renderViews(outline); renderViews(outline, xblockAncestorInfo);
verifyXBlockInfo(outlineOptions, 'section', 0, 'forward', false); verifyXBlockInfo(outlineOptions, 'section', 0, 'forward', true);
verifyXBlockInfo(outlineOptions, 'component', 0, 'backward', false); verifyXBlockInfo(outlineOptions, 'component', 0, 'backward', true);
}); });
it('shows correct behavior on breadcrumb navigation', function() { it('shows correct behavior on breadcrumb navigation', function() {
...@@ -268,43 +469,18 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -268,43 +469,18 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
_.each(['component', 'unit', 'subsection', 'section'], function(category) { _.each(['component', 'unit', 'subsection', 'section'], function(category) {
verifyListViewInfo(category, 1); verifyListViewInfo(category, 1);
if (category !== 'section') { 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() { it('shows the correct current location', function() {
var outlineOptions = {section: 2, subsection: 2, unit: 2, component: 2}, var outlineOptions = {section: 2, subsection: 2, unit: 2, component: 2},
outline = createCourseOutline(outlineOptions), outline = createCourseOutline(outlineOptions);
ancestorInfo = { renderViews(outline, xblockAncestorInfo);
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);
verifyXBlockInfo(outlineOptions, 'section', 0, 'forward', true); verifyXBlockInfo(outlineOptions, 'section', 0, 'forward', true);
// click the outline breadcrumb to render sections // 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); verifyXBlockInfo(outlineOptions, 'section', 1, 'forward', false);
}); });
...@@ -336,9 +512,114 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe ...@@ -336,9 +512,114 @@ define(['jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpe
_.each(_.range(info.forwardClicks), function() { _.each(_.range(info.forwardClicks), function() {
clickForwardButton(); clickForwardButton();
}); });
expect(moveXBlockListView.$el.find('.xblock-no-child-message').text().trim()).toEqual(info.message); expect(modal.moveXBlockListView.$el.find('.xblock-no-child-message').text().trim())
moveXBlockListView.undelegateEvents(); .toEqual(info.message);
moveXBlockBreadcrumbView.undelegateEvents(); 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 @@ ...@@ -2,25 +2,29 @@
* The MoveXblockModal to move XBlocks in course. * The MoveXblockModal to move XBlocks in course.
*/ */
define([ define([
'jquery', 'backbone', 'underscore', 'gettext', 'jquery',
'js/views/baseview', 'js/views/modals/base_modal', 'backbone',
'js/models/xblock_info', 'js/views/move_xblock_list', 'js/views/move_xblock_breadcrumb', 'underscore',
'common/js/components/views/feedback', 'gettext',
'js/views/baseview',
'js/views/utils/xblock_utils', 'js/views/utils/xblock_utils',
'js/views/utils/move_xblock_utils', 'js/views/utils/move_xblock_utils',
'edx-ui-toolkit/js/utils/html-utils', 'edx-ui-toolkit/js/utils/html-utils',
'edx-ui-toolkit/js/utils/string-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' 'text!templates/move-xblock-modal.underscore'
], ],
function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlockListView, MoveXBlockBreadcrumbView, function($, Backbone, _, gettext, BaseView, XBlockViewUtils, MoveXBlockUtils, HtmlUtils, StringUtils, Feedback,
Feedback, XBlockViewUtils, MoveXBlockUtils, HtmlUtils, StringUtils, MoveXblockModalTemplate) { XBlockInfoModel, BaseModal, MoveXBlockListView, MoveXBlockBreadcrumbView, MoveXblockModalTemplate) {
'use strict'; 'use strict';
var MoveXblockModal = BaseModal.extend({ var MoveXblockModal = BaseModal.extend({
modalSRTitle: gettext('Choose a location to move your component to'),
events: _.extend({}, BaseModal.prototype.events, { events: _.extend({}, BaseModal.prototype.events, {
'click .action-move': 'moveXBlock' 'click .action-move:not(.is-disabled)': 'moveXBlock'
}), }),
options: $.extend({}, BaseModal.prototype.options, { options: $.extend({}, BaseModal.prototype.options, {
...@@ -40,6 +44,7 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo ...@@ -40,6 +44,7 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
this.listenTo(Backbone, 'move:breadcrumbRendered', this.focusModal); this.listenTo(Backbone, 'move:breadcrumbRendered', this.focusModal);
this.sourceXBlockInfo = this.options.sourceXBlockInfo; this.sourceXBlockInfo = this.options.sourceXBlockInfo;
this.sourceParentXBlockInfo = this.options.sourceParentXBlockInfo; this.sourceParentXBlockInfo = this.options.sourceParentXBlockInfo;
this.targetParentXBlockInfo = null;
this.XBlockURLRoot = this.options.XBlockURLRoot; this.XBlockURLRoot = this.options.XBlockURLRoot;
this.XBlockAncestorInfoURL = StringUtils.interpolate( this.XBlockAncestorInfoURL = StringUtils.interpolate(
'{urlRoot}/{usageId}?fields=ancestorInfo', '{urlRoot}/{usageId}?fields=ancestorInfo',
...@@ -52,10 +57,9 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo ...@@ -52,10 +57,9 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
$('.breadcrumb-container').removeClass('is-hidden'); $('.breadcrumb-container').removeClass('is-hidden');
self.renderViews(courseOutlineInfo, ancestorInfo); self.renderViews(courseOutlineInfo, ancestorInfo);
}); });
this.targetParentXBlockInfo = null;
this.movedAlertView = null; this.movedAlertView = null;
this.moveXBlockBreadcrumbView = null; this.isValidMove = false;
this.moveXBlockListView = null; this.listenTo(Backbone, 'move:enableMoveOperation', this.enableMoveOperation);
}, },
getTitle: function() { getTitle: function() {
...@@ -71,7 +75,8 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo ...@@ -71,7 +75,8 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
show: function() { show: function() {
BaseModal.prototype.show.apply(this, [false]); BaseModal.prototype.show.apply(this, [false]);
Feedback.prototype.inFocus.apply(this, [this.options.modalWindowClass]); this.updateMoveState(false);
MoveXBlockUtils.hideMovedNotification();
}, },
hide: function() { hide: function() {
...@@ -122,18 +127,35 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo ...@@ -122,18 +127,35 @@ 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() { moveXBlock: function() {
var self = this; var self = this;
XBlockViewUtils.moveXBlock(self.sourceXBlockInfo.id, self.moveXBlockListView.parent_info.parent.id) XBlockViewUtils.moveXBlock(self.sourceXBlockInfo.id, self.targetParentXBlockInfo.id)
.done(function(response) { .done(function(response) {
if (response.move_source_locator) {
// hide modal // hide modal
self.hide(); self.hide();
// hide xblock element // hide xblock element
$("li.studio-xblock-wrapper[data-locator='" + self.sourceXBlockInfo.id + "']").hide(); $("li.studio-xblock-wrapper[data-locator='" + self.sourceXBlockInfo.id + "']").hide();
if (self.movedAlertView) {
self.movedAlertView.hide();
}
self.movedAlertView = MoveXBlockUtils.showMovedNotification( self.movedAlertView = MoveXBlockUtils.showMovedNotification(
StringUtils.interpolate( StringUtils.interpolate(
gettext('Success! "{displayName}" has been moved.'), gettext('Success! "{displayName}" has been moved.'),
...@@ -141,30 +163,14 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo ...@@ -141,30 +163,14 @@ function($, Backbone, _, gettext, BaseView, BaseModal, XBlockInfoModel, MoveXBlo
displayName: self.sourceXBlockInfo.get('display_name') 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'), sourceDisplayName: self.sourceXBlockInfo.get('display_name'),
sourceLocator: self.sourceXBlockInfo.id, sourceLocator: self.sourceXBlockInfo.id,
sourceParentLocator: self.sourceParentXBlockInfo.id, sourceParentLocator: self.sourceParentXBlockInfo.id,
targetIndex: response.source_index, targetParentLocator: response.parent_locator,
undoMove: gettext('Undo move') targetIndex: response.source_index
} }
)
); );
}
}); });
} }
}); });
......
...@@ -63,6 +63,7 @@ function($, Backbone, _, gettext, HtmlUtils, StringUtils, XBlockUtils, MoveXBloc ...@@ -63,6 +63,7 @@ function($, Backbone, _, gettext, HtmlUtils, StringUtils, XBlockUtils, MoveXBloc
) )
); );
Backbone.trigger('move:childrenRendered', this.breadcrumbInfo()); Backbone.trigger('move:childrenRendered', this.breadcrumbInfo());
Backbone.trigger('move:enableMoveOperation', this.parentInfo.parent);
return this; return this;
}, },
......
...@@ -198,7 +198,7 @@ define(['jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'common/j ...@@ -198,7 +198,7 @@ define(['jquery', 'underscore', 'gettext', 'js/views/pages/base_page', 'common/j
modal = new MoveXBlockModal({ modal = new MoveXBlockModal({
sourceXBlockInfo: XBlockUtils.findXBlockInfo(xblockElement, this.model), sourceXBlockInfo: XBlockUtils.findXBlockInfo(xblockElement, this.model),
sourceParentXBlockInfo: XBlockUtils.findXBlockInfo(parentXBlockElement, this.model), sourceParentXBlockInfo: XBlockUtils.findXBlockInfo(parentXBlockElement, this.model),
XBlockUrlRoot: this.getURLRoot(), XBlockURLRoot: this.getURLRoot(),
outlineURL: this.options.outlineURL outlineURL: this.options.outlineURL
}); });
......
/** /**
* Provides utilities for move xblock. * Provides utilities for move xblock.
*/ */
define(['jquery', 'underscore', 'common/js/components/views/feedback_alert', 'js/views/utils/xblock_utils', define([
'js/views/utils/move_xblock_utils', 'edx-ui-toolkit/js/utils/string-utils'], 'jquery',
function($, _, AlertView, XBlockViewUtils, MoveXBlockUtils, StringUtils) { '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'; 'use strict';
var MovedAlertView, showMovedNotification; var redirectLink, undoMoveXBlock, showMovedNotification, hideMovedNotification;
MovedAlertView = AlertView.Confirmation.extend({ redirectLink = function(link) {
events: _.extend({}, AlertView.Confirmation.prototype.events, { window.location.href = link;
'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) { undoMoveXBlock = function(data) {
var self = this, XBlockViewUtils.moveXBlock(data.sourceLocator, data.sourceParentLocator, data.targetIndex)
$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) { .done(function(response) {
// show XBlock element // show XBlock element
$('.studio-xblock-wrapper[data-locator="' + response.move_source_locator + '"]').show(); $('.studio-xblock-wrapper[data-locator="' + response.move_source_locator + '"]').show();
if (self.movedAlertView) { showMovedNotification(
self.movedAlertView.hide();
}
self.movedAlertView = showMovedNotification(
StringUtils.interpolate( StringUtils.interpolate(
gettext('Move cancelled. "{sourceDisplayName}" has been moved back to its original ' + gettext('Move cancelled. "{sourceDisplayName}" has been moved back to its original location.'),
'location.'),
{ {
sourceDisplayName: sourceDisplayName sourceDisplayName: data.sourceDisplayName
} }
) )
); );
}); });
} };
});
showMovedNotification = function(title, titleHtml, messageHtml) { showMovedNotification = function(title, data) {
var movedAlertView = new MovedAlertView({ var movedAlertView;
// data is provided when we click undo move button.
if (data) {
movedAlertView = new AlertView.Confirmation({
title: title, title: title,
titleHtml: titleHtml, actions: {
messageHtml: messageHtml, primary: {
maxShown: 10000 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);
}
}
]
}
}); });
} else {
movedAlertView = new AlertView.Confirmation({
title: title
});
}
movedAlertView.show(); movedAlertView.show();
// scroll to top // scroll to top
$.smoothScroll({ $.smoothScroll({
...@@ -60,10 +87,20 @@ define(['jquery', 'underscore', 'common/js/components/views/feedback_alert', 'js ...@@ -60,10 +87,20 @@ define(['jquery', 'underscore', 'common/js/components/views/feedback_alert', 'js
easing: 'swing', easing: 'swing',
speed: 1000 speed: 1000
}); });
movedAlertView.$('.wrapper').first().focus();
return movedAlertView; return movedAlertView;
}; };
hideMovedNotification = function() {
var movedAlertView = Feedback.active_alert;
if (movedAlertView) {
AlertView.prototype.hide.apply(movedAlertView);
}
};
return { return {
showMovedNotification: showMovedNotification redirectLink: redirectLink,
showMovedNotification: showMovedNotification,
hideMovedNotification: hideMovedNotification
}; };
}); });
...@@ -94,10 +94,10 @@ define(['jquery', 'underscore', 'gettext', 'common/js/components/utils/view_util ...@@ -94,10 +94,10 @@ define(['jquery', 'underscore', 'gettext', 'common/js/components/utils/view_util
/** /**
* Moves the specified xblock in a new parent xblock. * Moves the specified xblock in a new parent xblock.
* @param {String} sourceLocator The xblock element to be moved. * @param {String} sourceLocator Locator of xblock element to be moved.
* @param {String} targetParentLocator Target parent xblock locator of the xblock to be moved, * @param {String} targetParentLocator Locator of the target parent xblock, moved xblock would be placed
* new moved xblock would be placed under this xblock. * under this xblock.
* @param {String} targetIndex Intended index position of the xblock in parent xblock. If provided, * @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. * xblock would be placed at the particular index in the parent xblock.
* @returns {jQuery promise} A promise representing the moving of the 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 ...@@ -110,8 +110,8 @@ define(['jquery', 'underscore', 'gettext', 'common/js/components/utils/view_util
move_source_locator: sourceLocator, move_source_locator: sourceLocator,
parent_locator: targetParentLocator, parent_locator: targetParentLocator,
target_index: targetIndex target_index: targetIndex
}, function(data) { }, function(response) {
moveOperation.resolve(data); moveOperation.resolve(response);
}) })
.fail(function() { .fail(function() {
moveOperation.reject(); moveOperation.reject();
......
...@@ -297,6 +297,11 @@ ...@@ -297,6 +297,11 @@
.ui-loading { .ui-loading {
box-shadow: none; box-shadow: none;
} }
.modal-actions .action-move.is-disabled {
border: 1px solid $gray-l1 !important;
background: $gray-l1 !important;
}
} }
// upload modal // upload modal
......
...@@ -21,8 +21,6 @@ ...@@ -21,8 +21,6 @@
options: { options: {
title: '', title: '',
message: '', 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 intent: null, // "warning", "confirmation", "error", "announcement", "step-required", etc
type: null, // "alert", "notification", or "prompt": set by subclass type: null, // "alert", "notification", or "prompt": set by subclass
shown: true, // is this view currently being shown? shown: true, // is this view currently being shown?
......
/**
* 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 @@ ...@@ -15,9 +15,8 @@
<% } %> <% } %>
<div class="copy"> <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(obj.message) { %><p class="message" id="<%= type %>-<%= intent %>-description"><%- message %></p><% } %>
<% if(messageHtml) { %> <%= messageHtml %> <% } %>
</div> </div>
<% if(obj.actions) { %> <% if(obj.actions) { %>
......
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