Simple_Form: Use fieldset/legend for radio buttons / checkboxes - ruby-on-rails

From an accessibility point of view, it is very important to use fieldset/legend combinations for radio buttons and checkboxes:
fieldset
legend What is your sex?
label for="male" Male
input type="radio" id="male"
label for="female" Female
input type="radio" id="female"
This is even true when being inside a fieldset/legend pair already, like so:
fieldset
legend Personal information
label for="name"
input type="text" id="name"
fieldset // Yes, this is a fieldset inside a fieldset!
legend What is your sex?
label for="male" Male
input type="radio" id="male"
label for="female" Female
input type="radio" id="female"
label for="email"
input type="text" id="email"
Simple_Form automatically generates label elements for these legends, though. Is there an easy way to tell Simple_Form to create fieldset/legends, or do I have to work around this with custom wrappers?
Update
For a first work around, I created a custom wrapper like so:
config.wrappers :vertical_radio_and_checkboxes, tag: 'fieldset', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.optional :readonly
b.wrapper tag: 'legend', class: 'col-sm-3 control-label' do |ba|
ba.use :label # TODO: Doesn't need to be a label tag, see http://stackoverflow.com/questions/29261556/simple-form-use-fieldset-legend-for-radio-buttons-checkboxes
end
b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
ba.use :input
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end
This is okay for the time being, although I didn't find a way to tell Simple_Form not to generate a label element for the label (which doesn't point to any ID, which implies that it can't be a correct usage anyway, but it has some important CSS classes added to it, so I didn't want to just abandon it and use label_text).
I have added an issue to the Simple_Form-Bootstrap repo: fieldset/legend should be used for multiple checkboxes/radiobuttons.

To prevent the simple_form generated <label> you can use :label => false
for example in your _form.haml:
= f.input :name, :label => false
in your form.
Is that what you want to achieve? Not sure that's is all behind your question.
just in case, there might be some useful info here

Related

Rails simple_form: Prevent labels from wrapping input

I am unsure with some of the configuration options of simple_form in a Rails application: I am using the enum_help plugin https://github.com/zmbacker/enum_help and I display an enum as radio button options.
However the HTML generated wraps radio buttons inside the labels; I would like to have the inputs (radio buttons) first, then the labels.
Current output:
<!-- this is the "label" that seems to be reacting to b.label in the simple form initializer - see below -->
<label class="enum_radio_buttons optional">Rating</label>
<span class="radio radio radio">
<label for="book_rating_favourite">
<input class="enum_radio_buttons optional" type="radio" value="favourite" name="book[rating]" id="book_rating_favourite" />
Favourite
</label>
</span>
<span class="radio radio radio">
...
</span>
...
Desired output:
<label class="enum_radio_buttons optional">Rating</label>
<span class="radio radio radio">
<input class="enum_radio_buttons optional" type="radio" value="favourite" name="book[rating]" id="book_rating_favourite" />
<label for="book_rating_favourite">
Favourite
</label>
</span>
<span class="radio radio radio">
...
</span>
...
I am not quite sure what to do to change this behavior...
EDIT:
I tried to define a custom wrapper but that doesn't seem to work. The "label" is referring to the label of the full radio button group (edited code above for clarity), and so while I was able to change the order and have input first, and label second, the only thing that happened was that the title was BELOW the radio buttons.
Here's the relevant part of config/initializers/simple_form.rb; it seems that b.use :input simply renders the input nested within the individual label...
config.wrappers :enum_radio,
hint_class: :field_with_hint, error_class: :field_with_errors, valid_class: :field_without_errors do |b|
# we need the radio button first, and then the label
# the label should not wrap the button
b.use :html5
b.use :placeholder
b.use :input
# copied from :default wrapper
b.use :hint, wrap_with: { tag: :span, class: :hint }
b.use :error, wrap_with: { tag: :span, class: :error }
end
And here is how I try to use it in the form:
<%= simple_form_for book do |f| %>
<%= render 'shared/form_errors', form: f %>
...
<div class="form-col">
<%= f.input :rating, as: :radio_buttons, wrapper: :enum_radio %>
</div>
...
<% end %>
EnumHelp yields to SimpleForm
So, you'll have to do some SimpleForm customization to achieve this.
Check out custom wrappers as a starting point.

Simple Form- control width of label and input (with bootstrap)

I am using Simple_form and Bootstrap.
I would like to use a horizontal wrapper, but I need to make the label wider and the input narrower. I would like to do this with bootstrap 4 vs custom css.
The label and input should be on the same line (inline), and right aligned.
col-md-8 for label and col-md-4 for input. How do I do that?
<%= f.input :role, :wrapper =>:horizontal_range, input_wrapper_html: { class: "col-md-4"}, label_wrapper_html: {class: "col-md-8"} %>
EDIT: I should add that the f.input :role is contained within a
<div class='row'>
<div class='col-md-6">
f.input :role
</div>
</div>
Second,
I need to add a % at the end of my input. However, it does not seem to work.
<%= f.input :pct_of_car , append: "%", :wrapper =>:horizontal_range %>
However, this does not seem to work. What is wrong with my syntax?
Third, how would one modify the vertical collection_inline to have 2 or 3 columns?
# vertical input for inline radio buttons and check boxes
config.wrappers :bs_vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
b.use :html5
b.optional :readonly
b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
ba.use :label_text
end
b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
end
Currently all the checkboxes are listed inline and have no structure. I need to list the choices in 2 or 3 columns so that they are easier to read. This is also a long list, so may need to wrap the choices in a scroll box.
You need to define your own custom wrapper or modify the default ones.
First, ensure you have the default wrappers in your repository.
Run rails g simple_form:install --bootstrap if you can not locate app/initializers/simple_form_bootstrap.rb.
Second, locate the Bootstrap grid classes in this initializer. For a horizontal form, they are found in a block that looks like this:
config.wrappers :horizontal_form, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'col-sm-3 control-label'
b.wrapper tag: 'div', class: 'col-sm-9' do |ba|
ba.use :input, class: 'form-control'
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end
Third, change the wrapper!
In this snippet, change col-sm-9 to col-sm-8.
Also Change col-sm-3 to col-sm-4.
Finishing up
Save. Restart your Rails server. Now all forms will have 33% width labels, and 66% width inputs.
If you don't want to apply this globally, pass the wrapper: :my_wrapper option to any input and create a new custom wrapper that does what you'd like it to do.
Gotta love SimpleForm!
To set class for input and label, please use input_html and label_html:
<%= f.input :role, :wrapper => :horizontal_range, input_html: { class: "col-md-4"}, label_html: {class: "col-md-8"} %>
Regarding the second issue - if you are not using hints, probably the easiest solution would be to do it like this:
<%= f.input :pct_of_car , hint: "%", :wrapper =>:horizontal_range %>
Then, you can style it by adding a custom class to the hint with hint_html option (similar to input_html and label_html).
Update
A better solution for adding custom text is to use block for the input:
<%= f.input(:role, :wrapper => :horizontal_range, label_html: {class: "col-md-8"}) do %>
<div class='col-md-4'>
<%= f.input(:role, components: [:input], :wrapper => false, label: false) %>
<span>%</span>
</div>
<% end %>
Thanks to that, your label has col-md-8 class, and input with your text are wrapped in a div with col-md-4 class.

How to remove default input class type from Simple Form, using Rails 4 and Bootstrap 4?

I'm using Rails 4.2.4 with Bootstrap 4 (using the bootstrap_ruby gem).
Simple Form adds input type classes to the form... if the input is a string it will add a class string to the input. I wanted to know how to stop this from happening?
For example, if I had a form with a file input.
simple_form_for #attachment do |f|
f.input_field :file, as: :file
end
It will produce the following HTML:
<form>
...
<div class="form-group file optional photo_image">
<label class="file optional control-label" for="attachment_file">File</label>
<input class="file optional" type="file" name="attachment[file]" id="attachment_file"></div>
...
</form>
It adds the file class to the label and input fields. Is there a way to remove the file class when the form is being built?
I'm trying to use Bootstrap 4 (Alpha) and it's clashing with the file class name.
I thought I could do it on the config.wrappers but it adds the type of input as a class eg. string, file.
Thanks in advance.
Bootstrap 4's naming choice on the custom file field is unfortunate, as it is very generic.
Unfortunately, there is no easy way to toggle that automatically added css-classes with SimpleForm.
My solution is to introduce a new input field which just inherits from the FileInput, so it receives a different name and so a different css-class:
// initializer
# create a new file_hack input type same as file
class SimpleForm::Inputs::FileHackInput < SimpleForm::Inputs::FileInput
end
# optional: map file_hack to a input type configuration for easier classes etc.
SimpleForm.setup do |config|
...
config.wrapper_mappings = {
# check_boxes: :vertical_radio_and_checkboxes,
# radio_buttons: :horizontal_radio_and_checkboxes,
# file: :vertical_file_input,
file_hack: :vertical_file_input,
# boolean: :vertical_boolean
}
config.wrappers :vertical_file_input, tag: 'fieldset', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.use :placeholder
b.use :label, class: 'control-label'
b.wrapper tag: 'div' do |ba|
ba.use :input, class: 'form-control-file'
ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }
ba.use :hint, wrap_with: { tag: 'div', class: 'text-muted' }
end
end
Call the file field as the new input "type".
// form
= f.input :file, as: :file_hack

Rails 3 + SimpleForm + Client side validations + bootstrap: adding error wrapper for whole control group

I have a form with control group that has multiple input fields, that are displayed in-line. One of these fields has error validations. Unfortunately it is in the middle of the control group and for some crazy design reasons (not my call) I cannot move it to the far right of the group. So I would like the error messages to be appended to the whole control group, not the individual input fields, like it is by default. So my questions are:
1) Is it possible to do in the easy, non-intrusive way eg. only changing configuration files? I tried to meddle with:
config/initializers/simple_form_bootstrap.rb
but the best I could do was:
config.wrappers :none, :tag => false do |b|
b.use :html5
b.use :placeholder
b.use :label
b.use :input
end
config.wrapper_mappings = { some_input: :none }
and it's almost working, except the error message disappears after I click submit. Other attempts resulted in form not being displayed at all.
2) How can I change the behavior of Simple Form and/or Client Side Validations in a way that I explicitly say which div I want my error message to be displayed? I am aware that this contradicts the idea of errors being shown somewhere around the bad input field, but I really don't want to write custom validation just for this one form.
I think you should reinstall bootstrap and simple form.
you can try this command:
rails generate simple_form:install --bootstrap
or you can refer to my wrapper config
config.wrappers :bootstrap, tag: 'div', class: 'control-group', error_class: 'error' do |b|
b.use :html5
b.use :placeholder
b.use :label
b.wrapper tag: 'div', class: 'controls' do |ba|
ba.use :input, class: 'form-control'
ba.use :error, wrap_with: { tag: 'span', class: 'help-inline' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end end
default wrapper: config.default_wrapper = :bootstrap
check attributes in your css files

SimpleForm 2 : input tags inside a wrapper

by using the default simple_form 2 wrapper, it generates a markup that looks like this :
<div class="...">
<label>...</label>
<input ... />
</div>
I would like to get a markup in which the input tag is itself inside a wrapper like this :
<div class="...">
<label>...</label>
<div class="...">
<input ... />
</div>
</div>
Do I have to create a custom component for this behaviour ?
Thanks.
Nicolas.
Check this out:
The wrapper API isn't well documented yet, but I've spent some time trying to figure it out. Here is a simple example, which can be setup inside the simple_form initializer.
# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
# Wrappers are used by the form builder to generate a complete input.
# You can remove any component from the wrapper, change the order or even
# add your own to the stack. The options given to the wrappers method
# are used to wrap the whole input (if any exists).
config.wrappers :GIVE_YOUR_WRAPPER_A_NAME, :class => 'clearfix', :error_class => nil do |b|
b.use :placeholder
b.use :label
b.use :tag => 'div', :class => 'WHATEVER_YOU_WANT_TO_CALL_IT' do |ba|
ba.use :input
ba.use :error, :tag => :span, :class => :'help-inline'
ba.use :hint, :tag => :span, :class => :'help-block'
end
end
end
Then, when you create a form just specify the wrapper you want to use:
<%= simple_form_for #user, wrapper: 'WHATEVER_YOU_CALLED_YOUR_WRAPPER' do |form| %>
<%end%>
Hope this helps.

Resources