govuk_design_system_formbuilder 0.7.0 → 0.7.1

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: fb762e8a359cd08175a12d4589052ea02fe5d30cdd74b0d970ba8b52c8ac09c2
4
- data.tar.gz: a190f9073ebcd4d59abb9c62485be024f729343544fbc278c2dbbb3d5949b14a
3
+ metadata.gz: 843bd26d3611fad40854e1bdfc5864617e64f90ffc51ededfcf1af0a74f4d2ae
4
+ data.tar.gz: 530a95ed1fb38467f56ef2904110d153c8b1fe5832d5226914f38ae482c8aca7
5
5
  SHA512:
6
- metadata.gz: 20a8147da81aa6add15ec3500ffd849e18ef5f29add89a4d10511d90be78eca08a278315ae17036c5c5ae1e7861408d27133c8f2dcb84faf30e032b7219b142c
7
- data.tar.gz: 5f1e669b7248b062727a7b960aa4448c216a9ad4d30d5ead7314d8989430925b31fd700ed7cd34d4613e0c81465a358d61d59ca4ff47abc8a161b145338e8879
6
+ metadata.gz: 129ddeaf0fba3811dfd02b6730af2e30adfe1c7a312744aea73ee8bafddade947d92912f86f6e94b94307385b9a6f7dfbf2f58487e4b5d4345c5dc2d53cc1ebd
7
+ data.tar.gz: 8ff31c945db767cb82dff353e7355e52f1901d6dc7e517f08f6b6a40ed5816bde09a1832f428b9c1c4d9f71304d64f539acbc9dfdd26e4ccccecd5cd2661ab73
data/README.md CHANGED
@@ -1 +1,78 @@
1
- # GOV.UK Form Builder
1
+ # GOV.UK Design System Form Builder for Rails
2
+
3
+ ## What's included
4
+
5
+ * 100% compatibility with the GOV.UK Design System
6
+ * Full control of labels, hints, fieldsets and legends
7
+ * No overwriting of Rails' default form helpers
8
+ * Automatic ARIA associations between hints, errors and inputs
9
+ * Most helpers take a block for arbitrary content
10
+ * Additional params for programmatically adding hints to check box and radio
11
+ button collections
12
+
13
+ ## Installation
14
+
15
+ You can install the form builder gem by running the `gem install
16
+ govuk_design_system_formbuilder` or by adding the following line
17
+ to your `Gemfile`
18
+
19
+ ```sh
20
+ gem 'govuk_design_system_formbuilder'
21
+ ```
22
+
23
+ ## Setup
24
+
25
+ To use the form builder in an ad hoc basis you can specify it
26
+ as an argument to `form_for`
27
+
28
+
29
+ ```slim
30
+ = form_for @some_object, builder: GOVUKDesignSystemFormBuilder::FormBuilder do |f|
31
+ ```
32
+
33
+ To set it globally, just add this to your `ApplicationController`
34
+
35
+ ```ruby
36
+ class ApplicationController < ActionController::Base
37
+ default_form_builder GOVUKDesignSystemFormBuilder::FormBuilder
38
+ end
39
+ ```
40
+
41
+ Now we can get started! 🎉
42
+
43
+ ```slim
44
+ = form_for @person do |f|
45
+
46
+ = f.govuk_text_field :full_name, label: { text: 'Your full name' }
47
+
48
+ = f.govuk_number_field :age, label: { text: 'Age' }
49
+
50
+ = f.submit 'Away we go!'
51
+ ```
52
+
53
+ ## Developing and running the tests
54
+
55
+ The form builder is covered by RSpec, to run all the tests first ensure that
56
+ all of the development and testing prerequisite gems are installed. At the root
57
+ of a freshly-cloned repo run
58
+
59
+ ```sh
60
+ bundle
61
+ ```
62
+
63
+ Now, if everything was successful, run RSpec
64
+
65
+ ```sh
66
+ rspec -fd
67
+ ```
68
+
69
+ ## Thanks
70
+
71
+ This project was inspired by [MoJ's GovukElementsFormBuilder](https://github.com/ministryofjustice/govuk_elements_form_builder),
72
+ but tackles some problems in a different manner. It provides the following
73
+ advantages by providing:
74
+
75
+ * no reliance on a custom proxy/block buffer
76
+ * idiomatic Ruby
77
+ * a modular codebase
78
+ * a more-robust test suite
@@ -6,10 +6,8 @@ require 'govuk_design_system_formbuilder/elements/hint'
6
6
  require 'govuk_design_system_formbuilder/elements/label'
7
7
  require 'govuk_design_system_formbuilder/elements/input'
8
8
  require 'govuk_design_system_formbuilder/elements/date'
9
- require 'govuk_design_system_formbuilder/elements/radio'
10
9
  require 'govuk_design_system_formbuilder/elements/radios/collection_radio'
11
10
  require 'govuk_design_system_formbuilder/elements/radios/fieldset_radio'
12
- require 'govuk_design_system_formbuilder/elements/check_box'
13
11
  require 'govuk_design_system_formbuilder/elements/check_boxes/collection_check_box'
14
12
  require 'govuk_design_system_formbuilder/elements/check_boxes/label'
15
13
  require 'govuk_design_system_formbuilder/elements/check_boxes/hint'
@@ -11,19 +11,19 @@ module GOVUKDesignSystemFormBuilder
11
11
  end
12
12
 
13
13
  def hint_id
14
- return nil unless @text.present?
14
+ return nil unless @hint_text.present?
15
15
 
16
- @hint_id || [@object_name, @attribute_name, 'hint'].join('-').parameterize
16
+ [@object_name, @attribute_name, @value, 'hint'].compact.join('-').parameterize
17
17
  end
18
18
 
19
19
  def error_id
20
20
  return nil unless has_errors?
21
21
 
22
- [@object_name, @attribute_name, 'error'].join('-').parameterize
22
+ [@object_name, @attribute_name, 'error'].compact.join('-').parameterize
23
23
  end
24
24
 
25
25
  def conditional_id
26
- [@object_name, @attribute_name, @value, 'conditional'].join('-').parameterize
26
+ [@object_name, @attribute_name, @value, 'conditional'].compact.join('-').parameterize
27
27
  end
28
28
 
29
29
  def has_errors?
@@ -41,5 +41,12 @@ module GOVUKDesignSystemFormBuilder
41
41
  attribute_name: @attribute_name
42
42
  }
43
43
  end
44
+
45
+ def wrap_conditional(block)
46
+ conditional = @builder.content_tag('div', class: conditional_classes, id: conditional_id) do
47
+ @builder.capture { block.call }
48
+ end
49
+ return conditional, conditional_id
50
+ end
44
51
  end
45
52
  end
@@ -1,35 +1,150 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Builder
3
- def govuk_text_field(attribute_name, hint: {}, label: {}, **args)
4
- Elements::Input.new(self, object_name, attribute_name, attribute_type: :text, hint: hint, label: label, **args).html
3
+
4
+ # Generates a input of type `text`
5
+ #
6
+ # @param attribute_name [Symbol] The name of the attribute
7
+ # @param hint_text [String] The content of the hint. No hint will be injected if left +nil+
8
+ # @param [Hash] label configures the associated label
9
+ # @option label text [String] the label text
10
+ # @option label size [String] the size of the label font, can be +large+, +medium+, +regular+ or +small+
11
+ # @option label weight [String] the weight of the label font, can be +bold+ or +regular+
12
+ # @option args [Hash] args additional arguments are applied as attributes to +input+ element
13
+ # @return [ActiveSupport::SafeBuffer] HTML output
14
+ #
15
+ # @example A required full name field with a placeholder
16
+ # = govuk_text_field :name,
17
+ # label: { text: 'Full name' },
18
+ # hint_text: 'It says it on your birth certificate',
19
+ # required: true,
20
+ # placeholder: 'Ralph Wiggum'
21
+ def govuk_text_field(attribute_name, hint_text: nil, label: {}, **args)
22
+ Elements::Input.new(self, object_name, attribute_name, attribute_type: :text, hint_text: hint_text, label: label, **args).html
5
23
  end
6
24
 
7
- def govuk_phone_field(attribute_name, hint: {}, label: {}, **args)
8
- Elements::Input.new(self, object_name, attribute_name, attribute_type: :phone, hint: hint, label: label, **args).html
25
+ # Generates a input of type `tel`
26
+ #
27
+ # @param attribute_name [Symbol] The name of the attribute
28
+ # @param hint_text [String] The content of the hint. No hint will be injected if left +nil+
29
+ # @param [Hash] label configures the associated label
30
+ # @option label text [String] the label text
31
+ # @option label size [String] the size of the label font, can be +large+, +medium+, +regular+ or +small+
32
+ # @option label weight [String] the weight of the label font, can be +bold+ or +regular+
33
+ # @option args [Hash] args additional arguments are applied as attributes to +input+ element
34
+ # @return [ActiveSupport::SafeBuffer] HTML output
35
+ #
36
+ # @example A required phone number field with a placeholder
37
+ # = govuk_phone_field :phone_number,
38
+ # label: { text: 'UK telephone number' },
39
+ # hint_text: 'Include the dialling code',
40
+ # required: true,
41
+ # placeholder: '0123 456 789'
42
+ def govuk_phone_field(attribute_name, hint_text: nil, label: {}, **args)
43
+ Elements::Input.new(self, object_name, attribute_name, attribute_type: :phone, hint_text: hint_text, label: label, **args).html
9
44
  end
10
45
 
11
- def govuk_email_field(attribute_name, hint: {}, label: {}, **args)
12
- Elements::Input.new(self, object_name, attribute_name, attribute_type: :email, hint: hint, label: label, **args).html
46
+ # Generates a input of type `email`
47
+ #
48
+ # @param attribute_name [Symbol] The name of the attribute
49
+ # @param hint_text [String] The content of the hint. No hint will be injected if left +nil+
50
+ # @param [Hash] label configures the associated label
51
+ # @option label text [String] the label text
52
+ # @option label size [String] the size of the label font, can be +large+, +medium+, +regular+ or +small+
53
+ # @option label weight [String] the weight of the label font, can be +bold+ or +regular+
54
+ # @option args [Hash] args additional arguments are applied as attributes to +input+ element
55
+ # @return [ActiveSupport::SafeBuffer] HTML output
56
+ #
57
+ # @example An email address field with a placeholder
58
+ # = govuk_email_field :email_address,
59
+ # label: { text: 'Enter your email address' },
60
+ # placeholder: 'ralph.wiggum@springfield.edu'
61
+ def govuk_email_field(attribute_name, hint_text: nil, label: {}, **args)
62
+ Elements::Input.new(self, object_name, attribute_name, attribute_type: :email, hint_text: hint_text, label: label, **args).html
13
63
  end
14
64
 
15
- def govuk_url_field(attribute_name, hint: {}, label: {}, **args)
16
- Elements::Input.new(self, object_name, attribute_name, attribute_type: :url, hint: hint, label: label, **args).html
65
+ # Generates a input of type `url`
66
+ #
67
+ # @param attribute_name [Symbol] The name of the attribute
68
+ # @param hint_text [String] The content of the hint. No hint will be injected if left +nil+
69
+ # @param [Hash] label configures the associated label
70
+ # @option label text [String] the label text
71
+ # @option label size [String] the size of the label font, can be +large+, +medium+, +regular+ or +small+
72
+ # @option label weight [String] the weight of the label font, can be +bold+ or +regular+
73
+ # @option args [Hash] args additional arguments are applied as attributes to +input+ element
74
+ # @return [ActiveSupport::SafeBuffer] HTML output
75
+ #
76
+ # @example A url field with autocomplete
77
+ # = govuk_url_field :favourite_website,
78
+ # label: { text: 'Enter your favourite website' },
79
+ # placeholder: 'https://www.gov.uk',
80
+ # autocomplete: 'url'
81
+ def govuk_url_field(attribute_name, hint_text: nil, label: {}, **args)
82
+ Elements::Input.new(self, object_name, attribute_name, attribute_type: :url, hint_text: hint_text, label: label, **args).html
17
83
  end
18
84
 
19
- def govuk_number_field(attribute_name, hint: {}, label: {}, **args)
20
- Elements::Input.new(self, object_name, attribute_name, attribute_type: :number, hint: hint, label: label, **args).html
85
+ # Generates a input of type `number`
86
+ #
87
+ # @param attribute_name [Symbol] The name of the attribute
88
+ # @param hint_text [String] The content of the hint. No hint will be injected if left +nil+
89
+ # @param [Hash] label configures the associated label
90
+ # @option label text [String] the label text
91
+ # @option label size [String] the size of the label font, can be +large+, +medium+, +regular+ or +small+
92
+ # @option label weight [String] the weight of the label font, can be +bold+ or +regular+
93
+ # @option args [Hash] args additional arguments are applied as attributes to the +input+ element
94
+ # @return [ActiveSupport::SafeBuffer] HTML output
95
+ #
96
+ # @example A number field with placeholder, min, max and step
97
+ # = govuk_number_field :iq,
98
+ # label: { text: 'What is your IQ?' },
99
+ # placeholder: 100,
100
+ # min: 80,
101
+ # max: 150,
102
+ # step: 5
103
+ def govuk_number_field(attribute_name, hint_text: nil, label: {}, **args)
104
+ Elements::Input.new(self, object_name, attribute_name, attribute_type: :number, hint_text: hint_text, label: label, **args).html
21
105
  end
22
106
 
23
- def govuk_text_area(attribute_name, hint: {}, label: {}, max_words: nil, max_chars: nil, rows: 5, **args)
24
- Elements::TextArea.new(self, object_name, attribute_name, hint: hint, label: label, max_words: max_words, max_chars: max_chars, rows: rows, **args).html
107
+
108
+ # Generates a +textarea+ element with a label, optional hint. Also offers
109
+ # the ability to add the GOV.UK character and word counting components
110
+ # automatically
111
+ #
112
+ # @param attribute_name [Symbol] The name of the attribute
113
+ # @param hint_text [String] The content of the hint. No hint will be injected if left +nil+
114
+ # @param [Hash] label configures the associated label
115
+ # @option label text [String] the label text
116
+ # @option label size [String] the size of the label font, can be +large+, +medium+, +regular+ or +small+
117
+ # @option label weight [String] the weight of the label font, can be +bold+ or +regular+
118
+ # @param max_words [Integer] adds the GOV.UK max word count
119
+ # @param max_chars [Integer] adds the GOV.UK max characters count
120
+ # @option args [Hash] args additional arguments are applied as attributes to the +textarea+ element
121
+ # @return [ActiveSupport::SafeBuffer] HTML output
122
+ # @see https://design-system.service.gov.uk/components/character-count GOV.UK character count component
123
+ # @note Setting +max_chars+ or +max_words+ will add a caption beneath the +textarea+ with a live count of words
124
+ # or characters
125
+ #
126
+ # @example A number field with placeholder, min, max and step
127
+ # = govuk_number_field :cv,
128
+ # label: { text: 'Tell us about your work history' },
129
+ # rows: 8,
130
+ # max_words: 300
131
+ def govuk_text_area(attribute_name, hint_text: nil, label: {}, max_words: nil, max_chars: nil, rows: 5, **args)
132
+ Elements::TextArea.new(self, object_name, attribute_name, hint_text: hint_text, label: label, max_words: max_words, max_chars: max_chars, rows: rows, **args).html
25
133
  end
26
134
 
27
- # FIXME #govuk_collection_select args differ from Rails' #collection_select args in that
28
- # options: and :html_options are keyword arguments. Leaving them as regular args with
29
- # defaults and following them with keyword args (hint and label) doesn't seem to work
30
- def govuk_collection_select(attribute_name, collection, value_method, text_method, options: {}, html_options: {}, hint: {}, label: {})
135
+ # Generates a +select+ element containing +option+ for each member in the provided collection
136
+ #
137
+ # @param attribute_name [Symbol] The name of the attribute
138
+ # @param collection [Enumerable<Object>] Options to be added to the +select+ element
139
+ # @param value_method [Symbol] The method called against each member of the collection to provide the value
140
+ # @param text_method [Symbol] The method called against each member of the collection to provide the text
141
+ # @param hint_text [String] The content of the hint. No hint will be injected if left +nil+
142
+ # @option label text [String] the label text
143
+ # @option label size [String] the size of the label font, can be +large+, +medium+, +regular+ or +small+
144
+ # @option label weight [String] the weight of the label font, can be +bold+ or +regular+
145
+ def govuk_collection_select(attribute_name, collection, value_method, text_method, options: {}, html_options: {}, hint_text: nil, label: {})
31
146
  label_element = Elements::Label.new(self, object_name, attribute_name, label)
32
- hint_element = Elements::Hint.new(self, object_name, attribute_name, hint)
147
+ hint_element = Elements::Hint.new(self, object_name, attribute_name, hint_text)
33
148
  error_element = Elements::ErrorMessage.new(self, object_name, attribute_name)
34
149
 
35
150
  Containers::FormGroup.new(self, object_name, attribute_name).html do
@@ -59,105 +174,83 @@ module GOVUKDesignSystemFormBuilder
59
174
  end
60
175
  end
61
176
 
62
- def govuk_collection_radio_buttons(attribute_name, collection, value_method, text_method, hint_method = nil, options: { inline: false }, html_options: {}, hint: {}, legend: {})
63
- hint_element = Elements::Hint.new(self, object_name, attribute_name, hint)
177
+ def govuk_collection_radio_buttons(attribute_name, collection, value_method, text_method, hint_method = nil, options: { inline: false }, html_options: {}, hint_text: nil, legend: {})
178
+ hint_element = Elements::Hint.new(self, object_name, attribute_name, hint_text)
179
+ error_element = Elements::ErrorMessage.new(self, object_name, attribute_name)
180
+
64
181
  Containers::FormGroup.new(self, object_name, attribute_name).html do
65
- safe_join(
66
- [
67
- hint_element.html,
68
- Containers::Fieldset.new(self, object_name, attribute_name, legend: legend, described_by: hint_element.hint_id).html do
69
- safe_join(
70
- [
71
- (yield if block_given?),
72
- Containers::Radios.new(self, inline: options.dig(:inline)).html do
73
- safe_join(
74
- collection.map do |item|
75
- Elements::Radios::CollectionRadio.new(self, object_name, attribute_name, item, value_method, text_method, hint_method).html
76
- end
77
- )
182
+ Containers::Fieldset.new(self, object_name, attribute_name, legend: legend, described_by: [error_element.error_id, hint_element.hint_id]).html do
183
+ safe_join(
184
+ [
185
+ hint_element.html,
186
+ error_element.html,
187
+
188
+ (yield if block_given?),
189
+ Containers::Radios.new(self, inline: options.dig(:inline)).html do
190
+ safe_join(
191
+ collection.map do |item|
192
+ Elements::Radios::CollectionRadio.new(self, object_name, attribute_name, item, value_method, text_method, hint_method).html
78
193
  end
79
- ].compact
80
- )
81
- end
82
- ]
83
- )
194
+ )
195
+ end
196
+ ].compact
197
+ )
198
+ end
84
199
  end
85
200
  end
86
201
 
87
- def govuk_radio_buttons_fieldset(attribute_name, options: { inline: false }, hint: {}, legend: {})
88
- hint_element = Elements::Hint.new(self, object_name, attribute_name, hint)
202
+ def govuk_radio_buttons_fieldset(attribute_name, options: { inline: false }, hint_text: nil, legend: {})
203
+ hint_element = Elements::Hint.new(self, object_name, attribute_name, hint_text)
204
+ error_element = Elements::ErrorMessage.new(self, object_name, attribute_name)
89
205
 
90
206
  Containers::FormGroup.new(self, object_name, attribute_name).html do
91
- safe_join([
92
- hint_element.html,
93
- Containers::Fieldset.new(self, object_name, attribute_name, legend: legend, described_by: hint_element.hint_id).html do
207
+ Containers::Fieldset.new(self, object_name, attribute_name, legend: legend, described_by: [error_element.error_id, hint_element.hint_id]).html do
208
+ safe_join([
209
+ hint_element.html,
210
+ error_element.html,
94
211
  Containers::Radios.new(self, inline: options.dig(:inline)).html do
95
212
  yield
96
213
  end
97
- end
98
- ])
214
+ ])
215
+ end
99
216
  end
100
217
  end
101
218
 
102
219
  # only intended for use inside a #govuk_radio_buttons_fieldset
103
- def govuk_radio_button(attribute_name, value, hint: {}, label: {})
104
- Elements::Radios::FieldsetRadio.new(self, object_name, attribute_name, value, hint: hint, label: label).html do
105
- (yield if block_given?)
106
- end
220
+ def govuk_radio_button(attribute_name, value, hint_text: nil, label: {}, &block)
221
+ Elements::Radios::FieldsetRadio.new(self, object_name, attribute_name, value, hint_text: hint_text, label: label, &block).html
107
222
  end
108
223
 
109
224
  def govuk_radio_divider(text = 'or')
110
225
  tag.div(text, class: %w(govuk-radios__divider))
111
226
  end
112
227
 
113
- def govuk_collection_check_boxes(attribute_name, collection, value_method, text_method, hint_method = nil, html_options: {}, hint: {}, legend: {})
114
- hint_element = Elements::Hint.new(self, object_name, attribute_name, hint)
228
+ def govuk_collection_check_boxes(attribute_name, collection, value_method, text_method, hint_method = nil, html_options: {}, hint_text: nil, legend: {})
229
+ hint_element = Elements::Hint.new(self, object_name, attribute_name, hint_text)
230
+ error_element = Elements::ErrorMessage.new(self, object_name, attribute_name)
231
+
115
232
  Containers::FormGroup.new(self, object_name, attribute_name).html do
116
- safe_join(
117
- [
118
- hint_element.html,
119
- (yield if block_given?),
120
- Containers::Fieldset.new(self, object_name, attribute_name, legend: legend, described_by: hint_element.hint_id).html do
233
+ Containers::Fieldset.new(self, object_name, attribute_name, legend: legend, described_by: [error_element.error_id, hint_element.hint_id]).html do
234
+ safe_join(
235
+ [
236
+ hint_element.html,
237
+ error_element.html,
238
+ (yield if block_given?),
121
239
  collection_check_boxes(attribute_name, collection, value_method, text_method) do |check_box|
122
240
  Elements::CheckBoxes::CollectionCheckBox.new(self, attribute_name, check_box, hint_method).html
123
241
  end
124
- end
125
- ]
126
- )
127
- end
128
- end
129
-
130
- def govuk_check_boxes_fieldset(attribute_name, html_options: {}, hint: {}, legend: {})
131
- hint_element = Elements::Hint.new(self, object_name, attribute_name, hint)
132
-
133
- Containers::FormGroup.new(self, object_name, attribute_name).html do
134
- safe_join([
135
- hint_element.html,
136
- Containers::Fieldset.new(self, object_name, attribute_name, legend: legend, described_by: hint_element.hint_id).html do
137
- Containers::CheckBoxes.new(self).html do
138
- yield
139
- end
140
- end
141
- ])
142
- end
143
- end
144
-
145
- def govuk_check_box(attribute_name, value, hint: {}, label: {})
146
- Elements::CheckBox.new(self, object_name, attribute_name, value, hint: hint, label: label).html do
147
- (yield if block_given?)
242
+ ]
243
+ )
244
+ end
148
245
  end
149
246
  end
150
247
 
151
- def govuk_submit(text = 'Continue', warning: false, secondary: false, prevent_double_click: true)
152
- Elements::Submit.new(self, text, warning: warning, secondary: secondary, prevent_double_click: prevent_double_click).html do
153
- (yield if block_given?)
154
- end
248
+ def govuk_submit(text = 'Continue', warning: false, secondary: false, prevent_double_click: true, &block)
249
+ Elements::Submit.new(self, text, warning: warning, secondary: secondary, prevent_double_click: prevent_double_click, &block).html
155
250
  end
156
251
 
157
- def govuk_date_field(attribute_name, hint: {}, legend: {})
158
- Elements::Date.new(self, object_name, attribute_name, hint: hint, legend: legend).html do
159
- (yield if block_given?)
160
- end
252
+ def govuk_date_field(attribute_name, hint_text: nil, legend: {}, date_of_birth: false, &block)
253
+ Elements::Date.new(self, object_name, attribute_name, hint_text: hint_text, legend: legend, date_of_birth: date_of_birth, &block).html
161
254
  end
162
255
  end
163
256
  end
@@ -2,12 +2,14 @@ module GOVUKDesignSystemFormBuilder
2
2
  module Containers
3
3
  class Fieldset < GOVUKDesignSystemFormBuilder::Base
4
4
  LEGEND_DEFAULTS = { text: nil, tag: 'h1', size: 'xl' }.freeze
5
+
6
+ # FIXME standardise sizes with labels
5
7
  LEGEND_SIZES = %w(xl l m s)
6
8
 
7
9
  def initialize(builder, object_name, attribute_name, legend: {}, described_by: nil)
8
10
  super(builder, object_name, attribute_name)
9
11
  @legend = LEGEND_DEFAULTS.merge(legend)
10
- @described_by = described_by
12
+ @described_by = descriptors(described_by)
11
13
  end
12
14
 
13
15
  def html
@@ -42,6 +44,12 @@ module GOVUKDesignSystemFormBuilder
42
44
  def legend_heading_classes
43
45
  %(govuk-fieldset__heading)
44
46
  end
47
+
48
+ def descriptors(described_by)
49
+ return nil unless described_by.present?
50
+
51
+ Array.wrap(described_by).join(' ')
52
+ end
45
53
  end
46
54
  end
47
55
  end
@@ -3,19 +3,19 @@ module GOVUKDesignSystemFormBuilder
3
3
  module CheckBoxes
4
4
  class CollectionCheckBox < GOVUKDesignSystemFormBuilder::Base
5
5
  def initialize(builder, attribute, checkbox, hint_method = nil)
6
- @builder = builder
6
+ @builder = builder
7
7
  @attribute = attribute
8
- @checkbox = checkbox
9
- @item = checkbox.object
10
- @hint = @item.send(hint_method) if hint_method.present?
8
+ @checkbox = checkbox
9
+ @item = checkbox.object
10
+ @hint_text = @item.send(hint_method) if hint_method.present?
11
11
  end
12
12
 
13
13
  def html
14
- hint = Hint.new(@builder, @attribute, @checkbox, @hint)
14
+ hint = Hint.new(@builder, @attribute, @checkbox, @hint_text)
15
15
  @builder.content_tag('div', class: 'govuk-checkboxes__item') do
16
16
  @builder.safe_join(
17
17
  [
18
- @checkbox.check_box(class: "govuk-checkboxes_input", aria: { describedby: hint.id }),
18
+ @checkbox.check_box(class: "govuk-checkboxes__input", aria: { describedby: hint.id }),
19
19
  Label.new(@checkbox).html,
20
20
  hint.html
21
21
  ]
@@ -3,21 +3,23 @@ module GOVUKDesignSystemFormBuilder
3
3
  class Date < GOVUKDesignSystemFormBuilder::Base
4
4
  SEGMENTS = { day: '3i', month: '2i', year: '1i' }
5
5
 
6
- def initialize(builder, object_name, attribute_name, legend:, hint:)
6
+ def initialize(builder, object_name, attribute_name, legend:, hint_text:, date_of_birth: false, &block)
7
7
  super(builder, object_name, attribute_name)
8
8
  @legend = legend
9
- @hint = hint
9
+ @hint_text = hint_text
10
+ @date_of_birth = date_of_birth
11
+ @block_content = @builder.capture { block.call }.html_safe if block_given?
10
12
  end
11
13
 
12
14
  def html
13
- hint_element = Elements::Hint.new(@builder, @object_name, @attribute_name, @hint)
15
+ hint_element = Elements::Hint.new(@builder, @object_name, @attribute_name, @hint_text)
14
16
 
15
17
  Containers::FormGroup.new(@builder, @object_name, @attribute_name).html do
16
18
  Containers::Fieldset.new(@builder, @object_name, @attribute_name, legend: @legend, described_by: hint_element.hint_id).html do
17
19
  @builder.safe_join(
18
20
  [
19
21
  hint_element.html,
20
- (yield if block_given?),
22
+ @block_content,
21
23
  @builder.content_tag('div', class: 'govuk-date-input') do
22
24
  @builder.safe_join(
23
25
  [
@@ -57,7 +59,8 @@ module GOVUKDesignSystemFormBuilder
57
59
  min: min,
58
60
  max: max,
59
61
  step: 1,
60
- value: value
62
+ value: value,
63
+ autocomplete: date_of_birth_autocomplete_value(segment)
61
64
  )
62
65
  ]
63
66
  )
@@ -84,6 +87,12 @@ module GOVUKDesignSystemFormBuilder
84
87
  segment: SEGMENTS.fetch(segment)
85
88
  }
86
89
  end
90
+
91
+ def date_of_birth_autocomplete_value(segment)
92
+ return nil unless @date_of_birth
93
+
94
+ { day: 'bday-day', month: 'bday-month', year: 'bday-year' }.fetch(segment)
95
+ end
87
96
  end
88
97
  end
89
98
  end
@@ -1,23 +1,32 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  class Hint < GOVUKDesignSystemFormBuilder::Base
4
- def initialize(builder, object_name, attribute_name, options = {})
4
+ def initialize(builder, object_name, attribute_name, text, value = nil, radio: false, checkbox: false)
5
5
  super(builder, object_name, attribute_name)
6
- @text = options&.dig(:text)
7
- @extra_classes = options&.dig(:class)
8
- @hint_id = options&.dig(:id)
6
+ @value = value
7
+ @hint_text = text
8
+ @radio_class = radio_class(radio)
9
+ @checkbox_class = checkbox_class(checkbox)
9
10
  end
10
11
 
11
12
  def html
12
- return nil unless @text.present?
13
+ return nil unless @hint_text.present?
13
14
 
14
- @builder.tag.span(@text, class: hint_classes, id: hint_id)
15
+ @builder.tag.span(@hint_text, class: hint_classes, id: hint_id)
15
16
  end
16
17
 
17
18
  private
18
19
 
19
20
  def hint_classes
20
- %w(govuk-hint).push(@extra_classes).compact
21
+ %w(govuk-hint).push(@radio_class, @checkbox_class).compact
22
+ end
23
+
24
+ def radio_class(radio)
25
+ radio ? 'govuk-radios__hint' : nil
26
+ end
27
+
28
+ def checkbox_class(checkbox)
29
+ checkbox ? 'govuk-checkboxes__hint' : nil
21
30
  end
22
31
  end
23
32
  end
@@ -1,18 +1,18 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  class Input < GOVUKDesignSystemFormBuilder::Base
4
- def initialize(builder, object_name, attribute_name, attribute_type:, hint:, label:, **extra_args)
4
+ def initialize(builder, object_name, attribute_name, attribute_type:, hint_text:, label:, **extra_args)
5
5
  super(builder, object_name, attribute_name)
6
6
 
7
- @extra_args = extra_args.dup
8
- @width = @extra_args.delete(:width)
7
+ @extra_args = extra_args.dup
8
+ @width = @extra_args.delete(:width)
9
9
  @builder_method = [attribute_type, 'field'].join('_')
10
- @label = label
11
- @hint = hint
10
+ @label = label
11
+ @hint_text = hint_text
12
12
  end
13
13
 
14
14
  def html
15
- hint_element = Elements::Hint.new(@builder, @object_name, @attribute_name, @hint)
15
+ hint_element = Elements::Hint.new(@builder, @object_name, @attribute_name, @hint_text)
16
16
  label_element = Elements::Label.new(@builder, @object_name, @attribute_name, @label)
17
17
  error_element = Elements::ErrorMessage.new(@builder, @object_name, @attribute_name)
18
18
 
@@ -26,7 +26,6 @@ module GOVUKDesignSystemFormBuilder
26
26
  @builder_method,
27
27
  @attribute_name,
28
28
  class: input_classes,
29
- name: attribute_descriptor,
30
29
  aria: {
31
30
  describedby: [
32
31
  hint_element.hint_id,
@@ -1,13 +1,15 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  class Label < GOVUKDesignSystemFormBuilder::Base
4
- def initialize(builder, object_name, attribute_name, text: nil, value: nil, size: 'regular', weight: 'regular')
4
+ def initialize(builder, object_name, attribute_name, text: nil, value: nil, size: 'regular', weight: 'regular', radio: false, checkbox: false)
5
5
  super(builder, object_name, attribute_name)
6
6
 
7
- @text = label_text(text)
8
- @value = value # used by attribute_descriptor
9
- @size_class = label_size_class(size)
10
- @weight_class = label_weight_class(weight)
7
+ @text = label_text(text)
8
+ @value = value # used by attribute_descriptor
9
+ @size_class = label_size_class(size)
10
+ @weight_class = label_weight_class(weight)
11
+ @radio_class = radio_class(radio)
12
+ @checkbox_class = checkbox_class(checkbox)
11
13
  end
12
14
 
13
15
  def html
@@ -17,7 +19,7 @@ module GOVUKDesignSystemFormBuilder
17
19
  @attribute_name,
18
20
  @text,
19
21
  value: @value,
20
- class: %w(govuk-label).push(@size_class, @weight_class).compact
22
+ class: %w(govuk-label).push(@size_class, @weight_class, @radio_class, @checkbox_class).compact
21
23
  )
22
24
  end
23
25
 
@@ -27,6 +29,14 @@ module GOVUKDesignSystemFormBuilder
27
29
  [option_text, @value, @attribute_name.capitalize].compact.first
28
30
  end
29
31
 
32
+ def radio_class(radio)
33
+ radio ? 'govuk-radios__label' : nil
34
+ end
35
+
36
+ def checkbox_class(checkbox)
37
+ checkbox ? 'govuk-checkboxes__label' : nil
38
+ end
39
+
30
40
  def label_size_class(size)
31
41
  case size
32
42
  when 'large' then "govuk-!-font-size-48"
@@ -1,13 +1,13 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  module Radios
4
- class CollectionRadio < Radio
4
+ class CollectionRadio < Base
5
5
  def initialize(builder, object_name, attribute_name, item, value_method, text_method, hint_method)
6
6
  super(builder, object_name, attribute_name)
7
7
  @item = item
8
8
  @value = item.send(value_method)
9
9
  @text = item.send(text_method)
10
- @hint = item.send(hint_method) if hint_method.present?
10
+ @hint_text = item.send(hint_method) if hint_method.present?
11
11
  end
12
12
 
13
13
  def html
@@ -18,10 +18,11 @@ module GOVUKDesignSystemFormBuilder
18
18
  @attribute_name,
19
19
  @value,
20
20
  id: attribute_descriptor,
21
- aria: { describedby: hint_id }
21
+ aria: { describedby: hint_id },
22
+ class: %w(govuk-radios__input)
22
23
  ),
23
24
  Elements::Label.new(@builder, @object_name, @attribute_name, text: @text, value: @value).html,
24
- Elements::Hint.new(@builder, @object_name, @attribute_name, id: hint_id, class: radio_hint_classes, text: @hint).html,
25
+ Elements::Hint.new(@builder, @object_name, @attribute_name, @hint_text, @value, radio: true).html,
25
26
  ].compact
26
27
  )
27
28
  end
@@ -1,24 +1,23 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  module Radios
4
- class FieldsetRadio < Radio
5
- def initialize(builder, object_name, attribute_name, value, label:, hint:)
4
+ class FieldsetRadio < Base
5
+ def initialize(builder, object_name, attribute_name, value, label:, hint_text:, &block)
6
6
  super(builder, object_name, attribute_name)
7
7
  @value = value
8
8
  @label = label
9
- @hint = hint
9
+ @hint_text = hint_text
10
+ @conditional_content, @conditional_id = wrap_conditional(block) if block_given?
10
11
  end
11
12
 
12
- def html(&block)
13
- @conditional_content, @conditional_id = process(block)
14
-
13
+ def html
15
14
  @builder.content_tag('div', class: 'govuk-radios__item') do
16
15
  @builder.safe_join(
17
16
  [
18
17
  input,
19
- Elements::Label.new(@builder, @object_name, @attribute_name, @label).html,
20
- Elements::Hint.new(@builder, @object_name, @attribute_name, id: hint_id, class: radio_hint_classes, text: @hint).html,
21
- conditional_content
18
+ Elements::Label.new(@builder, @object_name, @attribute_name, radio: true, value: @value, **@label).html,
19
+ Elements::Hint.new(@builder, @object_name, @attribute_name, @hint_text, radio: true).html,
20
+ @conditional_content
22
21
  ]
23
22
  )
24
23
  end
@@ -32,22 +31,11 @@ module GOVUKDesignSystemFormBuilder
32
31
  @value,
33
32
  id: attribute_descriptor,
34
33
  aria: { describedby: hint_id },
35
- data: { 'aria-controls' => @conditional_id }
34
+ data: { 'aria-controls' => @conditional_id },
35
+ class: %w(govuk-radios__input)
36
36
  )
37
37
  end
38
38
 
39
- def conditional_content
40
- return nil unless @conditional_content.present?
41
-
42
- @builder.content_tag('div', class: conditional_classes, id: @conditional_id) do
43
- @conditional_content
44
- end
45
- end
46
-
47
- def process(block)
48
- return content = block.call, (content && conditional_id)
49
- end
50
-
51
39
  def conditional_classes
52
40
  %w(govuk-radios__conditional govuk-radios__conditional--hidden)
53
41
  end
@@ -1,7 +1,7 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  class Submit < GOVUKDesignSystemFormBuilder::Base
4
- def initialize(builder, text, warning:, secondary:, prevent_double_click:)
4
+ def initialize(builder, text, warning:, secondary:, prevent_double_click:, &block)
5
5
  @builder = builder
6
6
  @text = text
7
7
  @prevent_double_click = prevent_double_click
@@ -9,15 +9,14 @@ module GOVUKDesignSystemFormBuilder
9
9
  fail ArgumentError, 'buttons can be warning or secondary' if (warning && secondary)
10
10
  @warning = warning
11
11
  @secondary = secondary
12
+ @block_content = @builder.capture { block.call } if block_given?
12
13
  end
13
14
 
14
- def html(&block)
15
- content = process(block)
16
-
15
+ def html
17
16
  @builder.content_tag('div', class: %w(govuk-form-group)) do
18
17
  @builder.safe_join([
19
- @builder.submit(@text, class: submit_button_classes(content.present?), **extra_args),
20
- content
18
+ @builder.submit(@text, class: submit_button_classes(@block_content.present?), **extra_args),
19
+ @block_content
21
20
  ])
22
21
  end
23
22
  end
@@ -37,10 +36,6 @@ module GOVUKDesignSystemFormBuilder
37
36
  def extra_args
38
37
  { data: { 'prevent-double-click' => (@prevent_double_click || nil) }.compact }
39
38
  end
40
-
41
- def process(block)
42
- block.call
43
- end
44
39
  end
45
40
  end
46
41
  end
@@ -1,10 +1,10 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  class TextArea < Base
4
- def initialize(builder, object_name, attribute_name, hint:, label:, rows:, max_words:, max_chars:, **extra_args)
4
+ def initialize(builder, object_name, attribute_name, hint_text:, label:, rows:, max_words:, max_chars:, **extra_args)
5
5
  super(builder, object_name, attribute_name)
6
6
  @label = label
7
- @hint = hint
7
+ @hint_text = hint_text
8
8
  @extra_args = extra_args
9
9
  @max_words = max_words
10
10
  @max_chars = max_chars
@@ -12,7 +12,7 @@ module GOVUKDesignSystemFormBuilder
12
12
  end
13
13
 
14
14
  def html
15
- hint_element = Elements::Hint.new(@builder, @object_name, @attribute_name, @hint)
15
+ hint_element = Elements::Hint.new(@builder, @object_name, @attribute_name, @hint_text)
16
16
  label_element = Elements::Label.new(@builder, @object_name, @attribute_name, @label)
17
17
  error_element = Elements::ErrorMessage.new(@builder, @object_name, @attribute_name)
18
18
 
@@ -1,3 +1,3 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
- VERSION = '0.7.0'.freeze
2
+ VERSION = '0.7.1'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: govuk_design_system_formbuilder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Yates
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-25 00:00:00.000000000 Z
11
+ date: 2019-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -50,14 +50,14 @@ dependencies:
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '0'
53
+ version: '3.8'
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '0'
60
+ version: '3.8'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: rspec-html-matchers
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -92,28 +92,28 @@ dependencies:
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '0'
95
+ version: 0.12.2
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '0'
102
+ version: 0.12.2
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: pry-byebug
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: '0'
109
+ version: 3.7.0
110
110
  type: :development
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: '0'
116
+ version: 3.7.0
117
117
  description: A Rails form builder that generates form inputs adhering to the GOV.UK
118
118
  Design System
119
119
  email:
@@ -132,7 +132,6 @@ files:
132
132
  - lib/govuk_design_system_formbuilder/containers/fieldset.rb
133
133
  - lib/govuk_design_system_formbuilder/containers/form_group.rb
134
134
  - lib/govuk_design_system_formbuilder/containers/radios.rb
135
- - lib/govuk_design_system_formbuilder/elements/check_box.rb
136
135
  - lib/govuk_design_system_formbuilder/elements/check_boxes/collection_check_box.rb
137
136
  - lib/govuk_design_system_formbuilder/elements/check_boxes/hint.rb
138
137
  - lib/govuk_design_system_formbuilder/elements/check_boxes/label.rb
@@ -141,7 +140,6 @@ files:
141
140
  - lib/govuk_design_system_formbuilder/elements/hint.rb
142
141
  - lib/govuk_design_system_formbuilder/elements/input.rb
143
142
  - lib/govuk_design_system_formbuilder/elements/label.rb
144
- - lib/govuk_design_system_formbuilder/elements/radio.rb
145
143
  - lib/govuk_design_system_formbuilder/elements/radios/collection_radio.rb
146
144
  - lib/govuk_design_system_formbuilder/elements/radios/fieldset_radio.rb
147
145
  - lib/govuk_design_system_formbuilder/elements/submit.rb
@@ -1,84 +0,0 @@
1
- module GOVUKDesignSystemFormBuilder
2
- module Elements
3
- class CheckBox < GOVUKDesignSystemFormBuilder::Base
4
- def initialize(builder, object_name, attribute_name, value, label:, hint:)
5
- super(builder, object_name, attribute_name)
6
- @value = value
7
- @label = label
8
- @hint = hint
9
- end
10
-
11
- def html(&block)
12
- @conditional_content, @conditional_id = process(block)
13
-
14
- @builder.content_tag('div', class: 'govuk-checkboxes__item') do
15
- @builder.safe_join(
16
- [
17
- input,
18
-
19
- Elements::Label.new(
20
- @builder,
21
- @object_name,
22
- @attribute_name,
23
- { value: @value.parameterize, text: @value }.merge(@label)
24
- ).html,
25
-
26
- Elements::Hint.new(
27
- @builder,
28
- @object_name,
29
- @attribute_name,
30
- id: hint_id,
31
- class: check_box_hint_classes,
32
- **@hint
33
- ).html,
34
-
35
- conditional_content
36
- ]
37
- )
38
- end
39
- end
40
-
41
- private
42
-
43
- def input
44
- @builder.check_box(
45
- @attribute_name,
46
- id: attribute_descriptor,
47
- class: check_box_classes,
48
- aria: { describedby: hint_id },
49
- data: { 'aria-controls' => @conditional_id }
50
- )
51
- end
52
-
53
- def conditional_content
54
- return nil unless @conditional_content.present?
55
-
56
- @builder.content_tag('div', class: conditional_classes, id: @conditional_id) do
57
- @conditional_content
58
- end
59
- end
60
-
61
- def process(block)
62
- return content = block.call, (content && conditional_id)
63
- end
64
-
65
- def conditional_classes
66
- %w(govuk-checkboxes__conditional govuk-checkboxes__conditional--hidden)
67
- end
68
-
69
- def hint_id
70
- return nil unless @hint['text'].present?
71
-
72
- [@object_name, @attribute_name, @value, 'hint'].join('-').parameterize
73
- end
74
-
75
- def check_box_classes
76
- %w(govuk-checkbox)
77
- end
78
-
79
- def check_box_hint_classes
80
- %w(govuk-hint govuk-checkboxes__hint)
81
- end
82
- end
83
- end
84
- end
@@ -1,15 +0,0 @@
1
- module GOVUKDesignSystemFormBuilder
2
- module Elements
3
- class Radio < GOVUKDesignSystemFormBuilder::Base
4
- def hint_id
5
- return nil unless @hint.present?
6
-
7
- [@object_name, @attribute_name, @value, 'hint'].join('-').parameterize
8
- end
9
-
10
- def radio_hint_classes
11
- %w(govuk-hint govuk-radios__hint)
12
- end
13
- end
14
- end
15
- end