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: + +![Example 32](demo/doc/screenshots/bootstrap/readme/32_example.png "Example 32") +```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. -![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 %> ``` @@ -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" %> ``` @@ -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" %> ``` @@ -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 ".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 -![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' %> @@ -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) %> ``` @@ -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 %> ``` @@ -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 %> @@ -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 %> ``` @@ -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 %> @@ -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 %> @@ -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 %> @@ -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 %> @@ -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 %> @@ -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 %> @@ -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 %> @@ -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." %> @@ -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 %> @@ -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 %> @@ -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| %> @@ -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| %> @@ -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| %> @@ -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 %> @@ -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" %> @@ -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| %>
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?