diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 000000000..7cc801f7c --- /dev/null +++ b/.github/workflows/ruby.yml @@ -0,0 +1,46 @@ +name: Ruby + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + Lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7.2 + bundler-cache: true + - name: Danger + if: ${{ github.event_name == 'pull_request' }} + run: | + gem install danger + export DANGER_GITHUB_API_TOKEN=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 + danger + - name: Rubocop + run: bundle exec rubocop --auto-correct + Test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby-version: [ '3.0', '2.7', '2.6' ] + gemfile: [ '6.1', '6.0', '5.2', 'edge' ] + exclude: + - { ruby-version: '3.0', gemfile: "5.2" } + - { ruby-version: '2.6', gemfile: "edge" } + env: + BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true + - name: Run tests + run: bundle exec rake test diff --git a/.gitignore b/.gitignore index e7bb8f752..7c02cff54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .bundle/ +.idea log/*.log pkg/ demo/db/*.sqlite3 @@ -13,7 +14,7 @@ test/gemfiles/*.lock Vagrantfile .vagrant -// For the demo app. +# For the demo app. # Ignore uploaded files in development. demo/storage/* @@ -30,3 +31,17 @@ demo/node_modules demo/yarn-error.log demo/yarn-debug.log* demo/.yarn-integrity + +# For stuff that gets created if using the Dockerfile image +.bundle/ +.cache/ +vendor/bundle + +# or .local/share/pry/pry_history if you need to be more exact +.local/ +.irb_history +.byebug_history +# For Debian images with Bash +.bash_history +# For Alpine images +.ash_history diff --git a/.rubocop.yml b/.rubocop.yml index 88cc286dc..213e69d98 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,3 @@ -# Taken from: https://github.com/mattbrictson/rails-template/blob/master/rubocop.yml.tt -# Modified for demo app in `demo/` directory. require: - rubocop-performance - rubocop-rails @@ -8,23 +6,32 @@ AllCops: DisplayCopNames: true DisplayStyleGuide: true TargetRubyVersion: 2.5 + NewCops: enable Exclude: - - "bin/*" + - bin/* - Capfile - demo/bin/* - - "demo/bower_components/**/*" + - demo/bower_components/**/* - demo/config/boot.rb - demo/config/environment.rb - demo/config/initializers/version.rb - demo/db/schema.rb - - "demo/node_modules/**/*" + - demo/node_modules/**/* - demo/Rakefile - - "demo/tmp/**/*" - - "demo/vendor/**/*" + - demo/tmp/**/* + - demo/vendor/**/* - Gemfile - gemfiles/vendor/bundle/**/* + - vendor/bundle/**/* - Guardfile - Rakefile + - vendor/**/* + +Layout/LineLength: + Max: 132 + Exclude: + - "demo/config/**/*" + - "demo/db/**/*" Layout/SpaceAroundEqualsInParameterDefault: EnforcedStyle: no_space @@ -45,12 +52,6 @@ Metrics/ClassLength: - "demo/test/**/*" - "test/**/*" -Metrics/LineLength: - Max: 132 - Exclude: - - "demo/config/**/*" - - "demo/db/**/*" - Metrics/MethodLength: Max: 12 Exclude: diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6a9867a25..000000000 --- a/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -language: ruby -rvm: - - 2.7.1 - - 2.6.5 - - 2.5.7 -gemfile: - - gemfiles/6.0.gemfile - - gemfiles/5.2.gemfile -cache: - bundler: true -script: - - bundle exec rake test - -matrix: - include: - # Bleeding edge Ruby and Rails - - rvm: ruby-head - gemfile: gemfiles/edge.gemfile - - # Next version of Rails - - rvm: 2.7.1 - gemfile: gemfiles/edge.gemfile - - # Running one job to execute DANGER bot and linting - - rvm: 2.7.1 - gemfile: gemfiles/6.0.gemfile - script: - - gem install danger - - danger - - bundle exec rake rubocop - - allow_failures: - - rvm: ruby-head - - rvm: 2.7.1 - gemfile: gemfiles/edge.gemfile diff --git a/CHANGELOG.md b/CHANGELOG.md index fe0dff508..faf90194f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,28 @@ ### New features -* Your contribution here! +* [#572](https://github.com/bootstrap-ruby/bootstrap_form/issues/572): Simplify the formatting of the file upload control to follow the new Bootstrap 5 styles +* [#573](https://github.com/bootstrap-ruby/bootstrap_form/issues/573): Add support for Bootstrap 5's floating labels ### Bugfixes +* [#582](https://github.com/bootstrap-ruby/bootstrap_form/issues/582): Fix tests in bootstrap-5 branch, removes Rubocop offenses, and adds testing with Rails 6.1. + +## [5.0.0.alpha1][] + +### Breaking changes + +* [#569] Remove `role="form"` from the default generated form HTML so forms pass W3C validation. (Only a breaking change if you depended on the `form` attribute. `bootstrap_form` doesn't depend on it.) + +### New features + * Your contribution here! +### Bugfixes + +* [#586](https://github.com/bootstrap-ruby/bootstrap_form/pull/586): Fix Rails 6.1 tests on master - [@thimo](https://github.com/thimo). +* [#587](https://github.com/bootstrap-ruby/bootstrap_form/pull/587): Replace `strip_heredoc` with `<<~` - [@thimo](https://github.com/thimo). + ## [4.5.0][] (2020-04-29) ### New features diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4610a54de..46a4b29c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ There are a number of ways you can contribute to `bootstrap_form`: *Note:* If you want to work on preparing `bootstrap_form` for Bootstrap 5, please start from the `bootstrap-5` branch. If you're submitting a pull request with code or documentation, -say that you want to merge your branch to the `bootstrap-5` branch. +target the pull request to the `bootstrap-5` branch. ## Code Contributions @@ -63,7 +63,7 @@ You may find the demo application useful for development and debugging. ### 7. Done! Somebody will shortly review your pull request and if everything is good, it will be -merged into the master branch. Eventually the gem will be published with your changes. +merged into the main branch. Eventually the gem will be published with your changes. ### Coding guidelines @@ -82,6 +82,58 @@ The goal of `bootstrap_form` is to support all versions of Rails currently suppo The Ruby on Rails support policy is [here](https://guides.rubyonrails.org/maintenance_policy.html). +### Developing with Docker + +This repository includes a `Dockerfile` to build an image with the minimum `bootstrap_form`-supported Ruby environment. To build the image: + +```bash +docker build --tag bootstrap_form . +``` + +This builds an image called `bootstrap_form`. You can change that to any tag you wish. Just make sure you use the same tag name in the `docker run` command. + +If you want to use a different Ruby version, or a smaller Linux distribution (although the distro may be missing tools you need): + +```bash +docker build --build-arg "RUBY_VERSION=2.7" --build-arg "DISTRO=slim-buster" --tag bootstrap_form . +``` + +Then run the container you built with the shell, and create the bundle: + +```bash +docker run --volume "$PWD:/app" --user $UID:`grep ^$USERNAME /etc/passwd | cut -d: -f4` -it bootstrap_form /bin/bash +bundle install +``` + +You can run tests in the container as normal, with `rake test`. + +(Some of that command line is need for Linux hosts, to run the container as the current user.) + +### The Demo App + +There is a demo app in this repository. It shows some of the features of `bootstrap_form`, and provides a base on which to build ad-hoc testing, if you need it. + +To run the demo app, set up the database and run the server: + +```bash +cd demo +export BUNDLER_GEMFILE=../gemfiles/6.1.gemfile +rails db:setup +rails s -b 0.0.0.0 +``` + +To run the demo app in the Docker container: + +```bash +docker run --volume "$PWD:/app" --user $UID:`grep ^$USERNAME /etc/passwd | cut -d: -f4` -p 3000:3000 -it bootstrap_form /bin/bash +cd demo +export BUNDLER_GEMFILE=../gemfiles/6.1.gemfile +rails db:setup +rails s -b 0.0.0.0 +``` + +To use other supported versions of Rails, change the `export BUNDLER_GEMFILE...` line to another gem file. + ## Documentation Contributions Contributions to documentation are always welcome. Even fixing one typo improves the quality of `bootstrap_form`. To make a documentation contribution, follow steps 1-3 of Code Contributions, then make the documentation changes, then make the pull request (step 6 of Code Contributions). diff --git a/Dangerfile b/Dangerfile index 121167be4..bd985496c 100644 --- a/Dangerfile +++ b/Dangerfile @@ -44,11 +44,9 @@ end # ------------------------------------------------------------------------------ # Did you remove the CHANGELOG's "Your contribution here!" line? # ------------------------------------------------------------------------------ -if has_changelog_changes - if IO.read("CHANGELOG.md").scan(/^\s*[-\*] Your contribution here/i).count < 3 - raise( - "Please put the `- Your contribution here!` line back into CHANGELOG.md.", - sticky: false - ) - end +if has_changelog_changes && IO.read("CHANGELOG.md").scan(/^\s*[-*] Your contribution here/i).count < 3 + raise( + "Please put the `- Your contribution here!` line back into CHANGELOG.md.", + sticky: false + ) end diff --git a/Dockerfile b/Dockerfile index 617ed8496..ba0915ec6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,59 +1,26 @@ -# From: https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development -ARG RUBY_VERSION -# See explanation below -FROM ruby:$RUBY_VERSION-slim-buster +ARG DISTRO=buster +ARG RUBY_VERSION=2.6 -ARG NODE_MAJOR -ARG BUNDLER_VERSION -ARG YARN_VERSION +FROM ruby:$RUBY_VERSION-$DISTRO -# Common dependencies -RUN apt-get update -qq \ - && DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ - build-essential \ - gnupg2 \ - curl \ - less \ - git \ - sqlite3 \ - libsqlite3-dev \ - && apt-get clean \ - && rm -rf /var/cache/apt/archives/* \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \ - && truncate -s 0 /var/log/*log - -# Add NodeJS to sources list -RUN curl -sL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash - - -# Add Yarn to the sources list -RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo 'deb http://dl.yarnpkg.com/debian/ stable main' > /etc/apt/sources.list.d/yarn.list - -# Application dependencies -RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \ - DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ - nodejs \ - yarn=$YARN_VERSION-1 && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ - truncate -s 0 /var/log/*log - -# Configure bundler -ENV LANG=C.UTF-8 \ - BUNDLE_JOBS=4 \ - BUNDLE_RETRY=3 - -# Uncomment this line if you store Bundler settings in the project's root -# ENV BUNDLE_APP_CONFIG=.bundle - -# Uncomment this line if you want to run binstubs without prefixing with `bin/` or `bundle exec` -# ENV PATH /app/bin:$PATH - -# Upgrade RubyGems and install required Bundler version -RUN gem update --system && \ - gem install bundler:$BUNDLER_VERSION - -# Create a directory for the app code RUN mkdir -p /app - -WORKDIR /app \ No newline at end of file +ENV HOME /app +WORKDIR /app + +ENV GEM_HOME $HOME/vendor/bundle +ENV BUNDLE_APP_CONFIG="$GEM_HOME" +ENV PATH ./bin:$GEM_HOME/bin:$PATH +RUN (echo 'docker'; echo 'docker') | passwd root + +# Yarn installs nodejs. +RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ + echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ + apt update -y -q && \ + apt install -y -q yarn sqlite3 + +# Ruby now comes with bundler, but we're not using the default version yet, because we were using +# a newer version of bundler already. Ruby 3 comes with Bundler 2.2.3. Ruby 2.7 has Bundler 2.1.2, +# which is still behind what we were using. +RUN gem install bundler -v 2.1.4 + +EXPOSE 3000 diff --git a/Gemfile b/Gemfile index 625bb099f..793f35dfb 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,7 @@ gemspec path: __dir__ # Uncomment and change rails version for testing purposes # gem "rails", "~> 5.2.0" # gem "rails", "~> 6.0.0" +# gem "rails", "~> 6.1.0" # gem "rails", git: "https://github.com/rails/rails.git" group :development do @@ -17,7 +18,6 @@ group :development do end group :test do - # can relax version requirement for Rails 5.2.beta3+ gem "diffy" gem "equivalent-xml" gem "mocha" diff --git a/README.md b/README.md index dec13828d..4faa0a781 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,9 @@ -If you are using Bootstrap v3, refer to the legacy [legacy-2.7](https://github.com/bootstrap-ruby/bootstrap_form/tree/legacy-2.7) branch. - -This is a new take on the `bootstrap_form` README. Please leave comments at: #520. You can go back to the traditional [README](/OLD-README.md). - ---- - # bootstrap_form -[![Build Status](https://travis-ci.org/bootstrap-ruby/bootstrap_form.svg?branch=master)](https://travis-ci.org/bootstrap-ruby/bootstrap_form) +[![Ruby](https://github.com/bootstrap-ruby/bootstrap_form/actions/workflows/ruby.yml/badge.svg)](https://github.com/bootstrap-ruby/bootstrap_form/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/bootstrap_form.svg)](https://rubygems.org/gems/bootstrap_form) -`bootstrap_form` is a Rails form builder that makes it super easy to integrate Bootstrap v4-style forms into your Rails application. It provides form helpers that augment the Rails form helpers. `bootstrap_forms`'s form helpers generate the form field and its label and all the Bootstrap mark-up required for proper Bootstrap display. `bootstrap_form` also provides: +`bootstrap_form` is a Rails form builder that makes it super easy to integrate Bootstrap v5-style forms into your Rails application. It provides form helpers that augment the Rails form helpers. `bootstrap_forms`'s form helpers generate the form field and its label and all the Bootstrap mark-up required for proper Bootstrap display. `bootstrap_form` also provides: * [Validation error messages](#validation-and-errors) below the field they correspond to, by default. You can also put the error messages after the label, or turn off `bootstrap_form`'s validation error handling and do it yourself. _Note that this applies to Rails-generated validation messages._ HTML 5 client-side validation and Rails validation out of the box don't really work well together. One discussion of the challenges and some solutions is [here](https://www.jorgemanrubia.com/2019/02/16/form-validations-with-html5-and-modern-rails/) * Automatic [mark-up for the `required` attribute](#required-fields) on required fields. @@ -33,14 +27,14 @@ Some other nice things that `bootstrap_form` does for you are: * Ruby 2.5+ * Rails 5.2+ -* Bootstrap 4.0+ +* Bootstrap 5.0+ ## Installation Add it to your Gemfile: ```ruby -gem "bootstrap_form", "~> 4.0" +gem "bootstrap_form", "~> 5.0" ``` Then: @@ -79,12 +73,12 @@ This generates the following HTML: ```html
-
- +
+
-
- +
+
@@ -125,14 +119,14 @@ To get started, just use the `bootstrap_form_with` helper in place of `form_with This generates: ```html - + -
- +
+
-
- +
+ A good password should be at least six characters long
@@ -150,7 +144,6 @@ in `form_with`. `form_with` has some important differences compared to `form_for` and `form_tag`, and these differences apply to `bootstrap_form_with`. A good summary of the differences can be found at: https://m.patrikonrails.com/rails-5-1s-form-with-vs-old-form-helpers-3a5f72a8c78a, or in the [Rails documentation](api.rubyonrails.org). - ## Configuration `bootstrap_form` can be used out-of-the-box without any configuration. However, `bootstrap_form` does have an optional configuration file at `config/initializers/bootstrap_form.rb` for setting options that affect all generated forms in an application. @@ -159,14 +152,14 @@ The current configuration options are: | Option | Default value | Description | |---------------------------|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `default_form_attributes` | `{role: "form"}` | version [2.2.0](https://github.com/bootstrap-ruby/bootstrap_form/blob/master/CHANGELOG.md#220-2014-09-16) added a role="form" attribute to all forms. The W3C validator will raise a **warning** on forms with a role="form" attribute. Set this option to `{}` to make forms be W3C compliant. | - +| `default_form_attributes` | | `bootstrap_form` versions 3 and 4 added a role="form" attribute to all forms. The W3C validator will raise a **warning** on forms with a role="form" attribute. `bootstrap_form` version 5 drops this attribute by default. Set this option to `{ role: "form" }` to make forms non-compliant with W3C, but generate the `role="form"` attribute like `bootstrap_form` versions 3 and 4. | Example: + ```ruby # config/initializers/bootstrap_form.rb BootstrapForm.configure do |c| - c.default_form_attributes = {} # to make forms W3C compliant + c.default_form_attributes = { role: "form" } # to make forms non-compliant with W3C. end ``` @@ -319,7 +312,7 @@ To add a class to the input group wrapper, use the `:input_group_class` option. ### Additional Form Group Attributes -Bootstrap mark-up dictates that most input field types have the label and input wrapped in a `div.form-group`. +Bootstrap mark-up dictates that most input field types have the label and input wrapped in a `div.mb-3`. If you want to add an additional CSS class or any other attribute to the form group div, you can use the `wrapper: { class: 'additional-class', data: { foo: 'bar' } }` option. @@ -330,8 +323,8 @@ If you want to add an additional CSS class or any other attribute to the form gr Which produces the following output: ```erb -
- +
+
``` @@ -404,10 +397,10 @@ Check boxes and radio buttons are wrapped in a `div.form-check`. You can add cla ### Switches -To render checkboxes as switches with Bootstrap 4.2+, use `custom: :switch`: +To render checkboxes as switches with Bootstrap 4.2+, use `switch: true`: ```erb -<%= f.check_box :remember_me, custom: :switch %> +<%= f.check_box :remember_me, switch: true %> ``` ### Collections @@ -440,8 +433,8 @@ You can create a static control like this: Here's the output for a horizontal layout: ```html -
- +
+
@@ -553,8 +546,8 @@ If you're using Rails 6, `bootstrap_form` supports the `rich_text_area` helper. will be rendered as: ```html -
- +
+ @@ -700,6 +693,21 @@ The `custom` option can be used to replace the browser default styles for check <% end %> ``` +### Floating Labels + +The `floating` option can be used to enable Bootstrap 5's floating labels. This option is supported on text fields +and dropdowns. Here's an example: + +```erb +<%= bootstrap_form_for(@user) do |f| %> + <%= f.email_field :email, floating: true %> + <%= f.password_field :password, floating: true %> + <%= f.password_field :password, floating: true %> + <%= f.select :status, [["Active", 1], ["Inactive", 2]], include_blank: "Select a value", floating: true %> + <%= f.submit "Log In" %> +<% end %> +``` + ## Validation and Errors Rails normally wraps fields with validation errors in a `div.field_with_errors`, but this behaviour isn't consistent with Bootstrap 4 styling. By default, `bootstrap_form` generations in-line errors which appear below the field. But it can also generate errors on the label, or not display any errors, leaving it up to you. @@ -710,8 +718,8 @@ By default, fields that have validation errors will be outlined in red and the error will be displayed below the field. Here's an example: ```html -
- +
+ can't be blank
@@ -788,8 +796,7 @@ Which outputs: ### Errors On -If you want to display a custom inline error for a specific attribute not -represented by a form field, use the `errors_on` helper. +If you want to display a custom inline error for a specific attribute not represented by a form field, use the `errors_on` helper. ```erb <%= f.errors_on :tasks %> @@ -798,7 +805,7 @@ represented by a form field, use the `errors_on` helper. Which outputs: ```html -
Tasks can't be blank.
+
Tasks can't be blank.
``` You can hide the attribute name like this: @@ -810,7 +817,19 @@ You can hide the attribute name like this: Which outputs: ```html -
can't be blank.
+
can't be blank.
+``` + +You can also use a custom class for the wrapping div, like this: + +```erb +<%= f.errors_on :tasks, custom_class: 'custom-error' %> +``` + +Which outputs: + +```html +
can't be blank.
``` ## Required Fields @@ -874,6 +893,10 @@ If you're considering contributing to bootstrap_form, please review the [Contributing](/CONTRIBUTING.md) document first. +## Previous Version + +If you're looking for `bootstrap_form` for Bootstrap 4, go [here](https://github.com/bootstrap-ruby/bootstrap_form/tree/bootstrap-4). + ## License -MIT License. Copyright 2012-2020 Stephen Potenza (https://github.com/potenza) and others +MIT License. Copyright 2012-2021 Stephen Potenza (https://github.com/potenza) and others diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md new file mode 100644 index 000000000..ddc937df4 --- /dev/null +++ b/UPGRADE-5.0.md @@ -0,0 +1,25 @@ +# Upgrading to `bootstrap_form` 5.0 + +We made every effort to make the upgrade from `bootstrap_form` v4 (Bootstrap 4) to `bootstrap_form` 5.0 (Bootstrap 5) as easy as possible. However, Bootstrap 5 is fundamentally different from Bootstrap 4, so some changes may be necessary in your code. + +## Bootstrap 5 Changes + +Upgrading `bootstrap_form` to version 5 means you must upgrade your whole application to Bootstrap 5. Read the [Bootstrap 5 migration guide](https://v5.getbootstrap.com/docs/5.0/migration/) to see what changes you have to make to your views. This will also help you understand changes you might have to make to your `bootstrap_form` code. + +## `bootstrap_form` Version 5 Changes + +## No `role="form"` Attribute + +As explained in #560, the `role="form"` attribute generated by `bootstrap_4` caused the W3C validator to output a warning. The `role="form"` attribute was deprecated in the 4.5.0 and is being remove completely in 5.0.0. This has no impact on `bootstrap_form` code itself, but may affect your application if it depended on a form having this attribute set. (Issue #569) + +## Different behavior for `errors_on` helper + +The `errors_on` helper now wraps the error message in a CSS class `invalid-feedback`, instead of `alert` and `alert-danger`, as before. + +This will display the error as any other [Bootstrap inline form error](https://getbootstrap.com/docs/5.0/forms/validation/#server-side), instead of displaying it as an [Bootstrap alert](https://getbootstrap.com/docs/5.0/components/alerts/). + +You can use the `custom_class` options for this helper with `alert alert-danger` to restore the old behaviour: + +```erb +<%= f.errors_on :tasks, custom_class: 'alert alert-danger' %> +``` diff --git a/bootstrap_form.gemspec b/bootstrap_form.gemspec index 7f02f0bc1..522ef11cb 100644 --- a/bootstrap_form.gemspec +++ b/bootstrap_form.gemspec @@ -3,6 +3,8 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "bootstrap_form/version" +REQUIRED_RAILS_VERSION = ">= 5.2".freeze + Gem::Specification.new do |s| s.name = "bootstrap_form" s.version = BootstrapForm::VERSION @@ -10,13 +12,11 @@ Gem::Specification.new do |s| s.email = ["potenza@gmail.com", "carlos.el.lopes@gmail.com"] s.homepage = "https://github.com/bootstrap-ruby/bootstrap_form" s.summary = "Rails form builder that makes it easy to style forms using "\ - "Bootstrap 4" + "Bootstrap 5" s.description = "bootstrap_form is a rails form builder that makes it super "\ - "easy to create beautiful-looking forms using Bootstrap 4" + "easy to create beautiful-looking forms using Bootstrap 5" s.license = "MIT" - s.post_install_message = "Default form attribute role=\"form\" will be dropped in 5.0.0" - s.files = `git ls-files -z`.split("\x0").reject do |f| f.match(%r{^(test)/}) end @@ -26,8 +26,6 @@ Gem::Specification.new do |s| s.required_ruby_version = ">= 2.5" - REQUIRED_RAILS_VERSION = ">= 5.2".freeze - s.add_dependency("actionpack", REQUIRED_RAILS_VERSION) s.add_dependency("activemodel", REQUIRED_RAILS_VERSION) diff --git a/demo/app/assets/config/manifest.js b/demo/app/assets/config/manifest.js index 591819335..6b1bec7fb 100644 --- a/demo/app/assets/config/manifest.js +++ b/demo/app/assets/config/manifest.js @@ -1,2 +1 @@ -//= link_tree ../images -//= link_directory ../stylesheets .css +//= link application.css diff --git a/demo/app/views/bootstrap/form.html.erb b/demo/app/views/bootstrap/form.html.erb index 4778e3045..1ae3f8676 100644 --- a/demo/app/views/bootstrap/form.html.erb +++ b/demo/app/views/bootstrap/form.html.erb @@ -52,3 +52,16 @@ <%= form.submit %> <% end %> <% end %> + +

Floating Labels

+ +<%= form_with_source do %> + <%= bootstrap_form_for @user do |form| %> + <%= form.email_field :email, placeholder: "Enter Email", label: "Email address", help: "We'll never share your email with anyone else", floating: true %> + <%= form.password_field :password, placeholder: "Password", floating: true %> + <%= form.text_field :misc, floating: true %> + <%= form.text_area :comments, floating: true %> + <%= form.select :status, [["Active", 1], ["Inactive", 2]], include_blank: "Select a value", floating: true %> + <%= form.submit %> + <% end %> +<% end %> diff --git a/demo/app/views/layouts/application.html.erb b/demo/app/views/layouts/application.html.erb index 9712b54ca..65d529861 100644 --- a/demo/app/views/layouts/application.html.erb +++ b/demo/app/views/layouts/application.html.erb @@ -6,7 +6,7 @@ - + @@ -44,8 +44,8 @@ - - + + diff --git a/demo/config/puma.rb b/demo/config/puma.rb index 1e19380dc..44256ac09 100644 --- a/demo/config/puma.rb +++ b/demo/config/puma.rb @@ -4,12 +4,12 @@ # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } +threads_count = ENV.fetch("RAILS_MAX_THREADS", 5) threads threads_count, threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. # -port ENV.fetch("PORT") { 3000 } +port ENV.fetch("PORT", 3000) # Specifies the `environment` that Puma will run in. # diff --git a/demo/db/schema.rb b/demo/db/schema.rb index 8054b51b6..835314fcc 100644 --- a/demo/db/schema.rb +++ b/demo/db/schema.rb @@ -2,8 +2,8 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# This file is the source Rails uses to define your schema when running `rails -# db:schema:load`. When creating a new database, `rails db:schema:load` tends to +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to # be faster and is potentially less error prone than running all of your # migrations from scratch. Old migrations may fail to apply correctly if those # migrations use external dependencies or application code. diff --git a/gemfiles/6.1.gemfile b/gemfiles/6.1.gemfile new file mode 100644 index 000000000..a1e06796e --- /dev/null +++ b/gemfiles/6.1.gemfile @@ -0,0 +1,4 @@ +gems = "#{File.dirname __dir__}/Gemfile" +eval File.read(gems), binding, gems # rubocop: disable Security/Eval + +gem "rails", "~> 6.1.0" diff --git a/lib/bootstrap_form.rb b/lib/bootstrap_form.rb index 0de892db9..a50d077bd 100644 --- a/lib/bootstrap_form.rb +++ b/lib/bootstrap_form.rb @@ -2,8 +2,7 @@ # name and not in the usual autoload-reachable way. # The following line is definitely need to make `bootstrap_form` work. if ::Rails::VERSION::STRING > "6" - require Gem::Specification.find_by_name("actiontext").gem_dir + - "/app/helpers/action_text/tag_helper" + require "#{Gem::Specification.find_by_name('actiontext').gem_dir}/app/helpers/action_text/tag_helper" end require "action_view" require "action_pack" diff --git a/lib/bootstrap_form/components/labels.rb b/lib/bootstrap_form/components/labels.rb index dd213600f..a209ce143 100644 --- a/lib/bootstrap_form/components/labels.rb +++ b/lib/bootstrap_form/components/labels.rb @@ -23,7 +23,7 @@ def generate_label(id, name, options, custom_label_col, group_layout) end def label_classes(name, options, custom_label_col, group_layout) - classes = [options[:class], label_layout_classes(custom_label_col, group_layout)] + classes = ["form-label", options[:class], label_layout_classes(custom_label_col, group_layout)] case options.delete(:required) when true diff --git a/lib/bootstrap_form/components/validation.rb b/lib/bootstrap_form/components/validation.rb index 1c7facfc4..215d8b76f 100644 --- a/lib/bootstrap_form/components/validation.rb +++ b/lib/bootstrap_form/components/validation.rb @@ -14,7 +14,7 @@ def error?(name) def required_attribute?(obj, attribute) return false unless obj && attribute - target = obj.class == Class ? obj : obj.class + target = obj.instance_of?(Class) ? obj : obj.class target_validators = if target.respond_to? :validators_on target.validators_on(attribute).map(&:class) diff --git a/lib/bootstrap_form/configuration.rb b/lib/bootstrap_form/configuration.rb index 9b010b081..43c4bb3dc 100644 --- a/lib/bootstrap_form/configuration.rb +++ b/lib/bootstrap_form/configuration.rb @@ -16,8 +16,7 @@ def default_form_attributes=(attributes) def default_form_attributes return @default_form_attributes if defined? @default_form_attributes - # TODO: Return blank hash ({}) in 5.0.0. Role "form" for form tags is redundant and makes W3C to raise a warning. - { role: "form" } + {} end end end diff --git a/lib/bootstrap_form/form_builder.rb b/lib/bootstrap_form/form_builder.rb index 429802c78..305bd7d72 100644 --- a/lib/bootstrap_form/form_builder.rb +++ b/lib/bootstrap_form/form_builder.rb @@ -45,7 +45,6 @@ class FormBuilder < ActionView::Helpers::FormBuilder delegate :content_tag, :capture, :concat, :tag, to: :@template - # rubocop:disable Metrics/AbcSize def initialize(object_name, object, template, options) @layout = options[:layout] || default_layout @label_col = options[:label_col] || default_label_col @@ -61,7 +60,6 @@ def initialize(object_name, object, template, options) add_default_form_attributes_and_form_inline options super end - # rubocop:enable Metrics/AbcSize def add_default_form_attributes_and_form_inline(options) options[:html] ||= {} @@ -69,7 +67,7 @@ def add_default_form_attributes_and_form_inline(options) return unless options[:layout] == :inline - options[:html][:class] = [options[:html][:class], "form-inline"].compact.join(" ") + options[:html][:class] = [options[:html][:class], "col-auto", "g-3"].compact.join(" ") end def fields_for_with_bootstrap(record_name, record_object=nil, fields_options={}, &block) diff --git a/lib/bootstrap_form/form_group.rb b/lib/bootstrap_form/form_group.rb index b611323b1..783acd120 100644 --- a/lib/bootstrap_form/form_group.rb +++ b/lib/bootstrap_form/form_group.rb @@ -10,9 +10,9 @@ def form_group(*args, &block) options[:class] = form_group_classes(options) - tag.div(options.except(:append, :id, :label, :help, :icon, - :input_group_class, :label_col, :control_col, - :add_control_col_class, :layout, :prepend)) do + tag.div(**options.except(:append, :id, :label, :help, :icon, + :input_group_class, :label_col, :control_col, + :add_control_col_class, :layout, :prepend, :floating)) do form_group_content( generate_label(options[:id], name, options[:label], options[:label_col], options[:layout]), generate_help(name, options[:help]), options, &block @@ -24,7 +24,7 @@ def form_group(*args, &block) def form_group_content_tag(name, field_name, without_field_name, options, html_options) html_class = control_specific_class(field_name) - html_class = "#{html_class} form-inline" if @layout == :horizontal && options[:skip_inline].blank? + 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 send(without_field_name, name, options, html_options) @@ -36,8 +36,10 @@ def form_group_content(label, help_text, options, &block) if group_layout_horizontal?(options[:layout]) concat(label).concat(tag.div(capture(&block) + help_text, class: form_group_control_class(options))) else - concat(label) + # Floating labels need to be rendered after the field + concat(label) unless options[:floating] concat(capture(&block)) + concat(label) if options[:floating] concat(help_text) if help_text end end @@ -50,15 +52,24 @@ def form_group_control_class(options) end def form_group_classes(options) - classes = ["form-group", options[:class].try(:split)].flatten.compact - classes << "row" if group_layout_horizontal?(options[:layout]) && classes.exclude?("form-row") - classes << "form-inline" if field_inline_override?(options[:layout]) + classes = ["mb-3", options[:class].try(:split)].flatten.compact + classes << "row" if horizontal_group_with_gutters?(options[:layout], classes) + classes << "col-auto g-3" if field_inline_override?(options[:layout]) classes << feedback_class if options[:icon] + classes << "form-floating" if options[:floating] classes end + def horizontal_group_with_gutters?(layout, classes) + group_layout_horizontal?(layout) && !classes_include_gutters?(classes) + end + def group_layout_horizontal?(layout) get_group_layout(layout) == :horizontal end + + def classes_include_gutters?(classes) + classes.any? { |c| c =~ /^g-\d+$/ } + end end end diff --git a/lib/bootstrap_form/form_group_builder.rb b/lib/bootstrap_form/form_group_builder.rb index 4407266c0..6bd733e7d 100644 --- a/lib/bootstrap_form/form_group_builder.rb +++ b/lib/bootstrap_form/form_group_builder.rb @@ -6,7 +6,7 @@ module FormGroupBuilder private - def form_group_builder(method, options, html_options=nil) + def form_group_builder(method, options, html_options=nil, &block) no_wrapper = options[:wrapper] == false options = form_group_builder_options(options, method) @@ -21,16 +21,14 @@ def form_group_builder(method, options, html_options=nil) if no_wrapper yield else - form_group(method, form_group_options) { yield } + form_group(method, form_group_options, &block) end end def form_group_builder_options(options, method) options.symbolize_keys! options = convert_form_tag_options(method, options) if acts_like_form_tag - unless options[:skip_label] - options[:required] = form_group_required(options) if options.key?(:skip_required) - end + options[:required] = form_group_required(options) if !options[:skip_label] && options.key?(:skip_required) options end @@ -48,7 +46,8 @@ def form_group_opts(options, css_options) id: options[:id], help: options[:help], icon: options[:icon], label_col: options[:label_col], control_col: options[:control_col], add_control_col_class: options[:add_control_col_class], - layout: get_group_layout(options[:layout]), class: options[:wrapper_class] + layout: get_group_layout(options[:layout]), class: options[:wrapper_class], + floating: options[:floating] } form_group_options.merge!(wrapper_options) if wrapper_options.is_a?(Hash) @@ -57,12 +56,11 @@ def form_group_opts(options, css_options) end def form_group_label(options, css_options) - hash = { + { text: form_group_label_text(options[:label]), class: form_group_label_class(options), required: options[:required] }.merge(css_options[:id].present? ? { for: css_options[:id] } : {}) - hash end def form_group_label_text(label) diff --git a/lib/bootstrap_form/helpers/bootstrap.rb b/lib/bootstrap_form/helpers/bootstrap.rb index 3e5554632..8c11616ff 100644 --- a/lib/bootstrap_form/helpers/bootstrap.rb +++ b/lib/bootstrap_form/helpers/bootstrap.rb @@ -14,7 +14,7 @@ def submit(name=nil, options={}) def primary(name=nil, options={}, &block) setup_css_class "btn btn-primary", options - if options[:render_as_button] || block_given? + if options[:render_as_button] || block options.except! :render_as_button button(name, options, &block) else @@ -22,7 +22,7 @@ def primary(name=nil, options={}, &block) end end - def alert_message(title, options={}) # rubocop:disable Metrics/AbcSize + def alert_message(title, options={}) css = options[:class] || "alert alert-danger" return unless object.respond_to?(:errors) && object.errors.full_messages.any? @@ -46,8 +46,9 @@ def errors_on(name, options={}) return unless error?(name) hide_attribute_name = options[:hide_attribute_name] || false + custom_class = options[:custom_class] || false - tag.div class: "alert alert-danger" do + tag.div class: custom_class || "invalid-feedback" do if hide_attribute_name object.errors[name].join(", ") else @@ -108,7 +109,7 @@ def static_class def attach_input(options, key) tags = [*options[key]].map do |item| - tag.div(input_group_content(item), class: "input-group-#{key}") + input_group_content(item) end ActiveSupport::SafeBuffer.new(tags.join) end diff --git a/lib/bootstrap_form/inputs/base.rb b/lib/bootstrap_form/inputs/base.rb index d84c4e9b3..a8855c229 100644 --- a/lib/bootstrap_form/inputs/base.rb +++ b/lib/bootstrap_form/inputs/base.rb @@ -10,7 +10,8 @@ def bootstrap_field(field_name) define_method "#{field_name}_with_bootstrap" do |name, options={}| form_group_builder(name, options) do prepend_and_append_input(name, options) do - send("#{field_name}_without_bootstrap".to_sym, name, options) + options[:placeholder] ||= name if options[:floating] + send("#{field_name}_without_bootstrap".to_sym, name, options.except(:floating)) end end end @@ -19,11 +20,10 @@ def bootstrap_field(field_name) end def bootstrap_select_group(field_name) - with_field_name = "#{field_name}_with_bootstrap" - without_field_name = "#{field_name}_without_bootstrap" - define_method(with_field_name) do |name, options={}, html_options={}| + define_method("#{field_name}_with_bootstrap") do |name, options={}, html_options={}| + 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, without_field_name, options, html_options) + form_group_content_tag(name, field_name, "#{field_name}_without_bootstrap", options, html_options) end end diff --git a/lib/bootstrap_form/inputs/check_box.rb b/lib/bootstrap_form/inputs/check_box.rb index abfd44bfd..245ca7d69 100644 --- a/lib/bootstrap_form/inputs/check_box.rb +++ b/lib/bootstrap_form/inputs/check_box.rb @@ -10,7 +10,7 @@ module CheckBox def check_box_with_bootstrap(name, options={}, checked_value="1", unchecked_value="0", &block) options = options.symbolize_keys! check_box_options = options.except(:class, :label, :label_class, :error_message, :help, - :inline, :custom, :hide_label, :skip_label, :wrapper_class) + :inline, :hide_label, :skip_label, :wrapper_class, :switch) check_box_options[:class] = check_box_classes(name, options) tag.div(class: check_box_wrapper_class(options)) do @@ -38,7 +38,7 @@ def check_box_label(name, options, checked_value, &block) end def check_box_description(name, options, &block) - content = block_given? ? capture(&block) : options[:label] + content = block ? capture(&block) : options[:label] content || object&.class&.human_attribute_name(name) || name.to_s.humanize end @@ -50,40 +50,26 @@ def check_box_value(name, value) end def check_box_classes(name, options) - classes = [options[:class]] - classes << (options[:custom] ? "custom-control-input" : "form-check-input") + classes = Array(options[:class]) << "form-check-input" classes << "is-invalid" if error?(name) classes << "position-static" if options[:skip_label] || options[:hide_label] classes.flatten.compact end def check_box_label_class(options) - classes = [] - classes << (options[:custom] ? "custom-control-label" : "form-check-label") + classes = ["form-check-label"] classes << options[:label_class] classes << hide_class if options[:hide_label] classes.flatten.compact end def check_box_wrapper_class(options) - classes = [] - if options[:custom] - classes << custom_check_box_wrapper_class(options) - else - classes << "form-check" - classes << "form-check-inline" if layout_inline?(options[:inline]) - end + classes = ["form-check"] + classes << "form-check-inline" if layout_inline?(options[:inline]) + classes << "form-switch" if options[:switch] classes << options[:wrapper_class] if options[:wrapper_class].present? classes.flatten.compact end - - def custom_check_box_wrapper_class(options) - classes = [] - classes << "custom-control" - classes << (options[:custom] == :switch ? "custom-switch" : "custom-checkbox") - classes << "custom-control-inline" if layout_inline?(options[:inline]) - classes - end end end end diff --git a/lib/bootstrap_form/inputs/collection_select.rb b/lib/bootstrap_form/inputs/collection_select.rb index b5ac1472c..74f7ffd81 100644 --- a/lib/bootstrap_form/inputs/collection_select.rb +++ b/lib/bootstrap_form/inputs/collection_select.rb @@ -10,6 +10,7 @@ module CollectionSelect # Disabling Metrics/ParameterLists because the upstream Rails method has the same parameters # rubocop:disable Metrics/ParameterLists def collection_select_with_bootstrap(method, collection, value_method, text_method, 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 collection_select_without_bootstrap(method, collection, value_method, text_method, options, html_options) diff --git a/lib/bootstrap_form/inputs/file_field.rb b/lib/bootstrap_form/inputs/file_field.rb index 297a74140..92b195179 100644 --- a/lib/bootstrap_form/inputs/file_field.rb +++ b/lib/bootstrap_form/inputs/file_field.rb @@ -8,28 +8,16 @@ module FileField included do def file_field_with_bootstrap(name, options={}) - options = options.reverse_merge(control_class: "custom-file-input") + options = options.reverse_merge(control_class: "form-control") form_group_builder(name, options) do - tag.div(class: "custom-file") do - input_with_error(name) do - file_field_input(name, options) - end + input_with_error(name) do + file_field_without_bootstrap(name, options) end end end bootstrap_alias :file_field end - - private - - def file_field_input(name, options) - placeholder = options.delete(:placeholder) || "Choose file" - placeholder_opts = { class: "custom-file-label" } - placeholder_opts[:for] = options[:id] if options[:id].present? - - file_field_without_bootstrap(name, options) + label(name, placeholder, placeholder_opts) - end end end end diff --git a/lib/bootstrap_form/inputs/grouped_collection_select.rb b/lib/bootstrap_form/inputs/grouped_collection_select.rb index 7ed37d347..a62a9b864 100644 --- a/lib/bootstrap_form/inputs/grouped_collection_select.rb +++ b/lib/bootstrap_form/inputs/grouped_collection_select.rb @@ -12,6 +12,7 @@ module GroupedCollectionSelect def grouped_collection_select_with_bootstrap(method, collection, group_method, group_label_method, option_key_method, option_value_method, 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 grouped_collection_select_without_bootstrap(method, collection, group_method, diff --git a/lib/bootstrap_form/inputs/radio_button.rb b/lib/bootstrap_form/inputs/radio_button.rb index a4c4d595b..c0bfa647c 100644 --- a/lib/bootstrap_form/inputs/radio_button.rb +++ b/lib/bootstrap_form/inputs/radio_button.rb @@ -10,7 +10,7 @@ module RadioButton def radio_button_with_bootstrap(name, value, *args) options = args.extract_options!.symbolize_keys! radio_button_options = options.except(:class, :label, :label_class, :error_message, :help, - :inline, :custom, :hide_label, :skip_label, :wrapper_class) + :inline, :hide_label, :skip_label, :wrapper_class) radio_button_options[:class] = radio_button_classes(name, options) @@ -34,43 +34,25 @@ def radio_button_label(name, value, options) end def radio_button_classes(name, options) - classes = [options[:class]] - classes << (options[:custom] ? "custom-control-input" : "form-check-input") + classes = Array(options[:class]) << "form-check-input" classes << "is-invalid" if error?(name) classes << "position-static" if options[:skip_label] || options[:hide_label] classes.flatten.compact end def radio_button_label_class(options) - classes = [] - classes << (options[:custom] ? "custom-control-label" : "form-check-label") + classes = ["form-check-label"] classes << options[:label_class] classes << hide_class if options[:hide_label] classes.flatten.compact end def radio_button_wrapper_class(options) - classes = [] - classes << if options[:custom] - custom_radio_button_wrapper_class(options) - else - standard_radio_button_wrapper_class(options) - end - classes << options[:wrapper_class] if options[:wrapper_class].present? - classes.flatten.compact - end - - def standard_radio_button_wrapper_class(options) - classes = %w[form-check] + classes = ["form-check"] classes << "form-check-inline" if layout_inline?(options[:inline]) classes << "disabled" if options[:disabled] - classes - end - - def custom_radio_button_wrapper_class(options) - classes = %w[custom-control custom-radio] - classes << "custom-control-inline" if layout_inline?(options[:inline]) - classes + classes << options[:wrapper_class] if options[:wrapper_class].present? + classes.flatten.compact end end end diff --git a/lib/bootstrap_form/inputs/select.rb b/lib/bootstrap_form/inputs/select.rb index abceaee56..5a20ae5ff 100644 --- a/lib/bootstrap_form/inputs/select.rb +++ b/lib/bootstrap_form/inputs/select.rb @@ -8,6 +8,7 @@ module Select included do def select_with_bootstrap(method, choices=nil, options={}, html_options={}, &block) + html_options = html_options.reverse_merge(control_class: "form-select") form_group_builder(method, options, html_options) do prepend_and_append_input(method, options) do select_without_bootstrap(method, choices, options, html_options, &block) diff --git a/lib/bootstrap_form/inputs/time_zone_select.rb b/lib/bootstrap_form/inputs/time_zone_select.rb index 45bd9a5e6..555c2902d 100644 --- a/lib/bootstrap_form/inputs/time_zone_select.rb +++ b/lib/bootstrap_form/inputs/time_zone_select.rb @@ -8,6 +8,7 @@ module TimeZoneSelect included do 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 time_zone_select_without_bootstrap(method, priority_zones, options, html_options) diff --git a/lib/bootstrap_form/version.rb b/lib/bootstrap_form/version.rb index 435686513..d0deb47a7 100644 --- a/lib/bootstrap_form/version.rb +++ b/lib/bootstrap_form/version.rb @@ -1,3 +1,3 @@ module BootstrapForm - VERSION = "4.5.0".freeze + VERSION = "5.0.0.alpha1".freeze end diff --git a/test/bootstrap_checkbox_test.rb b/test/bootstrap_checkbox_test.rb index 8e22ce2c2..0b34d131f 100644 --- a/test/bootstrap_checkbox_test.rb +++ b/test/bootstrap_checkbox_test.rb @@ -6,7 +6,7 @@ class BootstrapCheckboxTest < ActionView::TestCase setup :setup_test_fixture test "check_box is wrapped correctly" do - expected = <<-HTML.strip_heredoc + expected = <<~HTML
@@ -19,7 +19,7 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "check_box empty label" do - expected = <<-HTML.strip_heredoc + expected = <<~HTML
@@ -31,7 +31,7 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "disabled check_box has proper wrapper classes" do - expected = <<-HTML.strip_heredoc + expected = <<~HTML
@@ -44,7 +44,7 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "check_box label allows html" do - expected = <<-HTML.strip_heredoc + expected = <<~HTML
@@ -57,7 +57,7 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "check_box accepts a block to define the label" do - expected = <<-HTML.strip_heredoc + expected = <<~HTML
@@ -70,7 +70,7 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "check_box accepts a custom label class" do - expected = <<-HTML.strip_heredoc + expected = <<~HTML
@@ -83,7 +83,7 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "check_box 'id' attribute is used to specify label 'for' attribute" do - expected = <<-HTML.strip_heredoc + expected = <<~HTML
@@ -96,7 +96,7 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "check_box responds to checked_value and unchecked_value arguments" do - expected = <<-HTML.strip_heredoc + expected = <<~HTML
@@ -109,7 +109,7 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "inline checkboxes" do - expected = <<-HTML.strip_heredoc + expected = <<~HTML
@@ -122,8 +122,8 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "inline checkboxes from form layout" do - expected = <<-HTML.strip_heredoc - + expected = <<~HTML + #{'' unless ::Rails::VERSION::STRING >= '6'}
@@ -141,7 +141,7 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "disabled inline check_box" do - expected = <<-HTML.strip_heredoc + expected = <<~HTML
@@ -155,24 +155,24 @@ class BootstrapCheckboxTest < ActionView::TestCase end test "inline checkboxes with custom label class" do - expected = <<-HTML.strip_heredoc -
- - - -
+ expected = <<~HTML +
+ + + +
HTML assert_equivalent_xml expected, @builder.check_box(:terms, inline: true, label_class: "btn") end test "collection_check_boxes renders the form_group correctly" do collection = [Address.new(id: 1, street: "Foobar")] - expected = <<-HTML.strip_heredoc + expected = <<~HTML -
- +
+
@@ -187,10 +187,10 @@ class BootstrapCheckboxTest < ActionView::TestCase test "collection_check_boxes renders multiple checkboxes correctly" do collection = [Address.new(id: 1, street: "Foo"), Address.new(id: 2, street: "Bar")] - expected = <<-HTML.strip_heredoc + expected = <<~HTML -
- +
+