Skip to content
This repository has been archived by the owner on May 5, 2020. It is now read-only.

Support attributes defined as a string #69

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions lib/govuk_elements_form_builder/form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,8 @@ def form_group_id attribute
end

def form_group_classes attributes
attributes = [attributes] if !attributes.respond_to? :count
classes = 'form-group'
classes += ' error' if attributes.find { |a| error_for? a }
classes += ' error' if Array(attributes).find { |a| error_for? a }
classes
end

Expand Down
73 changes: 16 additions & 57 deletions spec/lib/govuk_elements_form_builder/form_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class TestHelper < ActionView::Base; end

let(:helper) { TestHelper.new }
let(:resource) { Person.new }
let(:builder) { described_class.new :person, resource, helper, {} }
subject(:builder) { described_class.new :person, resource, helper, {} }

def expect_equal output, expected
split_output = output.gsub(">\n</textarea>", ' />').split("<").join("\n<").split(">").join(">\n").squeeze("\n").strip + '>'
Expand All @@ -30,76 +30,42 @@ def type_for(method, type)
end

shared_examples_for 'input field' do |method, type|

def size(method, size)
method == :text_area ? '' : %'size="#{size}" '
end
let(:default_builder_options) { { resource: 'person' } }

it 'outputs label and input wrapped in div' do
output = builder.send method, :name

expect_equal output, [
'<div class="form-group">',
'<label class="form-label" for="person_name">',
'Full name',
'</label>',
%'<#{element_for(method)} class="form-control" #{type_for(method, type)}name="person[name]" id="person_name" />',
'</div>'
]
expect(output).to match_form_group(default_builder_options.merge(field: :name, input_type: method))
end

it 'supports attributes defined as a string' do
output = builder.send method, 'name'

expect(output).to match_form_group(default_builder_options.merge(field: :name, input_type: method))
end

it 'adds custom class to input when passed class: "custom-class"' do
output = builder.send method, :name, class: 'custom-class'

expect_equal output, [
'<div class="form-group">',
'<label class="form-label" for="person_name">',
'Full name',
'</label>',
%'<#{element_for(method)} class="form-control custom-class" #{type_for(method, type)}name="person[name]" id="person_name" />',
'</div>'
]
expect(output).to match_form_group(default_builder_options.merge(field: :name, input_type: method, class: 'custom-class'))
end

it 'adds custom classes to input when passed class: ["custom-class", "another-class"]' do
output = builder.send method, :name, class: ['custom-class', 'another-class']

expect_equal output, [
'<div class="form-group">',
'<label class="form-label" for="person_name">',
'Full name',
'</label>',
%'<#{element_for(method)} class="form-control custom-class another-class" #{type_for(method, type)}name="person[name]" id="person_name" />',
'</div>'
]
expect(output).to match_form_group(default_builder_options.merge(field: :name, input_type: method, class: 'custom-class another-class'))
end

it 'passes options passed to text_field onto super text_field implementation' do
output = builder.send method, :name, size: 100
expect_equal output, [
'<div class="form-group">',
'<label class="form-label" for="person_name">',
'Full name',
'</label>',
%'<#{element_for(method)} #{size(method, 100)}class="form-control" #{type_for(method, type)}name="person[name]" id="person_name" />',
'</div>'
]

expect(output).to match_form_group(default_builder_options.merge(field: :name, input_type: method, size: 100))
end

context 'when hint text provided' do
it 'outputs hint text in span inside label' do
output = builder.send method, :ni_number
expect_equal output, [
'<div class="form-group">',
'<label class="form-label" for="person_ni_number">',
'National Insurance number',
'<span class="form-hint">',
'It’ll be on your last payslip. For example, JH 21 90 0A.',
'</span>',
'</label>',
%'<#{element_for(method)} class="form-control" #{type_for(method, type)}name="person[ni_number]" id="person_ni_number" />',
'</div>'
]
expect(output).to match_form_group(default_builder_options.merge(field: :ni_number, input_type: method))
end
end

Expand All @@ -108,14 +74,7 @@ def size(method, size)
output = builder.fields_for(:address, Address.new) do |f|
f.send method, :postcode
end
expect_equal output, [
'<div class="form-group">',
'<label class="form-label" for="person_address_attributes_postcode">',
'Postcode',
'</label>',
%'<#{element_for(method)} class="form-control" #{type_for(method, type)}name="person[address_attributes][postcode]" id="person_address_attributes_postcode" />',
'</div>'
]
expect(output).to match_form_group(default_builder_options.merge(field: :postcode, input_type: method, fields_for: :address))
end
end

Expand Down Expand Up @@ -224,7 +183,7 @@ def expected_error_html method, type, attribute, name_value, label, error
end

describe '#text_area' do
include_examples 'input field', :text_area, nil
include_examples 'input field', :text_area
end

describe '#email_field' do
Expand Down
2 changes: 2 additions & 0 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#
# Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
require_relative 'support/translation_helper.rb'
require_relative 'support/matcher_helpers.rb'

# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
Expand Down Expand Up @@ -49,4 +50,5 @@
# The different available types are documented in the features, such as in
# https://relishapp.com/rspec/rspec-rails/docs
config.infer_spec_type_from_file_location!
config.include MatcherHelpers
end
7 changes: 7 additions & 0 deletions spec/support/matcher_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require_relative 'matchers/form_group_matcher_helpers'

module MatcherHelpers
def self.included(base)
base.send(:include, FormGroupMatcherHelpers)
end
end
115 changes: 115 additions & 0 deletions spec/support/matchers/form_group_matcher_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
require 'rspec/expectations'

module FormGroupMatcherHelpers
extend RSpec::Matchers::DSL

class FormGroupObject
attr_reader :options, :resource, :field,
:label, :input_type, :input_class, :input_size,
:fields_for

def initialize(options)
@options = options
@resource = options[:resource]
@field = options.fetch(:field)
@label = options.fetch(:label) { @field.to_s.humanize }
@input_type = options.fetch(:input_type)
@input_class = options[:class]
@input_size = options[:size]
@fields_for = options[:fields_for]
end

def field_id
[resource, fields_for_attributes, field].compact.join('_')
end

def field_name
return "#{resource}[#{fields_for_attributes}][#{field}]" if resource && fields_for_attributes
return "#{resource}[#{field}]" if resource
field
end

def to_s
['<div class="form-group">', label_tag, field_tag, '</div>'].join("\n")
end

private

def fields_for_attributes
return unless fields_for
"#{fields_for}_attributes"
end

def text_area_input?
input_type.to_s == 'text_area'
end

def input_tag
return '<textarea' if text_area_input?
'<input'
end

def input_tag_type
return if text_area_input?
type = {
text_field: 'text',
email_field: 'email',
number_field: 'number',
password_field: 'password',
phone_field: 'tel',
range_field: 'range',
search_field: 'search',
telephone_field: 'tel',
url_field: 'url'
}[input_type.to_sym]
%[type="#{type}"]
end

def input_tag_class
classes = 'form-control'
classes << ' ' << input_class if input_class
%[class="#{classes}"]
end

def input_tag_size
return if text_area_input?
return unless input_size
%[size="#{input_size}"]
end

def hint_tag
localized_path = ['helpers', 'hint', resource, field].compact.join('.')
localized_hint = I18n.t(localized_path, default: '')
return unless localized_hint.present?
['<span class="form-hint">', localized_hint, '</span>'].join("\n")
end

def label_tag
localized_path = ['helpers', 'label', resource, field].compact.join('.')
localized_label = I18n.t(localized_path, default: field.to_s.humanize)
[%[<label class="form-label" for="#{field_id}">], localized_label, hint_tag, '</label>'].compact.join("\n")
end

def field_tag
[input_tag, input_tag_size, input_tag_class, input_tag_type, %[name="#{field_name}"], %[id="#{field_id}"], '/>'].compact.join(' ')
end
end

matcher :match_form_group do |expected|
define_method :formatted_expected do
FormGroupObject.new(expected).to_s
end

define_method :formatted_actual do |actual|
actual.gsub(">\n</textarea>", ' />').split("<").join("\n<").split(">").join(">\n").squeeze("\n").strip + '>'
end

match do |actual|
formatted_actual(actual) == formatted_expected
end

failure_message do |actual|
"expected\n#{formatted_actual(actual)}\nto match\n#{formatted_expected}"
end
end
end