Commit b32d2205 by Ahsan Ulhaq Committed by Chris Rodriguez

Course navigation menu accessibility issue

Following have been done
1- Change the structure of the course navigation Menu HTML
2- Add some basic styling
3- Add basic JS for html rendering
4- Update tests according to new structure

AC-76
parent 28fddda4
...@@ -82,7 +82,7 @@ class CourseNavPage(PageObject): ...@@ -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) # 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 # Add one to convert from list index to CSS index
section_css = 'nav>div.chapter:nth-of-type({0})>h3>a'.format(sec_index + 1) section_css = 'nav>div.chapter:nth-of-type({0})'.format(sec_index + 1)
self.q(css=section_css).first.click() self.q(css=section_css).first.click()
# Get the subsection by index # Get the subsection by index
...@@ -94,7 +94,7 @@ class CourseNavPage(PageObject): ...@@ -94,7 +94,7 @@ class CourseNavPage(PageObject):
return return
# Convert list indices (start at zero) to CSS indices (start at 1) # Convert list indices (start at zero) to CSS indices (start at 1)
subsection_css = "nav>div.chapter:nth-of-type({0})>ul>li:nth-of-type({1})>a".format( subsection_css = "nav>div.chapter-content-container:nth-of-type({0})>div>ol>li:nth-of-type({1})>a".format(
sec_index + 1, subsec_index + 1 sec_index + 1, subsec_index + 1
) )
...@@ -130,7 +130,7 @@ class CourseNavPage(PageObject): ...@@ -130,7 +130,7 @@ class CourseNavPage(PageObject):
""" """
Return a list of all section titles on the page. Return a list of all section titles on the page.
""" """
chapter_css = 'nav > div.chapter > h3 > a' chapter_css = 'nav > button.chapter > h3'
return self.q(css=chapter_css).map(lambda el: el.text.strip()).results return self.q(css=chapter_css).map(lambda el: el.text.strip()).results
def _subsection_titles(self, section_index): def _subsection_titles(self, section_index):
...@@ -140,7 +140,9 @@ class CourseNavPage(PageObject): ...@@ -140,7 +140,9 @@ class CourseNavPage(PageObject):
""" """
# Retrieve the subsection title for the section # Retrieve the subsection title for the section
# Add one to the list index to get the CSS index, which starts at one # Add one to the list index to get the CSS index, which starts at one
subsection_css = 'nav>div.chapter:nth-of-type({0})>ul>li>a>p:nth-of-type(1)'.format(section_index) subsection_css = 'nav>.chapter-content-container:nth-of-type({0})>div>ol>li>a>p:nth-of-type(1)'.format(
section_index
)
# If the element is visible, we can get its text directly # If the element is visible, we can get its text directly
# Otherwise, we need to get the HTML # Otherwise, we need to get the HTML
...@@ -171,8 +173,8 @@ class CourseNavPage(PageObject): ...@@ -171,8 +173,8 @@ class CourseNavPage(PageObject):
That's true right after we click the section/subsection, but not true in general 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). (the user could go to a section, then expand another tab).
""" """
current_section_list = self.q(css='nav>div.chapter.is-open>h3>a').text current_section_list = self.q(css='nav>button.chapter.is-open>h3').text
current_subsection_list = self.q(css='nav>div.chapter.is-open li.active>a>p').text current_subsection_list = self.q(css='nav div.chapter-content-container ol li.active>a>p').text
if len(current_section_list) == 0: if len(current_section_list) == 0:
self.warning("Could not find the current section") self.warning("Could not find the current section")
......
...@@ -14,7 +14,7 @@ class CoursewarePage(CoursePage): ...@@ -14,7 +14,7 @@ class CoursewarePage(CoursePage):
url_path = "courseware/" url_path = "courseware/"
xblock_component_selector = '.vert .xblock' xblock_component_selector = '.vert .xblock'
section_selector = '.chapter' section_selector = '.chapter'
subsection_selector = '.chapter ul li' subsection_selector = '.chapter-content-container ol li'
def is_browser_on_page(self): def is_browser_on_page(self):
return self.q(css='body.courseware').present return self.q(css='body.courseware').present
...@@ -102,7 +102,7 @@ class CoursewarePage(CoursePage): ...@@ -102,7 +102,7 @@ class CoursewarePage(CoursePage):
""" """
return the url of the active subsection in the left nav return the url of the active subsection in the left nav
""" """
return self.q(css='.chapter ul li.active a').attrs('href')[0] return self.q(css='.chapter-content-container ol li.active a').attrs('href')[0]
@property @property
def can_start_proctored_exam(self): def can_start_proctored_exam(self):
......
...@@ -1121,7 +1121,7 @@ class EntranceExamTest(UniqueCourseTest): ...@@ -1121,7 +1121,7 @@ class EntranceExamTest(UniqueCourseTest):
When I view the courseware that has an entrance exam When I view the courseware that has an entrance exam
Then there should be an "Entrance Exam" chapter.' Then there should be an "Entrance Exam" chapter.'
""" """
entrance_exam_link_selector = 'div#accordion nav div h3 a' entrance_exam_link_selector = 'div#accordion nav button h3'
# visit courseware page and make sure there is not entrance exam chapter. # visit courseware page and make sure there is not entrance exam chapter.
self.courseware_page.visit() self.courseware_page.visit()
self.courseware_page.wait_for_page() self.courseware_page.wait_for_page()
......
...@@ -92,7 +92,7 @@ def when_i_navigate_to_a_section(step): ...@@ -92,7 +92,7 @@ def when_i_navigate_to_a_section(step):
world.disable_jquery_animations() world.disable_jquery_animations()
# Open the 2nd section # Open the 2nd section
world.css_click(css_selector='div.chapter', index=1) world.css_click(css_selector='button.chapter', index=1)
subsection_css = 'a[href*="Test_Subsection_2/"]' subsection_css = 'a[href*="Test_Subsection_2/"]'
# Click on the subsection to see the content # Click on the subsection to see the content
......
...@@ -74,6 +74,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError ...@@ -74,6 +74,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.x_module import XModuleDescriptor from xmodule.x_module import XModuleDescriptor
from xmodule.mixin import wrap_with_license from xmodule.mixin import wrap_with_license
from util.json_request import JsonResponse 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.sandboxing import can_execute_unsafe_code, get_python_lib_zip
from util import milestones_helpers from util import milestones_helpers
from verify_student.services import ReverificationService from verify_student.services import ReverificationService
...@@ -165,6 +166,7 @@ def toc_for_course(user, request, course, active_chapter, active_section, field_ ...@@ -165,6 +166,7 @@ def toc_for_course(user, request, course, active_chapter, active_section, field_
for chapter in chapters: for chapter in chapters:
# Only show required content, if there is required content # Only show required content, if there is required content
# chapter.hide_from_toc is read-only (boo) # chapter.hide_from_toc is read-only (boo)
display_id = slugify(chapter.display_name_with_default)
local_hide_from_toc = False local_hide_from_toc = False
if required_content: if required_content:
if unicode(chapter.location) not in required_content: if unicode(chapter.location) not in required_content:
...@@ -246,6 +248,7 @@ def toc_for_course(user, request, course, active_chapter, active_section, field_ ...@@ -246,6 +248,7 @@ def toc_for_course(user, request, course, active_chapter, active_section, field_
sections.append(section_context) sections.append(section_context)
toc_chapters.append({ toc_chapters.append({
'display_name': chapter.display_name_with_default, 'display_name': chapter.display_name_with_default,
'display_id': display_id,
'url_name': chapter.url_name, 'url_name': chapter.url_name,
'sections': sections, 'sections': sections,
'active': chapter.url_name == active_chapter 'active': chapter.url_name == active_chapter
......
...@@ -155,7 +155,8 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -155,7 +155,8 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase):
} }
], ],
'url_name': u'Entrance_Exam_Section_-_Chapter_1', 'url_name': u'Entrance_Exam_Section_-_Chapter_1',
'display_name': u'Entrance Exam Section - Chapter 1' 'display_name': u'Entrance Exam Section - Chapter 1',
'display_id': u'Entrance-Exam-Section---Chapter-1',
} }
] ]
) )
...@@ -182,19 +183,22 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -182,19 +183,22 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase):
} }
], ],
'url_name': u'Overview', 'url_name': u'Overview',
'display_name': u'Overview' 'display_name': u'Overview',
'display_id': u'Overview'
}, },
{ {
'active': False, 'active': False,
'sections': [], 'sections': [],
'url_name': u'Week_1', 'url_name': u'Week_1',
'display_name': u'Week 1' 'display_name': u'Week 1',
'display_id': u'Week-1'
}, },
{ {
'active': False, 'active': False,
'sections': [], 'sections': [],
'url_name': u'Instructor', 'url_name': u'Instructor',
'display_name': u'Instructor' 'display_name': u'Instructor',
'display_id': u'Instructor'
}, },
{ {
'active': True, 'active': True,
...@@ -209,7 +213,8 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -209,7 +213,8 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase):
} }
], ],
'url_name': u'Entrance_Exam_Section_-_Chapter_1', 'url_name': u'Entrance_Exam_Section_-_Chapter_1',
'display_name': u'Entrance Exam Section - Chapter 1' 'display_name': u'Entrance Exam Section - Chapter 1',
'display_id': u'Entrance-Exam-Section---Chapter-1'
} }
] ]
) )
......
...@@ -642,11 +642,11 @@ class TestTOC(ModuleStoreTestCase): ...@@ -642,11 +642,11 @@ class TestTOC(ModuleStoreTestCase):
'format': '', 'due': None, 'active': False}, 'format': '', 'due': None, 'active': False},
{'url_name': 'video_4f66f493ac8f', 'display_name': 'Video', 'graded': True, {'url_name': 'video_4f66f493ac8f', 'display_name': 'Video', 'graded': True,
'format': '', 'due': None, 'active': False}], 'format': '', 'due': None, 'active': False}],
'url_name': 'Overview', 'display_name': u'Overview'}, 'url_name': 'Overview', 'display_name': u'Overview', 'display_id': u'Overview'},
{'active': False, 'sections': {'active': False, 'sections':
[{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True, [{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
'format': '', 'due': None, 'active': False}], 'format': '', 'due': None, 'active': False}],
'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) 'url_name': 'secret:magic', 'display_name': 'secret:magic', 'display_id': 'secretmagic'}])
course = self.store.get_course(self.toy_course.id, depth=2) course = self.store.get_course(self.toy_course.id, depth=2)
with check_mongo_calls(toc_finds): with check_mongo_calls(toc_finds):
...@@ -682,11 +682,11 @@ class TestTOC(ModuleStoreTestCase): ...@@ -682,11 +682,11 @@ class TestTOC(ModuleStoreTestCase):
'format': '', 'due': None, 'active': False}, 'format': '', 'due': None, 'active': False},
{'url_name': 'video_4f66f493ac8f', 'display_name': 'Video', 'graded': True, {'url_name': 'video_4f66f493ac8f', 'display_name': 'Video', 'graded': True,
'format': '', 'due': None, 'active': False}], 'format': '', 'due': None, 'active': False}],
'url_name': 'Overview', 'display_name': u'Overview'}, 'url_name': 'Overview', 'display_name': u'Overview', 'display_id': u'Overview'},
{'active': False, 'sections': {'active': False, 'sections':
[{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True, [{'url_name': 'toyvideo', 'display_name': 'toyvideo', 'graded': True,
'format': '', 'due': None, 'active': False}], 'format': '', 'due': None, 'active': False}],
'url_name': 'secret:magic', 'display_name': 'secret:magic'}]) 'url_name': 'secret:magic', 'display_name': 'secret:magic', 'display_id': 'secretmagic'}])
with check_mongo_calls(toc_finds): with check_mongo_calls(toc_finds):
actual = render.toc_for_course( actual = render.toc_for_course(
......
...@@ -2,5 +2,11 @@ ...@@ -2,5 +2,11 @@
<header id="open_close_accordion"> <header id="open_close_accordion">
<a href="#">close</a> <a href="#">close</a>
</header> </header>
<div id="accordion"></div> <div id="accordion">
</div> <button class="chapter">
<h3>
Introduction Chapter
</h3>
</button>
</div>
</div>
\ No newline at end of file
...@@ -8,7 +8,7 @@ describe 'Navigation', -> ...@@ -8,7 +8,7 @@ describe 'Navigation', ->
describe 'when there is an active section', -> describe 'when there is an active section', ->
beforeEach -> beforeEach ->
spyOn $.fn, 'accordion' spyOn $.fn, 'accordion'
$('#accordion').append('<ul><li></li></ul><ul><li class="active"></li></ul>') $('#accordion').append('<div><div><ol><li></li></ol></div></div><div><div><ol><li class="active"></li></ol></div></div>')
new Navigation new Navigation
it 'activate the accordion with correct active section', -> it 'activate the accordion with correct active section', ->
...@@ -21,7 +21,7 @@ describe 'Navigation', -> ...@@ -21,7 +21,7 @@ describe 'Navigation', ->
describe 'when there is no active section', -> describe 'when there is no active section', ->
beforeEach -> beforeEach ->
spyOn $.fn, 'accordion' spyOn $.fn, 'accordion'
$('#accordion').append('<ul><li></li></ul><ul><li></li></ul>') $('#accordion').append('<div><div><ol><li></li></ol></div></div><div><div><ol><li></li></ol></div></div>')
new Navigation new Navigation
it 'activate the accordian with no section as active', -> it 'activate the accordian with no section as active', ->
...@@ -37,6 +37,9 @@ describe 'Navigation', -> ...@@ -37,6 +37,9 @@ describe 'Navigation', ->
it 'bind the navigation toggle', -> it 'bind the navigation toggle', ->
expect($('#open_close_accordion a')).toHandleWith 'click', @navigation.toggle expect($('#open_close_accordion a')).toHandleWith 'click', @navigation.toggle
it 'bind the setChapter', ->
expect($('#accordion .chapter')).toHandleWith 'click', @navigation.setChapter
describe 'when the #accordion does not exists', -> describe 'when the #accordion does not exists', ->
beforeEach -> beforeEach ->
$('#accordion').remove() $('#accordion').remove()
...@@ -70,3 +73,25 @@ describe 'Navigation', -> ...@@ -70,3 +73,25 @@ describe 'Navigation', ->
expect(Logger.log).toHaveBeenCalledWith 'accordion', expect(Logger.log).toHaveBeenCalledWith 'accordion',
newheader: 'new' newheader: 'new'
oldheader: 'old' oldheader: 'old'
describe 'setChapter', ->
beforeEach ->
$('#accordion').append('<div><div><ol><li class="active"><a href="#"></a></li></ol></div></div>')
new Navigation
it 'Chapter opened', ->
e = jQuery.Event('click')
$('#accordion .chapter').trigger(e)
expect($('.chapter')).toHaveClass('is-open')
it 'content active on chapter opened', ->
e = jQuery.Event('click')
$('#accordion .chapter').trigger(e)
expect($('.chapter').next('div').children('div')).toHaveClass('ui-accordion-content-active')
expect($('.ui-accordion-content-active')).toHaveAttr('aria-hidden', 'false')
it 'focus move to first child on chapter opened', ->
spyOn($.fn, 'focus')
e = jQuery.Event('click')
$('#accordion .chapter').trigger(e)
expect($('.ui-accordion-content-active li:first-child a').focus).toHaveBeenCalled()
...@@ -2,7 +2,7 @@ class @Navigation ...@@ -2,7 +2,7 @@ class @Navigation
constructor: -> constructor: ->
if $('#accordion').length if $('#accordion').length
# First look for an active section # First look for an active section
active = $('#accordion ul:has(li.active)').index('#accordion ul') active = $('#accordion div div ol:has(li.active)').index('#accordion div div ol')
# if we didn't find one, look for an active chapter # if we didn't find one, look for an active chapter
if active < 0 if active < 0
active = $('#accordion h3.active').index('#accordion h3') active = $('#accordion h3.active').index('#accordion h3')
...@@ -15,9 +15,15 @@ class @Navigation ...@@ -15,9 +15,15 @@ class @Navigation
autoHeight: false autoHeight: false
heightStyle: 'content' heightStyle: 'content'
$('#accordion .ui-state-active').closest('.chapter').addClass('is-open') $('#accordion .ui-state-active').closest('.chapter').addClass('is-open')
$('#accordion .ui-state-active').parent().next('div').children('div').addClass('ui-accordion-content-active')
$('#accordion .ui-state-active').parent().next('div').show()
$('#accordion .ui-state-active').parent().attr('aria-expanded' , 'true').attr('aria-pressed' , 'true')
$('#accordion div').filter(':not(.chapter-content-container)').addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide()
$('.ui-accordion-content div').attr('aria-hidden', 'false')
$('.ui-accordion-content-active div').attr('aria-hidden', 'true')
$('#open_close_accordion a').click @toggle $('#open_close_accordion a').click @toggle
$('#accordion').show() $('#accordion').show()
$('#accordion a').click @setChapter $('#accordion .chapter').click @setChapter
log: (event, ui) -> log: (event, ui) ->
Logger.log 'accordion', Logger.log 'accordion',
...@@ -28,6 +34,14 @@ class @Navigation ...@@ -28,6 +34,14 @@ class @Navigation
$('.course-wrapper').toggleClass('closed') $('.course-wrapper').toggleClass('closed')
setChapter: -> setChapter: ->
$('#accordion .is-open').removeClass('is-open') $('#accordion .is-open').removeClass('is-open').attr('aria-expanded' , 'false').attr('aria-pressed' , 'false').find('h3 span').removeClass('ui-icon-triangle-1-s').addClass('ui-icon-triangle-1-e')
$(this).closest('.chapter').addClass('is-open') $(this).closest('.chapter').addClass('is-open').attr('aria-expanded' , 'true').attr('aria-pressed' , 'true').find('h3 span').removeClass('ui-icon-triangle-1-e').addClass('ui-icon-triangle-1-s')
$('.ui-accordion-content-active').attr('aria-hidden', 'true')
\ No newline at end of file $('.ui-accordion-content-active').parent().hide()
$('#accordion .ui-accordion-content-active').removeClass('ui-accordion-content-active')
$(this).closest('.chapter').next('div').children('div').addClass('ui-accordion-content-active')
$('.ui-accordion-content-active').parent().show()
$('.ui-accordion-content-active').show()
$('.ui-accordion-content-active li:first-child a').focus()
$('.ui-accordion-content-active').attr('aria-hidden', 'false')
...@@ -17,43 +17,36 @@ ...@@ -17,43 +17,36 @@
} }
div#accordion { div#accordion {
@extend %t-copy-sub1;
width: auto; width: auto;
font-size: 14px;
h3 { h3 {
border-radius: 0; @extend %t-title6;
margin: 0; @include padding-left($baseline);
overflow: visible; overflow: visible;
font-size: 16px; margin: 0;
border-radius: 0;
box-shadow: none;
color: $link-color;
&:first-child { &:first-child {
border: none; border: none;
} }
&:hover, &:focus {
color: #666;
}
&.ui-state-hover, &.ui-state-focus {
a {
color: #666;
}
}
&.ui-accordion-header { &.ui-accordion-header {
@extend %t-regular;
border-bottom: none; border-bottom: none;
color: $black;
a { a {
@include padding-left($baseline);
border-radius: 0; border-radius: 0;
box-shadow: none; box-shadow: none;
@include padding-left(19px);
color: $link-color; color: $link-color;
} }
&.ui-state-active { &.ui-state-active {
@extend .active;
border-bottom: none; border-bottom: none;
color: $base-font-color;
&:hover, &:focus { &:hover, &:focus {
background: none; background: none;
...@@ -61,9 +54,9 @@ ...@@ -61,9 +54,9 @@
} }
span.ui-icon { span.ui-icon {
@include left(0); @include left($baseline);
opacity: 0.3;
background-image: url("/static/images/ui-icons_222222_256x240.png"); // jQuery UI sprite background-image: url("/static/images/ui-icons_222222_256x240.png"); // jQuery UI sprite
opacity: 0.3;
&.ui-icon-triangle-1-e { &.ui-icon-triangle-1-e {
...@@ -82,25 +75,24 @@ ...@@ -82,25 +75,24 @@
} }
.chapter { .chapter {
width: 100% !important;
@include box-sizing(border-box); @include box-sizing(border-box);
padding: 11px 14px;
@include linear-gradient(top, $sidebar-chapter-bg-top, $sidebar-chapter-bg-bottom); @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); @include transition(background-color .1s linear 0s);
width: 100% !important;
padding: 0;
border: 0;
border-radius: 0;
box-shadow: 0 1px 0 $white inset, 0 -1px 0 $shadow-l1 inset;
background-color: $sidebar-chapter-bg;
color: $link-color;
&.is-open { h3 {
background: $white; padding: ($baseline*.75) $baseline ($baseline*.75) ($baseline*2);
}
&:first-child {
border-radius: 3px 0 0 0;
} }
&:last-child { &.is-open {
border-radius: 0 0 0 3px; background: $white;
box-shadow: 0 1px 0 $white inset; box-shadow: none;
} }
&:hover, &:focus { &:hover, &:focus {
...@@ -108,41 +100,47 @@ ...@@ -108,41 +100,47 @@
} }
} }
ul.ui-accordion-content { div.chapter-content-container{
background: transparent; display: none;
padding: 0 14px ($baseline/2) 14px;
border-bottom: 1px solid $shadow-l1;
background: $white;
}
div.ui-accordion-content {
margin: 0;
padding: 0 ($baseline*.75);
border: none; border: none;
border-radius: 0; border-radius: 0;
margin: 0; background: transparent;
padding: 9px 0 9px 9px;
overflow: auto;
width: 100%;
li { li {
margin: ($baseline/5) 0;
// margin-bottom: ($baseline/5);
border-bottom: 0; border-bottom: 0;
border-radius: 0; border-radius: 0;
margin-bottom: ($baseline/5);
a { a {
background: transparent; @extend %t-strong;
border-radius: 4px; @include padding(($baseline/4) ($baseline/2));
display: block;
@include padding( ($baseline/4), ($baseline*1.5), ($baseline/4), ($baseline/2));
position: relative; position: relative;
display: block;
border-radius: ($baseline/5);
background: transparent;
text-decoration: none; text-decoration: none;
p { p {
font-weight: bold; @extend %t-action3;
font-family: $sans-serif; @extend %t-strong;
margin-bottom: 0; margin-bottom: 0;
line-height: 1.3; font-family: $sans-serif;
&.subtitle { &.subtitle {
@extend %t-copy-sub2; @extend %t-action3;
@extend %t-weight2; @extend %t-regular;
display: inline-block; display: block;
width: 90%;
color: $gray-d1;
margin: 0; margin: 0;
color: $gray-d1;
&:empty { &:empty {
display: none; display: none;
...@@ -164,7 +162,6 @@ ...@@ -164,7 +162,6 @@
} }
&:hover, &:focus { &:hover, &:focus {
background: $shadow-l1;
> a p { > a p {
color: $gray-d3; color: $gray-d3;
...@@ -182,19 +179,19 @@ ...@@ -182,19 +179,19 @@
} }
&.active { &.active {
@extend %t-weight5; @extend %t-strong;
&:after { &:after {
@extend %t-regular;
@include transition(none);
@include right($baseline);
content: '›'; content: '›';
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 20px;
margin-top: -13px; margin-top: -13px;
font-size: 30px; font-size: 30px;
font-weight: normal; color: $gray-d3;
color: #333;
opacity: 0; opacity: 0;
@include transition(none);
} }
> a { > a {
...@@ -208,24 +205,27 @@ ...@@ -208,24 +205,27 @@
} }
p { p {
color: #333; color: $gray-d3;
} }
} }
span.subtitle { span.subtitle {
@extend %t-weight2; @extend %t-regular;
} }
} }
&.graded { &.graded {
> a { > a {
.icon { > img {
vertical-align: middle; @include right($baseline/4);
} position: absolute;
bottom: ($baseline/4);
margin: auto;
}
} }
&.active > a { &.active > a {
background: linear-gradient(to bottom, #e6e6e6, #d6d6d6); background: linear-gradient(to bottom, $gray-l4, #d6d6d6);
} }
} }
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%> %>
<%def name="make_chapter(chapter)"> <%def name="make_chapter(chapter)">
<div class="chapter"> <button class="chapter" id="${chapter['display_id']}-parent" aria-controls="${chapter['display_id']}-child" aria-expanded="false" aria-pressed="false">
<% <%
if chapter.get('active'): if chapter.get('active'):
aria_label = _('{chapter}, current chapter').format(chapter=chapter['display_name']) aria_label = _('{chapter}, current chapter').format(chapter=chapter['display_name'])
...@@ -16,63 +16,36 @@ ...@@ -16,63 +16,36 @@
active_class = '' active_class = ''
%> %>
<h3 ${active_class} aria-label="${aria_label}"> <h3 ${active_class} aria-label="${aria_label}">
<a href="#">
${chapter['display_name']} ${chapter['display_name']}
</a>
</h3> </h3>
</button>
<div class="chapter-content-container">
<div id="${chapter['display_id']}-child" role="region" aria-labelledby="${chapter['display_id']}-parent" aria-hidden="false">
<ol>
% for section in chapter['sections']:
<li class="${'active' if 'active' in section and section['active'] else ''} ${'graded' if 'graded' in section and section['graded'] else ''}">
<a href="${reverse('courseware_section', args=[course_id, chapter['url_name'], section['url_name']])}">
<p>${section['display_name']} ${'<span class="sr">, current section</span>' if 'active' in section and section['active'] else ''}</p>
<%
if section.get('due') is None:
due_date = ''
else:
formatted_string = get_time_display(section['due'], due_date_display_format, coerce_tz=settings.TIME_ZONE_DISPLAYED_FOR_DEADLINES)
due_date = '' if len(formatted_string)==0 else _('due {date}').format(date=formatted_string)
%>
<p class="subtitle">${section['format']} ${due_date}</p>
<ul> % if 'graded' in section and section['graded']:
% for section in chapter['sections']: <img src="/static/images/graded.png" alt="Graded Section">
<li class="${'active' if 'active' in section and section['active'] else ''} ${'graded' if 'graded' in section and section['graded'] else ''}"> % endif
<a href="${reverse('courseware_section', args=[course_id, chapter['url_name'], section['url_name']])}"> </a>
<p>${section['display_name']} ${'<span class="sr">, current section</span>' if 'active' in section and section['active'] else ''}</p> </li>
<% % endfor
if section.get('due') is None: </ol>
due_date = '' </div>
else: </div>
formatted_string = get_time_display(section['due'], due_date_display_format, coerce_tz=settings.TIME_ZONE_DISPLAYED_FOR_DEADLINES)
due_date = '' if len(formatted_string)==0 else _('due {date}').format(date=formatted_string)
%>
## There is behavior differences between
## rending of sections which have proctoring/timed examinations
## and those that do not.
##
## Proctoring exposes a exam status message field as well as
## a status icon
% if section['format'] or due_date or 'proctoring' in section:
<p class="subtitle">
% if 'proctoring' in section:
## Display the proctored exam status icon and status message
<i class="fa ${section['proctoring'].get('suggested_icon', 'fa-lock')} ${section['proctoring'].get('status', 'eligible')}"></i>&nbsp;
<span class="subtitle-name">${section['proctoring'].get('short_description', '')}
</span>
## completed proctored exam statuses should not show the due date
## since the exam has already been submitted by the user
% if not section['proctoring'].get('in_completed_state', False):
<span class="subtitle-name">${due_date}</span>
% endif
% else:
## non-proctored section, we just show the exam format and the due date
## this is the standard case in edx-platform
<span class="subtitle-name">${section['format']} ${due_date}
</span>
% endif
</p>
% endif
% if 'graded' in section and section['graded']:
## sections that are graded should indicate this through an icon
<i class="icon fa fa-pencil-square-o" aria-hidden="true" data-tooltip="${_("This section is graded.")}"></i>
% endif
</a>
</li>
% endfor
</ul>
</div>
</%def> </%def>
% for chapter in toc: % for chapter in toc:
${make_chapter(chapter)} ${make_chapter(chapter)}
% endfor % endfor
\ No newline at end of file
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