diff --git a/cms/djangoapps/contentstore/features/course-outline.py b/cms/djangoapps/contentstore/features/course-outline.py index 865a5ad5c2..8786cfcaf1 100644 --- a/cms/djangoapps/contentstore/features/course-outline.py +++ b/cms/djangoapps/contentstore/features/course-outline.py @@ -69,7 +69,7 @@ def i_add_a_section(step): @step(u'I press the "section" delete icon') def i_press_the_section_delete_icon(step): - delete_locator = 'section .outline-section > .wrapper-xblock-header a.delete-button' + delete_locator = 'section .outline-section > .section-header a.delete-button' world.css_click(delete_locator) @@ -82,27 +82,27 @@ def i_confirm_all_alerts(step): @step(u'I see the "([^"]*) All Sections" link$') def i_see_the_collapse_expand_all_span(step, text): if text == "Collapse": - span_locator = '.toggle-button-expand-collapse .collapse-all .label' + span_locator = '.button-toggle-expand-collapse .collapse-all .label' elif text == "Expand": - span_locator = '.toggle-button-expand-collapse .expand-all .label' + span_locator = '.button-toggle-expand-collapse .expand-all .label' assert_true(world.css_visible(span_locator)) @step(u'I do not see the "([^"]*) All Sections" link$') def i_do_not_see_the_collapse_expand_all_span(step, text): if text == "Collapse": - span_locator = '.toggle-button-expand-collapse .collapse-all .label' + span_locator = '.button-toggle-expand-collapse .collapse-all .label' elif text == "Expand": - span_locator = '.toggle-button-expand-collapse .expand-all .label' + span_locator = '.button-toggle-expand-collapse .expand-all .label' assert_false(world.css_visible(span_locator)) @step(u'I click the "([^"]*) All Sections" link$') def i_click_the_collapse_expand_all_span(step, text): if text == "Collapse": - span_locator = '.toggle-button-expand-collapse .collapse-all .label' + span_locator = '.button-toggle-expand-collapse .collapse-all .label' elif text == "Expand": - span_locator = '.toggle-button-expand-collapse .expand-all .label' + span_locator = '.button-toggle-expand-collapse .expand-all .label' assert_true(world.browser.is_element_present_by_css(span_locator)) world.css_click(span_locator) diff --git a/cms/static/js/views/course_outline.js b/cms/static/js/views/course_outline.js index 72f61aae8d..88f787055d 100644 --- a/cms/static/js/views/course_outline.js +++ b/cms/static/js/views/course_outline.js @@ -46,7 +46,7 @@ define(["jquery", "underscore", "js/views/xblock_outline", "js/views/utils/view_ var expandedLocators = []; this.$('.outline-item.is-collapsible').each(function(index, rawElement) { var element = $(rawElement); - if (!element.hasClass('collapsed')) { + if (!element.hasClass('is-collapsed')) { expandedLocators.push(element.data('locator')); } }); diff --git a/cms/static/js/views/xblock_string_field_editor.js b/cms/static/js/views/xblock_string_field_editor.js index 4002bb0be9..9e52c55e52 100644 --- a/cms/static/js/views/xblock_string_field_editor.js +++ b/cms/static/js/views/xblock_string_field_editor.js @@ -13,6 +13,7 @@ define(["js/views/baseview", "js/views/utils/xblock_utils"], 'click .xblock-field-value-edit': 'showInput', 'click button[name=submit]': 'onClickSubmit', 'click button[name=cancel]': 'onClickCancel', + 'click .xblock-string-field-editor': 'onClickEditor', 'change .xblock-field-input': 'updateField', 'focusout .xblock-field-input': 'onInputFocusLost', 'keyup .xblock-field-input': 'handleKeyUp' @@ -54,14 +55,20 @@ define(["js/views/baseview", "js/views/utils/xblock_utils"], onClickSubmit: function(event) { event.preventDefault(); + event.stopPropagation(); this.updateField(); }, onClickCancel: function(event) { event.preventDefault(); + event.stopPropagation(); this.cancelInput(); }, + onClickEditor: function(event) { + event.stopPropagation(); + }, + onChangeField: function() { var value = this.model.get(this.fieldName); this.getLabel().text(value); @@ -72,6 +79,7 @@ define(["js/views/baseview", "js/views/utils/xblock_utils"], showInput: function(event) { var input = this.getInput(); event.preventDefault(); + event.stopPropagation(); this.$el.addClass('is-editing'); input.focus(); }, diff --git a/common/test/acceptance/pages/studio/overview.py b/common/test/acceptance/pages/studio/overview.py index 0354aa05b5..d02bad9c5d 100644 --- a/common/test/acceptance/pages/studio/overview.py +++ b/common/test/acceptance/pages/studio/overview.py @@ -6,7 +6,7 @@ from bok_choy.promise import EmptyPromise from .course_page import CoursePage from .container import ContainerPage -from .utils import set_input_value_and_save, click_css, confirm_prompt +from .utils import set_input_value_and_save, click_css, confirm_prompt, set_input_value class CourseOutlineItem(object): @@ -46,11 +46,23 @@ class CourseOutlineItem(object): else: return None + def edit_name(self): + """ + Puts the item's name into editable form. + """ + self.q(css=self._bounded_selector(self.EDIT_BUTTON_SELECTOR)).first.click() + + def enter_name(self, new_name): + """ + Enters new_name as the item's display name. + """ + set_input_value(self, self._bounded_selector(self.NAME_INPUT_SELECTOR), new_name) + def change_name(self, new_name): """ Changes the container's name. """ - self.q(css=self._bounded_selector(self.EDIT_BUTTON_SELECTOR)).first.click() + self.edit_name() set_input_value_and_save(self, self._bounded_selector(self.NAME_INPUT_SELECTOR), new_name) self.wait_for_ajax() @@ -130,7 +142,7 @@ class CourseOutlineContainer(CourseOutlineItem): currently_expanded = subsection_expanded() - self.q(css=self._bounded_selector('.ui-toggle-expansion')).first.click() + self.q(css=self._bounded_selector('.ui-toggle-expansion i')).first.click() EmptyPromise( lambda: subsection_expanded() != currently_expanded, @@ -144,7 +156,7 @@ class CourseOutlineContainer(CourseOutlineItem): """ Return whether this outline item is currently collapsed. """ - return "collapsed" in self.q(css=self._bounded_selector('')).first.attrs("class")[0] + return "is-collapsed" in self.q(css=self._bounded_selector('')).first.attrs("class")[0] class CourseOutlineChild(PageObject, CourseOutlineItem): diff --git a/common/test/acceptance/pages/studio/utils.py b/common/test/acceptance/pages/studio/utils.py index 92db97bfd6..7bb43b2b10 100644 --- a/common/test/acceptance/pages/studio/utils.py +++ b/common/test/acceptance/pages/studio/utils.py @@ -139,14 +139,3 @@ def set_input_value_and_save(page, css, value): action = action.send_keys(Keys.BACKSPACE) # Send the new text, then hit the enter key so that the change event is triggered). action.send_keys(value).send_keys(Keys.ENTER).perform() - - -def confirm_prompt(page, cancel=False): - """ - Ensures that a modal prompt and confirmation button are visible, then clicks the button. The prompt is canceled iff - cancel is True. - """ - page.wait_for_element_visibility('.prompt', 'Prompt is visible') - confirmation_button_css = '.prompt .action-' + ('secondary' if cancel else 'primary') - page.wait_for_element_visibility(confirmation_button_css, 'Confirmation button is visible') - click_css(page, confirmation_button_css, require_notification=(not cancel)) diff --git a/common/test/acceptance/tests/test_studio_outline.py b/common/test/acceptance/tests/test_studio_outline.py index d08ff59bd4..eae6f8b317 100644 --- a/common/test/acceptance/tests/test_studio_outline.py +++ b/common/test/acceptance/tests/test_studio_outline.py @@ -131,6 +131,25 @@ class EditNamesTest(CourseOutlineTest): 'Test Subsection' ) + def test_editing_names_does_not_expand_collapse(self): + """ + Scenario: A section stays in the same expand/collapse state while its name is edited + Given that I have created a section + And the section is expanded + When I click on the name of the section + Then the section is expanded + And given that I have entered a new name + Then the section is expanded + """ + self.course_outline_page.visit() + self.assertFalse(self.course_outline_page.section_at(0).in_editable_form()) + self.assertFalse(self.course_outline_page.section_at(0).is_collapsed) + self.course_outline_page.section_at(0).edit_name() + self.assertTrue(self.course_outline_page.section_at(0).in_editable_form()) + self.assertFalse(self.course_outline_page.section_at(0).is_collapsed) + self.course_outline_page.section_at(0).enter_name('Changed') + self.assertFalse(self.course_outline_page.section_at(0).is_collapsed) + class CreateSectionsTest(CourseOutlineTest): """ @@ -469,6 +488,22 @@ class ExpandCollapseSingleSectionTest(CourseOutlineTest): self.assertEquals(self.course_outline_page.expand_collapse_link_state, ExpandCollapseLinkState.MISSING) self.assertTrue(self.course_outline_page.has_no_content_message) + def test_old_subsection_stays_collapsed_after_creation(self): + """ + Scenario: Collapsed subsection stays collapsed after creating a new subsection + Given I have a course with one section and subsection + And I navigate to the course outline page + Then the subsection is collapsed + And when I create a new subsection + Then the first subsection is collapsed + And the second subsection is expanded + """ + self.course_outline_page.visit() + self.assertTrue(self.course_outline_page.section_at(0).subsection_at(0).is_collapsed) + self.course_outline_page.section_at(0).add_subsection() + self.assertTrue(self.course_outline_page.section_at(0).subsection_at(0).is_collapsed) + self.assertFalse(self.course_outline_page.section_at(0).subsection_at(1).is_collapsed) + class ExpandCollapseEmptyTest(CourseOutlineTest): """