From fe04106e0f6a88b5e3cf490773f2cb625465e98a Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 18 Mar 2014 13:09:25 +0100 Subject: [PATCH] Add/remove main menu action when switching between desktop/mobile mode --- core/js/js.js | 59 +++++++++++++++++-- core/js/tests/specs/coreSpec.js | 101 ++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 6 deletions(-) diff --git a/core/js/js.js b/core/js/js.js index 23d16b6923..aefca23509 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -488,7 +488,7 @@ var OC={ registerMenu: function($toggle, $menuEl) { $menuEl.addClass('menu'); $toggle.addClass('menutoggle'); - $toggle.on('click', function(event) { + $toggle.on('click.menu', function(event) { if ($menuEl.is(OC._currentMenu)) { $menuEl.hide(); OC._currentMenu = null; @@ -505,6 +505,17 @@ var OC={ OC._currentMenuToggle = $toggle; return false }); + }, + + unregisterMenu: function($toggle, $menuEl) { + // close menu if opened + if ($menuEl.is(OC._currentMenu)) { + $menuEl.hide(); + OC._currentMenu = null; + OC._currentMenuToggle = null; + } + $toggle.off('click.menu').removeClass('menutoggle'); + $menuEl.removeClass('menu'); } }; OC.search.customResults={}; @@ -978,13 +989,49 @@ function initCore() { OC._currentMenuToggle = null; }); - // toggle the navigation on mobile - if (window.matchMedia) { - var mq = window.matchMedia('(max-width: 600px)'); - if (mq && mq.matches) { - OC.registerMenu($('#header #owncloud'), $('#navigation')); + + /** + * Set up the main menu toggle to react to media query changes. + * If the screen is small enough, the main menu becomes a toggle. + * If the screen is bigger, the main menu is not a toggle any more. + */ + function setupMainMenu() { + // toggle the navigation on mobile + if (window.matchMedia) { + var mq = window.matchMedia('(max-width: 600px)'); + var lastMatch = mq.matches; + var $toggle = $('#header #owncloud'); + var $navigation = $('#navigation'); + + function updateMainMenu() { + // mobile mode ? + if (lastMatch && !$toggle.hasClass('menutoggle')) { + // init the menu + OC.registerMenu($toggle, $navigation); + $toggle.data('oldhref', $toggle.attr('href')); + $toggle.attr('href', '#'); + $navigation.hide(); + } + else { + OC.unregisterMenu($toggle, $navigation); + $toggle.attr('href', $toggle.data('oldhref')); + $navigation.show(); + } + } + + updateMainMenu(); + + // TODO: debounce this + $(window).resize(function() { + if (lastMatch !== mq.matches) { + lastMatch = mq.matches; + updateMainMenu(); + } + }); } } + + setupMainMenu(); } $(document).ready(initCore); diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js index 069546387c..7fa0b8e9e6 100644 --- a/core/js/tests/specs/coreSpec.js +++ b/core/js/tests/specs/coreSpec.js @@ -279,5 +279,106 @@ describe('Core base tests', function() { expect(OC.generateUrl('apps/files/download{file}', {file: '/Welcome.txt'})).toEqual(OC.webroot + '/index.php/apps/files/download/Welcome.txt'); }); }); + describe('Main menu mobile toggle', function() { + var oldMatchMedia; + var $toggle; + var $navigation; + + beforeEach(function() { + oldMatchMedia = window.matchMedia; + window.matchMedia = sinon.stub(); + $('#testArea').append('' + + ''); + $toggle = $('#owncloud'); + $navigation = $('#navigation'); + }); + + afterEach(function() { + window.matchMedia = oldMatchMedia; + }); + it('Sets up menu toggle in mobile mode', function() { + window.matchMedia.returns({matches: true}); + window.initCore(); + expect($toggle.hasClass('menutoggle')).toEqual(true); + expect($navigation.hasClass('menu')).toEqual(true); + }); + it('Does not set up menu toggle in desktop mode', function() { + window.matchMedia.returns({matches: false}); + window.initCore(); + expect($toggle.hasClass('menutoggle')).toEqual(false); + expect($navigation.hasClass('menu')).toEqual(false); + }); + it('Switches on menu toggle when mobile mode changes', function() { + var mq = {matches: false}; + window.matchMedia.returns(mq); + window.initCore(); + expect($toggle.hasClass('menutoggle')).toEqual(false); + mq.matches = true; + $(window).trigger('resize'); + expect($toggle.hasClass('menutoggle')).toEqual(true); + }); + it('Switches off menu toggle when mobile mode changes', function() { + var mq = {matches: true}; + window.matchMedia.returns(mq); + window.initCore(); + expect($toggle.hasClass('menutoggle')).toEqual(true); + mq.matches = false; + $(window).trigger('resize'); + expect($toggle.hasClass('menutoggle')).toEqual(false); + }); + it('Clicking menu toggle toggles navigation in mobile mode', function() { + window.matchMedia.returns({matches: true}); + window.initCore(); + $navigation.hide(); // normally done through media query triggered CSS + expect($navigation.is(':visible')).toEqual(false); + $toggle.click(); + expect($navigation.is(':visible')).toEqual(true); + $toggle.click(); + expect($navigation.is(':visible')).toEqual(false); + }); + it('Clicking menu toggle does not toggle navigation in desktop mode', function() { + window.matchMedia.returns({matches: false}); + window.initCore(); + expect($navigation.is(':visible')).toEqual(true); + $toggle.click(); + expect($navigation.is(':visible')).toEqual(true); + }); + it('Switching to mobile mode hides navigation', function() { + var mq = {matches: false}; + window.matchMedia.returns(mq); + window.initCore(); + expect($navigation.is(':visible')).toEqual(true); + mq.matches = true; + $(window).trigger('resize'); + expect($navigation.is(':visible')).toEqual(false); + }); + it('Switching to desktop mode shows navigation', function() { + var mq = {matches: true}; + window.matchMedia.returns(mq); + window.initCore(); + expect($navigation.is(':visible')).toEqual(false); + mq.matches = false; + $(window).trigger('resize'); + expect($navigation.is(':visible')).toEqual(true); + }); + it('Switch to desktop with opened menu then back to mobile resets toggle', function() { + var mq = {matches: true}; + window.matchMedia.returns(mq); + window.initCore(); + expect($navigation.is(':visible')).toEqual(false); + $toggle.click(); + expect($navigation.is(':visible')).toEqual(true); + mq.matches = false; + $(window).trigger('resize'); + expect($navigation.is(':visible')).toEqual(true); + mq.matches = true; + $(window).trigger('resize'); + expect($navigation.is(':visible')).toEqual(false); + $toggle.click(); + expect($navigation.is(':visible')).toEqual(true); + }); + }); });