Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
55 changes: 55 additions & 0 deletions lib/nunjucks/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,59 @@ exports.getHTMLCode = function (path) {
})
}

/**
* This helper function traverses the navigation object to find an item with a
* given `url`, and returns the ancestors of that item.
*
* NOTE: This is used to assemble breadcrumb navigation, so outputs the result
* with keys and values used by that component. These differ from those used
* internally.
*
* @param {string} targetUrl - The URL to look for, without leading `/`
* @returns {AncestorPage[]} Array of ancestor pages to this one
*/
exports.getAncestorPages = function (targetUrl) {
// Get navigation object from the template context
const navigationContext = this.lookup('navigation') ?? []

// Create a stack to store our levels of hierarchy in.
// The homepage doesn't appear in the `navigation` object,
// so it needs adding manually
const ancestors = [{ text: 'Home', href: '/' }]

// Create a recursive function we can use to navigate the nested objects
const traverse = function (navItemArray) {
// Using a for instead of a forEach so that we can break the loop once done.
for (let i = 0; i < navItemArray.length; i++) {
const navItem = navItemArray[i]

// If the target URL and item URL match, it's the current page. It doesn't
// get added to the stack, but it implies that we've reached our target
// so there's no reason to continue searching.
if (targetUrl === navItem.url) {
break
}

// If the target URL *begins* with the current item URL, it's an ancestor
// of the target page. Add it to the stack and start looking through it's
// child items.
if (navItem.items && targetUrl.startsWith(navItem.url)) {
ancestors.push({ text: navItem.label, href: `/${navItem.url}` })
traverse(navItem.items)
}
}
}

// Call the recursive function
traverse(navigationContext)

return ancestors
}

exports.getMacroOptions = getMacroOptions

/**
* @typedef {object} AncestorPage
* @property {string} href - The URL of the ancestor page
* @property {string} text - The title of the ancestor page
*/
2 changes: 2 additions & 0 deletions src/stylesheets/components/_split-pane.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ $toc-width-tablet: 210px;
@include govuk-media-query($from: tablet) {
display: flex;
min-width: 0;

padding-left: govuk-spacing(6);
flex: 1 1 100%;
flex-direction: column;
}
Expand Down
8 changes: 7 additions & 1 deletion src/stylesheets/components/_subnav.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Since the back to top link is outside the flow of the document we need to create a space for it.
// This number is magic and was determined by manually judging the visual spacing.
margin-bottom: 100px;
padding: govuk-spacing(6) govuk-spacing(3) 0 0;
padding-right: govuk-spacing(3);
@include govuk-font(16);
}

Expand Down Expand Up @@ -57,4 +57,10 @@
margin: 0;
padding: govuk-spacing(2) govuk-spacing(3) govuk-spacing(2) 0;
color: govuk-colour("dark-grey");

// Avoid padding on the first heading so its baseline is close
// enough to the breadcrumbs' baseline that they look aligned
&:first-of-type {
padding-top: 0;
}
}
16 changes: 12 additions & 4 deletions src/stylesheets/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,23 @@ $app-code-color: #d13118;
@include govuk-width-container(1100px);
}

.app-content {
padding: govuk-spacing(3) govuk-spacing(0) govuk-spacing(4);
.app-main-wrapper {
padding-top: govuk-spacing(3);
padding-bottom: govuk-spacing(4);

@include govuk-media-query($from: tablet) {
padding: govuk-spacing(6);
padding-right: 0;
padding-top: govuk-spacing(6);
padding-bottom: govuk-spacing(8);
}
}

.app-breadcrumbs {
// match top padding of the first item in the pane navigation so that the
// breadcrumbs line up nicely
margin: govuk-spacing(1) 0;
}

.app-content {
h1 {
max-width: 15em;
}
Expand Down
3 changes: 3 additions & 0 deletions views/layouts/_generic.njk
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{% extends "govuk/template.njk" %}

{% from "govuk/components/breadcrumbs/macro.njk" import govukBreadcrumbs %}
{% from "_cookie-banner.njk" import cookieBanner %}

{% set assetUrl = 'https://design-system.service.gov.uk/assets' %}
Expand Down Expand Up @@ -46,6 +48,7 @@
{% include "_header.njk" %}
{% include "_navigation.njk" %}
{% include "_banner.njk" %}

{% block body %}
{{ contents | safe }}
{% endblock %}
Expand Down
11 changes: 5 additions & 6 deletions views/layouts/layout-pane.njk
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@
{% set includeThemeInPageHeading = theme and "…" in theme %}

{% block body %}
<div class="app-split-pane app-width-container">
<div class="app-split-pane app-width-container app-main-wrapper">
<div class="app-split-pane__nav">
{% include "_subnav.njk" %}
</div>
<div class="app-split-pane__content">
{{ govukBreadcrumbs({
classes: "app-breadcrumbs",
items: getAncestorPages(permalink)
}) }}
<main id="main-content" class="app-content" role="main">
{% if not includeThemeInPageHeading %}
<span class="govuk-caption-xl">
{{ theme if theme else section }}
</span>
{% endif %}
<h1 class="govuk-heading-xl {%- if status %} govuk-!-margin-bottom-5{% endif %}">
{% if includeThemeInPageHeading %}
<span class="govuk-caption-xl">
Expand Down
14 changes: 9 additions & 5 deletions views/layouts/layout-single-page.njk
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
{% extends "_generic.njk" %}

{% block body %}
<main id="main-content" class="govuk-main-wrapper govuk-main-wrapper--l" role="main">
<div class="app-width-container">
<div class="app-width-container app-main-wrapper">
{{ govukBreadcrumbs({
classes: "app-breadcrumbs",
items: getAncestorPages(permalink)
}) }}

<main id="main-content" role="main">
{{ contents | safe }}
</div>
</main>
</main>
</div>

{% include "_footer.njk" %}

{% endblock %}
17 changes: 12 additions & 5 deletions views/layouts/layout.njk
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
{% extends "_generic.njk" %}

{% block body %}
{# Only render breadcrumbs on pages that aren't the homepage. This is a bit
hacky, but works as the homepage has an empty string for a permalink. #}
{% if permalink %}
{{ govukBreadcrumbs({
classes: "app-width-container",
items: getAncestorPages(permalink)
}) }}
{% endif %}

<main id="main-content" role="main">
{{ contents | safe }}
</main>

{% include "_footer.njk" %}
<main id="main-content" role="main">
{{ contents | safe }}
</main>

{% include "_footer.njk" %}
{% endblock %}