govuk_design_system_formbuilder 0.7.9 → 0.7.10

Sign up to get free protection for your applications and to get access to all the features.
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