diff --git a/src/govuk/components/header/header.js b/src/govuk/components/header/header.js index 9fd4281d28..84eb54f8d3 100644 --- a/src/govuk/components/header/header.js +++ b/src/govuk/components/header/header.js @@ -1,46 +1,53 @@ -import '../../vendor/polyfills/Function/prototype/bind' -import '../../vendor/polyfills/Event' // addEventListener and event.target normalization +import '../../vendor/polyfills/Event' import '../../vendor/polyfills/Element/prototype/classList' +import '../../vendor/polyfills/Function/prototype/bind' function Header ($module) { this.$module = $module + this.$menuButton = $module && $module.querySelector('.govuk-js-header-toggle') + this.$menu = this.$menuButton && $module.querySelector( + '#' + this.$menuButton.getAttribute('aria-controls') + ) } +/** + * Initialise header + * + * Check for the presence of the header, menu and menu button – if any are + * missing then there's nothing to do so return early. + */ Header.prototype.init = function () { - // Check for module - var $module = this.$module - if (!$module) { - return - } - - // Check for button - var $toggleButton = $module.querySelector('.govuk-js-header-toggle') - if (!$toggleButton) { + if (!this.$module || !this.$menuButton || !this.$menu) { return } - // Handle $toggleButton click events - $toggleButton.addEventListener('click', this.handleClick.bind(this)) + this.syncState(this.$menu.classList.contains('govuk-header__navigation--open')) + this.$menuButton.addEventListener('click', this.handleMenuButtonClick.bind(this)) } /** -* An event handler for click event on $toggleButton -* @param {object} event event -*/ -Header.prototype.handleClick = function (event) { - var $module = this.$module - var $toggleButton = event.target || event.srcElement - var $target = $module.querySelector('#' + $toggleButton.getAttribute('aria-controls')) - - // If a button with aria-controls, handle click - if ($toggleButton && $target) { - var isVisible = $target.classList.toggle('govuk-header__navigation--open') - - $toggleButton.classList.toggle('govuk-header__menu-button--open', isVisible) + * Sync menu state + * + * Sync the menu button class and the accessible state of the menu and the menu + * button with the visible state of the menu + * + * @param {boolean} isVisible Whether the menu is currently visible + */ +Header.prototype.syncState = function (isVisible) { + this.$menuButton.classList.toggle('govuk-header__menu-button--open', isVisible) + this.$menuButton.setAttribute('aria-expanded', isVisible) + this.$menu.setAttribute('aria-hidden', !isVisible) +} - $toggleButton.setAttribute('aria-expanded', isVisible) - $target.setAttribute('aria-hidden', !isVisible) - } +/** + * Handle menu button click + * + * When the menu button is clicked, change the visibility of the menu and then + * sync the accessibility state and menu button state + */ +Header.prototype.handleMenuButtonClick = function () { + var isVisible = this.$menu.classList.toggle('govuk-header__navigation--open') + this.syncState(isVisible) } export default Header diff --git a/src/govuk/components/header/header.test.js b/src/govuk/components/header/header.test.js index c99dc58f34..4cac9aa592 100644 --- a/src/govuk/components/header/header.test.js +++ b/src/govuk/components/header/header.test.js @@ -31,6 +31,31 @@ describe('/components/header', () => { }) describe('when JavaScript is available', () => { + describe('when no navigation is present', () => { + it('exits gracefully with no errors', async () => { + // Errors logged to the console will cause this test to fail + await page.goto(baseUrl + '/components/header/preview', { waitUntil: 'load' }) + }) + }) + + describe('on page load', () => { + beforeAll(async () => { + await page.goto(baseUrl + '/components/header/with-navigation/preview', { waitUntil: 'load' }) + }) + + it('exposes the hidden state of the menu using aria-hidden', async () => { + const ariaHidden = await page.$eval('.govuk-header__navigation', el => el.getAttribute('aria-hidden')) + + expect(ariaHidden).toBe('true') + }) + + it('exposes the collapsed state of the menu button using aria-expanded', async () => { + const ariaExpanded = await page.$eval('.govuk-header__menu-button', el => el.getAttribute('aria-expanded')) + + expect(ariaExpanded).toBe('false') + }) + }) + describe('when menu button is pressed', () => { it('should indicate the open state of the toggle button', async () => { await page.goto(baseUrl + '/components/header/with-navigation/preview', { waitUntil: 'load' })