blacklight 7.33.0 → 8.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 (427) hide show
  1. checksums.yaml +4 -4
  2. data/.env +3 -2
  3. data/.github/workflows/ruby.yml +38 -107
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +243 -2
  6. data/.rubocop_todo.yml +216 -270
  7. data/Gemfile +7 -6
  8. data/README.md +12 -4
  9. data/Rakefile +18 -0
  10. data/VERSION +1 -1
  11. data/app/assets/config/blacklight/manifest.js +3 -0
  12. data/app/assets/javascripts/blacklight/blacklight.esm.js +376 -0
  13. data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -0
  14. data/app/assets/javascripts/blacklight/blacklight.js +332 -485
  15. data/app/assets/javascripts/blacklight/blacklight.js.map +1 -0
  16. data/app/assets/stylesheets/blacklight/_autocomplete.scss +25 -0
  17. data/app/assets/stylesheets/blacklight/_blacklight_base.scss +1 -1
  18. data/app/assets/stylesheets/blacklight/_bookmark.scss +16 -0
  19. data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +8 -0
  20. data/app/assets/stylesheets/blacklight/_facets.scss +104 -49
  21. data/app/assets/stylesheets/blacklight/_header.scss +0 -15
  22. data/app/assets/stylesheets/blacklight/_icons.scss +0 -14
  23. data/app/assets/stylesheets/blacklight/_mixins.scss +20 -0
  24. data/app/assets/stylesheets/blacklight/_modal.scss +8 -2
  25. data/app/assets/stylesheets/blacklight/_search_form.scss +30 -3
  26. data/app/assets/stylesheets/blacklight/_search_history.scss +10 -1
  27. data/app/assets/stylesheets/blacklight/_search_results.scss +6 -2
  28. data/app/assets/stylesheets/blacklight/blacklight_defaults.scss +4 -1
  29. data/app/builders/blacklight/action_builder.rb +18 -9
  30. data/app/components/blacklight/advanced_search_form_component.html.erb +1 -1
  31. data/app/components/blacklight/advanced_search_form_component.rb +7 -9
  32. data/app/components/blacklight/constraints_component.rb +27 -37
  33. data/app/components/blacklight/document/action_component.html.erb +2 -9
  34. data/app/components/blacklight/document/action_component.rb +26 -8
  35. data/app/components/blacklight/document/actions_component.rb +2 -2
  36. data/app/components/blacklight/document/bookmark_component.html.erb +9 -0
  37. data/app/components/blacklight/document/bookmark_component.rb +4 -2
  38. data/app/components/blacklight/document/citation_component.rb +5 -3
  39. data/app/components/blacklight/document/group_component.rb +7 -3
  40. data/app/components/blacklight/document/show_tools_component.html.erb +12 -0
  41. data/app/components/blacklight/document/show_tools_component.rb +23 -0
  42. data/app/components/blacklight/document/sidebar_component.html.erb +2 -0
  43. data/app/components/blacklight/document/sidebar_component.rb +16 -0
  44. data/app/components/blacklight/document/thumbnail_component.html.erb +2 -7
  45. data/app/components/blacklight/document/thumbnail_component.rb +1 -9
  46. data/app/components/blacklight/document_component.html.erb +4 -0
  47. data/app/components/blacklight/document_component.rb +54 -42
  48. data/app/components/blacklight/document_metadata_component.html.erb +4 -2
  49. data/app/components/blacklight/document_metadata_component.rb +11 -6
  50. data/app/components/blacklight/document_title_component.rb +4 -1
  51. data/app/components/blacklight/facet_component.rb +1 -1
  52. data/app/components/blacklight/facet_field_checkboxes_component.html.erb +3 -3
  53. data/app/components/blacklight/facet_field_checkboxes_component.rb +1 -1
  54. data/app/components/blacklight/facet_field_component.html.erb +5 -5
  55. data/app/components/blacklight/facet_field_component.rb +9 -2
  56. data/app/components/blacklight/facet_field_list_component.html.erb +3 -3
  57. data/app/components/blacklight/facet_field_list_component.rb +37 -5
  58. data/app/components/blacklight/facet_field_no_layout_component.rb +0 -2
  59. data/app/components/blacklight/facet_item_component.rb +1 -43
  60. data/app/components/blacklight/facet_item_pivot_component.rb +21 -23
  61. data/app/components/blacklight/header_component.html.erb +2 -0
  62. data/app/components/blacklight/header_component.rb +26 -0
  63. data/app/components/blacklight/hidden_search_state_component.rb +1 -2
  64. data/app/components/blacklight/icons/legacy_icon_component.rb +30 -0
  65. data/app/components/blacklight/icons/list_component.rb +16 -0
  66. data/app/components/blacklight/icons/search_component.rb +16 -0
  67. data/app/components/blacklight/metadata_field_component.html.erb +2 -2
  68. data/app/components/blacklight/metadata_field_component.rb +24 -8
  69. data/app/components/blacklight/metadata_field_layout_component.rb +7 -22
  70. data/app/components/blacklight/metadata_field_plain_text_layout_component.rb +17 -0
  71. data/app/components/blacklight/response/facet_group_component.html.erb +5 -6
  72. data/app/components/blacklight/response/facet_group_component.rb +29 -8
  73. data/app/components/blacklight/response/sort_component.rb +2 -2
  74. data/app/components/blacklight/response/spellcheck_component.rb +18 -7
  75. data/app/components/blacklight/response/view_type_button_component.rb +3 -7
  76. data/app/components/blacklight/response/view_type_component.rb +4 -6
  77. data/app/components/blacklight/search/sidebar_component.html.erb +8 -0
  78. data/app/components/blacklight/search/sidebar_component.rb +17 -0
  79. data/app/components/blacklight/search_bar_component.html.erb +20 -15
  80. data/app/components/blacklight/search_bar_component.rb +2 -16
  81. data/app/components/blacklight/search_button_component.rb +3 -3
  82. data/app/components/blacklight/search_context/server_applied_params_component.html.erb +4 -0
  83. data/app/components/blacklight/search_context/server_applied_params_component.rb +13 -0
  84. data/app/components/blacklight/search_context/server_item_pagination_component.rb +71 -0
  85. data/app/components/blacklight/search_header_component.html.erb +2 -0
  86. data/app/components/blacklight/search_header_component.rb +6 -0
  87. data/app/components/blacklight/search_navbar_component.html.erb +5 -0
  88. data/app/components/blacklight/search_navbar_component.rb +34 -0
  89. data/app/components/blacklight/start_over_button_component.rb +5 -3
  90. data/app/components/blacklight/system/dropdown_component.rb +3 -3
  91. data/app/components/blacklight/system/flash_message_component.html.erb +3 -1
  92. data/app/components/blacklight/system/flash_message_component.rb +12 -3
  93. data/app/components/blacklight/system/modal_component.html.erb +1 -1
  94. data/app/components/blacklight/system/modal_component.rb +1 -3
  95. data/app/components/blacklight/top_navbar_component.html.erb +12 -0
  96. data/app/components/blacklight/top_navbar_component.rb +17 -0
  97. data/app/controllers/bookmarks_controller.rb +1 -0
  98. data/app/controllers/catalog_controller.rb +1 -0
  99. data/app/controllers/concerns/blacklight/bookmarks.rb +11 -6
  100. data/app/controllers/concerns/blacklight/catalog.rb +21 -72
  101. data/app/controllers/concerns/blacklight/controller.rb +6 -43
  102. data/app/controllers/concerns/blacklight/search_context.rb +71 -13
  103. data/app/controllers/concerns/blacklight/search_history.rb +2 -0
  104. data/app/controllers/concerns/blacklight/searchable.rb +12 -1
  105. data/app/controllers/concerns/blacklight/token_based_user.rb +1 -0
  106. data/app/controllers/search_history_controller.rb +1 -0
  107. data/app/helpers/blacklight/blacklight_helper_behavior.rb +1 -400
  108. data/app/helpers/blacklight/catalog_helper_behavior.rb +40 -182
  109. data/app/helpers/blacklight/component_helper_behavior.rb +1 -63
  110. data/app/helpers/blacklight/configuration_helper_behavior.rb +2 -117
  111. data/app/helpers/blacklight/document_helper_behavior.rb +76 -0
  112. data/app/helpers/blacklight/facets_helper_behavior.rb +4 -321
  113. data/app/helpers/blacklight/icon_helper_behavior.rb +5 -4
  114. data/app/helpers/blacklight/layout_helper_behavior.rb +60 -0
  115. data/app/helpers/blacklight/render_partials_helper_behavior.rb +12 -30
  116. data/app/helpers/blacklight/url_helper_behavior.rb +21 -97
  117. data/app/helpers/blacklight_helper.rb +1 -0
  118. data/app/helpers/catalog_helper.rb +1 -0
  119. data/app/javascript/blacklight/bookmark_toggle.js +9 -22
  120. data/app/javascript/blacklight/button_focus.js +9 -7
  121. data/app/javascript/blacklight/checkbox_submit.js +68 -122
  122. data/app/javascript/blacklight/core.js +6 -5
  123. data/app/javascript/blacklight/index.js +14 -0
  124. data/app/javascript/blacklight/modal.js +100 -165
  125. data/app/javascript/blacklight/modalForm.js +60 -0
  126. data/app/javascript/blacklight/search_context.js +13 -29
  127. data/app/models/blacklight/facet_paginator.rb +3 -2
  128. data/app/models/blacklight/icon.rb +4 -2
  129. data/app/models/bookmark.rb +0 -2
  130. data/app/models/concerns/blacklight/configurable.rb +5 -4
  131. data/app/models/concerns/blacklight/document/active_model_shim.rb +1 -0
  132. data/app/models/concerns/blacklight/document/attributes.rb +50 -0
  133. data/app/models/concerns/blacklight/document/cache_key.rb +1 -0
  134. data/app/models/concerns/blacklight/document/dublin_core.rb +2 -1
  135. data/app/models/concerns/blacklight/document/export.rb +2 -1
  136. data/app/models/concerns/blacklight/document/extensions.rb +1 -0
  137. data/app/models/concerns/blacklight/document/schema_org.rb +1 -0
  138. data/app/models/concerns/blacklight/document/semantic_fields.rb +1 -0
  139. data/app/models/concerns/blacklight/document.rb +23 -20
  140. data/app/models/concerns/blacklight/suggest/response.rb +1 -0
  141. data/app/models/concerns/blacklight/user.rb +17 -8
  142. data/app/models/record_mailer.rb +23 -18
  143. data/app/models/search.rb +0 -1
  144. data/app/models/solr_document.rb +1 -0
  145. data/app/presenters/blacklight/clause_presenter.rb +1 -1
  146. data/app/presenters/blacklight/document_presenter.rb +24 -30
  147. data/app/presenters/blacklight/facet_field_presenter.rb +39 -14
  148. data/app/presenters/blacklight/facet_grouped_item_presenter.rb +1 -5
  149. data/app/presenters/blacklight/facet_item_pivot_presenter.rb +60 -0
  150. data/app/presenters/blacklight/facet_item_presenter.rb +3 -9
  151. data/app/presenters/blacklight/field_presenter.rb +1 -0
  152. data/app/presenters/blacklight/index_presenter.rb +2 -40
  153. data/app/presenters/blacklight/json_presenter.rb +7 -5
  154. data/app/presenters/blacklight/rendering/abstract_step.rb +4 -0
  155. data/app/presenters/blacklight/rendering/join.rb +7 -1
  156. data/app/presenters/blacklight/rendering/link_to_facet.rb +3 -6
  157. data/app/presenters/blacklight/rendering/microdata.rb +1 -1
  158. data/app/presenters/blacklight/show_presenter.rb +1 -9
  159. data/app/presenters/blacklight/thumbnail_presenter.rb +1 -1
  160. data/app/services/blacklight/bookmarks_search_builder.rb +22 -0
  161. data/app/services/blacklight/field_retriever.rb +1 -8
  162. data/app/services/blacklight/search_service.rb +9 -7
  163. data/app/values/blacklight/types.rb +95 -11
  164. data/app/views/bookmarks/_clear_bookmarks_widget.html.erb +8 -1
  165. data/app/views/bookmarks/_tools.html.erb +7 -12
  166. data/app/views/catalog/_advanced_search_form.html.erb +0 -1
  167. data/app/views/catalog/_bookmark_control.html.erb +1 -1
  168. data/app/views/catalog/_constraints.html.erb +1 -14
  169. data/app/views/catalog/_document.atom.builder +12 -14
  170. data/app/views/catalog/_document.html.erb +3 -6
  171. data/app/views/catalog/_document.rss.builder +2 -4
  172. data/app/views/catalog/_document_list.html.erb +2 -1
  173. data/app/views/catalog/_facet_layout.html.erb +2 -2
  174. data/app/views/catalog/_facets.html.erb +5 -4
  175. data/app/views/catalog/_home_text.html.erb +2 -14
  176. data/app/views/catalog/_search_form.html.erb +3 -3
  177. data/app/views/catalog/_search_header.html.erb +1 -2
  178. data/app/views/catalog/_search_results.html.erb +2 -2
  179. data/app/views/catalog/_search_sidebar.html.erb +5 -1
  180. data/app/views/catalog/_show_main_content.html.erb +8 -19
  181. data/app/views/catalog/_show_sidebar.html.erb +2 -2
  182. data/app/views/catalog/_show_tools.html.erb +1 -19
  183. data/app/views/catalog/_view_type_group.html.erb +1 -1
  184. data/app/views/catalog/email_success.html.erb +5 -6
  185. data/app/views/catalog/facet.html.erb +7 -5
  186. data/app/views/catalog/index.atom.builder +12 -14
  187. data/app/views/catalog/index.html.erb +7 -1
  188. data/app/views/catalog/index.json.jbuilder +19 -19
  189. data/app/views/catalog/index.rss.builder +1 -1
  190. data/app/views/catalog/opensearch.xml.builder +1 -1
  191. data/app/views/catalog/show.html.erb +1 -6
  192. data/app/views/catalog/sms_success.html.erb +5 -6
  193. data/app/views/catalog/suggest.html.erb +3 -0
  194. data/app/views/layouts/blacklight/base.html.erb +19 -9
  195. data/app/views/record_mailer/email_record.text.erb +4 -3
  196. data/app/views/record_mailer/sms_record.text.erb +4 -4
  197. data/app/views/search_history/index.html.erb +6 -2
  198. data/app/views/shared/_header_navbar.html.erb +1 -22
  199. data/app/views/shared/_modal.html.erb +3 -3
  200. data/blacklight.gemspec +8 -10
  201. data/config/importmap.rb +3 -0
  202. data/config/locales/blacklight.ar.yml +209 -233
  203. data/config/locales/blacklight.ca.yml +0 -7
  204. data/config/locales/blacklight.de.yml +197 -216
  205. data/config/locales/blacklight.en.yml +2 -7
  206. data/config/locales/blacklight.es.yml +196 -216
  207. data/config/locales/blacklight.fr.yml +196 -220
  208. data/config/locales/blacklight.hu.yml +193 -213
  209. data/config/locales/blacklight.it.yml +197 -216
  210. data/config/locales/blacklight.nl.yml +193 -213
  211. data/config/locales/blacklight.pt-BR.yml +195 -216
  212. data/config/locales/blacklight.sq.yml +193 -213
  213. data/config/locales/blacklight.zh.yml +194 -214
  214. data/config/routes.rb +3 -2
  215. data/db/migrate/20140202020201_create_searches.rb +1 -0
  216. data/db/migrate/20140202020202_create_bookmarks.rb +1 -0
  217. data/db/migrate/20140320000000_add_polymorphic_type_to_bookmarks.rb +1 -0
  218. data/docker-compose.yml +1 -0
  219. data/lib/blacklight/abstract_repository.rb +1 -0
  220. data/lib/blacklight/configuration/context.rb +4 -4
  221. data/lib/blacklight/configuration/display_field.rb +7 -9
  222. data/lib/blacklight/configuration/facet_field.rb +17 -11
  223. data/lib/blacklight/configuration/field.rb +1 -0
  224. data/lib/blacklight/configuration/fields.rb +12 -15
  225. data/lib/blacklight/configuration/index_field.rb +1 -0
  226. data/lib/blacklight/configuration/null_display_field.rb +17 -0
  227. data/lib/blacklight/configuration/search_field.rb +1 -0
  228. data/lib/blacklight/configuration/session_tracking_config.rb +45 -0
  229. data/lib/blacklight/configuration/show_field.rb +1 -0
  230. data/lib/blacklight/configuration/sort_field.rb +1 -0
  231. data/lib/blacklight/configuration/tool_config.rb +1 -0
  232. data/lib/blacklight/configuration/view_config.rb +16 -10
  233. data/lib/blacklight/configuration.rb +322 -341
  234. data/lib/blacklight/engine.rb +13 -15
  235. data/lib/blacklight/exceptions.rb +2 -2
  236. data/lib/blacklight/nested_open_struct_with_hash_access.rb +6 -12
  237. data/lib/blacklight/open_struct_with_hash_access.rb +19 -17
  238. data/lib/blacklight/parameters.rb +7 -21
  239. data/lib/blacklight/routes/exportable.rb +1 -0
  240. data/lib/blacklight/routes/searchable.rb +3 -1
  241. data/lib/blacklight/routes.rb +1 -0
  242. data/lib/blacklight/search_builder.rb +10 -10
  243. data/lib/blacklight/search_state/filter_field.rb +8 -25
  244. data/lib/blacklight/search_state/pivot_filter_field.rb +144 -0
  245. data/lib/blacklight/search_state.rb +23 -79
  246. data/lib/blacklight/solr/document.rb +1 -0
  247. data/lib/blacklight/solr/facet_paginator.rb +1 -0
  248. data/lib/blacklight/solr/repository.rb +1 -0
  249. data/lib/blacklight/solr/request.rb +3 -0
  250. data/lib/blacklight/solr/response/facets.rb +21 -5
  251. data/lib/blacklight/solr/response/group.rb +1 -0
  252. data/lib/blacklight/solr/response/group_response.rb +1 -0
  253. data/lib/blacklight/solr/response/more_like_this.rb +1 -0
  254. data/lib/blacklight/solr/response/pagination_methods.rb +4 -3
  255. data/lib/blacklight/solr/response/params.rb +5 -4
  256. data/lib/blacklight/solr/response/response.rb +1 -0
  257. data/lib/blacklight/solr/response/spelling.rb +1 -0
  258. data/lib/blacklight/solr/response.rb +4 -3
  259. data/lib/blacklight/solr/search_builder_behavior.rb +16 -32
  260. data/lib/blacklight/solr.rb +1 -0
  261. data/lib/blacklight/version.rb +1 -0
  262. data/lib/blacklight.rb +8 -11
  263. data/lib/generators/blacklight/assets/importmap_generator.rb +56 -0
  264. data/lib/generators/blacklight/assets/propshaft_generator.rb +34 -0
  265. data/lib/generators/blacklight/assets/sprockets_generator.rb +68 -0
  266. data/lib/generators/blacklight/assets_generator.rb +14 -88
  267. data/lib/generators/blacklight/controller_generator.rb +3 -2
  268. data/lib/generators/blacklight/document_generator.rb +1 -0
  269. data/lib/generators/blacklight/install_generator.rb +7 -4
  270. data/lib/generators/blacklight/models_generator.rb +1 -0
  271. data/lib/generators/blacklight/search_builder_generator.rb +1 -0
  272. data/lib/generators/blacklight/solr_generator.rb +1 -1
  273. data/lib/generators/blacklight/templates/catalog_controller.rb +37 -7
  274. data/lib/generators/blacklight/templates/solr_document.rb +1 -7
  275. data/lib/generators/blacklight/test_support_generator.rb +4 -2
  276. data/lib/generators/blacklight/user_generator.rb +7 -9
  277. data/lib/railties/blacklight.rake +2 -3
  278. data/package.json +10 -10
  279. data/rollup.config.js +37 -0
  280. data/spec/components/blacklight/constraints_component_spec.rb +17 -13
  281. data/spec/components/blacklight/document_component_spec.rb +74 -33
  282. data/spec/components/blacklight/facet_component_spec.rb +2 -7
  283. data/spec/components/blacklight/facet_field_checkboxes_component_spec.rb +1 -2
  284. data/spec/components/blacklight/facet_field_list_component_spec.rb +7 -6
  285. data/spec/components/blacklight/facet_item_pivot_component_spec.rb +9 -7
  286. data/spec/components/blacklight/header_component_spec.rb +20 -0
  287. data/spec/components/blacklight/response/view_type_component_spec.rb +83 -0
  288. data/spec/components/blacklight/search_bar_component_spec.rb +1 -1
  289. data/spec/components/blacklight/search_context/server_applied_params_component_spec.rb +29 -0
  290. data/spec/components/blacklight/search_context/server_item_pagination_component_spec.rb +51 -0
  291. data/spec/controllers/blacklight/catalog/component_configuration_spec.rb +1 -6
  292. data/spec/controllers/blacklight/{base_spec.rb → catalog_spec.rb} +2 -2
  293. data/spec/controllers/bookmarks_controller_spec.rb +2 -3
  294. data/spec/controllers/catalog_controller_spec.rb +24 -126
  295. data/spec/features/autocomplete_spec.rb +1 -1
  296. data/spec/features/axe_spec.rb +1 -1
  297. data/spec/features/bookmarks_spec.rb +1 -1
  298. data/spec/features/component_template_override_spec.rb +8 -0
  299. data/spec/features/facets_spec.rb +1 -1
  300. data/spec/features/modal_spec.rb +12 -0
  301. data/spec/features/search_context_spec.rb +4 -10
  302. data/spec/features/search_results_spec.rb +0 -33
  303. data/spec/features/sitelinks_search_box_spec.rb +13 -0
  304. data/spec/helpers/blacklight/configuration_helper_behavior_spec.rb +2 -138
  305. data/spec/helpers/blacklight/facets_helper_behavior_spec.rb +0 -387
  306. data/spec/helpers/blacklight/icon_helper_behavior_spec.rb +8 -0
  307. data/spec/helpers/blacklight/render_partials_helper_behavior_spec.rb +5 -7
  308. data/spec/helpers/blacklight/url_helper_behavior_spec.rb +10 -132
  309. data/spec/helpers/blacklight_helper_spec.rb +3 -298
  310. data/spec/helpers/catalog_helper_spec.rb +60 -115
  311. data/spec/i18n_spec.rb +1 -0
  312. data/spec/integration/generators/blacklight/solr_generator_spec.rb +1 -1
  313. data/spec/lib/blacklight/configuration/facet_field_spec.rb +27 -16
  314. data/spec/lib/blacklight/configuration/field_spec.rb +1 -1
  315. data/spec/lib/blacklight/configuration/session_tracking_config_spec.rb +38 -0
  316. data/spec/lib/blacklight/configuration/view_config_spec.rb +1 -1
  317. data/spec/lib/blacklight/open_struct_with_hash_access_spec.rb +4 -4
  318. data/spec/lib/blacklight/parameters_spec.rb +1 -4
  319. data/spec/lib/blacklight/search_state/filter_field_spec.rb +4 -4
  320. data/spec/lib/blacklight/search_state/pivot_filter_field_spec.rb +117 -0
  321. data/spec/lib/blacklight/search_state_spec.rb +80 -198
  322. data/spec/lib/tasks/blacklight_task_spec.rb +1 -0
  323. data/spec/models/blacklight/configuration_spec.rb +41 -53
  324. data/spec/models/blacklight/document/active_model_shim_spec.rb +2 -2
  325. data/spec/models/blacklight/icon_spec.rb +29 -15
  326. data/spec/models/blacklight/search_builder_spec.rb +12 -12
  327. data/spec/models/blacklight/solr/document_spec.rb +3 -3
  328. data/spec/models/blacklight/solr/request_spec.rb +2 -2
  329. data/spec/models/blacklight/solr/response/facets_spec.rb +27 -27
  330. data/spec/models/blacklight/solr/response/group_response_spec.rb +1 -0
  331. data/spec/models/blacklight/solr/response/group_spec.rb +1 -0
  332. data/spec/models/blacklight/solr/response_spec.rb +9 -2
  333. data/spec/models/blacklight/solr/search_builder_spec.rb +32 -23
  334. data/spec/models/blacklight/user_spec.rb +22 -0
  335. data/spec/models/record_mailer_spec.rb +36 -23
  336. data/spec/models/solr_document_spec.rb +21 -3
  337. data/spec/presenters/blacklight/clause_presenter_spec.rb +1 -0
  338. data/spec/presenters/blacklight/document_presenter_spec.rb +3 -2
  339. data/spec/presenters/blacklight/facet_field_presenter_spec.rb +85 -12
  340. data/spec/presenters/blacklight/facet_grouped_item_presenter_spec.rb +1 -0
  341. data/spec/presenters/blacklight/facet_item_presenter_spec.rb +14 -13
  342. data/spec/presenters/blacklight/field_presenter_spec.rb +2 -16
  343. data/spec/presenters/blacklight/index_presenter_spec.rb +3 -4
  344. data/spec/presenters/blacklight/json_presenter_spec.rb +1 -0
  345. data/spec/presenters/blacklight/link_alternate_presenter_spec.rb +3 -2
  346. data/spec/presenters/blacklight/show_presenter_spec.rb +11 -25
  347. data/spec/presenters/pipeline_spec.rb +13 -2
  348. data/spec/presenters/thumbnail_presenter_spec.rb +1 -1
  349. data/spec/requests/load_suggestions_spec.rb +16 -0
  350. data/spec/routing/catalog_routing_spec.rb +6 -1
  351. data/spec/services/blacklight/search_service_spec.rb +31 -68
  352. data/spec/spec_helper.rb +4 -3
  353. data/spec/support/controller_level_helpers.rb +1 -2
  354. data/spec/support/features/search_helpers.rb +39 -0
  355. data/spec/support/features/session_helpers.rb +1 -0
  356. data/spec/support/features.rb +3 -0
  357. data/spec/support/view_component_test_helpers.rb +21 -0
  358. data/spec/test_app_templates/Gemfile.extra +4 -0
  359. data/spec/test_app_templates/lib/generators/test_app_generator.rb +17 -2
  360. data/spec/views/catalog/_document.html.erb_spec.rb +23 -33
  361. data/spec/views/catalog/_document_list.html.erb_spec.rb +7 -2
  362. data/spec/views/catalog/_facet_index_navigation.html.erb_spec.rb +1 -1
  363. data/spec/views/catalog/_search_header.erb_spec.rb +1 -0
  364. data/spec/views/catalog/_show_sidebar.erb_spec.rb +1 -0
  365. data/spec/views/catalog/_show_tools.html.erb_spec.rb +7 -54
  366. data/spec/views/catalog/_view_type_group.html.erb_spec.rb +17 -9
  367. data/spec/views/catalog/email_success.html.erb_spec.rb +2 -2
  368. data/spec/views/catalog/facet.html.erb_spec.rb +6 -3
  369. data/spec/views/catalog/index.atom.builder_spec.rb +18 -11
  370. data/spec/views/catalog/index.html.erb_spec.rb +5 -6
  371. data/spec/views/catalog/index.json.jbuilder_spec.rb +3 -3
  372. data/spec/views/catalog/show.html.erb_spec.rb +3 -25
  373. data/spec/views/catalog/sms_success.html.erb_spec.rb +2 -2
  374. data/tasks/blacklight.rake +7 -5
  375. metadata +95 -123
  376. data/.babelrc +0 -11
  377. data/app/assets/images/blacklight/list.svg +0 -1
  378. data/app/assets/images/blacklight/search.svg +0 -1
  379. data/app/assets/stylesheets/blacklight/_twitter_typeahead.scss +0 -37
  380. data/app/components/blacklight/content_areas_shim.rb +0 -13
  381. data/app/components/blacklight/search_context_component.rb +0 -34
  382. data/app/controllers/concerns/blacklight/base.rb +0 -7
  383. data/app/controllers/concerns/blacklight/default_component_configuration.rb +0 -64
  384. data/app/controllers/concerns/blacklight/facet.rb +0 -69
  385. data/app/controllers/concerns/blacklight/search_fields.rb +0 -46
  386. data/app/helpers/blacklight/hash_as_hidden_fields_helper_behavior.rb +0 -27
  387. data/app/helpers/blacklight/render_constraints_helper_behavior.rb +0 -188
  388. data/app/helpers/blacklight/search_history_constraints_helper_behavior.rb +0 -97
  389. data/app/helpers/blacklight/suggest_helper_behavior.rb +0 -13
  390. data/app/javascript/blacklight/autocomplete.js +0 -36
  391. data/app/javascript/blacklight/facet_load.js +0 -22
  392. data/app/models/concerns/blacklight/document/email.rb +0 -26
  393. data/app/models/concerns/blacklight/document/sms.rb +0 -24
  394. data/app/presenters/blacklight/search_bar_presenter.rb +0 -47
  395. data/app/views/catalog/_constraints_element.html.erb +0 -14
  396. data/app/views/catalog/_document_action.html.erb +0 -5
  397. data/app/views/catalog/_facet_group.html.erb +0 -5
  398. data/app/views/catalog/_facet_limit.html.erb +0 -3
  399. data/app/views/catalog/_index.html.erb +0 -1
  400. data/app/views/catalog/_index_header.html.erb +0 -22
  401. data/app/views/catalog/_previous_next_doc.html.erb +0 -2
  402. data/app/views/catalog/_show.html.erb +0 -6
  403. data/app/views/catalog/_show_header.html.erb +0 -2
  404. data/app/views/catalog/_thumbnail.html.erb +0 -1
  405. data/lib/blacklight/deprecations/engine_configuration.rb +0 -66
  406. data/lib/blacklight/deprecations/search_state_normalization.rb +0 -52
  407. data/spec/controllers/blacklight/facet_spec.rb +0 -33
  408. data/spec/controllers/blacklight/search_fields_spec.rb +0 -62
  409. data/spec/features/sitelinks_search_box.rb +0 -13
  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 -108
  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/models/blacklight/document/email_spec.rb +0 -57
  416. data/spec/models/blacklight/document/sms_spec.rb +0 -58
  417. data/spec/presenters/blacklight/search_bar_presenter_spec.rb +0 -94
  418. data/spec/support/view_component_capybara_test_helpers.rb +0 -8
  419. data/spec/views/catalog/_constraints.html.erb_spec.rb +0 -33
  420. data/spec/views/catalog/_facet_group.html.erb_spec.rb +0 -84
  421. data/spec/views/catalog/_facets.html.erb_spec.rb +0 -15
  422. data/spec/views/catalog/_index.html.erb_spec.rb +0 -62
  423. data/spec/views/catalog/_index_header.html.erb_spec.rb +0 -35
  424. data/spec/views/catalog/_previous_next_doc.html.erb_spec.rb +0 -22
  425. data/spec/views/catalog/_show.html.erb_spec.rb +0 -62
  426. data/spec/views/catalog/_thumbnail.html.erb_spec.rb +0 -38
  427. /data/app/components/blacklight/{search_context_component.html.erb → search_context/server_item_pagination_component.html.erb} +0 -0
@@ -1,537 +1,384 @@
1
- "use strict";
2
-
3
- var Blacklight = function () {
4
- var buffer = new Array();
5
- return {
6
- onLoad: function onLoad(func) {
7
- buffer.push(func);
8
- },
9
- activate: function activate() {
10
- for (var i = 0; i < buffer.length; i++) {
11
- buffer[i].call();
12
- }
13
- },
14
- listeners: function listeners() {
15
- var listeners = [];
16
-
17
- if (typeof Turbo !== 'undefined') {
18
- listeners.push('turbo:load');
19
- } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
20
- // Turbolinks 5
21
- if (Turbolinks.BrowserAdapter) {
22
- listeners.push('turbolinks:load');
23
- } else {
24
- // Turbolinks < 5
25
- listeners.push('page:load', 'DOMContentLoaded');
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3
+ typeof define === 'function' && define.amd ? define(factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Blacklight = factory());
5
+ })(this, (function () { 'use strict';
6
+
7
+ /* Converts a "toggle" form, with single submit button to add/remove
8
+ something, like used for Bookmarks, into an AJAXy checkbox instead.
9
+ Apply to a form. Does require certain assumption about the form:
10
+ 1) The same form 'action' href must be used for both ADD and REMOVE
11
+ actions, with the different being the hidden input name="_method"
12
+ being set to "put" or "delete" -- that's the Rails method to pretend
13
+ to be doing a certain HTTP verb. So same URL, PUT to add, DELETE
14
+ to remove. This plugin assumes that.
15
+ Plus, the form this is applied to should provide a data-doc-id
16
+ attribute (HTML5-style doc-*) that contains the id/primary key
17
+ of the object in question -- used by plugin for a unique value for
18
+ DOM id's.
19
+ Uses HTML for a checkbox compatible with Bootstrap 4.
20
+ new CheckboxSubmit(document.querySelector('form.something')).render()
21
+ */
22
+ class CheckboxSubmit {
23
+ constructor(form) {
24
+ this.form = form;
25
+ }
26
+
27
+ async clicked(evt) {
28
+ this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress');
29
+ this.labelTarget.setAttribute('disabled', 'disabled');
30
+ this.checkboxTarget.setAttribute('disabled', 'disabled');
31
+ const response = await fetch(this.formTarget.getAttribute('action'), {
32
+ body: new FormData(this.formTarget),
33
+ method: this.formTarget.getAttribute('method').toUpperCase(),
34
+ headers: {
35
+ 'Accept': 'application/json',
36
+ 'X-Requested-With': 'XMLHttpRequest',
37
+ 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content
26
38
  }
39
+ });
40
+ this.labelTarget.removeAttribute('disabled');
41
+ this.checkboxTarget.removeAttribute('disabled');
42
+ if (response.ok) {
43
+ const json = await response.json();
44
+ this.updateStateFor(!this.checked);
45
+ document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count;
27
46
  } else {
28
- listeners.push('DOMContentLoaded');
47
+ alert('Error');
29
48
  }
49
+ }
30
50
 
31
- return listeners;
51
+ get checked() {
52
+ return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)
32
53
  }
33
- };
34
- }(); // turbolinks triggers page:load events on page transition
35
- // If app isn't using turbolinks, this event will never be triggered, no prob.
36
54
 
55
+ get formTarget() {
56
+ return this.form
57
+ }
37
58
 
38
- Blacklight.listeners().forEach(function (listener) {
39
- document.addEventListener(listener, function () {
40
- Blacklight.activate();
41
- });
42
- });
43
- Blacklight.onLoad(function () {
44
- var elem = document.querySelector('.no-js'); // The "no-js" class may already have been removed because this function is
45
- // run on every turbo:load event, in that case, it won't find an element.
46
-
47
- if (!elem) return;
48
- elem.classList.remove('no-js');
49
- elem.classList.add('js');
50
- });
51
- window.Blacklight = Blacklight;
52
- /*global Bloodhound */
53
-
54
- Blacklight.onLoad(function () {
55
- 'use strict';
56
-
57
- $('[data-autocomplete-enabled="true"]').each(function () {
58
- var $el = $(this);
59
-
60
- if ($el.hasClass('tt-hint')) {
61
- return;
59
+ get labelTarget() {
60
+ return this.form.querySelector('[data-checkboxsubmit-target="label"]')
62
61
  }
63
62
 
64
- var suggestUrl = $el.data().autocompletePath;
65
- var terms = new Bloodhound({
66
- datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
67
- queryTokenizer: Bloodhound.tokenizers.whitespace,
68
- remote: {
69
- url: suggestUrl + '?q=%QUERY',
70
- wildcard: '%QUERY'
71
- }
72
- });
73
- terms.initialize();
74
- $el.typeahead({
75
- hint: true,
76
- highlight: true,
77
- minLength: 2
78
- }, {
79
- name: 'terms',
80
- displayKey: 'term',
81
- source: terms.ttAdapter()
82
- });
83
- });
84
- });
85
-
86
- (function ($) {
87
- //change form submit toggle to checkbox
88
- Blacklight.doBookmarkToggleBehavior = function () {
89
- if (typeof Blacklight.do_bookmark_toggle_behavior == 'function') {
90
- console.warn("do_bookmark_toggle_behavior is deprecated. Use doBookmarkToggleBehavior instead.");
91
- return Blacklight.do_bookmark_toggle_behavior();
63
+ get checkboxTarget() {
64
+ return this.form.querySelector('[data-checkboxsubmit-target="checkbox"]')
92
65
  }
93
66
 
94
- $(Blacklight.doBookmarkToggleBehavior.selector).blCheckboxSubmit({
95
- // cssClass is added to elements added, plus used for id base
96
- cssClass: 'toggle-bookmark',
97
- success: function success(checked, response) {
98
- if (response.bookmarks) {
99
- $('[data-role=bookmark-counter]').text(response.bookmarks.count);
100
- }
101
- }
102
- });
103
- };
67
+ get spanTarget() {
68
+ return this.form.querySelector('[data-checkboxsubmit-target="span"]')
69
+ }
104
70
 
105
- Blacklight.doBookmarkToggleBehavior.selector = 'form.bookmark-toggle';
106
- Blacklight.onLoad(function () {
107
- Blacklight.doBookmarkToggleBehavior();
108
- });
109
- })(jQuery);
110
-
111
- Blacklight.onLoad(function () {
112
- // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
113
- // Safari both do not set focus to a button on button click.
114
- // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
115
- document.querySelectorAll('button.collapse-toggle').forEach(function (button) {
116
- button.addEventListener('click', function () {
117
- event.target.focus();
118
- });
119
- });
120
- });
121
- /* A JQuery plugin (should this be implemented as a widget instead? not sure)
122
- that will convert a "toggle" form, with single submit button to add/remove
123
- something, like used for Bookmarks, into an AJAXy checkbox instead.
124
-
125
- Apply to a form. Does require certain assumption about the form:
126
- 1) The same form 'action' href must be used for both ADD and REMOVE
127
- actions, with the different being the hidden input name="_method"
128
- being set to "put" or "delete" -- that's the Rails method to pretend
129
- to be doing a certain HTTP verb. So same URL, PUT to add, DELETE
130
- to remove. This plugin assumes that.
131
-
132
- Plus, the form this is applied to should provide a data-doc-id
133
- attribute (HTML5-style doc-*) that contains the id/primary key
134
- of the object in question -- used by plugin for a unique value for
135
- DOM id's.
136
-
137
- Uses HTML for a checkbox compatible with Bootstrap 3.
138
-
139
- Pass in options for your class name and labels:
140
- $("form.something").blCheckboxSubmit({
141
- //cssClass is added to elements added, plus used for id base
142
- cssClass: "toggle_my_kinda_form",
143
- error: function() {
144
- #optional callback
145
- },
146
- success: function(after_success_check_state) {
147
- #optional callback
148
- }
149
- });
150
- */
151
-
152
- (function ($) {
153
- $.fn.blCheckboxSubmit = function (argOpts) {
154
- this.each(function () {
155
- var options = $.extend({}, $.fn.blCheckboxSubmit.defaults, argOpts);
156
- var form = $(this);
157
- form.children().hide(); //We're going to use the existing form to actually send our add/removes
158
- //This works conveneintly because the exact same action href is used
159
- //for both bookmarks/$doc_id. But let's take out the irrelevant parts
160
- //of the form to avoid any future confusion.
161
-
162
- form.find('input[type=submit]').remove(); //View needs to set data-doc-id so we know a unique value
163
- //for making DOM id
164
-
165
- var uniqueId = form.attr('data-doc-id') || Math.random(); // if form is currently using method delete to change state,
166
- // then checkbox is currently checked
167
-
168
- var checked = form.find('input[name=_method][value=delete]').length != 0;
169
- var checkbox = $('<input type="checkbox">').addClass(options.cssClass).attr('id', options.cssClass + '_' + uniqueId);
170
- var label = $('<label>').addClass(options.cssClass).attr('for', options.cssClass + '_' + uniqueId).attr('title', form.attr('title') || '');
171
- var span = $('<span>');
172
- label.append(checkbox);
173
- label.append(' ');
174
- label.append(span);
175
- var checkboxDiv = $('<div class="checkbox" />').addClass(options.cssClass).append(label);
176
-
177
- function updateStateFor(state) {
178
- checkbox.prop('checked', state);
179
- label.toggleClass('checked', state);
180
-
181
- if (state) {
182
- //Set the Rails hidden field that fakes an HTTP verb
183
- //properly for current state action.
184
- form.find('input[name=_method]').val('delete');
185
- span.html(form.attr('data-present'));
186
- } else {
187
- form.find('input[name=_method]').val('put');
188
- span.html(form.attr('data-absent'));
189
- }
190
- }
71
+ updateStateFor(state) {
72
+ this.checkboxTarget.checked = state;
191
73
 
192
- form.append(checkboxDiv);
193
- updateStateFor(checked);
194
- checkbox.click(function () {
195
- span.html(form.attr('data-inprogress'));
196
- label.attr('disabled', 'disabled');
197
- checkbox.attr('disabled', 'disabled');
198
- $.ajax({
199
- url: form.attr('action'),
200
- dataType: 'json',
201
- type: form.attr('method').toUpperCase(),
202
- data: form.serialize(),
203
- error: function error() {
204
- label.removeAttr('disabled');
205
- checkbox.removeAttr('disabled');
206
- options.error.call();
207
- },
208
- success: function success(data, status, xhr) {
209
- //if app isn't running at all, xhr annoyingly
210
- //reports success with status 0.
211
- if (xhr.status != 0) {
212
- checked = !checked;
213
- updateStateFor(checked);
214
- label.removeAttr('disabled');
215
- checkbox.removeAttr('disabled');
216
- options.success.call(form, checked, xhr.responseJSON);
217
- } else {
218
- label.removeAttr('disabled');
219
- checkbox.removeAttr('disabled');
220
- options.error.call();
221
- }
222
- }
223
- });
224
- return false;
225
- }); //checkbox.click
226
- }); //this.each
74
+ if (state) {
75
+ this.labelTarget.classList.add('checked');
76
+ //Set the Rails hidden field that fakes an HTTP verb
77
+ //properly for current state action.
78
+ this.formTarget.querySelector('input[name=_method]').value = 'delete';
79
+ this.spanTarget.innerHTML = this.form.getAttribute('data-present');
80
+ } else {
81
+ this.labelTarget.classList.remove('checked');
82
+ this.formTarget.querySelector('input[name=_method]').value = 'put';
83
+ this.spanTarget.innerHTML = this.form.getAttribute('data-absent');
84
+ }
85
+ }
86
+ }
227
87
 
228
- return this;
88
+ const BookmarkToggle = (e) => {
89
+ if (e.target.matches('[data-checkboxsubmit-target="checkbox"]')) {
90
+ const form = e.target.closest('form');
91
+ if (form) new CheckboxSubmit(form).clicked(e);
92
+ }
229
93
  };
230
94
 
231
- $.fn.blCheckboxSubmit.defaults = {
232
- //cssClass is added to elements added, plus used for id base
233
- cssClass: 'blCheckboxSubmit',
234
- error: function error() {
235
- alert("Error");
236
- },
237
- success: function success() {} //callback
95
+ document.addEventListener('click', BookmarkToggle);
238
96
 
97
+ const ButtonFocus = (e) => {
98
+ // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
99
+ // Safari both do not set focus to a button on button click.
100
+ // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
101
+ if (e.target.matches('[data-toggle="collapse"]') || e.target.matches('[data-bs-toggle="collapse"]')) {
102
+ e.target.focus();
103
+ }
239
104
  };
240
- })(jQuery);
241
- /*global Blacklight */
242
105
 
106
+ document.addEventListener('click', ButtonFocus);
243
107
 
244
- 'use strict';
108
+ /*
109
+ The blacklight modal plugin can display some interactions inside a Bootstrap
110
+ modal window, including some multi-page interactions.
245
111
 
246
- Blacklight.doResizeFacetLabelsAndCounts = function () {
247
- // adjust width of facet columns to fit their contents
248
- function longer(a, b) {
249
- return b.textContent.length - a.textContent.length;
250
- }
112
+ It supports unobtrusive Javascript, where a link or form that would have caused
113
+ a new page load is changed to display it's results inside a modal dialog,
114
+ by this plugin. The plugin assumes there is a Bootstrap modal div
115
+ on the page with id #blacklight-modal to use as the modal -- the standard Blacklight
116
+ layout provides this.
251
117
 
252
- document.querySelectorAll('.facet-values, .pivot-facet').forEach(function (elem) {
253
- var nodes = elem.querySelectorAll('.facet-count'); // TODO: when we drop ie11 support, this can become the spread operator:
118
+ To make a link or form have their results display inside a modal, add
119
+ `data-blacklight-modal="trigger"` to the link or form. (Note, form itself not submit input)
120
+ With Rails link_to helper, you'd do that like:
254
121
 
255
- var longest = Array.from(nodes).sort(longer)[0];
122
+ link_to something, link, data: { blacklight_modal: "trigger" }
256
123
 
257
- if (longest && longest.textContent) {
258
- var width = longest.textContent.length + 1 + 'ch';
259
- elem.querySelector('.facet-count').style.width = width;
260
- }
261
- });
262
- };
263
-
264
- Blacklight.onLoad(function () {
265
- Blacklight.doResizeFacetLabelsAndCounts();
266
- });
267
- /*
268
- The blacklight modal plugin can display some interactions inside a Bootstrap
269
- modal window, including some multi-page interactions.
270
-
271
- It supports unobtrusive Javascript, where a link or form that would have caused
272
- a new page load is changed to display it's results inside a modal dialog,
273
- by this plugin. The plugin assumes there is a Bootstrap modal div
274
- on the page with id #blacklight-modal to use as the modal -- the standard Blacklight
275
- layout provides this.
276
-
277
- To make a link or form have their results display inside a modal, add
278
- `data-blacklight-modal="trigger"` to the link or form. (Note, form itself not submit input)
279
- With Rails link_to helper, you'd do that like:
280
-
281
- link_to something, link, data: { blacklight_modal: "trigger" }
282
-
283
- The results of the link href or form submit will be displayed inside
284
- a modal -- they should include the proper HTML markup for a bootstrap modal's
285
- contents. Also, you ordinarily won't want the Rails template with wrapping
286
- navigational elements to be used. The Rails controller could suppress
287
- the layout when a JS AJAX request is detected, OR the response
288
- can include a `<div data-blacklight-modal="container">` -- only the contents
289
- of the container will be placed inside the modal, the rest of the
290
- page will be ignored.
291
-
292
- If you'd like to have a link or button that closes the modal,
293
- you can just add a `data-dismiss="modal"` to the link,
294
- standard Bootstrap convention. But you can also have
295
- an href on this link for non-JS contexts, we'll make sure
296
- inside the modal it closes the modal and the link is NOT followed.
297
-
298
- Link or forms inside the modal will ordinarily cause page loads
299
- when they are triggered. However, if you'd like their results
300
- to stay within the modal, just add `data-blacklight-modal="preserve"`
301
- to the link or form.
302
-
303
- Here's an example of what might be returned, demonstrating most of the devices available:
304
-
305
- <div data-blacklight-modal="container">
306
- <div class="modal-header">
307
- <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
308
- <h3 class="modal-title">Request Placed</h3>
309
- </div>
124
+ The results of the link href or form submit will be displayed inside
125
+ a modal -- they should include the proper HTML markup for a bootstrap modal's
126
+ contents. Also, you ordinarily won't want the Rails template with wrapping
127
+ navigational elements to be used. The Rails controller could suppress
128
+ the layout when a JS AJAX request is detected, OR the response
129
+ can include a `<div data-blacklight-modal="container">` -- only the contents
130
+ of the container will be placed inside the modal, the rest of the
131
+ page will be ignored.
310
132
 
311
- <div class="modal-body">
312
- <p>Some message</p>
313
- <%= link_to "This result will still be within modal", some_link, data: { blacklight_modal: "preserve" } %>
314
- </div>
133
+ Link or forms inside the modal will ordinarily cause page loads
134
+ when they are triggered. However, if you'd like their results
135
+ to stay within the modal, just add `data-blacklight-modal="preserve"`
136
+ to the link or form.
315
137
 
138
+ Here's an example of what might be returned, demonstrating most of the devices available:
316
139
 
317
- <div class="modal-footer">
318
- <%= link_to "Close the modal", request_done_path, class: "submit button dialog-close", data: { dismiss: "modal" } %>
319
- </div>
320
- </div>
321
-
322
-
323
- One additional feature. If the content returned from the AJAX modal load
324
- has an element with `data-blacklight-modal=close`, that will trigger the modal
325
- to be closed. And if this element includes a node with class "flash_messages",
326
- the flash-messages node will be added to the main page inside #main-flahses.
327
-
328
- == Events
329
-
330
- We'll send out an event 'loaded.blacklight.blacklight-modal' with the #blacklight-modal
331
- dialog as the target, right after content is loaded into the modal but before
332
- it is shown (if not already a shown modal). In an event handler, you can
333
- inspect loaded content by looking inside $(this). If you call event.preventDefault(),
334
- we won't 'show' the dialog (although it may already have been shown, you may want to
335
- $(this).modal("hide") if you want to ensure hidden/closed.
336
-
337
- The data-blacklight-modal=close behavior is implemented with this event, see for example.
338
- */
339
- // We keep all our data in Blacklight.modal object.
340
- // Create lazily if someone else created first.
341
-
342
- if (Blacklight.modal === undefined) {
343
- Blacklight.modal = {};
344
- } // a Bootstrap modal div that should be already on the page hidden
345
-
346
-
347
- Blacklight.modal.modalSelector = '#blacklight-modal'; // Trigger selectors identify forms or hyperlinks that should open
348
- // inside a modal dialog.
349
-
350
- Blacklight.modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';
351
- Blacklight.modal.triggerFormSelector = 'form[data-blacklight-modal~=trigger]'; // preserve selectors identify forms or hyperlinks that, if activated already
352
- // inside a modal dialog, should have destinations remain inside the modal -- but
353
- // won't trigger a modal if not already in one.
354
- //
355
- // No need to repeat selectors from trigger selectors, those will already
356
- // be preserved. MUST be manually prefixed with the modal selector,
357
- // so they only apply to things inside a modal.
358
-
359
- Blacklight.modal.preserveLinkSelector = Blacklight.modal.modalSelector + ' a[data-blacklight-modal~=preserve]';
360
- Blacklight.modal.containerSelector = '[data-blacklight-modal~=container]';
361
- Blacklight.modal.modalCloseSelector = '[data-blacklight-modal~=close]'; // Called on fatal failure of ajax load, function returns content
362
- // to show to user in modal. Right now called only for extreme
363
- // network errors.
364
-
365
- Blacklight.modal.onFailure = function (jqXHR, textStatus, errorThrown) {
366
- console.error('Server error:', this.url, jqXHR.status, errorThrown);
367
- var contents = '<div class="modal-header">' + '<div class="modal-title">There was a problem with your request.</div>' + '<button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" aria-label="Close">' + ' <span aria-hidden="true">&times;</span>' + '</button></div>' + ' <div class="modal-body"><p>Expected a successful response from the server, but got an error</p>' + '<pre>' + this.type + ' ' + this.url + "\n" + jqXHR.status + ': ' + errorThrown + '</pre></div>';
368
- $(Blacklight.modal.modalSelector).find('.modal-content').html(contents);
369
- Blacklight.modal.show();
370
- };
371
-
372
- Blacklight.modal.receiveAjax = function (contents) {
373
- // does it have a data- selector for container?
374
- // important we don't execute script tags, we shouldn't.
375
- // code modelled off of JQuery ajax.load. https://github.com/jquery/jquery/blob/main/src/ajax/load.js?source=c#L62
376
- var container = $('<div>').append(jQuery.parseHTML(contents)).find(Blacklight.modal.containerSelector).first();
377
-
378
- if (container.length !== 0) {
379
- contents = container.html();
380
- }
140
+ <div data-blacklight-modal="container">
141
+ <div class="modal-header">
142
+ <button type="button" class="close" data-bl-dismiss="modal" aria-hidden="true">×</button>
143
+ <h3 class="modal-title">Request Placed</h3>
144
+ </div>
381
145
 
382
- $(Blacklight.modal.modalSelector).find('.modal-content').html(contents); // send custom event with the modal dialog div as the target
383
-
384
- var e = $.Event('loaded.blacklight.blacklight-modal');
385
- $(Blacklight.modal.modalSelector).trigger(e); // if they did preventDefault, don't show the dialog
386
-
387
- if (e.isDefaultPrevented()) return;
388
- Blacklight.modal.show();
389
- };
390
-
391
- Blacklight.modal.modalAjaxLinkClick = function (e) {
392
- e.preventDefault();
393
- $.ajax({
394
- url: $(this).attr('href')
395
- }).fail(Blacklight.modal.onFailure).done(Blacklight.modal.receiveAjax);
396
- };
397
-
398
- Blacklight.modal.modalAjaxFormSubmit = function (e) {
399
- e.preventDefault();
400
- $.ajax({
401
- url: $(this).attr('action'),
402
- data: $(this).serialize(),
403
- type: $(this).attr('method') // POST
404
-
405
- }).fail(Blacklight.modal.onFailure).done(Blacklight.modal.receiveAjax);
406
- };
407
-
408
- Blacklight.modal.setupModal = function () {
409
- // Event indicating blacklight is setting up a modal link,
410
- // you can catch it and call e.preventDefault() to abort
411
- // setup.
412
- var e = $.Event('setup.blacklight.blacklight-modal');
413
- $('body').trigger(e);
414
- if (e.isDefaultPrevented()) return; // Register both trigger and preserve selectors in ONE event handler, combining
415
- // into one selector with a comma, so if something matches BOTH selectors, it
416
- // still only gets the event handler called once.
417
-
418
- $('body').on('click', Blacklight.modal.triggerLinkSelector + ', ' + Blacklight.modal.preserveLinkSelector, Blacklight.modal.modalAjaxLinkClick);
419
- $('body').on('submit', Blacklight.modal.triggerFormSelector + ', ' + Blacklight.modal.preserveFormSelector, Blacklight.modal.modalAjaxFormSubmit); // Catch our own custom loaded event to implement data-blacklight-modal=closed
420
-
421
- $('body').on('loaded.blacklight.blacklight-modal', Blacklight.modal.checkCloseModal); // we support doing data-dismiss=modal on a <a> with a href for non-ajax
422
- // use, we need to suppress following the a's href that's there for
423
- // non-JS contexts.
424
-
425
- $('body').on('click', Blacklight.modal.modalSelector + ' a[data-dismiss~=modal]', function (e) {
426
- e.preventDefault();
427
- });
428
- }; // A function used as an event handler on loaded.blacklight.blacklight-modal
429
- // to catch contained data-blacklight-modal=closed directions
146
+ <div class="modal-body">
147
+ <p>Some message</p>
148
+ <%= link_to "This result will still be within modal", some_link, data: { blacklight_modal: "preserve" } %>
149
+ </div>
430
150
 
431
151
 
432
- Blacklight.modal.checkCloseModal = function (event) {
433
- if ($(event.target).find(Blacklight.modal.modalCloseSelector).length) {
434
- var modalFlashes = $(this).find('.flash_messages');
435
- Blacklight.modal.hide(event.target);
436
- event.preventDefault();
437
- var mainFlashes = $('#main-flashes');
438
- mainFlashes.append(modalFlashes);
439
- modalFlashes.fadeIn(500);
440
- }
441
- };
152
+ <div class="modal-footer">
153
+ <button type="button" class="btn btn-secondary" data-bl-dismiss="modal">Close</button>
154
+ </div>
155
+ </div>
442
156
 
443
- Blacklight.modal.hide = function (el) {
444
- if (typeof bootstrap !== 'undefined' && typeof bootstrap.Modal !== 'undefined' && bootstrap.Modal.VERSION >= "5") {
445
- bootstrap.Modal.getOrCreateInstance(el || document.querySelector(Blacklight.modal.modalSelector)).hide();
446
- } else {
447
- $(el || Blacklight.modal.modalSelector).modal('hide');
448
- }
449
- };
450
157
 
451
- Blacklight.modal.show = function (el) {
452
- if (typeof bootstrap !== 'undefined' && typeof bootstrap.Modal !== 'undefined' && bootstrap.Modal.VERSION >= "5") {
453
- bootstrap.Modal.getOrCreateInstance(el || document.querySelector(Blacklight.modal.modalSelector)).show();
454
- } else {
455
- $(el || Blacklight.modal.modalSelector).modal('show');
456
- }
457
- };
158
+ One additional feature. If the content returned from the AJAX form submission
159
+ can be a turbo-stream that defines some HTML fragementsand where on the page to put them:
160
+ https://turbo.hotwired.dev/handbook/streams
161
+ */
162
+
163
+ const Modal = (() => {
164
+ const modal = {};
165
+
166
+ // a Bootstrap modal div that should be already on the page hidden
167
+ modal.modalSelector = '#blacklight-modal';
168
+
169
+ // Trigger selectors identify forms or hyperlinks that should open
170
+ // inside a modal dialog.
171
+ modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';
172
+
173
+ // preserve selectors identify forms or hyperlinks that, if activated already
174
+ // inside a modal dialog, should have destinations remain inside the modal -- but
175
+ // won't trigger a modal if not already in one.
176
+ //
177
+ // No need to repeat selectors from trigger selectors, those will already
178
+ // be preserved. MUST be manually prefixed with the modal selector,
179
+ // so they only apply to things inside a modal.
180
+ modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';
181
+
182
+ modal.containerSelector = '[data-blacklight-modal~=container]';
183
+
184
+ // Called on fatal failure of ajax load, function returns content
185
+ // to show to user in modal. Right now called only for network errors.
186
+ modal.onFailure = function (error) {
187
+ console.error('Server error:', this.url, error);
188
+
189
+ const contents = `<div class="modal-header">
190
+ <div class="modal-title">There was a problem with your request.</div>
191
+ <button type="button" class="blacklight-modal-close btn-close close" data-bl-dismiss="modal" aria-label="Close">
192
+ <span aria-hidden="true" class="visually-hidden">&times;</span>
193
+ </button>
194
+ </div>
195
+ <div class="modal-body">
196
+ <p>Expected a successful response from the server, but got an error</p>
197
+ <pre>${this.url}\n${error}</pre>
198
+ </div>`;
199
+
200
+ document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents;
201
+
202
+ modal.show();
203
+ };
204
+
205
+ // Add the passed in contents to the modal and display it.
206
+ modal.receiveAjax = function (contents) {
207
+ const domparser = new DOMParser();
208
+ const dom = domparser.parseFromString(contents, "text/html");
209
+ // If there is a containerSelector on the document, use its children.
210
+ let elements = dom.querySelectorAll(`${modal.containerSelector} > *`);
211
+ if (elements.length == 0) {
212
+ // If the containerSelector wasn't found, use the whole document
213
+ elements = dom.body.childNodes;
214
+ }
458
215
 
459
- Blacklight.onLoad(function () {
460
- Blacklight.modal.setupModal();
461
- });
216
+ document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements);
217
+
218
+ modal.show();
219
+ };
220
+
221
+
222
+ modal.modalAjaxLinkClick = function(e) {
223
+ e.preventDefault();
224
+ const href = e.target.getAttribute('href');
225
+ fetch(href)
226
+ .then(response => {
227
+ if (!response.ok) {
228
+ throw new TypeError("Request failed");
229
+ }
230
+ return response.text();
231
+ })
232
+ .then(data => modal.receiveAjax(data))
233
+ .catch(error => modal.onFailure(error));
234
+ };
235
+
236
+ modal.setupModal = function() {
237
+ // Register both trigger and preserve selectors in ONE event handler, combining
238
+ // into one selector with a comma, so if something matches BOTH selectors, it
239
+ // still only gets the event handler called once.
240
+ document.addEventListener('click', (e) => {
241
+ if (e.target.closest(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))
242
+ modal.modalAjaxLinkClick(e);
243
+ else if (e.target.closest('[data-bl-dismiss="modal"]'))
244
+ modal.hide();
245
+ });
246
+ };
247
+
248
+ modal.hide = function (el) {
249
+ const dom = document.querySelector(modal.modalSelector);
250
+
251
+ if (!dom.open) return
252
+ dom.close();
253
+ };
254
+
255
+ modal.show = function(el) {
256
+ const dom = document.querySelector(modal.modalSelector);
257
+
258
+ if (dom.open) return
259
+ dom.showModal();
260
+ };
261
+
262
+ modal.setupModal();
263
+
264
+ return modal;
265
+ })();
266
+
267
+ const SearchContext = (e) => {
268
+ if (e.target.matches('[data-context-href]')) {
269
+ SearchContext.handleSearchContextMethod.call(e.target, e);
270
+ }
271
+ };
462
272
 
463
- Blacklight.doSearchContextBehavior = function () {
464
- if (typeof Blacklight.do_search_context_behavior == 'function') {
465
- console.warn("do_search_context_behavior is deprecated. Use doSearchContextBehavior instead.");
466
- return Blacklight.do_search_context_behavior();
467
- }
273
+ SearchContext.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content;
274
+ SearchContext.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content;
468
275
 
469
- var elements = document.querySelectorAll('a[data-context-href]'); // Equivalent to Array.from(), but supports ie11
276
+ // this is the Rails.handleMethod with a couple adjustments, described inline:
277
+ // first, we're attaching this directly to the event handler, so we can check for meta-keys
278
+ SearchContext.handleSearchContextMethod = function(event) {
279
+ const link = this;
470
280
 
471
- var nodes = Array.prototype.slice.call(elements);
472
- nodes.forEach(function (element) {
473
- element.addEventListener('click', function (e) {
474
- Blacklight.handleSearchContextMethod.call(e.currentTarget, e);
475
- });
476
- });
477
- };
281
+ // instead of using the normal href, we need to use the context href instead
282
+ let href = link.getAttribute('data-context-href');
283
+ let target = link.getAttribute('target');
284
+ let csrfToken = SearchContext.csrfToken();
285
+ let csrfParam = SearchContext.csrfParam();
286
+ let form = document.createElement('form');
287
+ form.method = 'post';
288
+ form.action = href;
478
289
 
479
- Blacklight.csrfToken = function () {
480
- var _document$querySelect;
481
290
 
482
- return (_document$querySelect = document.querySelector('meta[name=csrf-token]')) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.content;
483
- };
291
+ let formContent = `<input name="_method" value="post" type="hidden" />
292
+ <input name="redirect" value="${link.getAttribute('href')}" type="hidden" />`;
484
293
 
485
- Blacklight.csrfParam = function () {
486
- var _document$querySelect2;
294
+ // check for meta keys.. if set, we should open in a new tab
295
+ if(event.metaKey || event.ctrlKey) {
296
+ target = '_blank';
297
+ }
487
298
 
488
- return (_document$querySelect2 = document.querySelector('meta[name=csrf-param]')) === null || _document$querySelect2 === void 0 ? void 0 : _document$querySelect2.content;
489
- }; // this is the Rails.handleMethod with a couple adjustments, described inline:
490
- // first, we're attaching this directly to the event handler, so we can check for meta-keys
299
+ if (csrfParam !== undefined && csrfToken !== undefined) {
300
+ formContent += `<input name="${csrfParam}" value="${csrfToken}" type="hidden" />`;
301
+ }
491
302
 
303
+ // Must trigger submit by click on a button, else "submit" event handler won't work!
304
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
305
+ formContent += '<input type="submit" />';
492
306
 
493
- Blacklight.handleSearchContextMethod = function (event) {
494
- if (typeof Blacklight.handle_search_context_method == 'function') {
495
- console.warn("handle_search_context_method is deprecated. Use handleSearchContextMethod instead.");
496
- return Blacklight.handle_search_context_method(event);
497
- }
307
+ if (target) { form.setAttribute('target', target); }
498
308
 
499
- var link = this; // instead of using the normal href, we need to use the context href instead
309
+ form.style.display = 'none';
310
+ form.innerHTML = formContent;
311
+ document.body.appendChild(form);
312
+ form.querySelector('[type="submit"]').click();
500
313
 
501
- var href = link.getAttribute('data-context-href');
502
- var target = link.getAttribute('target');
503
- var csrfToken = Blacklight.csrfToken();
504
- var csrfParam = Blacklight.csrfParam();
505
- var form = document.createElement('form');
506
- form.method = 'post';
507
- form.action = href;
508
- var formContent = "<input name=\"_method\" value=\"post\" type=\"hidden\" />\n <input name=\"redirect\" value=\"".concat(link.getAttribute('href'), "\" type=\"hidden\" />"); // check for meta keys.. if set, we should open in a new tab
314
+ event.preventDefault();
315
+ };
509
316
 
510
- if (event.metaKey || event.ctrlKey) {
511
- target = '_blank';
512
- }
317
+ document.addEventListener('click', SearchContext);
513
318
 
514
- if (csrfParam !== undefined && csrfToken !== undefined) {
515
- formContent += "<input name=\"".concat(csrfParam, "\" value=\"").concat(csrfToken, "\" type=\"hidden\" />");
516
- } // Must trigger submit by click on a button, else "submit" event handler won't work!
517
- // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
319
+ const Blacklight = function() {
320
+ const buffer = new Array;
321
+ return {
322
+ onLoad: function(func) {
323
+ buffer.push(func);
324
+ },
518
325
 
326
+ activate: function() {
327
+ for(let i = 0; i < buffer.length; i++) {
328
+ buffer[i].call();
329
+ }
330
+ },
331
+
332
+ listeners: function () {
333
+ const listeners = [];
334
+ if (typeof Turbo !== 'undefined') {
335
+ listeners.push('turbo:load', 'turbo:frame-load');
336
+ } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
337
+ // Turbolinks 5
338
+ if (Turbolinks.BrowserAdapter) {
339
+ listeners.push('turbolinks:load');
340
+ } else {
341
+ // Turbolinks < 5
342
+ listeners.push('page:load', 'DOMContentLoaded');
343
+ }
344
+ } else {
345
+ listeners.push('DOMContentLoaded');
346
+ }
519
347
 
520
- formContent += '<input type="submit" />';
348
+ return listeners;
349
+ }
350
+ };
351
+ }();
352
+
353
+ // turbolinks triggers page:load events on page transition
354
+ // If app isn't using turbolinks, this event will never be triggered, no prob.
355
+ Blacklight.listeners().forEach(function(listener) {
356
+ document.addEventListener(listener, function() {
357
+ Blacklight.activate();
358
+ });
359
+ });
521
360
 
522
- if (target) {
523
- form.setAttribute('target', target);
524
- }
361
+ Blacklight.onLoad(function () {
362
+ const elem = document.querySelector('.no-js');
525
363
 
526
- form.style.display = 'none';
527
- form.innerHTML = formContent;
528
- document.body.appendChild(form);
529
- form.querySelector('[type="submit"]').click();
530
- event.preventDefault();
531
- event.stopPropagation();
532
- };
364
+ // The "no-js" class may already have been removed because this function is
365
+ // run on every turbo:load event, in that case, it won't find an element.
366
+ if (!elem) return;
367
+
368
+ elem.classList.remove('no-js');
369
+ elem.classList.add('js');
370
+ });
371
+
372
+ const index = {
373
+ BookmarkToggle,
374
+ ButtonFocus,
375
+ Modal,
376
+ SearchContext,
377
+ Core: Blacklight,
378
+ onLoad: Blacklight.onLoad
379
+ };
533
380
 
534
- Blacklight.onLoad(function () {
535
- Blacklight.doSearchContextBehavior();
536
- });
381
+ return index;
537
382
 
383
+ }));
384
+ //# sourceMappingURL=blacklight.js.map