govuk_design_system_formbuilder 0.7.9 → 0.7.10

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +52 -18
  3. data/lib/govuk_design_system_formbuilder.rb +19 -4
  4. data/lib/govuk_design_system_formbuilder/base.rb +54 -17
  5. data/lib/govuk_design_system_formbuilder/builder.rb +147 -93
  6. data/lib/govuk_design_system_formbuilder/containers/check_boxes_fieldset.rb +28 -0
  7. data/lib/govuk_design_system_formbuilder/containers/fieldset.rb +3 -9
  8. data/lib/govuk_design_system_formbuilder/containers/radio_buttons_fieldset.rb +29 -0
  9. data/lib/govuk_design_system_formbuilder/elements/check_boxes/collection.rb +62 -0
  10. data/lib/govuk_design_system_formbuilder/elements/check_boxes/collection_check_box.rb +21 -7
  11. data/lib/govuk_design_system_formbuilder/elements/check_boxes/fieldset_check_box.rb +75 -0
  12. data/lib/govuk_design_system_formbuilder/elements/check_boxes/hint.rb +9 -17
  13. data/lib/govuk_design_system_formbuilder/elements/check_boxes/label.rb +5 -4
  14. data/lib/govuk_design_system_formbuilder/elements/date.rb +18 -14
  15. data/lib/govuk_design_system_formbuilder/elements/error_message.rb +6 -4
  16. data/lib/govuk_design_system_formbuilder/elements/error_summary.rb +23 -8
  17. data/lib/govuk_design_system_formbuilder/elements/file.rb +40 -0
  18. data/lib/govuk_design_system_formbuilder/elements/hint.rb +1 -1
  19. data/lib/govuk_design_system_formbuilder/elements/input.rb +4 -12
  20. data/lib/govuk_design_system_formbuilder/elements/label.rb +16 -6
  21. data/lib/govuk_design_system_formbuilder/elements/radios/collection.rb +55 -0
  22. data/lib/govuk_design_system_formbuilder/elements/radios/collection_radio_button.rb +48 -0
  23. data/lib/govuk_design_system_formbuilder/elements/radios/{fieldset_radio.rb → fieldset_radio_button.rb} +17 -8
  24. data/lib/govuk_design_system_formbuilder/elements/select.rb +51 -0
  25. data/lib/govuk_design_system_formbuilder/elements/submit.rb +21 -13
  26. data/lib/govuk_design_system_formbuilder/elements/text_area.rb +4 -8
  27. data/lib/govuk_design_system_formbuilder/version.rb +1 -1
  28. metadata +32 -12
  29. data/lib/govuk_design_system_formbuilder/elements/radios/collection_radio.rb +0 -33
@@ -0,0 +1,28 @@
1
+ module GOVUKDesignSystemFormBuilder
2
+ module Containers
3
+ class CheckBoxesFieldset < GOVUKDesignSystemFormBuilder::Base
4
+ def initialize(builder, object_name, attribute_name, hint_text:, legend:, small:, &block)
5
+ super(builder, object_name, attribute_name)
6
+
7
+ @legend = legend
8
+ @hint_text = hint_text
9
+ @small = small
10
+ @block_content = @builder.capture { block.call }
11
+ end
12
+
13
+ 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
+ @builder.safe_join([
17
+ hint_element.html,
18
+ error_element.html,
19
+ Containers::CheckBoxes.new(@builder, small: @small).html do
20
+ @block_content
21
+ end
22
+ ])
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -7,7 +7,7 @@ module GOVUKDesignSystemFormBuilder
7
7
  def initialize(builder, legend: {}, described_by: nil)
8
8
  @builder = builder
9
9
  @legend = LEGEND_DEFAULTS.merge(legend)
10
- @described_by = descriptors(described_by)
10
+ @described_by = described_by(described_by)
11
11
  end
12
12
 
13
13
  def html
@@ -35,20 +35,14 @@ module GOVUKDesignSystemFormBuilder
35
35
 
36
36
  def legend_classes
37
37
  size = @legend.dig(:size)
38
- fail "invalid size #{size}, must be #{LEGEND_SIZES.join(', ')}" unless size.in?(LEGEND_SIZES)
38
+ fail "invalid size '#{size}', must be #{LEGEND_SIZES.join(', ')}" unless size.in?(LEGEND_SIZES)
39
39
 
40
- "govuk-fieldset__legend govuk-fieldset__legend--#{size}"
40
+ ["govuk-fieldset__legend", "govuk-fieldset__legend--#{size}"]
41
41
  end
42
42
 
43
43
  def legend_heading_classes
44
44
  %(govuk-fieldset__heading)
45
45
  end
46
-
47
- def descriptors(described_by)
48
- return nil unless described_by.present?
49
-
50
- described_by.compact.join(' ').presence
51
- end
52
46
  end
53
47
  end
54
48
  end
@@ -0,0 +1,29 @@
1
+ module GOVUKDesignSystemFormBuilder
2
+ module Containers
3
+ class RadioButtonsFieldset < GOVUKDesignSystemFormBuilder::Base
4
+ def initialize(builder, object_name, attribute_name, hint_text:, legend:, inline:, small:, &block)
5
+ super(builder, object_name, attribute_name)
6
+
7
+ @inline = inline
8
+ @small = small
9
+ @legend = legend
10
+ @hint_text = hint_text
11
+ @block_content = @builder.capture { block.call }
12
+ end
13
+
14
+ 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
+ @builder.safe_join([
18
+ hint_element.html,
19
+ error_element.html,
20
+ Containers::Radios.new(@builder, inline: @inline, small: @small).html do
21
+ @block_content
22
+ end
23
+ ])
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,62 @@
1
+ module GOVUKDesignSystemFormBuilder
2
+ module Elements
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)
6
+ super(builder, object_name, attribute_name)
7
+
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
15
+ @block_content = @builder.capture { block.call } if block_given?
16
+ end
17
+
18
+ def html
19
+ Containers::FormGroup.new(@builder, @object_name, @attribute_name).html do
20
+ Containers::Fieldset.new(@builder, legend: @legend, described_by: [error_element.error_id, hint_element.hint_id]).html do
21
+ @builder.safe_join(
22
+ [
23
+ hint_element.html,
24
+ error_element.html,
25
+ @block_content,
26
+ Containers::CheckBoxes.new(@builder, small: @small).html do
27
+ build_collection
28
+ end
29
+ ]
30
+ )
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # Builds a collection of check {Elements::CheckBoxes::CheckBox}
38
+ # @return [ActiveSupport::SafeBuffer] HTML output
39
+ #
40
+ # @note The GOV.UK design system requires that error summary links should
41
+ # link to the first checkbox directly. As we don't know if a collection will
42
+ # be rendered when it happens we need to work on the chance that it might, so
43
+ # the +link_errors+ variable is set to +true+ if this attribute has errors and
44
+ # always set back to +false+ after the first checkbox has been rendered
45
+ def build_collection
46
+ link_errors = has_errors?
47
+
48
+ @builder.collection_check_boxes(@attribute_name, @collection, @value_method, @text_method) do |check_box|
49
+ Elements::CheckBoxes::CollectionCheckBox.new(
50
+ @builder,
51
+ @object_name,
52
+ @attribute_name,
53
+ check_box,
54
+ @hint_method,
55
+ link_errors: link_errors
56
+ ).html.tap { link_errors = false }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -2,26 +2,40 @@ module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  module CheckBoxes
4
4
  class CollectionCheckBox < GOVUKDesignSystemFormBuilder::Base
5
- def initialize(builder, attribute, checkbox, hint_method = nil)
6
- @builder = builder
7
- @attribute = attribute
5
+ def initialize(builder, object_name, attribute_name, checkbox, hint_method = nil, link_errors: false)
6
+ super(builder, object_name, attribute_name)
8
7
  @checkbox = checkbox
9
8
  @item = checkbox.object
9
+ @value = checkbox.value
10
10
  @hint_text = @item.send(hint_method) if hint_method.present?
11
+ @link_errors = link_errors
11
12
  end
12
13
 
13
14
  def html
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 }),
19
- Label.new(@checkbox).html,
20
- hint.html
18
+ @checkbox.check_box(
19
+ id: field_id(link_errors: @link_errors),
20
+ class: "govuk-checkboxes__input",
21
+ aria: { describedby: hint_element.hint_id }
22
+ ),
23
+ label_element.html,
24
+ hint_element.html
21
25
  ]
22
26
  )
23
27
  end
24
28
  end
29
+
30
+ private
31
+
32
+ def label_element
33
+ @label_element ||= Label.new(@checkbox, @object_name, @attribute_name, value: @value)
34
+ end
35
+
36
+ def hint_element
37
+ @hint_element ||= Hint.new(@builder, @object_name, @attribute_name, @hint_text, value: @value)
38
+ end
25
39
  end
26
40
  end
27
41
  end
@@ -0,0 +1,75 @@
1
+ module GOVUKDesignSystemFormBuilder
2
+ module Elements
3
+ module CheckBoxes
4
+ class FieldsetCheckBox < GOVUKDesignSystemFormBuilder::Base
5
+ def initialize(builder, object_name, attribute_name, value, label:, hint_text:, link_errors:, multiple:, &block)
6
+ super(builder, object_name, attribute_name)
7
+
8
+ @value = value
9
+ @label = label
10
+ @hint_text = hint_text
11
+ @multiple = multiple
12
+ @link_errors = link_errors
13
+
14
+ if block_given?
15
+ @conditional_content = wrap_conditional(block)
16
+ @conditional_id = conditional_id
17
+ end
18
+ end
19
+
20
+ def html
21
+ @builder.content_tag('div', class: 'govuk-checkboxes__item') do
22
+ @builder.safe_join([input, label, hint, @conditional_content])
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def input
29
+ @builder.check_box(
30
+ @attribute_name,
31
+ {
32
+ id: field_id(link_errors: @link_errors),
33
+ class: check_box_classes,
34
+ multiple: @multiple,
35
+ aria: { describedby: hint_id },
36
+ data: { 'aria-controls' => @conditional_id }
37
+ },
38
+ @value,
39
+ false
40
+ )
41
+ end
42
+
43
+ def label
44
+ Elements::Label.new(
45
+ @builder,
46
+ @object_name,
47
+ @attribute_name,
48
+ value: @value,
49
+ checkbox: true,
50
+ **@label,
51
+ ).html
52
+ end
53
+
54
+ def hint
55
+ Elements::Hint.new(
56
+ @builder,
57
+ @object_name,
58
+ @attribute_name,
59
+ @hint_text,
60
+ @value,
61
+ checkbox: true
62
+ ).html
63
+ end
64
+
65
+ def conditional_classes
66
+ %w(govuk-checkboxes__conditional govuk-checkboxes__conditional--hidden)
67
+ end
68
+
69
+ def check_box_classes
70
+ %w(govuk-checkboxes__input)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,30 +1,22 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  module CheckBoxes
4
- class Hint
5
- def initialize(builder, attribute, checkbox, text)
6
- @builder = builder
7
- @attribute = attribute
8
- @checkbox = checkbox
9
- @text = text
4
+ class Hint < GOVUKDesignSystemFormBuilder::Base
5
+ def initialize(builder, object_name, attribute_name, hint_text, value:)
6
+ super(builder, object_name, attribute_name)
7
+
8
+ @value = value
9
+ @hint_text = hint_text
10
10
  end
11
11
 
12
12
  def html
13
- return nil if @text.blank?
13
+ return nil if @hint_text.blank?
14
14
 
15
- @builder.tag.span(@text, class: hint_classes, id: id)
15
+ @builder.tag.span(@hint_text, class: hint_classes, id: id)
16
16
  end
17
17
 
18
18
  def id
19
- return nil if @text.blank?
20
-
21
- [
22
- @attribute,
23
- @checkbox.object.id,
24
- 'hint'
25
- ]
26
- .join('-')
27
- .parameterize
19
+ hint_id
28
20
  end
29
21
 
30
22
  private
@@ -1,13 +1,14 @@
1
1
  module GOVUKDesignSystemFormBuilder
2
2
  module Elements
3
3
  module CheckBoxes
4
- class Label
5
- def initialize(builder)
6
- @builder = builder
4
+ class Label < GOVUKDesignSystemFormBuilder::Base
5
+ def initialize(builder, object_name, attribute_name, value:)
6
+ super(builder, object_name, attribute_name)
7
+ @value = value
7
8
  end
8
9
 
9
10
  def html
10
- @builder.label(class: label_classes)
11
+ @builder.label(for: field_id, class: label_classes)
11
12
  end
12
13
 
13
14
  private
@@ -8,13 +8,10 @@ module GOVUKDesignSystemFormBuilder
8
8
  @legend = legend
9
9
  @hint_text = hint_text
10
10
  @date_of_birth = date_of_birth
11
- @block_content = @builder.capture { block.call }.html_safe if block_given?
11
+ @block_content = @builder.capture { block.call } if block_given?
12
12
  end
13
13
 
14
14
  def html
15
- hint_element = Elements::Hint.new(@builder, @object_name, @attribute_name, @hint_text)
16
- error_element = Elements::ErrorMessage.new(@builder, @object_name, @attribute_name)
17
-
18
15
  Containers::FormGroup.new(@builder, @object_name, @attribute_name).html do
19
16
  Containers::Fieldset.new(@builder, legend: @legend, described_by: [error_element.error_id, hint_element.hint_id]).html do
20
17
  @builder.safe_join(
@@ -25,9 +22,9 @@ module GOVUKDesignSystemFormBuilder
25
22
  @builder.content_tag('div', class: 'govuk-date-input') do
26
23
  @builder.safe_join(
27
24
  [
28
- date_input_group(:day),
29
- date_input_group(:month),
30
- date_input_group(:year, width: 4)
25
+ date_input_item(:day, link_errors: true),
26
+ date_input_item(:month),
27
+ date_input_item(:year, width: 4)
31
28
  ]
32
29
  )
33
30
  end
@@ -39,7 +36,7 @@ module GOVUKDesignSystemFormBuilder
39
36
 
40
37
  private
41
38
 
42
- def date_input_group(segment, width: 2)
39
+ def date_input_item(segment, width: 2, link_errors: false)
43
40
  value = @builder.object.try(@attribute_name).try(segment)
44
41
 
45
42
  @builder.content_tag('div', class: %w(govuk-date-input__item)) do
@@ -49,13 +46,13 @@ module GOVUKDesignSystemFormBuilder
49
46
  @builder.tag.label(
50
47
  segment.capitalize,
51
48
  class: date_input_label_classes,
52
- for: date_attribute_descriptor(segment)
49
+ for: date_attribute_id(segment, link_errors)
53
50
  ),
54
51
 
55
52
  @builder.tag.input(
56
- id: date_attribute_descriptor(segment),
53
+ id: date_attribute_id(segment, link_errors),
57
54
  class: date_input_classes(width),
58
- name: date_attribute_identifier(segment),
55
+ name: date_attribute_name(segment),
59
56
  type: 'text',
60
57
  pattern: '[0-9]*',
61
58
  inputmode: 'numeric',
@@ -79,11 +76,18 @@ module GOVUKDesignSystemFormBuilder
79
76
  %w(govuk-label govuk-date-input__label)
80
77
  end
81
78
 
82
- def date_attribute_descriptor(segment)
83
- [@object_name, @attribute_name, SEGMENTS.fetch(segment)].join("_")
79
+ # if the field has errors we want the govuk_error_summary to
80
+ # be able to link to the day field. Otherwise, generate IDs
81
+ # in the normal fashion
82
+ def date_attribute_id(segment, link_errors)
83
+ if has_errors? && link_errors
84
+ field_id(link_errors: link_errors)
85
+ else
86
+ [@object_name, @attribute_name, SEGMENTS.fetch(segment)].join("_")
87
+ end
84
88
  end
85
89
 
86
- def date_attribute_identifier(segment)
90
+ def date_attribute_name(segment)
87
91
  "%<object_name>s[%<attribute_name>s(%<segment>s)]" % {
88
92
  object_name: @object_name,
89
93
  attribute_name: @attribute_name,
@@ -9,10 +9,12 @@ module GOVUKDesignSystemFormBuilder
9
9
  return nil unless has_errors?
10
10
 
11
11
  @builder.content_tag('span', class: 'govuk-error-message', id: error_id) do
12
- @builder.safe_join([
13
- @builder.tag.span('Error: ', class: 'govuk-visually-hidden'),
14
- message
15
- ])
12
+ @builder.safe_join(
13
+ [
14
+ @builder.tag.span('Error: ', class: 'govuk-visually-hidden'),
15
+ message
16
+ ]
17
+ )
16
18
  end
17
19
  end
18
20
 
@@ -10,12 +10,12 @@ module GOVUKDesignSystemFormBuilder
10
10
  def html
11
11
  return nil unless object_has_errors?
12
12
 
13
- @builder.content_tag('div', class: 'govuk-error-summary', **error_summary_attributes) do
13
+ @builder.content_tag('div', class: summary_class, **error_summary_attributes) do
14
14
  @builder.safe_join(
15
15
  [
16
- @builder.tag.h2(@title, id: error_summary_title_id, class: 'govuk-error-summary__title'),
17
- @builder.content_tag('div', class: 'govuk-error-summary__body') do
18
- @builder.content_tag('ul', class: 'govuk-list govuk-error-summary__list') do
16
+ @builder.tag.h2(@title, id: error_summary_title_id, class: summary_class('title')),
17
+ @builder.content_tag('div', class: summary_class('body')) do
18
+ @builder.content_tag('ul', class: ['govuk-list', summary_class('list')]) do
19
19
  @builder.safe_join(
20
20
  @builder.object.errors.messages.map do |attribute, messages|
21
21
  error_list_item(attribute, messages)
@@ -32,15 +32,30 @@ module GOVUKDesignSystemFormBuilder
32
32
 
33
33
  def error_list_item(attribute, messages)
34
34
  @builder.content_tag('li') do
35
- @builder.tag.a(
35
+ @builder.link_to(
36
36
  messages.join(', '),
37
- href: ['#', error_id(attribute)].join
37
+ same_page_link(field_id(attribute)),
38
+ data: {
39
+ turbolinks: false
40
+ }
38
41
  )
39
42
  end
40
43
  end
41
44
 
42
- def error_id(attribute)
43
- build_id('error', override_attribute_name: attribute)
45
+ def same_page_link(target)
46
+ '#'.concat(target)
47
+ end
48
+
49
+ def summary_class(part = nil)
50
+ if part
51
+ 'govuk-error-summary'.concat('__', part)
52
+ else
53
+ 'govuk-error-summary'
54
+ end
55
+ end
56
+
57
+ def field_id(attribute)
58
+ build_id('field-error', attribute_name: attribute)
44
59
  end
45
60
 
46
61
  def error_summary_title_id