govspeak 8.3.0 → 8.3.2

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: 92a47d834646bd30072105c15ed418c6fbd09ad18c5469928be7d76a6cdf8d97
4
- data.tar.gz: 94a44d03aa697a24937596786e8dff07da213ad085ca39ce52394cb040c3c7d1
3
+ metadata.gz: 5a58c52e5a9797911c326259920750977c19b81068825b6b947728fe6af410de
4
+ data.tar.gz: de9ac98e971c04c0ac4cc6d7e8f1cc987b2a5310ce2efb5bae4aa2b6ec5ccdc0
5
5
  SHA512:
6
- metadata.gz: b0e59b5d11641d737428bc419c77c749300115625442fb44b0eba026e1b3755e648a1a14ff5199c004ef18cdc72a66953f3c90feb883592dfd8e86719536068c
7
- data.tar.gz: 3ab9bb92193243b5c0b0920260c7ae23773495d3683df54197775aef23337d743edc091a6068291d744fee7010142650e3896a6735310e7456227ab7e587465d
6
+ metadata.gz: 21b984b959a200d44869aad30b0d4db910feba0985d701b918152df0fb507cc46710ce0ee9c661f32e93686955f6c2aa722abeb2e95a96527cf3122d53742497
7
+ data.tar.gz: b33b3accc379b22a62a3354d041b943fdde26a5a3bc040308008d1eb1ebcd2d63b27d18d12a40a774b30b1052ef6b9741f733c6b9f5e3ab495831c305df233f5
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 8.3.2
2
+
3
+ * Various bug fixes related to legislative list components ([#298](https://github.com/alphagov/govspeak/pull/298))
4
+
5
+ ## 8.3.1
6
+
7
+ * Bug fixes related to block elements in call to action and legislative list components ([#293](https://github.com/alphagov/govspeak/pull/293))
8
+
1
9
  ## 8.3.0
2
10
 
3
11
  * Various bug fixes related to abbreviations in call to action and legislative list components ([#291](https://github.com/alphagov/govspeak/pull/291))
@@ -1,3 +1,3 @@
1
1
  module Govspeak
2
- VERSION = "8.3.0".freeze
2
+ VERSION = "8.3.2".freeze
3
3
  end
data/lib/govspeak.rb CHANGED
@@ -13,7 +13,6 @@ require "govspeak/header_extractor"
13
13
  require "govspeak/structured_header_extractor"
14
14
  require "govspeak/html_validator"
15
15
  require "govspeak/html_sanitizer"
16
- require "govspeak/kramdown_overrides"
17
16
  require "govspeak/blockquote_extra_quote_remover"
18
17
  require "govspeak/post_processor"
19
18
  require "govspeak/link_extractor"
@@ -278,9 +277,7 @@ module Govspeak
278
277
 
279
278
  extension("call-to-action", surrounded_by("$CTA")) do |body|
280
279
  <<~BODY
281
- {::options parse_block_html=\"true\" /}
282
- <div class="call-to-action">#{body}</div>
283
- {::options parse_block_html=\"false\" /}
280
+ <div class="call-to-action" markdown="1">#{body}</div>
284
281
  BODY
285
282
  end
286
283
 
@@ -300,12 +297,12 @@ module Govspeak
300
297
  end
301
298
 
302
299
  extension("legislative list", /#{NEW_PARAGRAPH_LOOKBEHIND}\$LegislativeList\s*$(.*?)\$EndLegislativeList/m) do |body|
303
- # The surrounding div is neccessary to control flow in `parse_block_html` and
304
- # maintain the same functionality as a previous version of this extension.
300
+ # The surrounding div is neccessary to accurately identify legislative lists
301
+ # in post-processing.
305
302
  <<~BODY
306
- {::options parse_block_html=\"true\" ordered_lists_disabled=\"true\" /}
307
- <div class="legislative-list-wrapper">#{body}</div>
308
- {::options parse_block_html=\"false\" ordered_lists_disabled=\"false\" /}
303
+ {::options ordered_lists_disabled=\"true\" /}
304
+ <div class="legislative-list-wrapper" markdown="1">#{body}</div>
305
+ {::options ordered_lists_disabled=\"false\" /}
309
306
  BODY
310
307
  end
311
308
 
@@ -53,17 +53,111 @@ module Kramdown
53
53
  def parse_block_html
54
54
  return false if CUSTOM_INLINE_ELEMENTS.include?(@src[1].downcase)
55
55
 
56
+ super
57
+ end
58
+
59
+ # We override the parse_list method with a modified version where ordered lists are
60
+ # disabled (for use with legislative lists). The majority of the method body is copied
61
+ # over (from https://github.com/gettalong/kramdown/blob/REL_2_3_1/lib/kramdown/parser/kramdown/list.rb#L54),
62
+ # only the LIST_START is changed. Previously, we dynamically modified some of the
63
+ # class-scoped variables used in this method; this provides a thread-safe alternative.
64
+ def parse_list
56
65
  return super unless @options[:ordered_lists_disabled]
57
66
 
58
- # Kramdown loads parsers into the instance from `options` which are scoped to
59
- # the class. Because we are changing these options inside an instance, we must
60
- # reconfigure the parser before and after disbaling ordered lists.
61
- Govspeak::KramdownOverrides.with_kramdown_ordered_lists_disabled do
62
- configure_parser
63
- super
67
+ return if @src.check(LIST_START_OL)
68
+
69
+ start_line_number = @src.current_line_number
70
+ type, list_start_re = (@src.check(LIST_START_UL) ? [:ul, LIST_START_UL] : [:ol, LIST_START_OL])
71
+ list = new_block_el(type, nil, nil, location: start_line_number)
72
+
73
+ item = nil
74
+ content_re, lazy_re, indent_re = nil
75
+ eob_found = false
76
+ nested_list_found = false
77
+ last_is_blank = false
78
+ until @src.eos?
79
+ start_line_number = @src.current_line_number
80
+ if last_is_blank && @src.check(HR_START)
81
+ break
82
+ elsif @src.scan(EOB_MARKER)
83
+ eob_found = true
84
+ break
85
+ elsif @src.scan(list_start_re)
86
+ list.options[:first_list_marker] ||= @src[1].strip
87
+ item = Element.new(:li, nil, nil, location: start_line_number)
88
+ item.value, indentation, content_re, lazy_re, indent_re =
89
+ parse_first_list_line(@src[1].length, @src[2])
90
+ list.children << item
91
+
92
+ item.value.sub!(self.class::LIST_ITEM_IAL) do
93
+ parse_attribute_list(::Regexp.last_match(1), item.options[:ial] ||= {})
94
+ ""
95
+ end
96
+
97
+ list_start_re = fetch_pattern(type, indentation)
98
+ nested_list_found = (item.value =~ LIST_START_UL)
99
+ last_is_blank = false
100
+ item.value = [item.value]
101
+ elsif (result = @src.scan(content_re)) || (!last_is_blank && (result = @src.scan(lazy_re)))
102
+ result.sub!(/^(\t+)/) { " " * 4 * ::Regexp.last_match(1).length }
103
+ indentation_found = result.sub!(indent_re, "")
104
+ if !nested_list_found && indentation_found && result =~ LIST_START_UL
105
+ item.value << +""
106
+ nested_list_found = true
107
+ elsif nested_list_found && !indentation_found && result =~ LIST_START_UL
108
+ result = " " * (indentation + 4) << result
109
+ end
110
+ item.value.last << result
111
+ last_is_blank = false
112
+ elsif (result = @src.scan(BLANK_LINE))
113
+ nested_list_found = true
114
+ last_is_blank = true
115
+ item.value.last << result
116
+ else
117
+ break
118
+ end
64
119
  end
65
120
 
66
- configure_parser
121
+ @tree.children << list
122
+
123
+ last = nil
124
+ list.children.each do |it|
125
+ temp = Element.new(:temp, nil, nil, location: it.options[:location])
126
+
127
+ env = save_env
128
+ location = it.options[:location]
129
+ it.value.each do |val|
130
+ @src = ::Kramdown::Utils::StringScanner.new(val, location)
131
+ parse_blocks(temp)
132
+ location = @src.current_line_number
133
+ end
134
+ restore_env(env)
135
+
136
+ it.children = temp.children
137
+ it.value = nil
138
+
139
+ it_children = it.children
140
+ next if it_children.empty?
141
+
142
+ # Handle the case where an EOB marker is inserted by a block IAL for the first paragraph
143
+ it_children.delete_at(1) if it_children.first.type == :p &&
144
+ it_children.length >= 2 && it_children[1].type == :eob && it_children.first.options[:ial]
145
+
146
+ if it_children.first.type == :p &&
147
+ (it_children.length < 2 || it_children[1].type != :blank ||
148
+ (it == list.children.last && it_children.length == 2 && !eob_found)) &&
149
+ (list.children.last != it || list.children.size == 1 ||
150
+ list.children[0..-2].any? { |cit| !cit.children.first || cit.children.first.type != :p || cit.children.first.options[:transparent] })
151
+ it_children.first.children.first.value << "\n" if it_children.size > 1 && it_children[1].type != :blank
152
+ it_children.first.options[:transparent] = true
153
+ end
154
+
155
+ last = (it_children.last.type == :blank ? it_children.pop : nil)
156
+ end
157
+
158
+ @tree.children << last if !last.nil? && !eob_found
159
+
160
+ true
67
161
  end
68
162
  end
69
163
  end
@@ -4,13 +4,6 @@ require "govspeak_test_helper"
4
4
  class GovspeakImagesTest < Minitest::Test
5
5
  include GovspeakTestHelper
6
6
 
7
- def build_image(attrs = {})
8
- attrs[:alt_text] ||= "my alt"
9
- attrs[:url] ||= "http://example.com/image.jpg"
10
- attrs[:id] ||= "image-id"
11
- attrs
12
- end
13
-
14
7
  test "Image:image-id syntax renders an image in options[:images]" do
15
8
  given_govspeak "[Image:image-id]", images: [build_image] do
16
9
  assert_html_output(
@@ -683,6 +683,24 @@ Teston
683
683
  )
684
684
  end
685
685
 
686
+ test "CTA with image" do
687
+ given_govspeak "
688
+ $CTA
689
+ [Image:image-id]
690
+ $CTA
691
+
692
+ Some text
693
+ ", images: [build_image] do
694
+ assert_html_output %(
695
+ <div class="call-to-action">
696
+ <figure class="image embedded"><div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div></figure>
697
+ </div>
698
+
699
+ <p>Some text</p>
700
+ )
701
+ end
702
+ end
703
+
686
704
  test_given_govspeak "
687
705
  1. rod
688
706
  2. jane
@@ -1268,6 +1286,24 @@ Teston
1268
1286
  )
1269
1287
  end
1270
1288
 
1289
+ test "LegislativeList with image" do
1290
+ given_govspeak "
1291
+ $LegislativeList
1292
+ [Image:image-id]
1293
+ $EndLegislativeList
1294
+
1295
+ Some text
1296
+ ", images: [build_image] do
1297
+ assert_html_output %(
1298
+ <div class="legislative-list-wrapper">
1299
+ <figure class="image embedded"><div class="img"><img src="http://example.com/image.jpg" alt="my alt"></div></figure>
1300
+ </div>
1301
+
1302
+ <p>Some text</p>
1303
+ )
1304
+ end
1305
+ end
1306
+
1271
1307
  test_given_govspeak "
1272
1308
  Zippy, Bungle and George did not qualify for the tax exemption in s428. They filled in their tax return accordingly.
1273
1309
  " do
@@ -73,6 +73,13 @@ module GovspeakTestHelper
73
73
  coder.decode(html)
74
74
  end
75
75
 
76
+ def build_image(attrs = {})
77
+ attrs[:alt_text] ||= "my alt"
78
+ attrs[:url] ||= "http://example.com/image.jpg"
79
+ attrs[:id] ||= "image-id"
80
+ attrs
81
+ end
82
+
76
83
  module ClassMethods
77
84
  def test_given_govspeak(govspeak, options = {}, &block)
78
85
  test "Given #{govspeak}" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govspeak
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.3.0
4
+ version: 8.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - GOV.UK Dev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-12 00:00:00.000000000 Z
11
+ date: 2023-12-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionview
@@ -235,7 +235,6 @@ files:
235
235
  - lib/govspeak/header_extractor.rb
236
236
  - lib/govspeak/html_sanitizer.rb
237
237
  - lib/govspeak/html_validator.rb
238
- - lib/govspeak/kramdown_overrides.rb
239
238
  - lib/govspeak/link_extractor.rb
240
239
  - lib/govspeak/post_processor.rb
241
240
  - lib/govspeak/presenters/attachment_image_presenter.rb
@@ -327,7 +326,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
327
326
  - !ruby/object:Gem::Version
328
327
  version: '0'
329
328
  requirements: []
330
- rubygems_version: 3.4.20
329
+ rubygems_version: 3.4.22
331
330
  signing_key:
332
331
  specification_version: 4
333
332
  summary: Markup language for single domain
@@ -1,38 +0,0 @@
1
- module Govspeak
2
- module KramdownOverrides
3
- # This depends on two internal parts of Kramdown.
4
- # 1. Parser registry (kramdown/parser/kramdown.rb#define_parser)
5
- # 2. Kramdown list regexes (kramdown/parser/kramdown/list.rb)
6
- # Updating the Kramdown gem therefore also means updating this file to to
7
- # match Kramdown's internals.
8
-
9
- def self.with_kramdown_ordered_lists_disabled
10
- original_list_start = list_start
11
- redefine_kramdown_const(:LIST_START, list_start_ul)
12
- list_parser = kramdown_parsers.delete(:list)
13
- Kramdown::Parser::Kramdown.define_parser(:list, list_start_ul)
14
-
15
- yield
16
- ensure
17
- redefine_kramdown_const(:LIST_START, original_list_start)
18
- kramdown_parsers[:list] = list_parser
19
- end
20
-
21
- def self.list_start
22
- Kramdown::Parser::Kramdown::LIST_START
23
- end
24
-
25
- def self.list_start_ul
26
- Kramdown::Parser::Kramdown::LIST_START_UL
27
- end
28
-
29
- def self.kramdown_parsers
30
- Kramdown::Parser::Kramdown.class_variable_get("@@parsers")
31
- end
32
-
33
- def self.redefine_kramdown_const(const, value)
34
- Kramdown::Parser::Kramdown.send(:remove_const, const)
35
- Kramdown::Parser::Kramdown.send(:const_set, const, value)
36
- end
37
- end
38
- end