blacklight 7.16.0 → 7.17.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/VERSION +1 -1
  4. data/app/components/blacklight/advanced_search_form_component.html.erb +9 -3
  5. data/app/components/blacklight/advanced_search_form_component.rb +48 -35
  6. data/app/components/blacklight/constraints_component.html.erb +19 -3
  7. data/app/components/blacklight/constraints_component.rb +5 -1
  8. data/app/components/blacklight/content_areas_shim.rb +12 -0
  9. data/app/components/blacklight/document/action_component.rb +4 -0
  10. data/app/components/blacklight/document/actions_component.html.erb +3 -5
  11. data/app/components/blacklight/document/actions_component.rb +14 -1
  12. data/app/components/blacklight/document_component.html.erb +4 -7
  13. data/app/components/blacklight/document_component.rb +73 -73
  14. data/app/components/blacklight/document_metadata_component.html.erb +2 -2
  15. data/app/components/blacklight/document_metadata_component.rb +13 -2
  16. data/app/components/blacklight/document_title_component.html.erb +17 -0
  17. data/app/components/blacklight/document_title_component.rb +59 -0
  18. data/app/components/blacklight/facet_field_checkboxes_component.html.erb +2 -2
  19. data/app/components/blacklight/facet_field_component.rb +4 -1
  20. data/app/components/blacklight/facet_field_list_component.html.erb +2 -2
  21. data/app/components/blacklight/facet_field_no_layout_component.rb +4 -1
  22. data/app/components/blacklight/metadata_field_component.html.erb +2 -2
  23. data/app/components/blacklight/metadata_field_layout_component.html.erb +3 -1
  24. data/app/components/blacklight/metadata_field_layout_component.rb +26 -1
  25. data/app/components/blacklight/response/view_type_button_component.html.erb +4 -0
  26. data/app/components/blacklight/response/view_type_button_component.rb +36 -0
  27. data/app/components/blacklight/response/view_type_component.html.erb +2 -5
  28. data/app/components/blacklight/response/view_type_component.rb +9 -13
  29. data/app/components/blacklight/search_bar_component.rb +4 -1
  30. data/app/components/blacklight/system/dropdown_component.html.erb +4 -7
  31. data/app/components/blacklight/system/dropdown_component.rb +24 -0
  32. data/app/components/blacklight/system/flash_message_component.html.erb +1 -1
  33. data/app/components/blacklight/system/flash_message_component.rb +7 -1
  34. data/app/components/blacklight/system/modal_component.rb +7 -1
  35. data/app/views/catalog/_citation.html.erb +1 -1
  36. data/app/views/catalog/_document.html.erb +2 -2
  37. data/app/views/catalog/_facet_layout.html.erb +2 -2
  38. data/app/views/catalog/_show_main_content.html.erb +3 -3
  39. data/app/views/catalog/email.html.erb +2 -2
  40. data/app/views/catalog/email_success.html.erb +1 -1
  41. data/app/views/catalog/facet.html.erb +3 -3
  42. data/app/views/catalog/sms.html.erb +2 -2
  43. data/app/views/catalog/sms_success.html.erb +1 -1
  44. data/blacklight.gemspec +1 -1
  45. data/config/locales/blacklight.de.yml +2 -2
  46. data/lib/blacklight/engine.rb +3 -1
  47. data/lib/blacklight/solr/facet_paginator.rb +2 -0
  48. data/lib/blacklight/solr/request.rb +31 -0
  49. data/lib/blacklight/solr/response.rb +2 -16
  50. data/lib/blacklight/solr/response/facets.rb +76 -22
  51. data/lib/blacklight/solr/response/params.rb +104 -0
  52. data/lib/blacklight/solr/search_builder_behavior.rb +49 -29
  53. data/lib/generators/blacklight/assets_generator.rb +6 -2
  54. data/lib/generators/blacklight/user_generator.rb +1 -1
  55. data/spec/components/blacklight/document_component_spec.rb +3 -3
  56. data/spec/models/blacklight/solr/facet_paginator_spec.rb +4 -0
  57. data/spec/models/blacklight/solr/request_spec.rb +62 -29
  58. data/spec/models/blacklight/solr/response/facets_spec.rb +109 -0
  59. data/spec/models/blacklight/solr/response_spec.rb +10 -0
  60. data/spec/models/blacklight/solr/search_builder_spec.rb +17 -0
  61. metadata +14 -8
@@ -1,5 +1,5 @@
1
1
  <dl class="document-metadata dl-invert row">
2
- <% @fields.each do |field| -%>
3
- <%= @view_context.render(field_component(field).new(field: field, show: @show)) %>
2
+ <% fields.each do |field| -%>
3
+ <%= field %>
4
4
  <% end -%>
5
5
  </dl>
@@ -2,16 +2,27 @@
2
2
 
3
3
  module Blacklight
4
4
  class DocumentMetadataComponent < ::ViewComponent::Base
5
+ renders_many :fields, (lambda do |component: nil, **kwargs|
6
+ (component || Blacklight::MetadataFieldComponent).new(**kwargs)
7
+ end)
5
8
  with_collection_parameter :fields
6
9
 
7
10
  # @param fields [Enumerable<Blacklight::FieldPresenter>] Document field presenters
8
- def initialize(fields:, show: false)
11
+ def initialize(fields: [], show: false)
9
12
  @fields = fields
10
13
  @show = show
11
14
  end
12
15
 
16
+ def before_render
17
+ return unless fields
18
+
19
+ @fields.each do |field|
20
+ field(component: field_component(field), field: field, show: @show)
21
+ end
22
+ end
23
+
13
24
  def render?
14
- @fields.any?
25
+ fields.present?
15
26
  end
16
27
 
17
28
  def field_component(field)
@@ -0,0 +1,17 @@
1
+ <header class="documentHeader row">
2
+ <%= content_tag @as, class: @classes do %>
3
+ <% before_title.each do |t| %>
4
+ <%= t %>
5
+ <% end %>
6
+
7
+ <%= counter -%><%= title -%>
8
+
9
+ <% after_title.each do |t| %>
10
+ <%= t %>
11
+ <% end %>
12
+ <% end %>
13
+
14
+ <% actions.each do |action| %>
15
+ <%= action %>
16
+ <% end %>
17
+ </header>
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blacklight
4
+ class DocumentTitleComponent < ::ViewComponent::Base
5
+ renders_many :before_title
6
+ renders_many :after_title
7
+ renders_many :actions
8
+
9
+ # rubocop:disable Metrics/ParameterLists
10
+ def initialize(title = nil, document: nil, presenter: nil, as: :h3, counter: nil, classes: 'index_title document-title-heading col', link_to_document: true, document_component: nil)
11
+ raise ArgumentError, 'missing keyword: :document or :presenter' if presenter.nil? && document.nil?
12
+
13
+ @title = title
14
+ @document = document
15
+ @presenter = presenter
16
+ @as = as || :h3
17
+ @counter = counter
18
+ @classes = classes
19
+ @link_to_document = link_to_document
20
+ @document_component = document_component
21
+ end
22
+ # rubocop:enable Metrics/ParameterLists
23
+
24
+ # Content for the document title area; should be an inline element
25
+ def title
26
+ if @link_to_document
27
+ @view_context.link_to_document presenter.document, @title.presence || content.presence, counter: @counter, itemprop: 'name'
28
+ else
29
+ content_tag('span', @title.presence || content.presence || presenter.heading, itemprop: 'name')
30
+ end
31
+ end
32
+
33
+ # Content for the document actions area
34
+ def actions
35
+ if block_given?
36
+ @has_actions_slot = true
37
+ return super
38
+ end
39
+
40
+ (@has_actions_slot && get_slot(:actions)) ||
41
+ ([@document_component&.actions] if @document_component&.actions.present?) ||
42
+ [@view_context.render_index_doc_actions(presenter.document, wrapping_class: 'index-document-functions col-sm-3 col-lg-2')]
43
+ end
44
+
45
+ def counter
46
+ return unless @counter
47
+
48
+ content_tag :span, class: 'document-counter' do
49
+ t('blacklight.search.documents.counter', counter: @counter)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def presenter
56
+ @presenter ||= @view_context.document_presenter(@document)
57
+ end
58
+ end
59
+ end
@@ -1,9 +1,9 @@
1
1
  <%= render(@layout.new(facet_field: @facet_field)) do |component| %>
2
- <% component.with(:label) do %>
2
+ <% component.label do %>
3
3
  <%= @facet_field.label %>
4
4
  <% end %>
5
5
 
6
- <% component.with(:body) do %>
6
+ <% component.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>
@@ -2,7 +2,10 @@
2
2
 
3
3
  module Blacklight
4
4
  class FacetFieldComponent < ::ViewComponent::Base
5
- with_content_areas :label, :body
5
+ include Blacklight::ContentAreasShim
6
+
7
+ renders_one :label
8
+ renders_one :body
6
9
 
7
10
  def initialize(facet_field:)
8
11
  @facet_field = facet_field
@@ -1,8 +1,8 @@
1
1
  <%= render(@layout.new(facet_field: @facet_field)) do |component| %>
2
- <% component.with(:label) do %>
2
+ <% component.label do %>
3
3
  <%= @facet_field.label %>
4
4
  <% end %>
5
- <% component.with(:body) do %>
5
+ <% component.body do %>
6
6
  <%= @view_context.render(Blacklight::FacetFieldInclusiveConstraintComponent.new(facet_field: @facet_field)) %>
7
7
  <ul class="facet-values list-unstyled">
8
8
  <%= render_facet_limit_list @facet_field.paginator, @facet_field.key %>
@@ -2,7 +2,10 @@
2
2
 
3
3
  module Blacklight
4
4
  class FacetFieldNoLayoutComponent < ::ViewComponent::Base
5
- with_content_areas :label, :body
5
+ include Blacklight::ContentAreasShim
6
+
7
+ renders_one :label
8
+ renders_one :body
6
9
 
7
10
  def initialize(**); end
8
11
 
@@ -1,8 +1,8 @@
1
1
  <%= render(@layout.new(field: @field)) do |component| %>
2
- <% component.with(:label) do %>
2
+ <% component.label do %>
3
3
  <%= label %>
4
4
  <% end %>
5
- <% component.with(:value) do %>
5
+ <% component.value do %>
6
6
  <%= @field.render %>
7
7
  <% end %>
8
8
  <% end %>
@@ -1,2 +1,4 @@
1
1
  <dt class="blacklight-<%= @key %> <%= @label_class %>"><%= label %></dt>
2
- <dd class="blacklight-<%= @key %> <%= @value_class %>"><%= value %></dd>
2
+ <% values.each do |v| %>
3
+ <%= v %>
4
+ <% end %>
@@ -2,8 +2,17 @@
2
2
 
3
3
  module Blacklight
4
4
  class MetadataFieldLayoutComponent < ::ViewComponent::Base
5
+ include Blacklight::ContentAreasShim
6
+
5
7
  with_collection_parameter :field
6
- with_content_areas :label, :value
8
+ renders_one :label
9
+ renders_many :values, (lambda do |value: nil, &block|
10
+ if block
11
+ content_tag :dd, class: "#{@value_class} blacklight-#{@key}", &block
12
+ else
13
+ content_tag :dd, value, class: "#{@value_class} blacklight-#{@key}"
14
+ end
15
+ end)
7
16
 
8
17
  # @param field [Blacklight::FieldPresenter]
9
18
  def initialize(field:, label_class: 'col-md-3', value_class: 'col-md-9')
@@ -12,5 +21,21 @@ module Blacklight
12
21
  @label_class = label_class
13
22
  @value_class = value_class
14
23
  end
24
+
25
+ def value(*args, **kwargs, &block)
26
+ return set_slot(:values, *args, **kwargs, &block) if block_given?
27
+
28
+ Deprecation.warn('The `value` content area is deprecated; render from the values slot instead')
29
+
30
+ values.first
31
+ end
32
+
33
+ def with(slot_name, *args, **kwargs, &block)
34
+ if slot_name == :value
35
+ super(:values, *args, **kwargs, &block)
36
+ else
37
+ super
38
+ end
39
+ end
15
40
  end
16
41
  end
@@ -0,0 +1,4 @@
1
+ <%= link_to url, title: label, class: "#{Array(@classes).join(' ')} view-type-#{ @key.to_s.parameterize } #{"active" if selected?}" do %>
2
+ <%= icon %>
3
+ <span class="caption"><%= label %></span>
4
+ <% end %>
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blacklight
4
+ module Response
5
+ # Render spellcheck results for a search query
6
+ class ViewTypeButtonComponent < ViewComponent::Base
7
+ with_collection_parameter :view
8
+ # @param [Blacklight::Configuration::View] view
9
+ def initialize(view:, key: nil, selected: false, search_state: nil, classes: 'btn btn-outline-secondary btn-icon')
10
+ @view = view
11
+ @key = key || view.key
12
+ @selected = selected
13
+ @classes = classes
14
+ @search_state = search_state
15
+ end
16
+
17
+ def icon
18
+ @view_context.render_view_type_group_icon(@view)
19
+ end
20
+
21
+ def label
22
+ Deprecation.silence(Blacklight::ConfigurationHelperBehavior) do
23
+ @view_context.view_label(@view)
24
+ end
25
+ end
26
+
27
+ def url
28
+ @view_context.url_for(@search_state.to_h.merge(view: @key))
29
+ end
30
+
31
+ def selected?
32
+ @selected
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,11 +1,8 @@
1
1
  <div class="view-type">
2
2
  <span class="sr-only"><%= t('blacklight.search.view_title') %></span>
3
3
  <div class="view-type-group btn-group">
4
- <% @views.each do |view, config| %>
5
- <%= link_to url(view), title: label(view), class: "btn btn-outline-secondary btn-icon view-type-#{ view.to_s.parameterize } #{"active" if @selected == view}" do %>
6
- <%= icon(view) %>
7
- <span class="caption"><%= label(view) %></span>
8
- <% end %>
4
+ <% views.each do |view| %>
5
+ <%= view %>
9
6
  <% end %>
10
7
  </div>
11
8
  </div>
@@ -4,6 +4,8 @@ module Blacklight
4
4
  module Response
5
5
  # Render spellcheck results for a search query
6
6
  class ViewTypeComponent < ViewComponent::Base
7
+ renders_many :views, 'Blacklight::Response::ViewTypeButtonComponent'
8
+
7
9
  # @param [Blacklight::Response] response
8
10
  def initialize(response:, views: {}, search_state:, selected: nil)
9
11
  @response = response
@@ -12,25 +14,19 @@ module Blacklight
12
14
  @selected = selected
13
15
  end
14
16
 
15
- def render?
16
- Deprecation.silence(Blacklight::ConfigurationHelperBehavior) do
17
- @view_context.has_alternative_views?
18
- end
19
- end
17
+ def before_render
18
+ return if views.any?
20
19
 
21
- def icon(view)
22
- @view_context.render_view_type_group_icon(view)
20
+ @views.each do |key, config|
21
+ view(key: key, view: config, selected: @selected == key, search_state: @search_state)
22
+ end
23
23
  end
24
24
 
25
- def label(view)
25
+ def render?
26
26
  Deprecation.silence(Blacklight::ConfigurationHelperBehavior) do
27
- @view_context.view_label(view)
27
+ @view_context.has_alternative_views?
28
28
  end
29
29
  end
30
-
31
- def url(view)
32
- @view_context.url_for(@search_state.to_h.merge(view: view))
33
- end
34
30
  end
35
31
  end
36
32
  end
@@ -2,7 +2,10 @@
2
2
 
3
3
  module Blacklight
4
4
  class SearchBarComponent < ::ViewComponent::Base
5
- with_content_areas :append, :prepend
5
+ include Blacklight::ContentAreasShim
6
+
7
+ renders_one :append
8
+ renders_one :prepend
6
9
 
7
10
  # rubocop:disable Metrics/ParameterLists
8
11
  def initialize(
@@ -1,12 +1,9 @@
1
1
  <%= content_tag :div, id: @id, class: @classes.join(' ') do %>
2
- <button type="button" class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
3
- <%= t(:button_label_html, default: :label_html, scope: "blacklight.search.#{@param}", @interpolation => label_for_value(@selected)) %> <span class="caret"></span>
4
- </button>
2
+ <%= button %>
5
3
 
6
4
  <div class="dropdown-menu" role="menu">
7
- <%- @choices.each do |option| %>
8
- <% text, value = option_text_and_value(option) %>
9
- <%= link_to(text, @view_context.url_for(@search_state.params_for_search(@param => value)), class: 'dropdown-item', role: 'menuitem') %>
10
- <%- end -%>
5
+ <% options.each do |option| %>
6
+ <%= option %>
7
+ <% end %>
11
8
  </div>
12
9
  <% end %>
@@ -3,6 +3,15 @@
3
3
  module Blacklight
4
4
  module System
5
5
  class DropdownComponent < ViewComponent::Base
6
+ renders_one :button, (lambda do |classes:, label:|
7
+ button_tag class: classes, aria: { expanded: false }, data: { toggle: 'dropdown' } do
8
+ safe_join([label, content_tag(:span, '', class: 'caret')])
9
+ end
10
+ end)
11
+ renders_many :options, (lambda do |text:, url:, selected: false|
12
+ link_to(text, url, class: "dropdown-item #{'active' if selected}", role: 'menuitem', aria: { current: ('page' if selected) })
13
+ end)
14
+
6
15
  # rubocop:disable Metrics/ParameterLists
7
16
  def initialize(param:, choices:, search_state:, id: nil, classes: [], default: nil, selected: nil, interpolation: :field)
8
17
  @param = param
@@ -15,6 +24,21 @@ module Blacklight
15
24
  end
16
25
  # rubocop:enable Metrics/ParameterLists
17
26
 
27
+ def button_label
28
+ t(:button_label_html, default: :label_html, scope: "blacklight.search.#{@param}", @interpolation => label_for_value(@selected))
29
+ end
30
+
31
+ def before_render
32
+ button(classes: 'btn btn-outline-secondary dropdown-toggle', label: button_label) unless button
33
+
34
+ return if options.any?
35
+
36
+ options(@choices.map do |option|
37
+ text, value = option_text_and_value(option)
38
+ { text: text, url: @view_context.url_for(@search_state.params_for_search(@param => value)), selected: @selected == value }
39
+ end)
40
+ end
41
+
18
42
  def render?
19
43
  @choices.many?
20
44
  end
@@ -1,4 +1,4 @@
1
1
  <div class="alert <%= @classes %>">
2
- <%= @message %>
2
+ <%= message %>
3
3
  <a class="close" data-dismiss="alert" href="#">&times;</a>
4
4
  </div>
@@ -3,13 +3,19 @@
3
3
  module Blacklight
4
4
  module System
5
5
  class FlashMessageComponent < ViewComponent::Base
6
+ renders_one :message
7
+
6
8
  with_collection_parameter :message
7
9
 
8
- def initialize(message:, type:)
10
+ def initialize(message: nil, type:)
9
11
  @message = message
10
12
  @classes = alert_class(type)
11
13
  end
12
14
 
15
+ def before_render
16
+ message { @message } if @message
17
+ end
18
+
13
19
  def alert_class(type)
14
20
  case type.to_s
15
21
  when 'success' then "alert-success"
@@ -3,7 +3,13 @@
3
3
  module Blacklight
4
4
  module System
5
5
  class ModalComponent < ViewComponent::Base
6
- with_content_areas :prefix, :header, :title, :body, :footer
6
+ include Blacklight::ContentAreasShim
7
+
8
+ renders_one :prefix
9
+ renders_one :header
10
+ renders_one :title
11
+ renders_one :body
12
+ renders_one :footer
7
13
  end
8
14
  end
9
15
  end