diff --git a/cms/djangoapps/contentstore/views/tests/test_course_index.py b/cms/djangoapps/contentstore/views/tests/test_course_index.py index d444742a7c..90d20e9a34 100644 --- a/cms/djangoapps/contentstore/views/tests/test_course_index.py +++ b/cms/djangoapps/contentstore/views/tests/test_course_index.py @@ -259,7 +259,7 @@ class TestCourseOutline(CourseTestCase): def test_course_outline_initial_state(self): course_module = modulestore().get_item(self.course.location) course_structure = create_xblock_info( - course_module, + course_module, include_child_info=True, include_children_predicate=lambda xblock: not xblock.category == 'vertical' ) diff --git a/cms/djangoapps/contentstore/views/tests/test_item.py b/cms/djangoapps/contentstore/views/tests/test_item.py index 8f1afc7820..0f052ea321 100644 --- a/cms/djangoapps/contentstore/views/tests/test_item.py +++ b/cms/djangoapps/contentstore/views/tests/test_item.py @@ -1190,7 +1190,7 @@ class TestXBlockInfo(ItemTest): for child_response in xblock_info['child_info']['children']: self.validate_xblock_info_consistency( child_response, - has_child_info=(not child_response.get('child_info', None) == None) + has_child_info=(not child_response.get('child_info', None) is None) ) else: self.assertIsNone(xblock_info.get('child_info', None)) diff --git a/cms/static/js/spec/views/pages/course_outline_spec.js b/cms/static/js/spec/views/pages/course_outline_spec.js index f69fd182c2..c78a14df38 100644 --- a/cms/static/js/spec/views/pages/course_outline_spec.js +++ b/cms/static/js/spec/views/pages/course_outline_spec.js @@ -245,6 +245,22 @@ define(["jquery", "js/spec_helpers/create_sinon", "js/spec_helpers/view_helpers" describe("Section", function() { it('can be deleted', function() { + var promptSpy = view_helpers.createPromptSpy(), requestCount; + createCourseOutlinePage(this, createMockCourseJSON('mock-course', 'Mock Course', [ + createMockSectionJSON('mock-section', 'Mock Section', []), + createMockSectionJSON('mock-section-2', 'Mock Section 2', []) + ])); + outlinePage.$('.outline-item-section .delete-button').first().click(); + view_helpers.confirmPrompt(promptSpy); + requestCount = requests.length; + create_sinon.expectJsonRequest(requests, 'DELETE', '/xblock/mock-section'); + create_sinon.respondWithJson(requests, {}); + expect(requests.length).toBe(requestCount); // No fetch should be performed + expect(outlinePage.$('[data-locator="mock-section"]')).not.toExist(); + expect(outlinePage.$('[data-locator="mock-section-2"]')).toExist(); + }); + + it('can be deleted if it is the only section', function() { var promptSpy = view_helpers.createPromptSpy(); createCourseOutlinePage(this, mockSingleSectionCourseJSON); outlinePage.$('.outline-item-section .delete-button').click(); diff --git a/cms/static/js/views/course_outline.js b/cms/static/js/views/course_outline.js index 4f0725fa49..caa0d845bc 100644 --- a/cms/static/js/views/course_outline.js +++ b/cms/static/js/views/course_outline.js @@ -117,6 +117,19 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ } }, + onChildDeleted: function(childView) { + var xblockInfo = this.model, + childCategory = childView.model.get('category'), + children = xblockInfo.get('child_info') && xblockInfo.get('child_info').children; + // If deleting a section that isn't the final one, just remove it for efficiency + // as it cannot visually effect the other sections. + if (childCategory === 'chapter' && children && children.length > 1) { + childView.$el.remove(); + } else { + this.refresh(); + } + }, + createNewItemViewState: function(locator, scrollOffset) { return { locator_to_show: locator, diff --git a/cms/static/js/views/xblock_outline.js b/cms/static/js/views/xblock_outline.js index b0a0a9f441..a342743270 100644 --- a/cms/static/js/views/xblock_outline.js +++ b/cms/static/js/views/xblock_outline.js @@ -135,10 +135,7 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ */ addButtonActions: function(element) { var self = this; - element.find('.delete-button').click(function(event) { - event.preventDefault(); - self.deleteXBlock($(event.target)); - }); + element.find('.delete-button').click(_.bind(this.handleDeleteEvent, this)); element.find('.add-button').click(_.bind(this.handleAddEvent, this)); }, @@ -203,6 +200,14 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ this.initialState = null; }, + /** + * Refresh the view's model from the server, which will cause the view to refresh. + * @returns {jQuery promise} A promise representing the refresh operation. + */ + refresh: function() { + return this.model.fetch(); + }, + onChildAdded: function(locator, category) { // For units, redirect to the new page, and for everything else just refresh inline. if (category === 'vertical') { @@ -220,23 +225,17 @@ define(["jquery", "underscore", "gettext", "js/views/baseview", "js/views/utils/ this.refresh(); }, - deleteXBlock: function() { - var parentView = this.parentView; + handleDeleteEvent: function(event) { + var self = this, + parentView = this.parentView; + event.preventDefault(); XBlockViewUtils.deleteXBlock(this.model).done(function() { if (parentView) { - parentView.onChildDeleted(); + parentView.onChildDeleted(self, event); } }); }, - /** - * Refresh the view's model from the server, which will cause the view to refresh. - * @returns {jQuery promise} A promise representing the refresh operation. - */ - refresh: function() { - return this.model.fetch(); - }, - handleAddEvent: function(event) { var self = this, target = $(event.target),