govuk_tech_docs 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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