Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add html classes to inputs with the wrappers API #622

Closed
wants to merge 3 commits into from
Closed
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
5 changes: 5 additions & 0 deletions lib/simple_form/components/label_input.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ module LabelInput
end

def label_input
[:input_html, :label_html].each do |key|
if options.has_key? key
options[key].merge! options.fetch(:label_input_html, {})
end
end
options[:label] == false ? input : (label + input)
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/simple_form/components/labels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def label_html_options
if options.key?(:input_html) && options[:input_html].key?(:id)
label_options[:for] = options[:input_html][:id]
end
label_options
@label_options = label_options
end

protected
Expand Down
10 changes: 8 additions & 2 deletions lib/simple_form/inputs/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,20 @@ def initialize(builder, attribute_name, column, input_type, options = {})
@html_classes = SimpleForm.additional_classes_for(:input) { additional_classes }

@input_html_classes = @html_classes.dup
@input_html_options = {}

if SimpleForm.input_class && !input_html_classes.empty?
input_html_classes << SimpleForm.input_class
end
end

@input_html_options = html_options_for(:input, input_html_classes).tap do |o|
def input_html_options
html_options = html_options_for(:input, input_html_classes).tap do |o|
o[:readonly] = true if has_readonly?
o[:disabled] = true if has_disabled?
o[:autofocus] = true if has_autofocus?
end
@input_html_options.merge! html_options
end

def input
Expand Down Expand Up @@ -126,8 +131,9 @@ def reflection_or_attribute_name
def html_options_for(namespace, css_classes)
html_options = options[:"#{namespace}_html"]
html_options = html_options ? html_options.dup : {}
html_options.reverse_merge!(@builder.wrapper.options[:"#{namespace}_html"] || {})
css_classes << html_options[:class] if html_options.key?(:class)
html_options[:class] = css_classes unless css_classes.empty?
html_options[:class] = css_classes.uniq unless css_classes.empty?
html_options
end

Expand Down
14 changes: 12 additions & 2 deletions lib/simple_form/wrappers/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,24 @@ module Wrappers
# In the example above, hint defaults to false, which means it won't automatically
# do the lookup anymore. It will only be triggered when :hint is explicitly set.
class Builder

def initialize(options)
@options = options
@components = []
end

def use(name, options=nil, &block)
if options && wrapper = options[:wrap_with]
@components << Single.new(name, wrapper)
if options && (SimpleForm::FormBuilder::ATTRIBUTE_COMPONENTS.include?(name) \
&& !(options.except(:wrap_with).keys.empty?))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can still use the options.keys != [:wrap_with] check here.

raise ArgumentError, "Invalid options #{options.except(:wrap_with).keys.inspect} passed to #{name}."
end

if options && options[:wrap_with]
@options[:"#{name}_html"] = options.except(:wrap_with)
@components << Single.new(name, options[:wrap_with])
elsif options
@options[:"#{name}_html"] = options
@components << name
else
@components << name
end
Expand Down
58 changes: 58 additions & 0 deletions test/form_builder/wrapper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,18 @@ class WrapperTest < ActionView::TestCase
assert_select "section.custom_wrapper div.another_wrapper input.string"
end

test 'wrappers with use classes on input basis with arrays' do
swap_wrapper :default do
with_form_for @user, :name
assert_select "section.custom_wrapper div.class1.class2 input.class3.class4"

output_buffer.replace ""

with_form_for @user, :name, wrapper: custom_wrapper_with_no_wrapping_tag
assert_select "div.custom_wrapper div.elem input.input_class_yo.other_class_yo"
end
end

test 'access wrappers with indifferent access' do
swap_wrapper :another do
with_form_for @user, :name, wrapper: "another"
Expand All @@ -172,6 +184,52 @@ class WrapperTest < ActionView::TestCase
end
end

test 'single element without wrap_with applies options to component tags' do
swap_wrapper :default, custom_wrapper_with_no_wrapping_tag do
with_form_for @user, :name
assert_select "div.custom_wrapper div.elem input.input_class_yo"
assert_select "div.custom_wrapper div.elem input.other_class_yo"
assert_select "div.custom_wrapper div.elem input.string"
assert_select "div.custom_wrapper div.elem label[data-yo='yo']"
assert_select "div.custom_wrapper div.elem span.custom_yo", :text => "custom"
assert_select "div.custom_wrapper div.elem label.both_yo"
assert_select "div.custom_wrapper div.elem input.both_yo"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assert_select accepts a block, may make this a bit more clear?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, forget about it. Other tests need more clean up with that, we can do that later.

end
end

test 'single element with wrap with and component options applies to both' do
swap_wrapper :default, custom_wrapper_with_wrapping_tag_and_component_options do
with_form_for @user, :name
assert_no_select "div.custom_wrapper > input"
assert_select "div.custom_wrapper div.wrap input.input_class_yo"
assert_select "div.custom_wrapper div.wrap input.other_class_yo"
end
end

test 'adding any option to tag components on the input ignores them' do
with_concat_form_for @user do |f|
concat f.input :name, :invalid => 'thing'
end
assert_no_select "input[invalid]"
end

test 'adding any option to tag components in wrapper makes html attributes' do
swap_wrapper :default, custom_wrapper_with_wrapping_tag_and_invalid_attributes do
with_input_for @user, :name, :string, :other_invalid => 'other_thing'
assert_select "input.input_class_yo"
assert_select "input[invalid='thing']"
assert_no_select "input[other_invalid]"
end
end

test 'adding invalid options to non-tag components raises an exception' do
assert_raise ArgumentError, "Invalid options [:class] passed to placeholder." do
swap_wrapper :default, custom_wrapper_with_invalid_options do
with_form_for @user, :name
end
end
end

test 'do not duplicate label classes for different inputs' do
swap_wrapper :default, self.custom_wrapper_with_label_html_option do
with_concat_form_for(@user) do |f|
Expand Down
48 changes: 48 additions & 0 deletions test/support/misc_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,48 @@ def custom_wrapper
ba.use :label
ba.use :input
end
b.wrapper :arrays, class: ["class1", "class2"] do |ba|
ba.use :input, class: ["class3", "class4"]
ba.use :label
end
b.wrapper :error_wrapper, tag: :div, class: "error_wrapper" do |be|
be.use :error, wrap_with: { tag: :span, class: "omg_error" }
end
b.use :hint, wrap_with: { class: "omg_hint" }
end
end

def custom_wrapper_with_no_wrapping_tag
SimpleForm.build :tag => :div, :class => "custom_wrapper" do |b|
b.wrapper :tag => :div, :class => 'elem' do |component|
component.use :input, :class => ['input_class_yo', 'other_class_yo']
component.use :label, :"data-yo" => 'yo'
component.use :label_input, :class => 'both_yo'
component.use :custom_component, :class => 'custom_yo'
end
end
end

def custom_wrapper_with_wrapping_tag_and_component_options
SimpleForm.build :tag => :div, :class => 'custom_wrapper' do |b|
b.use :input, :class => ['input_class_yo', 'other_class_yo'],
:wrap_with => { :tag => :div, :class => 'wrap' }
end
end

def custom_wrapper_with_wrapping_tag_and_invalid_attributes
SimpleForm.build :tag => :div, :class => "custom_wrapper" do |b|
b.use :input, :class => 'input_class_yo', :invalid => 'thing'
end
end

def custom_wrapper_with_invalid_options
SimpleForm.build :tag => :div, :class => "custom_wrapper" do |b|
b.use :placeholder, :class => 'no_effect'
b.use :input
end
end

def custom_wrapper_with_wrapped_input
SimpleForm.build tag: :div, class: "custom_wrapper" do |b|
b.wrapper tag: :div, class: 'elem' do |component|
Expand Down Expand Up @@ -158,3 +193,16 @@ def input(attribute_name, *args, &block)
class CustomMapTypeFormBuilder < SimpleForm::FormBuilder
map_type :custom_type, to: SimpleForm::Inputs::StringInput
end

module SimpleForm::Components::CustomComponent
def custom_component
@custom_component ||= begin
custom_options = options[:custom_component_html]
template.content_tag(:span, custom_options) do
"custom".html_safe
end
end
end
end

SimpleForm::Inputs::Base.send(:include, SimpleForm::Components::CustomComponent)