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

0.3.0 #6

Merged
merged 7 commits into from
May 15, 2023
Merged
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
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
rails_bootstrap_form (0.2.3)
rails_bootstrap_form (0.3.0)

GEM
remote: http://rubygems.org/
Expand Down
5 changes: 5 additions & 0 deletions app/assets/images/exclamation-triangle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions app/assets/stylesheets/rails_bootstrap_form.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,9 @@ label.required::after {
top: -2px;
font-weight: bolder;
}
.form-control.is-invalid, .form-select.is-invalid {
background-image: url("exclamation-triangle.svg") !important;
background-repeat: no-repeat !important;
background-position: right 0.5rem center, center right 2rem !important;
background-size: 24px 24px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem) !important;
}
3 changes: 2 additions & 1 deletion lib/rails_bootstrap_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ module RailsBootstrapForm
autoload :Configuration
autoload :BootstrapFormOptions
autoload :BootstrapFormBuilder
autoload :FieldWrapperBuilder
autoload :Components
autoload :FieldWrapperBuilder
autoload :InputGroupBuilder
autoload :Inputs
end

Expand Down
1 change: 1 addition & 0 deletions lib/rails_bootstrap_form/bootstrap_form_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class BootstrapFormBuilder < ActionView::Helpers::FormBuilder

include RailsBootstrapForm::FieldWrapperBuilder
include RailsBootstrapForm::Components
include RailsBootstrapForm::InputGroupBuilder
include RailsBootstrapForm::Inputs

delegate :capture, :concat, :tag, to: :@template
Expand Down
33 changes: 31 additions & 2 deletions lib/rails_bootstrap_form/bootstrap_form_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ class BootstrapFormOptions
attr_accessor :help_text

# An option to override automatically generated label text.
# Default is `nil`.
attr_accessor :label_text

# An option to custmize whether the label is to be displayed or not.
# Default is `false`.
attr_accessor :skip_label

# An option to customize whether the label is only visible to screen readers.
# Default is `false`.
attr_accessor :hide_label

# The CSS class that will be used when the label is only accessible by screen
Expand All @@ -52,9 +55,33 @@ class BootstrapFormOptions
attr_accessor :label_class

# An additional CSS class that will be added along with the existing
# `label_class` of the label. Default is nil.
# `label_class` of the label. Default is `nil`.
attr_accessor :additional_label_class

# Input group specific options. Input groups allow prepending and appending
# arbitrary html or text to the field.
#
# Example:
#
# form.text_field :dollars, bootstrap_form: {input_group: {prepend: "$", append: ".00"}}
# form.text_field :search, bootstrap_form: {input_group: {append: button_tag("Go", type: :submit, class: "btn btn-secondary")}}
#
# Raw or HTML content to be prepended to the field.
# Default is `nil`.
attr_accessor :prepend

# Raw or HTML content to be appended to the field.
# Default is `nil`.
attr_accessor :append

# Append additional CSS class added to the input group wrapper.
# Default is `nil`.
attr_accessor :additional_input_group_class

# Option to control whether the field should have a floating label.
# Default is false.
attr_accessor :floating

def initialize(options = {})
set_defaults
set_bootstrap_form_options(options)
Expand Down Expand Up @@ -105,12 +132,14 @@ def set_defaults

@help_text = nil

@label_text = ""
@label_text = nil
@skip_label = false
@hide_label = false
@hide_class = "visually-hidden"
@label_class = "form-label"
@additional_label_class = nil

@floating = false
end

private :set_defaults
Expand Down
2 changes: 2 additions & 0 deletions lib/rails_bootstrap_form/components.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ module Components
autoload :HelpText
autoload :Labels
autoload :RequiredField
autoload :Errors

include HelpText
include Labels
include RequiredField
include Errors
end
end
67 changes: 67 additions & 0 deletions lib/rails_bootstrap_form/components/errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# -*- encoding: utf-8 -*-
# -*- frozen_string_literal: true -*-
# -*- warn_indent: true -*-

module RailsBootstrapForm
module Components
module Errors
extend ActiveSupport::Concern

def self.included(base_class)
def is_invalid?(attribute)
(attribute && object.respond_to?(:errors) && object.errors[attribute].any?) ||
has_association_error?(attribute)
end

def input_with_error(attribute, &block)
input = capture(&block)
input << generate_error(attribute)
input
end

def generate_error(attribute)
if is_invalid?(attribute)
error_text = error_messages(attribute)
error_klass = "invalid-feedback"

tag.div(error_text, class: error_klass)
end
end

def has_association_error?(attribute)
object.class.reflections.any? do |association_name, association|
next unless is_belongs_to_association?(association)
next unless is_association_same?(attribute, association)

object.errors[association_name].any?
end
end

def error_messages(attribute)
messages = object.errors[attribute]

object.class.reflections.each do |association_name, association|
next unless is_belongs_to_association?(association)
next unless is_association_same?(attribute, association)

messages << object.errors[association_name]
end

messages.flatten.to_sentence
end

def is_belongs_to_association?(association)
association.is_a?(ActiveRecord::Reflection::BelongsToReflection)
end

def is_association_same?(attribute, association)
(association.foreign_key == attribute.to_s)
end

private :is_invalid?, :input_with_error, :generate_error,
:has_association_error?, :error_messages,
:is_belongs_to_association?, :is_association_same?
end
end
end
end
1 change: 1 addition & 0 deletions lib/rails_bootstrap_form/components/labels.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def label_classes(attribute, bootstrap_options)
classes = [bootstrap_options.label_class, bootstrap_options.additional_label_class]
classes << bootstrap_options.hide_class if bootstrap_options.hide_label
classes << "required" if is_attribute_required?(attribute)
classes << "is-invalid" if is_invalid?(attribute)
classes.flatten.compact
end

Expand Down
2 changes: 1 addition & 1 deletion lib/rails_bootstrap_form/components/required_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def is_field_required?(attribute, options)
end
end

def required_field_options(options, attribute)
def required_field_options(attribute, options)
required = is_field_required?(attribute, options)

{}.tap do |option|
Expand Down
40 changes: 35 additions & 5 deletions lib/rails_bootstrap_form/field_wrapper_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,24 @@ def field_wrapper(attribute, bootstrap_options, options, &block)
label = label(attribute, bootstrap_options)
help_text = help_text(attribute, bootstrap_options)

tag.div(class: field_wrapper_classes) do
concat(label)
concat(capture(&block))
concat(help_text)
if bootstrap_options.floating
tag.div(class: field_wrapper_classes) do
concat(input_group_wrapper(attribute, bootstrap_options) do
tag.div(class: floating_label_classes(attribute)) do
concat(capture(&block))
concat(label)
end
end)
concat(help_text)
end
else
tag.div(class: field_wrapper_classes) do
concat(label)
concat(input_group_wrapper(attribute, bootstrap_options) do
capture(&block)
end)
concat(help_text)
end
end
end

Expand All @@ -42,12 +56,28 @@ def field_css_options(attribute, bootstrap_options, options, html_options)
bootstrap_options.field_class,
bootstrap_options.additional_field_class
]
field_classes << "is-invalid" if is_invalid?(attribute)

css_options[:class] = field_classes.flatten.compact

css_options.merge!(required_field_options(attribute, options))

if bootstrap_options.floating
css_options[:placeholder] ||= label_text(attribute, bootstrap_options)
end

css_options
end

def floating_label_classes(attribute)
classes = Array("form-floating")
# Floating label fields with input group requires `is-invalid` class in
# order to display error messages.
classes << "is-invalid" if is_invalid?(attribute)
classes
end

private :field_wrapper, :field_wrapper_classes, :form_wrapper_default_class,
:field_css_options
:field_css_options, :floating_label_classes
end
end
49 changes: 49 additions & 0 deletions lib/rails_bootstrap_form/input_group_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- encoding: utf-8 -*-
# -*- frozen_string_literal: true -*-
# -*- warn_indent: true -*-

module RailsBootstrapForm
module InputGroupBuilder
extend ActiveSupport::Concern

def self.included(base_class)
def input_group_wrapper(attribute, bootstrap_options, &block)
input = capture(&block) || ActiveSupport::SafeBuffer.new

prepend = attach_input(bootstrap_options, :prepend)
append = attach_input(bootstrap_options, :append)

input = prepend + input + append
input += generate_error(attribute)

input = tag.div(input, class: input_group_classes(attribute, bootstrap_options))

input
end

def input_group_classes(attribute, bootstrap_options)
classes = ["input-group", bootstrap_options.additional_input_group_class]
# Require `has-validation` class if field has errors.
classes << "has-validation" if is_invalid?(attribute)
classes.flatten.compact
end

def attach_input(bootstrap_options, key)
tags = [*bootstrap_options.send(key)].map do |item|
input_group_content(item)
end

ActiveSupport::SafeBuffer.new(tags.join)
end

def input_group_content(content)
return content if /button|submit/.match?(content)

tag.span(content.html_safe, class: "input-group-text")
end

private :input_group_wrapper, :input_group_classes, :attach_input,
:input_group_content
end
end
end
2 changes: 1 addition & 1 deletion lib/rails_bootstrap_form/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
# -*- warn_indent: true -*-

module RailsBootstrapForm
VERSION = "0.2.3".freeze
VERSION = "0.3.0".freeze
REQUIRED_RAILS_VERSION = "~> 7.0".freeze
end
2 changes: 1 addition & 1 deletion spec/rails_bootstrap_form_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

RSpec.describe RailsBootstrapForm do
it "has a valid version number" do
expect(RailsBootstrapForm::VERSION).to eq("0.2.3")
expect(RailsBootstrapForm::VERSION).to eq("0.3.0")
end

it "has a valid rails version number" do
Expand Down