blacklight 9.0.0.beta1 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. checksums.yaml +4 -4
  2. data/.docker/app/Dockerfile +2 -1
  3. data/.github/matrix.json +55 -0
  4. data/.github/workflows/build.yml +16 -0
  5. data/.github/workflows/lint.yml +25 -0
  6. data/.github/workflows/main.yml +22 -0
  7. data/.github/workflows/release_7_x_scheduled.yml +39 -0
  8. data/.github/workflows/release_8_x_scheduled.yml +39 -0
  9. data/.github/workflows/test.yml +53 -0
  10. data/.rubocop.yml +81 -3
  11. data/.rubocop_todo.yml +46 -71
  12. data/.solr_wrapper.yml +3 -1
  13. data/README.md +2 -2
  14. data/VERSION +1 -1
  15. data/app/assets/builds/blacklight.css +51 -38
  16. data/app/assets/javascripts/blacklight/blacklight.esm.js +62 -77
  17. data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -1
  18. data/app/assets/javascripts/blacklight/blacklight.js +62 -77
  19. data/app/assets/javascripts/blacklight/blacklight.js.map +1 -1
  20. data/app/assets/stylesheets/blacklight/_blacklight_base.scss +0 -1
  21. data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +4 -0
  22. data/app/assets/stylesheets/blacklight/_facets.scss +37 -29
  23. data/app/assets/stylesheets/blacklight/_header.scss +4 -0
  24. data/app/assets/stylesheets/blacklight/_modal.scss +9 -8
  25. data/app/assets/stylesheets/blacklight/_pagination.scss +1 -3
  26. data/app/assets/stylesheets/blacklight/_search_history.scss +0 -4
  27. data/app/assets/stylesheets/blacklight/blacklight_defaults.scss +3 -0
  28. data/app/components/blacklight/advanced_search_form_component.html.erb +4 -4
  29. data/app/components/blacklight/advanced_search_form_component.rb +6 -5
  30. data/app/components/blacklight/constraint_component.html.erb +1 -5
  31. data/app/components/blacklight/constraint_component.rb +11 -0
  32. data/app/components/blacklight/constraint_layout_component.html.erb +1 -6
  33. data/app/components/blacklight/constraint_layout_component.rb +9 -0
  34. data/app/components/blacklight/constraints_component.html.erb +3 -8
  35. data/app/components/blacklight/constraints_component.rb +59 -14
  36. data/app/components/blacklight/document/action_component.rb +1 -3
  37. data/app/components/blacklight/document/bookmark_component.html.erb +3 -3
  38. data/app/components/blacklight/document/bookmark_component.rb +10 -4
  39. data/app/components/blacklight/document/group_component.html.erb +1 -1
  40. data/app/components/blacklight/document/more_like_this_component.rb +2 -2
  41. data/app/components/blacklight/document/page_header_component.rb +3 -3
  42. data/app/components/blacklight/document/thumbnail_component.html.erb +3 -7
  43. data/app/components/blacklight/document/thumbnail_component.rb +7 -6
  44. data/app/components/blacklight/document_component.html.erb +3 -3
  45. data/app/components/blacklight/document_component.rb +22 -26
  46. data/app/components/blacklight/document_title_component.rb +6 -10
  47. data/app/components/blacklight/facet_field_checkboxes_component.rb +4 -18
  48. data/app/components/blacklight/facet_field_component.rb +4 -15
  49. data/app/components/blacklight/facet_field_filter_component.rb +4 -19
  50. data/app/components/blacklight/facet_field_inclusive_constraint_component.rb +4 -23
  51. data/app/components/blacklight/facet_field_list_component.rb +4 -30
  52. data/app/components/blacklight/facet_field_no_layout_component.rb +4 -8
  53. data/app/components/blacklight/facet_field_pagination_component.html.erb +2 -2
  54. data/app/components/blacklight/facet_item_component.rb +4 -72
  55. data/app/components/blacklight/facet_item_pivot_component.rb +8 -4
  56. data/app/components/blacklight/{facet_field_checkboxes_component.html.erb → facets/checkboxes_component.html.erb} +4 -4
  57. data/app/components/blacklight/facets/checkboxes_component.rb +26 -0
  58. data/app/components/blacklight/facets/count_component.rb +23 -0
  59. data/app/components/blacklight/{facet_field_component.html.erb → facets/field_component.html.erb} +1 -3
  60. data/app/components/blacklight/facets/field_component.rb +23 -0
  61. data/app/components/blacklight/facets/filters_component.html.erb +4 -0
  62. data/app/components/blacklight/facets/filters_component.rb +39 -0
  63. data/app/components/blacklight/{facet_field_inclusive_constraint_component.html.erb → facets/inclusive_constraint_component.html.erb} +1 -1
  64. data/app/components/blacklight/facets/inclusive_constraint_component.rb +31 -0
  65. data/app/components/blacklight/{facet_field_filter_component.html.erb → facets/index_navigation_component.html.erb} +1 -1
  66. data/app/components/blacklight/facets/index_navigation_component.rb +32 -0
  67. data/app/components/blacklight/facets/item_component.rb +76 -0
  68. data/app/components/blacklight/facets/list_component.html.erb +11 -0
  69. data/app/components/blacklight/facets/list_component.rb +46 -0
  70. data/app/components/blacklight/facets/no_layout_component.rb +16 -0
  71. data/app/components/blacklight/facets/pivot_list_component.rb +14 -0
  72. data/app/components/blacklight/facets/selected_value_component.rb +29 -0
  73. data/app/components/blacklight/facets/suggest_component.html.erb +17 -0
  74. data/app/components/blacklight/facets/suggest_component.rb +21 -0
  75. data/app/components/blacklight/header_component.rb +2 -2
  76. data/app/components/blacklight/icons/remove_component.rb +2 -2
  77. data/app/components/blacklight/metadata_field_component.html.erb +2 -2
  78. data/app/components/blacklight/metadata_field_component.rb +2 -1
  79. data/app/components/blacklight/metadata_field_layout_component.rb +9 -4
  80. data/app/components/blacklight/metadata_field_plain_text_layout_component.rb +2 -2
  81. data/app/components/blacklight/response/facet_group_component.html.erb +3 -3
  82. data/app/components/blacklight/response/facet_group_component.rb +9 -1
  83. data/app/components/blacklight/search_bar_component.html.erb +2 -2
  84. data/app/components/blacklight/system/dropdown_component.html.erb +1 -1
  85. data/app/components/blacklight/system/dropdown_component.rb +1 -1
  86. data/app/components/blacklight/system/flash_message_component.html.erb +1 -1
  87. data/app/components/blacklight/system/flash_message_component.rb +0 -9
  88. data/app/components/blacklight/system/modal_component.html.erb +1 -2
  89. data/app/components/blacklight/top_navbar_component.html.erb +2 -2
  90. data/app/controllers/concerns/blacklight/bookmarks.rb +13 -3
  91. data/app/controllers/concerns/blacklight/catalog.rb +27 -30
  92. data/app/controllers/concerns/blacklight/controller.rb +1 -1
  93. data/app/controllers/concerns/blacklight/search_context.rb +1 -1
  94. data/app/controllers/concerns/blacklight/searchable.rb +1 -1
  95. data/app/helpers/blacklight/catalog_helper_behavior.rb +5 -3
  96. data/app/helpers/blacklight/configuration_helper_behavior.rb +2 -2
  97. data/app/helpers/blacklight/document_helper_behavior.rb +3 -1
  98. data/app/helpers/blacklight/facets_helper_behavior.rb +7 -12
  99. data/app/helpers/blacklight/icon_helper_behavior.rb +2 -2
  100. data/app/javascript/blacklight-frontend/bookmark_toggle.js +7 -2
  101. data/app/javascript/blacklight-frontend/button_focus.js +1 -1
  102. data/app/javascript/blacklight-frontend/checkbox_submit.js +9 -2
  103. data/app/javascript/blacklight-frontend/debounce.js +1 -1
  104. data/app/javascript/blacklight-frontend/facet_suggest.js +27 -3
  105. data/app/javascript/blacklight-frontend/index.js +0 -2
  106. data/app/javascript/blacklight-frontend/modal.js +15 -8
  107. data/app/javascript/blacklight-frontend/search_context.js +3 -2
  108. data/app/presenters/blacklight/constraint_presenter.rb +22 -0
  109. data/app/presenters/blacklight/document_presenter.rb +6 -5
  110. data/app/presenters/blacklight/facet_field_presenter.rb +18 -2
  111. data/app/presenters/blacklight/facet_grouped_item_presenter.rb +6 -0
  112. data/app/presenters/blacklight/facet_item_pivot_presenter.rb +1 -5
  113. data/app/presenters/blacklight/facet_item_presenter.rb +5 -18
  114. data/app/presenters/blacklight/field_presenter.rb +4 -2
  115. data/app/presenters/blacklight/json_presenter.rb +8 -4
  116. data/app/presenters/blacklight/rendering/abstract_step.rb +7 -1
  117. data/app/presenters/blacklight/rendering/helper_method.rb +4 -4
  118. data/app/presenters/blacklight/rendering/join.rb +11 -7
  119. data/app/presenters/blacklight/rendering/terminator.rb +1 -1
  120. data/app/services/blacklight/field_retriever.rb +1 -1
  121. data/app/services/blacklight/search_service.rb +10 -8
  122. data/app/values/blacklight/types.rb +2 -2
  123. data/app/views/blacklight/nav/_bookmark.html.erb +1 -1
  124. data/app/views/catalog/_document.atom.builder +1 -1
  125. data/app/views/catalog/_document.html.erb +1 -1
  126. data/app/views/catalog/_email_form.html.erb +4 -4
  127. data/app/views/catalog/_home_text.html.erb +2 -2
  128. data/app/views/catalog/_show_main_content.html.erb +9 -5
  129. data/app/views/catalog/_sms_form.html.erb +4 -4
  130. data/app/views/catalog/_sort_and_per_page.html.erb +1 -1
  131. data/app/views/catalog/advanced_search.html.erb +1 -1
  132. data/app/views/catalog/facet.html.erb +8 -10
  133. data/app/views/catalog/index.html.erb +0 -1
  134. data/app/views/catalog/index.json.jbuilder +5 -3
  135. data/app/views/catalog/show.html.erb +2 -2
  136. data/app/views/layouts/blacklight/base.html.erb +1 -1
  137. data/blacklight.gemspec +1 -1
  138. data/config/locales/blacklight.ar.yml +14 -14
  139. data/config/locales/blacklight.ca.yml +2 -2
  140. data/config/locales/blacklight.de.yml +13 -13
  141. data/config/locales/blacklight.en.yml +3 -3
  142. data/config/locales/blacklight.es.yml +13 -13
  143. data/config/locales/blacklight.fr.yml +14 -14
  144. data/config/locales/blacklight.hu.yml +15 -15
  145. data/config/locales/blacklight.it.yml +16 -16
  146. data/config/locales/blacklight.nl.yml +13 -13
  147. data/config/locales/blacklight.pt-BR.yml +15 -15
  148. data/config/locales/blacklight.sq.yml +14 -14
  149. data/config/locales/blacklight.zh.yml +12 -12
  150. data/lib/blacklight/abstract_repository.rb +2 -2
  151. data/lib/blacklight/component.rb +7 -1
  152. data/lib/blacklight/configuration/context.rb +3 -3
  153. data/lib/blacklight/configuration/facet_field.rb +11 -6
  154. data/lib/blacklight/configuration/field.rb +4 -4
  155. data/lib/blacklight/configuration/fields.rb +0 -1
  156. data/lib/blacklight/configuration/search_field.rb +1 -1
  157. data/lib/blacklight/configuration/view_config.rb +32 -18
  158. data/lib/blacklight/configuration.rb +64 -10
  159. data/lib/blacklight/nested_open_struct_with_hash_access.rb +1 -1
  160. data/lib/blacklight/open_struct_with_hash_access.rb +2 -2
  161. data/lib/blacklight/routes/searchable.rb +1 -1
  162. data/lib/blacklight/search_builder.rb +2 -1
  163. data/lib/blacklight/search_state/filter_field.rb +4 -4
  164. data/lib/blacklight/search_state/pivot_filter_field.rb +5 -5
  165. data/lib/blacklight/search_state.rb +2 -0
  166. data/lib/blacklight/solr/abstract_filter_query_builder.rb +77 -0
  167. data/lib/blacklight/solr/default_filter_query_builder.rb +20 -0
  168. data/lib/blacklight/solr/field_reflection_search_builder.rb +11 -0
  169. data/lib/blacklight/solr/repository.rb +17 -15
  170. data/lib/blacklight/solr/response/facets.rb +2 -2
  171. data/lib/blacklight/solr/response/params.rb +0 -4
  172. data/lib/blacklight/solr/response.rb +5 -1
  173. data/lib/blacklight/solr/search_builder_behavior.rb +48 -97
  174. data/lib/blacklight/solr/single_doc_search_builder.rb +25 -0
  175. data/lib/blacklight.rb +6 -4
  176. data/lib/generators/blacklight/assets/importmap_generator.rb +3 -5
  177. data/lib/generators/blacklight/assets_generator.rb +1 -1
  178. data/lib/generators/blacklight/search_builder_generator.rb +1 -1
  179. data/lib/generators/blacklight/templates/.solr_wrapper.yml +3 -1
  180. data/lib/generators/blacklight/templates/catalog_controller.rb +34 -8
  181. data/lib/generators/blacklight/templates/solr/conf/solrconfig.xml +0 -71
  182. data/package.json +3 -3
  183. data/spec/components/blacklight/advanced_search_form_component_spec.rb +18 -22
  184. data/spec/components/blacklight/constraint_layout_component_spec.rb +8 -8
  185. data/spec/components/blacklight/constraints_component_spec.rb +11 -11
  186. data/spec/components/blacklight/document/action_component_spec.rb +23 -15
  187. data/spec/components/blacklight/document/group_component_spec.rb +10 -15
  188. data/spec/components/blacklight/document/page_header_component_spec.rb +35 -28
  189. data/spec/components/blacklight/document/sidebar_component_spec.rb +5 -11
  190. data/spec/components/blacklight/document_component_spec.rb +105 -79
  191. data/spec/components/blacklight/facet_component_spec.rb +12 -8
  192. data/spec/components/blacklight/facet_item_pivot_component_spec.rb +12 -12
  193. data/spec/components/blacklight/{facet_field_checkboxes_component_spec.rb → facets/checkboxes_component_spec.rb} +13 -13
  194. data/spec/components/blacklight/facets/filters_component_spec.rb +36 -0
  195. data/spec/components/blacklight/facets/index_navigation_component_spec.rb +41 -0
  196. data/spec/components/blacklight/{facet_item_component_spec.rb → facets/item_component_spec.rb} +10 -10
  197. data/spec/components/blacklight/{facet_field_list_component_spec.rb → facets/list_component_spec.rb} +34 -23
  198. data/spec/components/blacklight/facets/pivot_list_component_spec.rb +53 -0
  199. data/spec/components/blacklight/facets/suggest_component_spec.rb +82 -0
  200. data/spec/components/blacklight/header_component_spec.rb +2 -4
  201. data/spec/components/blacklight/hidden_search_state_component_spec.rb +7 -7
  202. data/spec/components/blacklight/metadata_field_component_spec.rb +17 -15
  203. data/spec/components/blacklight/response/facet_group_component_spec.rb +37 -0
  204. data/spec/components/blacklight/response/pagination_component_spec.rb +1 -1
  205. data/spec/components/blacklight/response/spellcheck_component_spec.rb +1 -1
  206. data/spec/components/blacklight/search_bar_component_spec.rb +28 -5
  207. data/spec/components/blacklight/search_context/server_applied_params_component_spec.rb +2 -2
  208. data/spec/components/blacklight/search_context/server_item_pagination_component_spec.rb +3 -5
  209. data/spec/components/blacklight/skip_link_component_spec.rb +8 -11
  210. data/spec/components/blacklight/start_over_button_component_spec.rb +4 -4
  211. data/spec/components/blacklight/system/dropdown_component_spec.rb +26 -0
  212. data/spec/components/blacklight/system/flash_message_component_spec.rb +7 -11
  213. data/spec/controllers/blacklight/catalog_spec.rb +1 -1
  214. data/spec/controllers/catalog_controller_spec.rb +12 -20
  215. data/spec/features/advanced_search_spec.rb +42 -21
  216. data/spec/features/facets_spec.rb +84 -12
  217. data/spec/features/modal_spec.rb +1 -1
  218. data/spec/features/search_filters_spec.rb +6 -4
  219. data/spec/features/search_spec.rb +3 -3
  220. data/spec/helpers/blacklight/facets_helper_behavior_spec.rb +10 -0
  221. data/spec/lib/blacklight/configuration/facet_field_spec.rb +2 -2
  222. data/spec/lib/blacklight/parameters_spec.rb +12 -1
  223. data/spec/lib/blacklight/search_state/filter_field_spec.rb +18 -0
  224. data/spec/models/blacklight/configuration_spec.rb +178 -28
  225. data/spec/models/blacklight/solr/default_filter_query_builder_spec.rb +72 -0
  226. data/spec/models/blacklight/solr/document_spec.rb +0 -4
  227. data/spec/models/blacklight/solr/repository_spec.rb +37 -29
  228. data/spec/models/blacklight/solr/response/facets_spec.rb +86 -40
  229. data/spec/models/blacklight/solr/response/group_response_spec.rb +8 -5
  230. data/spec/models/blacklight/solr/response/group_spec.rb +9 -5
  231. data/spec/models/blacklight/solr/response_spec.rb +96 -64
  232. data/spec/models/blacklight/solr/search_builder_behavior_spec.rb +38 -57
  233. data/spec/models/solr_document_spec.rb +5 -1
  234. data/spec/presenters/blacklight/constraint_presenter_spec.rb +32 -0
  235. data/spec/presenters/blacklight/document_presenter_spec.rb +3 -3
  236. data/spec/presenters/blacklight/facet_item_presenter_spec.rb +0 -7
  237. data/spec/presenters/blacklight/field_presenter_spec.rb +103 -22
  238. data/spec/presenters/blacklight/json_presenter_spec.rb +17 -0
  239. data/spec/presenters/blacklight/rendering/pipeline_spec.rb +130 -14
  240. data/spec/services/blacklight/search_service_spec.rb +6 -27
  241. data/spec/spec_helper.rb +0 -1
  242. data/spec/support/presenter_test_helpers.rb +1 -1
  243. data/spec/support/view_component_test_helpers.rb +0 -18
  244. data/spec/views/catalog/facet.html.erb_spec.rb +10 -3
  245. data/spec/views/catalog/index.atom.builder_spec.rb +8 -3
  246. data/spec/views/catalog/index.html.erb_spec.rb +3 -1
  247. metadata +69 -38
  248. data/.github/workflows/ruby.yml +0 -98
  249. data/app/assets/stylesheets/blacklight/_icons.scss +0 -4
  250. data/app/components/blacklight/facet_field_list_component.html.erb +0 -19
  251. data/app/components/blacklight/search/facet_suggest_input.html.erb +0 -9
  252. data/app/components/blacklight/search/facet_suggest_input.rb +0 -16
  253. data/app/javascript/blacklight-frontend/modalForm.js +0 -60
  254. data/app/views/catalog/_facet_index_navigation.html.erb +0 -1
  255. data/app/views/catalog/_facet_layout.html.erb +0 -8
  256. data/app/views/catalog/_facet_pagination.html.erb +0 -1
  257. data/app/views/catalog/_facet_pivot.html.erb +0 -3
  258. data/app/views/shared/_sitelinks_search_box.html.erb +0 -12
  259. data/spec/components/blacklight/document_metadata_component_spec.rb +0 -0
  260. data/spec/components/blacklight/search/facet_suggest_input_spec.rb +0 -33
  261. data/spec/features/sitelinks_search_box_spec.rb +0 -13
  262. data/spec/views/catalog/_facet_index_navigation.html.erb_spec.rb +0 -43
  263. data/spec/views/catalog/_facet_layout.html.erb_spec.rb +0 -41
@@ -2,7 +2,7 @@ const ButtonFocus = (e) => {
2
2
  // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
3
3
  // Safari both do not set focus to a button on button click.
4
4
  // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
5
- if (e.target.matches('[data-toggle="collapse"]') || e.target.matches('[data-bs-toggle="collapse"]')) {
5
+ if (e.target.matches('[data-bs-toggle="collapse"]')) {
6
6
  e.target.focus()
7
7
  }
8
8
  }
@@ -10,7 +10,7 @@
10
10
  attribute (HTML5-style doc-*) that contains the id/primary key
11
11
  of the object in question -- used by plugin for a unique value for
12
12
  DOM id's.
13
- Uses HTML for a checkbox compatible with Bootstrap 4.
13
+ Uses HTML for a checkbox compatible with Bootstrap.
14
14
  new CheckboxSubmit(document.querySelector('form.something')).render()
15
15
  */
16
16
  export default class CheckboxSubmit {
@@ -36,10 +36,16 @@ export default class CheckboxSubmit {
36
36
  }).then((json) => {
37
37
  this.labelTarget.removeAttribute('disabled')
38
38
  this.checkboxTarget.removeAttribute('disabled')
39
+ // For accessibility return keyboard focus
40
+ // back to the checkbox after form submission
41
+ this.checkboxTarget.focus()
39
42
  this.updateStateFor(!this.checked)
40
43
  this.bookmarksCounter().forEach(counter => {
41
44
  counter.innerHTML = json.bookmarks.count;
42
45
  });
46
+
47
+ var e = new CustomEvent('bookmark.blacklight', { detail: { checked: this.checked }, bubbles: true });
48
+ this.formTarget.dispatchEvent(e)
43
49
  }).catch((error) => {
44
50
  this.handleError(error)
45
51
  })
@@ -74,7 +80,8 @@ export default class CheckboxSubmit {
74
80
  }
75
81
 
76
82
  updateStateFor(state) {
77
- this.checkboxTarget.checked = state
83
+ if (state) { this.checkboxTarget.setAttribute('checked', state) }
84
+ else { this.checkboxTarget.removeAttribute('checked') }
78
85
 
79
86
  if (state) {
80
87
  this.labelTarget.classList.add('checked')
@@ -3,7 +3,7 @@
3
3
  // const basicFunction = (entry) => console.log(entry)
4
4
  // const debounced = debounce(basicFunction("I should only be called once"));
5
5
  //
6
- // debounced // does NOT print to the screen becase it is invoked again less than 200 milliseconds later
6
+ // debounced // does NOT print to the screen because it is invoked again less than 200 milliseconds later
7
7
  // debounced // does print to the screen
8
8
  // ```
9
9
  export default function debounce(func, timeout = 200) {
@@ -4,20 +4,44 @@ const FacetSuggest = async (e) => {
4
4
  if (e.target.matches('.facet-suggest')) {
5
5
  const queryFragment = e.target.value?.trim();
6
6
  const facetField = e.target.dataset.facetField;
7
+ const facetArea = document.querySelector('.facet-extended-list');
8
+ const prevNextLinks = document.querySelectorAll('.prev_next_links');
9
+
7
10
  if (!facetField) { return; }
8
11
 
9
- const urlToFetch = `/catalog/facet_suggest/${facetField}/${queryFragment}${window.location.search}`
12
+ // Get the search params from the current query so the facet suggestions
13
+ // can retain that context.
14
+ const facetSearchContext = e.target.dataset.facetSearchContext;
15
+ const url = new URL(facetSearchContext, window.location.origin);
16
+
17
+ // Drop facet.page so a filtered suggestion list will always start on page 1
18
+ url.searchParams.delete('facet.page');
19
+ // add our queryFragment for facet filtering
20
+ url.searchParams.append('query_fragment', queryFragment);
21
+
22
+ const facetSearchParams = url.searchParams.toString();
23
+ const basePathComponent = url.pathname.split('/')[1];
24
+
25
+ const urlToFetch = `/${basePathComponent}/facet_suggest/${facetField}?${facetSearchParams}`;
26
+
10
27
  const response = await fetch(urlToFetch);
11
28
  if (response.ok) {
12
29
  const blob = await response.blob()
13
30
  const text = await blob.text()
14
31
 
15
- const facetArea = document.querySelector('.facet-extended-list');
16
-
17
32
  if (text && facetArea) {
18
33
  facetArea.innerHTML = text
19
34
  }
20
35
  }
36
+
37
+ // Hide the prev/next links when a user enters text in the facet
38
+ // suggestion input. They don't work with a filtered list.
39
+ prevNextLinks.forEach(element => {
40
+ element.classList.toggle('invisible', !!queryFragment);
41
+ });
42
+
43
+ // Add a class to distinguish suggested facet values vs. regular.
44
+ facetArea.classList.toggle('facet-suggestions', !!queryFragment);
21
45
  }
22
46
  };
23
47
 
@@ -2,7 +2,6 @@ import BookmarkToggle from 'blacklight-frontend/bookmark_toggle'
2
2
  import ButtonFocus from 'blacklight-frontend/button_focus'
3
3
  import FacetSuggest from 'blacklight-frontend/facet_suggest'
4
4
  import Modal from 'blacklight-frontend/modal'
5
- import ModalForm from 'blacklight-frontend/modalForm'
6
5
  import SearchContext from 'blacklight-frontend/search_context'
7
6
  import Core from 'blacklight-frontend/core'
8
7
 
@@ -11,7 +10,6 @@ export default {
11
10
  ButtonFocus,
12
11
  FacetSuggest,
13
12
  Modal,
14
- ModalForm,
15
13
  SearchContext,
16
14
  Core,
17
15
  onLoad: Core.onLoad
@@ -32,7 +32,7 @@
32
32
 
33
33
  <div data-blacklight-modal="container">
34
34
  <div class="modal-header">
35
- <button type="button" class="close" data-bl-dismiss="modal" aria-hidden="true">×</button>
35
+ <button type="button" class="btn-close" data-bl-dismiss="modal" aria-hidden="true">×</button>
36
36
  <h3 class="modal-title">Request Placed</h3>
37
37
  </div>
38
38
 
@@ -56,9 +56,6 @@
56
56
  const Modal = (() => {
57
57
  const modal = {}
58
58
 
59
- // bootstrap class that will stop body scrolling when modal is open
60
- const bootstrapBodyClassOpen = "modal-open";
61
-
62
59
  // a Bootstrap modal div that should be already on the page hidden
63
60
  modal.modalSelector = '#blacklight-modal';
64
61
 
@@ -84,8 +81,7 @@ const Modal = (() => {
84
81
 
85
82
  const contents = `<div class="modal-header">
86
83
  <div class="modal-title">There was a problem with your request.</div>
87
- <button type="button" class="blacklight-modal-close btn-close close" data-bl-dismiss="modal" aria-label="Close">
88
- <span aria-hidden="true" class="visually-hidden">&times;</span>
84
+ <button type="button" class="blacklight-modal-close btn-close" data-bl-dismiss="modal" aria-label="Close">
89
85
  </button>
90
86
  </div>
91
87
  <div class="modal-body">
@@ -137,8 +133,8 @@ const Modal = (() => {
137
133
 
138
134
  modal.modalAjaxLinkClick = function(e) {
139
135
  e.preventDefault();
140
- const href = e.target.getAttribute('href')
141
- fetch(href)
136
+ const href = e.target.closest('a').getAttribute('href')
137
+ fetch(href, { headers: { 'X-Requested-With': 'XMLHttpRequest' }})
142
138
  .then(response => {
143
139
  if (!response.ok) {
144
140
  throw new TypeError("Request failed");
@@ -161,6 +157,17 @@ const Modal = (() => {
161
157
  else if (e.target.matches(`${modal.modalSelector}`) || e.target.closest('[data-bl-dismiss="modal"]'))
162
158
  modal.hide()
163
159
  })
160
+
161
+ // Make sure user-agent dismissal of html 'dialog', etc `esc` key, triggers
162
+ // our hide logic, including events and scroll restoration.
163
+ const modalDom = modal.target();
164
+ if (modalDom) {
165
+ modal.target().addEventListener('cancel', (e) => {
166
+ e.preventDefault(); // 'hide' will close the modal unless cancelled
167
+
168
+ modal.hide();
169
+ });
170
+ }
164
171
  };
165
172
 
166
173
  modal.hide = function (el) {
@@ -1,6 +1,7 @@
1
1
  const SearchContext = (e) => {
2
- if (e.target.matches('[data-context-href]')) {
3
- SearchContext.handleSearchContextMethod.call(e.target, e)
2
+ const contextLink = e.target.closest('[data-context-href]')
3
+ if (contextLink) {
4
+ SearchContext.handleSearchContextMethod.call(contextLink, e)
4
5
  }
5
6
  }
6
7
 
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Blacklight
4
+ class ConstraintPresenter
5
+ def initialize(facet_item_presenter:, field_label:)
6
+ @facet_item_presenter = facet_item_presenter
7
+ @field_label = field_label
8
+ end
9
+
10
+ attr_accessor :facet_item_presenter, :field_label
11
+
12
+ delegate :key, :remove_href, to: :facet_item_presenter
13
+
14
+ # Get the displayable version of the facet's value for use
15
+ # in e.g. the constraints widget
16
+ #
17
+ # @return [String]
18
+ def constraint_label
19
+ facet_item_presenter.label
20
+ end
21
+ end
22
+ end
@@ -44,11 +44,11 @@ module Blacklight
44
44
  #
45
45
  # @return [String]
46
46
  def heading
47
- return field_value(view_config.title_field) if view_config.title_field.is_a? Blacklight::Configuration::Field
47
+ return field_value(view_config.title_field, join: true).first if view_config.title_field.is_a? Blacklight::Configuration::Field
48
48
 
49
49
  fields = Array.wrap(view_config.title_field) + [configuration.document_model.unique_key]
50
50
  f = fields.lazy.map { |field| field_config(field) }.detect { |field_config| field_presenter(field_config).any? }
51
- f ? field_value(f, except_operations: [Rendering::HelperMethod]) : ""
51
+ f ? field_value(f, except_operations: [Rendering::HelperMethod], join: true).first : ""
52
52
  end
53
53
 
54
54
  ##
@@ -58,12 +58,12 @@ module Blacklight
58
58
  # @see #document_heading
59
59
  # @return [String]
60
60
  def html_title
61
- return field_value(view_config.html_title_field) if view_config.html_title_field.is_a? Blacklight::Configuration::Field
61
+ return field_value(view_config.html_title_field, join: true).first if view_config.html_title_field.is_a? Blacklight::Configuration::Field
62
62
 
63
63
  if view_config.html_title_field
64
64
  fields = Array.wrap(view_config.html_title_field) + [configuration.document_model.unique_key]
65
65
  f = fields.lazy.map { |field| field_config(field) }.detect { |field_config| field_presenter(field_config).any? }
66
- field_value(f)
66
+ field_value(f, join: true).first
67
67
  else
68
68
  heading
69
69
  end
@@ -93,8 +93,9 @@ module Blacklight
93
93
  # @param [Configuration::Field] field_config
94
94
  # @param [Hash] options
95
95
  # @option options [String] :value
96
+ # @return [Array]
96
97
  def field_value field_config, options = {}
97
- field_presenter(field_config, options).render
98
+ Array.wrap(field_presenter(field_config, options).render)
98
99
  end
99
100
 
100
101
  def thumbnail_presenter_class
@@ -7,11 +7,27 @@ module Blacklight
7
7
  delegate :key, to: :facet_field
8
8
  delegate :field_name, to: :display_facet
9
9
 
10
- def initialize(facet_field, display_facet, view_context, search_state = view_context.search_state)
10
+ # @param [Blacklight::Configuration::FacetField] facet_field
11
+ # @param [Blacklight::Solr::Response::Facets::FacetField] display_facet
12
+ # @param [#search_action_path,#facet_field_presenter] view_context
13
+ # @param [Blacklight::SearchState] search_state
14
+ def initialize(facet_field, display_facet, view_context, search_state = nil)
11
15
  @facet_field = facet_field
12
16
  @display_facet = display_facet
13
17
  @view_context = view_context
14
- @search_state = search_state
18
+ @search_state = search_state || view_context&.search_state
19
+ end
20
+
21
+ # @param [Blacklight::Solr::Response::Facets::FacetItem, String] facet_item
22
+ def item_presenter(facet_item)
23
+ facet_field.item_presenter.new(facet_item, facet_field, view_context, key, search_state)
24
+ end
25
+
26
+ # Is facet suggest feature configured on for this facet, or by global default.
27
+ #
28
+ # @return [Boolean]
29
+ def suggest?
30
+ !!(facet_field.suggest.nil? ? blacklight_config.default_facet_suggest : facet_field.suggest)
15
31
  end
16
32
 
17
33
  def collapsed?
@@ -6,6 +6,12 @@ module Blacklight
6
6
 
7
7
  delegate :key, to: :facet_config
8
8
 
9
+ # @param [Array<String>] group
10
+ # @param [String] facet_item
11
+ # @param [Blacklight::Configuration::FacetField] facet_config
12
+ # @param [#search_action_path] view_context
13
+ # @param [String] facet_field
14
+ # @param [Blacklight::SearchState] search_state
9
15
  def initialize(group, facet_item, facet_config, view_context, facet_field, search_state = view_context.search_state)
10
16
  super(facet_item, facet_config, view_context, facet_field, search_state)
11
17
  @group = group
@@ -13,10 +13,6 @@ module Blacklight
13
13
  selected? || facet_item_presenters.any? { |x| x.try(:shown?) }
14
14
  end
15
15
 
16
- def field_label
17
- facet_field_presenter.label
18
- end
19
-
20
16
  def facet_item_presenters
21
17
  return to_enum(:facet_item_presenters) unless block_given?
22
18
  return [] unless items
@@ -25,7 +21,7 @@ module Blacklight
25
21
  end
26
22
 
27
23
  def facet_item_presenter(facet_item)
28
- facet_config.item_presenter.new(facet_item, facet_config, view_context, facet_field, search_state)
24
+ view_context.facet_field_presenter(facet_config, {}).item_presenter(facet_item)
29
25
  end
30
26
 
31
27
  ##
@@ -6,6 +6,11 @@ module Blacklight
6
6
 
7
7
  delegate :key, to: :facet_config
8
8
 
9
+ # @param [Blacklight::Solr::Response::Facets::FacetItem, String] facet_item
10
+ # @param [Blacklight::Configuration::FacetField] facet_config
11
+ # @param [#search_action_path,#facet_field_presenter] view_context
12
+ # @param [String] facet_field the name of the facet field. Same as facet_config.key
13
+ # @param [Blacklight::SearchState] search_state
9
14
  def initialize(facet_item, facet_config, view_context, facet_field, search_state = view_context.search_state)
10
15
  @facet_item = facet_item
11
16
  @facet_config = facet_config
@@ -33,10 +38,6 @@ module Blacklight
33
38
  search_state.filter(facet_config).include?(value)
34
39
  end
35
40
 
36
- def field_label
37
- facet_field_presenter.label
38
- end
39
-
40
41
  ##
41
42
  # Get the displayable version of a facet's value
42
43
  #
@@ -62,14 +63,6 @@ module Blacklight
62
63
  end
63
64
  end
64
65
 
65
- # Get the displayable version of the facet's value for use
66
- # in e.g. the constraints widget
67
- #
68
- # @return [String]
69
- def constraint_label
70
- label
71
- end
72
-
73
66
  def value
74
67
  if facet_item.respond_to? :value
75
68
  facet_item.value
@@ -99,11 +92,5 @@ module Blacklight
99
92
  view_context.search_action_path(search_state.add_facet_params_and_redirect(facet_config.key, facet_item).merge(path_options))
100
93
  end
101
94
  end
102
-
103
- private
104
-
105
- def facet_field_presenter
106
- @facet_field_presenter ||= view_context.facet_field_presenter(facet_config, {})
107
- end
108
95
  end
109
96
  end
@@ -33,9 +33,11 @@ module Blacklight
33
33
 
34
34
  delegate :key, :component, to: :field_config
35
35
 
36
- # @return [String]
36
+ # @return [Array<String>]
37
37
  def render
38
- Rendering::Pipeline.new(values, field_config, document, view_context, pipeline_steps, options).render
38
+ Array.wrap(
39
+ Rendering::Pipeline.new(values, field_config, document, view_context, pipeline_steps, options).render
40
+ )
39
41
  end
40
42
 
41
43
  # @return [Enumerable]
@@ -11,11 +11,9 @@ module Blacklight
11
11
 
12
12
  attr_reader :blacklight_config
13
13
 
14
- delegate :facet_field_names, :facet_configuration_for_field, to: :blacklight_config
14
+ delegate :facet_fields, :facet_configuration_for_field, to: :blacklight_config
15
15
 
16
- def documents
17
- @response.documents
18
- end
16
+ delegate :documents, to: :@response
19
17
 
20
18
  # @return [Array<Blacklight::Solr::Response::Facets::FacetField>]
21
19
  def search_facets
@@ -25,6 +23,12 @@ module Blacklight
25
23
  .select { |display_facet| display_facet.items.present? }
26
24
  end
27
25
 
26
+ # NOTE: we don't use the facet_field_names method in Blacklight::Configuration because that
27
+ # only returns the field names of the group where the name is nil.
28
+ def facet_field_names
29
+ facet_fields.values.map(&:field)
30
+ end
31
+
28
32
  # extract the pagination info from the response object
29
33
  def pagination_info
30
34
  h = {}
@@ -22,7 +22,13 @@ module Blacklight
22
22
  end
23
23
 
24
24
  def html?
25
- options[:format].nil? || options[:format].to_s == 'html'
25
+ format.nil? || format.to_s == 'html'
26
+ end
27
+
28
+ def format
29
+ return options[:format] unless context.respond_to?(:search_state)
30
+
31
+ options[:format] || context.search_state&.params&.dig(:format)
26
32
  end
27
33
  end
28
34
  end
@@ -15,10 +15,10 @@ module Blacklight
15
15
 
16
16
  def render_helper
17
17
  context.send(config.helper_method,
18
- **options.merge(document: document,
19
- field: config.field,
20
- config: config,
21
- value: values))
18
+ **options, document: document,
19
+ field: config.field,
20
+ config: config,
21
+ value: values)
22
22
  end
23
23
  end
24
24
  end
@@ -5,19 +5,23 @@ module Blacklight
5
5
  class Join < AbstractStep
6
6
  def render
7
7
  options = config.separator_options || {}
8
- if values.one? || values.none?
9
- next_step(values.first)
10
- elsif !html?
11
- next_step(values.to_sentence(options))
12
- else
8
+
9
+ if join? && html? && values.many?
13
10
  next_step(values.map { |x| x.html_safe? ? x : html_escape(x) }.to_sentence(options).html_safe)
11
+ else
12
+ next_step(values)
14
13
  end
15
14
  end
16
15
 
17
16
  private
18
17
 
19
- def html_escape(*args)
20
- ERB::Util.html_escape(*args)
18
+ def html_escape(*)
19
+ ERB::Util.html_escape(*)
20
+ end
21
+
22
+ # @return [Boolean]
23
+ def join?
24
+ options[:join] || config.join || config.separator_options
21
25
  end
22
26
  end
23
27
  end
@@ -4,7 +4,7 @@ module Blacklight
4
4
  module Rendering
5
5
  class Terminator < AbstractStep
6
6
  def render
7
- values
7
+ Array.wrap values
8
8
  end
9
9
  end
10
10
  end
@@ -41,7 +41,7 @@ module Blacklight
41
41
 
42
42
  def retrieve_simple
43
43
  # regular document field
44
- if field_config.default && field_config.default.is_a?(Proc)
44
+ if field_config.default.is_a?(Proc)
45
45
  document.fetch(field_config.field, &field_config.default)
46
46
  else
47
47
  document.fetch(field_config.field, field_config.default)
@@ -3,9 +3,9 @@
3
3
  # SearchService returns search results from the repository
4
4
  module Blacklight
5
5
  class SearchService
6
- def initialize(config:, search_state: nil, user_params: nil, search_builder_class: config.search_builder_class, **context)
6
+ def initialize(config:, search_state:, search_builder_class: config.search_builder_class, **context)
7
7
  @blacklight_config = config
8
- @search_state = search_state || Blacklight::SearchState.new(user_params || {}, config)
8
+ @search_state = search_state
9
9
  @user_params = @search_state.params
10
10
  @search_builder_class = search_builder_class
11
11
  @context = context
@@ -22,8 +22,10 @@ module Blacklight
22
22
  @search_state.class
23
23
  end
24
24
 
25
- # a solr query method
26
- # @yield [search_builder] optional block yields configured SearchBuilder, caller can modify or create new SearchBuilder to be used. Block should return SearchBuilder to be used.
25
+ # Fetch query results from solr
26
+ # @yield [search_builder] optional block yields configured SearchBuilder, caller can modify or create new
27
+ # SearchBuilder to be used. Block should return SearchBuilder to be used.
28
+ # This is used in blacklight_range_limit
27
29
  # @return [Blacklight::Solr::Response] the solr response object
28
30
  def search_results
29
31
  builder = search_builder.with(search_state)
@@ -31,7 +33,7 @@ module Blacklight
31
33
  builder.rows = search_state.per_page
32
34
 
33
35
  builder = yield(builder) if block_given?
34
- response = repository.search(builder)
36
+ response = repository.search(params: builder)
35
37
 
36
38
  if response.grouped? && grouped_key_for_results
37
39
  response.group(grouped_key_for_results)
@@ -59,12 +61,12 @@ module Blacklight
59
61
  # @return [Blacklight::Solr::Response] the solr response
60
62
  def facet_field_response(facet_field, extra_controller_params = {})
61
63
  query = search_builder.with(search_state).facet(facet_field)
62
- repository.search(query.merge(extra_controller_params))
64
+ repository.search(params: query.merge(extra_controller_params))
63
65
  end
64
66
 
65
67
  def facet_suggest_response(facet_field, facet_suggestion_query, extra_controller_params = {})
66
68
  query = search_builder.with(search_state).facet(facet_field).facet_suggestion_query(facet_suggestion_query)
67
- repository.search(query.merge(extra_controller_params))
69
+ repository.search(params: query.merge(extra_controller_params))
68
70
  end
69
71
 
70
72
  # Get the previous and next document from a search result
@@ -73,7 +75,7 @@ module Blacklight
73
75
  p = previous_and_next_document_params(index)
74
76
  new_state = request_params.is_a?(Blacklight::SearchState) ? request_params : Blacklight::SearchState.new(request_params, blacklight_config)
75
77
  query = search_builder.with(new_state).start(p.delete(:start)).rows(p.delete(:rows)).merge(extra_controller_params).merge(p)
76
- response = repository.search(query)
78
+ response = repository.search(params: query)
77
79
  document_list = response.documents
78
80
 
79
81
  # only get the previous doc if there is one
@@ -95,8 +95,8 @@ module Blacklight
95
95
  end
96
96
 
97
97
  class Selector < Array
98
- def initialize(by: nil, block: nil, **kwargs)
99
- super(**kwargs)
98
+ def initialize(by: nil, block: nil, **)
99
+ super(**)
100
100
  @by = by
101
101
  @block = block
102
102
  end
@@ -1,4 +1,4 @@
1
1
  <%= link_to bookmarks_path, id:'bookmarks_nav', class: 'nav-link' do %>
2
2
  <%= t('blacklight.header_links.bookmarks') %>
3
- <span class="badge badge-secondary bg-secondary" data-role='bookmark-counter'><%= current_or_guest_user.bookmarks.count %></span>
3
+ <span class="badge rounded-pill text-bg-secondary bookmark-counter" data-role='bookmark-counter'><%= current_or_guest_user.bookmarks.count %></span>
4
4
  <% end %>
@@ -21,7 +21,7 @@ xml.entry do
21
21
  with_format(:html) do
22
22
  xml.summary "type" => "html" do
23
23
  document_component = blacklight_config.view_config(:atom).summary_component
24
- xml.text! render document_component.new(document_component.collection_parameter => document_presenter(document), component: :div, show: true)
24
+ xml.text! render document_component.new(document: document_presenter(document), component: :div, show: true)
25
25
  end
26
26
  end
27
27
 
@@ -1,4 +1,4 @@
1
1
  <% # container for a single doc -%>
2
2
  <% view_config = local_assigns[:view_config] || blacklight_config.view_config(document_index_view_type, action_name: action_name) %>
3
3
  <% document_component = view_config.document_component -%>
4
- <%= render document_component.new(document_component.collection_parameter => document_presenter(document), counter: document_counter_with_offset(document_counter), partials: view_config&.partials) %>
4
+ <%= render document_component.new(document: document_presenter(document), counter: document_counter_with_offset(document_counter), partials: view_config&.partials) %>
@@ -6,8 +6,8 @@
6
6
 
7
7
  <div class="modal-body">
8
8
  <%= render '/shared/flash_msg' %>
9
- <div class="form-group row">
10
- <label class="control-label col-sm-2" for="to">
9
+ <div class="row">
10
+ <label class="col-form-label col-sm-2" for="to">
11
11
  <%= t('blacklight.email.form.to') %>
12
12
  </label>
13
13
  <div class="col-sm-10">
@@ -15,8 +15,8 @@
15
15
  </div>
16
16
  </div>
17
17
 
18
- <div class="form-group row">
19
- <label class="control-label col-sm-2" for="message">
18
+ <div class="row">
19
+ <label class="col-form-label col-sm-2" for="message">
20
20
  <%= t('blacklight.email.form.message') %>
21
21
  </label>
22
22
  <div class="col-sm-10">
@@ -1,5 +1,5 @@
1
- <div class="jumbotron text-center p-5 mb-4 bg-light rounded-3">
2
- <h1 class="jumbotron-heading"><%= t('blacklight.welcome') %></h1>
1
+ <div class="text-center p-5 mb-4 bg-light rounded-3">
2
+ <h1 class="fw-bold"><%= t('blacklight.welcome') %></h1>
3
3
 
4
4
  <p class="lead">Blacklight is a multi-institutional open-source collaboration building a better discovery platform framework.</p>
5
5
 
@@ -1,14 +1,18 @@
1
- <%= render blacklight_config.view_config(:show).document_header_component.new(document: @document, search_context: @search_context, search_session: search_session) %>
2
- <% @page_title = t('blacklight.search.show.title', document_title: document_presenter(@document).html_title, application_name: application_name).html_safe %>
1
+ <%# locals: (presenter: nil) %>
2
+ <% presenter ||= document_presenter(@document) %>
3
+
4
+ <% @page_title = t('blacklight.search.show.title', document_title: presenter.html_title, application_name: application_name).html_safe %>
3
5
  <% content_for(:head) { render_link_rel_alternates } %>
4
6
 
5
- <% document_component = blacklight_config.view_config(:show).document_component -%>
6
- <%= render (document_component).new(document_component.collection_parameter => document_presenter(@document), component: :div, show: true, partials: blacklight_config.view_config(:show).partials) do |component| %>
7
+ <%= render presenter.view_config.document_header_component.new(document: @document, search_context: @search_context, search_session: search_session) %>
8
+
9
+ <% document_component = presenter.view_config.document_component -%>
10
+ <%= render document_component.new(id: 'document', document: presenter, component: :div, show: true, partials: presenter.view_config.partials) do |component| %>
7
11
  <% component.with_title(as: 'h1', classes: '', link_to_document: false, actions: false) %>
8
12
  <% component.with_footer do %>
9
13
  <% if @document.respond_to?(:export_as_openurl_ctx_kev) %>
10
14
  <!-- COinS, for Zotero among others. -->
11
- <span class="Z3988" title="<%= @document.export_as_openurl_ctx_kev(document_presenter(@document).display_type) %>"></span>
15
+ <span class="Z3988" title="<%= @document.export_as_openurl_ctx_kev(presenter.display_type) %>"></span>
12
16
  <% end %>
13
17
  <% end %>
14
18
  <% end %>