blacklight 8.0.0.beta1 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/.env +1 -0
  3. data/.github/workflows/ruby.yml +6 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop_todo.yml +3 -1
  6. data/README.md +1 -1
  7. data/Rakefile +18 -0
  8. data/VERSION +1 -1
  9. data/app/assets/config/blacklight/manifest.js +3 -0
  10. data/app/assets/javascripts/blacklight/blacklight.esm.js +132 -140
  11. data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -1
  12. data/app/assets/javascripts/blacklight/blacklight.js +132 -140
  13. data/app/assets/javascripts/blacklight/blacklight.js.map +1 -1
  14. data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +1 -1
  15. data/app/assets/stylesheets/blacklight/_facets.scss +32 -5
  16. data/app/assets/stylesheets/blacklight/blacklight_defaults.scss +1 -1
  17. data/app/components/blacklight/advanced_search_form_component.rb +3 -3
  18. data/app/components/blacklight/document/actions_component.rb +1 -1
  19. data/app/components/blacklight/document/show_tools_component.html.erb +12 -0
  20. data/app/components/blacklight/document/show_tools_component.rb +23 -0
  21. data/app/components/blacklight/document_component.rb +48 -20
  22. data/app/components/blacklight/document_metadata_component.html.erb +4 -2
  23. data/app/components/blacklight/document_metadata_component.rb +8 -2
  24. data/app/components/blacklight/facet_field_checkboxes_component.html.erb +2 -2
  25. data/app/components/blacklight/facet_field_list_component.html.erb +2 -2
  26. data/app/components/blacklight/metadata_field_component.html.erb +2 -2
  27. data/app/components/blacklight/metadata_field_layout_component.rb +7 -4
  28. data/app/components/blacklight/metadata_field_plain_text_layout_component.rb +17 -0
  29. data/app/components/blacklight/response/facet_group_component.html.erb +2 -1
  30. data/app/components/blacklight/response/facet_group_component.rb +1 -1
  31. data/app/components/blacklight/response/sort_component.rb +1 -1
  32. data/app/components/blacklight/response/spellcheck_component.rb +1 -1
  33. data/app/components/blacklight/response/view_type_button_component.rb +1 -1
  34. data/app/components/blacklight/response/view_type_component.rb +2 -2
  35. data/app/components/blacklight/search/sidebar_component.html.erb +1 -1
  36. data/app/components/blacklight/search_context/server_applied_params_component.html.erb +4 -0
  37. data/app/components/blacklight/search_context/server_applied_params_component.rb +13 -0
  38. data/app/components/blacklight/search_context/server_item_pagination_component.rb +71 -0
  39. data/app/components/blacklight/system/dropdown_component.rb +3 -3
  40. data/app/components/blacklight/system/flash_message_component.rb +2 -2
  41. data/app/components/blacklight/system/modal_component.rb +1 -1
  42. data/app/components/blacklight/top_navbar_component.html.erb +1 -1
  43. data/app/components/blacklight/top_navbar_component.rb +4 -0
  44. data/app/controllers/concerns/blacklight/bookmarks.rb +1 -1
  45. data/app/controllers/concerns/blacklight/search_context.rb +46 -6
  46. data/app/helpers/blacklight/blacklight_helper_behavior.rb +0 -101
  47. data/app/helpers/blacklight/catalog_helper_behavior.rb +27 -54
  48. data/app/helpers/blacklight/component_helper_behavior.rb +0 -11
  49. data/app/helpers/blacklight/document_helper_behavior.rb +76 -0
  50. data/app/helpers/blacklight/layout_helper_behavior.rb +59 -0
  51. data/app/helpers/blacklight/render_partials_helper_behavior.rb +1 -0
  52. data/app/helpers/blacklight/url_helper_behavior.rb +11 -3
  53. data/app/javascript/blacklight/bookmark_toggle.js +8 -15
  54. data/app/javascript/blacklight/button_focus.js +10 -10
  55. data/app/javascript/blacklight/index.js +6 -5
  56. data/app/javascript/blacklight/modal.js +24 -24
  57. data/app/javascript/blacklight/search_context.js +51 -57
  58. data/app/models/concerns/blacklight/document.rb +11 -0
  59. data/app/models/record_mailer.rb +19 -15
  60. data/app/presenters/blacklight/document_presenter.rb +13 -11
  61. data/app/presenters/blacklight/index_presenter.rb +1 -1
  62. data/app/presenters/blacklight/rendering/abstract_step.rb +4 -0
  63. data/app/presenters/blacklight/rendering/join.rb +7 -1
  64. data/app/presenters/blacklight/rendering/link_to_facet.rb +1 -1
  65. data/app/presenters/blacklight/rendering/microdata.rb +1 -1
  66. data/app/services/blacklight/search_service.rb +2 -1
  67. data/app/views/catalog/_document.atom.builder +1 -1
  68. data/app/views/catalog/_document.html.erb +3 -8
  69. data/app/views/catalog/_document_list.html.erb +2 -1
  70. data/app/views/catalog/_facet_layout.html.erb +2 -2
  71. data/app/views/catalog/_show_main_content.html.erb +5 -11
  72. data/app/views/catalog/_show_tools.html.erb +1 -14
  73. data/app/views/catalog/facet.html.erb +3 -3
  74. data/app/views/catalog/index.html.erb +3 -0
  75. data/app/views/catalog/show.html.erb +1 -6
  76. data/app/views/layouts/blacklight/base.html.erb +4 -5
  77. data/app/views/record_mailer/email_record.text.erb +4 -3
  78. data/app/views/record_mailer/sms_record.text.erb +4 -4
  79. data/blacklight.gemspec +2 -2
  80. data/config/importmap.rb +1 -1
  81. data/config/locales/blacklight.ar.yml +209 -232
  82. data/config/locales/blacklight.ca.yml +0 -6
  83. data/config/locales/blacklight.de.yml +197 -215
  84. data/config/locales/blacklight.en.yml +2 -6
  85. data/config/locales/blacklight.es.yml +196 -215
  86. data/config/locales/blacklight.fr.yml +196 -219
  87. data/config/locales/blacklight.hu.yml +193 -212
  88. data/config/locales/blacklight.it.yml +197 -215
  89. data/config/locales/blacklight.nl.yml +193 -212
  90. data/config/locales/blacklight.pt-BR.yml +195 -215
  91. data/config/locales/blacklight.sq.yml +193 -212
  92. data/config/locales/blacklight.zh.yml +194 -213
  93. data/docker-compose.yml +1 -0
  94. data/lib/blacklight/configuration/session_tracking_config.rb +45 -0
  95. data/lib/blacklight/configuration/view_config.rb +0 -4
  96. data/lib/blacklight/configuration.rb +8 -4
  97. data/lib/blacklight/engine.rb +6 -7
  98. data/lib/blacklight/routes/searchable.rb +1 -0
  99. data/lib/generators/blacklight/assets/importmap_generator.rb +9 -8
  100. data/lib/generators/blacklight/assets/propshaft_generator.rb +11 -2
  101. data/lib/generators/blacklight/assets/sprockets_generator.rb +2 -0
  102. data/lib/generators/blacklight/assets_generator.rb +3 -1
  103. data/lib/generators/blacklight/install_generator.rb +3 -1
  104. data/lib/generators/blacklight/templates/solr_document.rb +0 -6
  105. data/package.json +7 -4
  106. data/rollup.config.js +11 -1
  107. data/spec/components/blacklight/document_component_spec.rb +66 -15
  108. data/spec/components/blacklight/header_component_spec.rb +20 -0
  109. data/spec/components/blacklight/response/view_type_component_spec.rb +17 -0
  110. data/spec/components/blacklight/search_bar_component_spec.rb +1 -1
  111. data/spec/components/blacklight/search_context/server_applied_params_component_spec.rb +29 -0
  112. data/spec/components/blacklight/{search_context_component_spec.rb → search_context/server_item_pagination_component_spec.rb} +15 -4
  113. data/spec/controllers/bookmarks_controller_spec.rb +1 -1
  114. data/spec/controllers/catalog_controller_spec.rb +18 -6
  115. data/spec/features/component_template_override_spec.rb +8 -0
  116. data/spec/features/modal_spec.rb +12 -0
  117. data/spec/features/sitelinks_search_box_spec.rb +13 -0
  118. data/spec/helpers/blacklight/url_helper_behavior_spec.rb +1 -1
  119. data/spec/helpers/blacklight_helper_spec.rb +0 -56
  120. data/spec/helpers/catalog_helper_spec.rb +56 -0
  121. data/spec/lib/blacklight/configuration/session_tracking_config_spec.rb +38 -0
  122. data/spec/lib/blacklight/open_struct_with_hash_access_spec.rb +2 -2
  123. data/spec/models/blacklight/configuration_spec.rb +2 -2
  124. data/spec/models/blacklight/icon_spec.rb +0 -2
  125. data/spec/models/blacklight/search_builder_spec.rb +3 -3
  126. data/spec/models/blacklight/solr/request_spec.rb +2 -2
  127. data/spec/models/record_mailer_spec.rb +36 -23
  128. data/spec/presenters/blacklight/document_presenter_spec.rb +1 -0
  129. data/spec/presenters/blacklight/field_presenter_spec.rb +2 -2
  130. data/spec/presenters/blacklight/index_presenter_spec.rb +1 -0
  131. data/spec/presenters/blacklight/show_presenter_spec.rb +1 -0
  132. data/spec/presenters/pipeline_spec.rb +13 -2
  133. data/spec/routing/catalog_routing_spec.rb +4 -0
  134. data/spec/spec_helper.rb +1 -1
  135. data/spec/support/view_component_test_helpers.rb +21 -0
  136. data/spec/test_app_templates/Gemfile.extra +4 -0
  137. data/spec/test_app_templates/lib/generators/test_app_generator.rb +8 -0
  138. data/spec/views/catalog/_document.html.erb_spec.rb +22 -1
  139. data/spec/views/catalog/_document_list.html.erb_spec.rb +7 -2
  140. data/spec/views/catalog/_show_tools.html.erb_spec.rb +2 -2
  141. data/spec/views/catalog/index.json.jbuilder_spec.rb +1 -1
  142. data/tasks/blacklight.rake +1 -1
  143. metadata +39 -21
  144. data/app/components/blacklight/search_context_component.rb +0 -68
  145. data/app/models/concerns/blacklight/document/email.rb +0 -27
  146. data/app/models/concerns/blacklight/document/sms.rb +0 -25
  147. data/spec/features/sitelinks_search_box.rb +0 -13
  148. data/spec/models/blacklight/document/email_spec.rb +0 -57
  149. data/spec/models/blacklight/document/sms_spec.rb +0 -58
  150. data/spec/support/view_component_capybara_test_helpers.rb +0 -8
  151. /data/app/components/blacklight/{search_context_component.html.erb → search_context/server_item_pagination_component.html.erb} +0 -0
@@ -1,7 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'view_component/version'
4
+
3
5
  module Blacklight
6
+ ##
7
+ # A component for rendering a single document
8
+ #
9
+ # @note when subclassing this component, if you override the initializer,
10
+ # you must explicitly specify the counter variable `document_counter` even if you don't use it.
11
+ # Otherwise view_component will not provide the count value when calling the component.
12
+ #
13
+ # @see https://viewcomponent.org/guide/collections.html#collection-counter
14
+ #
15
+ # @example
16
+ # class MyDocumentComponent < Blacklight::DocumentComponent
17
+ # def initialize(document_counter: nil, **kwargs)
18
+ # super
19
+ # ... custom code ...
20
+ # end
21
+ # end
4
22
  class DocumentComponent < Blacklight::Component
23
+ with_collection_parameter :document
24
+
25
+ # ViewComponent 3 changes iteration counters to begin at 0 rather than 1
26
+ COLLECTION_INDEX_OFFSET = ViewComponent::VERSION::MAJOR < 3 ? 0 : 1
27
+
5
28
  # Content appearing before the document
6
29
  renders_one :header
7
30
 
@@ -14,7 +37,7 @@ module Blacklight
14
37
 
15
38
  # The document title with some reasonable default behavior
16
39
  renders_one :title, (lambda do |*args, component: nil, **kwargs|
17
- component ||= Blacklight::DocumentTitleComponent
40
+ component ||= @presenter&.view_config&.title_component || Blacklight::DocumentTitleComponent
18
41
 
19
42
  component.new(*args, counter: @counter, document: @document, presenter: @presenter, as: @title_component, actions: !@show, link_to_document: !@show, document_component: self, **kwargs)
20
43
  end)
@@ -33,7 +56,7 @@ module Blacklight
33
56
  renders_one :metadata, (lambda do |static_content = nil, *args, component: nil, fields: nil, **kwargs|
34
57
  next static_content if static_content.present?
35
58
 
36
- component ||= Blacklight::DocumentMetadataComponent
59
+ component ||= @presenter&.view_config&.metadata_component || Blacklight::DocumentMetadataComponent
37
60
 
38
61
  component.new(*args, fields: fields || @presenter&.field_presenters || [], **kwargs)
39
62
  end)
@@ -56,38 +79,36 @@ module Blacklight
56
79
  # Backwards compatibility
57
80
  renders_one :actions
58
81
 
59
- with_collection_parameter :document
60
-
61
82
  # rubocop:disable Metrics/ParameterLists
62
- # @param document [Blacklight::Document]
63
- # @param presenter [Blacklight::DocumentPresenter]
83
+ # @param document [Blacklight::DocumentPresenter]
84
+ # @param presenter [Blacklight::DocumentPresenter] alias for document
85
+ # @param partials [Array, nil] view partial names that should be used to provide content for the `partials` slot
64
86
  # @param id [String] HTML id for the root element
65
87
  # @param classes [Array, String] additional HTML classes for the root element
66
88
  # @param component [Symbol, String] HTML tag type to use for the root element
67
89
  # @param title_component [Symbol, String] HTML tag type to use for the title element
68
90
  # @param counter [Number, nil] a pre-computed counter for the position of this document in a search result set
69
- # @param document_counter [Number, nil] alternatively, the document's position in a collection and,
70
- # @param counter_offset [Number] with `document_counter`, the offset of the start of that collection counter to the overall result set
91
+ # @param document_counter [Number, nil] provided by ViewComponent collection iteration
92
+ # @param counter_offset [Number] the offset of the start of the collection counter parameter for the component to the overall result set
71
93
  # @param show [Boolean] are we showing only a single document (vs a list of search results); used for backwards-compatibility
72
- def initialize(document: nil, presenter: nil,
94
+ def initialize(document: nil, presenter: nil, partials: nil,
73
95
  id: nil, classes: [], component: :article, title_component: nil,
74
96
  counter: nil, document_counter: nil, counter_offset: 0,
75
- show: false)
76
- if presenter.nil? && document.nil?
77
- raise ArgumentError, 'missing keyword: :document or :presenter'
78
- end
97
+ show: false, **args)
98
+ Blacklight.deprecation.warn('the `presenter` argument to DocumentComponent#initialize is deprecated; pass the `presenter` in as document instead') if presenter
79
99
 
80
- @document = document || presenter&.document
81
- @presenter = presenter
100
+ @presenter = presenter || document || args[self.class.collection_parameter]
101
+ @document = @presenter.document
102
+ @view_partials = partials || []
82
103
 
83
104
  @component = component
84
105
  @title_component = title_component
85
106
  @id = id || ('document' if show)
86
107
  @classes = classes
87
108
 
88
- @document_counter = document_counter
89
109
  @counter = counter
90
- @counter ||= document_counter + 1 + counter_offset if document_counter.present?
110
+ @document_counter = document_counter || args.fetch(self.class.collection_counter_parameter, nil)
111
+ @counter ||= @document_counter + COLLECTION_INDEX_OFFSET + counter_offset if @document_counter.present?
91
112
 
92
113
  @show = show
93
114
  end
@@ -108,13 +129,20 @@ module Blacklight
108
129
  set_slot(:thumbnail, nil) unless thumbnail || show?
109
130
  set_slot(:metadata, nil, fields: presenter.field_presenters) unless metadata
110
131
  set_slot(:embed, nil) unless embed
132
+ if view_partials.present?
133
+ view_partials.each do |view_partial|
134
+ with_partial(view_partial) do
135
+ helpers.render_document_partial @document, view_partial, component: self, document_counter: @counter
136
+ end
137
+ end
138
+ else
139
+ set_slot(:partials, nil)
140
+ end
111
141
  end
112
142
 
113
143
  private
114
144
 
115
- def presenter
116
- @presenter ||= helpers.document_presenter(@document)
117
- end
145
+ attr_reader :document_counter, :presenter, :view_partials
118
146
 
119
147
  def show?
120
148
  @show
@@ -1,5 +1,7 @@
1
- <dl class="document-metadata dl-invert row">
1
+ <% content = capture do %>
2
2
  <% fields.each do |field| -%>
3
3
  <%= field %>
4
4
  <% end -%>
5
- </dl>
5
+ <% end %>
6
+
7
+ <%= @tag.nil? ? content : tag.public_send(@tag, content, class: @classes, **@component_args) %>
@@ -8,17 +8,23 @@ module Blacklight
8
8
  with_collection_parameter :fields
9
9
 
10
10
  # @param fields [Enumerable<Blacklight::FieldPresenter>] Document field presenters
11
- def initialize(fields: [], show: false, view_type: nil)
11
+ # rubocop:disable Metrics/ParameterLists
12
+ def initialize(fields: [], tag: 'dl', classes: %w[document-metadata dl-invert row], show: false, view_type: nil, field_layout: nil, **component_args)
12
13
  @fields = fields
14
+ @tag = tag
15
+ @classes = classes
13
16
  @show = show
14
17
  @view_type = view_type
18
+ @field_layout = field_layout
19
+ @component_args = component_args
15
20
  end
21
+ # rubocop:enable Metrics/ParameterLists
16
22
 
17
23
  def before_render
18
24
  return unless fields
19
25
 
20
26
  @fields.each do |field|
21
- field(component: field.component, field: field, show: @show, view_type: @view_type)
27
+ with_field(component: field.component, field: field, show: @show, view_type: @view_type, layout: @field_layout)
22
28
  end
23
29
  end
24
30
 
@@ -1,9 +1,9 @@
1
1
  <%= render(@layout.new(facet_field: @facet_field)) do |component| %>
2
- <% component.label do %>
2
+ <% component.with_label do %>
3
3
  <%= @facet_field.label %>
4
4
  <% end %>
5
5
 
6
- <% component.body do %>
6
+ <% component.with_body do %>
7
7
  <ul class="facet-values list-unstyled blacklight-facet-checkboxes">
8
8
  <% presenters.each_with_index do |presenter, idx| -%>
9
9
  <li>
@@ -1,8 +1,8 @@
1
1
  <%= render(@layout.new(facet_field: @facet_field)) do |component| %>
2
- <% component.label do %>
2
+ <% component.with_label do %>
3
3
  <%= @facet_field.label %>
4
4
  <% end %>
5
- <% component.body do %>
5
+ <% component.with_body do %>
6
6
  <%= helpers.render(Blacklight::FacetFieldInclusiveConstraintComponent.new(facet_field: @facet_field)) %>
7
7
  <ul class="facet-values list-unstyled">
8
8
  <%= render facet_items %>
@@ -1,8 +1,8 @@
1
1
  <%= render(@layout.new(field: @field)) do |component| %>
2
- <% component.label do %>
2
+ <% component.with_label do %>
3
3
  <%= label %>
4
4
  <% end %>
5
- <% component.value do %>
5
+ <% component.with_value do %>
6
6
  <%= @field.render %>
7
7
  <% end %>
8
8
  <% end %>
@@ -5,18 +5,21 @@ module Blacklight
5
5
  with_collection_parameter :field
6
6
  renders_one :label
7
7
  renders_many :values, (lambda do |value: nil, &block|
8
- if block
9
- content_tag :dd, class: "#{@value_class} blacklight-#{@key}", &block
8
+ if @value_tag.nil?
9
+ block&.call || value
10
+ elsif block
11
+ content_tag @value_tag, class: "#{@value_class} blacklight-#{@key}", &block
10
12
  else
11
- content_tag :dd, value, class: "#{@value_class} blacklight-#{@key}"
13
+ content_tag @value_tag, value, class: "#{@value_class} blacklight-#{@key}"
12
14
  end
13
15
  end)
14
16
 
15
17
  # @param field [Blacklight::FieldPresenter]
16
- def initialize(field:, label_class: 'col-md-3', value_class: 'col-md-9')
18
+ def initialize(field:, value_tag: 'dd', label_class: 'col-md-3', value_class: 'col-md-9')
17
19
  @field = field
18
20
  @key = @field.key.parameterize
19
21
  @label_class = label_class
22
+ @value_tag = value_tag
20
23
  @value_class = value_class
21
24
  end
22
25
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blacklight
4
+ class MetadataFieldPlainTextLayoutComponent < Blacklight::MetadataFieldLayoutComponent
5
+ with_collection_parameter :field
6
+
7
+ def initialize(field:, **kwargs)
8
+ super(field: field, **kwargs, value_tag: nil)
9
+ end
10
+
11
+ # rubocop:disable Rails/OutputSafety
12
+ def call
13
+ [label.to_s.strip, helpers.strip_tags(CGI.unescape_html(safe_join(values, "\n")).strip)].compact.join(' ').html_safe
14
+ end
15
+ # rubocop:enable Rails/OutputSafety
16
+ end
17
+ end
@@ -17,7 +17,8 @@
17
17
  expanded: 'false',
18
18
  label: t('blacklight.search.facets.group.toggle'),
19
19
  } do %>
20
- <span class="navbar-toggler-icon"></span>
20
+ <span data-show-label><%= t('blacklight.search.facets.group.open') %></span>
21
+ <span data-hide-label><%= t('blacklight.search.facets.group.close') %></span>
21
22
  <% end %>
22
23
  </div>
23
24
 
@@ -23,7 +23,7 @@ module Blacklight
23
23
 
24
24
  # Provide fallback behavior for rendering this object without a body slot
25
25
  def before_render
26
- set_slot(:body, nil) { default_body } unless body
26
+ set_slot(:body, nil) { default_body } unless body?
27
27
  end
28
28
 
29
29
  def render?
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Blacklight
4
4
  module Response
5
- class SortComponent < ViewComponent::Base
5
+ class SortComponent < Blacklight::Component
6
6
  def initialize(search_state:, param: 'sort', choices: {}, id: 'sort-dropdown', classes: [], selected: nil)
7
7
  @param = param
8
8
  @choices = choices
@@ -3,7 +3,7 @@
3
3
  module Blacklight
4
4
  module Response
5
5
  # Render spellcheck results for a search query
6
- class SpellcheckComponent < ViewComponent::Base
6
+ class SpellcheckComponent < Blacklight::Component
7
7
  # @param [Blacklight::Response] response
8
8
  # @param [Array<String>] options explicit spellcheck options to render
9
9
  def initialize(response:, options: nil)
@@ -3,7 +3,7 @@
3
3
  module Blacklight
4
4
  module Response
5
5
  # Render spellcheck results for a search query
6
- class ViewTypeButtonComponent < ViewComponent::Base
6
+ class ViewTypeButtonComponent < Blacklight::Component
7
7
  with_collection_parameter :view
8
8
  # @param [Blacklight::Configuration::View] view
9
9
  def initialize(view:, key: nil, selected: false, search_state: nil, classes: 'btn btn-outline-secondary btn-icon')
@@ -3,7 +3,7 @@
3
3
  module Blacklight
4
4
  module Response
5
5
  # Render spellcheck results for a search query
6
- class ViewTypeComponent < ViewComponent::Base
6
+ class ViewTypeComponent < Blacklight::Component
7
7
  renders_many :views, 'Blacklight::Response::ViewTypeButtonComponent'
8
8
 
9
9
  # @param [Blacklight::Response] response
@@ -18,7 +18,7 @@ module Blacklight
18
18
  return if views.any?
19
19
 
20
20
  @views.each do |key, config|
21
- view(key: key, view: config, selected: @selected == key, search_state: @search_state)
21
+ with_view(key: key, view: config, selected: @selected == key, search_state: @search_state)
22
22
  end
23
23
  end
24
24
 
@@ -1,7 +1,7 @@
1
1
  <% facet_group_names.each do |groupname| %>
2
2
  <% fields = facet_fields_in_group(groupname) %>
3
3
  <%= render group_component_class.new(id: groupname, fields: fields, response: response) do |component| %>
4
- <% component.body do %>
4
+ <% component.with_body do %>
5
5
  <%= render Blacklight::FacetComponent.with_collection(fields, response: response) %>
6
6
  <% end %>
7
7
  <% end %>
@@ -0,0 +1,4 @@
1
+ <div id="appliedParams" class="clearfix constraints-container">
2
+ <%= render 'start_over' %>
3
+ <%= link_back_to_catalog class: 'btn btn-outline-secondary' %>
4
+ </div>
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blacklight
4
+ module SearchContext
5
+ class ServerAppliedParamsComponent < Blacklight::Component
6
+ delegate :current_search_session, :link_back_to_catalog, to: :helpers
7
+
8
+ def render?
9
+ current_search_session
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blacklight
4
+ module SearchContext
5
+ class ServerItemPaginationComponent < Blacklight::Component
6
+ with_collection_parameter :search_context
7
+
8
+ def initialize(search_context:, search_session:, current_document:)
9
+ @search_context = search_context
10
+ @search_session = search_session
11
+ @current_document_id = current_document.id
12
+ end
13
+
14
+ def render?
15
+ @search_context.present? && (@search_context[:prev] || @search_context[:next]) && (@search_session['document_id'] == @current_document_id)
16
+ end
17
+
18
+ ##
19
+ # Displays "showing X of Y items" message.
20
+ #
21
+ # @return [String]
22
+ def item_page_entry_info
23
+ t('blacklight.search.entry_pagination_info.other', current: number_with_delimiter(count),
24
+ total: number_with_delimiter(total),
25
+ count: total).html_safe
26
+ end
27
+
28
+ def link_to_previous_document(previous_document = nil, classes: 'previous', **link_opts)
29
+ previous_document ||= @search_context[:prev]
30
+ link_opts = session_tracking_params(previous_document, count - 1, per_page: per_page, search_id: search_id).merge(class: classes, rel: 'prev').merge(link_opts)
31
+ link_to_unless previous_document.nil?, raw(t('views.pagination.previous')), url_for_document(previous_document), link_opts do
32
+ tag.span raw(t('views.pagination.previous')), class: 'previous'
33
+ end
34
+ end
35
+
36
+ def link_to_next_document(next_document = nil, classes: 'next', **link_opts)
37
+ next_document ||= @search_context[:next]
38
+ link_opts = session_tracking_params(next_document, count + 1, per_page: per_page, search_id: search_id).merge(class: classes, rel: 'next').merge(link_opts)
39
+ link_to_unless next_document.nil?, raw(t('views.pagination.next')), url_for_document(next_document), link_opts do
40
+ tag.span raw(t('views.pagination.next')), class: 'next'
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def count
47
+ @search_session['counter'].to_i
48
+ end
49
+
50
+ def per_page
51
+ @search_session['per_page']
52
+ end
53
+
54
+ def total
55
+ @search_session['total'].to_i
56
+ end
57
+
58
+ def search_id
59
+ @search_session['id'] || helpers.current_search_session&.id
60
+ end
61
+
62
+ def session_tracking_params(...)
63
+ helpers.session_tracking_params(...)
64
+ end
65
+
66
+ def url_for_document(...)
67
+ helpers.search_state.url_for_document(...)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Blacklight
4
4
  module System
5
- class DropdownComponent < ViewComponent::Base
5
+ class DropdownComponent < Blacklight::Component
6
6
  renders_one :button, (lambda do |classes:, label:|
7
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')])
@@ -29,11 +29,11 @@ module Blacklight
29
29
  end
30
30
 
31
31
  def before_render
32
- button(classes: 'btn btn-outline-secondary dropdown-toggle', label: button_label) unless button
32
+ with_button(classes: 'btn btn-outline-secondary dropdown-toggle', label: button_label) unless button
33
33
 
34
34
  return if options.any?
35
35
 
36
- options(@choices.map do |option|
36
+ with_options(@choices.map do |option|
37
37
  text, value = option_text_and_value(option)
38
38
  { text: text, url: helpers.url_for(@search_state.params_for_search(@param => value)), selected: @selected == value }
39
39
  end)
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Blacklight
4
4
  module System
5
- class FlashMessageComponent < ViewComponent::Base
5
+ class FlashMessageComponent < Blacklight::Component
6
6
  renders_one :message
7
7
 
8
8
  with_collection_parameter :message
@@ -13,7 +13,7 @@ module Blacklight
13
13
  end
14
14
 
15
15
  def before_render
16
- message { @message } if @message
16
+ with_message { @message } if @message
17
17
  end
18
18
 
19
19
  # Bootstrap 4 requires the span, but Bootstrap 5 should not have it.
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Blacklight
4
4
  module System
5
- class ModalComponent < ViewComponent::Base
5
+ class ModalComponent < Blacklight::Component
6
6
  renders_one :prefix
7
7
  renders_one :header
8
8
  renders_one :title
@@ -1,6 +1,6 @@
1
1
  <nav class="navbar navbar-expand-md navbar-dark bg-dark topbar" role="navigation">
2
2
  <div class="<%= container_classes %>">
3
- <%= link_to application_name, blacklight_config.logo_link, class: 'mb-0 navbar-brand navbar-logo' %>
3
+ <%= logo_link %>
4
4
  <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-bs-toggle="collapse" data-target="#user-util-collapse" data-bs-target="#user-util-collapse" aria-controls="user-util-collapse" aria-expanded="false" aria-label="Toggle navigation">
5
5
  <span class="navbar-toggler-icon"></span>
6
6
  </button>
@@ -9,5 +9,9 @@ module Blacklight
9
9
  attr_reader :blacklight_config
10
10
 
11
11
  delegate :application_name, :container_classes, to: :helpers
12
+
13
+ def logo_link(title: application_name)
14
+ link_to title, blacklight_config.logo_link, class: 'mb-0 navbar-brand navbar-logo'
15
+ end
12
16
  end
13
17
  end
@@ -15,7 +15,7 @@ module Blacklight::Bookmarks
15
15
 
16
16
  before_action :verify_user
17
17
 
18
- blacklight_config.track_search_session = false
18
+ blacklight_config.track_search_session.storage = false
19
19
  blacklight_config.http_method = Blacklight::Engine.config.blacklight.bookmarks_http_method
20
20
  blacklight_config.add_results_collection_tool(:clear_bookmarks_widget)
21
21
 
@@ -28,6 +28,28 @@ module Blacklight::SearchContext
28
28
  session[:history].blank? ? ::Search.none : ::Search.where(id: session[:history]).order("updated_at desc")
29
29
  end
30
30
 
31
+ # GET previous and next document json for the document specified by
32
+ # the counter param in current search
33
+ def page_links
34
+ counter_param = params.delete(:counter)
35
+ @page_link_data = {}
36
+ if counter_param
37
+ index = counter_param.to_i - 1
38
+ response, documents = search_service.previous_and_next_documents_for_search index, search_state.reset_search
39
+ if documents.detect(&:present?)
40
+ @page_link_data[:prev] = page_links_document_path(documents.first, index)
41
+ @page_link_data[:next] = page_links_document_path(documents.last, index + 2)
42
+ end
43
+ if response&.total && response.total.positive?
44
+ @page_link_data[:counterRaw] = counter_param
45
+ @page_link_data[:counterDelimited] = helpers.number_with_delimiter(counter_param.to_i)
46
+ @page_link_data[:totalRaw] = response.total
47
+ @page_link_data[:totalDelimited] = helpers.number_with_delimiter(response.total)
48
+ end
49
+ end
50
+ render json: @page_link_data
51
+ end
52
+
31
53
  private
32
54
 
33
55
  # sets up the session[:search] hash if it doesn't already exist
@@ -85,6 +107,8 @@ module Blacklight::SearchContext
85
107
  end
86
108
 
87
109
  def find_or_initialize_search_session_from_params params
110
+ return unless blacklight_config.track_search_session.storage == 'server'
111
+
88
112
  params_copy = params.reject { |k, v| nonpersisted_search_session_params.include?(k.to_sym) || v.blank? }
89
113
 
90
114
  return if params_copy.reject { |k, _v| [:action, :controller].include? k.to_sym }.blank?
@@ -127,15 +151,31 @@ module Blacklight::SearchContext
127
151
  # calls setup_previous_document then setup_next_document.
128
152
  # used in the show action for single view pagination.
129
153
  def setup_next_and_previous_documents
130
- if search_session['counter'] && current_search_session
131
- index = search_session['counter'].to_i - 1
132
- response, documents = search_service.previous_and_next_documents_for_search index, search_state.reset(current_search_session.query_params)
154
+ return { counter: params[:counter] } if setup_next_and_previous_on_client?
155
+ return nil unless setup_next_and_previous_on_server?
133
156
 
134
- search_session['total'] = response.total
135
- { prev: documents.first, next: documents.last }
136
- end
157
+ index = search_session['counter'].to_i - 1
158
+ response, documents = search_service.previous_and_next_documents_for_search index, search_state.reset(current_search_session.query_params)
159
+
160
+ search_session['total'] = response.total
161
+ { prev: documents.first, next: documents.last }
137
162
  rescue Blacklight::Exceptions::InvalidRequest => e
138
163
  logger&.warn "Unable to setup next and previous documents: #{e}"
139
164
  nil
140
165
  end
166
+
167
+ def setup_next_and_previous_on_server?
168
+ search_session['counter'] && current_search_session && blacklight_config.track_search_session.storage == 'server'
169
+ end
170
+
171
+ def setup_next_and_previous_on_client?
172
+ params[:counter] && blacklight_config.track_search_session.storage == 'client'
173
+ end
174
+
175
+ def page_links_document_path(document, counter)
176
+ return nil unless document
177
+ return search_state.url_for_document(document, counter: counter) if blacklight_config.view_config(:show).route
178
+
179
+ solr_document_path(document, counter: counter)
180
+ end
141
181
  end