Upgrade Bootstrap to 5.1.3 with migrating datetimepicker to Flatpickr#3455
Conversation
Also removed old theming functionality, since existing ones are not compatible with Bootstrap 5
Pull Request Test Coverage Report for Build 1701174677
💛 - Coveralls |
codealchemy
left a comment
There was a problem hiding this comment.
Is this ready for review (and if so, would you have any additional notes / guidance for reviewers)?
Bootstrap 5 is a welcome (and significant!) change - and I started looking at the most obvious differences from the current UI - but there's quite a bit here and I'm not sure how much style / UX changes are intended, so stopped short of going further.
| <%= t('admin.loading') %> | ||
| </div> | ||
| <nav class="navbar navbar-default navbar-fixed-top"> | ||
| <nav class="navbar navbar-expand-xl fixed-top navbar-dark bg-primary border-bottom"> |
There was a problem hiding this comment.
To keep the same breakpoint as before, as well as the same color / styling
| <nav class="navbar navbar-expand-xl fixed-top navbar-dark bg-primary border-bottom"> | |
| <nav class="navbar navbar-expand-md fixed-top navbar-light bg-light border-bottom"> |
There was a problem hiding this comment.
You're right about navbar-expand-md, but navbar-dark bg-primary are intentional. That was to make theming functionality(explained later) to stand out when using other themes, instead of just showing grayish navbar.
|
A BIG YES FOR THIS! @mshibuya I can review / help if you want, let me know if you need anything for this |
|
@codealchemy Sorry for not giving enough context, I was lazy 🙇 @RocKhalil Your review is of course welcome! Please feel free to leave questions or comments 🙏 |
| ].compact.join.html_safe | ||
| if (edit_action = RailsAdmin::Config::Actions.find(:edit, controller: controller, abstract_model: abstract_model, object: _current_user)).try(:authorized?) | ||
| link_to content, rails_admin.url_for(action: edit_action.action_name, model_name: abstract_model.to_param, id: _current_user.id, controller: 'rails_admin/main') | ||
| link_to content, rails_admin.url_for(action: edit_action.action_name, model_name: abstract_model.to_param, id: _current_user.id, controller: 'rails_admin/main'), class: 'nav-link' |
There was a problem hiding this comment.
Most of these kind of changes are to keep up with the change in Bootstrap 5, like navbar needing nav-item and nav-link class for elements inside it.
https://getbootstrap.com/docs/5.1/components/navbar/
| content_tag :li, class: 'dropdown', style: 'float:right' do | ||
| content_tag(:a, class: 'dropdown-toggle', data: {toggle: 'dropdown'}, href: '#') { t('admin.misc.bulk_menu_title').html_safe + ' ' + '<b class="caret"></b>'.html_safe } + | ||
| content_tag :li, class: 'nav-item dropdown dropdown-menu-end' do | ||
| content_tag(:a, class: 'nav-link dropdown-toggle', data: {'bs-toggle': 'dropdown'}, href: '#') { t('admin.misc.bulk_menu_title').html_safe + ' ' + '<b class="caret"></b>'.html_safe } + |
There was a problem hiding this comment.
Also data-toggle or data-target need to be change to data-bs-toggle or data-bs-target.
| return if nested_field_association?(field, nested_in) | ||
|
|
||
| @template.content_tag(:div, class: "form-group control-group #{field.type_css_class} #{field.css_class} #{'error' if field.errors.present?}", id: "#{dom_id(field)}_field") do | ||
| @template.content_tag(:div, class: "control-group row mb-3 #{field.type_css_class} #{field.css_class} #{'error' if field.errors.present?}", id: "#{dom_id(field)}_field") do |
There was a problem hiding this comment.
form-group is no longer there, so replaced with row.
| <li class="disabled"> | ||
| <a href="#"> | ||
| <li class="page-item disabled"> | ||
| <a href="#" class="page-link"> |
There was a problem hiding this comment.
Pagination structure was also updated in Bootstrap 5.
https://getbootstrap.com/docs/5.1/components/pagination/
| @@ -1,9 +1,5 @@ | |||
| <ul class="nav nav-pills nav-stacked"> | |||
| <ul class="col-sm-3 col-md-2 btn-toggle-nav list-unstyled bg-light"> | |||
There was a problem hiding this comment.
I struggled a lot with this change because position: fixed is used here, but finally got equivalent appearance.
| <div class="btn-group" data-toggle="buttons"> | ||
| <% {'1': true, '0': false, '': nil}.each do |text, value| %> | ||
| <label class="<%= [field.css_classes[value], ("active" if field.form_value == value)].compact.join(' ') %> btn btn-default"> | ||
| <%= form.radio_button field.method_name, text, field.html_attributes.reverse_merge({ checked: field.form_value == value, required: field.required}) %> |
There was a problem hiding this comment.
Putting an input element inside the label seemed not to work in Bootstrap 5, hence this structural change.
| <div class="col-sm-2"> | ||
| <%= form.send field.view_helper, field.method_name, field.html_attributes.reverse_merge({class: 'form-control', value: field.form_value}) %> | ||
| </div> | ||
| </div> |
There was a problem hiding this comment.
This is for limiting width of the input element.
| <div class="controls col-sm-10" data-nestedmany="true"> | ||
| <div class="btn-group"> | ||
| <a class="<%= (field.active? ? 'active' : '') %> btn btn-info toggler" data-target="<%= form.jquery_namespace(field) %> > .tab-content, <%= form.jquery_namespace(field) %> > .controls > .nav" data-toggle="button"> | ||
| <a class="<%= (field.active? ? 'active' : 'active') %> btn btn-info toggler" data-bs-target="<%= form.jquery_namespace(field) %> .collapse" data-bs-toggle="collapse" role="button"> |
There was a problem hiding this comment.
This active is my mistake - I forgot to remove it after testing behavior when active.
| </button> | ||
| <% end %> | ||
| <button class="btn btn-default" data-disable-with="<%= t("admin.form.cancel") %>" formnovalidate="<%= true %>" name="_continue" type="submit"> | ||
| <button class="btn btn-light" data-disable-with="<%= t("admin.form.cancel") %>" formnovalidate="<%= true %>" name="_continue" type="submit"> |
| @@ -1,3 +1,3 @@ | |||
| <%= rails_admin_form_for @object, url: edit_path(@abstract_model, @object.id), as: @abstract_model.param_key, html: { method: "put", multipart: true, class: "form-horizontal denser", data: { title: @page_name } } do |form| %> | |||
| <%= rails_admin_form_for @object, url: edit_path(@abstract_model, @object.id), as: @abstract_model.param_key, html: { method: "put", multipart: true, class: "main", data: { title: @page_name } } do |form| %> | |||
There was a problem hiding this comment.
form-horizontal is no more, and added main class to be used for applying our custom styles.
| <% sort_direction = (sort_reverse == 'true' ? "headerSortUp" : "headerSortDown" if selected) %> | ||
| <% end %> | ||
| <th class="<%= [property.sortable && "header pjax", property.sortable && sort_direction, property.sticky? && 'sticky', property.css_class, property.type_css_class].compact.join(' ') %>" data-href="<%= property.sortable && sort_location %>" rel="tooltip" title="<%= property.hint %>"> | ||
| <th class="<%= [property.sortable && "header pjax", property.sortable && sort_direction, property.sticky? && 'sticky', property.css_class, property.type_css_class].select(&:present?).join(' ') %>" data-href="<%= property.sortable && sort_location %>" rel="tooltip" title="<%= property.hint %>"> |
There was a problem hiding this comment.
Use of compact resulted in false showing up as CSS class, so this change.
| '%z' => 'ZZ', # Time zone as hour and minute offset from UTC (e.g. +0900) | ||
| '%:z' => 'Z', # Time zone as hour and minute offset from UTC with a colon (e.g. +09:00), | ||
| # Ruby format options as a key and flatpickr format options as a value | ||
| FLATPICKR_TRANSLATIONS = { |
There was a problem hiding this comment.
All format options are translated into Flatpickr's ones.
https://flatpickr.js.org/formatting/
| .addClass(index == 0 ? "default" : "between") | ||
| .css("display", visible ? "inline-block" : "none") | ||
| .html( | ||
| $('<input type="hidden" />') |
There was a problem hiding this comment.
Flatpickr's altInput functionality enabled us to remove this hidden input.
| is_expected.to have_content 'New Field test' | ||
| attach_file 'Carrierwave asset', file_path('test.jpg') | ||
| find('#modal .save-action').click | ||
| find('#modal .save-action').click # do it again, as a workaround for intermittent failure |
| document.dispatchEvent(event); | ||
|
|
||
| form.bind("ajax:complete", function (event) { | ||
| console.log(event); |
There was a problem hiding this comment.
Forgot to remove debug stuff 💦
| widget.dialog = $( | ||
| '<div id="modal" class="modal fade">\ | ||
| <div class="modal-dialog">\ | ||
| <div class="modal-dialog modal-lg">\ |
| .tab-pane { | ||
| @include clearfix; | ||
| border-left: 5px solid #049cdb; | ||
| border-left: 5px solid map-get($theme-colors, primary); |
There was a problem hiding this comment.
Avoided putting color values directly, for the sake of theming functionality.
| @@ -1 +1,3 @@ | |||
| /* RailsAdmin SASS variables */ | |||
|
|
|||
| $font-size-base: 0.85rem !default; | |||
There was a problem hiding this comment.
Bootstrap5's default font size is slightly larger than Bootstrap3, so changing it smaller
| position: absolute; | ||
| cursor: default; | ||
| z-index: 1051 !important; | ||
| z-index: 1056 !important; |
There was a problem hiding this comment.
Autocomplete popup need to come in front of modals.
|
So about theming. Since we already have webpacker-support in place we can easily integrate with Bootstrap themes like Bootswatch. $ yarn add bootswatchapp/javascript/stylesheets/rails_admin.scss + @import "~bootswatch/dist/journal/variables";
@import "~rails_admin/src/rails_admin/styles/base.scss";
+ @import "~bootswatch/dist/journal/bootswatch";and you will get Don't you think it's cool? |
- Fix inconsistent breakpoint - Remove debug stuffs
| overflow-x: auto; | ||
| .table { | ||
| margin-bottom: 0; | ||
| /* To silence Popper's warning: 'CSS "margin" styles cannot be used...' */ |
There was a problem hiding this comment.
To fix the icon rendering against the span text in the nav

| /* To silence Popper's warning: 'CSS "margin" styles cannot be used...' */ | |
| a > i { | |
| padding: 0 0.3rem; | |
| } | |
| /* To silence Popper's warning: 'CSS "margin" styles cannot be used...' */ |
though it may be worth considering having a high-level rule for i in general (to fix the same issue elsewhere in the UI, such as removing a filter option)

There was a problem hiding this comment.
Adding a whitespace between icon and text might be the better way, since we're doing so a lot elsewhere.
There was a problem hiding this comment.
Sounds good (assuming that wouldn't cause issues with RTL-rendered content - which doesn't seem to be the case but mentioning it as a concern to be sure)
| <% visible_fields = @model_config.export.with(view: self, object: @abstract_model.model.new, controller: self.controller).visible_fields %> | ||
| <%= form_tag export_path(params.merge(all: true)), method: 'post', class: 'form-horizontal' do %> | ||
| <%= form_tag export_path(params.merge(all: true)), method: 'post', class: "main" do %> | ||
| <input name="send_data" type="hidden" value="true">/</input> |
There was a problem hiding this comment.
(Not related to the bootstrap changes but noticed in review) this is rendering the / to the page, seems to have come from aa6ef95
…h other inputs Co-authored-by: Alex Schmitt <codealchemy@users.noreply.github.com>
- Prevent `true` showing up as a css class - form-control-sm must come with form-control: https://getbootstrap.com/docs/5.1/forms/form-control/#sizing - btn-small didn't have any effect - Fix action menu having too narrow gap between icon and text
since all JS dependencies are ES module ready now
|
Let me merge this for now, feel free to leave comments here and I'll address them! |
During the bootstrap 5 update in 6878670 (railsadminteam#3455) this class was changed from `content` to `container`. The `container` class, unlike `content`, has a max width of 1320px. Restore prior behavior of using the full width by using `container-fluid` to avoid wasted whitespace.
During the bootstrap 5 update in 6878670 (railsadminteam#3455) this class was changed from `content` to `container`. The `container` class, unlike `content`, has a max width of 1320px. Restore prior behavior of using the full width by using `container-fluid` to avoid wasted whitespace.
During the bootstrap 5 update in 6878670 (railsadminteam#3455) this class was changed from `content` to `container`. The `container` class, unlike `content`, has a max width of 1320px. Restore prior behavior of using the full width by using `container-fluid` to avoid wasted whitespace.
During the bootstrap 5 update in 6878670 (railsadminteam#3455) this class was changed from `content` to `container`. The `container` class, unlike `content`, has a max width of 1320px. Restore prior behavior of using the full width by using `container-fluid` to avoid wasted whitespace.
During the bootstrap 5 update in 6878670 (railsadminteam#3455) this class was changed from `content` to `container`. The `container` class, unlike `content`, has a max width of 1320px. Restore prior behavior of using the full width by using `container-fluid` to avoid wasted whitespace.
During the bootstrap 5 update in 6878670 (railsadminteam#3455) this class was changed from `content` to `container`. The `container` class, unlike `content`, has a max width of 1320px. Restore prior behavior of using the full width by using `container-fluid` to avoid wasted whitespace.

Closes #3083
Reason for upgrading Bootstrap to 5
Reason for replacing Bootstrap Datetimepicker with Flatpickr
Considered options
momentjs_formatto Flatpickr's one if they use it.