diff --git a/app/components/header/navigation_component.html.erb b/app/components/header/navigation_component.html.erb index 9a200bff04..880ad99243 100644 --- a/app/components/header/navigation_component.html.erb +++ b/app/components/header/navigation_component.html.erb @@ -1,7 +1,33 @@ - + <% end %> +<% end %> + + +<%= 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 %> + + + + + + +<% end %> diff --git a/app/components/header/navigation_component.rb b/app/components/header/navigation_component.rb index d2faa1f902..24d3a90fd7 100644 --- a/app/components/header/navigation_component.rb +++ b/app/components/header/navigation_component.rb @@ -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 @@ -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 corresponding_mode(mode) + mode == :mobile ? :desktop : :mobile + end + + def nav_link(resource, mode) + title = resource.title + path = resource.path + li_id = "#{path.parameterize}-#{mode}" + corresponding_li_id = "#{path.parameterize}-#{corresponding_mode(mode)}" + child_menu_id = category_list_id(resource, mode) + corresponding_child_menu_id = category_list_id(resource, corresponding_mode(mode)) + child_menu_ids = [child_menu_id, corresponding_child_menu_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: { "corresponding-id": corresponding_li_id, "child-menu-id": child_menu_id, "corresponding-child-menu-id": corresponding_child_menu_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}" + corresponding_li_id = "#{resource.path.parameterize}-#{title.parameterize}-#{corresponding_mode(mode)}" + child_menu_id = page_list_id(resource, subcategory, mode) + corresponding_child_menu_id = page_list_id(resource, subcategory, corresponding_mode(mode)) + child_menu_ids = [child_menu_id, corresponding_child_menu_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: { "corresponding-id": corresponding_li_id, "child-menu-id": child_menu_id, "corresponding-child-menu-id": corresponding_child_menu_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) diff --git a/app/components/header_component.html.erb b/app/components/header_component.html.erb index 907ac6c62e..e687d6e7bb 100644 --- a/app/components/header_component.html.erb +++ b/app/components/header_component.html.erb @@ -10,12 +10,15 @@ <%= render Header::ExtraNavigationComponent.new(search_input_id: "searchbox__input--desktop") %> <%= render Header::LogoComponent.new %> - <%= render Header::NavigationComponent.new %> + <%= render Header::NavigationComponent.new(front_matter: front_matter) %> <% if breadcrumbs %> diff --git a/app/components/header_component.rb b/app/components/header_component.rb index 0afb69fdeb..d1ef126e7e 100644 --- a/app/components/header_component.rb +++ b/app/components/header_component.rb @@ -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 diff --git a/app/models/pages/navigation.rb b/app/models/pages/navigation.rb index 13a2463e37..a75561e70c 100644 --- a/app/models/pages/navigation.rb +++ b/app/models/pages/navigation.rb @@ -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 diff --git a/app/views/layouts/category.html.erb b/app/views/layouts/category.html.erb index c62edbed50..9dec41fc9f 100644 --- a/app/views/layouts/category.html.erb +++ b/app/views/layouts/category.html.erb @@ -18,7 +18,7 @@ <% grouped_categories(@page.path).each do |title, pages| %>
- <%= 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 %>