govuk_design_system_formbuilder 0.9.8 → 2.0.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +40 -35
  3. data/lib/govuk_design_system_formbuilder/base.rb +21 -46
  4. data/lib/govuk_design_system_formbuilder/builder.rb +528 -111
  5. data/lib/govuk_design_system_formbuilder/containers/character_count.rb +8 -7
  6. data/lib/govuk_design_system_formbuilder/containers/check_boxes.rb +20 -8
  7. data/lib/govuk_design_system_formbuilder/containers/check_boxes_fieldset.rb +39 -14
  8. data/lib/govuk_design_system_formbuilder/containers/fieldset.rb +21 -25
  9. data/lib/govuk_design_system_formbuilder/containers/form_group.rb +20 -9
  10. data/lib/govuk_design_system_formbuilder/containers/radio_buttons_fieldset.rb +28 -14
  11. data/lib/govuk_design_system_formbuilder/containers/radios.rb +27 -10
  12. data/lib/govuk_design_system_formbuilder/containers/supplemental.rb +8 -9
  13. data/lib/govuk_design_system_formbuilder/elements/caption.rb +41 -0
  14. data/lib/govuk_design_system_formbuilder/elements/check_boxes/collection.rb +34 -22
  15. data/lib/govuk_design_system_formbuilder/elements/check_boxes/collection_check_box.rb +28 -18
  16. data/lib/govuk_design_system_formbuilder/elements/check_boxes/fieldset_check_box.rb +47 -40
  17. data/lib/govuk_design_system_formbuilder/elements/check_boxes/label.rb +9 -3
  18. data/lib/govuk_design_system_formbuilder/elements/date.rb +65 -53
  19. data/lib/govuk_design_system_formbuilder/elements/error_message.rb +13 -8
  20. data/lib/govuk_design_system_formbuilder/elements/error_summary.rb +30 -31
  21. data/lib/govuk_design_system_formbuilder/elements/file.rb +31 -24
  22. data/lib/govuk_design_system_formbuilder/elements/hint.rb +50 -14
  23. data/lib/govuk_design_system_formbuilder/elements/inputs/email.rb +8 -0
  24. data/lib/govuk_design_system_formbuilder/elements/inputs/number.rb +6 -0
  25. data/lib/govuk_design_system_formbuilder/elements/inputs/password.rb +17 -0
  26. data/lib/govuk_design_system_formbuilder/elements/inputs/phone.rb +6 -0
  27. data/lib/govuk_design_system_formbuilder/elements/inputs/text.rb +6 -0
  28. data/lib/govuk_design_system_formbuilder/elements/inputs/url.rb +6 -0
  29. data/lib/govuk_design_system_formbuilder/elements/label.rb +63 -34
  30. data/lib/govuk_design_system_formbuilder/elements/legend.rb +87 -0
  31. data/lib/govuk_design_system_formbuilder/elements/radios/collection.rb +51 -35
  32. data/lib/govuk_design_system_formbuilder/elements/radios/collection_radio_button.rb +41 -19
  33. data/lib/govuk_design_system_formbuilder/elements/radios/fieldset_radio_button.rb +37 -24
  34. data/lib/govuk_design_system_formbuilder/elements/select.rb +34 -32
  35. data/lib/govuk_design_system_formbuilder/elements/submit.rb +39 -29
  36. data/lib/govuk_design_system_formbuilder/elements/text_area.rb +76 -32
  37. data/lib/govuk_design_system_formbuilder/traits/caption.rb +15 -0
  38. data/lib/govuk_design_system_formbuilder/traits/collection_item.rb +16 -0
  39. data/lib/govuk_design_system_formbuilder/traits/conditional.rb +17 -0
  40. data/lib/govuk_design_system_formbuilder/traits/error.rb +17 -0
  41. data/lib/govuk_design_system_formbuilder/traits/hint.rb +30 -0
  42. data/lib/govuk_design_system_formbuilder/traits/input.rb +105 -0
  43. data/lib/govuk_design_system_formbuilder/traits/label.rb +22 -0
  44. data/lib/govuk_design_system_formbuilder/traits/localisation.rb +51 -0
  45. data/lib/govuk_design_system_formbuilder/traits/supplemental.rb +17 -0
  46. data/lib/govuk_design_system_formbuilder/version.rb +1 -1
  47. data/lib/govuk_design_system_formbuilder.rb +89 -44
  48. metadata +62 -52
  49. data/lib/govuk_design_system_formbuilder/elements/check_boxes/hint.rb +0 -30
  50. data/lib/govuk_design_system_formbuilder/elements/inputs/base.rb +0 -79
@@ -14,17 +14,18 @@ module GOVUKDesignSystemFormBuilder
14
14
  def html
15
15
  return yield unless limit?
16
16
 
17
- content_tag(
18
- 'div',
19
- class: 'govuk-character-count',
20
- data: { module: 'govuk-character-count' }.merge(**limit, **threshold).compact
21
- ) do
22
- yield
23
- end
17
+ tag.div(**options) { yield }
24
18
  end
25
19
 
26
20
  private
27
21
 
22
+ def options
23
+ {
24
+ class: %(#{brand}-character-count),
25
+ data: { module: %(#{brand}-character-count) }.merge(**limit, **threshold).compact
26
+ }
27
+ end
28
+
28
29
  def limit
29
30
  if @max_words
30
31
  { maxwords: @max_words }
@@ -1,23 +1,35 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Containers
3
3
  class CheckBoxes < Base
4
- def initialize(builder, small:)
4
+ def initialize(builder, small:, classes: nil)
5
5
  @builder = builder
6
6
  @small = small
7
+ @classes = classes
7
8
  end
8
9
 
9
10
  def html
10
- content_tag('div', class: check_boxes_classes, data: { module: 'govuk-checkboxes' }) do
11
- yield
12
- end
11
+ tag.div(**options) { yield }
13
12
  end
14
13
 
15
14
  private
16
15
 
17
- def check_boxes_classes
18
- %w(govuk-checkboxes).tap do |c|
19
- c.push('govuk-checkboxes--small') if @small
20
- end
16
+ def options
17
+ {
18
+ class: classes,
19
+ data: { module: %(#{brand}-checkboxes) }
20
+ }
21
+ end
22
+
23
+ def classes
24
+ [%(#{brand}-checkboxes), small_class, custom_classes].flatten.compact
25
+ end
26
+
27
+ def small_class
28
+ %(#{brand}-checkboxes--small) if @small
29
+ end
30
+
31
+ def custom_classes
32
+ Array.wrap(@classes)
21
33
  end
22
34
  end
23
35
  end
@@ -1,30 +1,55 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Containers
3
- class CheckBoxesFieldset < GOVUKDesignSystemFormBuilder::Base
4
- def initialize(builder, object_name, attribute_name, hint_text:, legend:, small:, &block)
3
+ class CheckBoxesFieldset < Base
4
+ include Traits::Error
5
+ include Traits::Hint
6
+
7
+ def initialize(builder, object_name, attribute_name, hint:, legend:, caption:, small:, classes:, form_group:, multiple:, &block)
5
8
  super(builder, object_name, attribute_name, &block)
6
9
 
7
10
  @legend = legend
8
- @hint_text = hint_text
11
+ @caption = caption
12
+ @hint = hint
9
13
  @small = small
14
+ @classes = classes
15
+ @form_group = form_group
16
+ @multiple = multiple
10
17
  @block_content = capture { block.call }
11
18
  end
12
19
 
13
20
  def html
14
- Containers::FormGroup.new(@builder, @object_name, @attribute_name).html do
15
- Containers::Fieldset.new(@builder, legend: @legend, described_by: [error_element.error_id, hint_element.hint_id]).html do
16
- safe_join(
17
- [
18
- hint_element.html,
19
- error_element.html,
20
- Containers::CheckBoxes.new(@builder, small: @small).html do
21
- @block_content
22
- end
23
- ]
24
- )
21
+ Containers::FormGroup.new(@builder, @object_name, @attribute_name, **@form_group).html do
22
+ Containers::Fieldset.new(@builder, @object_name, @attribute_name, **fieldset_options).html do
23
+ safe_join([hint_element, error_element, hidden_field, checkboxes])
25
24
  end
26
25
  end
27
26
  end
27
+
28
+ private
29
+
30
+ def hidden_field
31
+ return unless @multiple
32
+
33
+ @builder.hidden_field(@attribute_name, value: "", name: hidden_field_name)
34
+ end
35
+
36
+ def hidden_field_name
37
+ format("%<object_name>s[%<attribute_name>s][]", object_name: @object_name, attribute_name: @attribute_name)
38
+ end
39
+
40
+ def fieldset_options
41
+ {
42
+ legend: @legend,
43
+ caption: @caption,
44
+ described_by: [error_element.error_id, hint_element.hint_id]
45
+ }
46
+ end
47
+
48
+ def checkboxes
49
+ Containers::CheckBoxes.new(@builder, small: @small, classes: @classes).html do
50
+ @block_content
51
+ end
52
+ end
28
53
  end
29
54
  end
30
55
  end
@@ -1,44 +1,40 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Containers
3
- class Fieldset < GOVUKDesignSystemFormBuilder::Base
4
- LEGEND_DEFAULTS = { text: nil, tag: 'h1', size: 'm' }.freeze
5
- LEGEND_SIZES = %w(xl l m s).freeze
6
-
7
- def initialize(builder, legend: {}, described_by: nil)
8
- @builder = builder
9
- @legend = LEGEND_DEFAULTS.merge(legend)
10
- @described_by = described_by(described_by)
3
+ class Fieldset < Base
4
+ def initialize(builder, object_name = nil, attribute_name = nil, legend: {}, caption: {}, described_by: nil, &block)
5
+ super(builder, object_name, attribute_name, &block)
6
+
7
+ @legend = legend
8
+ @caption = caption
9
+ @described_by = described_by(described_by)
10
+ @attribute_name = attribute_name
11
11
  end
12
12
 
13
13
  def html
14
- content_tag('fieldset', class: fieldset_classes, aria: { describedby: @described_by }) do
15
- safe_join([build_legend, yield])
14
+ tag.fieldset(**options) do
15
+ safe_join([legend_element, (@block_content || yield)])
16
16
  end
17
17
  end
18
18
 
19
19
  private
20
20
 
21
- def build_legend
22
- if @legend.dig(:text).present?
23
- content_tag('legend', class: legend_classes) do
24
- tag.send(@legend.dig(:tag), @legend.dig(:text), class: legend_heading_classes)
25
- end
26
- end
21
+ def options
22
+ {
23
+ class: classes,
24
+ aria: { describedby: @described_by }
25
+ }
27
26
  end
28
27
 
29
- def fieldset_classes
30
- %w(govuk-fieldset)
28
+ def classes
29
+ %(#{brand}-fieldset)
31
30
  end
32
31
 
33
- def legend_classes
34
- size = @legend.dig(:size)
35
- fail "invalid size '#{size}', must be #{LEGEND_SIZES.join(', ')}" unless size.in?(LEGEND_SIZES)
36
-
37
- ["govuk-fieldset__legend", "govuk-fieldset__legend--#{size}"]
32
+ def legend_element
33
+ @legend_element ||= Elements::Legend.new(@builder, @object_name, @attribute_name, **legend_options)
38
34
  end
39
35
 
40
- def legend_heading_classes
41
- %(govuk-fieldset__heading)
36
+ def legend_options
37
+ { legend: @legend, caption: @caption }
42
38
  end
43
39
  end
44
40
  end
@@ -1,22 +1,33 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Containers
3
- class FormGroup < GOVUKDesignSystemFormBuilder::Base
4
- def initialize(builder, object_name, attribute_name)
3
+ class FormGroup < Base
4
+ def initialize(builder, object_name, attribute_name, classes: nil, **kwargs)
5
5
  super(builder, object_name, attribute_name)
6
+
7
+ @classes = classes
8
+ @html_attributes = kwargs
6
9
  end
7
10
 
8
11
  def html
9
- content_tag('div', class: form_group_classes) do
10
- yield
11
- end
12
+ tag.div(class: classes, **@html_attributes) { yield }
12
13
  end
13
14
 
14
15
  private
15
16
 
16
- def form_group_classes
17
- %w(govuk-form-group).tap do |classes|
18
- classes.push('govuk-form-group--error') if has_errors?
19
- end
17
+ def classes
18
+ [form_group_class, error_class, custom_classes].flatten.compact
19
+ end
20
+
21
+ def form_group_class
22
+ %(#{brand}-form-group)
23
+ end
24
+
25
+ def error_class
26
+ %(#{brand}-form-group--error) if has_errors?
27
+ end
28
+
29
+ def custom_classes
30
+ Array.wrap(@classes)
20
31
  end
21
32
  end
22
33
  end
@@ -1,31 +1,45 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Containers
3
- class RadioButtonsFieldset < GOVUKDesignSystemFormBuilder::Base
4
- def initialize(builder, object_name, attribute_name, hint_text:, legend:, inline:, small:, &block)
3
+ class RadioButtonsFieldset < Base
4
+ include Traits::Hint
5
+ include Traits::Error
6
+
7
+ def initialize(builder, object_name, attribute_name, hint:, legend:, caption:, inline:, small:, classes:, form_group:, &block)
5
8
  super(builder, object_name, attribute_name)
6
9
 
7
10
  @inline = inline
8
11
  @small = small
9
12
  @legend = legend
10
- @hint_text = hint_text
13
+ @caption = caption
14
+ @hint = hint
15
+ @classes = classes
16
+ @form_group = form_group
11
17
  @block_content = capture { block.call }
12
18
  end
13
19
 
14
20
  def html
15
- Containers::FormGroup.new(@builder, @object_name, @attribute_name).html do
16
- Containers::Fieldset.new(@builder, legend: @legend, described_by: [error_element.error_id, hint_element.hint_id]).html do
17
- safe_join(
18
- [
19
- hint_element.html,
20
- error_element.html,
21
- Containers::Radios.new(@builder, inline: @inline, small: @small).html do
22
- @block_content
23
- end
24
- ]
25
- )
21
+ Containers::FormGroup.new(@builder, @object_name, @attribute_name, **@form_group).html do
22
+ Containers::Fieldset.new(@builder, @object_name, @attribute_name, **fieldset_options).html do
23
+ safe_join([hint_element, error_element, radios])
26
24
  end
27
25
  end
28
26
  end
27
+
28
+ private
29
+
30
+ def fieldset_options
31
+ {
32
+ legend: @legend,
33
+ caption: @caption,
34
+ described_by: [error_element.error_id, hint_element.hint_id]
35
+ }
36
+ end
37
+
38
+ def radios
39
+ Containers::Radios.new(@builder, inline: @inline, small: @small, classes: @classes).html do
40
+ @block_content
41
+ end
42
+ end
29
43
  end
30
44
  end
31
45
  end
@@ -1,25 +1,42 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Containers
3
- class Radios < GOVUKDesignSystemFormBuilder::Base
4
- def initialize(builder, inline:, small:)
3
+ class Radios < Base
4
+ include Traits::Hint
5
+
6
+ def initialize(builder, inline:, small:, classes:)
5
7
  @builder = builder
6
8
  @inline = inline
7
9
  @small = small
10
+ @classes = classes
8
11
  end
9
12
 
10
13
  def html
11
- content_tag('div', class: radios_classes, data: { module: 'govuk-radios' }) do
12
- yield
13
- end
14
+ tag.div(**options) { yield }
14
15
  end
15
16
 
16
17
  private
17
18
 
18
- def radios_classes
19
- %w(govuk-radios).tap do |c|
20
- c.push('govuk-radios--inline') if @inline
21
- c.push('govuk-radios--small') if @small
22
- end
19
+ def options
20
+ {
21
+ class: classes,
22
+ data: { module: %(#{brand}-radios) }
23
+ }
24
+ end
25
+
26
+ def classes
27
+ [%(#{brand}-radios), inline_class, small_class, custom_classes].flatten.compact
28
+ end
29
+
30
+ def inline_class
31
+ %(#{brand}-radios--inline) if @inline
32
+ end
33
+
34
+ def small_class
35
+ %(#{brand}-radios--small) if @small
36
+ end
37
+
38
+ def custom_classes
39
+ Array.wrap(@classes)
23
40
  end
24
41
  end
25
42
  end
@@ -1,21 +1,20 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Containers
3
- class Supplemental < GOVUKDesignSystemFormBuilder::Base
3
+ class Supplemental < Base
4
4
  def initialize(builder, object_name, attribute_name, content)
5
- @builder = builder
6
- @object_name = object_name
7
- @attribute_name = attribute_name
8
- @content = content
5
+ super(builder, object_name, attribute_name)
6
+
7
+ @content = content
9
8
  end
10
9
 
11
10
  def html
12
- return nil unless @content.present?
11
+ return nil if @content.blank?
13
12
 
14
- content_tag('div', id: supplemental_id) do
15
- @content
16
- end
13
+ tag.div(id: supplemental_id) { @content }
17
14
  end
18
15
 
16
+ private
17
+
19
18
  def supplemental_id
20
19
  build_id('supplemental')
21
20
  end
@@ -0,0 +1,41 @@
1
+ module GOVUKDesignSystemFormBuilder
2
+ module Elements
3
+ class Caption < Base
4
+ include Traits::Localisation
5
+
6
+ def initialize(builder, object_name, attribute_name, text: nil, size: nil, **kwargs)
7
+ super(builder, object_name, attribute_name)
8
+
9
+ @text = text(text)
10
+ @size_class = size_class(size)
11
+ @html_attributes = kwargs
12
+ end
13
+
14
+ def html
15
+ return nil unless active?
16
+
17
+ tag.span(@text, class: @size_class, **@html_attributes)
18
+ end
19
+
20
+ private
21
+
22
+ def active?
23
+ @text.present?
24
+ end
25
+
26
+ def text(override)
27
+ override || localised_text(:caption)
28
+ end
29
+
30
+ def size_class(size)
31
+ case size || config.default_caption_size
32
+ when 'xl' then %(#{brand}-caption-xl)
33
+ when 'l' then %(#{brand}-caption-l)
34
+ when 'm' then %(#{brand}-caption-m)
35
+ else
36
+ fail ArgumentError, "invalid size '#{size}', must be xl, l or m"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,38 +1,50 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  module CheckBoxes
4
- class Collection < GOVUKDesignSystemFormBuilder::Base
5
- def initialize(builder, object_name, attribute_name, collection, value_method:, text_method:, hint_method: nil, hint_text:, legend:, small:, &block)
4
+ class Collection < Base
5
+ include Traits::Error
6
+ include Traits::Hint
7
+ include Traits::Supplemental
8
+
9
+ def initialize(builder, object_name, attribute_name, collection, value_method:, text_method:, hint_method: nil, hint:, legend:, caption:, small:, classes:, form_group:, &block)
6
10
  super(builder, object_name, attribute_name, &block)
7
11
 
8
- @collection = collection
9
- @value_method = value_method
10
- @text_method = text_method
11
- @hint_method = hint_method
12
- @small = small
13
- @legend = legend
14
- @hint_text = hint_text
12
+ @collection = collection
13
+ @value_method = value_method
14
+ @text_method = text_method
15
+ @hint_method = hint_method
16
+ @small = small
17
+ @legend = legend
18
+ @caption = caption
19
+ @hint = hint
20
+ @classes = classes
21
+ @form_group = form_group
15
22
  end
16
23
 
17
24
  def html
18
- Containers::FormGroup.new(@builder, @object_name, @attribute_name).html do
19
- Containers::Fieldset.new(@builder, legend: @legend, described_by: [error_id, hint_id, supplemental_id]).html do
20
- safe_join(
21
- [
22
- hint_element.html,
23
- error_element.html,
24
- supplemental_content.html,
25
- Containers::CheckBoxes.new(@builder, small: @small).html do
26
- build_collection
27
- end
28
- ]
29
- )
25
+ Containers::FormGroup.new(@builder, @object_name, @attribute_name, **@form_group).html do
26
+ Containers::Fieldset.new(@builder, @object_name, @attribute_name, **fieldset_options).html do
27
+ safe_join([supplemental_content, hint_element, error_element, check_boxes])
30
28
  end
31
29
  end
32
30
  end
33
31
 
34
32
  private
35
33
 
34
+ def fieldset_options
35
+ {
36
+ legend: @legend,
37
+ caption: @caption,
38
+ described_by: [error_id, hint_id, supplemental_id]
39
+ }
40
+ end
41
+
42
+ def check_boxes
43
+ Containers::CheckBoxes.new(@builder, small: @small, classes: @classes).html do
44
+ collection
45
+ end
46
+ end
47
+
36
48
  # Builds a collection of check {Elements::CheckBoxes::CheckBox}
37
49
  # @return [ActiveSupport::SafeBuffer] HTML output
38
50
  #
@@ -41,7 +53,7 @@ module GOVUKDesignSystemFormBuilder
41
53
  # be rendered when it happens we need to work on the chance that it might, so
42
54
  # the +link_errors+ variable is set to +true+ if this attribute has errors and
43
55
  # always set back to +false+ after the first checkbox has been rendered
44
- def build_collection
56
+ def collection
45
57
  link_errors = has_errors?
46
58
 
47
59
  @builder.collection_check_boxes(@attribute_name, @collection, @value_method, @text_method) do |check_box|
@@ -1,40 +1,50 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  module CheckBoxes
4
- class CollectionCheckBox < GOVUKDesignSystemFormBuilder::Base
4
+ class CollectionCheckBox < Base
5
+ include Traits::CollectionItem
6
+ include Traits::Hint
7
+
5
8
  def initialize(builder, object_name, attribute_name, checkbox, hint_method = nil, link_errors: false)
6
9
  super(builder, object_name, attribute_name)
7
- @checkbox = checkbox
8
- @item = checkbox.object
9
- @value = checkbox.value
10
- @hint_text = @item.send(hint_method) if hint_method.present?
10
+
11
+ @checkbox = checkbox
12
+ @item = checkbox.object
13
+ @value = checkbox.value
14
+ @hint = { text: retrieve(@item, hint_method) }
11
15
  @link_errors = link_errors
12
16
  end
13
17
 
14
18
  def html
15
- content_tag('div', class: 'govuk-checkboxes__item') do
16
- safe_join(
17
- [
18
- @checkbox.check_box(
19
- id: field_id(link_errors: @link_errors),
20
- class: "govuk-checkboxes__input",
21
- aria: { describedby: hint_id }
22
- ),
23
- label_element.html,
24
- hint_element.html
25
- ]
26
- )
19
+ tag.div(class: %(#{brand}-checkboxes__item)) do
20
+ safe_join([check_box, label_element, hint_element])
27
21
  end
28
22
  end
29
23
 
30
24
  private
31
25
 
26
+ def check_box
27
+ @checkbox.check_box(**options)
28
+ end
29
+
30
+ def options
31
+ {
32
+ id: field_id(link_errors: @link_errors),
33
+ class: %(#{brand}-checkboxes__input),
34
+ aria: { describedby: hint_id }
35
+ }
36
+ end
37
+
32
38
  def label_element
33
39
  @label_element ||= Label.new(@builder, @object_name, @attribute_name, @checkbox, value: @value, link_errors: @link_errors)
34
40
  end
35
41
 
36
42
  def hint_element
37
- @hint_element ||= Hint.new(@builder, @object_name, @attribute_name, @hint_text, value: @value)
43
+ @hint_element ||= Elements::Hint.new(@builder, @object_name, @attribute_name, **hint_options, **hint_content)
44
+ end
45
+
46
+ def hint_options
47
+ { value: @value, checkbox: true }
38
48
  end
39
49
  end
40
50
  end