blacklight 7.19.0 → 7.20.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.env +1 -1
  3. data/.github/workflows/ruby.yml +19 -1
  4. data/.rubocop.yml +4 -0
  5. data/README.md +1 -1
  6. data/VERSION +1 -1
  7. data/app/assets/javascripts/blacklight/blacklight.js +14 -5
  8. data/app/assets/stylesheets/blacklight/_balanced_list.scss +2 -2
  9. data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +1 -1
  10. data/app/assets/stylesheets/blacklight/_constraints.scss +8 -5
  11. data/app/assets/stylesheets/blacklight/_controls.scss +9 -1
  12. data/app/assets/stylesheets/blacklight/_facets.scss +5 -3
  13. data/app/assets/stylesheets/blacklight/_header.scss +6 -1
  14. data/app/assets/stylesheets/blacklight/_pagination.scss +1 -1
  15. data/app/assets/stylesheets/blacklight/_twitter_typeahead.scss +1 -0
  16. data/app/components/blacklight/constraint_layout_component.html.erb +1 -1
  17. data/app/components/blacklight/constraints_component.html.erb +2 -2
  18. data/app/components/blacklight/constraints_component.rb +6 -2
  19. data/app/components/blacklight/document_component.rb +4 -4
  20. data/app/components/blacklight/facet_field_component.html.erb +3 -1
  21. data/app/components/blacklight/facet_field_no_layout_component.rb +1 -1
  22. data/app/components/blacklight/facet_item_component.rb +4 -4
  23. data/app/components/blacklight/facet_item_pivot_component.rb +2 -2
  24. data/app/components/blacklight/metadata_field_layout_component.rb +1 -1
  25. data/app/components/blacklight/response/facet_group_component.html.erb +2 -0
  26. data/app/components/blacklight/response/pagination_component.html.erb +1 -1
  27. data/app/components/blacklight/response/view_type_component.html.erb +1 -1
  28. data/app/components/blacklight/search_bar_component.html.erb +3 -3
  29. data/app/components/blacklight/system/dropdown_component.rb +1 -1
  30. data/app/components/blacklight/system/flash_message_component.html.erb +1 -1
  31. data/app/components/blacklight/system/modal_component.html.erb +1 -1
  32. data/app/controllers/concerns/blacklight/bookmarks.rb +0 -3
  33. data/app/controllers/concerns/blacklight/catalog.rb +3 -0
  34. data/app/controllers/concerns/blacklight/controller.rb +9 -5
  35. data/app/controllers/concerns/blacklight/search_context.rb +1 -1
  36. data/app/helpers/blacklight/catalog_helper_behavior.rb +1 -3
  37. data/app/helpers/blacklight/component_helper_behavior.rb +1 -1
  38. data/app/javascript/blacklight/core.js +8 -1
  39. data/app/javascript/blacklight/modal.js +1 -1
  40. data/app/javascript/blacklight/search_context.js +5 -2
  41. data/app/views/blacklight/nav/_bookmark.html.erb +1 -1
  42. data/app/views/bookmarks/index.html.erb +1 -1
  43. data/app/views/catalog/_constraints.html.erb +2 -2
  44. data/app/views/catalog/_home_text.html.erb +3 -3
  45. data/app/views/catalog/_paginate_compact.html.erb +1 -0
  46. data/app/views/catalog/_per_page_widget.html.erb +1 -1
  47. data/app/views/catalog/_search_results.html.erb +2 -2
  48. data/app/views/search_history/index.html.erb +1 -1
  49. data/app/views/shared/_header_navbar.html.erb +3 -3
  50. data/blacklight.gemspec +4 -2
  51. data/config/locales/blacklight.ar.yml +2 -2
  52. data/config/locales/blacklight.ca.yml +2 -2
  53. data/config/locales/blacklight.de.yml +6 -4
  54. data/config/locales/blacklight.en.yml +8 -4
  55. data/config/locales/blacklight.es.yml +5 -3
  56. data/config/locales/blacklight.fr.yml +5 -3
  57. data/config/locales/blacklight.hu.yml +2 -2
  58. data/config/locales/blacklight.it.yml +5 -3
  59. data/config/locales/blacklight.nl.yml +2 -2
  60. data/config/locales/blacklight.pt-BR.yml +1 -1
  61. data/config/locales/blacklight.sq.yml +2 -2
  62. data/config/locales/blacklight.zh.yml +2 -2
  63. data/lib/blacklight/search_state/filter_field.rb +9 -0
  64. data/lib/blacklight/solr/request.rb +10 -7
  65. data/lib/blacklight/solr/response/group_response.rb +3 -2
  66. data/lib/blacklight/solr/response/pagination_methods.rb +1 -1
  67. data/lib/blacklight/solr/search_builder_behavior.rb +37 -17
  68. data/lib/blacklight.rb +5 -1
  69. data/lib/generators/blacklight/assets_generator.rb +4 -2
  70. data/lib/generators/blacklight/install_generator.rb +4 -1
  71. data/lib/generators/blacklight/user_generator.rb +1 -1
  72. data/package.json +2 -2
  73. data/spec/components/blacklight/constraint_layout_component_spec.rb +3 -7
  74. data/spec/components/blacklight/facet_field_checkboxes_component_spec.rb +3 -7
  75. data/spec/components/blacklight/facet_field_list_component_spec.rb +3 -7
  76. data/spec/components/blacklight/facet_item_component_spec.rb +8 -8
  77. data/spec/components/blacklight/facet_item_pivot_component_spec.rb +3 -7
  78. data/spec/components/blacklight/hidden_search_state_component_spec.rb +7 -8
  79. data/spec/components/blacklight/metadata_field_component_spec.rb +4 -8
  80. data/spec/features/axe_spec.rb +34 -0
  81. data/spec/features/facet_missing_spec.rb +59 -0
  82. data/spec/features/facets_spec.rb +3 -3
  83. data/spec/helpers/blacklight/configuration_helper_behavior_spec.rb +3 -3
  84. data/spec/helpers/blacklight/facets_helper_behavior_spec.rb +2 -2
  85. data/spec/lib/blacklight/search_state/filter_field_spec.rb +27 -0
  86. data/spec/models/blacklight/solr/request_spec.rb +0 -1
  87. data/spec/models/blacklight/solr/response/group_response_spec.rb +3 -2
  88. data/spec/models/blacklight/solr/search_builder_spec.rb +27 -1
  89. data/spec/spec_helper.rb +16 -9
  90. data/spec/support/view_component_capybara_test_helpers.rb +8 -0
  91. data/spec/test_app_templates/lib/generators/test_app_generator.rb +0 -3
  92. data/spec/views/catalog/_facet_layout.html.erb_spec.rb +2 -2
  93. metadata +45 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b17640ea1dd81a41048bfbb2362bf692300e228045fac8f0d42aa0fbd281a63
4
- data.tar.gz: 6956aa4b0fd67802b24d04b0f27677ca9f84d5692bdf8a34cd21b043d3fe8df9
3
+ metadata.gz: ee4c46b8533bb359a6619ef6dddd96b4de1449b4bfc96db65a357469bebc7cc1
4
+ data.tar.gz: 9eecbab00d1484a3e417bd60245479fc58d6bd9c41a7b16be91d3cef056846a2
5
5
  SHA512:
6
- metadata.gz: bc71e8e6c0ee060b4303052caf0b9341c58a42c71e5f9badfe03f8013c6e3b96ba43f66d0d477c525edb58263a1993b9d9da143cf3f5f1c7fdb032568df5db87
7
- data.tar.gz: e31975c219b1b709bdc6b765f123ffbf13978f56a26936f75d08cc7796e38703f163dc459e735f37bc36da3338e43f481d24b57d8308052509f6d19c0864bb7c
6
+ metadata.gz: 97d968403618ee1d25fac527cea64467e3be78040e0d58e8d672463b94c22f272ff7b30fec66af576084bf2a9e8d169a7d3bec9b58b0d1187bb8ffc5bdab2f21
7
+ data.tar.gz: 31855c12d2d58eb7863aa90a4b60c106d208b302460bd0f263fdd32baf8d6025f94025cdf953c82a42220a403c2d0eeae367e7729c7f714f8bc19d23bb561974
data/.env CHANGED
@@ -1,5 +1,5 @@
1
1
  ALPINE_RUBY_VERSION=2.6.5
2
- RAILS_VERSION=5.2.4.1
2
+ RAILS_VERSION=5.2.5
3
3
  SOLR_PORT=8983
4
4
  SOLR_URL=http://solr:8983/solr/blacklight-core
5
5
  SOLR_VERSION=latest
@@ -43,6 +43,24 @@ jobs:
43
43
  run: bundle exec rake ci
44
44
  env:
45
45
  ENGINE_CART_RAILS_OPTIONS: '--skip-git --skip-listen --skip-spring --skip-keeps --skip-action-cable --skip-coffee --skip-test'
46
+ test_bootstrap5:
47
+ runs-on: ubuntu-latest
48
+ strategy:
49
+ matrix:
50
+ ruby: [3.0]
51
+ steps:
52
+ - uses: actions/checkout@v2
53
+ - name: Set up Ruby
54
+ uses: ruby/setup-ruby@v1
55
+ with:
56
+ ruby-version: ${{ matrix.ruby }}
57
+ - name: Install dependencies
58
+ run: bundle install
59
+ - name: Run tests
60
+ run: bundle exec rake ci
61
+ env:
62
+ BOOTSTRAP_VERSION: '~> 5.0'
63
+ ENGINE_CART_RAILS_OPTIONS: '--skip-git --skip-listen --skip-spring --skip-keeps --skip-action-cable --skip-coffee --skip-test'
46
64
  test_rails6_0:
47
65
  runs-on: ubuntu-latest
48
66
  strategy:
@@ -87,7 +105,7 @@ jobs:
87
105
  runs-on: ubuntu-latest
88
106
  strategy:
89
107
  matrix:
90
- ruby: [2.7]
108
+ ruby: [2.7, 3.0]
91
109
  steps:
92
110
  - uses: actions/checkout@v2
93
111
  - name: Set up Ruby
data/.rubocop.yml CHANGED
@@ -50,6 +50,10 @@ Metrics/ModuleLength:
50
50
  - 'app/controllers/concerns/blacklight/catalog.rb'
51
51
  - 'lib/blacklight/solr/search_builder_behavior.rb'
52
52
 
53
+ Metrics/AbcSize:
54
+ Exclude:
55
+ - "lib/blacklight/search_state/filter_field.rb"
56
+
53
57
  Naming/HeredocDelimiterNaming:
54
58
  Enabled: false
55
59
 
data/README.md CHANGED
@@ -35,7 +35,7 @@ rails generate blacklight:install
35
35
 
36
36
  * Ruby 2.2+
37
37
  * Bundler
38
- * Rails 5.1+
38
+ * Rails 5.2+
39
39
 
40
40
  ## Contributing Code
41
41
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 7.19.0
1
+ 7.20.1
@@ -36,7 +36,12 @@ Blacklight.listeners().forEach(function (listener) {
36
36
  Blacklight.activate();
37
37
  });
38
38
  });
39
- $('.no-js').removeClass('no-js').addClass('js');
39
+ Blacklight.onLoad(function () {
40
+ const elem = document.querySelector('.no-js');
41
+ if (!elem) return;
42
+ elem.classList.remove('no-js');
43
+ elem.classList.add('js');
44
+ });
40
45
  /*global Bloodhound */
41
46
 
42
47
  Blacklight.onLoad(function () {
@@ -352,7 +357,7 @@ Blacklight.modal.modalCloseSelector = '[data-blacklight-modal~=close]'; // Calle
352
357
 
353
358
  Blacklight.modal.onFailure = function (jqXHR, textStatus, errorThrown) {
354
359
  console.error('Server error:', this.url, jqXHR.status, errorThrown);
355
- var contents = '<div class="modal-header">' + '<div class="modal-title">There was a problem with your request.</div>' + '<button type="button" class="blacklight-modal-close close" data-dismiss="modal" aria-label="Close">' + ' <span aria-hidden="true">&times;</span>' + '</button></div>' + ' <div class="modal-body"><p>Expected a successful response from the server, but got an error</p>' + '<pre>' + this.type + ' ' + this.url + "\n" + jqXHR.status + ': ' + errorThrown + '</pre></div>';
360
+ var contents = '<div class="modal-header">' + '<div class="modal-title">There was a problem with your request.</div>' + '<button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" aria-label="Close">' + ' <span aria-hidden="true">&times;</span>' + '</button></div>' + ' <div class="modal-body"><p>Expected a successful response from the server, but got an error</p>' + '<pre>' + this.type + ' ' + this.url + "\n" + jqXHR.status + ': ' + errorThrown + '</pre></div>';
356
361
  $(Blacklight.modal.modalSelector).find('.modal-content').html(contents);
357
362
  $(Blacklight.modal.modalSelector).modal('show');
358
363
  };
@@ -446,7 +451,11 @@ Blacklight.doSearchContextBehavior = function () {
446
451
  Blacklight.handleSearchContextMethod.call(e.currentTarget, e);
447
452
  });
448
453
  });
449
- }; // this is the Rails.handleMethod with a couple adjustments, described inline:
454
+ };
455
+
456
+ Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content;
457
+
458
+ Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content; // this is the Rails.handleMethod with a couple adjustments, described inline:
450
459
  // first, we're attaching this directly to the event handler, so we can check for meta-keys
451
460
 
452
461
 
@@ -460,8 +469,8 @@ Blacklight.handleSearchContextMethod = function (event) {
460
469
 
461
470
  let href = link.getAttribute('data-context-href');
462
471
  let target = link.getAttribute('target');
463
- let csrfToken = Rails.csrfToken();
464
- let csrfParam = Rails.csrfParam();
472
+ let csrfToken = Blacklight.csrfToken();
473
+ let csrfParam = Blacklight.csrfParam();
465
474
  let form = document.createElement('form');
466
475
  form.method = 'post';
467
476
  form.action = href;
@@ -2,11 +2,11 @@
2
2
  dt {
3
3
  font-weight: normal;
4
4
  color: $field_name_color;
5
- @media (max-width: breakpoint-max(sm)) {
5
+ @media (max-width: breakpoint-min(md)) {
6
6
  text-align: left;
7
7
  }
8
8
 
9
- @media (min-width: breakpoint-max(sm)) {
9
+ @media (min-width: breakpoint-min(md)) {
10
10
  text-align: right;
11
11
  }
12
12
  }
@@ -5,7 +5,7 @@
5
5
  // Facet field headings and buttons
6
6
  .facet-field-heading {
7
7
  border-bottom: 0;
8
-
8
+
9
9
  button {
10
10
  font-weight: $headings-font-weight;
11
11
 
@@ -1,27 +1,30 @@
1
1
  .constraints-container {
2
2
  @extend .mb-2;
3
+ @extend .d-flex;
3
4
  }
4
5
 
5
6
  .applied-filter {
7
+ @extend .mx-1;
8
+
6
9
  .constraint-value {
7
10
  cursor: default;
8
11
  text-overflow: ellipsis;
9
12
  overflow: hidden;
10
13
 
11
- @media (max-width: breakpoint-max(xs)) {
12
- max-width: breakpoint-max(xs) / 2;
14
+ @media (max-width: breakpoint-min(sm)) {
15
+ max-width: breakpoint-min(sm) * .5;
13
16
  }
14
17
 
15
18
  @media (min-width: breakpoint-min(sm)) and (max-width: breakpoint-max(sm)) {
16
- max-width: breakpoint-min(sm) / 2;
19
+ max-width: breakpoint-min(sm) * .5;
17
20
  }
18
21
 
19
22
  @media (min-width: breakpoint-min(md)) and (max-width: breakpoint-max(md)) {
20
- max-width: breakpoint-min(md) / 2;
23
+ max-width: breakpoint-min(md) * .5;
21
24
  }
22
25
 
23
26
  @media (min-width: breakpoint-min(lg)) {
24
- max-width: breakpoint-min(lg) / 2;
27
+ max-width: breakpoint-min(lg) * .5;
25
28
  }
26
29
 
27
30
  &:hover, &:active {
@@ -1,3 +1,10 @@
1
+ .search-widgets {
2
+ @extend .d-flex;
3
+ > * {
4
+ @extend .mx-1;
5
+ }
6
+ }
7
+
1
8
  .sort-pagination,
2
9
  .pagination-search-widgets {
3
10
  border-bottom: $pagination-border-width solid $pagination-border-color;
@@ -34,7 +41,8 @@
34
41
  display: inline-block;
35
42
 
36
43
  .caption {
37
- @extend .sr-only;
44
+ @extend .sr-only !optional;
45
+ @extend .visually-hidden !optional;
38
46
  }
39
47
  }
40
48
 
@@ -3,7 +3,8 @@
3
3
  border-color: $navbar-light-toggler-border-color;
4
4
  color: $navbar-light-active-color;
5
5
 
6
- @include hover-focus {
6
+ &:hover,
7
+ &:focus {
7
8
  color: $navbar-light-active-color;
8
9
  }
9
10
  }
@@ -82,7 +83,8 @@
82
83
  .remove {
83
84
  color: $text-muted;
84
85
  font-weight: bold;
85
- padding-left: $spacer / 2;
86
+ padding-left: $spacer * .5;
87
+ text-decoration: none;
86
88
 
87
89
  &:hover {
88
90
  color: theme-color("danger");
@@ -102,7 +104,7 @@
102
104
  padding-right: 1em;
103
105
  text-indent: -15px;
104
106
  padding-left: 15px;
105
- padding-bottom: $spacer / 2;
107
+ padding-bottom: $spacer * .5;
106
108
  @include hyphens-auto;
107
109
  }
108
110
 
@@ -44,7 +44,12 @@
44
44
  .submit-search-text {
45
45
  // hide 'search' label at very small screens
46
46
  @media screen and (max-width: breakpoint-max(xs)) {
47
- @include sr-only();
47
+ @if mixin-exists(sr-only) {
48
+ @include sr-only();
49
+ }
50
+ @if mixin-exists(visually-hidden) {
51
+ @include visually-hidden();
52
+ }
48
53
  }
49
54
  }
50
55
  }
@@ -6,7 +6,7 @@
6
6
  }
7
7
 
8
8
  .record-padding {
9
- margin-top: $spacer / 2;
9
+ margin-top: $spacer * .5;
10
10
  }
11
11
 
12
12
  .pagination {
@@ -1,4 +1,5 @@
1
1
  .twitter-typeahead {
2
+ display: inline-flex !important;
2
3
  flex-grow: 2;
3
4
  z-index: $zindex-typeahead;
4
5
 
@@ -10,7 +10,7 @@
10
10
  <% if @remove_path.present? %>
11
11
  <%= link_to(@remove_path, class: 'btn btn-outline-secondary remove') do %>
12
12
  <span class="remove-icon" aria-hidden="true">✖</span>
13
- <span class="sr-only">
13
+ <span class="sr-only visually-hidden">
14
14
  <%= if @label.blank?
15
15
  t('blacklight.search.filters.remove.value', value: @value)
16
16
  else
@@ -1,9 +1,9 @@
1
1
  <%= content_tag :div, id: @id, class: @classes do %>
2
- <h2 class="sr-only"><%= t('blacklight.search.search_constraints_header') %></h2>
2
+ <h2 class="sr-only visually-hidden"><%= t('blacklight.search.search_constraints_header') %></h2>
3
3
 
4
4
  <%= link_to t('blacklight.search.start_over'), start_over_path, class: "catalog_startOverLink btn btn-primary" %>
5
5
 
6
- <span class="constraints-label sr-only"><%= t('blacklight.search.filters.title') %></span>
6
+ <span class="constraints-label sr-only visually-hidden"><%= t('blacklight.search.filters.title') %></span>
7
7
  <% if query_constraints_area.present? %>
8
8
  <% query_constraints_area.each do |constraint| %>
9
9
  <%= constraint %>
@@ -63,10 +63,14 @@ module Blacklight
63
63
 
64
64
  Deprecation.silence(Blacklight::SearchState) do
65
65
  @search_state.filters.map do |facet|
66
+ missing_facet = @search_state.params.dig("f", "-#{facet.key}:").present?
66
67
  facet.values.map do |val|
67
- next if val.blank? # skip empty string
68
+ next if val.blank? && !missing_facet
68
69
 
69
- if val.is_a?(Array)
70
+ if missing_facet && val.blank?
71
+ missing = I18n.t("blacklight.search.facets.missing")
72
+ yield facet_item_presenter(facet.config, missing, facet.key)
73
+ elsif val.is_a?(Array)
70
74
  yield inclusive_facet_item_presenter(facet.config, val, facet.key) if val.any?(&:present?)
71
75
  else
72
76
  yield facet_item_presenter(facet.config, val, facet.key)
@@ -121,10 +121,10 @@ module Blacklight
121
121
  end
122
122
 
123
123
  def before_render
124
- set_slot(:title) unless title
125
- set_slot(:thumbnail, component: @thumbnail_component || presenter.view_config&.thumbnail_component) unless thumbnail || show?
126
- set_slot(:metadata, component: @metadata_component, fields: presenter.field_presenters) unless metadata
127
- set_slot(:embed, component: @embed_component || presenter.view_config&.embed_component) unless embed
124
+ set_slot(:title, nil) unless title
125
+ set_slot(:thumbnail, nil, component: @thumbnail_component || presenter.view_config&.thumbnail_component) unless thumbnail || show?
126
+ set_slot(:metadata, nil, component: @metadata_component, fields: presenter.field_presenters) unless metadata
127
+ set_slot(:embed, nil, component: @embed_component || presenter.view_config&.embed_component) unless embed
128
128
  end
129
129
 
130
130
  private
@@ -2,9 +2,11 @@
2
2
  <h3 class="card-header p-0 facet-field-heading" id="<%= @facet_field.html_id %>-header">
3
3
  <button
4
4
  type="button"
5
- class="btn btn-block p-2 text-left collapse-toggle <%= "collapsed" if @facet_field.collapsed? %>"
5
+ class="btn w-100 d-block btn-block p-2 text-start text-left collapse-toggle <%= "collapsed" if @facet_field.collapsed? %>"
6
6
  data-toggle="collapse"
7
+ data-bs-toggle="collapse"
7
8
  data-target="#<%= @facet_field.html_id %>"
9
+ data-bs-target="#<%= @facet_field.html_id %>"
8
10
  aria-expanded="<%= @facet_field.collapsed? ? 'false' : 'true' %>"
9
11
  >
10
12
  <%= label %>
@@ -10,7 +10,7 @@ module Blacklight
10
10
  def initialize(**); end
11
11
 
12
12
  def call
13
- body
13
+ body.to_s
14
14
  end
15
15
  end
16
16
  end
@@ -27,7 +27,7 @@ module Blacklight
27
27
  render_facet_value
28
28
  end
29
29
 
30
- return if content.blank?
30
+ return '' if content.blank?
31
31
  return content unless @wrapping_element
32
32
 
33
33
  content_tag @wrapping_element, content
@@ -72,7 +72,7 @@ module Blacklight
72
72
  # @private
73
73
  def render_facet_value
74
74
  tag.span(class: "facet-label") do
75
- link_to_unless(@suppress_link, @label, @href, class: "facet-select")
75
+ link_to_unless(@suppress_link, @label, @href, class: "facet-select", rel: "nofollow")
76
76
  end + render_facet_count
77
77
  end
78
78
 
@@ -85,9 +85,9 @@ module Blacklight
85
85
  tag.span(class: "facet-label") do
86
86
  tag.span(@label, class: "selected") +
87
87
  # remove link
88
- link_to(@href, class: "remove") do
88
+ link_to(@href, class: "remove", rel: "nofollow") do
89
89
  tag.span('✖', class: "remove-icon", aria: { hidden: true }) +
90
- tag.span(@view_context.t(:'blacklight.search.facets.selected.remove'), class: 'sr-only')
90
+ tag.span(@view_context.t(:'blacklight.search.facets.selected.remove'), class: 'sr-only visually-hidden')
91
91
  end
92
92
  end + render_facet_count(classes: ["selected"])
93
93
  end
@@ -56,7 +56,7 @@ module Blacklight
56
56
 
57
57
  def facet_toggle_button(id)
58
58
  content_tag 'button', class: 'btn facet-toggle-handle collapsed',
59
- data: { toggle: 'collapse', target: "##{id}" },
59
+ data: { toggle: 'collapse', 'bs-toggle': 'collapse', target: "##{id}", 'bs-target': "##{id}" },
60
60
  aria: { expanded: false, controls: id, describedby: "#{id}_label" } do
61
61
  concat toggle_icon(:show)
62
62
  concat toggle_icon(:hide)
@@ -66,7 +66,7 @@ module Blacklight
66
66
  def toggle_icon(type)
67
67
  content_tag 'span', class: type do
68
68
  concat @icons[type]
69
- concat content_tag('span', t(type, scope: 'blacklight.search.facets.pivot'), class: 'sr-only')
69
+ concat content_tag('span', t(type, scope: 'blacklight.search.facets.pivot'), class: 'sr-only visually-hidden')
70
70
  end
71
71
  end
72
72
 
@@ -23,7 +23,7 @@ module Blacklight
23
23
  end
24
24
 
25
25
  def value(*args, **kwargs, &block)
26
- return set_slot(:values, *args, **kwargs, &block) if block_given?
26
+ return set_slot(:values, nil, *args, **kwargs, &block) if block_given?
27
27
 
28
28
  Deprecation.warn(Blacklight::MetadataFieldLayoutComponent, 'The `value` content area is deprecated; render from the values slot instead')
29
29
 
@@ -9,6 +9,8 @@
9
9
  data: {
10
10
  toggle: 'collapse',
11
11
  target: "##{@panel_id}",
12
+ 'bs-toggle': 'collapse',
13
+ 'bs-target': "##{@panel_id}"
12
14
  },
13
15
  aria: {
14
16
  controls: @panel_id,
@@ -1,3 +1,3 @@
1
- <%= content_tag :nav, class: 'pagination', role: 'region', **@html_attr do %>
1
+ <%= content_tag :section, class: 'pagination', **@html_attr do %>
2
2
  <%= pagination %>
3
3
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <div class="view-type">
2
- <span class="sr-only"><%= t('blacklight.search.view_title') %></span>
2
+ <span class="sr-only visually-hidden"><%= t('blacklight.search.view_title') %></span>
3
3
  <div class="view-type-group btn-group">
4
4
  <% views.each do |view| %>
5
5
  <%= view %>
@@ -1,7 +1,7 @@
1
1
  <%= form_tag @url, method: @method, class: @classes.join(' '), role: 'search', 'aria-label' => scoped_t('submit') do %>
2
2
  <%= render_hash_as_hidden_fields(@params) %>
3
3
  <% if @search_fields.length > 1 %>
4
- <label for="search_field" class="sr-only"><%= scoped_t('search_field.label') %></label>
4
+ <label for="search_field" class="sr-only visually-hidden"><%= scoped_t('search_field.label') %></label>
5
5
  <% end %>
6
6
  <div class="input-group">
7
7
  <%= prepend %>
@@ -11,12 +11,12 @@
11
11
  options_for_select(@search_fields, h(@search_field)),
12
12
  title: scoped_t('search_field.title'),
13
13
  id: "#{@prefix}search_field",
14
- class: "custom-select search-field") %>
14
+ class: "custom-select form-select search-field") %>
15
15
  <% elsif @search_fields.length == 1 %>
16
16
  <%= hidden_field_tag :search_field, @search_fields.first.last %>
17
17
  <% end %>
18
18
 
19
- <label for="<%= @prefix %><%= @query_param %>" class="sr-only"><%= scoped_t('search.label') %></label>
19
+ <label for="<%= @prefix %><%= @query_param %>" class="sr-only visually-hidden"><%= scoped_t('search.label') %></label>
20
20
  <%= text_field_tag @query_param, @q, placeholder: scoped_t('search.placeholder'), class: "search-q q form-control rounded-#{@search_fields.length > 1 ? '0' : 'left'}", id: "#{@prefix}q", autocomplete: autocomplete_path.present? ? "off" : "", autofocus: @autofocus, data: { autocomplete_enabled: autocomplete_path.present?, autocomplete_path: autocomplete_path } %>
21
21
 
22
22
  <span class="input-group-append">
@@ -4,7 +4,7 @@ module Blacklight
4
4
  module System
5
5
  class DropdownComponent < ViewComponent::Base
6
6
  renders_one :button, (lambda do |classes:, label:|
7
- button_tag class: classes, aria: { expanded: false }, data: { toggle: 'dropdown' } do
7
+ button_tag class: classes, aria: { expanded: false }, data: { toggle: 'dropdown', 'bs-toggle': 'dropdown' } do
8
8
  safe_join([label, content_tag(:span, '', class: 'caret')])
9
9
  end
10
10
  end)
@@ -1,4 +1,4 @@
1
1
  <div class="alert <%= @classes %>">
2
2
  <%= message %>
3
- <a class="close" data-dismiss="alert" href="#">&times;</a>
3
+ <a class="btn-close close" data-dismiss="alert" href="#">&times;</a>
4
4
  </div>
@@ -6,7 +6,7 @@
6
6
  <h1 class="modal-title"><%= title %></h1>
7
7
  <% end) %>
8
8
 
9
- <button type="button" class="blacklight-modal-close close" data-dismiss="modal" aria-label="<%= t('blacklight.modal.close') %>">
9
+ <button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" aria-label="<%= t('blacklight.modal.close') %>">
10
10
  <span aria-hidden="true">&times;</span>
11
11
  </button>
12
12
  </div>
@@ -48,9 +48,6 @@ module Blacklight::Bookmarks
48
48
  format.html {}
49
49
  format.rss { render layout: false }
50
50
  format.atom { render layout: false }
51
- format.json do
52
- render json: render_search_results_as_json
53
- end
54
51
 
55
52
  additional_response_formats(format)
56
53
  document_export_formats(format)
@@ -2,6 +2,9 @@
2
2
  module Blacklight::Catalog
3
3
  extend ActiveSupport::Concern
4
4
 
5
+ # MimeResponds is part of ActionController::Base, but not ActionController::API
6
+ include ActionController::MimeResponds
7
+
5
8
  include Blacklight::Base
6
9
  include Blacklight::Facet
7
10
  include Blacklight::Searchable
@@ -3,6 +3,7 @@
3
3
  # as this module is mixed-in to the application controller in the hosting app on installation.
4
4
  module Blacklight::Controller
5
5
  extend ActiveSupport::Concern
6
+ extend Deprecation
6
7
 
7
8
  included do
8
9
  include Blacklight::SearchFields
@@ -38,11 +39,6 @@ module Blacklight::Controller
38
39
  # TODO: move to Searchable
39
40
  class_attribute :search_service_class
40
41
  self.search_service_class = Blacklight::SearchService
41
-
42
- # This callback runs when a user first logs in
43
-
44
- define_callbacks :logging_in_user
45
- set_callback :logging_in_user, :before, :transfer_guest_user_actions_to_current_user
46
42
  end
47
43
 
48
44
  # @private
@@ -136,6 +132,13 @@ module Blacklight::Controller
136
132
 
137
133
  ##
138
134
  # When a user logs in, transfer any saved searches or bookmarks to the current_user
135
+ def transfer_guest_to_user
136
+ Deprecation.silence(Blacklight::Controller) do
137
+ transfer_guest_user_actions_to_current_user
138
+ end
139
+ end
140
+
141
+ # @deprecated use canonical `transfer_guest_to_user` method instead
139
142
  def transfer_guest_user_actions_to_current_user
140
143
  return unless respond_to?(:current_user) && respond_to?(:guest_user) && current_user && guest_user
141
144
 
@@ -155,6 +158,7 @@ module Blacklight::Controller
155
158
  # let guest_user know we've moved some bookmarks from under it
156
159
  guest_user.reload if guest_user.persisted?
157
160
  end
161
+ deprecation_deprecate :transfer_guest_user_actions_to_current_user
158
162
 
159
163
  ##
160
164
  # To handle failed authorization attempts, redirect the user to the
@@ -73,7 +73,7 @@ module Blacklight::SearchContext
73
73
  #
74
74
  def agent_is_crawler?
75
75
  crawler_proc = blacklight_config.crawler_detector
76
- return false if crawler_proc.nil? || current_user.present?
76
+ return false if crawler_proc.nil? || (defined?(current_user) && current_user.present?)
77
77
 
78
78
  crawler_proc.call(request)
79
79
  end
@@ -47,11 +47,9 @@ module Blacklight::CatalogHelperBehavior
47
47
  entry_name = if entry_name
48
48
  entry_name.pluralize(collection.size, I18n.locale)
49
49
  else
50
- collection.entry_name(count: collection.size).to_s.downcase
50
+ collection.entry_name(count: collection.size).to_s
51
51
  end
52
52
 
53
- entry_name = entry_name.pluralize unless collection.total_count == 1
54
-
55
53
  # grouped response objects need special handling
56
54
  end_num = if collection.respond_to?(:groups) && render_grouped_response?(collection)
57
55
  collection.groups.length
@@ -14,7 +14,7 @@ module Blacklight
14
14
  if action_opts.path
15
15
  send(action_opts.path, url_opts)
16
16
  elsif url_opts[:id].class.respond_to?(:model_name)
17
- url_for([action_opts.key, url_opts[:id]])
17
+ url_for([action_opts.key.to_sym, url_opts[:id]])
18
18
  else
19
19
  send("#{action_opts.key}_#{controller_name}_path", url_opts)
20
20
  end
@@ -38,4 +38,11 @@ Blacklight.listeners().forEach(function(listener) {
38
38
  })
39
39
  })
40
40
 
41
- $('.no-js').removeClass('no-js').addClass('js');
41
+ Blacklight.onLoad(function () {
42
+ const elem = document.querySelector('.no-js');
43
+
44
+ if (!elem) return;
45
+
46
+ elem.classList.remove('no-js')
47
+ elem.classList.add('js')
48
+ })
@@ -106,7 +106,7 @@ Blacklight.modal.onFailure = function(jqXHR, textStatus, errorThrown) {
106
106
 
107
107
  var contents = '<div class="modal-header">' +
108
108
  '<div class="modal-title">There was a problem with your request.</div>' +
109
- '<button type="button" class="blacklight-modal-close close" data-dismiss="modal" aria-label="Close">' +
109
+ '<button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" aria-label="Close">' +
110
110
  ' <span aria-hidden="true">&times;</span>' +
111
111
  '</button></div>' +
112
112
  ' <div class="modal-body"><p>Expected a successful response from the server, but got an error</p>' +
@@ -15,6 +15,9 @@ Blacklight.doSearchContextBehavior = function() {
15
15
  })
16
16
  };
17
17
 
18
+ Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content
19
+ Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content
20
+
18
21
  // this is the Rails.handleMethod with a couple adjustments, described inline:
19
22
  // first, we're attaching this directly to the event handler, so we can check for meta-keys
20
23
  Blacklight.handleSearchContextMethod = function(event) {
@@ -27,8 +30,8 @@ Blacklight.handleSearchContextMethod = function(event) {
27
30
  // instead of using the normal href, we need to use the context href instead
28
31
  let href = link.getAttribute('data-context-href')
29
32
  let target = link.getAttribute('target')
30
- let csrfToken = Rails.csrfToken()
31
- let csrfParam = Rails.csrfParam()
33
+ let csrfToken = Blacklight.csrfToken()
34
+ let csrfParam = Blacklight.csrfParam()
32
35
  let form = document.createElement('form')
33
36
  form.method = 'post'
34
37
  form.action = href