blacklight 7.24.0 → 7.25.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +4 -4
  3. data/VERSION +1 -1
  4. data/app/assets/javascripts/blacklight/blacklight.js +2 -2
  5. data/app/components/blacklight/constraint_component.rb +1 -1
  6. data/app/components/blacklight/constraint_layout_component.rb +1 -1
  7. data/app/components/blacklight/constraints_component.rb +1 -1
  8. data/app/components/blacklight/document/action_component.rb +1 -1
  9. data/app/components/blacklight/document/actions_component.rb +1 -1
  10. data/app/components/blacklight/document/bookmark_component.rb +1 -1
  11. data/app/components/blacklight/document/citation_component.rb +2 -2
  12. data/app/components/blacklight/document/group_component.rb +3 -2
  13. data/app/components/blacklight/document/more_like_this_component.rb +1 -1
  14. data/app/components/blacklight/document/thumbnail_component.rb +1 -1
  15. data/app/components/blacklight/document_component.rb +1 -2
  16. data/app/components/blacklight/document_metadata_component.rb +1 -1
  17. data/app/components/blacklight/document_title_component.rb +1 -1
  18. data/app/components/blacklight/facet_field_checkboxes_component.rb +1 -1
  19. data/app/components/blacklight/facet_field_component.rb +1 -1
  20. data/app/components/blacklight/facet_field_filter_component.rb +1 -1
  21. data/app/components/blacklight/facet_field_inclusive_constraint_component.rb +1 -1
  22. data/app/components/blacklight/facet_field_list_component.rb +1 -1
  23. data/app/components/blacklight/facet_field_no_layout_component.rb +1 -1
  24. data/app/components/blacklight/facet_field_pagination_component.rb +1 -1
  25. data/app/components/blacklight/facet_item_component.rb +1 -1
  26. data/app/components/blacklight/facet_item_pivot_component.rb +1 -1
  27. data/app/components/blacklight/hidden_search_state_component.rb +2 -2
  28. data/app/components/blacklight/metadata_field_component.rb +1 -1
  29. data/app/components/blacklight/metadata_field_layout_component.rb +1 -1
  30. data/app/components/blacklight/response/facet_group_component.rb +1 -1
  31. data/app/components/blacklight/response/pagination_component.rb +1 -1
  32. data/app/components/blacklight/search_bar_component.html.erb +7 -7
  33. data/app/components/blacklight/search_bar_component.rb +18 -13
  34. data/app/components/blacklight/search_context_component.rb +1 -1
  35. data/app/components/blacklight/start_over_button_component.rb +1 -1
  36. data/app/helpers/blacklight/blacklight_helper_behavior.rb +3 -1
  37. data/app/helpers/blacklight/catalog_helper_behavior.rb +1 -1
  38. data/app/helpers/blacklight/render_constraints_helper_behavior.rb +5 -7
  39. data/app/javascript/blacklight/modal.js +2 -2
  40. data/app/presenters/blacklight/search_bar_presenter.rb +2 -0
  41. data/app/services/blacklight/field_retriever.rb +1 -1
  42. data/app/views/catalog/_constraints.html.erb +2 -2
  43. data/app/views/catalog/_facet_group.html.erb +1 -1
  44. data/app/views/catalog/_search_form.html.erb +2 -2
  45. data/app/views/shared/_header_navbar.html.erb +5 -1
  46. data/lib/blacklight/component.rb +40 -0
  47. data/lib/blacklight/configuration/facet_field.rb +1 -1
  48. data/lib/blacklight/configuration/fields.rb +10 -11
  49. data/lib/blacklight/configuration/view_config.rb +6 -0
  50. data/lib/blacklight/configuration.rb +320 -179
  51. data/lib/blacklight/deprecations/search_state_normalization.rb +52 -0
  52. data/lib/blacklight/search_state/filter_field.rb +44 -16
  53. data/lib/blacklight/search_state.rb +55 -31
  54. data/lib/blacklight/solr/search_builder_behavior.rb +3 -1
  55. data/lib/blacklight.rb +1 -0
  56. data/package.json +1 -1
  57. data/spec/components/blacklight/constraints_component_spec.rb +14 -1
  58. data/spec/components/blacklight/facet_field_list_component_spec.rb +6 -1
  59. data/spec/components/blacklight/facet_item_pivot_component_spec.rb +7 -1
  60. data/spec/controllers/blacklight/base_spec.rb +4 -1
  61. data/spec/helpers/blacklight/facets_helper_behavior_spec.rb +23 -6
  62. data/spec/helpers/blacklight/url_helper_behavior_spec.rb +7 -0
  63. data/spec/lib/blacklight/component_spec.rb +43 -0
  64. data/spec/lib/blacklight/search_state/filter_field_spec.rb +12 -1
  65. data/spec/lib/blacklight/search_state_spec.rb +17 -3
  66. data/spec/models/blacklight/configuration_spec.rb +2 -2
  67. data/spec/models/blacklight/solr/search_builder_spec.rb +15 -0
  68. data/spec/presenters/blacklight/facet_field_presenter_spec.rb +10 -4
  69. data/spec/presenters/blacklight/facet_grouped_item_presenter_spec.rb +6 -1
  70. data/spec/presenters/blacklight/field_presenter_spec.rb +2 -0
  71. data/spec/views/catalog/index.json.jbuilder_spec.rb +1 -0
  72. metadata +7 -3
@@ -6,12 +6,22 @@ module Blacklight
6
6
  class FilterField
7
7
  MISSING = { missing: true }.freeze
8
8
 
9
- # @param [Blacklight::Configuration::FacetField] config
9
+ # @!attribute config
10
+ # @return [Blacklight::Configuration::FacetField]
10
11
  attr_reader :config
11
12
 
12
- # @param [Blacklight::SearchState] search_state
13
+ # @!attribute search_state
14
+ # @return [Blacklight::SearchState]
13
15
  attr_reader :search_state
14
16
 
17
+ # @!attribute param
18
+ # @return [String,Symbol]
19
+ attr_reader :filters_key
20
+
21
+ # @!attribute inclusive_param
22
+ # @return [String,Symbol]
23
+ attr_reader :inclusive_filters_key
24
+
15
25
  # @return [String,Symbol]
16
26
  delegate :key, to: :config
17
27
 
@@ -20,9 +30,11 @@ module Blacklight
20
30
  def initialize(config, search_state)
21
31
  @config = config
22
32
  @search_state = search_state
33
+ @filters_key = :f
34
+ @inclusive_filters_key = :f_inclusive
23
35
  end
24
36
 
25
- # @param [String,#value] a filter item to add to the url
37
+ # @param [String,#value] item a filter item to add to the url
26
38
  # @return [Blacklight::SearchState] new state
27
39
  def add(item)
28
40
  new_state = search_state.reset_search
@@ -41,7 +53,7 @@ module Blacklight
41
53
 
42
54
  url_key = key
43
55
  params = new_state.params
44
- param = :f
56
+ param = filters_key
45
57
  value = as_url_parameter(item)
46
58
 
47
59
  if value == Blacklight::SearchState::FilterField::MISSING
@@ -49,7 +61,7 @@ module Blacklight
49
61
  value = Blacklight::Engine.config.blacklight.facet_missing_param
50
62
  end
51
63
 
52
- param = :f_inclusive if value.is_a?(Array)
64
+ param = inclusive_filters_key if value.is_a?(Array)
53
65
 
54
66
  # value could be a string
55
67
  params[param] = (params[param] || {}).dup
@@ -66,7 +78,7 @@ module Blacklight
66
78
  new_state.reset(params)
67
79
  end
68
80
 
69
- # @param [String,#value] a filter to remove from the url
81
+ # @param [String,#value] item a filter to remove from the url
70
82
  # @return [Blacklight::SearchState] new state
71
83
  def remove(item)
72
84
  new_state = search_state.reset_search
@@ -77,7 +89,7 @@ module Blacklight
77
89
  url_key = config.key
78
90
  params = new_state.params
79
91
 
80
- param = :f
92
+ param = filters_key
81
93
  value = as_url_parameter(item)
82
94
 
83
95
  if value == Blacklight::SearchState::FilterField::MISSING
@@ -85,7 +97,7 @@ module Blacklight
85
97
  value = Blacklight::Engine.config.blacklight.facet_missing_param
86
98
  end
87
99
 
88
- param = :f_inclusive if value.is_a?(Array)
100
+ param = inclusive_filters_key if value.is_a?(Array)
89
101
 
90
102
  # need to dup the facet values too,
91
103
  # if the values aren't dup'd, then the values
@@ -110,17 +122,25 @@ module Blacklight
110
122
  end
111
123
 
112
124
  # @return [Array] an array of applied filters
113
- def values
125
+ def values(except: [])
114
126
  params = search_state.params
115
- f = Array(params.dig(:f, key))
116
- f_inclusive = [params.dig(:f_inclusive, key)] if params.dig(:f_inclusive, key).present?
117
- f_missing = [Blacklight::SearchState::FilterField::MISSING] if params.dig(:f, "-#{key}")&.any? { |v| v == Blacklight::Engine.config.blacklight.facet_missing_param }
127
+ return [] if params.blank?
128
+
129
+ f = except.include?(:filters) ? [] : [params.dig(filters_key, key)].flatten.compact
130
+ f_inclusive = [params.dig(:f_inclusive, key)] unless params.dig(inclusive_filters_key, key).blank? || except.include?(:inclusive_filters)
131
+ f_missing = [Blacklight::SearchState::FilterField::MISSING] if params.dig(filters_key, "-#{key}")&.any? { |v| v == Blacklight::Engine.config.blacklight.facet_missing_param }
132
+ f_missing = [] if except.include?(:missing)
118
133
 
119
134
  f + (f_inclusive || []) + (f_missing || [])
120
135
  end
121
136
  delegate :any?, to: :values
122
137
 
123
- # @param [String,#value] a filter to remove from the url
138
+ # Appease rubocop rules by implementing #each_value
139
+ def each_value(except: [], &block)
140
+ values(except: except).each(&block)
141
+ end
142
+
143
+ # @param [String,#value] item a filter to remove from the url
124
144
  # @return [Boolean] whether the provided filter is currently applied/selected
125
145
  def include?(item)
126
146
  if item.respond_to?(:field) && item.field != key
@@ -131,14 +151,22 @@ module Blacklight
131
151
  params = search_state.params
132
152
 
133
153
  if value.is_a?(Array)
134
- (params.dig(:f_inclusive, key) || []).to_set == value.to_set
154
+ (params.dig(inclusive_filters_key, key) || []).to_set == value.to_set
135
155
  elsif value == Blacklight::SearchState::FilterField::MISSING
136
- (params.dig(:f, "-#{key}") || []).include?(Blacklight::Engine.config.blacklight.facet_missing_param)
156
+ (params.dig(filters_key, "-#{key}") || []).include?(Blacklight::Engine.config.blacklight.facet_missing_param)
137
157
  else
138
- (params.dig(:f, key) || []).include?(value)
158
+ (params.dig(filters_key, key) || []).include?(value)
139
159
  end
140
160
  end
141
161
 
162
+ def needs_normalization?(value_params)
163
+ value_params&.is_a?(Hash) && value_params != Blacklight::SearchState::FilterField::MISSING
164
+ end
165
+
166
+ def normalize(value_params)
167
+ needs_normalization?(value_params) ? value_params.values : value_params
168
+ end
169
+
142
170
  private
143
171
 
144
172
  # TODO: this code is duplicated in Blacklight::FacetsHelperBehavior
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'blacklight/deprecations/search_state_normalization'
3
4
  require 'blacklight/search_state/filter_field'
4
5
 
5
6
  module Blacklight
@@ -7,6 +8,7 @@ module Blacklight
7
8
  # parameters namely: :f, :q, :page, :per_page and, :sort
8
9
  class SearchState
9
10
  extend Deprecation
11
+ include Blacklight::Deprecations::SearchStateNormalization
10
12
 
11
13
  attr_reader :blacklight_config # Must be called blacklight_config, because Blacklight::Facet calls blacklight_config.
12
14
  attr_reader :params
@@ -17,18 +19,7 @@ module Blacklight
17
19
 
18
20
  delegate :facet_configuration_for_field, to: :blacklight_config
19
21
 
20
- # @param [ActionController::Parameters] params
21
- # @param [Blacklight::Config] blacklight_config
22
- # @param [ApplicationController] controller used for the routing helpers
23
- def initialize(params, blacklight_config, controller = nil)
24
- @params = self.class.normalize_params(params)
25
- @blacklight_config = blacklight_config
26
- @controller = controller
27
- end
28
-
29
- def self.normalize_params(untrusted_params = {})
30
- params = untrusted_params
31
-
22
+ def self.modifiable_params(params)
32
23
  if params.respond_to?(:to_unsafe_h)
33
24
  # This is the typical (not-ActionView::TestCase) code path.
34
25
  params = params.to_unsafe_h
@@ -40,12 +31,55 @@ module Blacklight
40
31
  else
41
32
  params = params.dup.to_h.with_indifferent_access
42
33
  end
34
+ params
35
+ end
43
36
 
44
- # Normalize facet parameters mangled by facebook
45
- params[:f] = normalize_facet_params(params[:f]) if facet_params_need_normalization(params[:f])
46
- params[:f_inclusive] = normalize_facet_params(params[:f_inclusive]) if facet_params_need_normalization(params[:f_inclusive])
37
+ # @param [ActionController::Parameters] params
38
+ # @param [Blacklight::Config] blacklight_config
39
+ # @param [ApplicationController] controller used for the routing helpers
40
+ def initialize(params, blacklight_config, controller = nil)
41
+ @blacklight_config = blacklight_config
42
+ @controller = controller
43
+ @params = SearchState.modifiable_params(params)
44
+ normalize_params! if needs_normalization?
45
+ end
47
46
 
48
- params
47
+ def needs_normalization?
48
+ return false if params.blank?
49
+ return true if (params.keys.map(&:to_s) - permitted_fields.map(&:to_s)).present?
50
+
51
+ !!filters.detect { |filter| filter.values.detect { |value| filter.needs_normalization?(value) } }
52
+ end
53
+
54
+ def normalize_params!
55
+ @params = normalize_params
56
+ end
57
+
58
+ def normalize_params
59
+ return params unless needs_normalization?
60
+
61
+ base_params = params.slice(*blacklight_config.search_state_fields)
62
+ normal_state = blacklight_config.facet_fields.each_value.inject(reset(base_params)) do |working_state, filter_key|
63
+ f = filter(filter_key)
64
+ next working_state unless f.any?
65
+
66
+ filter_values = f.values(except: [:inclusive_filters]).inject([]) do |memo, filter_value|
67
+ # flatten arrays that had been mangled into integer-indexed hashes
68
+ memo.concat([f.normalize(filter_value)].flatten)
69
+ end
70
+ filter_values = f.values(except: [:filters, :missing]).inject(filter_values) do |memo, filter_value|
71
+ memo << f.normalize(filter_value)
72
+ end
73
+ filter_values.inject(working_state) do |memo, filter_value|
74
+ memo.filter(filter_key).add(filter_value)
75
+ end
76
+ end
77
+ normal_state.params
78
+ end
79
+
80
+ def permitted_fields
81
+ filter_keys = filter_fields.inject(Set.new) { |memo, filter| memo.merge [filter.filters_key, filter.inclusive_filters_key] }
82
+ blacklight_config.search_state_fields + filter_keys.subtract([nil, '']).to_a
49
83
  end
50
84
 
51
85
  def to_hash
@@ -129,11 +163,7 @@ module Blacklight
129
163
  end
130
164
 
131
165
  def filters
132
- @filters ||= blacklight_config.facet_fields.each_value.map do |value|
133
- f = filter(value)
134
-
135
- f if f.any?
136
- end.compact
166
+ @filters ||= filter_fields.select(&:any?)
137
167
  end
138
168
 
139
169
  def filter(field_key_or_field)
@@ -248,16 +278,6 @@ module Blacklight
248
278
  params[facet_request_keys[:prefix]]
249
279
  end
250
280
 
251
- def self.facet_params_need_normalization(facet_params)
252
- facet_params.is_a?(Hash) && facet_params.values.any? { |x| x.is_a?(Hash) }
253
- end
254
-
255
- def self.normalize_facet_params(facet_params)
256
- facet_params.transform_values do |value|
257
- value.is_a?(Hash) ? value.values : value
258
- end
259
- end
260
-
261
281
  private
262
282
 
263
283
  def search_field_key
@@ -279,5 +299,9 @@ module Blacklight
279
299
  def reset_search_params
280
300
  Parameters.sanitize(params).except(:page, :counter)
281
301
  end
302
+
303
+ def filter_fields
304
+ blacklight_config.facet_fields.each_value.map { |value| filter(value) }
305
+ end
282
306
  end
283
307
  end
@@ -146,7 +146,9 @@ module Blacklight::Solr
146
146
  if filter.config.filter_query_builder
147
147
  filter_query, subqueries = filter.config.filter_query_builder.call(self, filter, solr_parameters)
148
148
 
149
- solr_parameters.append_filter_query(filter_query) if filter_query
149
+ Array(filter_query).each do |fq|
150
+ solr_parameters.append_filter_query(fq)
151
+ end
150
152
  solr_parameters.merge!(subqueries) if subqueries
151
153
  else
152
154
  filter.values.reject(&:blank?).each do |value|
data/lib/blacklight.rb CHANGED
@@ -7,6 +7,7 @@ require 'jbuilder'
7
7
 
8
8
  module Blacklight
9
9
  autoload :AbstractRepository, 'blacklight/abstract_repository'
10
+ autoload :Component, 'blacklight/component'
10
11
  autoload :Configuration, 'blacklight/configuration'
11
12
  autoload :Exceptions, 'blacklight/exceptions'
12
13
  autoload :Parameters, 'blacklight/parameters'
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blacklight-frontend",
3
- "version": "7.22.1",
3
+ "version": "7.25.0",
4
4
  "description": "[![Build Status](https://travis-ci.com/projectblacklight/blacklight.png?branch=main)](https://travis-ci.com/projectblacklight/blacklight) [![Gem Version](https://badge.fury.io/rb/blacklight.png)](http://badge.fury.io/rb/blacklight) [![Coverage Status](https://coveralls.io/repos/github/projectblacklight/blacklight/badge.svg?branch=main)](https://coveralls.io/github/projectblacklight/blacklight?branch=main)",
5
5
  "main": "app/assets/javascripts/blacklight",
6
6
  "scripts": {
@@ -12,7 +12,7 @@ RSpec.describe Blacklight::ConstraintsComponent, type: :component do
12
12
  end
13
13
 
14
14
  let(:blacklight_config) do
15
- Blacklight::Configuration.new.tap do |config|
15
+ Blacklight::Configuration.new.configure do |config|
16
16
  config.add_facet_field 'some_facet'
17
17
  end
18
18
  end
@@ -43,6 +43,19 @@ RSpec.describe Blacklight::ConstraintsComponent, type: :component do
43
43
  context 'with a facet' do
44
44
  let(:query_params) { { f: { some_facet: ['some value'] } } }
45
45
 
46
+ before do
47
+ controller.blacklight_config.configure do |config|
48
+ config.add_facet_field :some_facet
49
+ end
50
+ end
51
+
52
+ # this has to be cleaned up to prevent leaking class configuration
53
+ after do
54
+ controller.blacklight_config.configure do |config|
55
+ config['facet_fields'].delete('some_facet')
56
+ end
57
+ end
58
+
46
59
  it 'renders the query' do
47
60
  expect(rendered).to have_selector('.constraint-value > .filter-name', text: 'Some Facet').and(have_selector('.constraint-value > .filter-value', text: 'some value'))
48
61
  end
@@ -123,7 +123,12 @@ RSpec.describe Blacklight::FacetFieldListComponent, type: :component do
123
123
  )
124
124
  end
125
125
 
126
- let(:search_state) { Blacklight::SearchState.new(params.with_indifferent_access, Blacklight::Configuration.new) }
126
+ let(:blacklight_config) do
127
+ Blacklight::Configuration.new.configure do |config|
128
+ config.add_facet_field :field
129
+ end
130
+ end
131
+ let(:search_state) { Blacklight::SearchState.new(params.with_indifferent_access, blacklight_config) }
127
132
  let(:params) { { f_inclusive: { field: %w[a b c] } } }
128
133
 
129
134
  it 'displays the constraint above the list' do
@@ -7,8 +7,14 @@ RSpec.describe Blacklight::FacetItemPivotComponent, type: :component do
7
7
  render_inline_to_capybara_node(described_class.new(facet_item: facet_item))
8
8
  end
9
9
 
10
+ let(:blacklight_config) do
11
+ Blacklight::Configuration.new.configure do |config|
12
+ config.add_facet_field :z
13
+ end
14
+ end
15
+
10
16
  let(:search_state) do
11
- Blacklight::SearchState.new({}, Blacklight::Configuration.new)
17
+ Blacklight::SearchState.new({}, blacklight_config)
12
18
  end
13
19
 
14
20
  let(:facet_item) do
@@ -11,7 +11,10 @@ RSpec.describe Blacklight::Base do
11
11
  let(:raw_params) { HashWithIndifferentAccess.new a: 1 }
12
12
  let(:params) { ActionController::Parameters.new raw_params }
13
13
 
14
- before { allow(controller).to receive_messages(params: params) }
14
+ before do
15
+ controller.blacklight_config.search_state_fields << :a
16
+ allow(controller).to receive_messages(params: params)
17
+ end
15
18
 
16
19
  it "creates a path object" do
17
20
  expect(subject).to be_kind_of Blacklight::SearchState
@@ -246,6 +246,10 @@ RSpec.describe Blacklight::FacetsHelperBehavior do
246
246
  end
247
247
 
248
248
  describe "facet_field_in_params?" do
249
+ before do
250
+ blacklight_config.add_facet_field "some-field"
251
+ end
252
+
249
253
  it "checks if the facet field is selected in the user params" do
250
254
  allow(helper).to receive_messages(params: { f: { "some-field" => ["x"] } })
251
255
  expect(helper).to be_facet_field_in_params("some-field")
@@ -254,16 +258,29 @@ RSpec.describe Blacklight::FacetsHelperBehavior do
254
258
  end
255
259
 
256
260
  describe "facet_params" do
261
+ let(:facet_config) { ["some-field"] }
262
+ let(:params) { { f: { "some-field" => ["x"] } } }
263
+
264
+ before do
265
+ blacklight_config.add_facet_field *facet_config
266
+ allow(helper).to receive_messages(params: params)
267
+ end
268
+
257
269
  it "extracts the facet parameters for a field" do
258
- allow(helper).to receive_messages(params: { f: { "some-field" => ["x"] } })
259
270
  expect(helper.facet_params("some-field")).to match_array ["x"]
260
271
  end
261
272
 
262
- it "uses the blacklight key to extract the right fields" do
263
- blacklight_config.add_facet_field "some-key", field: "some-field"
264
- allow(helper).to receive_messages(params: { f: { "some-key" => ["x"] } })
265
- expect(helper.facet_params("some-key")).to match_array ["x"]
266
- expect(helper.facet_params("some-field")).to match_array ["x"]
273
+ context "a facet is not keyed by the field name" do
274
+ let(:facet_config) { ["some-key", { field: "some-field" }] }
275
+ let(:params) { { f: { "some-key" => ["x"] } } }
276
+
277
+ it "uses the blacklight key to extract the right fields" do
278
+ expect(helper.facet_params("some-key")).to match_array ["x"]
279
+ end
280
+
281
+ it "looks up facet params by configured field or key values" do
282
+ expect(helper.facet_params("some-field")).to match_array ["x"]
283
+ end
267
284
  end
268
285
  end
269
286
 
@@ -75,6 +75,13 @@ RSpec.describe Blacklight::UrlHelperBehavior do
75
75
  let(:query_params) { { q: "query", f: "facets", controller: 'catalog' } }
76
76
  let(:bookmarks_query_params) { { controller: 'bookmarks' } }
77
77
 
78
+ before do
79
+ # this is bad data but the legacy test exercises search fields, not filters
80
+ blacklight_config.configure do |config|
81
+ config.search_state_fields << :f
82
+ end
83
+ end
84
+
78
85
  it "builds a link tag to catalog using session[:search] for query params" do
79
86
  allow(helper).to receive(:current_search_session).and_return double(query_params: query_params)
80
87
  tag = helper.link_back_to_catalog
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Blacklight::Component do
4
+ let(:component_class) { Blacklight::DocumentTitleComponent }
5
+
6
+ context "subclassed" do
7
+ it "returns our Compiler implementation" do
8
+ expect(component_class.ancestors).to include described_class
9
+ expect(component_class.compiler).to be_a Blacklight::Component::EngineCompiler
10
+ end
11
+ end
12
+
13
+ describe Blacklight::Component::EngineCompiler do
14
+ subject(:compiler) { described_class.new(component_class) }
15
+
16
+ let(:original_compiler) { ViewComponent::Compiler.new(component_class) }
17
+ let(:original_path) { original_compiler.send(:templates).first[:path] }
18
+ let(:resolved_path) { compiler.templates.first[:path] }
19
+
20
+ context "without overrides" do
21
+ it "links to engine template" do
22
+ expect(resolved_path).not_to include(".internal_test_app")
23
+ expect(resolved_path).to eql(original_path)
24
+ end
25
+ end
26
+
27
+ context "with overrides" do
28
+ let(:path_match) do
29
+ Regexp.new(Regexp.escape(File.join(".internal_test_app", component_class.view_component_path)))
30
+ end
31
+
32
+ before do
33
+ allow(File).to receive(:exist?).and_call_original
34
+ allow(File).to receive(:exist?).with(path_match).and_return(true)
35
+ end
36
+
37
+ it "links to application template" do
38
+ expect(resolved_path).to include(".internal_test_app")
39
+ expect(resolved_path).not_to eql(original_path)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -6,10 +6,13 @@ RSpec.describe Blacklight::SearchState::FilterField do
6
6
  let(:params) { { f: { some_field: %w[1 2], another_field: ['3'] } } }
7
7
  let(:blacklight_config) do
8
8
  Blacklight::Configuration.new.configure do |config|
9
- config.add_facet_field 'some_field'
10
9
  config.add_facet_field 'another_field', single: true
10
+ simple_facet_fields.each { |simple_facet_field| config.add_facet_field simple_facet_field }
11
+ config.search_state_fields = config.search_state_fields + additional_search_fields
11
12
  end
12
13
  end
14
+ let(:simple_facet_fields) { [:some_field] }
15
+ let(:additional_search_fields) { [] }
13
16
  let(:controller) { double }
14
17
 
15
18
  describe '#add' do
@@ -50,6 +53,8 @@ RSpec.describe Blacklight::SearchState::FilterField do
50
53
  end
51
54
 
52
55
  context 'with a pivot facet-type item' do
56
+ let(:simple_facet_fields) { [:some_field, :some_other_field] }
57
+
53
58
  it 'includes the pivot facet fqs' do
54
59
  filter = search_state.filter('some_field')
55
60
  new_state = filter.add(OpenStruct.new(fq: { some_other_field: '5' }, value: '4'))
@@ -200,4 +205,10 @@ RSpec.describe Blacklight::SearchState::FilterField do
200
205
  expect(search_state.filter('some_field').include?(OpenStruct.new(value: '1'))).to eq true
201
206
  end
202
207
  end
208
+
209
+ describe '#needs_normalization?' do
210
+ it 'returns false for Blacklight::SearchState::FilterField::MISSING' do
211
+ expect(search_state.filter('some_field').needs_normalization?(Blacklight::SearchState::FilterField::MISSING)).to be false
212
+ end
213
+ end
203
214
  end
@@ -9,14 +9,19 @@ RSpec.describe Blacklight::SearchState do
9
9
  Blacklight::Configuration.new.configure do |config|
10
10
  config.index.title_field = 'title_tsim'
11
11
  config.index.display_type_field = 'format'
12
+ simple_facet_fields.each { |simple_facet_field| config.add_facet_field simple_facet_field }
13
+ config.search_state_fields = config.search_state_fields + additional_search_fields
12
14
  end
13
15
  end
14
16
 
15
17
  let(:parameter_class) { ActionController::Parameters }
16
18
  let(:controller) { double }
17
19
  let(:params) { parameter_class.new }
20
+ let(:simple_facet_fields) { [:facet_field_1, :facet_field_2] }
21
+ let(:additional_search_fields) { [] }
18
22
 
19
23
  describe '#to_h' do
24
+ let(:additional_search_fields) { [:a] }
20
25
  let(:data) { { a: '1' } }
21
26
  let(:params) { parameter_class.new data }
22
27
 
@@ -50,6 +55,7 @@ RSpec.describe Blacklight::SearchState do
50
55
  end
51
56
 
52
57
  context 'with facebooks badly mangled query parameters' do
58
+ let(:simple_facet_fields) { [:field] }
53
59
  let(:params) do
54
60
  { f: { field: { '0': 'first', '1': 'second' } },
55
61
  f_inclusive: { field: { '0': 'first', '1': 'second' } } }
@@ -62,6 +68,7 @@ RSpec.describe Blacklight::SearchState do
62
68
  end
63
69
 
64
70
  context 'deleting item from to_h' do
71
+ let(:additional_search_fields) { [:q_1] }
65
72
  let(:params) { { q: 'foo', q_1: 'bar' } }
66
73
 
67
74
  it 'does not mutate search_state to mutate search_state.to_h' do
@@ -74,6 +81,7 @@ RSpec.describe Blacklight::SearchState do
74
81
  end
75
82
 
76
83
  context 'deleting deep item from to_h' do
84
+ let(:additional_search_fields) { [:foo] }
77
85
  let(:params) { { foo: { bar: [] } } }
78
86
 
79
87
  it 'does not mutate search_state to deep mutate search_state.to_h' do
@@ -87,6 +95,7 @@ RSpec.describe Blacklight::SearchState do
87
95
  end
88
96
 
89
97
  describe 'interface compatibility with params' do
98
+ let(:simple_facet_fields) { [:ff] }
90
99
  let(:params) { parameter_class.new f: { ff: ['xyz'] } }
91
100
 
92
101
  it 'implements param methods' do
@@ -107,6 +116,7 @@ RSpec.describe Blacklight::SearchState do
107
116
  end
108
117
 
109
118
  describe '#filter_params' do
119
+ let(:simple_facet_fields) { [:ff] }
110
120
  let(:params) { parameter_class.new f: { ff: ['xyz'] } }
111
121
 
112
122
  it 'returns the query param' do
@@ -128,6 +138,7 @@ RSpec.describe Blacklight::SearchState do
128
138
  end
129
139
 
130
140
  context 'with a facet param' do
141
+ let(:simple_facet_fields) { [:ff] }
131
142
  let(:params) { parameter_class.new f: { ff: ['xyz'] } }
132
143
 
133
144
  it 'is true' do
@@ -137,6 +148,7 @@ RSpec.describe Blacklight::SearchState do
137
148
  end
138
149
 
139
150
  describe "params_for_search" do
151
+ let(:additional_search_fields) { [:default] }
140
152
  let(:params) { parameter_class.new 'default' => 'params' }
141
153
 
142
154
  it "takes original params" do
@@ -198,6 +210,7 @@ RSpec.describe Blacklight::SearchState do
198
210
  end
199
211
 
200
212
  context "with a block" do
213
+ let(:additional_search_fields) { [:a, :b, :c, :d] }
201
214
  let(:params) { parameter_class.new a: 1, b: 2 }
202
215
 
203
216
  it "evalutes the block and allow it to add or remove keys" do
@@ -356,6 +369,7 @@ RSpec.describe Blacklight::SearchState do
356
369
  end
357
370
 
358
371
  describe "#remove_facet_params" do
372
+ let(:simple_facet_fields) { [:some_field, :another_field] }
359
373
  let(:params) { parameter_class.new 'f' => facet_params }
360
374
  let(:facet_params) { {} }
361
375
 
@@ -382,9 +396,7 @@ RSpec.describe Blacklight::SearchState do
382
396
 
383
397
  context "when the facet has configuration" do
384
398
  before do
385
- allow(search_state).to receive(:facet_configuration_for_field).with('some_field').and_return(
386
- double(single: true, field: "a_solr_field", key: "some_key")
387
- )
399
+ blacklight_config.facet_fields['some_field'].merge!(single: true, field: "a_solr_field", key: 'some_key')
388
400
  end
389
401
 
390
402
  let(:facet_params) { { 'some_key' => %w[some_value another_value] } }
@@ -415,6 +427,8 @@ RSpec.describe Blacklight::SearchState do
415
427
  end
416
428
 
417
429
  describe '#reset' do
430
+ let(:additional_search_fields) { [:a] }
431
+
418
432
  it 'returns a search state with the given parameters' do
419
433
  new_state = search_state.reset('a' => 1)
420
434
 
@@ -552,7 +552,7 @@ RSpec.describe "Blacklight::Configuration", api: true do
552
552
  end
553
553
 
554
554
  it "takes ShowField argument" do
555
- config.add_sms_field("title_tsim", Blacklight::Configuration::SmsField.new(field: "title_display", label: "Title"))
555
+ config.add_sms_field("title_tsim", Blacklight::Configuration::DisplayField.new(field: "title_display", label: "Title"))
556
556
 
557
557
  expect(config.sms_fields["title_tsim"]).not_to be_nil
558
558
  expect(config.sms_fields["title_tsim"].label).to eq "Title"
@@ -598,7 +598,7 @@ RSpec.describe "Blacklight::Configuration", api: true do
598
598
  end
599
599
 
600
600
  it "takes ShowField argument" do
601
- config.add_email_field("title_tsim", Blacklight::Configuration::EmailField.new(field: "title_display", label: "Title"))
601
+ config.add_email_field("title_tsim", Blacklight::Configuration::DisplayField.new(field: "title_display", label: "Title"))
602
602
 
603
603
  expect(config.email_fields["title_tsim"]).not_to be_nil
604
604
  expect(config.email_fields["title_tsim"].label).to eq "Title"
@@ -273,6 +273,21 @@ RSpec.describe Blacklight::Solr::SearchBuilderBehavior, api: true do
273
273
  end
274
274
  end
275
275
 
276
+ context 'with a facet with a custom filter query builder that returns multiple values' do
277
+ let(:user_params) { { f: { some: ['value'] } }.with_indifferent_access }
278
+
279
+ before do
280
+ blacklight_config.add_facet_field 'some', filter_query_builder: (lambda do |*_args|
281
+ [['some:filter', 'another:filter'], { qq1: 'abc' }]
282
+ end)
283
+ end
284
+
285
+ it "has proper solr parameters" do
286
+ expect(subject[:fq]).to include('some:filter', 'another:filter')
287
+ expect(subject[:qq1]).to include('abc')
288
+ end
289
+ end
290
+
276
291
  describe 'with a json facet' do
277
292
  let(:user_params) { { f: { json_facet: ['value'] } }.with_indifferent_access }
278
293