From 3317176476855e1b8a95dfceb68a0ad9be703a82 Mon Sep 17 00:00:00 2001 From: AshGDS <8880610+AshGDS@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:31:51 +0000 Subject: [PATCH] Add new main menu block This will replace the main navigation block. Have created a new block to make the diff/changes easier to process. --- .../javascripts/modules/app-b-main-menu.js | 26 ++++ .../views/_landing_page/main-menu.scss | 131 ++++++++++++++++++ app/models/landing_page/block/main_menu.rb | 13 ++ .../landing_page/blocks/_main_menu.html.erb | 26 ++++ config/initializers/dartsass.rb | 1 + .../landing_page_content_items/homepage.yaml | 39 +++++- .../unit/modules/app-b-main-menu-spec.js | 71 ++++++++++ 7 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 app/assets/javascripts/modules/app-b-main-menu.js create mode 100644 app/assets/stylesheets/views/_landing_page/main-menu.scss create mode 100644 app/models/landing_page/block/main_menu.rb create mode 100644 app/views/landing_page/blocks/_main_menu.html.erb create mode 100644 spec/javascripts/unit/modules/app-b-main-menu-spec.js diff --git a/app/assets/javascripts/modules/app-b-main-menu.js b/app/assets/javascripts/modules/app-b-main-menu.js new file mode 100644 index 0000000000..3f17d23eee --- /dev/null +++ b/app/assets/javascripts/modules/app-b-main-menu.js @@ -0,0 +1,26 @@ +window.GOVUK = window.GOVUK || {} +window.GOVUK.Modules = window.GOVUK.Modules || {}; + +(function (Modules) { + 'use strict' + + function AppBMainMenu (module) { + this.module = module + this.module.button = this.module.querySelector('button') + this.module.navContainer = this.module.querySelector('#app-b-main-menu__nav-container') + this.module.button.classList.remove('app-b-main-menu__button--no-js') + } + + AppBMainMenu.prototype.init = function () { + this.module.button.addEventListener('click', this.toggleMenu.bind(this)) + } + + AppBMainMenu.prototype.toggleMenu = function (e) { + var ariaExpanded = this.module.button.getAttribute('aria-expanded') === 'true' + this.module.navContainer.classList.toggle('app-b-main-menu__nav-container--js-hidden') + this.module.button.setAttribute('aria-expanded', `${!ariaExpanded}`) + this.module.classList.toggle('app-b-main-menu--collapsed') + } + + Modules.AppBMainMenu = AppBMainMenu +})(window.GOVUK.Modules) diff --git a/app/assets/stylesheets/views/_landing_page/main-menu.scss b/app/assets/stylesheets/views/_landing_page/main-menu.scss new file mode 100644 index 0000000000..c5715c52f1 --- /dev/null +++ b/app/assets/stylesheets/views/_landing_page/main-menu.scss @@ -0,0 +1,131 @@ +@import "govuk_publishing_components/individual_component_support"; + +.app-b-main-menu { + border-bottom: 1px solid $govuk-border-colour; + @include govuk-font(19); +} + +.app-b-main-menu--collapsed { + border-bottom: 0; +} + +.app-b-main-menu__button-container { + background-color: govuk-colour("light-grey"); + border-bottom: 1px solid govuk-colour("mid-grey"); +} + +.app-b-main-menu__button { + height: 70px; + color: govuk-colour("blue"); + @include govuk-font(19); + background: none; + border-top: 0; + border-bottom: 0; + // Invisible border that matches the background colour is used here to keep the element width/focus styles consistent. + border-left: 1px solid govuk-colour("light-grey"); + border-right: 1px solid govuk-colour("light-grey"); + padding-left: govuk-spacing(2); + padding-right: govuk-spacing(2); + cursor: pointer; + position: relative; +} + +// Shared styles for the expanded and collapsed arrow icon. Based off of the super navigation menu button. +.app-b-main-menu__button::before { + border-bottom: 2px solid govuk-colour("blue"); + border-right: 2px solid govuk-colour("blue"); + content: ""; + display: inline-block; + height: 8px; + margin: 0 10px 0 2px; + vertical-align: middle; + width: 8px; +} + +.app-b-main-menu__button:focus { + border-color: $govuk-focus-colour; + box-shadow: 0 0, 0 4px govuk-colour("black"); + z-index: 999; // Prevents focus style black bottom border being obscured + + &.app-b-main-menu__button::before { + border-color: govuk-colour("black"); + } +} + +.app-b-main-menu__button[aria-expanded="false"]::before { + // Collapsed arrow icon + -webkit-transform: translateY(-35%) rotate(45deg) scale(1); + -ms-transform: translateY(-35%) rotate(45deg) scale(1); + transform: translateY(-35%) rotate(45deg) scale(1); +} + +.app-b-main-menu__button[aria-expanded="true"] { + border-color: govuk-colour("mid-grey"); + background-color: govuk-colour("white"); + + // Expanded arrow icon + &.app-b-main-menu__button::before { + -webkit-transform: translateY(1px) rotate(225deg) scale(1); + -ms-transform: translateY(1px) rotate(225deg) scale(1); + transform: translateY(1px) rotate(225deg) scale(1); + } +} + +.app-b-main-menu__button[aria-expanded="true"]:focus { + background-color: $govuk-focus-colour; + border-color: $govuk-focus-colour; +} + +// Adds a white border at the bottom of the button when the button is expanded. Used to make the button look like a tab that's seamlessly connected to element below it. +.app-b-main-menu__button[aria-expanded="true"]::after { + content: ""; + position: absolute; + border-bottom: 1px solid govuk-colour("white"); + width: 100%; + left: 0; + bottom: -1px; +} + +// Removes the white border on bottom of button when focus styles are active. +.app-b-main-menu__button[aria-expanded="true"]:focus::after { + border: 0; +} + +.app-b-main-menu__button--no-js { + display: none; +} + +.app-b-main-menu__button:hover::before { + border-color: govuk-colour("dark-blue"); +} + +.app-b-main-menu__nav-container { + margin-top: govuk-spacing(4); +} + +.js-enabled { + .app-b-main-menu__nav-container--js-hidden { + display: none; + } +} + +.app-b-main-menu__list { + padding-left: 0; + display: grid; + column-gap: govuk-spacing(7); + row-gap: govuk-spacing(4); + + @include govuk-media-query($from: mobile) { + grid-template-columns: 1fr 1fr; + } + @include govuk-media-query($from: tablet) { + grid-template-columns: 1fr 1fr 1fr; + } +} + +.app-b-main-menu__list-item { + list-style: none; + @include govuk-media-query($until: mobile) { + margin-top: govuk-spacing(3); + } +} diff --git a/app/models/landing_page/block/main_menu.rb b/app/models/landing_page/block/main_menu.rb new file mode 100644 index 0000000000..05ec86dc50 --- /dev/null +++ b/app/models/landing_page/block/main_menu.rb @@ -0,0 +1,13 @@ +module LandingPage::Block + class MainMenu < Base + attr_reader :links + + def initialize(block_hash, landing_page) + super + end + + def full_width? + true + end + end +end diff --git a/app/views/landing_page/blocks/_main_menu.html.erb b/app/views/landing_page/blocks/_main_menu.html.erb new file mode 100644 index 0000000000..3a19269da0 --- /dev/null +++ b/app/views/landing_page/blocks/_main_menu.html.erb @@ -0,0 +1,26 @@ +<% + add_view_stylesheet("landing_page/main-menu") +%> +
+
+
+ +
+
+
+ +
+
diff --git a/config/initializers/dartsass.rb b/config/initializers/dartsass.rb index 4a2115762b..0bafdb75bc 100644 --- a/config/initializers/dartsass.rb +++ b/config/initializers/dartsass.rb @@ -21,6 +21,7 @@ "views/_landing_page/featured.scss" => "views/_landing_page/featured.css", "views/_landing_page/grid.scss" => "views/_landing_page/grid.css", "views/_landing_page/main-navigation.scss" => "views/_landing_page/main-navigation.css", + "views/_landing_page/main-menu.scss" => "views/_landing_page/main-menu.css", "views/_landing_page/quote.scss" => "views/_landing_page/quote.css", }.freeze diff --git a/lib/data/landing_page_content_items/homepage.yaml b/lib/data/landing_page_content_items/homepage.yaml index b11c061709..35cf37198d 100644 --- a/lib/data/landing_page_content_items/homepage.yaml +++ b/lib/data/landing_page_content_items/homepage.yaml @@ -19,8 +19,43 @@ navigation_groups: - text: Be thankful href: "/landing-page/be-thankful" blocks: -- type: main_navigation - navigation_group_id: Top Menu +- type: main_menu + button_label: "Landing page menu" + groups: + - heading: "First heading" + links: + - text: Landing page homepage very long text here to demonstrate wrapping + href: "/landing-page/homepage" + - text: Landing page homepage + href: "/landing-page/homepage" + - text: Landing page homepage + href: "/landing-page/homepage" + - heading: "Goals" + links: + - text: Goal 1 + href: "/landing-page/goal-1" + - text: Goal 2 very long text here to demonstrate wrapping + href: "/landing-page/goal-2" + - text: Goal 3 + href: "/landing-page/goal-3" + - text: Goal 4 + href: "/landing-page/goal-4" + - text: Goal 5 + href: "/landing-page/goal-5" + - text: Goal 6 + href: "/landing-page/goal-6" + - heading: "Tasks" + links: + - text: Be kinder + href: "/landing-page/be-kinder" + - text: Exercise more + href: "/landing-page/exercise-more" + - text: Donate to charity and this has extra text to demonstrate more wrapping + href: "/landing-page/donate-to-charity" + - text: Learn something new + href: "/landing-page/learn-something-new" + - text: Be thankful + href: "/landing-page/be-thankful" - type: hero image: alt: "Placeholder alt text" diff --git a/spec/javascripts/unit/modules/app-b-main-menu-spec.js b/spec/javascripts/unit/modules/app-b-main-menu-spec.js new file mode 100644 index 0000000000..06075b054a --- /dev/null +++ b/spec/javascripts/unit/modules/app-b-main-menu-spec.js @@ -0,0 +1,71 @@ +describe('Main Menu Block module', function () { + 'use strict' + + var el, module + + beforeEach(function () { + var DOM = + `
+
+
+ +
+
+
+ +
+
+ ` + el = document.createElement('div') + el.innerHTML = DOM + document.body.appendChild(el) + module = new GOVUK.Modules.AppBMainMenu(el) + module.init() + }) + + afterEach(function () { + document.body.removeChild(el) + }) + + it('removes the no-js class from the button on initialisation', function () { + expect(document.querySelector('app-b-main-menu__button--no-js')).toBe(null) + }) + + it('toggles aria expanded on the button when it is clicked', function () { + var button = el.querySelector('button') + expect(button.getAttribute('aria-expanded')).toBe('false') + window.GOVUK.triggerEvent(button, 'click') + expect(button.getAttribute('aria-expanded')).toBe('true') + }) + + it('toggles the show/hide class when the button is clicked', function () { + var button = el.querySelector('button') + var toggledClass = '.app-b-main-menu__nav-container--js-hidden' + expect(document.querySelector(toggledClass)).not.toBe(null) + window.GOVUK.triggerEvent(button, 'click') + expect(document.querySelector(toggledClass)).toBe(null) + window.GOVUK.triggerEvent(button, 'click') + expect(document.querySelector(toggledClass)).not.toBe(null) + }) +})