blacklight 7.14.1 → 7.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/VERSION +1 -1
  4. data/app/assets/stylesheets/blacklight/_icons.scss +5 -1
  5. data/app/assets/stylesheets/blacklight/blacklight_defaults.scss +5 -0
  6. data/app/components/blacklight/advanced_search_form_component.html.erb +52 -0
  7. data/app/components/blacklight/advanced_search_form_component.rb +88 -0
  8. data/app/components/blacklight/constraint_component.html.erb +1 -1
  9. data/app/components/blacklight/constraint_layout_component.html.erb +1 -1
  10. data/app/components/blacklight/constraints_component.html.erb +19 -3
  11. data/app/components/blacklight/constraints_component.rb +41 -18
  12. data/app/components/blacklight/content_areas_shim.rb +12 -0
  13. data/app/components/blacklight/document/action_component.html.erb +1 -1
  14. data/app/components/blacklight/document/action_component.rb +6 -1
  15. data/app/components/blacklight/document/actions_component.html.erb +3 -5
  16. data/app/components/blacklight/document/actions_component.rb +16 -2
  17. data/app/components/blacklight/document/thumbnail_component.html.erb +1 -1
  18. data/app/components/blacklight/document/thumbnail_component.rb +4 -1
  19. data/app/components/blacklight/document_component.html.erb +4 -7
  20. data/app/components/blacklight/document_component.rb +73 -68
  21. data/app/components/blacklight/document_metadata_component.html.erb +2 -2
  22. data/app/components/blacklight/document_metadata_component.rb +13 -2
  23. data/app/components/blacklight/document_title_component.html.erb +17 -0
  24. data/app/components/blacklight/document_title_component.rb +59 -0
  25. data/app/components/blacklight/facet_field_checkboxes_component.html.erb +23 -0
  26. data/app/components/blacklight/facet_field_checkboxes_component.rb +24 -0
  27. data/app/components/blacklight/facet_field_component.rb +4 -1
  28. data/app/components/blacklight/facet_field_inclusive_constraint_component.html.erb +6 -0
  29. data/app/components/blacklight/facet_field_inclusive_constraint_component.rb +29 -0
  30. data/app/components/blacklight/facet_field_list_component.html.erb +3 -2
  31. data/app/components/blacklight/facet_field_no_layout_component.rb +4 -1
  32. data/app/components/blacklight/facet_field_pagination_component.rb +1 -1
  33. data/app/components/blacklight/facet_item_component.rb +4 -2
  34. data/app/components/blacklight/metadata_field_component.html.erb +2 -2
  35. data/app/components/blacklight/metadata_field_layout_component.html.erb +3 -1
  36. data/app/components/blacklight/metadata_field_layout_component.rb +26 -1
  37. data/app/components/blacklight/response/view_type_button_component.html.erb +4 -0
  38. data/app/components/blacklight/response/view_type_button_component.rb +36 -0
  39. data/app/components/blacklight/response/view_type_component.html.erb +2 -5
  40. data/app/components/blacklight/response/view_type_component.rb +9 -13
  41. data/app/components/blacklight/search_bar_component.html.erb +4 -0
  42. data/app/components/blacklight/search_bar_component.rb +13 -4
  43. data/app/components/blacklight/system/dropdown_component.html.erb +4 -7
  44. data/app/components/blacklight/system/dropdown_component.rb +24 -0
  45. data/app/components/blacklight/system/flash_message_component.html.erb +1 -1
  46. data/app/components/blacklight/system/flash_message_component.rb +7 -1
  47. data/app/components/blacklight/system/modal_component.rb +7 -1
  48. data/app/controllers/concerns/blacklight/catalog.rb +7 -1
  49. data/app/helpers/blacklight/blacklight_helper_behavior.rb +3 -4
  50. data/app/helpers/blacklight/component_helper_behavior.rb +2 -2
  51. data/app/helpers/blacklight/configuration_helper_behavior.rb +2 -2
  52. data/app/helpers/blacklight/render_constraints_helper_behavior.rb +2 -2
  53. data/app/presenters/blacklight/clause_presenter.rb +37 -0
  54. data/app/presenters/blacklight/document_presenter.rb +13 -5
  55. data/app/presenters/blacklight/facet_field_presenter.rb +4 -0
  56. data/app/presenters/blacklight/facet_grouped_item_presenter.rb +45 -0
  57. data/app/presenters/blacklight/facet_item_presenter.rb +32 -20
  58. data/app/presenters/blacklight/inclusive_facet_item_presenter.rb +16 -0
  59. data/app/presenters/blacklight/rendering/helper_method.rb +4 -4
  60. data/app/presenters/blacklight/search_bar_presenter.rb +4 -0
  61. data/app/services/blacklight/search_service.rb +1 -1
  62. data/app/views/bookmarks/_tools.html.erb +1 -1
  63. data/app/views/catalog/_advanced_search_form.html.erb +7 -0
  64. data/app/views/catalog/_advanced_search_help.html.erb +24 -0
  65. data/app/views/catalog/_citation.html.erb +1 -1
  66. data/app/views/catalog/_document.html.erb +2 -2
  67. data/app/views/catalog/_facet_layout.html.erb +2 -2
  68. data/app/views/catalog/_search_form.html.erb +1 -0
  69. data/app/views/catalog/_show_main_content.html.erb +3 -3
  70. data/app/views/catalog/advanced_search.html.erb +17 -0
  71. data/app/views/catalog/email.html.erb +2 -2
  72. data/app/views/catalog/email_success.html.erb +1 -1
  73. data/app/views/catalog/facet.html.erb +3 -3
  74. data/app/views/catalog/sms.html.erb +2 -2
  75. data/app/views/catalog/sms_success.html.erb +1 -1
  76. data/blacklight.gemspec +2 -2
  77. data/config/i18n-tasks.yml +1 -0
  78. data/config/locales/blacklight.de.yml +2 -2
  79. data/config/locales/blacklight.en.yml +17 -0
  80. data/lib/blacklight/configuration.rb +52 -6
  81. data/lib/blacklight/configuration/view_config.rb +16 -5
  82. data/lib/blacklight/engine.rb +3 -1
  83. data/lib/blacklight/open_struct_with_hash_access.rb +22 -1
  84. data/lib/blacklight/routes/searchable.rb +1 -0
  85. data/lib/blacklight/search_builder.rb +2 -0
  86. data/lib/blacklight/search_state.rb +7 -3
  87. data/lib/blacklight/search_state/filter_field.rb +17 -7
  88. data/lib/blacklight/solr/facet_paginator.rb +2 -0
  89. data/lib/blacklight/solr/repository.rb +11 -2
  90. data/lib/blacklight/solr/request.rb +31 -0
  91. data/lib/blacklight/solr/response.rb +2 -16
  92. data/lib/blacklight/solr/response/facets.rb +76 -22
  93. data/lib/blacklight/solr/response/params.rb +104 -0
  94. data/lib/blacklight/solr/search_builder_behavior.rb +126 -32
  95. data/lib/generators/blacklight/assets_generator.rb +6 -2
  96. data/lib/generators/blacklight/user_generator.rb +1 -1
  97. data/spec/components/blacklight/advanced_search_form_component_spec.rb +51 -0
  98. data/spec/components/blacklight/document_component_spec.rb +18 -3
  99. data/spec/components/blacklight/facet_field_checkboxes_component_spec.rb +55 -0
  100. data/spec/components/blacklight/facet_field_list_component_spec.rb +39 -4
  101. data/spec/controllers/catalog_controller_spec.rb +9 -0
  102. data/spec/features/advanced_search_spec.rb +67 -0
  103. data/spec/lib/blacklight/configuration/view_config_spec.rb +1 -1
  104. data/spec/lib/blacklight/open_struct_with_hash_access_spec.rb +20 -0
  105. data/spec/lib/blacklight/search_state/filter_field_spec.rb +65 -0
  106. data/spec/models/blacklight/configuration_spec.rb +64 -0
  107. data/spec/models/blacklight/solr/facet_paginator_spec.rb +4 -0
  108. data/spec/models/blacklight/solr/repository_spec.rb +12 -0
  109. data/spec/models/blacklight/solr/request_spec.rb +62 -29
  110. data/spec/models/blacklight/solr/response/facets_spec.rb +109 -0
  111. data/spec/models/blacklight/solr/response_spec.rb +10 -0
  112. data/spec/models/blacklight/solr/search_builder_spec.rb +77 -0
  113. data/spec/presenters/blacklight/clause_presenter_spec.rb +34 -0
  114. data/spec/presenters/blacklight/document_presenter_spec.rb +13 -0
  115. data/spec/presenters/blacklight/facet_grouped_item_presenter_spec.rb +41 -0
  116. data/spec/views/catalog/index.atom.builder_spec.rb +1 -1
  117. metadata +37 -9
@@ -68,11 +68,15 @@ module Blacklight
68
68
  private
69
69
 
70
70
  def turbolinks?
71
- @turbolinks ||= IO.read("app/assets/javascripts/application.js").include?('turbolinks')
71
+ @turbolinks ||= application_js.include?('turbolinks')
72
72
  end
73
73
 
74
74
  def has_blacklight_assets?
75
- IO.read("app/assets/javascripts/application.js").include?('blacklight/blacklight')
75
+ application_js.include?('blacklight/blacklight')
76
+ end
77
+
78
+ def application_js
79
+ IO.read(File.expand_path("app/assets/javascripts/application.js", destination_root))
76
80
  end
77
81
  end
78
82
  end
@@ -47,7 +47,7 @@ module Blacklight
47
47
  # Add Blacklight to the user model
48
48
  def inject_blacklight_user_behavior
49
49
  file_path = "app/models/#{model_name.underscore}.rb"
50
- if File.exist?(file_path)
50
+ if File.exist?(File.expand_path(file_path, destination_root))
51
51
  inject_into_class file_path, model_name.classify do
52
52
  "\n # Connects this user object to Blacklights Bookmarks." \
53
53
  "\n include Blacklight::User\n"
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Blacklight::AdvancedSearchFormComponent, type: :component do
6
+ subject(:render) do
7
+ component.render_in(view_context)
8
+ end
9
+
10
+ let(:component) { described_class.new(url: '/whatever', response: response, params: params) }
11
+ let(:response) { Blacklight::Solr::Response.new({ facet_counts: { facet_fields: { format: { 'Book' => 10, 'CD' => 5 } } } }.with_indifferent_access, {}) }
12
+ let(:params) { {} }
13
+
14
+ let(:rendered) do
15
+ Capybara::Node::Simple.new(render)
16
+ end
17
+
18
+ let(:view_context) { controller.view_context }
19
+
20
+ before do
21
+ allow(view_context).to receive(:facet_limit_for).and_return(nil)
22
+ end
23
+
24
+ context 'with additional parameters' do
25
+ let(:params) { { some: :parameter, an_array: [1, 2] } }
26
+
27
+ it 'adds additional parameters as hidden fields' do
28
+ expect(rendered).to have_field 'some', with: 'parameter', type: :hidden
29
+ expect(rendered).to have_field 'an_array[]', with: '1', type: :hidden
30
+ expect(rendered).to have_field 'an_array[]', with: '2', type: :hidden
31
+ end
32
+ end
33
+
34
+ it 'has text fields for each search field' do
35
+ expect(rendered).to have_selector '.advanced-search-field', count: 4
36
+ expect(rendered).to have_field 'clause_0_field', with: 'all_fields', type: :hidden
37
+ expect(rendered).to have_field 'clause_1_field', with: 'title', type: :hidden
38
+ expect(rendered).to have_field 'clause_2_field', with: 'author', type: :hidden
39
+ expect(rendered).to have_field 'clause_3_field', with: 'subject', type: :hidden
40
+ end
41
+
42
+ it 'has filters' do
43
+ expect(rendered).to have_selector '.blacklight-format'
44
+ expect(rendered).to have_field 'f_inclusive[format][]', with: 'Book'
45
+ expect(rendered).to have_field 'f_inclusive[format][]', with: 'CD'
46
+ end
47
+
48
+ it 'has a sort field' do
49
+ expect(rendered).to have_select 'sort', options: %w[relevance year author title]
50
+ end
51
+ end
@@ -3,7 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  RSpec.describe Blacklight::DocumentComponent, type: :component do
6
- subject(:component) { described_class.new(document: document, **attr) }
6
+ subject(:component) { described_class.new(document: document, presenter: view_context.document_presenter(document), **attr) }
7
7
 
8
8
  let(:attr) { {} }
9
9
  let(:view_context) { controller.view_context }
@@ -47,7 +47,7 @@ RSpec.describe Blacklight::DocumentComponent, type: :component do
47
47
  component.with(:embed, 'Embed')
48
48
  component.with(:metadata, 'Metadata')
49
49
  component.with(:thumbnail, 'Thumbnail')
50
- component.with(:actions, 'Actions')
50
+ component.with(:actions) { 'Actions' }
51
51
 
52
52
  expect(rendered).to have_content 'Title'
53
53
  expect(rendered).to have_content 'Embed'
@@ -65,7 +65,7 @@ RSpec.describe Blacklight::DocumentComponent, type: :component do
65
65
 
66
66
  context 'with a provided body' do
67
67
  it 'opts-out of normal component content' do
68
- component.with(:body, 'Body content')
68
+ component.with(:body) { 'Body content' }
69
69
 
70
70
  expect(rendered).to have_content 'Body content'
71
71
  expect(rendered).not_to have_selector 'header'
@@ -151,4 +151,19 @@ RSpec.describe Blacklight::DocumentComponent, type: :component do
151
151
  expect(rendered).to have_selector 'dd', text: 'Title'
152
152
  expect(rendered).not_to have_selector 'dt', text: 'ISBN:'
153
153
  end
154
+
155
+ context 'with a thumbnail component' do
156
+ let(:attr) { { thumbnail_component: thumbnail_component_class } }
157
+ let(:thumbnail_component_class) do
158
+ Class.new(ViewComponent::Base) do
159
+ def render_in(view_context)
160
+ view_context.capture { 'Thumb!' }
161
+ end
162
+ end
163
+ end
164
+
165
+ it 'uses the provided thumbnail component' do
166
+ expect(rendered).to have_content 'Thumb!'
167
+ end
168
+ end
154
169
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Blacklight::FacetFieldCheckboxesComponent, type: :component do
6
+ subject(:render) do
7
+ render_inline(described_class.new(facet_field: facet_field))
8
+ end
9
+
10
+ let(:rendered) do
11
+ Capybara::Node::Simple.new(render)
12
+ end
13
+
14
+ let(:facet_field) do
15
+ instance_double(
16
+ Blacklight::FacetFieldPresenter,
17
+ facet_field: Blacklight::Configuration::NullField.new(key: 'field'),
18
+ paginator: paginator,
19
+ key: 'field',
20
+ label: 'Field',
21
+ active?: false,
22
+ collapsed?: false,
23
+ modal_path: nil,
24
+ html_id: 'facet-field',
25
+ search_state: search_state
26
+ )
27
+ end
28
+
29
+ let(:paginator) do
30
+ instance_double(Blacklight::FacetPaginator, items: [
31
+ double(label: 'a', hits: 10, value: 'a'),
32
+ double(label: 'b', hits: 33, value: 'b'),
33
+ double(label: 'c', hits: 3, value: 'c')
34
+ ])
35
+ end
36
+
37
+ let(:search_state) { Blacklight::SearchState.new(params.with_indifferent_access, Blacklight::Configuration.new) }
38
+ let(:params) { { f: { field: ['a'] } } }
39
+
40
+ it 'renders a collapsible card' do
41
+ expect(rendered).to have_selector '.card'
42
+ expect(rendered).to have_button 'Field'
43
+ expect(rendered).to have_selector 'button[data-target="#facet-field"]'
44
+ expect(rendered).to have_selector '#facet-field.collapse.show'
45
+ end
46
+
47
+ it 'renders the facet items' do
48
+ expect(rendered).to have_selector 'ul.facet-values'
49
+ expect(rendered).to have_selector 'li', count: 3
50
+
51
+ expect(rendered).to have_field 'f_inclusive[field][]', with: 'a'
52
+ expect(rendered).to have_field 'f_inclusive[field][]', with: 'b'
53
+ expect(rendered).to have_field 'f_inclusive[field][]', with: 'c'
54
+ end
55
+ end
@@ -20,7 +20,8 @@ RSpec.describe Blacklight::FacetFieldListComponent, type: :component do
20
20
  active?: false,
21
21
  collapsed?: false,
22
22
  modal_path: nil,
23
- html_id: 'facet-field'
23
+ html_id: 'facet-field',
24
+ values: []
24
25
  )
25
26
  end
26
27
 
@@ -53,7 +54,8 @@ RSpec.describe Blacklight::FacetFieldListComponent, type: :component do
53
54
  active?: true,
54
55
  collapsed?: false,
55
56
  modal_path: nil,
56
- html_id: 'facet-field'
57
+ html_id: 'facet-field',
58
+ values: []
57
59
  )
58
60
  end
59
61
 
@@ -72,7 +74,8 @@ RSpec.describe Blacklight::FacetFieldListComponent, type: :component do
72
74
  active?: false,
73
75
  collapsed?: true,
74
76
  modal_path: nil,
75
- html_id: 'facet-field'
77
+ html_id: 'facet-field',
78
+ values: []
76
79
  )
77
80
  end
78
81
 
@@ -97,7 +100,8 @@ RSpec.describe Blacklight::FacetFieldListComponent, type: :component do
97
100
  active?: false,
98
101
  collapsed?: false,
99
102
  modal_path: '/catalog/facet/modal',
100
- html_id: 'facet-field'
103
+ html_id: 'facet-field',
104
+ values: []
101
105
  )
102
106
  end
103
107
 
@@ -105,4 +109,35 @@ RSpec.describe Blacklight::FacetFieldListComponent, type: :component do
105
109
  expect(rendered).to have_link 'more Field', href: '/catalog/facet/modal'
106
110
  end
107
111
  end
112
+
113
+ context 'with inclusive facets' do
114
+ let(:facet_field) do
115
+ instance_double(
116
+ Blacklight::FacetFieldPresenter,
117
+ paginator: paginator,
118
+ facet_field: Blacklight::Configuration::NullField.new(key: 'field'),
119
+ key: 'field',
120
+ label: 'Field',
121
+ active?: false,
122
+ collapsed?: false,
123
+ modal_path: nil,
124
+ html_id: 'facet-field',
125
+ values: [%w[a b c]],
126
+ search_state: search_state
127
+ )
128
+ end
129
+
130
+ let(:search_state) { Blacklight::SearchState.new(params.with_indifferent_access, Blacklight::Configuration.new) }
131
+ let(:params) { { f_inclusive: { field: %w[a b c] } } }
132
+
133
+ it 'displays the constraint above the list' do
134
+ expect(rendered).to have_content 'Any of:'
135
+ expect(rendered).to have_selector '.inclusive_or .facet-label', text: 'a'
136
+ expect(rendered).to have_link '[remove]', href: 'http://test.host/catalog?f_inclusive%5Bfield%5D%5B%5D=b&f_inclusive%5Bfield%5D%5B%5D=c'
137
+ expect(rendered).to have_selector '.inclusive_or .facet-label', text: 'b'
138
+ expect(rendered).to have_link '[remove]', href: 'http://test.host/catalog?f_inclusive%5Bfield%5D%5B%5D=a&f_inclusive%5Bfield%5D%5B%5D=c'
139
+ expect(rendered).to have_selector '.inclusive_or .facet-label', text: 'c'
140
+ expect(rendered).to have_link '[remove]', href: 'http://test.host/catalog?f_inclusive%5Bfield%5D%5B%5D=a&f_inclusive%5Bfield%5D%5B%5D=b'
141
+ end
142
+ end
108
143
  end
@@ -301,6 +301,15 @@ RSpec.describe CatalogController, api: true do
301
301
  end
302
302
  end
303
303
 
304
+ describe 'GET advanced_search' do
305
+ it 'renders an advanced search form' do
306
+ get :advanced_search
307
+ expect(response).to be_successful
308
+
309
+ assert_facets_have_values(assigns(:response).aggregations)
310
+ end
311
+ end
312
+
304
313
  # SHOW ACTION
305
314
  describe "show action" do
306
315
  describe "with format :html" do
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe "Blacklight Advanced Search Form" do
6
+ describe "advanced search form" do
7
+ before do
8
+ visit '/catalog/advanced?hypothetical_existing_param=true&q=ignore+this+existing+query'
9
+ end
10
+
11
+ it "has field and facet blocks" do
12
+ expect(page).to have_selector('.query-criteria')
13
+ expect(page).to have_selector('.limit-criteria')
14
+ end
15
+
16
+ describe "query column" do
17
+ it "gives the user a choice between and/or queries" do
18
+ expect(page).to have_selector('#op')
19
+ within('#op') do
20
+ expect(page).to have_selector('option[value="must"]')
21
+ expect(page).to have_selector('option[value="should"]')
22
+ end
23
+ end
24
+
25
+ it "lists the configured search fields" do
26
+ expect(page).to have_field 'All Fields'
27
+ expect(page).to have_field 'Title'
28
+ expect(page).to have_field 'Author'
29
+ expect(page).to have_field 'Subject'
30
+ end
31
+ end
32
+
33
+ describe "facet column" do
34
+ it "lists facets" do
35
+ expect(page).to have_selector('.blacklight-language_ssim')
36
+
37
+ within('.blacklight-language_ssim') do
38
+ expect(page).to have_content 'Language'
39
+ end
40
+ end
41
+ end
42
+
43
+ it 'scopes searches to fields' do
44
+ fill_in 'Title', with: 'Medicine'
45
+ click_on 'advanced-search-submit'
46
+ expect(page).to have_content 'Remove constraint Title: Medicine'
47
+ expect(page).to have_content 'Strong Medicine speaks'
48
+ end
49
+ end
50
+
51
+ describe "prepopulated advanced search form" do
52
+ before do
53
+ visit '/catalog/advanced?op=must&clause[0][field]=title&clause[0]query=medicine'
54
+ end
55
+
56
+ it "does not create hidden inputs for search fields" do
57
+ expect(page).to have_field 'Title', with: 'medicine'
58
+ end
59
+
60
+ it "does not have multiple parameters for a search field" do
61
+ fill_in 'Title', with: 'bread'
62
+ click_on 'advanced-search-submit'
63
+ expect(page.current_url).to match(/bread/)
64
+ expect(page.current_url).not_to match(/medicine/)
65
+ end
66
+ end
67
+ end
@@ -9,7 +9,7 @@ RSpec.describe Blacklight::Configuration::ViewConfig do
9
9
  describe '#display_label' do
10
10
  it "looks up the label to display for the given document and field" do
11
11
  allow(I18n).to receive(:t).with(:"blacklight.search.view_title.my_view", default: [:"blacklight.search.view.my_view", label, nil, "My view"]).and_return('x')
12
- expect(subject.display_label(key)).to eq 'x'
12
+ expect(subject.display_label).to eq 'x'
13
13
  end
14
14
  end
15
15
  end
@@ -123,6 +123,18 @@ RSpec.describe Blacklight::OpenStructWithHashAccess do
123
123
  end
124
124
  end
125
125
 
126
+ describe '#reverse_merge' do
127
+ before do
128
+ @h = described_class.new
129
+ @h[:a] = 1
130
+ @h[:b] = 2
131
+ end
132
+
133
+ it 'reverse merges the object with the hash, preserving the object class' do
134
+ expect(@h.reverse_merge(b: 3, c: 4)).to have_attributes a: 1, b: 2, c: 4
135
+ end
136
+ end
137
+
126
138
  describe "#to_json" do
127
139
  subject { described_class.new a: 1, b: 2 }
128
140
 
@@ -145,4 +157,12 @@ RSpec.describe Blacklight::OpenStructWithHashAccess do
145
157
  expect(copy.b[:c]).to eq 2
146
158
  end
147
159
  end
160
+
161
+ describe "#try" do
162
+ subject { described_class.new a: 1 }
163
+
164
+ it "works (and doesn't throw a stack error...)" do
165
+ expect(subject.try(:a)).to eq 1
166
+ end
167
+ end
148
168
  end
@@ -72,6 +72,33 @@ RSpec.describe Blacklight::SearchState::FilterField do
72
72
  expect(new_state.filter('some_field').values).to eq %w[1 2 4]
73
73
  end
74
74
  end
75
+
76
+ context 'with an array' do
77
+ let(:params) do
78
+ { f: { another_field: ['3'] }, f_inclusive: { some_field: %w[a b c] } }
79
+ end
80
+
81
+ it 'creates a new group with the new values' do
82
+ filter = search_state.filter('new_field')
83
+ new_state = filter.add(%w[x y z])
84
+
85
+ expect(new_state.filter('new_field').values).to eq [%w[x y z]]
86
+ end
87
+
88
+ it 'updates any existing groups with the new values' do
89
+ filter = search_state.filter('some_field')
90
+ new_state = filter.add(%w[x y z])
91
+
92
+ expect(new_state.filter('some_field').values).to eq [%w[x y z]]
93
+ end
94
+
95
+ it 'leaves existing filters alone' do
96
+ filter = search_state.filter('another_field')
97
+ new_state = filter.add(%w[x y z])
98
+
99
+ expect(new_state.filter('another_field').values).to eq ['3', %w[x y z]]
100
+ end
101
+ end
75
102
  end
76
103
 
77
104
  describe '#remove' do
@@ -104,12 +131,50 @@ RSpec.describe Blacklight::SearchState::FilterField do
104
131
 
105
132
  expect(new_state.filter('some_field').values).to eq ['2']
106
133
  end
134
+
135
+ context 'with an array' do
136
+ let(:params) do
137
+ { f: { another_field: ['3'] }, f_inclusive: { some_field: %w[a b c], another_field: %w[x y z] } }
138
+ end
139
+
140
+ it 'removes groups of values' do
141
+ filter = search_state.filter('some_field')
142
+ new_state = filter.remove(%w[a b c])
143
+
144
+ expect(new_state.params[:f_inclusive]).not_to include :some_field
145
+ expect(new_state.filter('some_field').values).to eq []
146
+ end
147
+
148
+ it 'can remove single values' do
149
+ filter = search_state.filter('some_field')
150
+ new_state = filter.remove(%w[a])
151
+
152
+ expect(new_state.filter('some_field').values).to eq [%w[b c]]
153
+ end
154
+
155
+ it 'leaves existing filters alone' do
156
+ filter = search_state.filter('another_field')
157
+ new_state = filter.remove(%w[x y z])
158
+
159
+ expect(new_state.filter('another_field').values).to eq ['3']
160
+ end
161
+ end
107
162
  end
108
163
 
109
164
  describe '#values' do
110
165
  it 'returns the currently selected values of the filter' do
111
166
  expect(search_state.filter('some_field').values).to eq %w[1 2]
112
167
  end
168
+
169
+ context 'with an array' do
170
+ let(:params) do
171
+ { f: { some_field: ['3'] }, f_inclusive: { some_field: %w[a b c] } }
172
+ end
173
+
174
+ it 'combines the exclusive and inclusive values' do
175
+ expect(search_state.filter('some_field').values).to eq ['3', %w[a b c]]
176
+ end
177
+ end
113
178
  end
114
179
 
115
180
  describe '#include?' do