govspeak 10.6.5 → 10.8.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
  SHA256:
3
- metadata.gz: b55c7097bba63f4d11e694c6cd683e13dddb4e4e444b61c3035e313c7836b4e5
4
- data.tar.gz: 1aa8619222200af1e482598ffc53573cd862ac406369f4e15fbd954d7c85d34a
3
+ metadata.gz: '0920f169885278f664642da1cb6d8441a856b175779b4ad701aa966051644d25'
4
+ data.tar.gz: f0924a2e6574dc0b8c79ca819195368696b20b3c1e1943c3db283b98d3fada34
5
5
  SHA512:
6
- metadata.gz: 8c0718694838de516cbb9583e599883422b0605ff150022e670c31fbe9896a376abaa92174053ab49ecff4e3a3c71da49a18a5ef3151eb24868282a4e2acf754
7
- data.tar.gz: cb7f5f0b6a29154d4d9d0675ca72fa29d9c30f4b42f989365c33df81e76d0577b9d2d96577c72fdb626a543763c82d16e09ff459b37fd9801062d172f563b9e1
6
+ metadata.gz: 75028eeb6446acc724c97b76f8f1ccbc04447e21c2016b89abfdb4154e18bea563b5176cb5ba014ef7c60579e07311ecae50a669999f4502253ce4951c25e810
7
+ data.tar.gz: 884b2da2acb61aaa721201b80c67d40d57a2cc0ce517b1ff15114d8e530f37de35ab11473b4dca946ad34c8537e14eaaae9f7bc57d629228565f8f06cae4285c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 10.8.0
4
+
5
+ * feature: Allow callers to specify which headers will be auto-numbered [PR #451](https://github.com/alphagov/govspeak/pull/451)
6
+
7
+ ## 10.7.0
8
+
9
+ * feature: Add auto-numbered headers option [PR #449](https://github.com/alphagov/govspeak/pull/449)
10
+ * fix: When finding/returning headers, filter links from header titles [PR #439](https://github.com/alphagov/govspeak/pull/439)
11
+
3
12
  ## 10.6.5
4
13
 
5
14
  * Update dependencies
data/README.md CHANGED
@@ -22,7 +22,7 @@ then create a new document
22
22
 
23
23
  Some additional steps or considerations are needed to ensure changes to govspeak cascade across GOV.UK in a holistic way.
24
24
 
25
- Once govspeak has been updated and version incremented then:
25
+ Once govspeak has been updated and version incremented then:
26
26
  - [`govuk_publishing_components` govspeak](https://components.publishing.service.gov.uk/component-guide/govspeak) will also need updating to reflect your most recent change.
27
27
  - [Publishing apps](https://docs.publishing.service.gov.uk/apps.html) (including but not limited to [content-publisher](https://github.com/alphagov/content-publisher) & [whitehall](https://github.com/alphagov/whitehall)) also use govspeak, these apps will need to be released with the new govspeak version present.
28
28
 
@@ -30,7 +30,7 @@ Also, consider if:
30
30
  - [whitehall](https://github.com/alphagov/whitehall) needs updating (as custom govspeak changes are present)
31
31
  - [govspeak-preview](https://github.com/alphagov/govspeak-preview) needs updating
32
32
 
33
- Any pages that use govspeak to generate Content will need to *republished* in order for the new changes to be reflected.
33
+ Any pages that use govspeak to generate Content will need to be *republished* in order for the new changes to be reflected.
34
34
 
35
35
  - Data Labs can help identify which pages need updating by [submitting a request](https://gov-uk.atlassian.net/wiki/spaces/GOVUK/pages/1860075525/GOV.UK+Data+Labs#Submitting-a-data-science-request) and [#govuk-2ndline](https://docs.publishing.service.gov.uk/manual/2nd-line.html) can help with republishing
36
36
 
@@ -48,6 +48,34 @@ with understanding how the content transforms from source through:
48
48
 
49
49
  In addition to the [standard Markdown syntax](http://daringfireball.net/projects/markdown/syntax "Markdown syntax"), we have added our own extensions.
50
50
 
51
+ ## Autonumbered headers
52
+
53
+ This extension is off by default, can be turned on with
54
+
55
+ doc = Govspeak::Document.new "## Header\n### Sub-header\n#### Sub-sub-header", auto_numbered_headers: true
56
+
57
+ Creates automatically numbered headers
58
+
59
+ ```html
60
+ <h2>1 Header</h2>
61
+ <h3>1.1 Sub-header</h3>
62
+ <h4>1.1.1 Sub-sub-header</h4>
63
+ ```
64
+
65
+ By default the numbering scheme will work for all headers apart from H1s, if you want to
66
+ restrict this you can specify the levels you want in an array (default value is [2,3,4,5,6])
67
+
68
+ doc = Govspeak::Document.new "## Header\n### Sub-header\n#### Sub-sub-header", auto_numbered_headers: true, auto_numbered_header_levels: [2,3]
69
+
70
+ Creates automatically numbered headers
71
+
72
+ ```html
73
+ <h2>1 Header</h2>
74
+ <h3>1.1 Sub-header</h3>
75
+ <h4>Sub-sub-header</h4>
76
+ ```
77
+
78
+
51
79
  ## Callouts
52
80
 
53
81
  ### Information callouts
@@ -20,11 +20,15 @@ module Govspeak
20
20
  private
21
21
 
22
22
  def id(element)
23
- element.attr.fetch("id", generate_id(element.options[:raw_text]))
23
+ element.attr.fetch("id", generate_id(text_with_links(element)))
24
24
  end
25
25
 
26
26
  def build_header(element)
27
- Header.new(element.options[:raw_text], element.options[:level], id(element))
27
+ Header.new(text_with_links(element), element.options[:level], id(element))
28
+ end
29
+
30
+ def text_with_links(element)
31
+ element.options[:raw_text].gsub(/\[(.+)\]\((.*)\)/, '\1')
28
32
  end
29
33
 
30
34
  def find_headers(parent)
@@ -160,6 +160,44 @@ module Govspeak
160
160
  end
161
161
  end
162
162
 
163
+ extension("use auto-numbered headers") do |document|
164
+ if govspeak_document.auto_numbered_headers
165
+ h2, h3, h4, h5, h6 = 0, 0, 0, 0, 0, 0
166
+
167
+ selector = govspeak_document.auto_numbered_header_levels.map { |i| "h#{i}" }.join(",")
168
+
169
+ document.css(selector).map do |el|
170
+ case el.name
171
+ when "h2"
172
+ h2 += 1
173
+ h3 = 0
174
+ h4 = 0
175
+ h5 = 0
176
+ h6 = 0
177
+ el.inner_html = "#{h2}. #{el.inner_html}"
178
+ when "h3"
179
+ h3 += 1
180
+ h4 = 0
181
+ h5 = 0
182
+ h6 = 0
183
+ el.inner_html = "#{h2}.#{h3} #{el.inner_html}"
184
+ when "h4"
185
+ h4 += 1
186
+ h5 = 0
187
+ h6 = 0
188
+ el.inner_html = "#{h2}.#{h3}.#{h4} #{el.inner_html}"
189
+ when "h5"
190
+ h5 += 1
191
+ h6 = 0
192
+ el.inner_html = "#{h2}.#{h3}.#{h4}.#{h5} #{el.inner_html}"
193
+ when "h6"
194
+ h6 += 1
195
+ el.inner_html = "#{h2}.#{h3}.#{h4}.#{h5}.#{h6} #{el.inner_html}"
196
+ end
197
+ end
198
+ end
199
+ end
200
+
163
201
  attr_reader :input, :govspeak_document
164
202
 
165
203
  def initialize(html, govspeak_document)
@@ -41,6 +41,8 @@ module Govspeak
41
41
  stack.push(header)
42
42
  end
43
43
 
44
+ add_auto_numbering(structured_headers, doc.auto_numbered_header_levels) if doc.auto_numbered_headers
45
+
44
46
  structured_headers
45
47
  end
46
48
 
@@ -96,6 +98,13 @@ module Govspeak
96
98
  @stack = []
97
99
  end
98
100
 
101
+ def add_auto_numbering(structured_headers, levels, prefix: "")
102
+ structured_headers.each.with_index(1) do |header, index|
103
+ header[:text] = "#{prefix}#{index}#{'.' if prefix == ''} #{header[:text]}" if levels.include?(header[:level])
104
+ add_auto_numbering(header[:headers], levels, prefix: "#{prefix}#{index}.")
105
+ end
106
+ end
107
+
99
108
  private
100
109
 
101
110
  attr_reader :doc, :stack, :structured_headers
@@ -1,3 +1,3 @@
1
1
  module Govspeak
2
- VERSION = "10.6.5".freeze
2
+ VERSION = "10.8.0".freeze
3
3
  end
data/lib/govspeak.rb CHANGED
@@ -38,7 +38,7 @@ module Govspeak
38
38
  @extensions = []
39
39
 
40
40
  attr_accessor :images
41
- attr_reader :attachments, :contacts, :links, :locale, :log_snapshots
41
+ attr_reader :attachments, :auto_numbered_headers, :auto_numbered_header_levels, :contacts, :links, :locale, :log_snapshots
42
42
 
43
43
  def self.to_html(source, options = {})
44
44
  new(source, options).to_html
@@ -55,11 +55,12 @@ module Govspeak
55
55
  @log_snapshots = options.fetch(:log_snapshots, false)
56
56
  log_snapshot("options", options)
57
57
  log_snapshot("source", @source)
58
-
59
58
  @images = options.delete(:images) || []
60
59
  @allowed_elements = options.delete(:allowed_elements) || []
61
60
  @allowed_image_hosts = options.delete(:allowed_image_hosts) || []
62
61
  @attachments = Array.wrap(options.delete(:attachments))
62
+ @auto_numbered_headers = options.fetch(:auto_numbered_headers, false)
63
+ @auto_numbered_header_levels = options.delete(:auto_numbered_header_levels) || [2, 3, 4, 5, 6]
63
64
  @links = Array.wrap(options.delete(:links))
64
65
  @contacts = Array.wrap(options.delete(:contacts))
65
66
  @locale = options.fetch(:locale, "en")
@@ -21,14 +21,16 @@ class GovspeakStructuredHeadersTest < Minitest::Test
21
21
 
22
22
  ### Sub heading 4.1
23
23
 
24
- #### Sub heading 4.1.1
24
+ #### Sub sub heading 4.1.1
25
25
 
26
- ##### Sub heading 4.1.1.1
26
+ ##### Sub sub sub heading 4.1.1.1
27
27
 
28
28
  ### Sub heading 4.2
29
29
 
30
30
  ## Heading 5
31
31
 
32
+ ### [Sub heading 5.1](https://www.example.com)
33
+
32
34
  )
33
35
  end
34
36
 
@@ -67,6 +69,11 @@ class GovspeakStructuredHeadersTest < Minitest::Test
67
69
  assert_equal "Sub heading 4.2", structured_headers[3].headers[1].text
68
70
  end
69
71
 
72
+ test "headers that are links are based on the link text not the link" do
73
+ assert_equal "Sub heading 5.1", structured_headers[4].headers[0].text
74
+ assert_equal "sub-heading-51", structured_headers[4].headers[0].id
75
+ end
76
+
70
77
  test "structured headers serialize to hashes recursively serializing sub headers" do
71
78
  serialized_headers = structured_headers[1].to_h
72
79
 
@@ -138,4 +145,46 @@ class GovspeakStructuredHeadersTest < Minitest::Test
138
145
  test "document with single h1 produces no headers" do
139
146
  assert_equal [], Govspeak::Document.new("# Heading\n").structured_headers
140
147
  end
148
+
149
+ test "auto-numbered headers are generated when the option is set on the document" do
150
+ doc = Govspeak::Document.new(document_body, auto_numbered_headers: true)
151
+
152
+ headers = doc.structured_headers
153
+
154
+ assert_equal "1. Heading 1", headers[0][:text]
155
+ assert_equal "2. Heading 2", headers[1][:text]
156
+ assert_equal "2.1 Sub heading 2.1", headers[1][:headers][0][:text]
157
+ assert_equal "2.2 Sub heading 2.2", headers[1][:headers][1][:text]
158
+ assert_equal "2.2.1 Sub sub heading 2.2.1", headers[1][:headers][1][:headers][0][:text]
159
+ assert_equal "2.3 Sub heading 2.3", headers[1][:headers][2][:text]
160
+ assert_equal "3. Heading 3", headers[2][:text]
161
+ assert_equal "4. Heading 4", headers[3][:text]
162
+ assert_equal "4.1 Sub heading 4.1", headers[3][:headers][0][:text]
163
+ assert_equal "4.1.1 Sub sub heading 4.1.1", headers[3][:headers][0][:headers][0][:text]
164
+ assert_equal "4.1.1.1 Sub sub sub heading 4.1.1.1", headers[3][:headers][0][:headers][0][:headers][0][:text]
165
+ assert_equal "4.2 Sub heading 4.2", headers[3][:headers][1][:text]
166
+ assert_equal "5. Heading 5", headers[4][:text]
167
+ assert_equal "5.1 Sub heading 5.1", headers[4][:headers][0][:text]
168
+ end
169
+
170
+ test "auto-numbered headers are restricted to appropriate levels when the option is set on the document" do
171
+ doc = Govspeak::Document.new(document_body, auto_numbered_headers: true, auto_numbered_header_levels: [2, 3])
172
+
173
+ headers = doc.structured_headers
174
+
175
+ assert_equal "1. Heading 1", headers[0][:text]
176
+ assert_equal "2. Heading 2", headers[1][:text]
177
+ assert_equal "2.1 Sub heading 2.1", headers[1][:headers][0][:text]
178
+ assert_equal "2.2 Sub heading 2.2", headers[1][:headers][1][:text]
179
+ assert_equal "Sub sub heading 2.2.1", headers[1][:headers][1][:headers][0][:text]
180
+ assert_equal "2.3 Sub heading 2.3", headers[1][:headers][2][:text]
181
+ assert_equal "3. Heading 3", headers[2][:text]
182
+ assert_equal "4. Heading 4", headers[3][:text]
183
+ assert_equal "4.1 Sub heading 4.1", headers[3][:headers][0][:text]
184
+ assert_equal "Sub sub heading 4.1.1", headers[3][:headers][0][:headers][0][:text]
185
+ assert_equal "Sub sub sub heading 4.1.1.1", headers[3][:headers][0][:headers][0][:headers][0][:text]
186
+ assert_equal "4.2 Sub heading 4.2", headers[3][:headers][1][:text]
187
+ assert_equal "5. Heading 5", headers[4][:text]
188
+ assert_equal "5.1 Sub heading 5.1", headers[4][:headers][0][:text]
189
+ end
141
190
  end
@@ -99,6 +99,50 @@ class GovspeakTest < Minitest::Test
99
99
  assert_equal "foo bar baz", doc.to_text
100
100
  end
101
101
 
102
+ test "adds auto-numbered headings when the auto_numbered_headings option is on" do
103
+ input = %(
104
+ ## H2 One
105
+
106
+ ### H3 One
107
+
108
+ ### H3 Two
109
+
110
+ ## H2 Two
111
+
112
+ ### H3 Three
113
+
114
+ #### H4 One
115
+
116
+ ##### H5 One
117
+
118
+ ###### H6 One
119
+ )
120
+ doc = Govspeak::Document.new(input, auto_numbered_headers: true)
121
+ assert_equal %(\n<h2 id="h2-one">1. H2 One</h2>\n\n<h3 id="h3-one">1.1 H3 One</h3>\n\n<h3 id="h3-two">1.2 H3 Two</h3>\n\n<h2 id="h2-two">2. H2 Two</h2>\n\n<h3 id="h3-three">2.1 H3 Three</h3>\n\n<h4 id="h4-one">2.1.1 H4 One</h4>\n\n<h5 id="h5-one">2.1.1.1 H5 One</h5>\n\n<h6 id="h6-one">2.1.1.1.1 H6 One</h6>\n), doc.to_html
122
+ end
123
+
124
+ test "restricts auto-numbered headings to specified levels when the auto_numbered_heading_levels option is set" do
125
+ input = %(
126
+ ## H2 One
127
+
128
+ ### H3 One
129
+
130
+ ### H3 Two
131
+
132
+ ## H2 Two
133
+
134
+ ### H3 Three
135
+
136
+ #### H4 One
137
+
138
+ ##### H5 One
139
+
140
+ ###### H6 One
141
+ )
142
+ doc = Govspeak::Document.new(input, auto_numbered_headers: true, auto_numbered_header_levels: [2, 3])
143
+ assert_equal %(\n<h2 id="h2-one">1. H2 One</h2>\n\n<h3 id="h3-one">1.1 H3 One</h3>\n\n<h3 id="h3-two">1.2 H3 Two</h3>\n\n<h2 id="h2-two">2. H2 Two</h2>\n\n<h3 id="h3-three">2.1 H3 Three</h3>\n\n<h4 id="h4-one">H4 One</h4>\n\n<h5 id="h5-one">H5 One</h5>\n\n<h6 id="h6-one">H6 One</h6>\n), doc.to_html
144
+ end
145
+
102
146
  test "trailing space after the address should not prevent parsing" do
103
147
  input = %($A
104
148
  123 Test Street
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govspeak
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.6.5
4
+ version: 10.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
@@ -18,7 +18,7 @@ dependencies:
18
18
  version: '6'
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
- version: 8.0.4
21
+ version: 8.1.2
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -28,7 +28,7 @@ dependencies:
28
28
  version: '6'
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
- version: 8.0.4
31
+ version: 8.1.2
32
32
  - !ruby/object:Gem::Dependency
33
33
  name: addressable
34
34
  requirement: !ruby/object:Gem::Requirement