Skip to content
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 .yarnrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# yarn lockfile v1


lastUpdateCheck 1761521327441
lastUpdateCheck 1761695563757
78 changes: 49 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,17 @@ Then:

`bundle install`

`bootstrap_form` uses a very small number of its own CSS styles. Add the styles to your CSS bundle (usually your `application.scss` file). The way to do this depends on whether you're using Propshaft (the Rails 8 default), or Sprockets (pre-Rails 8). (Check your `Gemfile` to see whether you're using `sprockets-rails` or `propshaft`.)
`bootstrap_form` uses a very small number of its own CSS styles. These styles are used in HTML generated by the `error_summary` and `alert_message` error helpers, and the `date_select`, `time_select`, and `datetime_select` helpers. If you're not using those helpers, you don't need to install the `bootstrap_form` CSS styles.

If you're using Propshaft, add the styles like this:
If you do need the CSS styles, add them to your CSS bundle (usually your `application.scss` file). The way to do this depends on whether you're using Propshaft (the Rails 8 default), or Sprockets (pre-Rails 8). (Check your `Gemfile` to see whether you're using `sprockets-rails` or `propshaft`.)

If you're using Propshaft, add the styles to your CSS bundle like this:

```scss
@use "rails_bootstrap_forms";
```

If you're using Sprockets, add the styles like this:
If you're using Sprockets, add the styles to your CSS bundle like this:

```scss
@import "rails_bootstrap_forms.css";
Expand Down Expand Up @@ -939,7 +941,25 @@ The value of the block would be used for the content of the static "control".
Bootstrap 4 actually creates and styles a disabled input field for static controls, so the value of the control has to be specified by the `value:` option.
Passing a block to `static_control` no longer has any effect.

## Date Helpers
## Date and Time Helpers

You can create a date picker, time picker, or date-time picker with `date_field`, `time_field`, or `datetime_field`, like this:

![Example 32](demo/doc/screenshots/bootstrap/readme/32_example.png "Example 32")
```erb
<%= f.date_field :joined_at, class: "w-auto" %>
```

This generates:

```html
<div class="mb-3">
<label class="form-label" for="user_joined_at">Joined at</label>
<input class="form-control w-auto" id="user_joined_at" name="user[joined_at]" type="date">
</div>
```

For backwards compatibility, there are also helpers for `date_select`, `time_select`, and `datetime_select`.

The multiple selects that the date and time helpers (`date_select`,
`time_select`, `datetime_select`) generate are wrapped inside a
Expand All @@ -952,7 +972,7 @@ this by defining these selects as `inline-block` and a width of `auto`.
The `btn btn-secondary` CSS classes are automatically added to your submit
buttons.

![Example 32](demo/doc/screenshots/bootstrap/readme/32_example.png "Example 32")
![Example 33](demo/doc/screenshots/bootstrap/readme/33_example.png "Example 33")
```erb
<%= f.submit %>
```
Expand All @@ -966,7 +986,7 @@ This generates:
You can also use the `primary` helper, which adds `btn btn-primary` to your
submit button:

![Example 33](demo/doc/screenshots/bootstrap/readme/33_example.png "Example 33")
![Example 34](demo/doc/screenshots/bootstrap/readme/34_example.png "Example 34")
```erb
<%= f.primary "Optional Label" %>
```
Expand All @@ -979,7 +999,7 @@ This generates:

You can specify your own classes like this:

![Example 34](demo/doc/screenshots/bootstrap/readme/34_example.png "Example 34")
![Example 35](demo/doc/screenshots/bootstrap/readme/35_example.png "Example 35")
```erb
<%= f.submit "Log In", class: "btn btn-success" %>
```
Expand All @@ -995,7 +1015,7 @@ it will be rendered as an HTML button, instead of an input tag. This allows you
to specify HTML content and styling for your buttons (such as adding
illustrative icons to them). For example, the following statements

![Example 35](demo/doc/screenshots/bootstrap/readme/35_example.png "Example 35")
![Example 36](demo/doc/screenshots/bootstrap/readme/36_example.png "Example 36")
```erb
<%= f.primary "Save changes <span class='bi bi-save'></span>".html_safe, render_as_button: true %>

Expand Down Expand Up @@ -1029,7 +1049,7 @@ Bootstrap classes), or for element targeting via CSS classes.
Be aware, however, that using the `class` option will discard any extra classes
you add. As an example, the following button declarations

![Example 36](demo/doc/screenshots/bootstrap/readme/36_example.png "Example 36")
![Example 37](demo/doc/screenshots/bootstrap/readme/37_example.png "Example 37")
```erb
<%= f.primary "My Nice Button", extra_class: 'my-button' %>

Expand All @@ -1047,7 +1067,7 @@ will be rendered as

## Rich Text Areas AKA Trix Editor

![Example 37](demo/doc/screenshots/bootstrap/readme/37_example.png "Example 37")
![Example 38](demo/doc/screenshots/bootstrap/readme/38_example.png "Example 38")
```erb
<%= f.rich_text_area(:life_story) %>
```
Expand Down Expand Up @@ -1115,7 +1135,7 @@ The `hidden_field` helper in `bootstrap_form` calls the Rails helper directly, a
If you want to use the original Rails form helpers for a particular field,
append `_without_bootstrap` to the helper:

![Example 38](demo/doc/screenshots/bootstrap/readme/38_example.png "Example 38")
![Example 39](demo/doc/screenshots/bootstrap/readme/39_example.png "Example 39")
```erb
<%= f.text_field_without_bootstrap :email %>
```
Expand All @@ -1137,7 +1157,7 @@ To use an inline-layout form, use the `layout: :inline` option. To hide labels,
use the `hide_label: true` option, which keeps your labels accessible to those
using screen readers.

![Example 39](demo/doc/screenshots/bootstrap/readme/39_example.png "Example 39")
![Example 40](demo/doc/screenshots/bootstrap/readme/40_example.png "Example 40")
```erb
<%= bootstrap_form_for(@user, layout: :inline) do |f| %>
<%= f.email_field :email, hide_label: true %>
Expand Down Expand Up @@ -1174,7 +1194,7 @@ This generates:

To skip label rendering at all, use `skip_label: true` option.

![Example 40](demo/doc/screenshots/bootstrap/readme/40_example.png "Example 40")
![Example 41](demo/doc/screenshots/bootstrap/readme/41_example.png "Example 41")
```erb
<%= f.password_field :password, skip_label: true %>
```
Expand All @@ -1196,7 +1216,7 @@ To use a horizontal-layout form with labels to the left of the control, use the
In the example below, the submit button has been wrapped in a `form_group` to
keep it properly aligned.

![Example 41](demo/doc/screenshots/bootstrap/readme/41_example.png "Example 41")
![Example 42](demo/doc/screenshots/bootstrap/readme/42_example.png "Example 42")
```erb
<%= bootstrap_form_for(@user, layout: :horizontal, label_col: "col-sm-2", control_col: "col-sm-10") do |f| %>
<%= f.email_field :email %>
Expand Down Expand Up @@ -1243,7 +1263,7 @@ This generates:

The `label_col` and `control_col` css classes can also be changed per control:

![Example 42](demo/doc/screenshots/bootstrap/readme/42_example.png "Example 42")
![Example 43](demo/doc/screenshots/bootstrap/readme/43_example.png "Example 43")
```erb
<%= bootstrap_form_for(@user, layout: :horizontal) do |f| %>
<%= f.email_field :email %>
Expand Down Expand Up @@ -1310,7 +1330,7 @@ end

Control col wrapper class can be modified with `add_control_col_class`. This option will preserve column definition:

![Example 43](demo/doc/screenshots/bootstrap/readme/43_example.png "Example 43")
![Example 44](demo/doc/screenshots/bootstrap/readme/44_example.png "Example 44")
```erb
<%= bootstrap_form_for(@user, layout: :horizontal) do |f| %>
<%= f.email_field :email %>
Expand Down Expand Up @@ -1349,7 +1369,7 @@ This generates:

The form-level `layout` can be overridden per field, unless the form-level layout was `inline`:

![Example 44](demo/doc/screenshots/bootstrap/readme/44_example.png "Example 44")
![Example 45](demo/doc/screenshots/bootstrap/readme/45_example.png "Example 45")
```erb
<%= bootstrap_form_for(@user, layout: :horizontal) do |f| %>
<%= f.email_field :email %>
Expand Down Expand Up @@ -1404,7 +1424,7 @@ A form-level `layout: :inline` can't be overridden because of the way Bootstrap
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:

![Example 45](demo/doc/screenshots/bootstrap/readme/45_example.png "Example 45")
![Example 46](demo/doc/screenshots/bootstrap/readme/46_example.png "Example 46")
```erb
<%= bootstrap_form_for(@user) do |f| %>
<%= f.email_field :email, floating: true %>
Expand Down Expand Up @@ -1452,7 +1472,7 @@ Rails normally wraps fields with validation errors in a `div.field_with_errors`,
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:

![Example 46](demo/doc/screenshots/bootstrap/readme/46_example.png "Example 46")
![Example 47](demo/doc/screenshots/bootstrap/readme/47_example.png "Example 47")
```erb
<%= bootstrap_form_for(@user_with_error) do |f| %>
<%= f.email_field :email %>
Expand Down Expand Up @@ -1519,7 +1539,7 @@ You can turn off inline errors for the entire form like this:
You can also display validation errors in the field's label; just turn
on the `:label_errors` option. Here's an example:

![Example 47](demo/doc/screenshots/bootstrap/readme/47_example.png "Example 47")
![Example 48](demo/doc/screenshots/bootstrap/readme/48_example.png "Example 48")
```erb
<%= bootstrap_form_for(@user_with_error, label_errors: true) do |f| %>
<%= f.email_field :email %>
Expand Down Expand Up @@ -1552,7 +1572,7 @@ To display an error message with an error summary, you can use the
`alert_message` helper. This won't output anything unless a model validation
has failed.

![Example 48](demo/doc/screenshots/bootstrap/readme/48_example.png "Example 48")
![Example 49](demo/doc/screenshots/bootstrap/readme/49_example.png "Example 49")
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
<%= f.alert_message "Please fix the errors below." %>
Expand All @@ -1576,7 +1596,7 @@ Which outputs:

You can turn off the error summary like this:

![Example 49](demo/doc/screenshots/bootstrap/readme/49_example.png "Example 49")
![Example 50](demo/doc/screenshots/bootstrap/readme/50_example.png "Example 50")
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
<%= f.alert_message "Please fix the errors below.", error_summary: false %>
Expand All @@ -1593,7 +1613,7 @@ This generates:

To output a simple unordered list of errors, use the `error_summary` helper.

![Example 50](demo/doc/screenshots/bootstrap/readme/50_example.png "Example 50")
![Example 51](demo/doc/screenshots/bootstrap/readme/51_example.png "Example 51")
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
<%= f.error_summary %>
Expand All @@ -1616,7 +1636,7 @@ Which outputs:

If you want to display a custom inline error for a specific attribute not represented by a form field, use the `errors_on` helper.

![Example 51](demo/doc/screenshots/bootstrap/readme/51_example.png "Example 51")
![Example 52](demo/doc/screenshots/bootstrap/readme/52_example.png "Example 52")
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
<input class="is-invalid" autocomplete="off" disabled type="hidden">
Expand All @@ -1637,7 +1657,7 @@ Note that the `invalid-feedback` `div` is hidden unless there is a preceding ele

You can hide the attribute name like this:

![Example 52](demo/doc/screenshots/bootstrap/readme/52_example.png "Example 52")
![Example 53](demo/doc/screenshots/bootstrap/readme/53_example.png "Example 53")
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
<input class="is-invalid" autocomplete="off" disabled type="hidden">
Expand All @@ -1656,7 +1676,7 @@ Which outputs:

You can also use a custom class for the wrapping div, like this:

![Example 53](demo/doc/screenshots/bootstrap/readme/53_example.png "Example 53")
![Example 54](demo/doc/screenshots/bootstrap/readme/54_example.png "Example 54")
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
<input class="is-invalid" autocomplete="off" disabled type="hidden">
Expand Down Expand Up @@ -1695,7 +1715,7 @@ ActiveModel::Validations::PresenceValidator.

In cases where this behaviour is undesirable, use the `required` option to force the class to be present or absent:

![Example 54](demo/doc/screenshots/bootstrap/readme/54_example.png "Example 54")
![Example 55](demo/doc/screenshots/bootstrap/readme/55_example.png "Example 55")
```erb
<%= f.password_field :login, label: "New Username", required: true %>
<%= f.password_field :password, label: "New Password", required: false %>
Expand All @@ -1718,7 +1738,7 @@ This generates:

Adding a form control for a `belongs_to` field will automatically pick up the associated presence validator.

![Example 55](demo/doc/screenshots/bootstrap/readme/55_example.png "Example 55")
![Example 56](demo/doc/screenshots/bootstrap/readme/56_example.png "Example 56")
```erb
<%= bootstrap_form_for(@address, url: '/address') do |f| %>
<%= f.collection_select :user_id, @users, :id, :email, include_blank: "Select a value" %>
Expand Down Expand Up @@ -1765,7 +1785,7 @@ Generated HTML:

Fields can be disabled using the standard Rails form helper option.

![Example 56](demo/doc/screenshots/bootstrap/readme/56_example.png "Example 56")
![Example 57](demo/doc/screenshots/bootstrap/readme/57_example.png "Example 57")
```erb
<%= bootstrap_form_for @user do |f| %>
<div class="row g-3">
Expand Down
3 changes: 3 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@ services:
tty: true
environment:
- LANG=en_CA.UTF-8
- LANGUAGE=en_CA.UTF-8
- LANG_WHERE=CA
- LANG_WHICH=en
ports:
- "7900"
1 change: 1 addition & 0 deletions demo/app/views/bootstrap/form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<%= form.collection_radio_buttons :radio_buttons, @collection, :id, :street %>
<%= form.file_field :file %>
<%= form.datetime_select :created_at, include_blank: true %>
<%= form.datetime_field :updated_at, class: "w-auto" %>

<%= form.submit %>
<% end %>
Expand Down
Binary file modified demo/doc/screenshots/bootstrap/index/00_horizontal_form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/32_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/33_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/34_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/35_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/36_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/37_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/38_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/39_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/40_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/41_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/42_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/43_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/44_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/45_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/46_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/47_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/48_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/49_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/50_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/51_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/52_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/53_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/54_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/55_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified demo/doc/screenshots/bootstrap/readme/56_example.png
3 changes: 1 addition & 2 deletions demo/test/application_system_test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ def remote_selenium? = @remote_selenium ||= ENV["SELENIUM_HOST"].present? || ENV

# Debugging tip: Change to `chrome` (not headless) and then browse to:
# http://localhost:7900/?autoconnect=1&resize=scale&password=secret. You can watch the fun there.
driven_by :selenium, using: :headless_chrome, screen_size: [960, 800], options: options do |capabilities|
driven_by :selenium, using: :headless_chrome, screen_size: [960, 900], options: options do |capabilities|
capabilities.add_argument("force-device-scale-factor=1")
capabilities.add_argument("lang=#{ENV.fetch('LANG', 'en_CA')}")
end

if remote_selenium?
Expand Down