diff --git a/lms/static/coffee/fixtures/accordion.html b/lms/static/coffee/fixtures/accordion.html
deleted file mode 100644
index 5d5d7f146b..0000000000
--- a/lms/static/coffee/fixtures/accordion.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/lms/static/coffee/spec/courseware_spec.coffee b/lms/static/coffee/spec/courseware_spec.coffee
index c6b0c605c7..129b4308a6 100644
--- a/lms/static/coffee/spec/courseware_spec.coffee
+++ b/lms/static/coffee/spec/courseware_spec.coffee
@@ -1,10 +1,5 @@
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
deleted file mode 100644
index 4970c545eb..0000000000
--- a/lms/static/coffee/spec/navigation_spec.coffee
+++ /dev/null
@@ -1,97 +0,0 @@
-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
-
- it 'bind the setChapter', ->
- expect($('#accordion .chapter')).toHandleWith 'click', @navigation.setChapter
-
- 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'
-
- describe 'setChapter', ->
- beforeEach ->
- $('#accordion').append('')
- 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()
-
diff --git a/lms/static/coffee/src/courseware.coffee b/lms/static/coffee/src/courseware.coffee
index 50fcb3ecec..49e27c484b 100644
--- a/lms/static/coffee/src/courseware.coffee
+++ b/lms/static/coffee/src/courseware.coffee
@@ -2,7 +2,6 @@ class @Courseware
@prefix: ''
constructor: ->
- new Navigation
Logger.bind()
@render()
diff --git a/lms/static/js/fixtures/accordion.html b/lms/static/js/fixtures/accordion.html
new file mode 100644
index 0000000000..40cb03a7c0
--- /dev/null
+++ b/lms/static/js/fixtures/accordion.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lms/static/js/spec/main.js b/lms/static/js/spec/main.js
index cb97519c78..5040318115 100644
--- a/lms/static/js/spec/main.js
+++ b/lms/static/js/spec/main.js
@@ -64,6 +64,7 @@
'_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',
@@ -803,6 +804,8 @@
'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
new file mode 100644
index 0000000000..90a0173df6
--- /dev/null
+++ b/lms/static/js/spec/navigation_spec.js
@@ -0,0 +1,74 @@
+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
new file mode 100644
index 0000000000..71d3080361
--- /dev/null
+++ b/lms/static/js/utils/navigation.js
@@ -0,0 +1,89 @@
+var edx = edx || {},
+
+ Navigation = (function() {
+
+ var navigation = {
+
+ init: function() {
+
+ if ($('#accordion').length) {
+
+ navigation.openAccordion();
+ navigation.checkForCurrent();
+ navigation.listenForClick();
+ }
+ },
+
+ openAccordion: function() {
+ $('#open_close_accordion a').click();
+ $('.course-wrapper').toggleClass('closed');
+ $('#accordion').show();
+ },
+
+ checkForCurrent: function() {
+ var active;
+
+ active = $('#accordion div div:has(a.active)').index('#accordion div div');
+
+ if (active < 0) {
+ active = $('#accordion group-heading.active').index('#accordion h3');
+ }
+
+ if (active < 0) {
+ active = 0;
+ }
+
+ if (active > 0) {
+ $('#accordion').find('.button-chapter:eq(' + active + ')').trigger('click');
+ }
+ },
+
+ listenForClick: function() {
+ $('#accordion').on('click', '.button-chapter', function(event) {
+ // close and reset all accrdions
+ navigation.resetAllAccordions();
+
+ // open this accordion and send focus
+ navigation.openAccordionSection(event.currentTarget);
+
+ // assign classes and set open aria
+ navigation.setAriaAttrs(event.currentTarget);
+ });
+ },
+
+ resetAllAccordions: function() {
+ $('.chapter-content-container').hide();
+ $('.chapter-content-container .chapter-menu').hide();
+
+ $('#accordion .button-chapter').each(function(event) {
+ $(this).removeClass('is-open').attr('aria-pressed', 'false');
+ $(this).next('.chapter-content-container').attr('aria-expanded', 'false');
+ $(this).children('.group-heading').removeClass('active');
+ $(this).children('.group-heading').find('.icon').addClass('fa-caret-right').removeClass('fa-caret-down');
+ });
+ },
+
+ openAccordionSection: function(section) {
+ $(section).next('.chapter-content-container').show().focus();
+ $(section).next('.chapter-content-container').find('.chapter-menu').show();
+
+ navigation.setAriaAttrs(section);
+ },
+
+ setAriaAttrs: function(section) {
+ $(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/templates/main.html b/lms/templates/main.html
index 40c62ee6f3..9bda41e64a 100644
--- a/lms/templates/main.html
+++ b/lms/templates/main.html
@@ -159,6 +159,7 @@ from branding import api as branding_api
<%block name="js_extra"/>
+