govuk_tech_docs 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 52f8eae7cd727358efc55feadd52cbbfdfae7e79
4
- data.tar.gz: d8268e894f4a7d2c39750fca552f94d3b414e598
3
+ metadata.gz: d445bcd8c1daf9778f0a3e0d8cb352fab848b46c
4
+ data.tar.gz: d7fbb83f2f8e7f339033063acb560ad555fae7ce
5
5
  SHA512:
6
- metadata.gz: ec6710814517280e5a6d9887088802ab45326db1ad60726f8eca1f9bafbf9fa04d127045d3341ce635ce966eb0d8a07b6fd65d603afcf5f7636fb685e2f077a9
7
- data.tar.gz: 2584e62bd114d6f6623a5e64685681b3cd8ddd94efa80a0e668f36d531edb747d308066cbaac7e692581612f3a48dff29ff14d13802579dee637dd89edf8b77a
6
+ metadata.gz: 5708cfb64dd99b8741711fcea030bffbcf81c7da49dfaed23a99215fae6140e07200a390760ff0e2334555787ffc9af191c454be96f74a679d74b93cf2723996
7
+ data.tar.gz: 42eb4aa9614ae826b2214ebfc4db24aae6c089e1d571986858baf348527382600940b3c7dc80c2fa864477397aca3752a2b8c4972185685f190173fe6ca72988
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.0
4
+
5
+ Adds multiple page navigation support and collapsible top level navigation
6
+ items. When enabled the table of contents will display other pages on the site.
7
+
8
+ More info:
9
+ - https://github.com/alphagov/tech-docs-gem/pull/27
10
+
3
11
  ## 1.3.1
4
12
 
5
13
  Minor update to Google Analytics tracking
@@ -49,6 +49,22 @@ Example:
49
49
  host: https://docs.cloud.service.gov.uk
50
50
  ```
51
51
 
52
+ ## `collapsible_nav`
53
+
54
+ Enable collapsible navigation in the sidebar. Defaults to false;
55
+
56
+ ```yaml
57
+ collapsible_nav: true
58
+ ```
59
+
60
+ ## `multipage_nav`
61
+
62
+ Enable multipage navigation in the sidebar. Defaults to false;
63
+
64
+ ```yaml
65
+ multipage_nav: true
66
+ ```
67
+
52
68
  ## `max_toc_heading_level`
53
69
 
54
70
  Table of contents depth – how many levels to include in the table of contents. If your ToC is too long, reduce this number and we'll only show higher-level headings.
data/docs/frontmatter.md CHANGED
@@ -110,6 +110,17 @@ title: My beautiful page
110
110
  ---
111
111
  ```
112
112
 
113
+ ## `weight`
114
+
115
+ Affects the order a page is displayed in the sidebar navigation tree. Lower
116
+ weights float to the top. Higher weights sink to the bottom.
117
+
118
+ ```yaml
119
+ ---
120
+ weight: 20
121
+ ---
122
+ ```
123
+
113
124
  ## `parent`
114
125
 
115
126
  The page that should be highlighted as ‘active’ in the navigation.
@@ -17,6 +17,12 @@ header_links:
17
17
  # Tracking ID from Google Analytics (e.g. UA-XXXX-Y)
18
18
  ga_tracking_id:
19
19
 
20
+ # Enable multipage navigation in the sidebar
21
+ multipage_nav: true
22
+
23
+ # Enable collapsible navigation in the sidebar
24
+ collapsible_nav: true
25
+
20
26
  # Table of contents depth – how many levels to include in the table of contents.
21
27
  # If your ToC is too long, reduce this number and we'll only show higher-level
22
28
  # headings.
@@ -0,0 +1,93 @@
1
+ (function($, Modules) {
2
+ 'use strict';
3
+
4
+ Modules.CollapsibleNavigation = function () {
5
+
6
+ var $contentPane;
7
+ var $nav;
8
+ var $topLevelItems;
9
+ var $headings;
10
+ var $listings;
11
+
12
+ var $openLink;
13
+ var $closeLink;
14
+
15
+ this.start = function ($element) {
16
+ $contentPane = $('.app-pane__content');
17
+ $nav = $element;
18
+ $topLevelItems = $nav.find('> ul > li');
19
+ $headings = $topLevelItems.find('> a');
20
+ $listings = $topLevelItems.find('> ul');
21
+
22
+ // Attach collapsible heading functionality,on mobile and desktop
23
+ collapsibleHeadings();
24
+ openActiveHeading();
25
+ $contentPane.on('scroll', _.debounce(openActiveHeading, 100, { maxWait: 100 }));
26
+
27
+ };
28
+
29
+ function collapsibleHeadings() {
30
+ for (var i = $topLevelItems.length - 1; i >= 0; i--) {
31
+ var $topLevelItem = $($topLevelItems[i]);
32
+ var $heading = $topLevelItem.find('> a');
33
+ var $listing = $topLevelItem.find('> ul');
34
+ // Only add collapsible functionality if there are children.
35
+ if ($listing.length == 0) {
36
+ continue;
37
+ }
38
+ $topLevelItem.addClass('collapsible');
39
+ $listing.addClass('collapsible__body')
40
+ .attr('aria-expanded', 'false');
41
+ $heading.addClass('collapsible__heading')
42
+ .after('<button class="collapsible__toggle"><span class="collapsible__toggle-label">Expand ' + $heading.text() + '</span><span class="collapsible__toggle-icon" aria-hidden="true"></button>')
43
+ $topLevelItem.on('click', '.collapsible__toggle', function(e) {
44
+ e.preventDefault();
45
+ var $parent = $(this).parent();
46
+ toggleHeading($parent);
47
+ });
48
+ }
49
+ }
50
+
51
+ function toggleHeading($topLevelItem) {
52
+ var isOpen = $topLevelItem.hasClass('is-open');
53
+ var $heading = $topLevelItem.find('> a');
54
+ var $body = $topLevelItem.find('collapsible__body');
55
+ var $toggleLabel = $topLevelItem.find('.collapsible__toggle-label');
56
+
57
+ $topLevelItem.toggleClass('is-open', !isOpen);
58
+ $body.attr('aria-expanded', isOpen ? 'true' : 'false');
59
+ $toggleLabel.text(isOpen ? 'Expand ' + $heading.text() : 'Collapse ' + $heading.text());
60
+ }
61
+
62
+ function openActiveHeading() {
63
+ var $activeElement;
64
+ var currentPath = window.location.pathname;
65
+ var isActiveTrail = '[href*="' + currentPath + '"]';
66
+ // Add an exception for the root page, as every href includes /
67
+ if(currentPath == '/') {
68
+ isActiveTrail = '[href="' + currentPath + window.location.hash + '"]'
69
+ }
70
+ for (var i = $topLevelItems.length - 1; i >= 0; i--) {
71
+ var $element = $($topLevelItems[i]);
72
+ var $heading = $element.find('> a');
73
+ // Check if this item href matches
74
+ if($heading.is(isActiveTrail)) {
75
+ $activeElement = $element;
76
+ break;
77
+ }
78
+ // Otherwise check the children
79
+ var $children = $element.find('li > a');
80
+ var $matchingChildren = $children.filter(isActiveTrail);
81
+ if ($matchingChildren.length) {
82
+ $activeElement = $element;
83
+ break;
84
+ }
85
+ }
86
+ if($activeElement && !$activeElement.hasClass('is-open')) {
87
+ toggleHeading($activeElement);
88
+ }
89
+ }
90
+
91
+
92
+ };
93
+ })(jQuery, window.GOVUK.Modules);
@@ -70,8 +70,12 @@
70
70
  }
71
71
 
72
72
  function highlightActiveItemInToc(fragment) {
73
- var $activeTocItem = $tocItems.filter('[href="' + fragment + '"]');
74
-
73
+ var $activeTocItem = $tocItems.filter('[href="' + window.location.pathname + fragment + '"]');
74
+ // Navigation items with children don't contain fragments in their url
75
+ // Check to see if any nav items contain just the path name.
76
+ if(!$activeTocItem.get(0)) {
77
+ $activeTocItem = $tocItems.filter('[href="' + window.location.pathname + '"]');
78
+ }
75
79
  if ($activeTocItem.get(0)) {
76
80
  $tocItems.removeClass('toc-link--in-view');
77
81
  $activeTocItem.addClass('toc-link--in-view');
@@ -4,15 +4,23 @@
4
4
  Modules.TableOfContents = function () {
5
5
  var $html = $('html');
6
6
 
7
+ var $contentPane;
7
8
  var $toc;
8
9
  var $tocList;
10
+ var $topLevelItems;
11
+ var $headings;
12
+ var $listings;
9
13
 
10
14
  var $openLink;
11
15
  var $closeLink;
12
16
 
13
17
  this.start = function ($element) {
18
+ $contentPane = $('.app-pane__content');
14
19
  $toc = $element;
15
20
  $tocList = $toc.find('.js-toc-list');
21
+ $topLevelItems = $tocList.find('> ul > li');
22
+ $headings = $topLevelItems.find('> a');
23
+ $listings = $topLevelItems.find('> ul');
16
24
 
17
25
  // Open link is not inside the module
18
26
  $openLink = $html.find('.js-toc-show');
@@ -44,7 +52,7 @@
44
52
  // scrolling in that direction will scroll the body 'behind' the table of
45
53
  // contents. Fix this by preventing ever reaching the top or bottom of the
46
54
  // table of contents (by 1 pixel).
47
- //
55
+ //
48
56
  // http://blog.christoffer.me/six-things-i-learnt-about-ios-safaris-rubber-band-scrolling/
49
57
  $toc.on("touchstart.toc", function () {
50
58
  var $this = $(this),
@@ -62,7 +70,7 @@
62
70
 
63
71
  function openNavigation() {
64
72
  $html.addClass('toc-open');
65
-
73
+
66
74
  toggleBackgroundVisiblity(false);
67
75
  updateAriaAttributes();
68
76
 
@@ -4,6 +4,7 @@
4
4
  //= require _modules/navigation
5
5
  //= require _modules/page-expiry
6
6
  //= require _modules/table-of-contents
7
+ //= require _modules/collapsible-navigation
7
8
 
8
9
  $(document).ready(function() {
9
10
  GOVUK.modules.start();
@@ -30,6 +30,7 @@ $desktop-breakpoint: 992px !default;
30
30
  @import "modules/contribution-banner";
31
31
  @import "modules/technical-documentation";
32
32
  @import "modules/toc";
33
+ @import "modules/collapsible";
33
34
 
34
35
  @import "accessibility";
35
36
 
@@ -0,0 +1,45 @@
1
+ // Collapsible JS component styling, made for the navigation tree.
2
+ // These classes are added in table-of-contents.js.
3
+ // They should not be applied without the JS.
4
+
5
+ .collapsible {
6
+ position: relative;
7
+ }
8
+ .collapsible__body {
9
+ display: none;
10
+ .collapsible.is-open & {
11
+ display: block
12
+ }
13
+ }
14
+ .collapsible__toggle {
15
+ position: absolute;
16
+ top: 0;
17
+ right: -25px;
18
+ width: 50px;
19
+ height: 40px;
20
+ overflow: hidden;
21
+ text-indent: -999em;
22
+ border: 0;
23
+ background: 0;
24
+ color: inherit;
25
+ padding: 0;
26
+ &:focus {
27
+ outline: 3px solid $focus-colour;
28
+ }
29
+ }
30
+ .collapsible__toggle-icon {
31
+ position: absolute;
32
+ top: 0;
33
+ right: 30px;
34
+ &::after {
35
+ content: '';
36
+ display: block;
37
+ background: no-repeat file-url('arrow-down.svg') center center;
38
+ background-size: 18px auto;
39
+ width: 20px;
40
+ height: 40px;
41
+ }
42
+ .collapsible.is-open &::after {
43
+ background-image: file-url('arrow-up.svg');
44
+ }
45
+ }
@@ -27,7 +27,7 @@
27
27
 
28
28
  a:link, a:visited {
29
29
  display: block;
30
- padding: 8px $gutter-half;
30
+ padding: 8px 40px 8px $gutter-half;
31
31
  margin: 0 $gutter-half * -1;
32
32
  border-left: 5px solid transparent;
33
33
 
@@ -46,11 +46,6 @@
46
46
  }
47
47
 
48
48
  @include media(tablet) {
49
- // Level 2
50
- > ul > li > ul {
51
- margin-bottom: 20px;
52
- }
53
-
54
49
  // Level 3
55
50
  li li li {
56
51
  a:link, a:visited {
@@ -1,10 +1,11 @@
1
1
  module GovukTechDocs
2
2
  module TableOfContents
3
3
  class Heading
4
- def initialize(element_name:, text:, attributes:)
4
+ def initialize(element_name:, text:, attributes:, page_url: '')
5
5
  @element_name = element_name
6
6
  @text = text
7
7
  @attributes = attributes
8
+ @page_url = page_url
8
9
  end
9
10
 
10
11
  def size
@@ -12,7 +13,7 @@ module GovukTechDocs
12
13
  end
13
14
 
14
15
  def href
15
- '#' + @attributes['id']
16
+ @page_url + '#' + @attributes['id']
16
17
  end
17
18
 
18
19
  def title
@@ -1,8 +1,9 @@
1
1
  module GovukTechDocs
2
2
  module TableOfContents
3
3
  class HeadingsBuilder
4
- def initialize(html)
4
+ def initialize(html, url)
5
5
  @html = html
6
+ @url = url
6
7
  end
7
8
 
8
9
  def headings
@@ -10,7 +11,8 @@ module GovukTechDocs
10
11
  Heading.new(
11
12
  element_name: element.node_name,
12
13
  text: element.content,
13
- attributes: convert_nokogiri_attr_objects_to_hashes(element.attributes)
14
+ attributes: convert_nokogiri_attr_objects_to_hashes(element.attributes),
15
+ page_url: @url
14
16
  )
15
17
  end
16
18
  end
@@ -7,16 +7,63 @@ require 'govuk_tech_docs/table_of_contents/headings_builder'
7
7
  module GovukTechDocs
8
8
  module TableOfContents
9
9
  module Helpers
10
- def table_of_contents(html, max_level: nil)
11
- headings = HeadingsBuilder.new(html).headings
10
+ def single_page_table_of_contents(html, url: '', max_level: nil)
11
+ headings = HeadingsBuilder.new(html, url).headings
12
12
 
13
13
  if headings.none? { |heading| heading.size == 1 }
14
- raise "No H1 tag found. You have to at least add one H1 heading to the page."
14
+ raise "No H1 tag found. You have to at least add one H1 heading to the page: " + url
15
15
  end
16
16
 
17
17
  tree = HeadingTreeBuilder.new(headings).tree
18
18
  HeadingTreeRenderer.new(tree, max_level: max_level).html
19
19
  end
20
+
21
+ def multi_page_table_of_contents(resources, current_page, config, current_page_html = nil)
22
+ # Only parse top level html files
23
+ # Sorted by weight frontmatter
24
+ resources = resources
25
+ .select { |r| r.path.end_with?(".html") && (r.parent.nil? || r.parent.url == "/") }
26
+ .sort_by { |r| [r.data.weight ? 0 : 1, r.data.weight || 0] }
27
+
28
+ render_page_tree(resources, current_page, config, current_page_html)
29
+ end
30
+
31
+ def render_page_tree(resources, current_page, config, current_page_html)
32
+ # Sort by weight frontmatter
33
+ resources = resources
34
+ .sort_by { |r| [r.data.weight ? 0 : 1, r.data.weight || 0] }
35
+ output = '';
36
+ resources.each do |resource|
37
+ # Reuse the generated content for the active page
38
+ # If we generate it twice it increments the heading ids
39
+ content =
40
+ if current_page.url == resource.url && current_page_html
41
+ current_page_html
42
+ else
43
+ resource.render(layout: false)
44
+ end
45
+ # Avoid redirect pages
46
+ next if content.include? "http-equiv=refresh"
47
+ # If this page has children, just print the title and recursively
48
+ # render the children.
49
+ # If not, print the heading structure.
50
+ # We avoid printing the children of the root index.html as it is the
51
+ # parent of every other top level file.
52
+ if resource.children.any? && resource.url != "/"
53
+ output += %{<ul><li><a href="#{resource.url}">#{resource.data.title}</a>\n}
54
+ output += render_page_tree(resource.children, current_page, config, current_page_html)
55
+ output += '</li></ul>'
56
+ else
57
+ output +=
58
+ single_page_table_of_contents(
59
+ content,
60
+ url: resource.url,
61
+ max_level: config[:tech_docs][:max_toc_heading_level]
62
+ )
63
+ end
64
+ end
65
+ output
66
+ end
20
67
  end
21
68
  end
22
69
  end
@@ -1,3 +1,3 @@
1
1
  module GovukTechDocs
2
- VERSION = "1.3.1".freeze
2
+ VERSION = "1.4.0".freeze
3
3
  end
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 34 34" style="enable-background:new 0 0 34 34;" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#0B0C0C;}
7
+ </style>
8
+ <polygon class="st0" points="17,25.7 1.6,11.5 4.4,8.5 17,20.3 29.6,8.5 32.4,11.5 "/>
9
+ </svg>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
4
+ viewBox="0 0 34 34" style="enable-background:new 0 0 34 34;" xml:space="preserve">
5
+ <style type="text/css">
6
+ .st0{fill:#0B0C0C;}
7
+ </style>
8
+ <polygon class="st0" points="29.6,25.5 17,13.7 4.4,25.5 1.6,22.5 17,8.3 32.4,22.5 "/>
9
+ </svg>
@@ -45,7 +45,7 @@
45
45
  <div class="app-pane__toc">
46
46
  <div class="toc" data-module="table-of-contents">
47
47
  <a href="#" class="toc__close js-toc-close" aria-controls="toc" aria-label="Hide table of contents"></a>
48
- <nav id="toc" class="js-toc-list toc__list" aria-labelledby="toc-heading">
48
+ <nav id="toc" class="js-toc-list toc__list" aria-labelledby="toc-heading"<%= " data-module=\"collapsible-navigation\"" if config[:tech_docs][:collapsible_nav] %>>
49
49
  <%= yield_content :sidebar %>
50
50
  </nav>
51
51
  </div>
@@ -5,10 +5,12 @@ wrap_layout :core do
5
5
  content_for(:toc_module, "in-page-navigation")
6
6
 
7
7
  content_for :sidebar do
8
- table_of_contents(
9
- html,
10
- max_level: config[:tech_docs][:max_toc_heading_level]
11
- )
8
+ if config[:tech_docs][:multipage_nav] %>
9
+ <%= multi_page_table_of_contents(sitemap.resources, current_page, config, html) %>
10
+ <% else %>
11
+ <%= single_page_table_of_contents(html, max_level: config[:tech_docs][:max_toc_heading_level]) %>
12
+ <% end %>
13
+ <%
12
14
  end
13
15
 
14
16
  html
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk_tech_docs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Government Digital Service
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-05-21 00:00:00.000000000 Z
11
+ date: 2018-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -280,6 +280,7 @@ files:
280
280
  - lib/assets/javascripts/_analytics.js
281
281
  - lib/assets/javascripts/_govuk/modules.js
282
282
  - lib/assets/javascripts/_modules/anchored-headings.js
283
+ - lib/assets/javascripts/_modules/collapsible-navigation.js
283
284
  - lib/assets/javascripts/_modules/in-page-navigation.js
284
285
  - lib/assets/javascripts/_modules/navigation.js
285
286
  - lib/assets/javascripts/_modules/page-expiry.js
@@ -314,6 +315,7 @@ files:
314
315
  - lib/assets/stylesheets/govuk_frontend_toolkit/design-patterns/_media-player.scss
315
316
  - lib/assets/stylesheets/modules/_anchored-heading.scss
316
317
  - lib/assets/stylesheets/modules/_app-pane.scss
318
+ - lib/assets/stylesheets/modules/_collapsible.scss
317
319
  - lib/assets/stylesheets/modules/_contribution-banner.scss
318
320
  - lib/assets/stylesheets/modules/_footer.scss
319
321
  - lib/assets/stylesheets/modules/_govuk-logo.scss
@@ -347,6 +349,8 @@ files:
347
349
  - lib/source/favicon.ico
348
350
  - lib/source/images/anchored-heading-icon-2x.png
349
351
  - lib/source/images/anchored-heading-icon.png
352
+ - lib/source/images/arrow-down.svg
353
+ - lib/source/images/arrow-up.svg
350
354
  - lib/source/images/gov.uk_logotype_crown-2x.png
351
355
  - lib/source/images/gov.uk_logotype_crown.png
352
356
  - lib/source/images/gov.uk_logotype_crown_invert_trans.png