Skip to content
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
5 changes: 0 additions & 5 deletions .yarnrc

This file was deleted.

30 changes: 15 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1490,38 +1490,38 @@ Generated HTML:
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
<div class="mb-3">
<label class="form-label required" for="user_email">Email</label>
<input class="form-control is-invalid" id="user_email" name="user[email]" required="required" type="email" value="steve.example.com">
<div class="invalid-feedback">is invalid</div>
<input aria-labelledby="user_email_feedback" class="form-control is-invalid" id="user_email" name="user[email]" required="required" type="email" value="steve.example.com">
<div class="invalid-feedback" id="user_email_feedback">is invalid</div>
</div>
<div class="mb-3">
<label class="form-label" for="user_misc">Misc</label>
<div class="form-check">
<input checked class="form-check-input is-invalid" id="user_misc_1" name="user[misc]" type="radio" value="1">
<input aria-labelledby="user_misc_feedback" checked class="form-check-input is-invalid" id="user_misc_1" name="user[misc]" type="radio" value="1">
<label class="form-check-label" for="user_misc_1">Mind reading</label>
</div>
<div class="form-check">
<input class="form-check-input is-invalid" id="user_misc_2" name="user[misc]" type="radio" value="2">
<input aria-labelledby="user_misc_feedback" class="form-check-input is-invalid" id="user_misc_2" name="user[misc]" type="radio" value="2">
<label class="form-check-label" for="user_misc_2">Farming</label>
<div class="invalid-feedback">is invalid</div>
<div class="invalid-feedback" id="user_misc_feedback">is invalid</div>
</div>
</div>
<input id="user_preferences" name="user[preferences][]" type="hidden" value="">
<div class="mb-3">
<label class="form-label" for="user_preferences">Preferences</label>
<div class="form-check">
<input checked class="form-check-input is-invalid" id="user_preferences_1" name="user[preferences][]" type="checkbox" value="1">
<input aria-labelledby="user_preferences_feedback" checked class="form-check-input is-invalid" id="user_preferences_1" name="user[preferences][]" type="checkbox" value="1">
<label class="form-check-label" for="user_preferences_1">Good</label>
</div>
<div class="form-check">
<input class="form-check-input is-invalid" id="user_preferences_2" name="user[preferences][]" type="checkbox" value="2">
<input aria-labelledby="user_preferences_feedback" class="form-check-input is-invalid" id="user_preferences_2" name="user[preferences][]" type="checkbox" value="2">
<label class="form-check-label" for="user_preferences_2">Bad</label>
<div class="invalid-feedback">is invalid</div>
<div class="invalid-feedback" id="user_preferences_feedback">is invalid</div>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="user_address_attributes_street">Street</label>
<input class="form-control is-invalid" id="user_address_attributes_street" name="user[address_attributes][street]" type="text" value="Bar">
<div class="invalid-feedback">is invalid</div>
<input aria-labelledby="user_address_attributes_street_feedback" class="form-control is-invalid" id="user_address_attributes_street" name="user[address_attributes][street]" type="text" value="Bar">
<div class="invalid-feedback" id="user_address_attributes_street_feedback">is invalid</div>
</div>
</form>
```
Expand Down Expand Up @@ -1551,8 +1551,8 @@ Generated HTML:
```html
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
<div class="mb-3">
<label class="form-label required text-danger" for="user_email">Email is invalid</label>
<input class="form-control is-invalid" id="user_email" name="user[email]" required="required" type="email" value="steve.example.com">
<label class="form-label required text-danger" for="user_email" id="user_email_feedback">Email is invalid</label>
<input aria-labelledby="user_email_feedback" class="form-control is-invalid" id="user_email" name="user[email]" required="required" type="email" value="steve.example.com">
</div>
</form>
```
Expand Down Expand Up @@ -1649,7 +1649,7 @@ Which outputs:
```html
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
<input autocomplete="off" class="is-invalid" disabled type="hidden">
<div class="invalid-feedback">Email is invalid</div>
<div class="invalid-feedback" id="user_email_feedback">Email is invalid</div>
</form>
```

Expand All @@ -1670,7 +1670,7 @@ Which outputs:
```html
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
<input autocomplete="off" class="is-invalid" disabled type="hidden">
<div class="invalid-feedback">is invalid</div>
<div class="invalid-feedback" id="user_email_feedback">is invalid</div>
</form>
```

Expand All @@ -1689,7 +1689,7 @@ Which outputs:
```html
<form accept-charset="UTF-8" action="/users" class="new_user" id="new_user" method="post">
<input autocomplete="off" class="is-invalid" disabled type="hidden">
<div class="custom-error">Email is invalid</div>
<div class="custom-error" id="user_email_feedback">Email is invalid</div>
</form>
```

Expand Down
1 change: 1 addition & 0 deletions lib/bootstrap_form/components/labels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def generate_label(id, name, options, custom_label_col, group_layout)

options[:class] = label_classes(name, options, custom_label_col, group_layout)
options.delete(:class) if options[:class].none?
options[:id] = id.present? ? "#{id}_feedback" : field_id(name, :feedback) if error?(name) && label_errors

label(name, label_text(name, options), options.except(:text))
end
Expand Down
5 changes: 3 additions & 2 deletions lib/bootstrap_form/components/validation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,15 @@ def inline_error?(name)
error?(name) && inline_errors
end

def generate_error(name)
def generate_error(name, id)
return unless inline_error?(name)

help_text = get_error_messages(name)
help_klass = "invalid-feedback"
help_tag = :div
id = id.present? ? "#{id}_feedback" : field_id(name, :feedback)

content_tag(help_tag, help_text, class: help_klass)
content_tag(help_tag, help_text, class: help_klass, id:)
end

def get_error_messages(name)
Expand Down
2 changes: 1 addition & 1 deletion lib/bootstrap_form/form_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def form_group_content_tag(name, field_name, without_field_name, options, html_o
html_class = control_specific_class(field_name)
html_class = "#{html_class} col-auto g-3" if @layout == :horizontal && options[:skip_inline].blank?
tag.div(class: html_class) do
input_with_error(name) do
input_with_error(name, options[:id]) do
send(without_field_name, name, options, html_options)
end
end
Expand Down
6 changes: 5 additions & 1 deletion lib/bootstrap_form/form_group_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ def form_group_css_options(method, html_options, options)
# Add control_class; allow it to be overridden by :control_class option
control_classes = css_options.delete(:control_class) { control_class }
css_options[:class] = safe_join([control_classes, css_options[:class]].compact, " ")
css_options[:class] << " is-invalid" if error?(method)
if error?(method)
css_options[:class] << " is-invalid"
labelledby = options[:id].present? ? "#{options[:id]}_feedback" : field_id(method, :feedback)
css_options[:aria] = { labelledby: }
end
css_options[:placeholder] = form_group_placeholder(options, method) if options[:label_as_placeholder]
css_options
end
Expand Down
9 changes: 5 additions & 4 deletions lib/bootstrap_form/helpers/bootstrap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def errors_on(name, options={})
hide_attribute_name = options[:hide_attribute_name] || false
custom_class = options[:custom_class] || false

tag.div class: custom_class || "invalid-feedback" do
tag.div(class: custom_class || "invalid-feedback", id: field_id(name, :feedback)) do
errors = if hide_attribute_name
object.errors[name]
else
Expand Down Expand Up @@ -66,20 +66,21 @@ def custom_control(*args, &)
end

def prepend_and_append_input(name, options, &)
id = options[:id]
options = options.extract!(:prepend, :append, :input_group_class).compact

input = capture(&) || ActiveSupport::SafeBuffer.new

input = attach_input(options, :prepend) + input + attach_input(options, :append)
input << generate_error(name)
input << generate_error(name, id)
options.present? &&
input = tag.div(input, class: ["input-group", options[:input_group_class]].compact)
input
end

def input_with_error(name, &)
def input_with_error(name, id, &)
input = capture(&)
input << generate_error(name)
input << generate_error(name, id)
end

def input_group_content(content)
Expand Down
1 change: 1 addition & 0 deletions lib/bootstrap_form/inputs/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def bootstrap_field(field_name)

def bootstrap_select_group(field_name)
define_method(:"#{field_name}_with_bootstrap") do |name, options={}, html_options={}|
options.delete(:id)
html_options = html_options.reverse_merge(control_class: "form-select")
form_group_builder(name, options, html_options) do
form_group_content_tag(name, field_name, "#{field_name}_without_bootstrap", options, html_options)
Expand Down
6 changes: 5 additions & 1 deletion lib/bootstrap_form/inputs/check_box.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def check_box_with_bootstrap(name, options={}, checked_value="1", unchecked_valu
content = tag.div(class: check_box_wrapper_class(options), **options[:wrapper].to_h.except(:class)) do
html = check_box_without_bootstrap(name, check_box_options(name, options), checked_value, unchecked_value)
html << check_box_label(name, options, checked_value, &block) unless options[:skip_label]
html << generate_error(name) if options[:error_message]
html << generate_error(name, options[:id]) if options[:error_message]
html
end
wrapper(content, options)
Expand Down Expand Up @@ -41,6 +41,10 @@ def check_box_options(name, options)
:inline, :label, :label_class, :label_col, :layout, :skip_label,
:switch, :wrapper, :wrapper_class)
check_box_options[:class] = check_box_classes(name, options)
if error?(name)
labelledby = options[:id].present? ? "#{options[:id]}_feedback" : field_id(name, :feedback)
check_box_options[:aria] = { labelledby: }
end
check_box_options.merge!(required_field_options(options, name))
end

Expand Down
1 change: 1 addition & 0 deletions lib/bootstrap_form/inputs/collection_check_boxes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module CollectionCheckBoxes

included do
def collection_check_boxes_with_bootstrap(*args)
args[4]&.delete(:id)
html = inputs_collection(*args) do |name, value, options|
options[:multiple] = true
check_box(name, options, value, nil)
Expand Down
1 change: 1 addition & 0 deletions lib/bootstrap_form/inputs/collection_radio_buttons.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module CollectionRadioButtons

included do
def collection_radio_buttons_with_bootstrap(*args)
args[4]&.delete(:id)
inputs_collection(*args) do |name, value, options|
radio_button(name, value, options)
end
Expand Down
6 changes: 5 additions & 1 deletion lib/bootstrap_form/inputs/radio_button.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def radio_button_with_bootstrap(name, value, *args)
tag.div(**wrapper_attributes) do
html = radio_button_without_bootstrap(name, value, radio_button_options(name, options))
html << radio_button_label(name, value, options) unless options[:skip_label]
html << generate_error(name) if options[:error_message]
html << generate_error(name, options[:id]) if options[:error_message]
html
end
end
Expand All @@ -28,6 +28,10 @@ def radio_button_options(name, options)
radio_button_options = options.except(:class, :label, :label_class, :error_message, :help,
:inline, :hide_label, :skip_label, :wrapper, :wrapper_class)
radio_button_options[:class] = radio_button_classes(name, options)
if error?(name)
labelledby = options[:id].present? ? "#{options[:id]}_feedback" : field_id(name, :feedback)
radio_button_options[:aria] = { labelledby: }
end
radio_button_options.merge!(required_field_options(options, name))
end

Expand Down
2 changes: 1 addition & 1 deletion lib/bootstrap_form/inputs/time_zone_select.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module TimeZoneSelect
def time_zone_select_with_bootstrap(method, priority_zones=nil, options={}, html_options={})
html_options = html_options.reverse_merge(control_class: "form-select")
form_group_builder(method, options, html_options) do
input_with_error(method) do
input_with_error(method, options[:id]) do
time_zone_select_without_bootstrap(method, priority_zones, options, html_options)
end
end
Expand Down
Loading