Skip to content

Commit 5199cbc

Browse files
committed
[WIP] push rough draft of nested nav
1 parent e6c05f9 commit 5199cbc

8 files changed

Lines changed: 212 additions & 13 deletions

File tree

app/assets/config/manifest.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//= link main.js
44
//= link dependencies.js
55
//= link test-dependencies.js
6+
//= link components/nested-navigation.js
67
//= link components/map.js
78
//= link components/map/defra-map.js
89
//= link components/map/map-style-liberty.js
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
window.GOVUK = window.GOVUK || {}
2+
window.GOVUK.Modules = window.GOVUK.Modules || {};
3+
4+
(function (Modules) {
5+
class NestedNavigation {
6+
constructor (module) {
7+
this.module = module
8+
const buttons = module.querySelectorAll('[data-app-c-nested-navigation-button]')
9+
10+
for (const button of buttons) {
11+
console.log(button)
12+
button.addEventListener('click', this.toggleMenu)
13+
}
14+
}
15+
16+
init () {
17+
console.log('Hello world!')
18+
}
19+
20+
toggleMenu (e) {
21+
const button = e.target
22+
23+
const menu = button.ariaControlsElements[0]
24+
menu.classList.toggle('js-app-c-nested-navigation--hidden')
25+
const ariaExpanded = button.getAttribute('aria-expanded') === 'true'
26+
button.setAttribute('aria-expanded', `${!ariaExpanded}`)
27+
}
28+
}
29+
Modules.NestedNavigation = NestedNavigation
30+
})(window.GOVUK.Modules)

app/assets/stylesheets/application.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
@import "components/calendar";
7272
@import "components/download-link";
7373
@import "components/map";
74+
@import "components/nested-navigation";
7475

7576
@import "govuk_web_banners/dependencies";
7677

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
.js-enabled {
2+
.js-app-c-nested-navigation--hidden {
3+
display: none;
4+
}
5+
6+
.app-c-nested-navigation__sub-list {
7+
position: absolute;
8+
}
9+
}
10+
11+
.app-c-nested-navigation {
12+
background-color: $govuk-blue-tint-95;
13+
border-bottom: 1px solid govuk-colour("blue");
14+
position: relative;
15+
}
16+
17+
.app-c-nested-navigation__button {
18+
background: none;
19+
border: 0;
20+
color: govuk-colour("blue");
21+
cursor: pointer;
22+
padding: 0;
23+
text-decoration: none;
24+
position: relative;
25+
@include govuk-link-style-default;
26+
@include govuk-font($size: 19);
27+
}
28+
29+
.app-c-nested-navigation__button[aria-expanded="false"] {
30+
background: none;
31+
}
32+
33+
.app-c-nested-navigation__button[aria-expanded="true"] {
34+
background-color: govuk-colour("white");
35+
}
36+
37+
.app-c-nested-navigation__button:focus[aria-expanded="true"], .app-c-nested-navigation__button:focus[aria-expanded="false"] {
38+
background-color: $govuk-focus-colour;
39+
}
40+
41+
42+
43+
.app-c-nested-navigation__link {
44+
text-decoration: none;
45+
}
46+
47+
// Shared styles for the expanded and collapsed arrow icon. Based off of the super navigation menu button.
48+
.app-c-nested-navigation__button::after {
49+
border-bottom: 2px solid govuk-colour("blue");
50+
border-right: 2px solid govuk-colour("blue");
51+
content: "";
52+
display: inline-block;
53+
height: 8px;
54+
margin-left: 10px;
55+
vertical-align: middle;
56+
width: 8px;
57+
}
58+
59+
.app-c-nested-navigation__button:focus::after {
60+
border-color: govuk-colour("black");
61+
}
62+
63+
.app-c-nested-navigation__button[aria-expanded="false"]::after {
64+
// Collapsed arrow icon
65+
transform: translateY(-35%) rotate(45deg) scale(1);
66+
}
67+
68+
.app-c-nested-navigation__button[aria-expanded="true"]::after {
69+
transform: translateY(1px) rotate(225deg) scale(1);
70+
}
71+
72+
73+
// .app-c-nested-navigation__button[aria-expanded=false]::after {
74+
// content: "▼"
75+
// }
76+
77+
// .app-c-nested-navigation__button[aria-expanded=true]::after {
78+
// content: "▲"
79+
// }
80+
81+
.app-c-nested-navigation__service-name:link {
82+
color: govuk-colour("black");
83+
font-weight: bold;
84+
}
85+
86+
.app-c-nested-navigation__sub-list {
87+
padding-left: 0;
88+
background-color: govuk-colour("white");
89+
}
90+
91+
.app-c-nested-navigation__sub-list-item {
92+
padding-top: 15px;
93+
padding-left: 15px;
94+
padding-right: 15px;
95+
list-style: none;
96+
@include govuk-font($size: 19);
97+
}
98+
99+
.app-c-nested-navigation__list-item {
100+
display: inline-block;
101+
vertical-align: top;
102+
@include govuk-font($size: 19);
103+
padding: 15px;
104+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
2+
<%
3+
items ||= nil
4+
first_item = items.shift if items
5+
%>
6+
7+
<%= javascript_include_tag "components/nested-navigation.js", integrity: false, type: "module" %>
8+
9+
<ul data-module="nested-navigation" class="app-c-nested-navigation">
10+
<li class="app-c-nested-navigation__list-item"> <%= link_to(first_item["text"], first_item["href"], class: "govuk-link app-c-nested-navigation__service-name") %> </li>
11+
<% items.each do | item | %>
12+
<% if item["links"] %>
13+
<li class="app-c-nested-navigation__list-item">
14+
<% menu_id = "nested-navigation-#{SecureRandom.hex(4)}" %>
15+
<%= tag.button data: { app_c_nested_navigation_button: ""}, aria: { controls: menu_id, expanded: false }, class: "app-c-nested-navigation__button" do %> <%= item["text"] %> <% end %>
16+
<ul class="js-app-c-nested-navigation--hidden app-c-nested-navigation__sub-list" id="<%= menu_id %>">
17+
<% item["links"].each do | sub_item | %>
18+
<li class="app-c-nested-navigation__sub-list-item"> <%= link_to(sub_item["text"], sub_item["href"], class: "govuk-link app-c-nested-navigation__link") %> </li>
19+
<% end %>
20+
</ul>
21+
</li>
22+
<% else %>
23+
<li class="app-c-nested-navigation__list-item"> <%= link_to(item["text"], item["href"], class: "govuk-link app-c-nested-navigation__link") %> </li>
24+
<% end %>
25+
<% end %>
26+
</ul>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name: Nested navigation (experimental)
2+
description: Renders a navigation that can contain a nested menu
3+
body: |
4+
5+
shared_accessibility_criteria:
6+
- sub_navigation_menu
7+
8+
accessibility_criteria: |
9+
To do
10+
11+
uses_component_wrapper_helper: true
12+
examples:
13+
default:
14+
data:
15+
items:
16+
- text: "Service Name"
17+
href: "/service-name"
18+
- text: "A basic link"
19+
href: "/something"
20+
- text: "Nested Heading"
21+
links:
22+
- text: "Example Link"
23+
href: "/hello-world"
24+
- text: "Example Link 2"
25+
href: "/hello-world"
26+
- text: "Example Link 3"
27+
href: "/hello-world"
28+
- text: "This is a very very very long link"
29+
href: "/hello-world"
30+
- text: "Another link that has a lot of characters"
31+
href: "/hello-world"
32+
- text: "Third link containing a lot of text"
33+
href: "/hello-world"
34+
- text: "Third Link"
35+
href: "/example1"
36+
- text: "Fourth Link"
37+
href: "/example1"
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// This file runs in the browser.
22

33
// Ensure window.__coverage__ is always present.
4-
window.__coverage__ ??= {};
4+
window.__coverage__ ??= {}
55

66
class NycTestCoverageReporter {
77
// The following method runs after every Jasmine describe block.
8-
static suiteDone(result) {
8+
static suiteDone (result) {
99
// Transfer test coverage information from the browser to node.js.
10-
jasmine.getEnv().setSuiteProperty("__coverage__", window.__coverage__);
10+
jasmine.getEnv().setSuiteProperty('__coverage__', window.__coverage__)
1111
}
1212
}
1313

14-
jasmine.getEnv().addReporter(NycTestCoverageReporter);
14+
jasmine.getEnv().addReporter(NycTestCoverageReporter)
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
// This file runs in node.js.
22

3-
import fsPromises from 'node:fs/promises';
4-
import {URL} from 'node:url';
5-
import path from 'node:path';
3+
import fsPromises from 'node:fs/promises'
4+
import { URL } from 'node:url'
5+
import path from 'node:path'
66

77
export default class NycTestCoverageReporter {
88
// The following method runs after every Jasmine describe block.
9-
suiteDone({properties}) {
10-
this.coverage = properties?.__coverage__ || this.coverage;
9+
suiteDone ({ properties }) {
10+
this.coverage = properties?.__coverage__ || this.coverage
1111
}
1212

13-
jasmineDone() {
13+
jasmineDone () {
1414
const rootDirname = path.dirname(
1515
new URL(import.meta.resolve('#root/package.json')).pathname
16-
);
17-
const nycCoverageFile = path.join(rootDirname, 'tmp', 'nyc_output', 'out.json');
18-
return fsPromises.writeFile(nycCoverageFile, JSON.stringify(this.coverage));
16+
)
17+
const nycCoverageFile = path.join(rootDirname, 'tmp', 'nyc_output', 'out.json')
18+
return fsPromises.writeFile(nycCoverageFile, JSON.stringify(this.coverage))
1919
}
2020
}

0 commit comments

Comments
 (0)