Skip to content

Commit d6e65de

Browse files
Merge pull request #86 from studio24/feature/refactor-navigation-patterns
Feature/refactor navigation patterns
2 parents 5fe4cb4 + 3b17366 commit d6e65de

40 files changed

+1457
-800
lines changed

.stylelintignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
# Print stylesheet
77
assets-src/styles/sass/30-base/_print.scss
88

9-
# Advanced styles for navigation (Temporary - Sass to be reviewed in separate branch)
10-
assets-src/styles/sass/60-advanced-components/_navigation.scss
9+
# Ignore styles relating to third-party packages, where we might have less control
10+
assets-src/styles/sass/70-third-party-plugins/*.scss
1111

1212
# Styles for the Amplify website
1313
assets-src/styles/sass/85-amplify/*.scss

assets-src/js/main.js

+31-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import {exists} from "./main/_exists.helper.js";
2-
31
import {cardEnhancement} from "./main/cards.js";
42
import {collapsibles} from "./main/collapsibles.js";
53
import {disclosureWidget} from "./main/disclosure-widget.js";
@@ -14,30 +12,52 @@ function domLoadedActions() {
1412
formErrorSummary();
1513

1614
/* Create a navSingleLevel object and initiate single-level navigation for a <ul> with the correct data-component attribute */
17-
const navExampleSingle = document.querySelector('ul[data-component="nav-single"]');
15+
const navExampleSingle = document.querySelector('[data-component="nav-single"]');
1816

19-
if (exists(navExampleSingle)) {
17+
if (navExampleSingle) {
2018
let siteNav = new navSingleLevel(navExampleSingle, {
2119
breakpoint: 768,
2220
});
2321
siteNav.init();
2422
}
2523

26-
/* Create a navDoubleLevel object and initiate double-level navigation for a <ul> with the correct data-component attribute */
27-
const navExampleDouble = document.querySelector('ul[data-component="nav-double"]');
24+
/* Create a navDoubleLevel object and initiate double-level navigation */
25+
const navExampleDoubleSimple = document.querySelector('[data-nav-example="dbl1"] [data-component="nav-double"]');
26+
27+
if (navExampleDoubleSimple) {
28+
let siteNav = new navDoubleLevel(navExampleDoubleSimple, {
29+
breakpoint: 768,
30+
});
31+
siteNav.init();
32+
}
33+
34+
/* Create a navDoubleLevel object and initiate double-level navigation */
35+
const navExampleDoubleBack = document.querySelector('[data-nav-example="dbl2"] [data-component="nav-double"]');
2836

29-
if (exists(navExampleDouble)) {
30-
let siteNav = new navDoubleLevel(navExampleDouble, {
37+
if (navExampleDoubleBack) {
38+
let siteNav = new navDoubleLevel(navExampleDoubleBack, {
3139
breakpoint: 768,
3240
submenuDirection: 'horizontal',
3341
});
3442
siteNav.init();
3543
}
3644

37-
/* Create a navDoubleLevel object and initiate double-level navigation for a <ul> with the correct data-component attribute */
38-
const navDoubleIntro = document.querySelector('ul[data-component="nav-double-intro"]');
45+
/* Create a navDoubleLevel object and initiate double-level navigation with both links and buttons */
46+
const navDoubleBoth = document.querySelector('[data-nav-example="dbl3"] [data-component="nav-double"]');
47+
48+
if (navDoubleBoth) {
49+
let siteNav = new navDoubleLevel(navDoubleBoth, {
50+
breakpoint: 768,
51+
cloneTopLevelLink: false,
52+
replaceTopLevelLinks: false
53+
});
54+
siteNav.init();
55+
}
56+
57+
/* Create a navDoubleLevel object and initiate double-level navigation with intro text */
58+
const navDoubleIntro = document.querySelector('[data-component="nav-double-intro"]');
3959

40-
if (exists(navDoubleIntro)) {
60+
if (navDoubleIntro) {
4161
let siteNav = new navDoubleLevel(navDoubleIntro, {
4262
breakpoint: 768,
4363
cloneTopLevelLink: false,

assets-src/js/main/_exists.helper.js

-11
This file was deleted.

assets-src/js/main/cards.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import {exists} from './_exists.helper.js';
2-
31
/**
42
* Card enhancement to trigger the main link whenever the card area is clicked
53
* @see https://css-tricks.com/block-links-the-search-for-a-perfect-solution/
@@ -8,7 +6,7 @@ import {exists} from './_exists.helper.js';
86
var cardEnhancement = function () {
97
var cardsArray = Array.prototype.slice.call(document.querySelectorAll('[data-component="card"]'));
108

11-
if (exists(cardsArray)) {
9+
if (cardsArray) {
1210
// Loop through cards adding a click event and identifying the main link
1311
cardsArray.forEach(function (card) {
1412
var mainLink = card.querySelector('.card__link');

assets-src/js/main/collapsibles.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import {exists} from './_exists.helper.js';
2-
31
/**
42
* Collapsible panels
53
* @see https://heydon.github.io/inclusive-components-demos/collapsible-sections/progressive.html
@@ -10,7 +8,7 @@ var collapsibles = function () {
108
// Get all the collapsible containers
119
var collapseArray = Array.prototype.slice.call(document.querySelectorAll('[data-component="collapsibles"]'));
1210

13-
if (exists(collapseArray)) {
11+
if (collapseArray) {
1412
// Loop through containers
1513
collapseArray.forEach(function (item) {
1614
// Get headings inside a collapsible container

assets-src/js/main/disclosure-widget.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {exists} from './_exists.helper.js';
21
import './_closest.polyfill.js';
32

43
/**
@@ -16,7 +15,7 @@ var disclosureWidget = function () {
1615
});
1716
}
1817

19-
if (exists(toggleButtonArray)) {
18+
if (toggleButtonArray) {
2019
toggleButtonArray.forEach(function (btn) {
2120
btn.removeAttribute('style');
2221
btn.setAttribute('aria-expanded', 'false');

assets-src/js/main/form-error-summary.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
import {exists} from './_exists.helper.js';
2-
31
/**
42
* Shift focus to form error summary, if present
53
* @see https://design-system.service.gov.uk/components/error-summary/#how-it-works
64
*/
75
var formErrorSummary = function () {
86
var errorSummary = document.querySelector('[data-component="error-summary"]');
97

10-
if (exists(errorSummary)) {
8+
if (errorSummary) {
119
errorSummary.focus();
1210
}
1311
};

assets-src/js/main/nav-double-level.js

+44-13
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,33 @@ import './_closest.polyfill.js';
77
* Uses event delegation to handle events for improved performance, and data attributes for targeting elements
88
* Also manages button for toggling navigation on mobile
99
*
10-
* @param {Element} menu - the top level navigation <ul>
10+
* @param {Element} menu - the top level <ul> navigation
1111
* @param {Object} options - configuration options for the navigation
1212
* @param {number} [options.breakpoint=1024] - pixel value at which the button for toggling the mobile navigation is hidden. Is converted to em (assumes 16px browser default).
13-
* @param {boolean} [options.cloneTopLevelLink=true] - whether to copy the link to be replaced with a button and add it to the sub menu.
13+
* @param {boolean} [options.cloneTopLevelLink=true] - whether to copy the link that will be replaced with a button, and add it to the sub menu.
14+
* @param {boolean} [options.replaceTopLevelLinks=true] - whether to swap the top level link for a button, or add a button after the link
1415
* @param {string} [options.mobileIcon] - SVG icon used for the button to show/hide the navigation on mobile.
1516
* @param {string} [options.submenuIcon] - SVG icon used for sub menus and back button.
1617
* @param {string} [options.submenuDirection=vertical] - direction in which sub menus operate on mobile (vertical, or horizontal with a 'back' button).
1718
* @param {boolean} [options.submenuIntro=false] - whether the sub menu includes introductory text.
1819
*/
1920

2021
const navDoubleLevel = function(menu, options) {
21-
let container = menu.parentElement;
2222
let mobileToggle = document.querySelector('[data-trigger="mobile-nav"]');
2323

2424
// Default settings
2525
let defaults = {
2626
breakpoint: 1024,
2727
cloneTopLevelLink: true,
28+
replaceTopLevelLinks: true,
2829
mobileIcon: '<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" class="icon icon--24" focusable="false" aria-hidden="true" fill="currentColor">' +
2930
'<path class="open" d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/>' +
3031
'<path class="close" d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>' +
3132
'</svg>',
32-
submenuDirection: 'vertical',
3333
submenuIcon: '<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" class="icon icon--24" focusable="false" aria-hidden="true" fill="currentColor">' +
34-
'<path class="control-vertical" d="M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z" />' +
35-
'<path class="control-horizontal" d="M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>' +
34+
'<path d="M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z" />' +
3635
'</svg>',
36+
submenuDirection: 'vertical',
3737
submenuIntro: false,
3838
};
3939

@@ -143,23 +143,54 @@ const navDoubleLevel = function(menu, options) {
143143
}
144144

145145
function menuSetup() {
146-
container.setAttribute('id', 'js-click-nav-' + settings.submenuDirection);
147-
if (settings.submenuIntro === true) {
148-
container.classList.add('js-nav-with-intro');
149-
}
146+
if (settings.submenuIntro === true) {
147+
menu.setAttribute('id','js-click-nav-intro');
148+
} else if (settings.replaceTopLevelLinks === false) {
149+
menu.setAttribute('id','js-click-nav-both');
150+
} else {
151+
menu.setAttribute('id', 'js-click-nav-' + settings.submenuDirection);
152+
}
153+
150154
const subMenuWrappers = Array.prototype.slice.call(menu.querySelectorAll('[data-nav="submenu"]'));
151155

152156
subMenuWrappers.forEach(function (wrapper) {
153157
const menuItem = wrapper.parentElement;
154158

155159
if ('undefined' !== typeof wrapper) {
156-
let button = convertLinkToButton(menuItem);
157-
158-
setUpAria(wrapper, button);
160+
if (settings.replaceTopLevelLinks === true) {
161+
let button = convertLinkToButton(menuItem);
162+
setUpAria(wrapper, button);
163+
} else {
164+
let button = addButtonAfterLink(menuItem);
165+
setUpAria(wrapper, button);
166+
}
159167
}
160168
});
161169
}
162170

171+
function addButtonAfterLink(menuItem) {
172+
const link = menuItem.getElementsByTagName('a')[0];
173+
const icon = settings.submenuIcon;
174+
const button = document.createElement('button');
175+
let subMenu = link.nextElementSibling.querySelector('ul');
176+
button.setAttribute('data-trigger', 'sub-nav');
177+
button.innerHTML = icon + '<span class="visuallyhidden">' + link.textContent +' menu</span>';
178+
link.after(button);
179+
180+
if (settings.submenuDirection === 'horizontal') {
181+
// Insert a "back" button
182+
const backButton = document.createElement('button');
183+
backButton.setAttribute('data-button', 'mobile-back');
184+
backButton.setAttribute('class', 'button button--ghost');
185+
backButton.innerHTML = icon + ' Back';
186+
if (settings.submenuIntro === true) {
187+
subMenu.parentNode.insertBefore(backButton, subMenu.parentNode.children[0]);
188+
} else subMenu.parentNode.insertBefore(backButton, subMenu);
189+
}
190+
191+
return button;
192+
}
193+
163194
/**
164195
* Why do this? See https://justmarkup.com/articles/2019-01-21-the-link-to-button-enhancement/
165196
*/

assets-src/js/package-extensions/s24-splide.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44
*/
55

66
import Splide from "@splidejs/splide";
7-
import {exists} from '../main/_exists.helper.js';
87

98
// Get all carousel instances
109
var splideArray = Array.prototype.slice.call(document.querySelectorAll('[data-component="carousel"]'));
1110

12-
if (exists(splideArray)) {
11+
if (splideArray) {
1312
// Loop through them
1413
splideArray.forEach(function (carousel) {
1514
new Splide(carousel).mount();

assets-src/styles/advanced.scss

+30-20
Original file line numberDiff line numberDiff line change
@@ -5,55 +5,65 @@
55
/*------------------------------------*\
66
#Settings
77
\*------------------------------------*/
8-
@import "sass/00-settings/breakpoints";
9-
@import "sass/00-settings/colors";
8+
@import 'sass/00-settings/breakpoints';
9+
@import 'sass/00-settings/colors';
1010
//@import "sass/00-settings/debug";
11-
@import "sass/00-settings/typesetting";
11+
@import 'sass/00-settings/typesetting';
1212

1313

1414
/*------------------------------------*\
1515
#Functions
1616
\*------------------------------------*/
17-
@import "sass/10-functions/units";
17+
@import 'sass/10-functions/units';
1818

1919

2020
/*------------------------------------*\
2121
#Mixins
2222
\*------------------------------------*/
23-
@import "sass/20-mixins/font-sizes";
24-
@import "sass/20-mixins/media-query";
25-
@import "sass/20-mixins/stack";
23+
@import 'sass/20-mixins/font-sizes';
24+
@import 'sass/20-mixins/media-query';
25+
@import 'sass/20-mixins/stack';
2626

2727

2828
/*------------------------------------*\
2929
#Base
3030
3131
Repeated to allow for extending placeholders
3232
\*------------------------------------*/
33-
@import "sass/30-base/hide-and-show";
33+
@import 'sass/30-base/hide-and-show';
3434

3535

3636
/*------------------------------------*\
3737
#Advanced components
3838
3939
For JS enhancements
4040
\*------------------------------------*/
41-
@import "sass/60-advanced-components/navigation";
42-
@import "sass/60-advanced-components/cards";
43-
@import "sass/60-advanced-components/collapsibles";
44-
@import "sass/60-advanced-components/disclosure-widget";
45-
@import "sass/60-advanced-components/sortable-tables";
46-
//@import "sass/60-advanced-components/slider";
41+
@import 'sass/60-advanced-components/cards';
42+
@import 'sass/60-advanced-components/collapsibles';
43+
@import 'sass/60-advanced-components/disclosure-widget';
44+
@import 'sass/60-advanced-components/sortable-tables';
45+
46+
47+
/*------------------------------------*\
48+
#Advanced navigation
49+
50+
Where JS is involved
51+
\*------------------------------------*/
52+
@import 'sass/65-advanced-navigation/nav';
53+
@import 'sass/65-advanced-navigation/nav-double';
54+
@import 'sass/65-advanced-navigation/nav-double-both';
55+
@import 'sass/65-advanced-navigation/nav-double-with-intro';
56+
4757

4858
/*------------------------------------*\
4959
#Plugins
5060
5161
Add third-party plugins here.
5262
Add custom styles directly after the original in a separate file
5363
\*------------------------------------*/
54-
@import "../../node_modules/accessible-autocomplete/src/autocomplete";
55-
@import "sass/70-third-party-plugins/s24-accessible-autocomplete";
56-
@import "../../node_modules/@splidejs/splide/src/css/core/index";
57-
@import "sass/70-third-party-plugins/s24-splide-carousel";
58-
@import "../../node_modules/tabbyjs/src/sass/tabby-ui";
59-
@import "sass/70-third-party-plugins/s24-tabby-ui";
64+
@import '../../node_modules/accessible-autocomplete/src/autocomplete';
65+
@import 'sass/70-third-party-plugins/s24-accessible-autocomplete';
66+
@import '../../node_modules/@splidejs/splide/src/css/core/index';
67+
@import 'sass/70-third-party-plugins/s24-splide-carousel';
68+
@import '../../node_modules/tabbyjs/src/sass/tabby-ui';
69+
@import 'sass/70-third-party-plugins/s24-tabby-ui';

0 commit comments

Comments
 (0)