Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expandable/dropdown menus #3917

Merged
merged 78 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from 71 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
c7a2e8d
wip - root level nav updates
martyn-w Mar 18, 2024
1e9e9d9
wip - category menus
martyn-w Mar 22, 2024
8c2fb78
wip - javascript controller
martyn-w Mar 22, 2024
955a78d
wip - grid layout and tweaking JS
martyn-w Mar 26, 2024
fd7bd6c
wip - improving menu logic
martyn-w Mar 27, 2024
a2b85cb
linting
martyn-w Mar 27, 2024
dc88128
Merge branch 'master' into expandable-menus
martyn-w Mar 28, 2024
b858ba6
test fixes
martyn-w Mar 28, 2024
70e7644
linting fixes
martyn-w Mar 28, 2024
56b3ab1
test fix
martyn-w Mar 28, 2024
3542bfc
Merge branch 'expandable-menus' of github.com:DFE-Digital/get-into-te…
martyn-w Mar 28, 2024
748fa4c
Merge branch 'master' into expandable-menus
martyn-w Apr 2, 2024
f7f42a4
fix tab navigation, bugfix menu hide
martyn-w Apr 2, 2024
87f65e8
set ariaExpanded attribute
martyn-w Apr 3, 2024
cd03508
Merge branch 'master' into expandable-menus
martyn-w Apr 3, 2024
557691d
set expanded on anchor link
martyn-w Apr 3, 2024
375eb47
tweak styles
martyn-w Apr 3, 2024
61a68aa
fix linting error
martyn-w Apr 3, 2024
ae765b9
mobile styles wip
martyn-w Apr 8, 2024
12c976e
mobile javascript workflow
martyn-w Apr 9, 2024
3d075a2
rewrite of JS sync logic
martyn-w Apr 12, 2024
39cb638
ensure menu sync from different menu branchs
martyn-w Apr 15, 2024
540f4c8
Merge branch 'master' into expandable-menus
martyn-w Apr 16, 2024
b1a8c2b
linting
martyn-w Apr 16, 2024
e1fb51e
bugfix to close page-links menu
martyn-w Apr 16, 2024
a59f23f
fix arrows and spacing
martyn-w Apr 16, 2024
f1fbf8f
linting
martyn-w Apr 16, 2024
07daf5c
more styling tweaks
martyn-w Apr 17, 2024
590cb96
test fix
martyn-w Apr 17, 2024
4ae76af
more test fixes
martyn-w Apr 17, 2024
bf62879
expanded icon for active state
martyn-w Apr 17, 2024
d3921fc
tweak icon size
martyn-w Apr 18, 2024
be6c763
change link to button for better accessibility
martyn-w Apr 18, 2024
f206a34
testfix
martyn-w Apr 18, 2024
5cd3a16
simple non-js menu list
martyn-w Apr 18, 2024
858e6ea
test fix
martyn-w Apr 18, 2024
1030f4b
add selected item behaviour
martyn-w Apr 22, 2024
1c52273
Various style fixes
martyn-w Apr 22, 2024
e4d7f80
Update browse.html.erb
martyn-w Apr 22, 2024
02ff0a6
Expose uncategorised menu items
martyn-w Apr 24, 2024
16d261e
fix wrapping of icon for iQTS
martyn-w Apr 24, 2024
4090194
fix padding and margins
martyn-w Apr 24, 2024
9763946
linting
martyn-w Apr 24, 2024
924f8e5
Update navigation_component.rb
martyn-w Apr 24, 2024
3a472cb
Update navigation_component_spec.rb
martyn-w Apr 24, 2024
1001b16
bring uncategorised pages to level 2 menu
martyn-w Apr 25, 2024
c6a8e7b
fix increased height bug in "how to apply"
martyn-w Apr 26, 2024
96c997c
remove underline on top level items
martyn-w Apr 26, 2024
2f4d65c
cleanup
martyn-w Apr 26, 2024
0f60878
add right padding to mobile menu arrows
martyn-w Apr 26, 2024
dd07a97
remove indent on top level mobile menu
martyn-w Apr 26, 2024
0f5a342
Merge branch 'master' into expandable-menus
martyn-w Apr 29, 2024
428eefc
Update browse.html.erb
martyn-w Apr 29, 2024
bb43919
Align page menu with categories
martyn-w Apr 29, 2024
6b145a7
Merge branch 'master' into expandable-menus
martyn-w May 1, 2024
ed4976e
more wip
martyn-w May 7, 2024
f5061e2
more wip
martyn-w May 7, 2024
a0bcf5e
further style fixes
martyn-w May 7, 2024
ef97194
linting
martyn-w May 7, 2024
287ac10
Merge branch 'master' into expandable-menus
martyn-w May 7, 2024
19e9621
button width fix
martyn-w May 8, 2024
2893d33
tweak arrow colours on mobile
martyn-w May 8, 2024
ee4cc5c
Merge branch 'master' into expandable-menus
martyn-w May 8, 2024
7a7dac9
tabbing functionality on nav menu
martyn-w May 9, 2024
9fe8b6c
re-instate hover on top nav menu
martyn-w May 9, 2024
13339fb
minor bug fixes
martyn-w May 10, 2024
0386983
linting
martyn-w May 10, 2024
55310e4
updating styling of the browse page
jenhadfield-dfe May 13, 2024
e729f03
Linting whitespace
martyn-w May 13, 2024
781ef45
Merge branch 'master' into expandable-menus
martyn-w May 13, 2024
434c052
update to title on browse page
jenhadfield-dfe May 15, 2024
797a874
tidy up browse page
martyn-w May 16, 2024
d413004
refactor nav controller
martyn-w May 16, 2024
a39b848
Update app/webpacker/controllers/navigation_controller.js
martyn-w May 16, 2024
c271071
Merge branch 'expandable-menus' of github.com:DFE-Digital/get-into-te…
martyn-w May 16, 2024
3e0904e
fix show menu logic
martyn-w May 16, 2024
9e3cf4e
rename sync to corresponding
martyn-w May 16, 2024
1cac3ad
linting, update js spec
martyn-w May 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions app/components/header/navigation_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
<nav id="primary-navigation" class="hidden-mobile" data-navigation-target="nav" aria-label="Primary navigation" role="navigation">
<ol class="primary" data-navigation-target="primary">
<!-- mobile navigation -->
<%= tag.nav id: "primary-navigation", class: "hidden-mobile", data: { "navigation-target": "nav", action: "click->navigation#handleNavMenuClick" }, "aria-label": "Primary navigation", role: "navigation" do %>
<%= tag.ol class: "primary", data: { "navigation-target": "primary" } do %>
<% all_resources.each do |resource| %>
<%= nav_link(resource.title, resource.path) %>
<%= nav_link(resource, :mobile) %>
<% end %>
</ol>
</nav>
<% end %>
<% end %>

<!-- desktop dropdown navigation -->
<%= tag.div id: "secondary-navigation", class: "desktop-menu-container hidden-mobile", data: { "navigation-target": "desktop", action: "click->navigation#handleNavMenuClick" }, "aria-label": "Secondary navigation", role: "navigation" do %>
<div class="category-links">
<% all_resources.each do |resource| %>
<% if resource.children? %>
<%= category_list(resource, :desktop, css_class: "category-links-list hidden-menu") %>
<% end %>
<% end %>
</div>

<div class="page-links">
<% all_resources.each do |resource| %>
<% if resource.subcategories.present? %>
<% resource.subcategories.each do |subcategory| %>
<%= page_list(resource, subcategory, :desktop, css_class: "page-links-list hidden-menu") %>
<% end %>
<% end %>
<% end %>
</div>

<div class="key-links">
</div>

<% end %>
131 changes: 124 additions & 7 deletions app/components/header/navigation_component.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
module Header
class NavigationComponent < ViewComponent::Base
attr_reader :resources, :extra_resources
attr_reader :resources, :extra_resources, :front_matter

def initialize(resources: nil, extra_resources: {})
def initialize(resources: nil, extra_resources: {}, front_matter: {})
super

@resources = resources
@extra_resources = build_additional_resource_nodes(extra_resources)
@front_matter = front_matter
end

def before_render
Expand All @@ -19,14 +20,130 @@ def all_resources

private

def nav_link(link_text, link_path)
tag.li class: class_name(link_path) do
link_to_unless_current(link_text, link_path, class: "link--black link--no-underline") { tag.div(link_text) }
def sync_mode(mode)
mode == :mobile ? :desktop : :mobile
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems slightly counterintuitive at first glance

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe there's a way of expressing what sync means in this context

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep I think I could phrase that better 👍


def nav_link(resource, mode)
title = resource.title
path = resource.path
li_id = "#{path.parameterize}-#{mode}"
li_sync_id = "#{path.parameterize}-#{sync_mode(mode)}"
child_menu_id = category_list_id(resource, mode)
child_menu_sync_id = category_list_id(resource, sync_mode(mode))
child_menu_ids = [child_menu_id, child_menu_sync_id].join(" ")
li_css = ("active" if uri_is_root?(path) || first_uri_segment_matches_link?(path)).to_s
show_dropdown = resource.children?
link_css = "menu-link link link--black link--no-underline"
aria_attributes = show_dropdown ? { expanded: false, controls: child_menu_ids } : {}
tag.li id: li_id, class: li_css, data: { "sync-id": li_sync_id, "child-menu-id": child_menu_id, "child-menu-sync-id": child_menu_sync_id, "direct-link": !show_dropdown, "toggle-secondary-navigation": show_dropdown, action: "keydown.tab->navigation#handleMenuTab" } do
safe_join([
link_to(path, class: link_css, aria: aria_attributes) do
safe_join([
tag.span(title, class: "menu-title"),
contracted_icon(visible: show_dropdown),
])
end,
if mode == :mobile && resource.children?
[
row_break,
category_list(resource, mode, css_class: "category-links-list hidden-menu hidden-desktop"),
]
end,
])
end
end

def category_list(resource, mode, css_class:)
tag.ol(class: css_class, id: category_list_id(resource, mode)) do
safe_join(
[
resource.children_without_subcategory.map do |child_resource|
nav_link(child_resource, mode)
end,
resource.subcategories.map do |category|
category_link(category, resource, mode)
end,
view_all_link(resource, mode),
],
)
end
end

def category_list_id(resource, mode)
"#{resource.path.parameterize}-categories-#{mode}"
end

def category_link(subcategory, resource, mode)
title = subcategory
li_id = "#{resource.path.parameterize}-#{title.parameterize}-#{mode}"
li_sync_id = "#{resource.path.parameterize}-#{title.parameterize}-#{sync_mode(mode)}"
child_menu_id = page_list_id(resource, subcategory, mode)
child_menu_sync_id = page_list_id(resource, subcategory, sync_mode(mode))
child_menu_ids = [child_menu_id, child_menu_sync_id].join(" ")
li_css = ("active" if subcategory == front_matter["subcategory"]).to_s
link_css = "menu-link link link--black link--no-underline btn-as-link"
aria_attributes = { expanded: false, controls: child_menu_ids }
tag.li id: li_id, class: li_css, data: { "sync-id": li_sync_id, "child-menu-id": child_menu_id, "child-menu-sync-id": child_menu_sync_id, "direct-link": false, action: "keydown.tab->navigation#handleMenuTab" } do
safe_join(
[
tag.button(type: "button", class: link_css, aria: aria_attributes) do
safe_join(
[
tag.span(title, class: "menu-title"),
contracted_icon(visible: true),
],
)
end,
row_break,
if mode == :mobile
page_list(resource, subcategory, mode, css_class: "page-links-list hidden-menu hidden-desktop")
end,
],
)
end
end

def page_list_id(resource, subcategory, mode)
"#{resource.path.parameterize}-#{subcategory.parameterize}-pages-#{mode}"
end

def page_list(resource, subcategory, mode, css_class:)
tag.ol(class: css_class, id: page_list_id(resource, subcategory, mode)) do
safe_join(
[
resource.children_in_subcategory(subcategory).map do |child_resource|
nav_link(child_resource, mode)
end,
],
)
end
end

def class_name(link_path)
"active" if uri_is_root?(link_path) || first_uri_segment_matches_link?(link_path)
def view_all_link(resource, mode)
title = "View all in #{resource.title}"
path = resource.path
id = "menu-view-all-#{path.parameterize}-#{mode}"
li_css = "view-all #{'active' if uri_is_root?(path)}"
link_css = "menu-link link link--black"

tag.li class: li_css, data: { id: id, "direct-link": true } do
safe_join([
link_to(path, class: link_css) do
tag.span(title, class: "menu-title")
end,
])
end
end

def row_break
tag.div(class: "break", "aria-hidden": true)
end

def contracted_icon(visible: true)
if visible
tag.span(class: "nav-icon nav-icon__contracted", aria: { hidden: true })
end
end

def uri_is_root?(link_path)
Expand Down
7 changes: 5 additions & 2 deletions app/components/header_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
<%= render Header::ExtraNavigationComponent.new(search_input_id: "searchbox__input--desktop") %>
<%= render Header::LogoComponent.new %>
<div class="menu-button" id="mobile-navigation-menu-button">
<%= tag.button(class: "menu-button__button", id: 'menu-toggle', aria: { expanded: false, controls: 'primary-navigation'}, data: { action: "click->navigation#toggleNav", "navigation-target" => "menu" }) do %>
<%= link_to(browse_path, class: "menu-button__button hidden-when-js-enabled") do %>
<span class="menu-button__text">Menu</span>
<% end %>
<%= tag.button(class: "menu-button__button hidden-when-js-disabled", id: 'menu-toggle', aria: { expanded: false, controls: 'primary-navigation'}, data: { action: "click->navigation#toggleNav", "navigation-target" => "menu" }) do %>
<span class="menu-button__text">Menu</span>
<span class="menu-button__icon"></span>
<% end %>
</div>
<%= render Header::NavigationComponent.new %>
<%= render Header::NavigationComponent.new(front_matter: front_matter) %>
</header>

<% if breadcrumbs %>
Expand Down
5 changes: 3 additions & 2 deletions app/components/header_component.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
class HeaderComponent < ViewComponent::Base
attr_reader :breadcrumbs
attr_reader :breadcrumbs, :front_matter

def initialize(breadcrumbs: false)
def initialize(breadcrumbs: false, front_matter: {})
super

@breadcrumbs = breadcrumbs
@front_matter = front_matter
end
end
20 changes: 20 additions & 0 deletions app/models/pages/navigation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@ def children
navigation.all_pages.select { |page| page.path.start_with?(path) && page.path != path }
end

def children?
children.any?
end

def children_without_subcategory
children.select { |page| page.subcategory.nil? }
end

def children_in_subcategory(subcategory)
children.select { |page| page.subcategory == subcategory }
end

def subcategories
children.map(&:subcategory).compact.uniq
end

def subcategories?
subcategories.any?
end

def menu?
@menu
end
Expand Down
2 changes: 1 addition & 1 deletion app/views/layouts/category.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

<% grouped_categories(@page.path).each do |title, pages| %>
<section class="container category__cards main-section">
<%= tag.h2(title, class: "heading--box-blue") %>
<%= tag.h2(title, id: title.parameterize, class: "heading--box-blue") %>
<%= tag.nav(aria: { label: "#{title} category" }, class: "category__nav-cards inset") do %>
<ul class="inset">
<%= render(Categories::CardComponent.with_collection(pages, heading_tag: "h3")) %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/layouts/content.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<%= render "sections/head" %>
<%= body_tag do %>
<%= render "sections/govuk_javascript" %>
<%= render HeaderComponent.new(breadcrumbs: true) %>
<%= render HeaderComponent.new(breadcrumbs: true, front_matter: @front_matter) %>

<%= main_tag do %>
<%= render Content::HeroComponent.new(@front_matter) %>
Expand Down
15 changes: 15 additions & 0 deletions app/views/pages/browse.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!-- The <ul> element is dynamically generated -->
<!-- //NOSONAR -->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this?

<h1>Menu</h1>
<% Pages::Navigation.root_pages.each do |resource| %>
<h3>
<%= link_to(resource.title, resource.path) %>
<% if resource.subcategories? %>
</h3>
<ul>
<% resource.subcategories.map do |category| %>
<li><%= category %></li>
<% end %>
</ul>
<% end %>
<% end %>
Loading
Loading