diff --git a/.yarnrc b/.yarnrc
index 1966a41d..09e3b5a9 100644
--- a/.yarnrc
+++ b/.yarnrc
@@ -2,4 +2,4 @@
# yarn lockfile v1
-lastUpdateCheck 1761521327441
+lastUpdateCheck 1761695563757
diff --git a/README.md b/README.md
index 28201ec8..4e9d1f48 100644
--- a/README.md
+++ b/README.md
@@ -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";
@@ -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:
+
+
+```erb
+<%= f.date_field :joined_at, class: "w-auto" %>
+```
+
+This generates:
+
+```html
+
+
+
+
+```
+
+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
@@ -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.
-
+
```erb
<%= f.submit %>
```
@@ -966,7 +986,7 @@ This generates:
You can also use the `primary` helper, which adds `btn btn-primary` to your
submit button:
-
+
```erb
<%= f.primary "Optional Label" %>
```
@@ -979,7 +999,7 @@ This generates:
You can specify your own classes like this:
-
+
```erb
<%= f.submit "Log In", class: "btn btn-success" %>
```
@@ -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
-
+
```erb
<%= f.primary "Save changes ".html_safe, render_as_button: true %>
@@ -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
-
+
```erb
<%= f.primary "My Nice Button", extra_class: 'my-button' %>
@@ -1047,7 +1067,7 @@ will be rendered as
## Rich Text Areas AKA Trix Editor
-
+
```erb
<%= f.rich_text_area(:life_story) %>
```
@@ -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:
-
+
```erb
<%= f.text_field_without_bootstrap :email %>
```
@@ -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.
-
+
```erb
<%= bootstrap_form_for(@user, layout: :inline) do |f| %>
<%= f.email_field :email, hide_label: true %>
@@ -1174,7 +1194,7 @@ This generates:
To skip label rendering at all, use `skip_label: true` option.
-
+
```erb
<%= f.password_field :password, skip_label: true %>
```
@@ -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.
-
+
```erb
<%= bootstrap_form_for(@user, layout: :horizontal, label_col: "col-sm-2", control_col: "col-sm-10") do |f| %>
<%= f.email_field :email %>
@@ -1243,7 +1263,7 @@ This generates:
The `label_col` and `control_col` css classes can also be changed per control:
-
+
```erb
<%= bootstrap_form_for(@user, layout: :horizontal) do |f| %>
<%= f.email_field :email %>
@@ -1310,7 +1330,7 @@ end
Control col wrapper class can be modified with `add_control_col_class`. This option will preserve column definition:
-
+
```erb
<%= bootstrap_form_for(@user, layout: :horizontal) do |f| %>
<%= f.email_field :email %>
@@ -1349,7 +1369,7 @@ This generates:
The form-level `layout` can be overridden per field, unless the form-level layout was `inline`:
-
+
```erb
<%= bootstrap_form_for(@user, layout: :horizontal) do |f| %>
<%= f.email_field :email %>
@@ -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:
-
+
```erb
<%= bootstrap_form_for(@user) do |f| %>
<%= f.email_field :email, floating: true %>
@@ -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:
-
+
```erb
<%= bootstrap_form_for(@user_with_error) do |f| %>
<%= f.email_field :email %>
@@ -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:
-
+
```erb
<%= bootstrap_form_for(@user_with_error, label_errors: true) do |f| %>
<%= f.email_field :email %>
@@ -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.
-
+
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
<%= f.alert_message "Please fix the errors below." %>
@@ -1576,7 +1596,7 @@ Which outputs:
You can turn off the error summary like this:
-
+
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
<%= f.alert_message "Please fix the errors below.", error_summary: false %>
@@ -1593,7 +1613,7 @@ This generates:
To output a simple unordered list of errors, use the `error_summary` helper.
-
+
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
<%= f.error_summary %>
@@ -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.
-
+
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
@@ -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:
-
+
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
@@ -1656,7 +1676,7 @@ Which outputs:
You can also use a custom class for the wrapping div, like this:
-
+
```erb
<%= bootstrap_form_for @user_with_error do |f| %>
@@ -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:
-
+
```erb
<%= f.password_field :login, label: "New Username", required: true %>
<%= f.password_field :password, label: "New Password", required: false %>
@@ -1718,7 +1738,7 @@ This generates:
Adding a form control for a `belongs_to` field will automatically pick up the associated presence validator.
-
+
```erb
<%= bootstrap_form_for(@address, url: '/address') do |f| %>
<%= f.collection_select :user_id, @users, :id, :email, include_blank: "Select a value" %>
@@ -1765,7 +1785,7 @@ Generated HTML:
Fields can be disabled using the standard Rails form helper option.
-
+
```erb
<%= bootstrap_form_for @user do |f| %>
diff --git a/compose.yml b/compose.yml
index 96657d0b..c33b3a16 100644
--- a/compose.yml
+++ b/compose.yml
@@ -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"
diff --git a/demo/app/views/bootstrap/form.html.erb b/demo/app/views/bootstrap/form.html.erb
index bbcdf76e..1910133e 100644
--- a/demo/app/views/bootstrap/form.html.erb
+++ b/demo/app/views/bootstrap/form.html.erb
@@ -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 %>
diff --git a/demo/doc/screenshots/bootstrap/index/00_horizontal_form.png b/demo/doc/screenshots/bootstrap/index/00_horizontal_form.png
index 5d65f427..db568a30 100644
Binary files a/demo/doc/screenshots/bootstrap/index/00_horizontal_form.png and b/demo/doc/screenshots/bootstrap/index/00_horizontal_form.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/32_example.png b/demo/doc/screenshots/bootstrap/readme/32_example.png
index 0dbd943f..f556c6b3 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/32_example.png and b/demo/doc/screenshots/bootstrap/readme/32_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/33_example.png b/demo/doc/screenshots/bootstrap/readme/33_example.png
index c2e9ac3e..0dbd943f 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/33_example.png and b/demo/doc/screenshots/bootstrap/readme/33_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/34_example.png b/demo/doc/screenshots/bootstrap/readme/34_example.png
index cd7f8118..c2e9ac3e 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/34_example.png and b/demo/doc/screenshots/bootstrap/readme/34_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/35_example.png b/demo/doc/screenshots/bootstrap/readme/35_example.png
index 63b851c0..cd7f8118 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/35_example.png and b/demo/doc/screenshots/bootstrap/readme/35_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/36_example.png b/demo/doc/screenshots/bootstrap/readme/36_example.png
index 1bc46168..63b851c0 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/36_example.png and b/demo/doc/screenshots/bootstrap/readme/36_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/37_example.png b/demo/doc/screenshots/bootstrap/readme/37_example.png
index f4d76bb7..1bc46168 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/37_example.png and b/demo/doc/screenshots/bootstrap/readme/37_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/38_example.png b/demo/doc/screenshots/bootstrap/readme/38_example.png
index 993f43d6..f4d76bb7 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/38_example.png and b/demo/doc/screenshots/bootstrap/readme/38_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/39_example.png b/demo/doc/screenshots/bootstrap/readme/39_example.png
index a51825eb..993f43d6 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/39_example.png and b/demo/doc/screenshots/bootstrap/readme/39_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/40_example.png b/demo/doc/screenshots/bootstrap/readme/40_example.png
index bcb761e9..a51825eb 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/40_example.png and b/demo/doc/screenshots/bootstrap/readme/40_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/41_example.png b/demo/doc/screenshots/bootstrap/readme/41_example.png
index 433d10a5..bcb761e9 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/41_example.png and b/demo/doc/screenshots/bootstrap/readme/41_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/42_example.png b/demo/doc/screenshots/bootstrap/readme/42_example.png
index 9d870ab7..433d10a5 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/42_example.png and b/demo/doc/screenshots/bootstrap/readme/42_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/43_example.png b/demo/doc/screenshots/bootstrap/readme/43_example.png
index 07ba29be..9d870ab7 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/43_example.png and b/demo/doc/screenshots/bootstrap/readme/43_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/44_example.png b/demo/doc/screenshots/bootstrap/readme/44_example.png
index 4c45e77a..07ba29be 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/44_example.png and b/demo/doc/screenshots/bootstrap/readme/44_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/45_example.png b/demo/doc/screenshots/bootstrap/readme/45_example.png
index 1a804a98..4c45e77a 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/45_example.png and b/demo/doc/screenshots/bootstrap/readme/45_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/46_example.png b/demo/doc/screenshots/bootstrap/readme/46_example.png
index 1116bc52..1a804a98 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/46_example.png and b/demo/doc/screenshots/bootstrap/readme/46_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/47_example.png b/demo/doc/screenshots/bootstrap/readme/47_example.png
index f57ab1b9..1116bc52 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/47_example.png and b/demo/doc/screenshots/bootstrap/readme/47_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/48_example.png b/demo/doc/screenshots/bootstrap/readme/48_example.png
index de9655e7..f57ab1b9 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/48_example.png and b/demo/doc/screenshots/bootstrap/readme/48_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/49_example.png b/demo/doc/screenshots/bootstrap/readme/49_example.png
index dd04773c..de9655e7 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/49_example.png and b/demo/doc/screenshots/bootstrap/readme/49_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/50_example.png b/demo/doc/screenshots/bootstrap/readme/50_example.png
index b065278c..dd04773c 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/50_example.png and b/demo/doc/screenshots/bootstrap/readme/50_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/51_example.png b/demo/doc/screenshots/bootstrap/readme/51_example.png
index 07dc83a0..b065278c 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/51_example.png and b/demo/doc/screenshots/bootstrap/readme/51_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/52_example.png b/demo/doc/screenshots/bootstrap/readme/52_example.png
index d0c6f864..07dc83a0 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/52_example.png and b/demo/doc/screenshots/bootstrap/readme/52_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/53_example.png b/demo/doc/screenshots/bootstrap/readme/53_example.png
index 340e9244..d0c6f864 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/53_example.png and b/demo/doc/screenshots/bootstrap/readme/53_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/54_example.png b/demo/doc/screenshots/bootstrap/readme/54_example.png
index a6451827..340e9244 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/54_example.png and b/demo/doc/screenshots/bootstrap/readme/54_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/55_example.png b/demo/doc/screenshots/bootstrap/readme/55_example.png
index ce994981..a6451827 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/55_example.png and b/demo/doc/screenshots/bootstrap/readme/55_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/56_example.png b/demo/doc/screenshots/bootstrap/readme/56_example.png
index f116d753..ce994981 100644
Binary files a/demo/doc/screenshots/bootstrap/readme/56_example.png and b/demo/doc/screenshots/bootstrap/readme/56_example.png differ
diff --git a/demo/doc/screenshots/bootstrap/readme/57_example.png b/demo/doc/screenshots/bootstrap/readme/57_example.png
new file mode 100644
index 00000000..ec1d7bd6
Binary files /dev/null and b/demo/doc/screenshots/bootstrap/readme/57_example.png differ
diff --git a/demo/test/application_system_test_case.rb b/demo/test/application_system_test_case.rb
index 0bb93abb..5f91c179 100644
--- a/demo/test/application_system_test_case.rb
+++ b/demo/test/application_system_test_case.rb
@@ -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?