blacklight 7.41.0 → 8.0.0.beta1

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 (425) hide show
  1. checksums.yaml +4 -4
  2. data/.env +2 -3
  3. data/.github/workflows/ruby.yml +81 -0
  4. data/.rubocop.yml +243 -14
  5. data/.rubocop_todo.yml +137 -429
  6. data/Gemfile +11 -6
  7. data/README.md +11 -3
  8. data/VERSION +1 -1
  9. data/app/assets/javascripts/blacklight/blacklight.esm.js +384 -0
  10. data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -0
  11. data/app/assets/javascripts/blacklight/blacklight.js +374 -493
  12. data/app/assets/javascripts/blacklight/blacklight.js.map +1 -0
  13. data/app/assets/stylesheets/blacklight/_autocomplete.scss +25 -0
  14. data/app/assets/stylesheets/blacklight/_blacklight_base.scss +1 -1
  15. data/app/assets/stylesheets/blacklight/_bookmark.scss +16 -0
  16. data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +8 -0
  17. data/app/assets/stylesheets/blacklight/_constraints.scss +4 -4
  18. data/app/assets/stylesheets/blacklight/_facets.scss +72 -44
  19. data/app/assets/stylesheets/blacklight/_header.scss +0 -15
  20. data/app/assets/stylesheets/blacklight/_icons.scss +0 -14
  21. data/app/assets/stylesheets/blacklight/_mixins.scss +20 -0
  22. data/app/assets/stylesheets/blacklight/_modal.scss +8 -2
  23. data/app/assets/stylesheets/blacklight/_search_form.scss +30 -3
  24. data/app/assets/stylesheets/blacklight/_search_history.scss +10 -1
  25. data/app/assets/stylesheets/blacklight/_search_results.scss +6 -2
  26. data/app/assets/stylesheets/blacklight/blacklight_defaults.scss +4 -1
  27. data/app/builders/blacklight/action_builder.rb +18 -9
  28. data/app/components/blacklight/advanced_search_form_component.html.erb +3 -3
  29. data/app/components/blacklight/advanced_search_form_component.rb +8 -10
  30. data/app/components/blacklight/constraints_component.rb +27 -37
  31. data/app/components/blacklight/document/action_component.rb +12 -10
  32. data/app/components/blacklight/document/actions_component.rb +2 -2
  33. data/app/components/blacklight/document/bookmark_component.html.erb +9 -0
  34. data/app/components/blacklight/document/bookmark_component.rb +4 -2
  35. data/app/components/blacklight/document/citation_component.rb +5 -3
  36. data/app/components/blacklight/document/group_component.rb +7 -3
  37. data/app/components/blacklight/document/sidebar_component.html.erb +2 -0
  38. data/app/components/blacklight/document/sidebar_component.rb +16 -0
  39. data/app/components/blacklight/document/thumbnail_component.html.erb +2 -7
  40. data/app/components/blacklight/document/thumbnail_component.rb +1 -9
  41. data/app/components/blacklight/document_component.html.erb +1 -1
  42. data/app/components/blacklight/document_component.rb +16 -78
  43. data/app/components/blacklight/document_metadata_component.html.erb +2 -4
  44. data/app/components/blacklight/document_metadata_component.rb +5 -10
  45. data/app/components/blacklight/facet_component.rb +3 -3
  46. data/app/components/blacklight/facet_field_checkboxes_component.html.erb +4 -4
  47. data/app/components/blacklight/facet_field_checkboxes_component.rb +1 -1
  48. data/app/components/blacklight/facet_field_component.html.erb +4 -5
  49. data/app/components/blacklight/facet_field_component.rb +9 -2
  50. data/app/components/blacklight/facet_field_list_component.html.erb +3 -3
  51. data/app/components/blacklight/facet_field_list_component.rb +37 -5
  52. data/app/components/blacklight/facet_field_no_layout_component.rb +0 -2
  53. data/app/components/blacklight/facet_item_component.rb +1 -43
  54. data/app/components/blacklight/facet_item_pivot_component.rb +21 -23
  55. data/app/components/blacklight/header_component.rb +1 -1
  56. data/app/components/blacklight/hidden_search_state_component.rb +1 -2
  57. data/app/components/blacklight/icons/icon_component.rb +4 -9
  58. data/app/components/blacklight/icons/legacy_icon_component.rb +30 -0
  59. data/app/components/blacklight/icons/list_component.rb +16 -0
  60. data/app/components/blacklight/icons/search_component.rb +16 -0
  61. data/app/components/blacklight/metadata_field_component.html.erb +2 -2
  62. data/app/components/blacklight/metadata_field_component.rb +24 -8
  63. data/app/components/blacklight/metadata_field_layout_component.rb +4 -25
  64. data/app/components/blacklight/response/facet_group_component.html.erb +3 -5
  65. data/app/components/blacklight/response/facet_group_component.rb +29 -8
  66. data/app/components/blacklight/response/pagination_component.html.erb +1 -1
  67. data/app/components/blacklight/response/pagination_component.rb +2 -11
  68. data/app/components/blacklight/response/sort_component.html.erb +6 -1
  69. data/app/components/blacklight/response/sort_component.rb +1 -16
  70. data/app/components/blacklight/response/spellcheck_component.rb +18 -7
  71. data/app/components/blacklight/response/view_type_button_component.rb +3 -7
  72. data/app/components/blacklight/response/view_type_component.rb +4 -6
  73. data/app/components/blacklight/search/sidebar_component.html.erb +8 -0
  74. data/app/components/blacklight/search/sidebar_component.rb +17 -0
  75. data/app/components/blacklight/search_bar_component.html.erb +20 -15
  76. data/app/components/blacklight/search_bar_component.rb +2 -16
  77. data/app/components/blacklight/search_button_component.rb +3 -3
  78. data/app/components/blacklight/search_context_component.rb +43 -10
  79. data/app/components/blacklight/search_header_component.html.erb +2 -0
  80. data/app/components/blacklight/search_header_component.rb +6 -0
  81. data/app/components/blacklight/start_over_button_component.rb +5 -3
  82. data/app/components/blacklight/system/dropdown_component.rb +8 -5
  83. data/app/components/blacklight/system/flash_message_component.html.erb +4 -2
  84. data/app/components/blacklight/system/flash_message_component.rb +12 -3
  85. data/app/components/blacklight/system/modal_component.html.erb +1 -1
  86. data/app/components/blacklight/system/modal_component.rb +1 -3
  87. data/app/components/blacklight/top_navbar_component.html.erb +1 -1
  88. data/app/components/blacklight/top_navbar_component.rb +0 -4
  89. data/app/controllers/bookmarks_controller.rb +1 -0
  90. data/app/controllers/catalog_controller.rb +1 -0
  91. data/app/controllers/concerns/blacklight/bookmarks.rb +10 -9
  92. data/app/controllers/concerns/blacklight/catalog.rb +21 -83
  93. data/app/controllers/concerns/blacklight/controller.rb +3 -41
  94. data/app/controllers/concerns/blacklight/search_context.rb +25 -7
  95. data/app/controllers/concerns/blacklight/search_history.rb +2 -0
  96. data/app/controllers/concerns/blacklight/searchable.rb +12 -1
  97. data/app/controllers/concerns/blacklight/token_based_user.rb +13 -1
  98. data/app/controllers/search_history_controller.rb +1 -0
  99. data/app/helpers/blacklight/blacklight_helper_behavior.rb +12 -310
  100. data/app/helpers/blacklight/catalog_helper_behavior.rb +22 -139
  101. data/app/helpers/blacklight/component_helper_behavior.rb +2 -53
  102. data/app/helpers/blacklight/configuration_helper_behavior.rb +2 -119
  103. data/app/helpers/blacklight/facets_helper_behavior.rb +4 -321
  104. data/app/helpers/blacklight/icon_helper_behavior.rb +5 -7
  105. data/app/helpers/blacklight/layout_helper_behavior.rb +4 -3
  106. data/app/helpers/blacklight/render_partials_helper_behavior.rb +11 -31
  107. data/app/helpers/blacklight/url_helper_behavior.rb +12 -97
  108. data/app/helpers/blacklight_helper.rb +1 -0
  109. data/app/helpers/catalog_helper.rb +1 -0
  110. data/app/javascript/blacklight/bookmark_toggle.js +13 -19
  111. data/app/javascript/blacklight/button_focus.js +12 -10
  112. data/app/javascript/blacklight/checkbox_submit.js +68 -123
  113. data/app/javascript/blacklight/core.js +5 -7
  114. data/app/javascript/blacklight/index.js +13 -0
  115. data/app/javascript/blacklight/modal.js +99 -164
  116. data/app/javascript/blacklight/modalForm.js +60 -0
  117. data/app/javascript/blacklight/search_context.js +46 -54
  118. data/app/models/blacklight/facet_paginator.rb +3 -2
  119. data/app/models/blacklight/icon.rb +4 -2
  120. data/app/models/bookmark.rb +0 -2
  121. data/app/models/concerns/blacklight/configurable.rb +5 -4
  122. data/app/models/concerns/blacklight/document/active_model_shim.rb +1 -10
  123. data/app/models/concerns/blacklight/document/cache_key.rb +1 -0
  124. data/app/models/concerns/blacklight/document/dublin_core.rb +2 -1
  125. data/app/models/concerns/blacklight/document/email.rb +1 -0
  126. data/app/models/concerns/blacklight/document/export.rb +2 -1
  127. data/app/models/concerns/blacklight/document/extensions.rb +1 -0
  128. data/app/models/concerns/blacklight/document/schema_org.rb +1 -0
  129. data/app/models/concerns/blacklight/document/semantic_fields.rb +2 -1
  130. data/app/models/concerns/blacklight/document/sms.rb +1 -0
  131. data/app/models/concerns/blacklight/suggest/response.rb +1 -0
  132. data/app/models/concerns/blacklight/user.rb +17 -8
  133. data/app/models/record_mailer.rb +13 -12
  134. data/app/models/search.rb +1 -7
  135. data/app/models/solr_document.rb +1 -0
  136. data/app/presenters/blacklight/clause_presenter.rb +1 -1
  137. data/app/presenters/blacklight/document_presenter.rb +23 -50
  138. data/app/presenters/blacklight/facet_field_presenter.rb +39 -14
  139. data/app/presenters/blacklight/facet_grouped_item_presenter.rb +1 -5
  140. data/app/presenters/blacklight/facet_item_pivot_presenter.rb +60 -0
  141. data/app/presenters/blacklight/facet_item_presenter.rb +3 -9
  142. data/app/presenters/blacklight/field_presenter.rb +1 -0
  143. data/app/presenters/blacklight/index_presenter.rb +2 -40
  144. data/app/presenters/blacklight/json_presenter.rb +10 -6
  145. data/app/presenters/blacklight/rendering/link_to_facet.rb +2 -5
  146. data/app/presenters/blacklight/show_presenter.rb +1 -9
  147. data/app/presenters/blacklight/thumbnail_presenter.rb +1 -1
  148. data/app/services/blacklight/bookmarks_search_builder.rb +22 -0
  149. data/app/services/blacklight/field_retriever.rb +12 -21
  150. data/app/services/blacklight/search_service.rb +10 -17
  151. data/app/values/blacklight/types.rb +0 -18
  152. data/app/views/bookmarks/_clear_bookmarks_widget.html.erb +8 -1
  153. data/app/views/bookmarks/_tools.html.erb +7 -12
  154. data/app/views/catalog/_advanced_search_form.html.erb +0 -1
  155. data/app/views/catalog/_bookmark_control.html.erb +1 -1
  156. data/app/views/catalog/_citation.html.erb +1 -1
  157. data/app/views/catalog/_constraints.html.erb +1 -14
  158. data/app/views/catalog/_document.atom.builder +12 -14
  159. data/app/views/catalog/_document.html.erb +5 -3
  160. data/app/views/catalog/_document.rss.builder +2 -4
  161. data/app/views/catalog/_facet_layout.html.erb +2 -2
  162. data/app/views/catalog/_facets.html.erb +5 -4
  163. data/app/views/catalog/_home_text.html.erb +2 -14
  164. data/app/views/catalog/_per_page_widget.html.erb +10 -1
  165. data/app/views/catalog/_search_form.html.erb +2 -2
  166. data/app/views/catalog/_search_header.html.erb +1 -2
  167. data/app/views/catalog/_search_results.html.erb +2 -2
  168. data/app/views/catalog/_search_sidebar.html.erb +5 -1
  169. data/app/views/catalog/_show_main_content.html.erb +11 -16
  170. data/app/views/catalog/_show_sidebar.html.erb +2 -2
  171. data/app/views/catalog/_show_tools.html.erb +8 -14
  172. data/app/views/catalog/_view_type_group.html.erb +1 -1
  173. data/app/views/catalog/email.html.erb +2 -2
  174. data/app/views/catalog/email_success.html.erb +5 -6
  175. data/app/views/catalog/facet.html.erb +7 -5
  176. data/app/views/catalog/index.atom.builder +12 -14
  177. data/app/views/catalog/index.html.erb +4 -1
  178. data/app/views/catalog/index.json.jbuilder +19 -19
  179. data/app/views/catalog/index.rss.builder +1 -1
  180. data/app/views/catalog/opensearch.xml.builder +1 -1
  181. data/app/views/catalog/sms.html.erb +2 -2
  182. data/app/views/catalog/sms_success.html.erb +5 -6
  183. data/app/views/catalog/suggest.html.erb +3 -0
  184. data/app/views/kaminari/blacklight/_page.html.erb +2 -1
  185. data/app/views/layouts/blacklight/base.html.erb +13 -2
  186. data/app/views/search_history/index.html.erb +6 -2
  187. data/app/views/shared/_flash_messages.html.erb +1 -1
  188. data/app/views/shared/_modal.html.erb +3 -3
  189. data/blacklight.gemspec +7 -11
  190. data/config/importmap.rb +3 -0
  191. data/config/locales/blacklight.ar.yml +0 -1
  192. data/config/locales/blacklight.ca.yml +0 -1
  193. data/config/locales/blacklight.de.yml +0 -1
  194. data/config/locales/blacklight.en.yml +0 -2
  195. data/config/locales/blacklight.es.yml +0 -1
  196. data/config/locales/blacklight.fr.yml +0 -1
  197. data/config/locales/blacklight.hu.yml +0 -1
  198. data/config/locales/blacklight.it.yml +0 -1
  199. data/config/locales/blacklight.nl.yml +0 -1
  200. data/config/locales/blacklight.pt-BR.yml +0 -1
  201. data/config/locales/blacklight.sq.yml +0 -1
  202. data/config/locales/blacklight.zh.yml +0 -1
  203. data/config/routes.rb +3 -2
  204. data/db/migrate/20140202020201_create_searches.rb +1 -0
  205. data/db/migrate/20140202020202_create_bookmarks.rb +1 -0
  206. data/db/migrate/20140320000000_add_polymorphic_type_to_bookmarks.rb +1 -0
  207. data/docker-compose.yml +3 -3
  208. data/lib/blacklight/abstract_repository.rb +1 -6
  209. data/lib/blacklight/component.rb +47 -10
  210. data/lib/blacklight/configuration/context.rb +4 -4
  211. data/lib/blacklight/configuration/display_field.rb +7 -9
  212. data/lib/blacklight/configuration/facet_field.rb +17 -11
  213. data/lib/blacklight/configuration/field.rb +1 -0
  214. data/lib/blacklight/configuration/fields.rb +12 -15
  215. data/lib/blacklight/configuration/index_field.rb +1 -0
  216. data/lib/blacklight/configuration/null_display_field.rb +17 -0
  217. data/lib/blacklight/configuration/search_field.rb +1 -0
  218. data/lib/blacklight/configuration/show_field.rb +1 -0
  219. data/lib/blacklight/configuration/sort_field.rb +1 -0
  220. data/lib/blacklight/configuration/tool_config.rb +1 -0
  221. data/lib/blacklight/configuration/view_config.rb +14 -10
  222. data/lib/blacklight/configuration.rb +310 -365
  223. data/lib/blacklight/engine.rb +8 -24
  224. data/lib/blacklight/exceptions.rb +2 -2
  225. data/lib/blacklight/nested_open_struct_with_hash_access.rb +7 -13
  226. data/lib/blacklight/open_struct_with_hash_access.rb +23 -6
  227. data/lib/blacklight/parameters.rb +7 -21
  228. data/lib/blacklight/routes/exportable.rb +1 -0
  229. data/lib/blacklight/routes/searchable.rb +2 -1
  230. data/lib/blacklight/routes.rb +1 -0
  231. data/lib/blacklight/search_builder.rb +10 -10
  232. data/lib/blacklight/search_state/filter_field.rb +8 -25
  233. data/lib/blacklight/search_state/pivot_filter_field.rb +144 -0
  234. data/lib/blacklight/search_state.rb +23 -79
  235. data/lib/blacklight/solr/document.rb +1 -0
  236. data/lib/blacklight/solr/facet_paginator.rb +1 -0
  237. data/lib/blacklight/solr/repository.rb +4 -24
  238. data/lib/blacklight/solr/request.rb +1 -0
  239. data/lib/blacklight/solr/response/facets.rb +21 -5
  240. data/lib/blacklight/solr/response/group.rb +1 -0
  241. data/lib/blacklight/solr/response/group_response.rb +1 -0
  242. data/lib/blacklight/solr/response/more_like_this.rb +1 -0
  243. data/lib/blacklight/solr/response/pagination_methods.rb +4 -3
  244. data/lib/blacklight/solr/response/params.rb +5 -4
  245. data/lib/blacklight/solr/response/response.rb +1 -0
  246. data/lib/blacklight/solr/response/spelling.rb +1 -0
  247. data/lib/blacklight/solr/response.rb +16 -3
  248. data/lib/blacklight/solr/search_builder_behavior.rb +16 -35
  249. data/lib/blacklight/solr.rb +7 -0
  250. data/lib/blacklight/version.rb +1 -0
  251. data/lib/blacklight.rb +26 -14
  252. data/lib/generators/blacklight/assets/importmap_generator.rb +55 -0
  253. data/lib/generators/blacklight/assets/propshaft_generator.rb +25 -0
  254. data/lib/generators/blacklight/assets/sprockets_generator.rb +66 -0
  255. data/lib/generators/blacklight/assets_generator.rb +13 -86
  256. data/lib/generators/blacklight/controller_generator.rb +4 -3
  257. data/lib/generators/blacklight/document_generator.rb +1 -0
  258. data/lib/generators/blacklight/install_generator.rb +4 -3
  259. data/lib/generators/blacklight/models_generator.rb +1 -0
  260. data/lib/generators/blacklight/search_builder_generator.rb +1 -0
  261. data/lib/generators/blacklight/solr_generator.rb +1 -1
  262. data/lib/generators/blacklight/templates/catalog_controller.rb +34 -8
  263. data/lib/generators/blacklight/templates/solr/conf/solrconfig.xml +1 -70
  264. data/lib/generators/blacklight/test_support_generator.rb +5 -3
  265. data/lib/generators/blacklight/user_generator.rb +7 -9
  266. data/lib/railties/blacklight.rake +6 -7
  267. data/package.json +10 -13
  268. data/rollup.config.js +27 -0
  269. data/spec/components/blacklight/constraints_component_spec.rb +17 -13
  270. data/spec/components/blacklight/document/action_component_spec.rb +6 -1
  271. data/spec/components/blacklight/document_component_spec.rb +22 -131
  272. data/spec/components/blacklight/facet_component_spec.rb +3 -18
  273. data/spec/components/blacklight/facet_field_checkboxes_component_spec.rb +1 -2
  274. data/spec/components/blacklight/facet_field_list_component_spec.rb +7 -6
  275. data/spec/components/blacklight/facet_item_pivot_component_spec.rb +10 -9
  276. data/spec/components/blacklight/response/view_type_component_spec.rb +66 -0
  277. data/spec/components/blacklight/search_bar_component_spec.rb +1 -1
  278. data/spec/components/blacklight/search_context_component_spec.rb +17 -8
  279. data/spec/controllers/blacklight/catalog/component_configuration_spec.rb +1 -6
  280. data/spec/controllers/blacklight/{base_spec.rb → catalog_spec.rb} +2 -2
  281. data/spec/controllers/bookmarks_controller_spec.rb +2 -3
  282. data/spec/controllers/catalog_controller_spec.rb +13 -135
  283. data/spec/features/advanced_search_spec.rb +0 -56
  284. data/spec/features/autocomplete_spec.rb +1 -1
  285. data/spec/features/axe_spec.rb +1 -6
  286. data/spec/features/bookmarks_spec.rb +1 -1
  287. data/spec/features/facets_spec.rb +6 -4
  288. data/spec/features/search_context_spec.rb +5 -11
  289. data/spec/features/search_results_spec.rb +0 -33
  290. data/spec/features/sitelinks_search_box.rb +13 -0
  291. data/spec/helpers/blacklight/configuration_helper_behavior_spec.rb +2 -138
  292. data/spec/helpers/blacklight/facets_helper_behavior_spec.rb +0 -387
  293. data/spec/helpers/blacklight/icon_helper_behavior_spec.rb +8 -0
  294. data/spec/helpers/blacklight/layout_helper_behavior_spec.rb +3 -20
  295. data/spec/helpers/blacklight/render_partials_helper_behavior_spec.rb +5 -7
  296. data/spec/helpers/blacklight/url_helper_behavior_spec.rb +9 -131
  297. data/spec/helpers/blacklight_helper_spec.rb +8 -252
  298. data/spec/helpers/catalog_helper_spec.rb +7 -118
  299. data/spec/i18n_spec.rb +1 -0
  300. data/spec/integration/generators/blacklight/solr_generator_spec.rb +1 -1
  301. data/spec/lib/blacklight/component_spec.rb +27 -32
  302. data/spec/lib/blacklight/configuration/facet_field_spec.rb +27 -16
  303. data/spec/lib/blacklight/configuration/field_spec.rb +1 -1
  304. data/spec/lib/blacklight/configuration/view_config_spec.rb +1 -1
  305. data/spec/lib/blacklight/open_struct_with_hash_access_spec.rb +2 -2
  306. data/spec/lib/blacklight/parameters_spec.rb +1 -4
  307. data/spec/lib/blacklight/search_state/filter_field_spec.rb +4 -4
  308. data/spec/lib/blacklight/search_state/pivot_filter_field_spec.rb +117 -0
  309. data/spec/lib/blacklight/search_state_spec.rb +80 -198
  310. data/spec/lib/tasks/blacklight_task_spec.rb +1 -0
  311. data/spec/models/blacklight/configuration_spec.rb +17 -51
  312. data/spec/models/blacklight/document/active_model_shim_spec.rb +2 -2
  313. data/spec/models/blacklight/icon_spec.rb +31 -15
  314. data/spec/models/blacklight/search_builder_spec.rb +9 -9
  315. data/spec/models/blacklight/solr/document_spec.rb +3 -3
  316. data/spec/models/blacklight/solr/repository_spec.rb +0 -45
  317. data/spec/models/blacklight/solr/response/facets_spec.rb +27 -27
  318. data/spec/models/blacklight/solr/response/group_response_spec.rb +1 -0
  319. data/spec/models/blacklight/solr/response/group_spec.rb +1 -0
  320. data/spec/models/blacklight/solr/response_spec.rb +9 -2
  321. data/spec/models/blacklight/solr/search_builder_spec.rb +24 -44
  322. data/spec/models/blacklight/user_spec.rb +22 -0
  323. data/spec/models/solr_document_spec.rb +3 -9
  324. data/spec/presenters/blacklight/clause_presenter_spec.rb +1 -0
  325. data/spec/presenters/blacklight/document_presenter_spec.rb +2 -3
  326. data/spec/presenters/blacklight/facet_field_presenter_spec.rb +85 -12
  327. data/spec/presenters/blacklight/facet_grouped_item_presenter_spec.rb +1 -0
  328. data/spec/presenters/blacklight/facet_item_presenter_spec.rb +14 -13
  329. data/spec/presenters/blacklight/field_presenter_spec.rb +0 -14
  330. data/spec/presenters/blacklight/index_presenter_spec.rb +2 -5
  331. data/spec/presenters/blacklight/json_presenter_spec.rb +1 -0
  332. data/spec/presenters/blacklight/link_alternate_presenter_spec.rb +3 -2
  333. data/spec/presenters/blacklight/show_presenter_spec.rb +20 -30
  334. data/spec/presenters/thumbnail_presenter_spec.rb +1 -1
  335. data/spec/requests/load_suggestions_spec.rb +16 -0
  336. data/spec/routing/catalog_routing_spec.rb +2 -1
  337. data/spec/services/blacklight/search_service_spec.rb +39 -76
  338. data/spec/spec_helper.rb +8 -9
  339. data/spec/support/controller_level_helpers.rb +1 -2
  340. data/spec/support/features/search_helpers.rb +39 -0
  341. data/spec/support/features/session_helpers.rb +1 -0
  342. data/spec/support/features.rb +3 -0
  343. data/spec/support/view_component_capybara_test_helpers.rb +8 -0
  344. data/spec/test_app_templates/Gemfile.extra +1 -0
  345. data/spec/test_app_templates/lib/generators/test_app_generator.rb +9 -2
  346. data/spec/views/catalog/_document.html.erb_spec.rb +3 -34
  347. data/spec/views/catalog/_facet_index_navigation.html.erb_spec.rb +1 -1
  348. data/spec/views/catalog/_paginate_compact.html.erb_spec.rb +0 -2
  349. data/spec/views/catalog/_search_header.erb_spec.rb +1 -0
  350. data/spec/views/catalog/_show_sidebar.erb_spec.rb +1 -0
  351. data/spec/views/catalog/_show_tools.html.erb_spec.rb +5 -66
  352. data/spec/views/catalog/_view_type_group.html.erb_spec.rb +17 -9
  353. data/spec/views/catalog/email_success.html.erb_spec.rb +2 -2
  354. data/spec/views/catalog/facet.html.erb_spec.rb +6 -3
  355. data/spec/views/catalog/index.atom.builder_spec.rb +17 -11
  356. data/spec/views/catalog/index.html.erb_spec.rb +5 -6
  357. data/spec/views/catalog/index.json.jbuilder_spec.rb +2 -2
  358. data/spec/views/catalog/show.html.erb_spec.rb +3 -25
  359. data/spec/views/catalog/sms_success.html.erb_spec.rb +2 -2
  360. data/tasks/blacklight.rake +11 -9
  361. data/template.demo.rb +7 -7
  362. metadata +77 -189
  363. data/.babelrc +0 -11
  364. data/.github/matrix.json +0 -62
  365. data/.github/workflows/build.yml +0 -16
  366. data/.github/workflows/lint.yml +0 -23
  367. data/.github/workflows/main.yml +0 -23
  368. data/.github/workflows/test.yml +0 -53
  369. data/app/assets/images/blacklight/list.svg +0 -1
  370. data/app/assets/images/blacklight/search.svg +0 -1
  371. data/app/assets/stylesheets/blacklight/_twitter_typeahead.scss +0 -37
  372. data/app/components/blacklight/content_areas_shim.rb +0 -13
  373. data/app/components/blacklight/search/per_page_component.html.erb +0 -2
  374. data/app/components/blacklight/search/per_page_component.rb +0 -50
  375. data/app/components/blacklight/search_context/server_item_pagination_component.html.erb +0 -10
  376. data/app/components/blacklight/search_context/server_item_pagination_component.rb +0 -15
  377. data/app/components/blacklight/system/dropdown_button_component.rb +0 -18
  378. data/app/controllers/concerns/blacklight/base.rb +0 -12
  379. data/app/controllers/concerns/blacklight/default_component_configuration.rb +0 -64
  380. data/app/controllers/concerns/blacklight/facet.rb +0 -69
  381. data/app/controllers/concerns/blacklight/search_fields.rb +0 -46
  382. data/app/helpers/blacklight/hash_as_hidden_fields_helper_behavior.rb +0 -27
  383. data/app/helpers/blacklight/render_constraints_helper_behavior.rb +0 -188
  384. data/app/helpers/blacklight/search_history_constraints_helper_behavior.rb +0 -97
  385. data/app/helpers/blacklight/suggest_helper_behavior.rb +0 -13
  386. data/app/javascript/blacklight/autocomplete.js +0 -36
  387. data/app/javascript/blacklight/facet_load.js +0 -22
  388. data/app/presenters/blacklight/search_bar_presenter.rb +0 -47
  389. data/app/views/catalog/_constraints_element.html.erb +0 -14
  390. data/app/views/catalog/_document_action.html.erb +0 -5
  391. data/app/views/catalog/_facet_group.html.erb +0 -5
  392. data/app/views/catalog/_facet_limit.html.erb +0 -3
  393. data/app/views/catalog/_index.html.erb +0 -1
  394. data/app/views/catalog/_index_header.html.erb +0 -22
  395. data/app/views/catalog/_previous_next_doc.html.erb +0 -2
  396. data/app/views/catalog/_show.html.erb +0 -6
  397. data/app/views/catalog/_show_header.html.erb +0 -2
  398. data/app/views/catalog/_thumbnail.html.erb +0 -1
  399. data/lib/blacklight/deprecations/engine_configuration.rb +0 -66
  400. data/lib/blacklight/deprecations/search_state_normalization.rb +0 -52
  401. data/spec/components/blacklight/header_component_spec.rb +0 -20
  402. data/spec/components/blacklight/icons/icon_component_spec.rb +0 -42
  403. data/spec/components/blacklight/response/pagination_component_spec.rb +0 -53
  404. data/spec/components/blacklight/search_context/server_item_pagination_component_spec.rb +0 -35
  405. data/spec/controllers/blacklight/facet_spec.rb +0 -33
  406. data/spec/controllers/blacklight/search_fields_spec.rb +0 -62
  407. data/spec/features/citation_spec.rb +0 -10
  408. data/spec/features/sitelinks_search_box_spec.rb +0 -13
  409. data/spec/features/sms_spec.rb +0 -12
  410. data/spec/helpers/blacklight/hash_as_hidden_fields_behavior_spec.rb +0 -26
  411. data/spec/helpers/blacklight/render_constraints_helper_behavior_spec.rb +0 -92
  412. data/spec/helpers/blacklight/search_history_constraints_helper_behavior_spec.rb +0 -101
  413. data/spec/helpers/blacklight/suggest_helper_behavior_spec.rb +0 -48
  414. data/spec/lib/blacklight/engine_spec.rb +0 -41
  415. data/spec/presenters/blacklight/search_bar_presenter_spec.rb +0 -94
  416. data/spec/services/blacklight/field_retriever_spec.rb +0 -17
  417. data/spec/support/view_component_test_helpers.rb +0 -35
  418. data/spec/views/catalog/_constraints.html.erb_spec.rb +0 -33
  419. data/spec/views/catalog/_facet_group.html.erb_spec.rb +0 -84
  420. data/spec/views/catalog/_facets.html.erb_spec.rb +0 -15
  421. data/spec/views/catalog/_index.html.erb_spec.rb +0 -62
  422. data/spec/views/catalog/_index_header.html.erb_spec.rb +0 -35
  423. data/spec/views/catalog/_previous_next_doc.html.erb_spec.rb +0 -22
  424. data/spec/views/catalog/_show.html.erb_spec.rb +0 -62
  425. data/spec/views/catalog/_thumbnail.html.erb_spec.rb +0 -38
@@ -1,30 +1,11 @@
1
1
  # frozen_string_literal: true
2
- require 'blacklight/deprecations/engine_configuration'
2
+
3
3
  require 'view_component'
4
4
 
5
5
  module Blacklight
6
6
  class Engine < Rails::Engine
7
7
  engine_name "blacklight"
8
8
 
9
- config.before_configuration do
10
- # see https://github.com/fxn/zeitwerk#for_gem
11
- # Blacklight puts a generator into LOCAL APP lib/generators, so tell
12
- # zeitwerk to ignore the whole directory? If we're using zeitwerk
13
- #
14
- # https://github.com/cbeer/engine_cart/issues/117
15
- if Rails.try(:autoloaders).try(:main).respond_to?(:ignore)
16
- Rails.autoloaders.main.ignore(Rails.root.join('lib/generators'))
17
- end
18
- end
19
-
20
- # BlacklightHelper is needed by all helpers, so we inject it
21
- # into action view base here.
22
- initializer 'blacklight.helpers' do
23
- config.after_initialize do
24
- ActionView::Base.include BlacklightHelper
25
- end
26
- end
27
-
28
9
  config.after_initialize do
29
10
  Blacklight::Configuration.initialize_default_configuration
30
11
  end
@@ -39,7 +20,9 @@ module Blacklight
39
20
  end
40
21
 
41
22
  initializer "blacklight.assets.precompile" do
42
- PRECOMPILE_ASSETS = %w(favicon.ico blacklight/blacklight.js blacklight/blacklight.js.map).freeze
23
+ # rubocop:disable Lint/ConstantDefinitionInBlock
24
+ PRECOMPILE_ASSETS = %w(favicon.ico blacklight/blacklight.js blacklight/blacklight.js.map blacklight/blacklight.esm.js blacklight/blacklight.esm.js.map).freeze
25
+ # rubocop:enable Lint/ConstantDefinitionInBlock
43
26
 
44
27
  # When Rails has been generated in API mode, it does not have sprockets available
45
28
  if Rails.application.config.respond_to?(:assets)
@@ -47,6 +30,10 @@ module Blacklight
47
30
  end
48
31
  end
49
32
 
33
+ initializer "blacklight.importmap", before: "importmap" do |app|
34
+ app.config.importmap.paths << Engine.root.join("config/importmap.rb") if app.config.respond_to?(:importmap)
35
+ end
36
+
50
37
  bl_global_config = OpenStructWithHashAccess.new
51
38
 
52
39
  bl_global_config.sms_mappings = {
@@ -81,9 +68,6 @@ module Blacklight
81
68
  # Blacklight specific stuff in this single struct:
82
69
  Blacklight::Engine.config.blacklight = bl_global_config
83
70
 
84
- # Deprecate top-level access to legacy engine configuration
85
- Blacklight::Deprecations::EngineConfiguration.deprecate_in(Blacklight::Engine.config)
86
-
87
71
  config.action_dispatch.rescue_responses["Blacklight::Exceptions::RecordNotFound"] = :not_found
88
72
  end
89
73
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Blacklight
3
4
  module Exceptions
4
5
  class AccessDenied < StandardError
@@ -15,8 +16,7 @@ module Blacklight
15
16
 
16
17
  class ECONNREFUSED < ::Errno::ECONNREFUSED; end
17
18
 
18
- # NOTE: In Blacklight 8, the parent class will be Timeout::Error
19
- class RepositoryTimeout < InvalidRequest; end
19
+ class RepositoryTimeout < Timeout::Error; end
20
20
 
21
21
  class IconNotFound < StandardError
22
22
  end
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'ostruct'
3
4
  module Blacklight
4
5
  ##
5
6
  # An OpenStruct refinement that converts any hash-keys into
6
7
  # additional instances of NestedOpenStructWithHashAccess
7
8
  class NestedOpenStructWithHashAccess < OpenStructWithHashAccess
8
- extend Deprecation
9
- self.deprecation_horizon = 'blacklight 8.0'
10
-
11
9
  attr_reader :nested_class
12
10
 
13
11
  delegate :default_proc=, to: :to_h
@@ -66,8 +64,8 @@ module Blacklight
66
64
  self.class.new nested_class, @table.deep_dup
67
65
  end
68
66
 
69
- def select *args, &block
70
- self.class.new nested_class, to_h.select(*args, &block)
67
+ def select(...)
68
+ self.class.new nested_class, to_h.select(...)
71
69
  end
72
70
 
73
71
  ##
@@ -100,15 +98,11 @@ module Blacklight
100
98
  m = mid[0...-1].to_sym
101
99
  new_ostruct_member!(m)
102
100
  @table[m] = args.first
103
- elsif len.zero? && kwargs.blank? && !block
104
- Deprecation.warn(Blacklight::NestedOpenStructWithHashAccess,
105
- "Initializing a #{nested_class} implicitly (by calling #{mid}) is deprecated. Call it as #{mid}! or pass initialize arguments")
106
-
107
- new_ostruct_member!(mid)
108
- @table[mid]
109
- else
101
+ elsif len.positive? || kwargs.any? || block
110
102
  new_ostruct_member!(mid)
111
103
  @table[mid] = nested_class.new(key: mid, **(args.first || {}), **kwargs)
104
+ else
105
+ @table[mid]
112
106
  end
113
107
 
114
108
  block&.call(res)
@@ -120,7 +114,7 @@ module Blacklight
120
114
 
121
115
  def set_default_proc!
122
116
  self.default_proc = lambda do |hash, key|
123
- hash[key] = nested_class.new({ key: key })
117
+ hash[key] = nested_class.new(key: key)
124
118
  end
125
119
  end
126
120
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'ostruct'
3
4
  module Blacklight
4
5
  ##
@@ -15,16 +16,16 @@ module Blacklight
15
16
  @table
16
17
  end
17
18
 
18
- def select *args, &block
19
- self.class.new to_h.select(*args, &block)
19
+ def select(...)
20
+ self.class.new to_h.select(...)
20
21
  end
21
22
 
22
- def sort_by *args, &block
23
- self.class.new Hash[to_h.sort_by(*args, &block)]
23
+ def sort_by(...)
24
+ self.class.new to_h.sort_by(...).to_h
24
25
  end
25
26
 
26
- def sort_by! *args, &block
27
- replace Hash[to_h.sort_by(*args, &block)]
27
+ def sort_by!(...)
28
+ replace to_h.sort_by(...).to_h
28
29
  self
29
30
  end
30
31
 
@@ -51,5 +52,21 @@ module Blacklight
51
52
  def deep_dup
52
53
  self.class.new @table.deep_dup
53
54
  end
55
+
56
+ def deep_transform_values(&method)
57
+ self.class.new @table.deep_transform_values(&method)
58
+ end
59
+
60
+ def try(method_name = nil, *args, &block)
61
+ if method_name.nil? && block_given?
62
+ if b.arity.zero?
63
+ instance_eval(&block)
64
+ else
65
+ yield self
66
+ end
67
+ elsif respond_to?(method_name)
68
+ public_send(method_name, *args, &b)
69
+ end
70
+ end
54
71
  end
55
72
  end
@@ -1,17 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'hashdiff'
4
-
5
3
  module Blacklight
6
4
  class Parameters
7
- extend Deprecation
8
-
9
5
  ##
10
6
  # Sanitize the search parameters by removing unnecessary parameters
11
7
  # from the provided parameters.
12
8
  # @param [Hash] params parameters
13
9
  def self.sanitize params
14
- params.reject { |_k, v| v.nil? }
10
+ params.reject { |_k, v| v.nil? } # rubocop:disable Style/CollectionCompact not available in Rails 6.0
15
11
  .except(:action, :controller, :id, :commit, :utf8)
16
12
  end
17
13
 
@@ -30,7 +26,7 @@ module Blacklight
30
26
  complex_params_from_b, scalar_params_from_b = b.flatten.uniq.partition { |x| x.is_a? Hash }
31
27
  complex_params_from_b = complex_params_from_b.inject({}) { |tmp, h| _deep_merge_permitted_param_hashes(h, tmp) }
32
28
 
33
- (scalar_params_from_a + scalar_params_from_b + [_deep_merge_permitted_param_hashes(complex_params_from_a, complex_params_from_b)]).reject(&:blank?).uniq
29
+ (scalar_params_from_a + scalar_params_from_b + [_deep_merge_permitted_param_hashes(complex_params_from_a, complex_params_from_b)]).compact_blank.uniq
34
30
  end
35
31
 
36
32
  private_class_method def self._deep_merge_permitted_param_hashes(h1, h2)
@@ -74,33 +70,23 @@ module Blacklight
74
70
  if blacklight_config.filter_search_state_fields
75
71
  params.permit(*permitted_params)
76
72
  else
77
- warn_about_deprecated_parameter_handling(params, permitted_params)
78
73
  params.deep_dup.permit!
79
74
  end
80
75
  end
81
76
 
82
77
  private
83
78
 
84
- def warn_about_deprecated_parameter_handling(params, permitted_params)
85
- diff = Hashdiff.diff(params.to_unsafe_h, params.permit(*permitted_params).to_h)
86
- return if diff.empty?
87
-
88
- Deprecation.warn(Blacklight::Parameters, "Blacklight 8 will filter out non-search parameter, including: #{diff.map { |_op, key, *| key }.to_sentence}")
89
- end
90
-
91
79
  # Facebook's crawler turns array query parameters into a hash with numeric keys. Once we know
92
80
  # the expected parameter structure, we can unmangle those parameters to match our expected values.
93
81
  def deep_unmangle_params!(params, permitted_params)
94
82
  permitted_params.select { |p| p.is_a?(Hash) }.each do |permission|
95
83
  permission.each do |key, permitted_value|
96
- if params[key].is_a?(ActionController::Parameters) && permitted_value.is_a?(Hash)
84
+ next unless params[key].is_a?(ActionController::Parameters)
85
+
86
+ if permitted_value.is_a?(Hash)
97
87
  deep_unmangle_params!(params[key], [permitted_value])
98
- elsif permitted_value.is_a?(Array) && permitted_value.empty?
99
- if params[key].is_a?(ActionController::Parameters) && params[key]&.keys&.all? { |k| k.to_s =~ /\A\d+\z/ }
100
- params[key] = params[key].values
101
- elsif params[key].is_a?(String)
102
- params[key] = Array(params[key])
103
- end
88
+ elsif permitted_value.is_a?(Array) && permitted_value.empty? && params[key]&.keys&.all? { |k| k.to_s =~ /\A\d+\z/ }
89
+ params[key] = params[key].values
104
90
  end
105
91
  end
106
92
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Blacklight
3
4
  module Routes
4
5
  class Exportable
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Blacklight
3
4
  module Routes
4
5
  class Searchable
@@ -14,7 +15,7 @@ module Blacklight
14
15
  mapper.get ":id/raw", action: 'raw', as: 'raw', defaults: { format: 'json' }
15
16
 
16
17
  mapper.get "opensearch"
17
- mapper.get 'suggest', as: 'suggest_index', defaults: { format: 'json' }
18
+ mapper.get 'suggest', as: 'suggest_index'
18
19
  mapper.get "facet/:id", action: 'facet', as: 'facet'
19
20
  end
20
21
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Blacklight
3
4
  module Routes
4
5
  require 'blacklight/routes/exportable'
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Blacklight
3
4
  ##
4
5
  # Blacklight's SearchBuilder converts blacklight request parameters into
@@ -49,7 +50,6 @@ module Blacklight
49
50
  # @example
50
51
  # search_builder.where(id: [1,2,3]) # produces: q:"{!lucene}id:(1 OR 2 OR 3)"
51
52
  def where(conditions)
52
- Deprecation.warn(Blacklight::SearchBuilder, "SearchBuilder#where must be called with a hash, received #{conditions.inspect}.") unless conditions.is_a? Hash
53
53
  params_will_change!
54
54
  @search_state = @search_state.reset(@search_state.params.merge(q: conditions))
55
55
  @blacklight_params = @search_state.params
@@ -62,9 +62,9 @@ module Blacklight
62
62
  def append(*addl_processor_chain)
63
63
  params_will_change!
64
64
  builder = self.class.new(processor_chain + addl_processor_chain, scope)
65
- .with(search_state)
66
- .merge(@merged_params)
67
- .reverse_merge(@reverse_merged_params)
65
+ .with(search_state)
66
+ .merge(@merged_params)
67
+ .reverse_merge(@reverse_merged_params)
68
68
 
69
69
  builder.start = @start if @start
70
70
  builder.rows = @rows if @rows
@@ -82,9 +82,9 @@ module Blacklight
82
82
  # chain are ignored as no-ops, rather than raising.
83
83
  def except(*except_processor_chain)
84
84
  builder = self.class.new(processor_chain - except_processor_chain, scope)
85
- .with(search_state)
86
- .merge(@merged_params)
87
- .reverse_merge(@reverse_merged_params)
85
+ .with(search_state)
86
+ .merge(@merged_params)
87
+ .reverse_merge(@reverse_merged_params)
88
88
 
89
89
  builder.start = @start if @start
90
90
  builder.rows = @rows if @rows
@@ -121,9 +121,9 @@ module Blacklight
121
121
  return @params unless params_need_update?
122
122
 
123
123
  @params = processed_parameters
124
- .reverse_merge(@reverse_merged_params)
125
- .merge(@merged_params)
126
- .tap { clear_changes }
124
+ .reverse_merge(@reverse_merged_params)
125
+ .merge(@merged_params)
126
+ .tap { clear_changes }
127
127
  end
128
128
 
129
129
  alias_method :query, :to_hash
@@ -8,19 +8,13 @@ module Blacklight
8
8
 
9
9
  # @!attribute config
10
10
  # @return [Blacklight::Configuration::FacetField]
11
- attr_reader :config
12
-
13
11
  # @!attribute search_state
14
12
  # @return [Blacklight::SearchState]
15
- attr_reader :search_state
16
-
17
13
  # @!attribute param
18
14
  # @return [String,Symbol]
19
- attr_reader :filters_key
20
-
21
15
  # @!attribute inclusive_param
22
16
  # @return [String,Symbol]
23
- attr_reader :inclusive_filters_key
17
+ attr_reader :config, :search_state, :filters_key, :inclusive_filters_key
24
18
 
25
19
  # @return [String,Symbol]
26
20
  delegate :key, to: :config
@@ -47,9 +41,7 @@ module Blacklight
47
41
  end
48
42
  end
49
43
 
50
- if item.respond_to?(:field) && item.field != key
51
- return new_state.filter(item.field).add(item)
52
- end
44
+ return new_state.filter(item.field).add(item) if item.respond_to?(:field) && item.field != key
53
45
 
54
46
  url_key = key
55
47
  params = new_state.params
@@ -82,9 +74,8 @@ module Blacklight
82
74
  # @return [Blacklight::SearchState] new state
83
75
  def remove(item)
84
76
  new_state = search_state.reset_search
85
- if item.respond_to?(:field) && item.field != key
86
- return new_state.filter(item.field).remove(item)
87
- end
77
+
78
+ return new_state.filter(item.field).remove(item) if item.respond_to?(:field) && item.field != key
88
79
 
89
80
  url_key = config.key
90
81
  params = new_state.params
@@ -106,13 +97,6 @@ module Blacklight
106
97
  params[param][url_key] = (params[param][url_key] || []).dup
107
98
 
108
99
  collection = params[param][url_key]
109
- # collection should be an array, because we link to ?f[key][]=value,
110
- # however, Facebook (and maybe some other PHP tools) tranform that parameters
111
- # into ?f[key][0]=value, which Rails interprets as a Hash.
112
- if collection.is_a? Hash
113
- Deprecation.warn(self, 'Normalizing parameters in FilterField#remove is deprecated')
114
- collection = collection.values
115
- end
116
100
 
117
101
  params[param][url_key] = collection - Array(value)
118
102
  params[param].delete(url_key) if params[param][url_key].empty?
@@ -143,16 +127,15 @@ module Blacklight
143
127
  # @param [String,#value] item a filter to remove from the url
144
128
  # @return [Boolean] whether the provided filter is currently applied/selected
145
129
  def include?(item)
146
- if item.respond_to?(:field) && item.field != key
147
- return search_state.filter(item.field).selected?(item)
148
- end
130
+ return search_state.filter(item.field).selected?(item) if item.respond_to?(:field) && item.field != key
149
131
 
150
132
  value = as_url_parameter(item)
151
133
  params = search_state.params
152
134
 
153
- if value.is_a?(Array)
135
+ case value
136
+ when Array
154
137
  (params.dig(inclusive_filters_key, key) || []).to_set == value.to_set
155
- elsif value == Blacklight::SearchState::FilterField::MISSING
138
+ when Blacklight::SearchState::FilterField::MISSING
156
139
  (params.dig(filters_key, "-#{key}") || []).include?(Blacklight::Engine.config.blacklight.facet_missing_param)
157
140
  else
158
141
  (params.dig(filters_key, key) || []).include?(value)
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blacklight
4
+ class SearchState
5
+ # Modeling access to filter query parameters
6
+ class PivotFilterField < FilterField
7
+ STOP_VALUE = [:stop].freeze
8
+
9
+ # @return [Array]
10
+ delegate :pivot, to: :config
11
+
12
+ # @param [String,#value] item a filter item to add to the url
13
+ # @return [Blacklight::SearchState] new state
14
+ def add(item)
15
+ item = wrap_item(item)
16
+ new_state = search_state.reset_search
17
+ return new_state if include?(item)
18
+
19
+ pivot_values = pivot_fq(item).merge(pivot[0].to_sym => as_url_parameter(item))
20
+ pivot_values.inject(new_state) do |memo_state, entry|
21
+ url_key, value = entry
22
+ field_facade = FilterField.new(null_field(url_key), memo_state)
23
+ next memo_state if value.nil? || field_facade.include?(value)
24
+
25
+ field_facade.add(value)
26
+ end
27
+ end
28
+
29
+ # @param [String,#value] item a filter to remove from the url
30
+ # @return [Blacklight::SearchState] new state
31
+ def remove(item)
32
+ item = wrap_item(item)
33
+ new_state = search_state.reset_search
34
+ pivot_values = pivot_fq(item).merge(pivot[0].to_sym => as_url_parameter(item))
35
+ pivot_values.inject(new_state) do |memo_state, entry|
36
+ url_key, value = entry
37
+ next memo_state if value.nil?
38
+
39
+ FilterField.new(null_field(url_key), memo_state).remove(value)
40
+ end
41
+ end
42
+
43
+ # Matrix the values of the pivoted fields
44
+ # @return [Array] an array of applied filters
45
+ # rubocop:disable Lint/UnusedMethodArgument
46
+ def values(except: [])
47
+ return nil unless pivot.is_a?(Array) && pivot.present?
48
+
49
+ params = search_state.params
50
+ # values should have at most one terminal blank pivot
51
+ pivot_values = pivot.map { |k| Array(params.dig(:f, k)) || STOP_VALUE }
52
+ pivot_values = pivot_values[0..(pivot_values.index(STOP_VALUE) || -1)]
53
+ # put an explicit nil in for the matrix
54
+ pivot_values[-1] = [nil] if pivot_values.last == STOP_VALUE
55
+ top_level_values = pivot_values.shift
56
+ return [] if top_level_values.first.blank?
57
+
58
+ pivot_values.each { |pivot_value| pivot_value[0] ||= nil }
59
+ matrix_values = top_level_values.product(*pivot_values)
60
+ matrix_values.map do |vals|
61
+ PivotValue.new(value: vals.shift, fq: pivot[1..].map(&:to_sym).zip(vals).to_h)
62
+ end
63
+ end
64
+ # rubocop:enable Lint/UnusedMethodArgument
65
+
66
+ # @param [String, #value #fq] item a filter may represent in the url
67
+ # @return [Boolean] whether the provided filter is currently applied/selected
68
+ def include?(item)
69
+ return false unless pivot.is_a?(Array) && pivot.present?
70
+
71
+ params = search_state.params
72
+ item = wrap_item(item)
73
+ pivot_values = pivot_fq(item).merge(pivot[0].to_sym => as_url_parameter(item))
74
+ pivot_values.inject(true) do |m, entry|
75
+ k, v = entry
76
+ m && params.dig(:f, k)&.include?(as_url_parameter(v))
77
+ end
78
+ end
79
+
80
+ class PivotValue
81
+ attr_accessor :value, :fq
82
+
83
+ delegate :blank?, :present?, to: :value
84
+ alias empty? blank?
85
+
86
+ def initialize(value: nil, fq: {}, **_args) # rubocop:disable Naming/MethodParameterName
87
+ @value = value
88
+ @fq = fq
89
+ end
90
+ end
91
+
92
+ class QueryBuilder
93
+ # @return [Array] filter_query, subqueries
94
+ def self.call(search_builder, filter, solr_parameters)
95
+ existing = solr_parameters['fq']&.dup || []
96
+ queries = []
97
+ filter.values.compact_blank.each do |value|
98
+ queries << search_builder.send(:facet_value_to_fq_string, filter.pivot.first, value.value)
99
+ value.fq.each do |entry|
100
+ k, v = entry
101
+ queries << search_builder.send(:facet_value_to_fq_string, k, v) if v
102
+ end
103
+ queries.uniq!
104
+ end
105
+ [(queries - existing)]
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ def null_field(key)
112
+ Blacklight::Configuration::NullField.new(key: key)
113
+ end
114
+
115
+ def pivot_fq(item = nil)
116
+ fq_keys = pivot[1..].map(&:to_sym)
117
+ null_values = fq_keys.index_with(nil)
118
+ return null_values unless item.respond_to?(:fq)
119
+
120
+ item_fq = item.fq.to_h.symbolize_keys.slice(*fq_keys)
121
+ null_values.merge item_fq
122
+ end
123
+
124
+ def wrap_item(item)
125
+ item = invert_item(item) if item.respond_to?(:field) && item.field.to_sym != pivot.first.to_sym
126
+ return item if item.respond_to? :value
127
+ return PivotValue.new(**item) if item.is_a? Hash
128
+
129
+ PivotValue.new(value: item, fq: pivot_fq)
130
+ end
131
+
132
+ # the parsed Blacklight::Solr::Response::Facets::FacetItem objects are inverted -
133
+ # the subordinate value is at the top, and the other values are in fq - to permit
134
+ # easier value labeling in the UI. To manage the search links, they need to be
135
+ # reinverted.
136
+ def invert_item(item)
137
+ item_fq = item.fq.symbolize_keys
138
+ item_value = item_fq.delete(pivot[0].to_sym)
139
+ item_fq = item_fq.merge(item.field.to_sym => item.value).slice(*pivot.map(&:to_sym))
140
+ PivotValue.new(value: item_value, fq: item_fq)
141
+ end
142
+ end
143
+ end
144
+ end