From 5b545c2b0d546e62f3d65959dbd9a11f5c4a7c91 Mon Sep 17 00:00:00 2001 From: clrux Date: Fri, 28 Aug 2015 13:28:08 -0400 Subject: [PATCH] Revert "Course navigation menu accessibility issue" --- .../test/acceptance/pages/lms/course_nav.py | 20 +- .../test/acceptance/pages/lms/courseware.py | 4 +- common/test/acceptance/tests/lms/test_lms.py | 2 +- .../courseware/features/navigation.py | 2 +- lms/djangoapps/courseware/module_render.py | 3 - .../courseware/tests/test_entrance_exam.py | 15 +- .../courseware/tests/test_module_render.py | 9 +- .../courseware/tests/test_navigation.py | 1 + lms/djangoapps/courseware/testutils.py | 1 + lms/static/coffee/fixtures/accordion.html | 6 + lms/static/coffee/spec/courseware_spec.coffee | 5 + lms/static/coffee/spec/navigation_spec.coffee | 72 ++++ lms/static/coffee/src/courseware.coffee | 1 + lms/static/coffee/src/navigation.coffee | 33 ++ lms/static/js/fixtures/accordion.html | 56 --- lms/static/js/spec/main.js | 3 - lms/static/js/spec/navigation_spec.js | 74 ---- lms/static/js/utils/navigation.js | 70 ---- .../sass/course/courseware/_courseware.scss | 7 +- .../sass/course/courseware/_sidebar.scss | 379 +++++++++++------- lms/templates/courseware/accordion.html | 87 ++-- lms/templates/courseware/courseware.html | 7 +- .../instructor_dashboard_2/e-commerce.html | 4 +- lms/templates/main.html | 1 - lms/templates/staticbook.html | 3 + 25 files changed, 440 insertions(+), 425 deletions(-) create mode 100644 lms/static/coffee/fixtures/accordion.html create mode 100644 lms/static/coffee/spec/navigation_spec.coffee create mode 100644 lms/static/coffee/src/navigation.coffee delete mode 100644 lms/static/js/fixtures/accordion.html delete mode 100644 lms/static/js/spec/navigation_spec.js delete mode 100644 lms/static/js/utils/navigation.js diff --git a/common/test/acceptance/pages/lms/course_nav.py b/common/test/acceptance/pages/lms/course_nav.py index e204260bbf..a7bde281a7 100644 --- a/common/test/acceptance/pages/lms/course_nav.py +++ b/common/test/acceptance/pages/lms/course_nav.py @@ -82,7 +82,7 @@ class CourseNavPage(PageObject): # Click the section to ensure it's open (no harm in clicking twice if it's already open) # Add one to convert from list index to CSS index - section_css = '.course-navigation .chapter:nth-of-type({0})'.format(sec_index + 1) + section_css = 'nav>div.chapter:nth-of-type({0})>h3>a'.format(sec_index + 1) self.q(css=section_css).first.click() # Get the subsection by index @@ -94,10 +94,9 @@ class CourseNavPage(PageObject): return # Convert list indices (start at zero) to CSS indices (start at 1) - subsection_css = ( - ".course-navigation .chapter-content-container:nth-of-type({0}) " - ".chapter-menu .menu-item:nth-of-type({1})" - ).format(sec_index + 1, subsec_index + 1) + subsection_css = "nav>div.chapter:nth-of-type({0})>ul>li:nth-of-type({1})>a".format( + sec_index + 1, subsec_index + 1 + ) # Click the subsection and ensure that the page finishes reloading self.q(css=subsection_css).first.click() @@ -131,7 +130,7 @@ class CourseNavPage(PageObject): """ Return a list of all section titles on the page. """ - chapter_css = '.course-navigation .chapter .group-heading' + chapter_css = 'nav > div.chapter > h3 > a' return self.q(css=chapter_css).map(lambda el: el.text.strip()).results def _subsection_titles(self, section_index): @@ -141,10 +140,7 @@ class CourseNavPage(PageObject): """ # Retrieve the subsection title for the section # Add one to the list index to get the CSS index, which starts at one - subsection_css = ( - ".course-navigation .chapter-content-container:nth-of-type({0}) " - ".chapter-menu .menu-item a p:nth-of-type(1)" - ).format(section_index) + subsection_css = 'nav>div.chapter:nth-of-type({0})>ul>li>a>p:nth-of-type(1)'.format(section_index) # If the element is visible, we can get its text directly # Otherwise, we need to get the HTML @@ -175,8 +171,8 @@ class CourseNavPage(PageObject): That's true right after we click the section/subsection, but not true in general (the user could go to a section, then expand another tab). """ - current_section_list = self.q(css='.course-navigation .chapter.is-open .group-heading').text - current_subsection_list = self.q(css='.course-navigation .chapter-content-container .menu-item.active a p').text + current_section_list = self.q(css='nav>div.chapter.is-open>h3>a').text + current_subsection_list = self.q(css='nav>div.chapter.is-open li.active>a>p').text if len(current_section_list) == 0: self.warning("Could not find the current section") diff --git a/common/test/acceptance/pages/lms/courseware.py b/common/test/acceptance/pages/lms/courseware.py index 6f61da75d0..bb842682d8 100644 --- a/common/test/acceptance/pages/lms/courseware.py +++ b/common/test/acceptance/pages/lms/courseware.py @@ -14,7 +14,7 @@ class CoursewarePage(CoursePage): url_path = "courseware/" xblock_component_selector = '.vert .xblock' section_selector = '.chapter' - subsection_selector = '.chapter-content-container .chapter-menu a' + subsection_selector = '.chapter ul li' def is_browser_on_page(self): return self.q(css='body.courseware').present @@ -102,7 +102,7 @@ class CoursewarePage(CoursePage): """ return the url of the active subsection in the left nav """ - return self.q(css='.chapter-content-container .chapter-menu .menu-item.active a').attrs('href')[0] + return self.q(css='.chapter ul li.active a').attrs('href')[0] @property def can_start_proctored_exam(self): diff --git a/common/test/acceptance/tests/lms/test_lms.py b/common/test/acceptance/tests/lms/test_lms.py index 7a945b74ed..ca0a6a260e 100644 --- a/common/test/acceptance/tests/lms/test_lms.py +++ b/common/test/acceptance/tests/lms/test_lms.py @@ -1121,7 +1121,7 @@ class EntranceExamTest(UniqueCourseTest): When I view the courseware that has an entrance exam Then there should be an "Entrance Exam" chapter.' """ - entrance_exam_link_selector = '.accordion .course-navigation .chapter .group-heading' + entrance_exam_link_selector = 'div#accordion nav div h3 a' # visit courseware page and make sure there is not entrance exam chapter. self.courseware_page.visit() self.courseware_page.wait_for_page() diff --git a/lms/djangoapps/courseware/features/navigation.py b/lms/djangoapps/courseware/features/navigation.py index 1b3613fe07..72eb109a7a 100644 --- a/lms/djangoapps/courseware/features/navigation.py +++ b/lms/djangoapps/courseware/features/navigation.py @@ -92,7 +92,7 @@ def when_i_navigate_to_a_section(step): world.disable_jquery_animations() # Open the 2nd section - world.css_click(css_selector='button.chapter', index=1) + world.css_click(css_selector='div.chapter', index=1) subsection_css = 'a[href*="Test_Subsection_2/"]' # Click on the subsection to see the content diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 43b9866782..ddb321e4ed 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -74,7 +74,6 @@ from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.x_module import XModuleDescriptor from xmodule.mixin import wrap_with_license from util.json_request import JsonResponse -from util.model_utils import slugify from util.sandboxing import can_execute_unsafe_code, get_python_lib_zip from util import milestones_helpers from verify_student.services import ReverificationService @@ -166,7 +165,6 @@ def toc_for_course(user, request, course, active_chapter, active_section, field_ for chapter in chapters: # Only show required content, if there is required content # chapter.hide_from_toc is read-only (boo) - display_id = slugify(chapter.display_name_with_default) local_hide_from_toc = False if required_content: if unicode(chapter.location) not in required_content: @@ -248,7 +246,6 @@ def toc_for_course(user, request, course, active_chapter, active_section, field_ sections.append(section_context) toc_chapters.append({ 'display_name': chapter.display_name_with_default, - 'display_id': display_id, 'url_name': chapter.url_name, 'sections': sections, 'active': chapter.url_name == active_chapter diff --git a/lms/djangoapps/courseware/tests/test_entrance_exam.py b/lms/djangoapps/courseware/tests/test_entrance_exam.py index ae1fd8622a..636dcc1e3b 100644 --- a/lms/djangoapps/courseware/tests/test_entrance_exam.py +++ b/lms/djangoapps/courseware/tests/test_entrance_exam.py @@ -155,8 +155,7 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase): } ], 'url_name': u'Entrance_Exam_Section_-_Chapter_1', - 'display_name': u'Entrance Exam Section - Chapter 1', - 'display_id': u'entrance-exam-section-chapter-1', + 'display_name': u'Entrance Exam Section - Chapter 1' } ] ) @@ -183,22 +182,19 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase): } ], 'url_name': u'Overview', - 'display_name': u'Overview', - 'display_id': u'overview' + 'display_name': u'Overview' }, { 'active': False, 'sections': [], 'url_name': u'Week_1', - 'display_name': u'Week 1', - 'display_id': u'week-1' + 'display_name': u'Week 1' }, { 'active': False, 'sections': [], 'url_name': u'Instructor', - 'display_name': u'Instructor', - 'display_id': u'instructor' + 'display_name': u'Instructor' }, { 'active': True, @@ -213,8 +209,7 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase): } ], 'url_name': u'Entrance_Exam_Section_-_Chapter_1', - 'display_name': u'Entrance Exam Section - Chapter 1', - 'display_id': u'entrance-exam-section-chapter-1' + 'display_name': u'Entrance Exam Section - Chapter 1' } ] ) diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index 3f4da9b8a0..8f8849a100 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -633,7 +633,6 @@ class TestTOC(ModuleStoreTestCase): def test_toc_toy_from_chapter(self, default_ms, setup_finds, setup_sends, toc_finds): with self.store.default_store(default_ms): self.setup_modulestore(default_ms, setup_finds, setup_sends) - expected = ([{'active': True, 'sections': [{'url_name': 'Toy_Videos', 'display_name': u'Toy Videos', 'graded': True, 'format': u'Lecture Sequence', 'due': None, 'active': False}, @@ -643,11 +642,11 @@ class TestTOC(ModuleStoreTestCase): 'format': '', 'due': None, 'active': False}, {'url_name': 'video_4f66f493ac8f', 'display_name': 'Video', 'graded': True, 'format': '', 'due': None, 'active': False}], - 'url_name': 'Overview', 'display_name': u'Overview', 'display_id': u'overview'}, + 'url_name': 'Overview', 'display_name': u'Overview'}, {'active': False, 'sections': [{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True, 'format': '', 'due': None, 'active': False}], - 'url_name': 'secret:magic', 'display_name': 'secret:magic', 'display_id': 'secretmagic'}]) + 'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) course = self.store.get_course(self.toy_course.id, depth=2) with check_mongo_calls(toc_finds): @@ -683,11 +682,11 @@ class TestTOC(ModuleStoreTestCase): 'format': '', 'due': None, 'active': False}, {'url_name': 'video_4f66f493ac8f', 'display_name': 'Video', 'graded': True, 'format': '', 'due': None, 'active': False}], - 'url_name': 'Overview', 'display_name': u'Overview', 'display_id': u'overview'}, + 'url_name': 'Overview', 'display_name': u'Overview'}, {'active': False, 'sections': [{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True, 'format': '', 'due': None, 'active': False}], - 'url_name': 'secret:magic', 'display_name': 'secret:magic', 'display_id': 'secretmagic'}]) + 'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) with check_mongo_calls(toc_finds): actual = render.toc_for_course( diff --git a/lms/djangoapps/courseware/tests/test_navigation.py b/lms/djangoapps/courseware/tests/test_navigation.py index 0e669ed9ae..ec8b15ff5d 100644 --- a/lms/djangoapps/courseware/tests/test_navigation.py +++ b/lms/djangoapps/courseware/tests/test_navigation.py @@ -107,6 +107,7 @@ class TestNavigation(ModuleStoreTestCase, LoginEnrollmentTestCase): 'chapter': 'Chrome', 'section': displayname, })) + self.assertEquals('open_close_accordion' in response.content, accordion) self.assertEquals('course-tabs' in response.content, tabs) self.assertTabInactive('progress', response) diff --git a/lms/djangoapps/courseware/testutils.py b/lms/djangoapps/courseware/testutils.py index bd0c804f1b..b0e4e8b31f 100644 --- a/lms/djangoapps/courseware/testutils.py +++ b/lms/djangoapps/courseware/testutils.py @@ -25,6 +25,7 @@ class RenderXBlockTestMixin(object): # DOM elements that appear in the LMS Courseware, # but are excluded from the xBlock-only rendering. COURSEWARE_CHROME_HTML_ELEMENTS = [ + '
+
+ close +
+
+ diff --git a/lms/static/coffee/spec/courseware_spec.coffee b/lms/static/coffee/spec/courseware_spec.coffee index 129b4308a6..c6b0c605c7 100644 --- a/lms/static/coffee/spec/courseware_spec.coffee +++ b/lms/static/coffee/spec/courseware_spec.coffee @@ -1,5 +1,10 @@ describe 'Courseware', -> describe 'start', -> + it 'create the navigation', -> + spyOn(window, 'Navigation') + Courseware.start() + expect(window.Navigation).toHaveBeenCalled() + it 'binds the Logger', -> spyOn(Logger, 'bind') Courseware.start() diff --git a/lms/static/coffee/spec/navigation_spec.coffee b/lms/static/coffee/spec/navigation_spec.coffee new file mode 100644 index 0000000000..162eff3f2f --- /dev/null +++ b/lms/static/coffee/spec/navigation_spec.coffee @@ -0,0 +1,72 @@ +describe 'Navigation', -> + beforeEach -> + loadFixtures 'coffee/fixtures/accordion.html' + @navigation = new Navigation + + describe 'constructor', -> + describe 'when the #accordion exists', -> + describe 'when there is an active section', -> + beforeEach -> + spyOn $.fn, 'accordion' + $('#accordion').append('') + new Navigation + + it 'activate the accordion with correct active section', -> + expect($('#accordion').accordion).toHaveBeenCalledWith + active: 1 + header: 'h3' + autoHeight: false + heightStyle: 'content' + + describe 'when there is no active section', -> + beforeEach -> + spyOn $.fn, 'accordion' + $('#accordion').append('') + new Navigation + + it 'activate the accordian with no section as active', -> + expect($('#accordion').accordion).toHaveBeenCalledWith + active: 0 + header: 'h3' + autoHeight: false + heightStyle: 'content' + + it 'binds the accordionchange event', -> + expect($('#accordion')).toHandleWith 'accordionchange', @navigation.log + + it 'bind the navigation toggle', -> + expect($('#open_close_accordion a')).toHandleWith 'click', @navigation.toggle + + describe 'when the #accordion does not exists', -> + beforeEach -> + $('#accordion').remove() + + it 'does not activate the accordion', -> + spyOn $.fn, 'accordion' + expect($('#accordion').accordion).wasNotCalled() + + describe 'toggle', -> + it 'toggle closed class on the wrapper', -> + $('.course-wrapper').removeClass('closed') + + @navigation.toggle() + expect($('.course-wrapper')).toHaveClass('closed') + + @navigation.toggle() + expect($('.course-wrapper')).not.toHaveClass('closed') + + describe 'log', -> + beforeEach -> + spyOn Logger, 'log' + + it 'submit event log', -> + @navigation.log {}, { + newHeader: + text: -> "new" + oldHeader: + text: -> "old" + } + + expect(Logger.log).toHaveBeenCalledWith 'accordion', + newheader: 'new' + oldheader: 'old' diff --git a/lms/static/coffee/src/courseware.coffee b/lms/static/coffee/src/courseware.coffee index 49e27c484b..50fcb3ecec 100644 --- a/lms/static/coffee/src/courseware.coffee +++ b/lms/static/coffee/src/courseware.coffee @@ -2,6 +2,7 @@ class @Courseware @prefix: '' constructor: -> + new Navigation Logger.bind() @render() diff --git a/lms/static/coffee/src/navigation.coffee b/lms/static/coffee/src/navigation.coffee new file mode 100644 index 0000000000..06c38b781a --- /dev/null +++ b/lms/static/coffee/src/navigation.coffee @@ -0,0 +1,33 @@ +class @Navigation + constructor: -> + if $('#accordion').length + # First look for an active section + active = $('#accordion ul:has(li.active)').index('#accordion ul') + # if we didn't find one, look for an active chapter + if active < 0 + active = $('#accordion h3.active').index('#accordion h3') + # if that didn't work either, default to 0 + if active < 0 + active = 0 + $('#accordion').bind('accordionchange', @log).accordion + active: active + header: 'h3' + autoHeight: false + heightStyle: 'content' + $('#accordion .ui-state-active').closest('.chapter').addClass('is-open') + $('#open_close_accordion a').click @toggle + $('#accordion').show() + $('#accordion a').click @setChapter + + log: (event, ui) -> + Logger.log 'accordion', + newheader: ui.newHeader.text() + oldheader: ui.oldHeader.text() + + toggle: -> + $('.course-wrapper').toggleClass('closed') + + setChapter: -> + $('#accordion .is-open').removeClass('is-open') + $(this).closest('.chapter').addClass('is-open') + \ No newline at end of file diff --git a/lms/static/js/fixtures/accordion.html b/lms/static/js/fixtures/accordion.html deleted file mode 100644 index ae3f8bcbb4..0000000000 --- a/lms/static/js/fixtures/accordion.html +++ /dev/null @@ -1,56 +0,0 @@ -
-
- - - - -
-
\ No newline at end of file diff --git a/lms/static/js/spec/main.js b/lms/static/js/spec/main.js index 5040318115..cb97519c78 100644 --- a/lms/static/js/spec/main.js +++ b/lms/static/js/spec/main.js @@ -64,7 +64,6 @@ '_split': 'js/split', 'mathjax_delay_renderer': 'coffee/src/mathjax_delay_renderer', 'MathJaxProcessor': 'coffee/src/customwmd', - 'js/utils/navigation': 'js/utils/navigation', // Manually specify LMS files that are not converted to RequireJS 'history': 'js/vendor/history', @@ -804,8 +803,6 @@ 'lms/include/teams/js/spec/views/topic_teams_spec.js', 'lms/include/teams/js/spec/views/topics_spec.js', 'lms/include/teams/js/spec/views/team_profile_header_actions_spec.js' - 'lms/include/teams/js/spec/views/team_join_spec.js', - 'lms/include/js/spec/navigation_spec.js' ]); }).call(this, requirejs, define); diff --git a/lms/static/js/spec/navigation_spec.js b/lms/static/js/spec/navigation_spec.js deleted file mode 100644 index 85e37fd588..0000000000 --- a/lms/static/js/spec/navigation_spec.js +++ /dev/null @@ -1,74 +0,0 @@ -define(['jquery', 'js/utils/navigation'], function($) { - 'use strict'; - - describe('Course Navigation Accordion', function() { - var accordion, button, heading, chapterContent, chapterMenu; - - beforeEach(function() { - loadFixtures('js/fixtures/accordion.html'); - - accordion = $('.accordion'); - button = accordion.children('.button-chapter'); - heading = button.children('.group-heading'); - chapterContent = accordion.children('.chapter-content-container'); - chapterMenu = chapterContent.children('.chapter-menu'); - - spyOn($.fn, 'focus').andCallThrough(); - edx.util.navigation.init(); - }); - - describe('constructor', function() { - - describe('always', function() { - - it('ensures accordion is present', function() { - expect(accordion.length).toBe(1); - }); - - it('ensures aria attributes are present', function() { - expect(button).toHaveAttr({ - 'aria-pressed': 'true' - }); - - expect(chapterContent).toHaveAttr({ - 'aria-expanded': 'true' - }); - }); - - it('ensures only one active item', function() { - expect(chapterMenu.find('.active').length).toBe(1); - }); - }); - - describe('open section', function() { - - it('ensures new section is opened and previous section is closed', function() { - button.last().click(); - - expect(chapterContent.first()).toBeHidden(); - expect(chapterContent.last()).not.toBeHidden(); - - expect(button.first()).not.toHaveClass('is-open'); - expect(button.last()).toHaveClass('is-open'); - - expect(chapterContent.last().focus).toHaveBeenCalled(); - }); - - it('ensure proper aria and attrs', function() { - expect(button.last()).toHaveAttr({ - 'aria-pressed': 'false' - }); - expect(button.first()).toHaveAttr({ - 'aria-pressed': 'true' - }); - expect(chapterContent.last()).toHaveAttr({ - 'aria-expanded': 'false' - }); - expect(chapterContent.first()).toHaveAttr({ - 'aria-expanded': 'true' - }) - }); - }); - }); - }); -}); \ No newline at end of file diff --git a/lms/static/js/utils/navigation.js b/lms/static/js/utils/navigation.js deleted file mode 100644 index 1ba70581f1..0000000000 --- a/lms/static/js/utils/navigation.js +++ /dev/null @@ -1,70 +0,0 @@ -var edx = edx || {}, - - Navigation = (function() { - - var navigation = { - - init: function() { - - if ($('.accordion').length) { - - navigation.openAccordion(); - } - }, - - openAccordion: function() { - navigation.checkForCurrent(); - navigation.listenForClick(); - }, - - checkForCurrent: function() { - var active = $('.accordion .chapter-content-container .chapter-menu:has(.active)').index('.accordion .chapter-content-container .chapter-menu') ? $('.accordion .chapter-content-container .chapter-menu:has(.active)').index('.accordion .chapter-content-container .chapter-menu') : 0, - activeSection = $('.accordion .button-chapter:eq(' + active + ')'); - - navigation.closeAccordions(); - navigation.openAccordionSection(activeSection); - }, - - listenForClick: function() { - $('.accordion').on('click', '.button-chapter', function(event) { - navigation.closeAccordions(); - navigation.openAccordionSection(event.currentTarget); - }); - }, - - closeAccordions: function() { - $('.chapter-content-container').hide(); - $('.chapter-content-container .chapter-menu').hide(); - - $('.accordion .button-chapter').each(function(event) { - var el = $(this); - - el.removeClass('is-open').attr('aria-pressed', 'false'); - el.next('.chapter-content-container').attr('aria-expanded', 'false'); - el.children('.group-heading').removeClass('active'); - el.children('.group-heading').find('.icon').addClass('fa-caret-right').removeClass('fa-caret-down'); - }); - }, - - openAccordionSection: function(section) { - var elSection = $(section).next('.chapter-content-container'); - - elSection.show().focus(); - elSection.find('.chapter-menu').show(); - - $(section).addClass('is-open').attr('aria-pressed', 'true'); - $(section).next('.chapter-content-container').attr('aria-expanded', 'true'); - $(section).children('.group-heading').addClass('active'); - $(section).children('.group-heading').find('.icon').removeClass('fa-caret-right').addClass('fa-caret-down'); - } - }; - - return { - init: navigation.init - }; - - })(); - - edx.util = edx.util || {}; - edx.util.navigation = Navigation; - edx.util.navigation.init(); diff --git a/lms/static/sass/course/courseware/_courseware.scss b/lms/static/sass/course/courseware/_courseware.scss index 345c0931d2..6d4b0d6175 100644 --- a/lms/static/sass/course/courseware/_courseware.scss +++ b/lms/static/sass/course/courseware/_courseware.scss @@ -516,7 +516,7 @@ div.course-wrapper { } } - .accordion { + div#accordion { visibility: hidden; width: 10px; padding: 0; @@ -524,6 +524,11 @@ div.course-wrapper { nav { white-space: pre; overflow: hidden; + + ul { + overflow: hidden; + white-space: nowrap; + } } } } diff --git a/lms/static/sass/course/courseware/_sidebar.scss b/lms/static/sass/course/courseware/_sidebar.scss index d2ab1edab3..07a0bdf04d 100644 --- a/lms/static/sass/course/courseware/_sidebar.scss +++ b/lms/static/sass/course/courseware/_sidebar.scss @@ -1,161 +1,234 @@ .course-index { - @include transition( all .2s $ease-in-out-quad 0s); - @include border-right(1px solid $border-color-2); - @include border-radius(3px, 0, 0, 3px); - display: table-cell; // needed to extend the sidebar the full height of the area + @extend .sidebar; + @extend .tran; + @include border-right(1px solid $border-color-2); + @include border-radius(3px, 0, 0, 3px); + + #open_close_accordion { + display: none; + } + + header { + max-height: 47px; + + h2 { + white-space: nowrap; + } + } + + div#accordion { + width: auto; + font-size: 14px; - // reseting bolded fonts for the course index h3 { - @extend %t-regular; - } + border-radius: 0; + margin: 0; + overflow: visible; + font-size: 16px; - // we're targetting the .class now, instead of the #id - .accordion { - @extend %t-copy-sub1; + &:first-child { + border: none; + } - .course-navigation { + &:hover, &:focus { + color: #666; + } - .button-chapter { - @include box-sizing(border-box); - @include linear-gradient(top, $sidebar-chapter-bg-top, $sidebar-chapter-bg-bottom); - @include transition(background-color .1s linear 0s); - width: 100% !important; - border: 0; - border-radius: 0; - padding: 0; - box-shadow: 0 1px 0 $white inset, 0 -1px 0 $shadow-l1 inset; - background-color: $sidebar-chapter-bg; - color: $link-color; - - &.is-open { - background: $white; - box-shadow: none; - } - - &.active { - - .group-heading { - @extend %t-ultrastrong; - color: $base-font-color; - } - } - - .group-heading { - @extend %t-title6; - position: relative; - padding: ($baseline*.75) $baseline ($baseline*.75) ($baseline*2); - color: $link-color; - - .icon { - position: absolute; - @include left($baseline); - top: $baseline; - color: $gray-d1; - } - } - } - - .chapter-content-container { - display: none; // programmatically shown when the section is open; closed at the start - padding: 0 14px ($baseline/2) 14px; - border-bottom: 1px solid $shadow-l1; - background: $white; - - &:focus { - // we don't need the blue glow to be here at this point - // although focus is sent for correct keyboard/a11y - outline: none; - } - - .chapter-menu { - padding: 0 ($baseline*.75); - background: inherit; - overflow: hidden; - - .menu-item { - @extend %t-strong; - @include padding(($baseline/4) ($baseline/2)); - margin: ($baseline/5) 0; - border-radius: ($baseline/5); - background: inherit; - - a { - position: relative; - display: block; - color: $base-font-color; - - p { - @extend %t-action3; - @extend %t-strong; - font-family: $sans-serif; - - &.subtitle { - @extend %t-action3; - @extend %t-regular; - display: block; - margin: 0; - color: $gray-d1; - - &:empty { - // hide empty subtitles - display: none; - } - - // definitions for proctored exam attempt status indicators - .verified { - color: $success-color; - } - - .rejected { - color: $alert-color; - } - - .error { - color: $alert-color; - } - } - } - - &:hover, - &:focus { - color: $link-color; - - .subtitle { - color: $gray-d1; - } - } - } - - &.graded { - - .menu-icon { - @include right(0); - position: absolute; - bottom: 0; - } - } - - &.active { - @extend %t-strong; - background: linear-gradient(to bottom, $gray-l4, #d6d6d6); - - // pre-existing; but not sure where it appears - &:after { - @extend %t-regular; - @include transition(none); - @include right($baseline); - position: absolute; - top: 50%; - margin-top: -13px; - font-size: 30px; - color: $gray-d3; - content: '›'; - opacity: 0; - } - } - } - } - } + &.ui-state-hover, &.ui-state-focus { + a { + color: #666; } + } + + &.ui-accordion-header { + border-bottom: none; + color: $black; + + a { + border-radius: 0; + box-shadow: none; + @include padding-left(19px); + color: $link-color; + } + + &.ui-state-active { + @extend .active; + border-bottom: none; + + &:hover, &:focus { + background: none; + } + } + + span.ui-icon { + @include left(0); + opacity: 0.3; + background-image: url("/static/images/ui-icons_222222_256x240.png"); // jQuery UI sprite + + &.ui-icon-triangle-1-e { + + // CASE: left to right layout + @include ltr { + background-position: -32px -16px; // jQuery UI east arrow position + } + + // CASE: right to left layout + @include rtl { + background-position: -96px -16px; // jQuery UI west arrow position + } + } + } + } } -} \ No newline at end of file + + .chapter { + width: 100% !important; + @include box-sizing(border-box); + padding: 11px 14px; + @include linear-gradient(top, $sidebar-chapter-bg-top, $sidebar-chapter-bg-bottom); + background-color: $sidebar-chapter-bg; + box-shadow: 0 1px 0 $white inset, 0 -1px 0 $shadow-l1 inset; + @include transition(background-color .1s linear 0s); + + &.is-open { + background: $white; + } + + &:first-child { + border-radius: 3px 0 0 0; + } + + &:last-child { + border-radius: 0 0 0 3px; + box-shadow: 0 1px 0 $white inset; + } + + &:hover, &:focus { + background-color: $white; + } + } + + ul.ui-accordion-content { + background: transparent; + border: none; + border-radius: 0; + margin: 0; + padding: 9px 0 9px 9px; + overflow: auto; + width: 100%; + + li { + border-bottom: 0; + border-radius: 0; + margin-bottom: ($baseline/5); + + a { + background: transparent; + border-radius: 4px; + display: block; + @include padding( ($baseline/4), ($baseline*1.5), ($baseline/4), ($baseline/2)); + position: relative; + text-decoration: none; + + p { + font-weight: bold; + font-family: $sans-serif; + margin-bottom: 0; + line-height: 1.3; + + &.subtitle { + @extend %t-copy-sub2; + @extend %t-weight2; + display: inline-block; + width: 90%; + color: $gray-d1; + margin: 0; + + &:empty { + display: none; + } + + // definitions for proctored exam attempt status indicators + i.verified { + color: $success-color; + } + + i.rejected { + color: $alert-color; + } + + i.error { + color: $alert-color; + } + } + } + + &:hover, &:focus { + background: $shadow-l1; + + > a p { + color: $gray-d3; + } + } + + &:active { + box-shadow: inset 0 1px 14px 0 $shadow-l1; + + &:after { + opacity: 1.0; + right: 15px; + } + } + } + + &.active { + @extend %t-weight5; + + &:after { + content: '›'; + position: absolute; + top: 50%; + right: 20px; + margin-top: -13px; + font-size: 30px; + font-weight: normal; + color: #333; + opacity: 0; + @include transition(none); + } + + > a { + border: 1px solid $border-color-1; + box-shadow: 0 1px 0 rgba(255, 255, 255, .35) inset; + background: $sidebar-active-image; + + &:after { + opacity: 1.0; + right: 15px; + } + + p { + color: #333; + } + } + + span.subtitle { + @extend %t-weight2; + } + } + + &.graded { + > a { + .icon { + vertical-align: middle; + } + } + + &.active > a { + background: linear-gradient(to bottom, #e6e6e6, #d6d6d6); + } + } + } + } + } +} diff --git a/lms/templates/courseware/accordion.html b/lms/templates/courseware/accordion.html index 7cfb7fb5e7..2d429ef11d 100644 --- a/lms/templates/courseware/accordion.html +++ b/lms/templates/courseware/accordion.html @@ -6,44 +6,73 @@ %> <%def name="make_chapter(chapter)"> -<% -if chapter.get('active'): - aria_label = _('{chapter}, current chapter').format(chapter=chapter['display_name']) - active_class = 'active"' -else: - aria_label = chapter['display_name'] - active_class = '' -%> - - % for chapter in toc: ${make_chapter(chapter)} -% endfor \ No newline at end of file +% endfor diff --git a/lms/templates/courseware/courseware.html b/lms/templates/courseware/courseware.html index 28b70cccad..224a4efcf7 100644 --- a/lms/templates/courseware/courseware.html +++ b/lms/templates/courseware/courseware.html @@ -165,6 +165,9 @@ ${fragment.foot_html()} % if disable_accordion is UNDEFINED or not disable_accordion: