Skip to content

Commit 6f186a1

Browse files
authored
Merge pull request #10642 from alphagov/headers-headers-headers
[WHIT-2490] Fix html headers with leading symbols/numbers
2 parents 8552466 + 76cb75f commit 6f186a1

File tree

2 files changed

+48
-6
lines changed

2 files changed

+48
-6
lines changed

app/helpers/govspeak_helper.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,11 @@ def add_heading_numbers(govspeak)
151151
num = "#{h2}.#{h3}"
152152
end
153153

154-
# We have to manually derive and append a slug otherwise when Govspeak
155-
# generates the HTML, it includes the <span> and number in the ID. Hence
156-
# the `heading_text.parameterize`
157-
"#{hashes} <span class=\"number\">#{num} </span>#{heading_text} {##{heading_text.parameterize}}"
154+
custom_id_match = heading_text.match(/\{#([^}]+)\}/)
155+
clean_heading_text = custom_id_match ? heading_text.gsub(/\s*\{#[^}]+\}/, "") : heading_text
156+
id = (custom_id_match&.[](1) || clean_heading_text).gsub(/^[^a-zA-Z]+/, "").parameterize
157+
158+
"#{hashes} <span class=\"number\">#{num} </span>#{clean_heading_text} {##{id}}"
158159
end
159160
end
160161

test/unit/app/helpers/govspeak_helper_test.rb

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ def image_data.image_kind_config = Whitehall::ImageKind.new(
293293
it "adds numbers to h2 headings" do
294294
input = "# main\n\n## first\n\n## second"
295295
output = '<div class="govspeak"><h1 id="main">main</h1> <h2 id="first"> <span class="number">1. </span>first</h2> <h2 id="second"> <span class="number">2. </span>second</h2></div>'
296-
assert_equivalent_html output, govspeak_to_html(input, heading_numbering: :auto).gsub(/\s+/, " ")
296+
assert_equivalent_html output, collapse_whitespace(govspeak_to_html(input, heading_numbering: :auto))
297297
end
298298

299299
it "adds sub-numbers to h3 tags" do
@@ -303,7 +303,7 @@ def image_data.image_kind_config = Whitehall::ImageKind.new(
303303
expected_output_1b = '<h3 id="first-point-two"> <span class="number">1.2 </span>first point two</h3>'
304304
expected_output2 = '<h2 id="second"> <span class="number">2. </span>second</h2>'
305305
expected_output_2a = '<h3 id="second-point-one"> <span class="number">2.1 </span>second point one</h3>'
306-
actual_output = govspeak_to_html(input, heading_numbering: :auto).gsub(/\s+/, " ")
306+
actual_output = collapse_whitespace(govspeak_to_html(input, heading_numbering: :auto))
307307
assert_match %r{#{expected_output1}}, actual_output
308308
assert_match %r{#{expected_output_1a}}, actual_output
309309
assert_match %r{#{expected_output_1b}}, actual_output
@@ -332,6 +332,47 @@ def image_data.image_kind_config = Whitehall::ImageKind.new(
332332
assert_equivalent_html "<div class=\"govspeak\">#{contact_html}</div>", output
333333
end
334334

335+
it "adds manual numbering to heading tags" do
336+
input = "## 1. Main\n\n## 2. Second\n\n### Sub heading without a number\n\n## 42.12 Out of sequence"
337+
expected_output = '<div class="govspeak"><h2 id="main">1. Main</h2> <h2 id="second">2. Second</h2> <h3 id="sub-heading-without-a-number">Sub heading without a number</h3> <h2 id="out-of-sequence">42.12 Out of sequence</h2></div>'
338+
assert_equivalent_html expected_output, collapse_whitespace(govspeak_to_html(input))
339+
end
340+
341+
it "avoids adding manual numbering to heading tags that start with numbers but aren't intended for manual numbering" do
342+
# NB, the reason we expect a `gd-not-all-numeric-characters` ID rather than
343+
#  a `0gd-not-all-numeric-characters` is that pre-HTML5 IDs _must_ begin with
344+
# a letter: https://www.w3.org/TR/html4/types.html#type-id
345+
# See also this issue in Kramdown:
346+
# https://github.com/gettalong/kramdown/issues/711
347+
input = "## 0GD Not all numeric characters"
348+
expected_output = '<div class="govspeak"><h2 id="gd-not-all-numeric-characters">0GD Not all numeric characters</h2></div>'
349+
assert_equivalent_html expected_output, govspeak_to_html(input).gsub(/\s+/, " ")
350+
end
351+
352+
it "leaves heading numbers not occuring at the start of the heading text alone when using manual heading numbering" do
353+
input = "## Number 8"
354+
result = Nokogiri::HTML::DocumentFragment.parse(govspeak_to_html(input))
355+
assert_equal "Number 8", result.css("h2").first.text
356+
end
357+
358+
it "handles leading numbers and symbols" do
359+
input = "## £100,000 header text here"
360+
expected_output = '<div class="govspeak"><h2 id="header-text-here"><span class="number">1. </span>£100,000 header text here</h2></div>'
361+
assert_equivalent_html expected_output, collapse_whitespace(govspeak_to_html(input, heading_numbering: :auto))
362+
end
363+
364+
it "can manage a custom ID in the govspeak" do
365+
input = "## £100,000 header text here {#custom-id}"
366+
expected_output = '<div class="govspeak"><h2 id="custom-id"><span class="number">1. </span>£100,000 header text here</h2></div>'
367+
assert_equivalent_html expected_output, collapse_whitespace(govspeak_to_html(input, heading_numbering: :auto))
368+
end
369+
370+
it "can manage a custom ID with leading numbers" do
371+
input = "## £100,000 header text here {#10-custom-id}"
372+
expected_output = '<div class="govspeak"><h2 id="custom-id"><span class="number">1. </span>£100,000 header text here</h2></div>'
373+
assert_equivalent_html expected_output, collapse_whitespace(govspeak_to_html(input, heading_numbering: :auto))
374+
end
375+
335376
it "converts [Contact:<id>] into a rendering of contacts/_contact for the Contact with id = <id> with defined header level" do
336377
contact = build(:contact)
337378
Contact.stubs(:find_by).with(id: "1").returns(contact)

0 commit comments

Comments
 (0)