ccs-frontend_helpers 0.2.0 → 0.3.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: 294fb84084e25efd871cf66bf4513023648669964b1fbc026aece725bcd64c78
4
- data.tar.gz: cd55ae750739c51a9561871d5d2c782a067ace1b04f7014e3b41d05349db6f68
3
+ metadata.gz: d935d241fb0117c445c9e5a7eaa2cee257c5f7bc1e68457baf347aabf946a664
4
+ data.tar.gz: 4a8328285e0adad683eafbf72d8385ea7b3703ae352eded484eb6595bb8ce136
5
5
  SHA512:
6
- metadata.gz: d64eb288f9595343e58ec37eec080a925928b908273c1880efd409a3cde6f9015567221838496084c03d3799dd54ea7ae10328e4c545d446d0f876b0892efb9c
7
- data.tar.gz: d1c288ab82c3c78b1049a1bf8d580b515d213d52758aedf378799262efa854880eab99e69251e0cf74502bb29af8da7fa965adf6275f8755672896dc49787a43
6
+ metadata.gz: 3e4698ad6bcca9cfc345922d05da7580f8f21c4789ff2631d838001f0cd68090c0039467d95e2af4fee2b279822e9b522bfcc42f9ee198c89f4658e153e9e9ae
7
+ data.tar.gz: 5fb56ee826dc2350d685e6fa888e11e2644bbf199db306a9953174b386e21efd42393db88f62813268218f0a387f15e107f3f010d8d2964690063034808833f2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [0.3.0] - 2024-04-12
2
+
3
+ Update components to be compatible with GOV.UK Frontend v5.3
4
+
5
+ ### 🆕 New features
6
+
7
+ The following GOV.UK helpers have been added:
8
+
9
+ - Password input
10
+
1
11
  ## [0.2.0] - 2024-03-13
2
12
 
3
13
  ### 💥 Breaking changes
data/Gemfile CHANGED
@@ -7,7 +7,7 @@ gemspec
7
7
 
8
8
  # Gems for building the package
9
9
  gem 'bundler', '~> 2.3'
10
- gem 'rake', '~> 13.1'
10
+ gem 'rake', '~> 13.2'
11
11
 
12
12
  # Gems for testing the package
13
13
  gem 'capybara', '~> 3.40.0'
@@ -19,7 +19,7 @@ gem 'nokogiri-diff', '~> 0.3.0'
19
19
  gem 'rubocop', '~> 1.62'
20
20
  gem 'rubocop-rails', '~> 2.24'
21
21
  gem 'rubocop-rake', '~> 0.6'
22
- gem 'rubocop-rspec', '~> 2.27'
22
+ gem 'rubocop-rspec', '~> 2.29'
23
23
 
24
24
  # Gems for documenting the package
25
25
  gem 'yard', '~> 0.9'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ccs-frontend_helpers (0.2.0)
4
+ ccs-frontend_helpers (0.3.0)
5
5
  rails (>= 6.0)
6
6
 
7
7
  GEM
@@ -85,7 +85,7 @@ GEM
85
85
  public_suffix (>= 2.0.2, < 6.0)
86
86
  ast (2.4.2)
87
87
  base64 (0.2.0)
88
- bigdecimal (3.1.6)
88
+ bigdecimal (3.1.7)
89
89
  builder (3.2.4)
90
90
  capybara (3.40.0)
91
91
  addressable
@@ -112,7 +112,7 @@ GEM
112
112
  irb (1.11.2)
113
113
  rdoc
114
114
  reline (>= 0.4.2)
115
- json (2.7.1)
115
+ json (2.7.2)
116
116
  language_server-protocol (3.17.0.3)
117
117
  loofah (2.22.0)
118
118
  crass (~> 1.0.2)
@@ -125,7 +125,7 @@ GEM
125
125
  marcel (1.0.2)
126
126
  matrix (0.4.2)
127
127
  mini_mime (1.1.5)
128
- minitest (5.22.2)
128
+ minitest (5.22.3)
129
129
  mutex_m (0.2.0)
130
130
  net-imap (0.4.10)
131
131
  date
@@ -152,7 +152,7 @@ GEM
152
152
  stringio
153
153
  public_suffix (5.0.4)
154
154
  racc (1.7.3)
155
- rack (3.0.9.1)
155
+ rack (3.0.10)
156
156
  rack-session (2.0.0)
157
157
  rack (>= 3.0.0)
158
158
  rack-test (2.1.0)
@@ -190,7 +190,7 @@ GEM
190
190
  thor (~> 1.0, >= 1.2.2)
191
191
  zeitwerk (~> 2.6)
192
192
  rainbow (3.1.1)
193
- rake (13.1.0)
193
+ rake (13.2.1)
194
194
  rdoc (6.6.2)
195
195
  psych (>= 4.0.0)
196
196
  regexp_parser (2.9.0)
@@ -227,17 +227,20 @@ GEM
227
227
  rubocop (~> 1.41)
228
228
  rubocop-factory_bot (2.25.1)
229
229
  rubocop (~> 1.41)
230
- rubocop-rails (2.24.0)
230
+ rubocop-rails (2.24.1)
231
231
  activesupport (>= 4.2.0)
232
232
  rack (>= 1.1)
233
233
  rubocop (>= 1.33.0, < 2.0)
234
234
  rubocop-ast (>= 1.31.1, < 2.0)
235
235
  rubocop-rake (0.6.0)
236
236
  rubocop (~> 1.0)
237
- rubocop-rspec (2.27.1)
237
+ rubocop-rspec (2.29.1)
238
238
  rubocop (~> 1.40)
239
239
  rubocop-capybara (~> 2.17)
240
240
  rubocop-factory_bot (~> 2.22)
241
+ rubocop-rspec_rails (~> 2.28)
242
+ rubocop-rspec_rails (2.28.2)
243
+ rubocop (~> 1.40)
241
244
  ruby-progressbar (1.13.0)
242
245
  simplecov (0.22.0)
243
246
  docile (~> 1.1)
@@ -271,12 +274,12 @@ DEPENDENCIES
271
274
  capybara (~> 3.40.0)
272
275
  ccs-frontend_helpers!
273
276
  nokogiri-diff (~> 0.3.0)
274
- rake (~> 13.1)
277
+ rake (~> 13.2)
275
278
  rspec (~> 3.13)
276
279
  rubocop (~> 1.62)
277
280
  rubocop-rails (~> 2.24)
278
281
  rubocop-rake (~> 0.6)
279
- rubocop-rspec (~> 2.27)
282
+ rubocop-rspec (~> 2.29)
280
283
  simplecov (~> 0.21)
281
284
  yard (~> 0.9)
282
285
 
data/README.md CHANGED
@@ -14,6 +14,7 @@ The following table shows the version of CCS Frontend Helpers that you should us
14
14
 
15
15
  | CCS Frontend Helpers Version | Target GOV.UK Frontend Version |
16
16
  | ----------------------------- | ------------------------------ |
17
+ | [0.3.0](https://github.com/tim-s-ccs/ccs-frontend_helpers/releases/tag/v0.3.0) | [5.3.0](https://github.com/alphagov/govuk-frontend/releases/tag/v5.3.0) |
17
18
  | [0.2.0](https://github.com/tim-s-ccs/ccs-frontend_helpers/releases/tag/v0.2.0) | [5.2.0](https://github.com/alphagov/govuk-frontend/releases/tag/v5.2.0) |
18
19
  | [0.1.2](https://github.com/tim-s-ccs/ccs-frontend_helpers/releases/tag/v0.1.2) | [4.7.0](https://github.com/alphagov/govuk-frontend/releases/tag/v4.7.0) |
19
20
 
@@ -37,7 +37,6 @@ module CCS
37
37
  def initialize(navigation: nil, meta: nil, **options)
38
38
  super(**options)
39
39
 
40
- @options[:attributes][:role] = 'contentinfo'
41
40
  @options[:copyright] ||= '© Crown copyright'
42
41
 
43
42
  @navigation = navigation&.map { |navigation_item| Navigation.new(context: @context, **navigation_item) }
@@ -45,7 +45,6 @@ module CCS
45
45
  def initialize(service_authentication_items: nil, navigation: nil, menu_button: nil, service: nil, **options)
46
46
  super(**options)
47
47
 
48
- @options[:attributes][:role] = 'banner'
49
48
  @options[:container_classes] ||= 'govuk-width-container'
50
49
  @options[:homepage_url] ||= 'https://www.crowncommercial.gov.uk'
51
50
 
@@ -0,0 +1,62 @@
1
+ module CCS
2
+ module Components
3
+ module GovUK
4
+ class Field < Base
5
+ class Input < Field
6
+ class CharacterCount
7
+ # = GOV.UK Character count message
8
+ #
9
+ # This is used to generate the character count message
10
+ #
11
+ # @!attribute [r] count_message
12
+ # @return [Hint] Hint with the count message
13
+ # @!attribute [r] after_input
14
+ # @return [String] Text or HTML for after the textarea input
15
+
16
+ class CountMessage
17
+ include ActionView::Context
18
+ include ActionView::Helpers
19
+
20
+ private
21
+
22
+ attr_reader :count_message, :after_input
23
+
24
+ public
25
+
26
+ # @param character_count_attribute [String] the name of the field as it will appear in the textarea
27
+ # @param context [ActionView::Base] the view context
28
+ # @param character_count_options (see CCS::Components::GovUK::Field::Input::CharacterCount.get_character_count_from_group_options)
29
+ # @param after_input [String] Text or HTML that goes after the input
30
+ #
31
+ # @option (see CCS::Components::GovUK::Field::Input::CharacterCount.get_character_count_from_group_options)
32
+
33
+ def initialize(character_count_attribute:, context:, character_count_options:, after_input: nil)
34
+ count_message = character_count_options[:textarea_description] || {}
35
+
36
+ count_message_length = character_count_options[:maxwords] || character_count_options[:maxlength]
37
+ count_message_default = "You can enter up to %<count>s #{character_count_options[:maxwords] ? 'words' : 'characters'}"
38
+
39
+ text = count_message_length ? format(count_message[:count_message] || count_message_default, count: count_message_length) : ''
40
+ classes = "govuk-character-count__message #{count_message[:classes]}".rstrip
41
+
42
+ @count_message = Hint.new(text: text, classes: classes, attributes: { id: "#{character_count_attribute}-info" }, context: context)
43
+ @after_input = after_input
44
+ end
45
+
46
+ # Generates the HTML for the GOV.UK Character count message
47
+ #
48
+ # @return [ActiveSupport::SafeBuffer]
49
+
50
+ def render
51
+ capture do
52
+ concat(count_message.render)
53
+ concat(after_input) if after_input
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,5 +1,5 @@
1
1
  require_relative 'textarea'
2
- require_relative '../../hint'
2
+ require_relative 'character_count/count_message'
3
3
 
4
4
  module CCS
5
5
  module Components
@@ -21,12 +21,9 @@ module CCS
21
21
  # @return [Hash] HTML options for the character count
22
22
 
23
23
  class CharacterCount
24
- include ActionView::Context
25
- include ActionView::Helpers
26
-
27
24
  private
28
25
 
29
- attr_reader :textarea, :textarea_description, :character_count_html_options
26
+ attr_reader :textarea, :textarea_description
30
27
 
31
28
  public
32
29
 
@@ -41,21 +38,10 @@ module CCS
41
38
  def initialize(attribute:, context:, character_count_options: {}, **options)
42
39
  character_count_attribute = options[:form] ? "#{options[:form].object_name}_#{attribute}" : attribute
43
40
 
44
- initialise_textarea(attribute, character_count_attribute, context, options)
45
- initialise_character_count_html_options(character_count_options)
46
- initialise_textarea_description(character_count_attribute, context, character_count_options)
41
+ initialise_textarea(attribute, character_count_attribute, character_count_options, context, options)
47
42
  end
48
43
 
49
- # Generates the HTML for the GOV.UK Character count component
50
- #
51
- # @return [ActiveSupport::SafeBuffer]
52
-
53
- def render
54
- tag.div(**character_count_html_options) do
55
- concat(textarea.render)
56
- concat(textarea_description.render)
57
- end
58
- end
44
+ delegate :render, to: :textarea
59
45
 
60
46
  private
61
47
 
@@ -63,19 +49,23 @@ module CCS
63
49
  #
64
50
  # @param attribute [Symbol] the attribute of the field
65
51
  # @param character_count_attribute [String] the name of the field as it will appear in the textarea
52
+ # @param character_count_options [Hash] options for the character count
66
53
  # @param context [ActionView::Base] the view context
67
54
  # @param options [Hash] options that will be used for the textarea
68
55
  #
69
56
  # @option (see CCS::Components::GovUK::Field::Input::Textarea#initialize)
70
57
 
71
- def initialise_textarea(attribute, character_count_attribute, context, options)
58
+ def initialise_textarea(attribute, character_count_attribute, character_count_options, context, options)
59
+ set_character_count_from_group_options(character_count_options, options)
60
+
72
61
  ((options[:attributes] ||= {})[:aria] ||= {})[:describedby] = [options.dig(:attributes, :aria, :describedby), "#{character_count_attribute}-info"].compact.join(' ')
73
62
  options[:classes] = "govuk-js-character-count #{options[:classes]}".rstrip
74
63
 
75
- @textarea = Textarea.new(attribute: attribute, context: context, **options)
64
+ count_message = CountMessage.new(character_count_attribute: character_count_attribute, context: context, character_count_options: character_count_options, after_input: options.delete(:after_input))
65
+ @textarea = Textarea.new(attribute: attribute, context: context, after_input: count_message.render, **options)
76
66
  end
77
67
 
78
- # Initialises the charcter count options
68
+ # Sets the charcter count form group options
79
69
  #
80
70
  # @param character_count_options [Hash] options for the charcter count
81
71
  #
@@ -91,50 +81,29 @@ module CCS
91
81
  # - +:count_message+ replaced the default text for the count message.
92
82
  # If you want the count number to appear, put %<count>s in the string and it will be replaced with the number
93
83
  # - +classes+ additional CSS classes for the textarea description HTML
94
- # @option character_count_options [Hash] :characters_under_limit Message displayed when the number of characters is under the configured maximum, maxlength
84
+ # @option character_count_options [String] :characters_under_limit Message displayed when the number of characters is under the configured maximum, maxlength
95
85
  # @option character_count_options [String] :characters_at_limit_text Message displayed when the number of characters reaches the configured maximum, maxlength
96
- # @option character_count_options [Hash] :characters_over_limit Message displayed when the number of characters is over the configured maximum, maxlength
97
- # @option character_count_options [Hash] :words_under_limit Message displayed when the number of words is under the configured maximum, maxwords
86
+ # @option character_count_options [String] :characters_over_limit Message displayed when the number of characters is over the configured maximum, maxlength
87
+ # @option character_count_options [String] :words_under_limit Message displayed when the number of words is under the configured maximum, maxwords
98
88
  # @option character_count_options [String] :words_at_limit_text Message displayed when the number of words reaches the configured maximum, maxwords
99
- # @option character_count_options [Hash] :words_over_limit Message displayed when the number of words is over the configured maximum, maxwords
89
+ # @option character_count_options [String] :words_over_limit Message displayed when the number of words is over the configured maximum, maxwords
100
90
 
101
- def initialise_character_count_html_options(character_count_options)
102
- character_count_html_options = { class: 'govuk-character-count', data: { module: 'govuk-character-count' } }
91
+ def set_character_count_from_group_options(character_count_options, options)
92
+ (options[:form_group] ||= {})[:classes] = "govuk-character-count #{options.dig(:form_group, :classes)}".rstrip
93
+ ((options[:form_group][:attributes] ||= {})[:data] ||= {})[:module] = 'govuk-character-count'
103
94
 
104
95
  %i[maxlength threshold maxwords].each do |data_attribute|
105
- character_count_html_options[:data][data_attribute] = character_count_options[data_attribute].to_s if character_count_options[data_attribute]
96
+ options[:form_group][:attributes][:data][data_attribute] = character_count_options[data_attribute].to_s if character_count_options[data_attribute]
106
97
  end
107
98
 
108
- get_chacrter_count_translations(character_count_options).each do |data_attribute, value|
109
- character_count_html_options[:data][data_attribute] = value
99
+ get_chacrter_count_translations(character_count_options) do |data_attribute, value|
100
+ options[:form_group][:attributes][:data][data_attribute] = value
110
101
  end
111
-
112
- @character_count_html_options = character_count_html_options
113
- end
114
-
115
- # Initialises the charcter count textarea description
116
- #
117
- # @param character_count_attribute [String] the name of the field as it will appear in the textarea
118
- # @param context [ActionView::Base] the view context
119
- # @param (see initialise_character_count_html_options)
120
- #
121
- # @option (see initialise_character_count_html_options)
122
-
123
- def initialise_textarea_description(character_count_attribute, context, character_count_options)
124
- textarea_description = character_count_options[:textarea_description] || {}
125
-
126
- textarea_description_length = character_count_options[:maxwords] || character_count_options[:maxlength]
127
- textarea_description_default = "You can enter up to %<count>s #{character_count_options[:maxwords] ? 'words' : 'characters'}"
128
-
129
- text = textarea_description_length ? format(textarea_description[:count_message] || textarea_description_default, count: textarea_description_length) : ''
130
- classes = "govuk-character-count__message #{textarea_description[:classes]}".rstrip
131
-
132
- @textarea_description = Hint.new(text: text, classes: classes, attributes: { id: "#{character_count_attribute}-info" }, context: context)
133
102
  end
134
103
 
135
104
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
136
105
 
137
- # Returns the translation options for character count
106
+ # Generator for the translation options for character count
138
107
  #
139
108
  # @param (see initialise_character_count_html_options)
140
109
  #
@@ -143,24 +112,25 @@ module CCS
143
112
  # @return [Hash]
144
113
 
145
114
  def get_chacrter_count_translations(character_count_options)
146
- character_count_data_options = {}
147
-
148
115
  %i[characters_at_limit words_at_limit].each do |data_attribute|
149
116
  data_attribute_name = :"#{data_attribute}_text"
150
- character_count_data_options[:"i18n.#{data_attribute.to_s.gsub('_', '-')}"] = character_count_options[data_attribute_name] if character_count_options[data_attribute_name]
117
+
118
+ next unless character_count_options[data_attribute_name]
119
+
120
+ yield :"i18n.#{data_attribute.to_s.gsub('_', '-')}", character_count_options[data_attribute_name]
151
121
  end
152
122
 
153
123
  %i[characters_under_limit characters_over_limit words_under_limit words_over_limit].each do |data_attribute|
154
124
  next unless character_count_options[data_attribute]
155
125
 
156
126
  %i[other one].each do |plural_rule|
157
- character_count_data_options[:"i18n.#{data_attribute.to_s.gsub('_', '-')}.#{plural_rule}"] = character_count_options[data_attribute][plural_rule] if character_count_options[data_attribute][plural_rule]
127
+ next unless character_count_options[data_attribute][plural_rule]
128
+
129
+ yield :"i18n.#{data_attribute.to_s.gsub('_', '-')}.#{plural_rule}", character_count_options[data_attribute][plural_rule]
158
130
  end
159
131
  end
160
132
 
161
- character_count_data_options[:'i18n.textarea-description.other'] = character_count_options[:textarea_description][:count_message] if character_count_options.dig(:textarea_description, :count_message) && !(character_count_options[:maxwords] || character_count_options[:maxlength])
162
-
163
- character_count_data_options
133
+ yield :'i18n.textarea-description.other', character_count_options[:textarea_description][:count_message] if character_count_options.dig(:textarea_description, :count_message) && !(character_count_options[:maxwords] || character_count_options[:maxlength])
164
134
  end
165
135
 
166
136
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
@@ -0,0 +1,72 @@
1
+ require_relative '../../../button'
2
+
3
+ module CCS
4
+ module Components
5
+ module GovUK
6
+ class Field < Base
7
+ class Input < Field
8
+ class PasswordInput
9
+ # = GOV.UK Password input show/hide button
10
+ #
11
+ # This is used to generate the password input show/hide button
12
+ #
13
+ # @!attribute [r] show_hide_button
14
+ # @return [Button] Show/Hide button
15
+ # @!attribute [r] after_input
16
+ # @return [String] Text or HTML for after the textarea input
17
+
18
+ class ShowHideButton
19
+ include ActionView::Context
20
+ include ActionView::Helpers
21
+ include ActionView::Helpers::FormTagHelper
22
+
23
+ private
24
+
25
+ attr_reader :show_hide_button, :after_input
26
+
27
+ public
28
+
29
+ # @param attribute [String] the name of the field as it will appear in the input
30
+ # @param context [ActionView::Base] the view context
31
+ # @param button [Hash] options for the button
32
+ # @param after_input [String] Text or HTML that goes after the input
33
+ #
34
+ # @option button [String] :classes classes to add to the button
35
+ #
36
+ # @option (see CCS::Components::GovUK::Field::Input::PasswordInput.set_password_input_from_group_options)
37
+
38
+ def initialize(attribute:, context:, button: {}, after_input: nil, **options)
39
+ button[:classes] = "govuk-button--secondary govuk-password-input__toggle govuk-js-password-input-toggle #{button[:classes]}".rstrip
40
+ button[:attributes] = {
41
+ type: :button,
42
+ aria: {
43
+ controls: options.dig(:attributes, :id) || field_id(options[:form]&.object_name, attribute),
44
+ label: options[:show_password_aria_label_text] || 'Show password'
45
+ },
46
+ hidden: {
47
+ value: true,
48
+ optional: true
49
+ }
50
+ }
51
+
52
+ @show_hide_button = Button.new(text: options[:show_password_text] || 'Show', context: context, classes: button[:classes], attributes: button[:attributes])
53
+ @after_input = after_input
54
+ end
55
+
56
+ # Generates the HTML for the GOV.UK Password input show/hide button
57
+ #
58
+ # @return [ActiveSupport::SafeBuffer]
59
+
60
+ def render
61
+ capture do
62
+ concat(show_hide_button.render)
63
+ concat(after_input) if after_input
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,101 @@
1
+ require_relative 'text_input'
2
+ require_relative 'password_input/show_hide_button'
3
+
4
+ module CCS
5
+ module Components
6
+ module GovUK
7
+ class Field < Base
8
+ class Input < Field
9
+ # = GOV.UK Password input
10
+ #
11
+ # This is used for generating the password input component from the
12
+ # {https://design-system.service.gov.uk/components/password-input GDS - Components - Password Input}
13
+ #
14
+ # @!attribute [r] text_input
15
+ # @return [CCS::Components::GovUK::Field::Input::TextInput] Initialised text input component
16
+
17
+ class PasswordInput
18
+ private
19
+
20
+ attr_reader :text_input
21
+
22
+ public
23
+
24
+ # @param (see CCS::Components::GovUK::Field::Input::TextInput#initialize)
25
+ #
26
+ # @option (see CCS::Components::GovUK::Field::Input::TextInput#initialize)
27
+
28
+ def initialize(attribute:, context:, **options)
29
+ set_password_input_from_group_options(options)
30
+
31
+ show_hide_button = ShowHideButton.new(attribute: attribute, context: context, after_input: options.delete(:after_input), **options)
32
+
33
+ options[:attributes] ||= {}
34
+ options[:attributes][:spellcheck] = false
35
+ options[:attributes][:autocapitalize] = 'none'
36
+ options[:attributes][:autocomplete] ||= 'current-password'
37
+
38
+ @text_input = TextInput.new(
39
+ context: context,
40
+ attribute: attribute,
41
+ after_input: show_hide_button.render,
42
+ input_wrapper: {
43
+ classes: 'govuk-password-input__wrapper'
44
+ },
45
+ classes: "govuk-password-input__input govuk-js-password-input-input #{options.delete(:classes)}".rstrip,
46
+ field_type: :password,
47
+ **options
48
+ )
49
+ end
50
+
51
+ delegate :render, to: :text_input
52
+
53
+ private
54
+
55
+ # rubocop:disable Naming/AccessorMethodName
56
+
57
+ # Sets the password input form group options
58
+ #
59
+ # @param options [Hash] options for the password input
60
+ #
61
+ # @option options [String] :show_password_text button text when the password is hidden
62
+ # @option options [String] :hide_password_text button text when the password is visible
63
+ # @option options [String] :show_password_aria_label_text button text exposed to assistive technologies, like screen readers, when the password is hidden
64
+ # @option options [String] :hide_password_aria_label_text button text exposed to assistive technologies, like screen readers, when the password is visible
65
+ # @option options [String] :password_shown_announcement_text announcement made to screen reader users when their password has become visible in plain text
66
+ # @option options [String] :password_hidden_announcement_text announcement made to screen reader users when their password has been obscured and is not visible
67
+
68
+ def set_password_input_from_group_options(options)
69
+ (options[:form_group] ||= {})[:classes] = "govuk-password-input #{options.dig(:form_group, :classes)}".rstrip
70
+ ((options[:form_group][:attributes] ||= {})[:data] ||= {})[:module] = 'govuk-password-input'
71
+
72
+ get_password_input_translations(options) do |data_attribute, value|
73
+ options[:form_group][:attributes][:data][data_attribute] = value
74
+ end
75
+ end
76
+
77
+ # rubocop:enable Naming/AccessorMethodName
78
+
79
+ # Generator for the translation options for password input
80
+ #
81
+ # @param (see initialise_password_input_html_options)
82
+ #
83
+ # @option (see initialise_password_input_html_options)
84
+ #
85
+ # @yield Data attribute key and the value
86
+
87
+ def get_password_input_translations(password_input_options)
88
+ %i[show_password hide_password show_password_aria_label hide_password_aria_label password_shown_announcement password_hidden_announcement].each do |data_attribute|
89
+ data_attribute_name = :"#{data_attribute}_text"
90
+
91
+ next unless password_input_options[data_attribute_name]
92
+
93
+ yield :"i18n.#{data_attribute.to_s.gsub('_', '-')}", password_input_options[data_attribute_name]
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -19,11 +19,13 @@ module CCS
19
19
  # @return [Fix] The initialised prefix
20
20
  # @!attribute [r] suffix
21
21
  # @return [Fix] The initialised suffix
22
+ # @!attribute [r] input_wrapper_html_options
23
+ # @return [Fix] HTML options for the input wrapper
22
24
 
23
25
  class TextInput < Input
24
26
  private
25
27
 
26
- attr_reader :field_type, :value, :prefix, :suffix
28
+ attr_reader :field_type, :value, :prefix, :suffix, :input_wrapper_html_options
27
29
 
28
30
  public
29
31
 
@@ -36,33 +38,41 @@ module CCS
36
38
  # see {CCS::Components::GovUK::Field::Input::TextInput::Fix#initialize Fix#initialize} for more details.
37
39
  # @param suffix [Hash] optional suffix for the input field,
38
40
  # see {CCS::Components::GovUK::Field::Input::TextInput::Fix#initialize Fix#initialize} for more details.
41
+ # @param input_wrapper [Hash] HTML options for the input wrapper
39
42
  #
40
43
  # @option (see CCS::Components::GovUK::Field::Input#initialize)
41
44
 
42
- def initialize(attribute:, field_type: :text, value: nil, prefix: nil, suffix: nil, **options)
45
+ def initialize(attribute:, field_type: :text, value: nil, prefix: nil, suffix: nil, input_wrapper: {}, **options)
43
46
  super(attribute: attribute, **options)
44
47
 
45
48
  @field_type = :"#{field_type}_field"
46
49
  @value = @options[:model] ? @options[:model].send(attribute) : value
47
50
  @prefix = Fix.new(fix: 'pre', context: @context, **prefix) if prefix
48
51
  @suffix = Fix.new(fix: 'suf', context: @context, **suffix) if suffix
52
+ @input_wrapper_html_options = {
53
+ class: "govuk-input__wrapper #{input_wrapper[:classes]}".rstrip
54
+ }.merge(input_wrapper[:attributes] || {})
49
55
  end
50
56
 
51
57
  # rubocop:enable Metrics/ParameterLists
58
+ # rubocop:disable Metrics/AbcSize
52
59
 
53
60
  # Generates the HTML for the GOV.UK Text Input component
54
61
  #
55
62
  # @return [ActiveSupport::SafeBuffer]
56
63
 
57
64
  def render
58
- super() do
59
- text_input_wrapper do
65
+ form_group.render do |display_error_message|
66
+ concat(label.render)
67
+ concat(hint.render) if hint
68
+ concat(display_error_message)
69
+ concat(text_input_wrapper do
60
70
  if options[:form]
61
71
  options[:form].send(field_type, attribute, **options[:attributes])
62
72
  else
63
73
  context.send("#{field_type}_tag", attribute, value, **options[:attributes])
64
74
  end
65
- end
75
+ end)
66
76
  end
67
77
  end
68
78
 
@@ -72,6 +82,8 @@ module CCS
72
82
 
73
83
  private
74
84
 
85
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
86
+
75
87
  # Wrapper method used by {render} to wrap the text input with a prefix or suffix if they exist
76
88
  #
77
89
  # @yield the text input HTML
@@ -79,16 +91,20 @@ module CCS
79
91
  # @return [ActiveSupport::SafeBuffer]
80
92
 
81
93
  def text_input_wrapper
82
- if prefix || suffix
83
- tag.div(class: 'govuk-input__wrapper') do
94
+ if prefix || suffix || before_input || after_input
95
+ tag.div(**input_wrapper_html_options) do
96
+ concat(before_input) if before_input
84
97
  concat(prefix.render) if prefix
85
98
  concat(yield)
86
99
  concat(suffix.render) if suffix
100
+ concat(after_input) if after_input
87
101
  end
88
102
  else
89
103
  yield
90
104
  end
91
105
  end
106
+
107
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
92
108
  end
93
109
  end
94
110
  end
@@ -25,6 +25,8 @@ module CCS
25
25
 
26
26
  # @param (see CCS::Components::GovUK::Field#initialize)
27
27
  # @param label [Hash] attributes for the label, see {CCS::Components::GovUK::Label#initialize Label#initialize} for more details.
28
+ # @param before_input [String] text or HTML to go before the input
29
+ # @param after_input [String] text or HTML to go after the input
28
30
  #
29
31
  # @option (see CCS::Components::GovUK::Field#initialize)
30
32
 
@@ -52,7 +54,9 @@ module CCS
52
54
  concat(label.render)
53
55
  concat(hint.render) if hint
54
56
  concat(display_error_message)
57
+ concat(before_input) if before_input
55
58
  concat(yield)
59
+ concat(after_input) if after_input
56
60
  end
57
61
  end
58
62
  end
@@ -12,17 +12,8 @@ module CCS
12
12
  #
13
13
  # This is used for generating the checkboxes component from the
14
14
  # {https://design-system.service.gov.uk/components/checkboxes GDS - Components - Checkboxes}
15
- #
16
- # @!attribute [r] checkbox_items
17
- # @return [Array<Item::Divider|Item::Checkbox|Item::Checkbox>] An array of the initialised checkbox items
18
15
 
19
16
  class Checkboxes < Inputs
20
- private
21
-
22
- attr_reader :checkbox_items
23
-
24
- public
25
-
26
17
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
27
18
 
28
19
  # @param (see CCS::Components::GovUK::Field::Inputs#initialize)
@@ -42,23 +33,11 @@ module CCS
42
33
 
43
34
  checkbox_item_class = @options[:form] ? Item::Checkbox::Form : Inputs::Item::Checkbox::Tag
44
35
 
45
- @checkbox_items = checkbox_items.map { |checkbox_item| checkbox_item[:divider] ? Item::Divider.new(divider: checkbox_item[:divider], type: 'checkboxes') : checkbox_item_class.new(attribute: attribute, form: @options[:form], context: @context, **checkbox_item) }
36
+ @input_items = checkbox_items.map { |checkbox_item| checkbox_item[:divider] ? Item::Divider.new(divider: checkbox_item[:divider], type: 'checkboxes') : checkbox_item_class.new(attribute: attribute, form: @options[:form], context: @context, **checkbox_item) }
46
37
  end
47
38
 
48
39
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
49
40
 
50
- # Generates the HTML for the GOV.UK Checkboxes component
51
- #
52
- # @return [ActiveSupport::SafeBuffer]
53
-
54
- def render
55
- super() do
56
- tag.div(**options[:attributes]) do
57
- checkbox_items.each { |checkbox_item| concat(checkbox_item.render) }
58
- end
59
- end
60
- end
61
-
62
41
  # The default attributes for the checkboxes
63
42
 
64
43
  DEFAULT_ATTRIBUTES = { class: 'govuk-checkboxes', data: { module: 'govuk-checkboxes' } }.freeze
@@ -10,17 +10,8 @@ module CCS
10
10
  #
11
11
  # This is used for generating the date input component from the
12
12
  # {https://design-system.service.gov.uk/components/date-input GDS - Components - Date Input}
13
- #
14
- # @!attribute [r] date_input_items
15
- # @return [Array<DateInput::Item>] An array of the initialised date input items
16
13
 
17
14
  class DateInput < Inputs
18
- private
19
-
20
- attr_reader :date_input_items
21
-
22
- public
23
-
24
15
  # @param (see CCS::Components::GovUK::Field::Inputs#initialize)
25
16
  # @param date_items [Array<Hash>] an array of options for the date items.
26
17
  # See {Components::GovUK::Field::Inputs::DateInput::Item#initialize Item#initialize} for details of the items in the array.
@@ -34,19 +25,7 @@ module CCS
34
25
 
35
26
  date_items = default_date_items if date_items.blank?
36
27
 
37
- @date_input_items = date_items.map { |date_input_item| Item.new(attribute: attribute, error_message: @error_message, model: @options[:model], form: @options[:form], context: @context, **date_input_item) }
38
- end
39
-
40
- # Generates the HTML for the GOV.UK date input component
41
- #
42
- # @return [ActiveSupport::SafeBuffer]
43
-
44
- def render
45
- super() do
46
- tag.div(**options[:attributes]) do
47
- date_input_items.each { |date_input_item| concat(date_input_item.render) }
48
- end
49
- end
28
+ @input_items = date_items.map { |date_input_item| Item.new(attribute: attribute, error_message: @error_message, model: @options[:model], form: @options[:form], context: @context, **date_input_item) }
50
29
  end
51
30
 
52
31
  # The default attributes for the date input
@@ -12,14 +12,11 @@ module CCS
12
12
  #
13
13
  # This is used for generating the radios component from the
14
14
  # {https://design-system.service.gov.uk/components/radios GDS - Components - Radios}
15
- #
16
- # @!attribute [r] radio_items
17
- # @return [Array<Item::Divider|Item::Radio|Item::Radio>] An array of the initialised radio items
18
15
 
19
16
  class Radios < Inputs
20
17
  private
21
18
 
22
- attr_reader :radio_items
19
+ attr_reader :input_items
23
20
 
24
21
  public
25
22
 
@@ -40,23 +37,11 @@ module CCS
40
37
 
41
38
  radio_item_class = @options[:form] ? Item::Radio::Form : Inputs::Item::Radio::Tag
42
39
 
43
- @radio_items = radio_items.map { |radio_item| radio_item[:divider] ? Item::Divider.new(divider: radio_item[:divider], type: 'radios') : radio_item_class.new(attribute: attribute, form: @options[:form], context: @context, **radio_item) }
40
+ @input_items = radio_items.map { |radio_item| radio_item[:divider] ? Item::Divider.new(divider: radio_item[:divider], type: 'radios') : radio_item_class.new(attribute: attribute, form: @options[:form], context: @context, **radio_item) }
44
41
  end
45
42
 
46
43
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
47
44
 
48
- # Generates the HTML for the GOV.UK Radios component
49
- #
50
- # @return [ActiveSupport::SafeBuffer]
51
-
52
- def render
53
- super() do
54
- tag.div(**options[:attributes]) do
55
- radio_items.each { |radio_item| concat(radio_item.render) }
56
- end
57
- end
58
- end
59
-
60
45
  # The default attributes for the radios
61
46
 
62
47
  DEFAULT_ATTRIBUTES = { class: 'govuk-radios', data: { module: 'govuk-radios' } }.freeze
@@ -14,11 +14,13 @@ module CCS
14
14
  #
15
15
  # @!attribute [r] fieldset
16
16
  # @return [Fieldset] The initialised fieldset
17
+ # @!attribute [r] input_items
18
+ # @return [Array<Item>] An array of the initialised items
17
19
 
18
20
  class Inputs < Field
19
21
  private
20
22
 
21
- attr_reader :fieldset
23
+ attr_reader :fieldset, :input_items
22
24
 
23
25
  public
24
26
 
@@ -66,7 +68,11 @@ module CCS
66
68
  def field_inner_html(display_error_message)
67
69
  concat(hint.render) if hint
68
70
  concat(display_error_message)
69
- concat(yield)
71
+ concat(tag.div(**options[:attributes]) do
72
+ concat(before_input) if before_input
73
+ input_items.each { |input_item| concat(input_item.render) }
74
+ concat(after_input) if after_input
75
+ end)
70
76
  end
71
77
  end
72
78
  end
@@ -18,14 +18,20 @@ module CCS
18
18
  # @return [FormGroup] The initialised form group
19
19
  # @!attribute [r] hint
20
20
  # @return [Hint] The initialised hint
21
+ # @!attribute [r] before_input
22
+ # @return [String] Text or HTML to go before the input
23
+ # @!attribute [r] after_input
24
+ # @return [String] Text or HTML to go after the input
21
25
 
22
26
  class Field < Base
23
27
  private
24
28
 
25
- attr_reader :attribute, :error_message, :form_group, :hint
29
+ attr_reader :attribute, :error_message, :form_group, :hint, :before_input, :after_input
26
30
 
27
31
  public
28
32
 
33
+ # rubocop:disable Metrics/ParameterLists
34
+
29
35
  # @param attribute [String, Symbol] the attribute of the field
30
36
  # @param hint [Hash] attributes for the hint, see {CCS::Components::GovUK::Hint#initialize Hint#initialize} for more details.
31
37
  # If no hint is given then no hint will be rendered
@@ -39,7 +45,7 @@ module CCS
39
45
  # @option options [String] :classes additional CSS classes for the field HTML
40
46
  # @option options [Hash] :attributes ({}) any additional attributes that will added as part of the HTML
41
47
 
42
- def initialize(attribute:, form_group: nil, hint: nil, **options)
48
+ def initialize(attribute:, form_group: nil, hint: nil, before_input: nil, after_input: nil, **options)
43
49
  super(**options)
44
50
 
45
51
  (hint[:attributes] ||= {})[:id] = "#{attribute}-hint" if hint && !hint.dig(:attributes, :id)
@@ -49,8 +55,12 @@ module CCS
49
55
 
50
56
  @form_group = FormGroup.new(attribute: attribute, error_message: @error_message, context: @context, **(form_group || {}))
51
57
  @hint = Hint.new(context: @context, **hint) if hint
58
+ @before_input = before_input
59
+ @after_input = after_input
52
60
  end
53
61
 
62
+ # rubocop:enable Metrics/ParameterLists
63
+
54
64
  # Generates the HTML to wrap arround a GDS form input component
55
65
  #
56
66
  # @yield the field HTML
@@ -37,7 +37,6 @@ module CCS
37
37
  def initialize(navigation: nil, meta: nil, **options)
38
38
  super(**options)
39
39
 
40
- @options[:attributes][:role] = 'contentinfo'
41
40
  @options[:copyright] ||= '© Crown copyright'
42
41
 
43
42
  @navigation = navigation&.map { |navigation_item| Navigation.new(context: @context, **navigation_item) }
@@ -41,7 +41,6 @@ module CCS
41
41
  def initialize(navigation: nil, menu_button: nil, service: nil, **options)
42
42
  super(**options)
43
43
 
44
- @options[:attributes][:role] = 'banner'
45
44
  @options[:container_classes] ||= 'govuk-width-container'
46
45
  @options[:homepage_url] ||= '/'
47
46
  @options[:use_tudor_crown] = true if @options[:use_tudor_crown].nil?
@@ -46,7 +46,6 @@ module CCS
46
46
  block_is_level = pagination_items.blank? && (pagination_previous.present? || pagination_next.present?)
47
47
 
48
48
  @options[:attributes][:class] << ' govuk-pagination--block' if block_is_level
49
- @options[:attributes][:role] = 'navigation'
50
49
  (@options[:attributes][:aria] ||= {})[:label] ||= 'Pagination'
51
50
 
52
51
  @pagination_previous = Increment::Previous.new(block_is_level: block_is_level, form: @options[:form], context: @context, **pagination_previous) if pagination_previous
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../components/govuk/field/input/password_input'
4
+
5
+ module CCS
6
+ module FrontendHelpers
7
+ module GovUKFrontend
8
+ # = GOV.UK Password input
9
+ #
10
+ # This helper is used for generating the password input component from the
11
+ # {https://design-system.service.gov.uk/components/password-input GDS - Components - Password Input}
12
+
13
+ module PasswordInput
14
+ # Generates the HTML for the GOV.UK Password Input component
15
+ #
16
+ # @param (see CCS::Components::GovUK::Input::PasswordInput#initialize)
17
+ #
18
+ # @option (see CCS::Components::GovUK::Input::PasswordInput#initialize)
19
+ #
20
+ # @return (see CCS::Components::GovUK::Input::PasswordInput#render)
21
+
22
+ def govuk_password_input(attribute, **options)
23
+ Components::GovUK::Field::Input::PasswordInput.new(context: self, attribute: attribute, **options).render
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -24,6 +24,7 @@ require_relative 'govuk_frontend/label'
24
24
  require_relative 'govuk_frontend/notification_banner'
25
25
  require_relative 'govuk_frontend/pagination'
26
26
  require_relative 'govuk_frontend/panel'
27
+ require_relative 'govuk_frontend/password_input'
27
28
  require_relative 'govuk_frontend/phase_banner'
28
29
  require_relative 'govuk_frontend/radios'
29
30
  require_relative 'govuk_frontend/select'
@@ -67,6 +68,7 @@ module CCS
67
68
  include NotificationBanner
68
69
  include Pagination
69
70
  include Panel
71
+ include PasswordInput
70
72
  include PhaseBanner
71
73
  include Radios
72
74
  include Select
@@ -2,6 +2,6 @@
2
2
 
3
3
  module CCS
4
4
  module FrontendHelpers
5
- VERSION = '0.2.0'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
data/package.json CHANGED
@@ -5,6 +5,6 @@
5
5
  "author": "CCS",
6
6
  "license": "MIT",
7
7
  "devDependencies": {
8
- "govuk-frontend": "^5.2.0"
8
+ "govuk-frontend": "^5.3.0"
9
9
  }
10
10
  }
data/yarn.lock CHANGED
@@ -2,7 +2,7 @@
2
2
  # yarn lockfile v1
3
3
 
4
4
 
5
- govuk-frontend@^5.2.0:
6
- version "5.2.0"
7
- resolved "https://registry.yarnpkg.com/govuk-frontend/-/govuk-frontend-5.2.0.tgz#f8e0bf98b771b8ee1501fd45bbba24a091f3846d"
8
- integrity sha512-beD3wztHpkKz6JUpPwnwop1ejb4rTFMPLCutKLCIDmUS4BPpW59ggVUfctsRqHd2Zjw9wxljdRdeIJ8AZFyyTw==
5
+ govuk-frontend@^5.3.0:
6
+ version "5.3.0"
7
+ resolved "https://registry.yarnpkg.com/govuk-frontend/-/govuk-frontend-5.3.0.tgz#f9a2b405925beada90d074e17939f9d8ddcd256e"
8
+ integrity sha512-w6yaaDU3nqhVmWJFnOuJKRIUEB/2RrTFGzoA3z5n4cTG9h292EseT/q+JJA/LYTf5IYzeTIVAUbE5fFjUxefiQ==
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ccs-frontend_helpers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - tim-s-ccs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-13 00:00:00.000000000 Z
11
+ date: 2024-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -72,7 +72,10 @@ files:
72
72
  - lib/ccs/components/govuk/field.rb
73
73
  - lib/ccs/components/govuk/field/input.rb
74
74
  - lib/ccs/components/govuk/field/input/character_count.rb
75
+ - lib/ccs/components/govuk/field/input/character_count/count_message.rb
75
76
  - lib/ccs/components/govuk/field/input/file_upload.rb
77
+ - lib/ccs/components/govuk/field/input/password_input.rb
78
+ - lib/ccs/components/govuk/field/input/password_input/show_hide_button.rb
76
79
  - lib/ccs/components/govuk/field/input/select.rb
77
80
  - lib/ccs/components/govuk/field/input/text_input.rb
78
81
  - lib/ccs/components/govuk/field/input/text_input/fix.rb
@@ -174,6 +177,7 @@ files:
174
177
  - lib/ccs/frontend_helpers/govuk_frontend/notification_banner.rb
175
178
  - lib/ccs/frontend_helpers/govuk_frontend/pagination.rb
176
179
  - lib/ccs/frontend_helpers/govuk_frontend/panel.rb
180
+ - lib/ccs/frontend_helpers/govuk_frontend/password_input.rb
177
181
  - lib/ccs/frontend_helpers/govuk_frontend/phase_banner.rb
178
182
  - lib/ccs/frontend_helpers/govuk_frontend/radios.rb
179
183
  - lib/ccs/frontend_helpers/govuk_frontend/select.rb