diff --git a/__tests__/mobile-navigation.test.js b/__tests__/navigation.test.js similarity index 89% rename from __tests__/mobile-navigation.test.js rename to __tests__/navigation.test.js index ec3c2c1bbb..f996d49a57 100644 --- a/__tests__/mobile-navigation.test.js +++ b/__tests__/navigation.test.js @@ -9,8 +9,8 @@ const PORT = configPaths.testPort let page let baseUrl = 'http://localhost:' + PORT -const mobileNav = '.app-mobile-nav' -const mobileNavToggler = '.js-app-mobile-nav__toggler' +const mobileNav = '.app-navigation' +const mobileNavToggler = '.js-app-navigation__toggler' beforeAll(async () => { page = await setupPage(iPhone) @@ -25,8 +25,8 @@ describe('Homepage', () => { it('falls back to making the navigation visible', async () => { await page.setJavaScriptEnabled(false) await page.goto(baseUrl, { waitUntil: 'load' }) - const isMobileNavigationVisible = await page.waitForSelector('.js-app-mobile-nav', { visible: true, timeout: 1000 }) - expect(isMobileNavigationVisible).toBeTruthy() + const isAppNavigationVisible = await page.waitForSelector('.js-app-navigation', { visible: true, timeout: 1000 }) + expect(isAppNavigationVisible).toBeTruthy() }) }) @@ -35,7 +35,6 @@ describe('Homepage', () => { it('should apply the corresponding open state class to the menu button', async () => { await page.setJavaScriptEnabled(true) await page.goto(baseUrl, { waitUntil: 'load' }) - await page.click(mobileNavToggler) const toggleButtonIsOpen = await page.evaluate((mobileNavToggler) => @@ -47,7 +46,6 @@ describe('Homepage', () => { it('should indicate the expanded state of the toggle button using aria-expanded', async () => { await page.goto(baseUrl, { waitUntil: 'load' }) - await page.click(mobileNavToggler) const toggleButtonAriaExpanded = await page.evaluate((mobileNavToggler) => @@ -59,11 +57,10 @@ describe('Homepage', () => { it('should indicate the open state of the navigation', async () => { await page.goto(baseUrl, { waitUntil: 'load' }) - await page.click(mobileNavToggler) const navigationIsOpen = await page.evaluate((mobileNav) => - document.body.querySelector(mobileNav).classList.contains('app-mobile-nav--active'), + document.body.querySelector(mobileNav).classList.contains('app-navigation--active'), mobileNav) expect(navigationIsOpen).toBeTruthy() @@ -71,7 +68,6 @@ describe('Homepage', () => { it('should indicate the visible state of the navigation using the hidden attribute', async () => { await page.goto(baseUrl, { waitUntil: 'load' }) - await page.click(mobileNavToggler) const navigationIsHidden = await page.evaluate((mobileNav) => diff --git a/src/javascripts/application.js b/src/javascripts/application.js index 6c26b6ae96..eb5f9d77a8 100644 --- a/src/javascripts/application.js +++ b/src/javascripts/application.js @@ -4,7 +4,7 @@ import common from 'govuk-frontend/govuk/common' import Example from './components/example.js' import AppTabs from './components/tabs.js' import Copy from './components/copy.js' -import MobileNav from './components/mobile-navigation.js' +import Navigation from './components/navigation.js' import Search from './components/search.js' import OptionsTable from './components/options-table.js' import { getConsentCookie, isValidConsentCookie } from './components/cookie-functions.js' @@ -45,7 +45,7 @@ nodeListForEach($codeBlocks, function ($codeBlock) { }) // Initialise mobile navigation -new MobileNav().init() +new Navigation().init() // Initialise search var $searchContainer = document.querySelector('[data-module="app-search"]') diff --git a/src/javascripts/components/mobile-navigation.js b/src/javascripts/components/navigation.js similarity index 86% rename from src/javascripts/components/mobile-navigation.js rename to src/javascripts/components/navigation.js index b3214e1f86..197b81accc 100644 --- a/src/javascripts/components/mobile-navigation.js +++ b/src/javascripts/components/navigation.js @@ -3,19 +3,19 @@ import 'govuk-frontend/govuk/vendor/polyfills/Element/prototype/classList' import common from 'govuk-frontend/govuk/common' var nodeListForEach = common.nodeListForEach -var navActiveClass = 'app-mobile-nav--active' +var navActiveClass = 'app-navigation--active' var navMenuButtonActiveClass = 'app-header-mobile-nav-toggler--active' -var subNavActiveClass = 'app-mobile-nav__subnav--active' +var subNavActiveClass = 'app-navigation__subnav--active' // This one has the query dot at the beginning because it's only ever used in querySelector calls -var subNavJSClass = '.js-app-mobile-nav__subnav' +var subNavJSClass = '.js-app-navigation__subnav' -function MobileNav ($module) { +function Navigation ($module) { this.$module = $module || document - this.$nav = this.$module.querySelector('.js-app-mobile-nav') - this.$navToggler = this.$module.querySelector('.js-app-mobile-nav__toggler') - this.$navButtons = this.$module.querySelectorAll('.js-app-mobile-nav__button') - this.$navLinks = this.$module.querySelectorAll('.js-app-mobile-nav__link') + this.$nav = this.$module.querySelector('.js-app-navigation') + this.$navToggler = this.$module.querySelector('.js-app-navigation__toggler') + this.$navButtons = this.$module.querySelectorAll('.js-app-navigation__button') + this.$navLinks = this.$module.querySelectorAll('.js-app-navigation__link') // Save the opened/closed state for the nav in memory so that we can accurately maintain state when the screen is changed from small to big and back to small this.mobileNavOpen = false @@ -28,7 +28,8 @@ function MobileNav ($module) { // Checks if the saved window size has changed between now and when it was last recorded (on load and on viewport width changes) // Reapplies hidden attributes based on if the viewport has changed from big to small or vice verca // Saves the new window size -MobileNav.prototype.setHiddenStates = function () { + +Navigation.prototype.setHiddenStates = function () { if (this.mql === null || !this.mql.matches) { if (!this.mobileNavOpen) { this.$nav.setAttribute('hidden', '') @@ -58,7 +59,7 @@ MobileNav.prototype.setHiddenStates = function () { } } -MobileNav.prototype.setInitialAriaStates = function () { +Navigation.prototype.setInitialAriaStates = function () { this.$navToggler.setAttribute('aria-expanded', 'false') nodeListForEach(this.$navButtons, function ($button, index) { @@ -76,7 +77,7 @@ MobileNav.prototype.setInitialAriaStates = function () { }) } -MobileNav.prototype.bindUIEvents = function () { +Navigation.prototype.bindUIEvents = function () { var $nav = this.$nav var $navToggler = this.$navToggler var $navButtons = this.$navButtons @@ -122,7 +123,7 @@ MobileNav.prototype.bindUIEvents = function () { }) } -MobileNav.prototype.init = function () { +Navigation.prototype.init = function () { // Since the Mobile navigation is not included in IE8 // We detect features we need to use only available in IE9+ https://caniuse.com/#feat=addeventlistener // http://responsivenews.co.uk/post/18948466399/cutting-the-mustard @@ -145,4 +146,4 @@ MobileNav.prototype.init = function () { this.bindUIEvents() } -export default MobileNav +export default Navigation diff --git a/src/stylesheets/components/_mobile-navigation.scss b/src/stylesheets/components/_mobile-navigation.scss deleted file mode 100644 index 60c727dd42..0000000000 --- a/src/stylesheets/components/_mobile-navigation.scss +++ /dev/null @@ -1,129 +0,0 @@ -.app-mobile-nav { - border-bottom: 1px solid $govuk-border-colour; - background-color: $app-light-grey; - - @include govuk-media-query($from: tablet) { - display: block; - } - - [hidden], - &[hidden] { - display: none; - } -} - -.no-js .app-mobile-nav { - @include govuk-media-query($until: tablet) { - display: block; - } -} - -.app-mobile-nav__list { - margin: 0; - padding: 0; - list-style: none; - - @include govuk-media-query($from: tablet) { - position: relative; - left: govuk-spacing(-3); - } -} - -.app-mobile-nav__list-item { - position: relative; - - @include govuk-media-query($from: tablet) { - $navigation-height: 50px; - - @include govuk-font(19, $weight: bold, $line-height: $navigation-height); - box-sizing: border-box; - height: $navigation-height; - height: govuk-px-to-rem($navigation-height); // sass-lint:disable-line no-duplicate-properties - padding: 0 govuk-spacing(3); - float: left; - } -} - -.app-mobile-nav__link, -.app-mobile-nav__button { - margin: govuk-spacing(3) 0; - padding: 0; - @include govuk-typography-weight-bold; // Override .govuk-link weight - font-size: 19px; // We do not have a font mixin that produces 19px on mobile - font-size: govuk-px-to-rem(19px); // sass-lint:disable-line no-duplicate-properties - - // Expand the touch area of the link to the full menu width - &:after { - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - } -} - -.app-mobile-nav__link:not([hidden]) { - display: inline-block; - - @include govuk-media-query($from: tablet) { - display: inline; - } -} - -.app-mobile-nav__button { - @include govuk-link-common; - @include govuk-link-style-no-underline; - @include govuk-link-style-no-visited-state; - border: 0; - color: $govuk-link-colour; - background-color: transparent; -} - -.app-mobile-nav-subnav__link-heading { - display: inline-block; - margin: 0; -} - -.app-mobile-nav__subnav { - margin: 0 govuk-spacing(-3); - padding: govuk-spacing(2) 0; - list-style: none; - background-color: govuk-colour("white"); -} - -.js-enabled .app-mobile-nav__subnav--active { - display: block; -} - -.app-mobile-nav__subnav, -.js-enabled .app-mobile-nav__subnav--active { - @include govuk-media-query($from: tablet) { - display: none; - } -} - -.app-mobile-nav__subnav-item { - display: block; - position: relative; - padding: govuk-spacing(3); -} - -.app-mobile-nav__subnav-item--current { - $_current-indicator-width: 4px; - padding-left: govuk-spacing(4) - $_current-indicator-width; - border-left: $_current-indicator-width solid govuk-colour("blue"); -} - -.app-mobile-nav__theme { - @include govuk-typography-common; - position: relative; // this is to get around the artificial click area generated by the :after of the parent button - margin: 0; - padding: govuk-spacing(4) govuk-spacing(3) govuk-spacing(1); - color: govuk-colour("dark-grey"); - // Font is defined as a hard 19px so - // it does not re-size on mobile viewport - font-size: 19px; - font-size: govuk-px-to-rem(19px); // sass-lint:disable-line no-duplicate-properties - font-weight: normal; -} diff --git a/src/stylesheets/components/_navigation.scss b/src/stylesheets/components/_navigation.scss index 0975da2084..762bbaf4af 100644 --- a/src/stylesheets/components/_navigation.scss +++ b/src/stylesheets/components/_navigation.scss @@ -1,31 +1,47 @@ .app-navigation { + border-bottom: 1px solid $govuk-border-colour; background-color: $app-light-grey; - @include govuk-media-query($until: tablet) { + @include govuk-media-query($from: tablet) { + display: block; + } + + [hidden], + &[hidden] { display: none; } } +.no-js .app-navigation { + @include govuk-media-query($until: tablet) { + display: block; + } +} + .app-navigation__list { - position: relative; - left: -(govuk-spacing(3)); margin: 0; padding: 0; list-style: none; + + @include govuk-media-query($from: tablet) { + position: relative; + left: govuk-spacing(-3); + } } .app-navigation__list-item { - $navigation-height: 50px; + position: relative; - @include govuk-font(19, $weight: bold, $line-height: $navigation-height); + @include govuk-media-query($from: tablet) { + $navigation-height: 50px; - box-sizing: border-box; - display: block; - position: relative; - height: $navigation-height; - height: govuk-px-to-rem($navigation-height); // sass-lint:disable-line no-duplicate-properties - padding: 0 govuk-spacing(3); - float: left; + @include govuk-font(19, $weight: bold, $line-height: $navigation-height); + box-sizing: border-box; + height: $navigation-height; + height: govuk-px-to-rem($navigation-height); // sass-lint:disable-line no-duplicate-properties + padding: 0 govuk-spacing(3); + float: left; + } } .app-navigation__list-item--current { @@ -34,10 +50,15 @@ } } -.app-navigation__link { - @include govuk-typography-weight-bold; +.app-navigation__link, +.app-navigation__button { + margin: govuk-spacing(3) 0; + padding: 0; + @include govuk-typography-weight-bold; // Override .govuk-link weight + font-size: 19px; // We do not have a font mixin that produces 19px on mobile + font-size: govuk-px-to-rem(19px); // sass-lint:disable-line no-duplicate-properties - // Extend the touch area of the link to the list + // Expand the touch area of the link to the full menu width &:after { content: ""; position: absolute; @@ -48,6 +69,67 @@ } } -.app-navigation__list-item--current .app-navigation__link:hover { - text-decoration: none; +.app-navigation__link:not([hidden]) { + display: inline-block; + + @include govuk-media-query($from: tablet) { + display: inline; + } +} + +.app-navigation__button { + @include govuk-link-common; + @include govuk-link-style-no-underline; + @include govuk-link-style-no-visited-state; + border: 0; + color: $govuk-link-colour; + background-color: transparent; +} + +.app-navigation-subnav__link-heading { + display: inline-block; + margin: 0; +} + +.app-navigation__subnav { + margin: 0 govuk-spacing(-3); + padding: govuk-spacing(2) 0; + list-style: none; + background-color: govuk-colour("white"); +} + +.js-enabled .app-navigation__subnav--active { + display: block; +} + +.app-navigation__subnav, +.js-enabled .app-navigation__subnav--active { + @include govuk-media-query($from: tablet) { + display: none; + } +} + +.app-navigation__subnav-item { + display: block; + position: relative; + padding: govuk-spacing(3); +} + +.app-navigation__subnav-item--current { + $_current-indicator-width: 4px; + padding-left: govuk-spacing(4) - $_current-indicator-width; + border-left: $_current-indicator-width solid govuk-colour("blue"); +} + +.app-navigation__theme { + @include govuk-typography-common; + position: relative; // this is to get around the artificial click area generated by the :after of the parent button + margin: 0; + padding: govuk-spacing(4) govuk-spacing(3) govuk-spacing(1); + color: govuk-colour("dark-grey"); + // Font is defined as a hard 19px so + // it does not re-size on mobile viewport + font-size: 19px; + font-size: govuk-px-to-rem(19px); // sass-lint:disable-line no-duplicate-properties + font-weight: normal; } diff --git a/src/stylesheets/main.scss b/src/stylesheets/main.scss index f09dfa9dfe..866b539399 100644 --- a/src/stylesheets/main.scss +++ b/src/stylesheets/main.scss @@ -16,7 +16,6 @@ $app-code-color: #d13118; @import "components/highlight"; @import "components/inverted-button"; @import "components/masthead"; -@import "components/mobile-navigation"; @import "components/navigation"; @import "components/options"; @import "components/page-navigation"; diff --git a/views/layouts/_generic.njk b/views/layouts/_generic.njk index 11e5a790b9..06a8f6729b 100644 --- a/views/layouts/_generic.njk +++ b/views/layouts/_generic.njk @@ -35,7 +35,7 @@ {% block main %}