blacklight 7.37.0 → 8.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (400) hide show
  1. checksums.yaml +4 -4
  2. data/.env +2 -3
  3. data/.github/workflows/ruby.yml +33 -133
  4. data/.rubocop.yml +241 -2
  5. data/.rubocop_todo.yml +214 -270
  6. data/Gemfile +7 -10
  7. data/README.md +11 -3
  8. data/VERSION +1 -1
  9. data/app/assets/javascripts/blacklight/blacklight.esm.js +384 -0
  10. data/app/assets/javascripts/blacklight/blacklight.esm.js.map +1 -0
  11. data/app/assets/javascripts/blacklight/blacklight.js +374 -493
  12. data/app/assets/javascripts/blacklight/blacklight.js.map +1 -0
  13. data/app/assets/stylesheets/blacklight/_autocomplete.scss +25 -0
  14. data/app/assets/stylesheets/blacklight/_blacklight_base.scss +1 -1
  15. data/app/assets/stylesheets/blacklight/_bookmark.scss +16 -0
  16. data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +8 -0
  17. data/app/assets/stylesheets/blacklight/_facets.scss +72 -44
  18. data/app/assets/stylesheets/blacklight/_header.scss +0 -15
  19. data/app/assets/stylesheets/blacklight/_icons.scss +0 -14
  20. data/app/assets/stylesheets/blacklight/_mixins.scss +20 -0
  21. data/app/assets/stylesheets/blacklight/_modal.scss +8 -2
  22. data/app/assets/stylesheets/blacklight/_search_form.scss +30 -3
  23. data/app/assets/stylesheets/blacklight/_search_history.scss +10 -1
  24. data/app/assets/stylesheets/blacklight/_search_results.scss +6 -2
  25. data/app/assets/stylesheets/blacklight/blacklight_defaults.scss +4 -1
  26. data/app/builders/blacklight/action_builder.rb +18 -9
  27. data/app/components/blacklight/advanced_search_form_component.html.erb +2 -2
  28. data/app/components/blacklight/advanced_search_form_component.rb +8 -10
  29. data/app/components/blacklight/constraints_component.rb +27 -37
  30. data/app/components/blacklight/document/action_component.rb +9 -9
  31. data/app/components/blacklight/document/actions_component.rb +2 -2
  32. data/app/components/blacklight/document/bookmark_component.html.erb +9 -0
  33. data/app/components/blacklight/document/bookmark_component.rb +4 -2
  34. data/app/components/blacklight/document/citation_component.rb +5 -3
  35. data/app/components/blacklight/document/group_component.rb +7 -3
  36. data/app/components/blacklight/document/sidebar_component.html.erb +2 -0
  37. data/app/components/blacklight/document/sidebar_component.rb +16 -0
  38. data/app/components/blacklight/document/thumbnail_component.html.erb +2 -7
  39. data/app/components/blacklight/document/thumbnail_component.rb +1 -9
  40. data/app/components/blacklight/document_component.html.erb +4 -0
  41. data/app/components/blacklight/document_component.rb +14 -53
  42. data/app/components/blacklight/document_metadata_component.html.erb +2 -4
  43. data/app/components/blacklight/document_metadata_component.rb +5 -10
  44. data/app/components/blacklight/document_title_component.rb +4 -1
  45. data/app/components/blacklight/facet_component.rb +1 -1
  46. data/app/components/blacklight/facet_field_checkboxes_component.html.erb +3 -3
  47. data/app/components/blacklight/facet_field_checkboxes_component.rb +1 -1
  48. data/app/components/blacklight/facet_field_component.html.erb +4 -5
  49. data/app/components/blacklight/facet_field_component.rb +9 -2
  50. data/app/components/blacklight/facet_field_list_component.html.erb +3 -3
  51. data/app/components/blacklight/facet_field_list_component.rb +37 -5
  52. data/app/components/blacklight/facet_field_no_layout_component.rb +0 -2
  53. data/app/components/blacklight/facet_item_component.rb +1 -43
  54. data/app/components/blacklight/facet_item_pivot_component.rb +21 -23
  55. data/app/components/blacklight/header_component.rb +1 -1
  56. data/app/components/blacklight/hidden_search_state_component.rb +1 -2
  57. data/app/components/blacklight/icons/legacy_icon_component.rb +30 -0
  58. data/app/components/blacklight/icons/list_component.rb +16 -0
  59. data/app/components/blacklight/icons/search_component.rb +16 -0
  60. data/app/components/blacklight/metadata_field_component.html.erb +2 -2
  61. data/app/components/blacklight/metadata_field_component.rb +24 -8
  62. data/app/components/blacklight/metadata_field_layout_component.rb +4 -25
  63. data/app/components/blacklight/response/facet_group_component.html.erb +3 -5
  64. data/app/components/blacklight/response/facet_group_component.rb +29 -8
  65. data/app/components/blacklight/response/pagination_component.html.erb +1 -1
  66. data/app/components/blacklight/response/pagination_component.rb +1 -5
  67. data/app/components/blacklight/response/sort_component.rb +1 -1
  68. data/app/components/blacklight/response/spellcheck_component.rb +17 -6
  69. data/app/components/blacklight/response/view_type_button_component.rb +2 -6
  70. data/app/components/blacklight/response/view_type_component.rb +3 -5
  71. data/app/components/blacklight/search/sidebar_component.html.erb +8 -0
  72. data/app/components/blacklight/search/sidebar_component.rb +17 -0
  73. data/app/components/blacklight/search_bar_component.html.erb +20 -15
  74. data/app/components/blacklight/search_bar_component.rb +2 -16
  75. data/app/components/blacklight/search_button_component.rb +3 -3
  76. data/app/components/blacklight/search_context_component.rb +43 -9
  77. data/app/components/blacklight/search_header_component.html.erb +2 -0
  78. data/app/components/blacklight/search_header_component.rb +6 -0
  79. data/app/components/blacklight/start_over_button_component.rb +5 -3
  80. data/app/components/blacklight/system/dropdown_component.rb +2 -2
  81. data/app/components/blacklight/system/flash_message_component.html.erb +3 -1
  82. data/app/components/blacklight/system/flash_message_component.rb +11 -2
  83. data/app/components/blacklight/system/modal_component.html.erb +1 -1
  84. data/app/components/blacklight/system/modal_component.rb +0 -2
  85. data/app/components/blacklight/top_navbar_component.html.erb +1 -1
  86. data/app/components/blacklight/top_navbar_component.rb +0 -4
  87. data/app/controllers/bookmarks_controller.rb +1 -0
  88. data/app/controllers/catalog_controller.rb +1 -0
  89. data/app/controllers/concerns/blacklight/bookmarks.rb +10 -9
  90. data/app/controllers/concerns/blacklight/catalog.rb +21 -83
  91. data/app/controllers/concerns/blacklight/controller.rb +3 -41
  92. data/app/controllers/concerns/blacklight/search_context.rb +25 -7
  93. data/app/controllers/concerns/blacklight/search_history.rb +2 -0
  94. data/app/controllers/concerns/blacklight/searchable.rb +12 -1
  95. data/app/controllers/concerns/blacklight/token_based_user.rb +13 -1
  96. data/app/controllers/search_history_controller.rb +1 -0
  97. data/app/helpers/blacklight/blacklight_helper_behavior.rb +12 -310
  98. data/app/helpers/blacklight/catalog_helper_behavior.rb +22 -137
  99. data/app/helpers/blacklight/component_helper_behavior.rb +2 -53
  100. data/app/helpers/blacklight/configuration_helper_behavior.rb +2 -117
  101. data/app/helpers/blacklight/facets_helper_behavior.rb +4 -321
  102. data/app/helpers/blacklight/icon_helper_behavior.rb +5 -4
  103. data/app/helpers/blacklight/layout_helper_behavior.rb +1 -0
  104. data/app/helpers/blacklight/render_partials_helper_behavior.rb +11 -31
  105. data/app/helpers/blacklight/url_helper_behavior.rb +13 -97
  106. data/app/helpers/blacklight_helper.rb +1 -0
  107. data/app/helpers/catalog_helper.rb +1 -0
  108. data/app/javascript/blacklight/bookmark_toggle.js +13 -19
  109. data/app/javascript/blacklight/button_focus.js +12 -10
  110. data/app/javascript/blacklight/checkbox_submit.js +68 -123
  111. data/app/javascript/blacklight/core.js +5 -7
  112. data/app/javascript/blacklight/index.js +13 -0
  113. data/app/javascript/blacklight/modal.js +99 -164
  114. data/app/javascript/blacklight/modalForm.js +60 -0
  115. data/app/javascript/blacklight/search_context.js +46 -54
  116. data/app/models/blacklight/facet_paginator.rb +3 -2
  117. data/app/models/blacklight/icon.rb +4 -2
  118. data/app/models/bookmark.rb +0 -2
  119. data/app/models/concerns/blacklight/configurable.rb +5 -4
  120. data/app/models/concerns/blacklight/document/active_model_shim.rb +1 -10
  121. data/app/models/concerns/blacklight/document/cache_key.rb +1 -0
  122. data/app/models/concerns/blacklight/document/dublin_core.rb +2 -1
  123. data/app/models/concerns/blacklight/document/email.rb +1 -0
  124. data/app/models/concerns/blacklight/document/export.rb +2 -1
  125. data/app/models/concerns/blacklight/document/extensions.rb +1 -0
  126. data/app/models/concerns/blacklight/document/schema_org.rb +1 -0
  127. data/app/models/concerns/blacklight/document/semantic_fields.rb +1 -0
  128. data/app/models/concerns/blacklight/document/sms.rb +1 -0
  129. data/app/models/concerns/blacklight/suggest/response.rb +1 -0
  130. data/app/models/concerns/blacklight/user.rb +17 -8
  131. data/app/models/record_mailer.rb +13 -12
  132. data/app/models/search.rb +1 -7
  133. data/app/models/solr_document.rb +1 -0
  134. data/app/presenters/blacklight/clause_presenter.rb +1 -1
  135. data/app/presenters/blacklight/document_presenter.rb +13 -21
  136. data/app/presenters/blacklight/facet_field_presenter.rb +39 -14
  137. data/app/presenters/blacklight/facet_grouped_item_presenter.rb +1 -5
  138. data/app/presenters/blacklight/facet_item_pivot_presenter.rb +60 -0
  139. data/app/presenters/blacklight/facet_item_presenter.rb +3 -9
  140. data/app/presenters/blacklight/field_presenter.rb +1 -0
  141. data/app/presenters/blacklight/index_presenter.rb +2 -40
  142. data/app/presenters/blacklight/json_presenter.rb +7 -5
  143. data/app/presenters/blacklight/rendering/link_to_facet.rb +2 -5
  144. data/app/presenters/blacklight/show_presenter.rb +1 -9
  145. data/app/presenters/blacklight/thumbnail_presenter.rb +1 -1
  146. data/app/services/blacklight/bookmarks_search_builder.rb +22 -0
  147. data/app/services/blacklight/field_retriever.rb +12 -21
  148. data/app/services/blacklight/search_service.rb +8 -7
  149. data/app/values/blacklight/types.rb +0 -18
  150. data/app/views/bookmarks/_clear_bookmarks_widget.html.erb +8 -1
  151. data/app/views/bookmarks/_tools.html.erb +7 -12
  152. data/app/views/catalog/_advanced_search_form.html.erb +0 -1
  153. data/app/views/catalog/_bookmark_control.html.erb +1 -1
  154. data/app/views/catalog/_citation.html.erb +1 -1
  155. data/app/views/catalog/_constraints.html.erb +1 -14
  156. data/app/views/catalog/_document.atom.builder +12 -14
  157. data/app/views/catalog/_document.html.erb +5 -3
  158. data/app/views/catalog/_document.rss.builder +2 -4
  159. data/app/views/catalog/_facet_layout.html.erb +2 -2
  160. data/app/views/catalog/_facets.html.erb +5 -4
  161. data/app/views/catalog/_home_text.html.erb +2 -14
  162. data/app/views/catalog/_search_form.html.erb +2 -2
  163. data/app/views/catalog/_search_header.html.erb +1 -2
  164. data/app/views/catalog/_search_results.html.erb +2 -2
  165. data/app/views/catalog/_search_sidebar.html.erb +5 -1
  166. data/app/views/catalog/_show_main_content.html.erb +11 -16
  167. data/app/views/catalog/_show_sidebar.html.erb +2 -2
  168. data/app/views/catalog/_show_tools.html.erb +8 -14
  169. data/app/views/catalog/_view_type_group.html.erb +1 -1
  170. data/app/views/catalog/email.html.erb +2 -2
  171. data/app/views/catalog/email_success.html.erb +5 -6
  172. data/app/views/catalog/facet.html.erb +7 -5
  173. data/app/views/catalog/index.atom.builder +12 -14
  174. data/app/views/catalog/index.html.erb +4 -1
  175. data/app/views/catalog/index.json.jbuilder +19 -19
  176. data/app/views/catalog/index.rss.builder +1 -1
  177. data/app/views/catalog/opensearch.xml.builder +1 -1
  178. data/app/views/catalog/sms.html.erb +2 -2
  179. data/app/views/catalog/sms_success.html.erb +5 -6
  180. data/app/views/catalog/suggest.html.erb +3 -0
  181. data/app/views/layouts/blacklight/base.html.erb +13 -2
  182. data/app/views/search_history/index.html.erb +6 -2
  183. data/app/views/shared/_modal.html.erb +3 -3
  184. data/blacklight.gemspec +8 -9
  185. data/config/importmap.rb +3 -0
  186. data/config/locales/blacklight.ar.yml +0 -1
  187. data/config/locales/blacklight.ca.yml +0 -1
  188. data/config/locales/blacklight.de.yml +0 -1
  189. data/config/locales/blacklight.en.yml +0 -2
  190. data/config/locales/blacklight.es.yml +0 -1
  191. data/config/locales/blacklight.fr.yml +0 -1
  192. data/config/locales/blacklight.hu.yml +0 -1
  193. data/config/locales/blacklight.it.yml +0 -1
  194. data/config/locales/blacklight.nl.yml +0 -1
  195. data/config/locales/blacklight.pt-BR.yml +0 -1
  196. data/config/locales/blacklight.sq.yml +0 -1
  197. data/config/locales/blacklight.zh.yml +0 -1
  198. data/config/routes.rb +3 -2
  199. data/db/migrate/20140202020201_create_searches.rb +1 -0
  200. data/db/migrate/20140202020202_create_bookmarks.rb +1 -0
  201. data/db/migrate/20140320000000_add_polymorphic_type_to_bookmarks.rb +1 -0
  202. data/docker-compose.yml +0 -1
  203. data/lib/blacklight/abstract_repository.rb +1 -0
  204. data/lib/blacklight/configuration/context.rb +4 -4
  205. data/lib/blacklight/configuration/display_field.rb +7 -9
  206. data/lib/blacklight/configuration/facet_field.rb +17 -11
  207. data/lib/blacklight/configuration/field.rb +1 -0
  208. data/lib/blacklight/configuration/fields.rb +12 -15
  209. data/lib/blacklight/configuration/index_field.rb +1 -0
  210. data/lib/blacklight/configuration/null_display_field.rb +17 -0
  211. data/lib/blacklight/configuration/search_field.rb +1 -0
  212. data/lib/blacklight/configuration/show_field.rb +1 -0
  213. data/lib/blacklight/configuration/sort_field.rb +1 -0
  214. data/lib/blacklight/configuration/tool_config.rb +1 -0
  215. data/lib/blacklight/configuration/view_config.rb +16 -6
  216. data/lib/blacklight/configuration.rb +294 -358
  217. data/lib/blacklight/engine.rb +8 -24
  218. data/lib/blacklight/exceptions.rb +2 -2
  219. data/lib/blacklight/nested_open_struct_with_hash_access.rb +6 -12
  220. data/lib/blacklight/open_struct_with_hash_access.rb +19 -17
  221. data/lib/blacklight/parameters.rb +7 -21
  222. data/lib/blacklight/routes/exportable.rb +1 -0
  223. data/lib/blacklight/routes/searchable.rb +2 -1
  224. data/lib/blacklight/routes.rb +1 -0
  225. data/lib/blacklight/search_builder.rb +10 -10
  226. data/lib/blacklight/search_state/filter_field.rb +8 -25
  227. data/lib/blacklight/search_state/pivot_filter_field.rb +144 -0
  228. data/lib/blacklight/search_state.rb +23 -79
  229. data/lib/blacklight/solr/document.rb +1 -0
  230. data/lib/blacklight/solr/facet_paginator.rb +1 -0
  231. data/lib/blacklight/solr/repository.rb +3 -14
  232. data/lib/blacklight/solr/request.rb +1 -0
  233. data/lib/blacklight/solr/response/facets.rb +21 -5
  234. data/lib/blacklight/solr/response/group.rb +1 -0
  235. data/lib/blacklight/solr/response/group_response.rb +1 -0
  236. data/lib/blacklight/solr/response/more_like_this.rb +1 -0
  237. data/lib/blacklight/solr/response/pagination_methods.rb +4 -3
  238. data/lib/blacklight/solr/response/params.rb +5 -4
  239. data/lib/blacklight/solr/response/response.rb +1 -0
  240. data/lib/blacklight/solr/response/spelling.rb +1 -0
  241. data/lib/blacklight/solr/response.rb +4 -3
  242. data/lib/blacklight/solr/search_builder_behavior.rb +17 -34
  243. data/lib/blacklight/solr.rb +1 -0
  244. data/lib/blacklight/version.rb +1 -0
  245. data/lib/blacklight.rb +7 -10
  246. data/lib/generators/blacklight/assets/importmap_generator.rb +55 -0
  247. data/lib/generators/blacklight/assets/propshaft_generator.rb +25 -0
  248. data/lib/generators/blacklight/assets/sprockets_generator.rb +66 -0
  249. data/lib/generators/blacklight/assets_generator.rb +13 -89
  250. data/lib/generators/blacklight/controller_generator.rb +3 -2
  251. data/lib/generators/blacklight/document_generator.rb +1 -0
  252. data/lib/generators/blacklight/install_generator.rb +4 -3
  253. data/lib/generators/blacklight/models_generator.rb +1 -0
  254. data/lib/generators/blacklight/search_builder_generator.rb +1 -0
  255. data/lib/generators/blacklight/solr_generator.rb +1 -1
  256. data/lib/generators/blacklight/templates/catalog_controller.rb +34 -8
  257. data/lib/generators/blacklight/templates/solr/conf/solrconfig.xml +0 -69
  258. data/lib/generators/blacklight/test_support_generator.rb +4 -2
  259. data/lib/generators/blacklight/user_generator.rb +7 -9
  260. data/lib/railties/blacklight.rake +6 -7
  261. data/package.json +10 -13
  262. data/rollup.config.js +27 -0
  263. data/spec/components/blacklight/constraints_component_spec.rb +17 -13
  264. data/spec/components/blacklight/document_component_spec.rb +23 -82
  265. data/spec/components/blacklight/facet_component_spec.rb +3 -18
  266. data/spec/components/blacklight/facet_field_checkboxes_component_spec.rb +1 -2
  267. data/spec/components/blacklight/facet_field_list_component_spec.rb +7 -6
  268. data/spec/components/blacklight/facet_item_pivot_component_spec.rb +10 -9
  269. data/spec/components/blacklight/response/view_type_component_spec.rb +66 -0
  270. data/spec/components/blacklight/search_bar_component_spec.rb +1 -1
  271. data/spec/components/blacklight/search_context_component_spec.rb +40 -0
  272. data/spec/controllers/blacklight/catalog/component_configuration_spec.rb +1 -6
  273. data/spec/controllers/blacklight/{base_spec.rb → catalog_spec.rb} +2 -2
  274. data/spec/controllers/bookmarks_controller_spec.rb +2 -3
  275. data/spec/controllers/catalog_controller_spec.rb +13 -135
  276. data/spec/features/advanced_search_spec.rb +0 -56
  277. data/spec/features/autocomplete_spec.rb +1 -1
  278. data/spec/features/axe_spec.rb +1 -6
  279. data/spec/features/bookmarks_spec.rb +1 -1
  280. data/spec/features/facets_spec.rb +1 -1
  281. data/spec/features/search_context_spec.rb +4 -10
  282. data/spec/features/search_results_spec.rb +0 -33
  283. data/spec/features/sitelinks_search_box.rb +13 -0
  284. data/spec/helpers/blacklight/configuration_helper_behavior_spec.rb +2 -138
  285. data/spec/helpers/blacklight/facets_helper_behavior_spec.rb +0 -387
  286. data/spec/helpers/blacklight/icon_helper_behavior_spec.rb +8 -0
  287. data/spec/helpers/blacklight/render_partials_helper_behavior_spec.rb +5 -7
  288. data/spec/helpers/blacklight/url_helper_behavior_spec.rb +9 -131
  289. data/spec/helpers/blacklight_helper_spec.rb +8 -252
  290. data/spec/helpers/catalog_helper_spec.rb +7 -118
  291. data/spec/i18n_spec.rb +1 -0
  292. data/spec/integration/generators/blacklight/solr_generator_spec.rb +1 -1
  293. data/spec/lib/blacklight/configuration/facet_field_spec.rb +27 -16
  294. data/spec/lib/blacklight/configuration/field_spec.rb +1 -1
  295. data/spec/lib/blacklight/configuration/view_config_spec.rb +1 -1
  296. data/spec/lib/blacklight/open_struct_with_hash_access_spec.rb +2 -2
  297. data/spec/lib/blacklight/parameters_spec.rb +1 -4
  298. data/spec/lib/blacklight/search_state/filter_field_spec.rb +4 -4
  299. data/spec/lib/blacklight/search_state/pivot_filter_field_spec.rb +117 -0
  300. data/spec/lib/blacklight/search_state_spec.rb +80 -198
  301. data/spec/lib/tasks/blacklight_task_spec.rb +1 -0
  302. data/spec/models/blacklight/configuration_spec.rb +17 -51
  303. data/spec/models/blacklight/document/active_model_shim_spec.rb +2 -2
  304. data/spec/models/blacklight/icon_spec.rb +31 -15
  305. data/spec/models/blacklight/search_builder_spec.rb +9 -9
  306. data/spec/models/blacklight/solr/document_spec.rb +3 -3
  307. data/spec/models/blacklight/solr/repository_spec.rb +0 -27
  308. data/spec/models/blacklight/solr/response/facets_spec.rb +27 -27
  309. data/spec/models/blacklight/solr/response/group_response_spec.rb +1 -0
  310. data/spec/models/blacklight/solr/response/group_spec.rb +1 -0
  311. data/spec/models/blacklight/solr/response_spec.rb +9 -2
  312. data/spec/models/blacklight/solr/search_builder_spec.rb +24 -31
  313. data/spec/models/blacklight/user_spec.rb +22 -0
  314. data/spec/models/solr_document_spec.rb +2 -6
  315. data/spec/presenters/blacklight/clause_presenter_spec.rb +1 -0
  316. data/spec/presenters/blacklight/document_presenter_spec.rb +2 -2
  317. data/spec/presenters/blacklight/facet_field_presenter_spec.rb +85 -12
  318. data/spec/presenters/blacklight/facet_grouped_item_presenter_spec.rb +1 -0
  319. data/spec/presenters/blacklight/facet_item_presenter_spec.rb +14 -13
  320. data/spec/presenters/blacklight/field_presenter_spec.rb +0 -14
  321. data/spec/presenters/blacklight/index_presenter_spec.rb +2 -4
  322. data/spec/presenters/blacklight/json_presenter_spec.rb +1 -0
  323. data/spec/presenters/blacklight/link_alternate_presenter_spec.rb +3 -2
  324. data/spec/presenters/blacklight/show_presenter_spec.rb +20 -29
  325. data/spec/presenters/thumbnail_presenter_spec.rb +1 -1
  326. data/spec/requests/load_suggestions_spec.rb +16 -0
  327. data/spec/routing/catalog_routing_spec.rb +2 -1
  328. data/spec/services/blacklight/search_service_spec.rb +31 -68
  329. data/spec/spec_helper.rb +6 -32
  330. data/spec/support/controller_level_helpers.rb +1 -2
  331. data/spec/support/features/search_helpers.rb +39 -0
  332. data/spec/support/features/session_helpers.rb +1 -0
  333. data/spec/support/features.rb +3 -0
  334. data/spec/support/view_component_capybara_test_helpers.rb +8 -0
  335. data/spec/test_app_templates/lib/generators/test_app_generator.rb +9 -2
  336. data/spec/views/catalog/_document.html.erb_spec.rb +3 -34
  337. data/spec/views/catalog/_facet_index_navigation.html.erb_spec.rb +1 -1
  338. data/spec/views/catalog/_search_header.erb_spec.rb +1 -0
  339. data/spec/views/catalog/_show_sidebar.erb_spec.rb +1 -0
  340. data/spec/views/catalog/_show_tools.html.erb_spec.rb +5 -66
  341. data/spec/views/catalog/_view_type_group.html.erb_spec.rb +17 -9
  342. data/spec/views/catalog/email_success.html.erb_spec.rb +2 -2
  343. data/spec/views/catalog/facet.html.erb_spec.rb +6 -3
  344. data/spec/views/catalog/index.atom.builder_spec.rb +18 -11
  345. data/spec/views/catalog/index.html.erb_spec.rb +5 -6
  346. data/spec/views/catalog/index.json.jbuilder_spec.rb +2 -2
  347. data/spec/views/catalog/show.html.erb_spec.rb +3 -25
  348. data/spec/views/catalog/sms_success.html.erb_spec.rb +2 -2
  349. data/tasks/blacklight.rake +6 -4
  350. metadata +81 -134
  351. data/.babelrc +0 -11
  352. data/app/assets/images/blacklight/list.svg +0 -1
  353. data/app/assets/images/blacklight/search.svg +0 -1
  354. data/app/assets/stylesheets/blacklight/_twitter_typeahead.scss +0 -37
  355. data/app/components/blacklight/content_areas_shim.rb +0 -13
  356. data/app/controllers/concerns/blacklight/base.rb +0 -12
  357. data/app/controllers/concerns/blacklight/default_component_configuration.rb +0 -64
  358. data/app/controllers/concerns/blacklight/facet.rb +0 -69
  359. data/app/controllers/concerns/blacklight/search_fields.rb +0 -46
  360. data/app/helpers/blacklight/hash_as_hidden_fields_helper_behavior.rb +0 -27
  361. data/app/helpers/blacklight/render_constraints_helper_behavior.rb +0 -188
  362. data/app/helpers/blacklight/search_history_constraints_helper_behavior.rb +0 -97
  363. data/app/helpers/blacklight/suggest_helper_behavior.rb +0 -13
  364. data/app/javascript/blacklight/autocomplete.js +0 -36
  365. data/app/javascript/blacklight/facet_load.js +0 -22
  366. data/app/presenters/blacklight/search_bar_presenter.rb +0 -47
  367. data/app/views/catalog/_constraints_element.html.erb +0 -14
  368. data/app/views/catalog/_document_action.html.erb +0 -5
  369. data/app/views/catalog/_facet_group.html.erb +0 -5
  370. data/app/views/catalog/_facet_limit.html.erb +0 -3
  371. data/app/views/catalog/_index.html.erb +0 -1
  372. data/app/views/catalog/_index_header.html.erb +0 -22
  373. data/app/views/catalog/_previous_next_doc.html.erb +0 -2
  374. data/app/views/catalog/_show.html.erb +0 -6
  375. data/app/views/catalog/_show_header.html.erb +0 -2
  376. data/app/views/catalog/_thumbnail.html.erb +0 -1
  377. data/lib/blacklight/deprecations/engine_configuration.rb +0 -66
  378. data/lib/blacklight/deprecations/search_state_normalization.rb +0 -52
  379. data/spec/components/blacklight/header_component_spec.rb +0 -20
  380. data/spec/controllers/blacklight/facet_spec.rb +0 -33
  381. data/spec/controllers/blacklight/search_fields_spec.rb +0 -62
  382. data/spec/features/citation_spec.rb +0 -10
  383. data/spec/features/sitelinks_search_box_spec.rb +0 -13
  384. data/spec/features/sms_spec.rb +0 -12
  385. data/spec/helpers/blacklight/hash_as_hidden_fields_behavior_spec.rb +0 -26
  386. data/spec/helpers/blacklight/render_constraints_helper_behavior_spec.rb +0 -92
  387. data/spec/helpers/blacklight/search_history_constraints_helper_behavior_spec.rb +0 -101
  388. data/spec/helpers/blacklight/suggest_helper_behavior_spec.rb +0 -48
  389. data/spec/lib/blacklight/engine_spec.rb +0 -41
  390. data/spec/presenters/blacklight/search_bar_presenter_spec.rb +0 -94
  391. data/spec/services/blacklight/field_retriever_spec.rb +0 -17
  392. data/spec/support/view_component_test_helpers.rb +0 -35
  393. data/spec/views/catalog/_constraints.html.erb_spec.rb +0 -33
  394. data/spec/views/catalog/_facet_group.html.erb_spec.rb +0 -84
  395. data/spec/views/catalog/_facets.html.erb_spec.rb +0 -15
  396. data/spec/views/catalog/_index.html.erb_spec.rb +0 -62
  397. data/spec/views/catalog/_index_header.html.erb_spec.rb +0 -35
  398. data/spec/views/catalog/_previous_next_doc.html.erb_spec.rb +0 -22
  399. data/spec/views/catalog/_show.html.erb_spec.rb +0 -62
  400. data/spec/views/catalog/_thumbnail.html.erb_spec.rb +0 -38
@@ -1,10 +1,12 @@
1
- Blacklight.onLoad(function() {
2
- // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
3
- // Safari both do not set focus to a button on button click.
4
- // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
5
- document.querySelectorAll('button.collapse-toggle').forEach((button) => {
6
- button.addEventListener('click', () => {
7
- event.target.focus();
8
- });
9
- });
10
- });
1
+ const ButtonFocus = (() => {
2
+ document.addEventListener('click', (e) => {
3
+ // Button clicks should change focus. As of 10/3/19, Firefox for Mac and
4
+ // Safari both do not set focus to a button on button click.
5
+ // See https://zellwk.com/blog/inconsistent-button-behavior/ for background information
6
+ if (e.target.matches('[data-toggle="collapse"]') || e.target.matches('[data-bs-toggle="collapse"]')) {
7
+ e.target.focus()
8
+ }
9
+ })
10
+ })()
11
+
12
+ export default ButtonFocus
@@ -1,135 +1,80 @@
1
- /* A JQuery plugin (should this be implemented as a widget instead? not sure)
2
- that will convert a "toggle" form, with single submit button to add/remove
1
+ /* Converts a "toggle" form, with single submit button to add/remove
3
2
  something, like used for Bookmarks, into an AJAXy checkbox instead.
4
-
5
3
  Apply to a form. Does require certain assumption about the form:
6
4
  1) The same form 'action' href must be used for both ADD and REMOVE
7
5
  actions, with the different being the hidden input name="_method"
8
6
  being set to "put" or "delete" -- that's the Rails method to pretend
9
7
  to be doing a certain HTTP verb. So same URL, PUT to add, DELETE
10
8
  to remove. This plugin assumes that.
11
-
12
9
  Plus, the form this is applied to should provide a data-doc-id
13
10
  attribute (HTML5-style doc-*) that contains the id/primary key
14
11
  of the object in question -- used by plugin for a unique value for
15
12
  DOM id's.
16
-
17
- Uses HTML for a checkbox compatible with Bootstrap 3.
18
-
19
- Pass in options for your class name and labels:
20
- $("form.something").blCheckboxSubmit({
21
- //cssClass is added to elements added, plus used for id base
22
- cssClass: "toggle_my_kinda_form",
23
- error: function() {
24
- #optional callback
25
- },
26
- success: function(after_success_check_state) {
27
- #optional callback
28
- }
29
- });
13
+ Uses HTML for a checkbox compatible with Bootstrap 4.
14
+ new CheckboxSubmit(document.querySelector('form.something')).render()
30
15
  */
31
- (function($) {
32
- $.fn.blCheckboxSubmit = function(argOpts) {
33
- this.each(function() {
34
- var options = $.extend({}, $.fn.blCheckboxSubmit.defaults, argOpts);
35
-
36
-
37
- var form = $(this);
38
- form.children().hide();
39
- //We're going to use the existing form to actually send our add/removes
40
- //This works conveneintly because the exact same action href is used
41
- //for both bookmarks/$doc_id. But let's take out the irrelevant parts
42
- //of the form to avoid any future confusion.
43
- form.find('input[type=submit]').remove();
44
-
45
- //View needs to set data-doc-id so we know a unique value
46
- //for making DOM id
47
- var uniqueId = form.attr('data-doc-id') || Math.random();
48
- // if form is currently using method delete to change state,
49
- // then checkbox is currently checked
50
- var checked = (form.find('input[name=_method][value=delete]').length != 0);
51
-
52
- var checkbox = $('<input type="checkbox">')
53
- .addClass( options.cssClass )
54
- .attr('id', options.cssClass + '_' + uniqueId);
55
- var label = $('<label>')
56
- .addClass( options.cssClass )
57
- .attr('for', options.cssClass + '_' + uniqueId)
58
- .attr('title', form.attr('title') || '');
59
- var span = $('<span>');
60
-
61
- label.append(checkbox);
62
- label.append(' ');
63
- label.append(span);
64
-
65
- var checkboxDiv = $('<div class="checkbox" />')
66
- .addClass(options.cssClass)
67
- .append(label);
68
-
69
- function updateStateFor(state) {
70
- checkbox.prop('checked', state);
71
- label.toggleClass('checked', state);
72
- if (state) {
73
- //Set the Rails hidden field that fakes an HTTP verb
74
- //properly for current state action.
75
- form.find('input[name=_method]').val('delete');
76
- span.html(form.attr('data-present'));
77
- } else {
78
- form.find('input[name=_method]').val('put');
79
- span.html(form.attr('data-absent'));
80
- }
81
- }
82
-
83
- form.append(checkboxDiv);
84
- updateStateFor(checked);
85
-
86
- checkbox.click(function() {
87
- span.html(form.attr('data-inprogress'));
88
- label.attr('disabled', 'disabled');
89
- checkbox.attr('disabled', 'disabled');
90
-
91
- $.ajax({
92
- url: form.attr('action'),
93
- beforeSend: function(xhr) { xhr.setRequestHeader('X-CSRF-Token', Blacklight.csrfToken()) },
94
- dataType: 'json',
95
- type: form.attr('method').toUpperCase(),
96
- data: form.serialize(),
97
- error: function() {
98
- label.removeAttr('disabled');
99
- checkbox.removeAttr('disabled');
100
- options.error.call();
101
- },
102
- success: function(data, status, xhr) {
103
- //if app isn't running at all, xhr annoyingly
104
- //reports success with status 0.
105
- if (xhr.status != 0) {
106
- checked = ! checked;
107
- updateStateFor(checked);
108
- label.removeAttr('disabled');
109
- checkbox.removeAttr('disabled');
110
- options.success.call(form, checked, xhr.responseJSON);
111
- } else {
112
- label.removeAttr('disabled');
113
- checkbox.removeAttr('disabled');
114
- options.error.call();
115
- }
116
- }
117
- });
118
-
119
- return false;
120
- }); //checkbox.click
121
-
122
-
123
- }); //this.each
124
- return this;
125
- };
126
-
127
- $.fn.blCheckboxSubmit.defaults = {
128
- //cssClass is added to elements added, plus used for id base
129
- cssClass: 'blCheckboxSubmit',
130
- error: function() {
131
- alert("Error");
132
- },
133
- success: function() {} //callback
134
- };
135
- })(jQuery);
16
+ export default class CheckboxSubmit {
17
+ constructor(form) {
18
+ this.form = form
19
+ }
20
+
21
+ async clicked(evt) {
22
+ this.spanTarget.innerHTML = this.form.getAttribute('data-inprogress')
23
+ this.labelTarget.setAttribute('disabled', 'disabled');
24
+ this.checkboxTarget.setAttribute('disabled', 'disabled');
25
+ const response = await fetch(this.formTarget.getAttribute('action'), {
26
+ body: new FormData(this.formTarget),
27
+ method: this.formTarget.getAttribute('method').toUpperCase(),
28
+ headers: {
29
+ 'Accept': 'application/json',
30
+ 'X-Requested-With': 'XMLHttpRequest',
31
+ 'X-CSRF-Token': document.querySelector('meta[name=csrf-token]')?.content
32
+ }
33
+ })
34
+ this.labelTarget.removeAttribute('disabled')
35
+ this.checkboxTarget.removeAttribute('disabled')
36
+ if (response.ok) {
37
+ const json = await response.json()
38
+ this.updateStateFor(!this.checked)
39
+ document.querySelector('[data-role=bookmark-counter]').innerHTML = json.bookmarks.count
40
+ } else {
41
+ alert('Error')
42
+ }
43
+ }
44
+
45
+ get checked() {
46
+ return (this.form.querySelectorAll('input[name=_method][value=delete]').length != 0)
47
+ }
48
+
49
+ get formTarget() {
50
+ return this.form
51
+ }
52
+
53
+ get labelTarget() {
54
+ return this.form.querySelector('[data-checkboxsubmit-target="label"]')
55
+ }
56
+
57
+ get checkboxTarget() {
58
+ return this.form.querySelector('[data-checkboxsubmit-target="checkbox"]')
59
+ }
60
+
61
+ get spanTarget() {
62
+ return this.form.querySelector('[data-checkboxsubmit-target="span"]')
63
+ }
64
+
65
+ updateStateFor(state) {
66
+ this.checkboxTarget.checked = state
67
+
68
+ if (state) {
69
+ this.labelTarget.classList.add('checked')
70
+ //Set the Rails hidden field that fakes an HTTP verb
71
+ //properly for current state action.
72
+ this.formTarget.querySelector('input[name=_method]').value = 'delete'
73
+ this.spanTarget.innerHTML = this.form.getAttribute('data-present')
74
+ } else {
75
+ this.labelTarget.classList.remove('checked')
76
+ this.formTarget.querySelector('input[name=_method]').value = 'put'
77
+ this.spanTarget.innerHTML = this.form.getAttribute('data-absent')
78
+ }
79
+ }
80
+ }
@@ -1,20 +1,20 @@
1
1
  const Blacklight = function() {
2
- var buffer = new Array;
2
+ const buffer = new Array;
3
3
  return {
4
4
  onLoad: function(func) {
5
5
  buffer.push(func);
6
6
  },
7
7
 
8
8
  activate: function() {
9
- for(var i = 0; i < buffer.length; i++) {
9
+ for(let i = 0; i < buffer.length; i++) {
10
10
  buffer[i].call();
11
11
  }
12
12
  },
13
13
 
14
14
  listeners: function () {
15
- var listeners = [];
15
+ const listeners = [];
16
16
  if (typeof Turbo !== 'undefined') {
17
- listeners.push('turbo:load');
17
+ listeners.push('turbo:load', 'turbo:frame-load');
18
18
  } else if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) {
19
19
  // Turbolinks 5
20
20
  if (Turbolinks.BrowserAdapter) {
@@ -51,7 +51,5 @@ Blacklight.onLoad(function () {
51
51
  elem.classList.add('js')
52
52
  })
53
53
 
54
- Blacklight.csrfToken = () => document.querySelector('meta[name=csrf-token]')?.content
55
- Blacklight.csrfParam = () => document.querySelector('meta[name=csrf-param]')?.content
56
54
 
57
- window.Blacklight = Blacklight
55
+ export default Blacklight
@@ -0,0 +1,13 @@
1
+ import BookmarkToggle from './bookmark_toggle'
2
+ import ButtonFocus from './button_focus'
3
+ import Modal from './modal'
4
+ import SearchContext from './search_context'
5
+ import Core from './core'
6
+
7
+ export default {
8
+ BookmarkToggle,
9
+ ButtonFocus,
10
+ Modal,
11
+ SearchContext,
12
+ onLoad: Core.onLoad
13
+ }
@@ -23,12 +23,6 @@
23
23
  of the container will be placed inside the modal, the rest of the
24
24
  page will be ignored.
25
25
 
26
- If you'd like to have a link or button that closes the modal,
27
- you can just add a `data-dismiss="modal"` to the link,
28
- standard Bootstrap convention. But you can also have
29
- an href on this link for non-JS contexts, we'll make sure
30
- inside the modal it closes the modal and the link is NOT followed.
31
-
32
26
  Link or forms inside the modal will ordinarily cause page loads
33
27
  when they are triggered. However, if you'd like their results
34
28
  to stay within the modal, just add `data-blacklight-modal="preserve"`
@@ -49,178 +43,119 @@
49
43
 
50
44
 
51
45
  <div class="modal-footer">
52
- <%= link_to "Close the modal", request_done_path, class: "submit button dialog-close", data: { dismiss: "modal" } %>
46
+ <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
53
47
  </div>
54
48
  </div>
55
49
 
56
50
 
57
- One additional feature. If the content returned from the AJAX modal load
58
- has an element with `data-blacklight-modal=close`, that will trigger the modal
59
- to be closed. And if this element includes a node with class "flash_messages",
60
- the flash-messages node will be added to the main page inside #main-flahses.
51
+ One additional feature. If the content returned from the AJAX form submission
52
+ can be a turbo-stream that defines some HTML fragementsand where on the page to put them:
53
+ https://turbo.hotwired.dev/handbook/streams
54
+ */
55
+ import Blacklight from './core'
56
+ import ModalForm from './modalForm'
57
+
58
+ const Modal = (() => {
59
+ // We keep all our data in Blacklight.modal object.
60
+ // Create lazily if someone else created first.
61
+ if (Blacklight.modal === undefined) {
62
+ Blacklight.modal = {};
63
+ }
61
64
 
62
- == Events
65
+ const modal = Blacklight.modal
66
+
67
+ // a Bootstrap modal div that should be already on the page hidden
68
+ modal.modalSelector = '#blacklight-modal';
69
+
70
+ // Trigger selectors identify forms or hyperlinks that should open
71
+ // inside a modal dialog.
72
+ modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';
73
+
74
+ // preserve selectors identify forms or hyperlinks that, if activated already
75
+ // inside a modal dialog, should have destinations remain inside the modal -- but
76
+ // won't trigger a modal if not already in one.
77
+ //
78
+ // No need to repeat selectors from trigger selectors, those will already
79
+ // be preserved. MUST be manually prefixed with the modal selector,
80
+ // so they only apply to things inside a modal.
81
+ modal.preserveLinkSelector = modal.modalSelector + ' a[data-blacklight-modal~=preserve]';
82
+
83
+ modal.containerSelector = '[data-blacklight-modal~=container]';
84
+
85
+ // Called on fatal failure of ajax load, function returns content
86
+ // to show to user in modal. Right now called only for extreme
87
+ // network errors.
88
+ modal.onFailure = function (jqXHR, textStatus, errorThrown) {
89
+ console.error('Server error:', this.url, jqXHR.status, errorThrown);
90
+
91
+ const contents = `<div class="modal-header">
92
+ <div class="modal-title">There was a problem with your request.</div>
93
+ <button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" data-bs-dismiss="modal" aria-label="Close">
94
+ <span aria-hidden="true">&times;</span>
95
+ </button>
96
+ </div>
97
+ <div class="modal-body">
98
+ <p>Expected a successful response from the server, but got an error</p>
99
+ <pre>${this.type} ${this.url}\n${jqXHR.status}: ${errorThrown}</pre>
100
+ </div>`
101
+
102
+ document.querySelector(`${modal.modalSelector} .modal-content`).innerHTML = contents
103
+
104
+ modal.show();
105
+ }
63
106
 
64
- We'll send out an event 'loaded.blacklight.blacklight-modal' with the #blacklight-modal
65
- dialog as the target, right after content is loaded into the modal but before
66
- it is shown (if not already a shown modal). In an event handler, you can
67
- inspect loaded content by looking inside $(this). If you call event.preventDefault(),
68
- we won't 'show' the dialog (although it may already have been shown, you may want to
69
- $(this).modal("hide") if you want to ensure hidden/closed.
107
+ // Add the passed in contents to the modal and display it.
108
+ modal.receiveAjax = function (contents) {
109
+ const domparser = new DOMParser();
110
+ const dom = domparser.parseFromString(contents, "text/html")
111
+ const elements = dom.querySelectorAll(`${modal.containerSelector} > *`)
112
+ document.querySelector(`${modal.modalSelector} .modal-content`).replaceChildren(...elements)
70
113
 
71
- The data-blacklight-modal=close behavior is implemented with this event, see for example.
72
- */
114
+ modal.show();
115
+ };
73
116
 
74
- // We keep all our data in Blacklight.modal object.
75
- // Create lazily if someone else created first.
76
- if (Blacklight.modal === undefined) {
77
- Blacklight.modal = {};
78
- }
79
-
80
- // a Bootstrap modal div that should be already on the page hidden
81
- Blacklight.modal.modalSelector = '#blacklight-modal';
82
-
83
- // Trigger selectors identify forms or hyperlinks that should open
84
- // inside a modal dialog.
85
- Blacklight.modal.triggerLinkSelector = 'a[data-blacklight-modal~=trigger]';
86
- Blacklight.modal.triggerFormSelector = 'form[data-blacklight-modal~=trigger]';
87
-
88
- // preserve selectors identify forms or hyperlinks that, if activated already
89
- // inside a modal dialog, should have destinations remain inside the modal -- but
90
- // won't trigger a modal if not already in one.
91
- //
92
- // No need to repeat selectors from trigger selectors, those will already
93
- // be preserved. MUST be manually prefixed with the modal selector,
94
- // so they only apply to things inside a modal.
95
- Blacklight.modal.preserveLinkSelector = Blacklight.modal.modalSelector + ' a[data-blacklight-modal~=preserve]';
96
-
97
- Blacklight.modal.containerSelector = '[data-blacklight-modal~=container]';
98
-
99
- Blacklight.modal.modalCloseSelector = '[data-blacklight-modal~=close]';
100
-
101
- // Called on fatal failure of ajax load, function returns content
102
- // to show to user in modal. Right now called only for extreme
103
- // network errors.
104
- Blacklight.modal.onFailure = function(jqXHR, textStatus, errorThrown) {
105
- console.error('Server error:', this.url, jqXHR.status, errorThrown);
106
-
107
- var contents = '<div class="modal-header">' +
108
- '<div class="modal-title">There was a problem with your request.</div>' +
109
- '<button type="button" class="blacklight-modal-close btn-close close" data-dismiss="modal" aria-label="Close">' +
110
- ' <span aria-hidden="true">&times;</span>' +
111
- '</button></div>' +
112
- ' <div class="modal-body"><p>Expected a successful response from the server, but got an error</p>' +
113
- '<pre>' +
114
- this.type + ' ' + this.url + "\n" + jqXHR.status + ': ' + errorThrown +
115
- '</pre></div>';
116
- $(Blacklight.modal.modalSelector).find('.modal-content').html(contents);
117
- Blacklight.modal.show();
118
- }
119
-
120
- Blacklight.modal.receiveAjax = function (contents) {
121
- // does it have a data- selector for container?
122
- // important we don't execute script tags, we shouldn't.
123
- // code modelled off of JQuery ajax.load. https://github.com/jquery/jquery/blob/main/src/ajax/load.js?source=c#L62
124
- var container = $('<div>').
125
- append( jQuery.parseHTML(contents) ).find( Blacklight.modal.containerSelector ).first();
126
- if (container.length !== 0) {
127
- contents = container.html();
128
- }
129
-
130
- $(Blacklight.modal.modalSelector).find('.modal-content').html(contents);
131
-
132
- // send custom event with the modal dialog div as the target
133
- var e = $.Event('loaded.blacklight.blacklight-modal')
134
- $(Blacklight.modal.modalSelector).trigger(e);
135
- // if they did preventDefault, don't show the dialog
136
- if (e.isDefaultPrevented()) return;
137
-
138
- Blacklight.modal.show();
139
- };
140
-
141
-
142
- Blacklight.modal.modalAjaxLinkClick = function(e) {
143
- e.preventDefault();
144
-
145
- $.ajax({
146
- url: $(this).attr('href')
147
- })
148
- .fail(Blacklight.modal.onFailure)
149
- .done(Blacklight.modal.receiveAjax)
150
- };
151
-
152
- Blacklight.modal.modalAjaxFormSubmit = function(e) {
153
- e.preventDefault();
154
117
 
155
- $.ajax({
156
- url: $(this).attr('action'),
157
- data: $(this).serialize(),
158
- type: $(this).attr('method') // POST
159
- })
160
- .fail(Blacklight.modal.onFailure)
161
- .done(Blacklight.modal.receiveAjax)
162
- }
163
-
164
-
165
-
166
- Blacklight.modal.setupModal = function() {
167
- // Event indicating blacklight is setting up a modal link,
168
- // you can catch it and call e.preventDefault() to abort
169
- // setup.
170
- var e = $.Event('setup.blacklight.blacklight-modal');
171
- $('body').trigger(e);
172
- if (e.isDefaultPrevented()) return;
173
-
174
- // Register both trigger and preserve selectors in ONE event handler, combining
175
- // into one selector with a comma, so if something matches BOTH selectors, it
176
- // still only gets the event handler called once.
177
- $('body').on('click', Blacklight.modal.triggerLinkSelector + ', ' + Blacklight.modal.preserveLinkSelector,
178
- Blacklight.modal.modalAjaxLinkClick);
179
- $('body').on('submit', Blacklight.modal.triggerFormSelector + ', ' + Blacklight.modal.preserveFormSelector,
180
- Blacklight.modal.modalAjaxFormSubmit);
181
-
182
- // Catch our own custom loaded event to implement data-blacklight-modal=closed
183
- $('body').on('loaded.blacklight.blacklight-modal', Blacklight.modal.checkCloseModal);
184
-
185
- // we support doing data-dismiss=modal on a <a> with a href for non-ajax
186
- // use, we need to suppress following the a's href that's there for
187
- // non-JS contexts.
188
- $('body').on('click', Blacklight.modal.modalSelector + ' a[data-dismiss~=modal]', function (e) {
118
+ modal.modalAjaxLinkClick = function(e) {
189
119
  e.preventDefault();
190
- });
191
- };
192
-
193
- // A function used as an event handler on loaded.blacklight.blacklight-modal
194
- // to catch contained data-blacklight-modal=closed directions
195
- Blacklight.modal.checkCloseModal = function(event) {
196
- if ($(event.target).find(Blacklight.modal.modalCloseSelector).length) {
197
- var modalFlashes = $(this).find('.flash_messages');
120
+ const href = e.target.getAttribute('href')
121
+ fetch(href)
122
+ .then(response => {
123
+ if (!response.ok) {
124
+ throw new TypeError("Request failed");
125
+ }
126
+ return response.text();
127
+ })
128
+ .then(data => modal.receiveAjax(data))
129
+ .catch(error => modal.onFailure(error))
130
+ };
131
+
132
+ modal.setupModal = function() {
133
+ // Register both trigger and preserve selectors in ONE event handler, combining
134
+ // into one selector with a comma, so if something matches BOTH selectors, it
135
+ // still only gets the event handler called once.
136
+ document.addEventListener('click', (e) => {
137
+ if (e.target.matches(`${modal.triggerLinkSelector}, ${modal.preserveLinkSelector}`))
138
+ modal.modalAjaxLinkClick(e)
139
+ else if (e.target.matches('[data-bl-dismiss="modal"]'))
140
+ modal.hide()
141
+ })
142
+ };
198
143
 
199
- Blacklight.modal.hide(event.target);
200
- event.preventDefault();
144
+ modal.hide = function (el) {
145
+ const dom = document.querySelector(Blacklight.modal.modalSelector)
201
146
 
202
- var mainFlashes = $('#main-flashes');
203
- mainFlashes.append(modalFlashes);
204
- modalFlashes.fadeIn(500);
147
+ if (!dom.open) return
148
+ dom.close()
205
149
  }
206
- }
207
150
 
208
- Blacklight.modal.hide = function(el) {
209
- if (typeof bootstrap !== 'undefined' && typeof bootstrap.Modal !== 'undefined' && bootstrap.Modal.VERSION >= "5") {
210
- bootstrap.Modal.getOrCreateInstance(el || document.querySelector(Blacklight.modal.modalSelector)).hide();
211
- } else {
212
- $(el || Blacklight.modal.modalSelector).modal('hide');
213
- }
214
- }
151
+ modal.show = function(el) {
152
+ const dom = document.querySelector(Blacklight.modal.modalSelector)
215
153
 
216
- Blacklight.modal.show = function(el) {
217
- if (typeof bootstrap !== 'undefined' && typeof bootstrap.Modal !== 'undefined' && bootstrap.Modal.VERSION >= "5") {
218
- bootstrap.Modal.getOrCreateInstance(el || document.querySelector(Blacklight.modal.modalSelector)).show();
219
- } else {
220
- $(el || Blacklight.modal.modalSelector).modal('show');
154
+ if (dom.open) return
155
+ dom.showModal()
221
156
  }
222
- }
223
157
 
224
- Blacklight.onLoad(function() {
225
- Blacklight.modal.setupModal();
226
- });
158
+ modal.setupModal()
159
+ })()
160
+
161
+ export default Modal
@@ -0,0 +1,60 @@
1
+ // The email and sms forms are displayed inside a modal. When the form is submitted,
2
+ // this script closes the modal and puts the output on the main part of the page.
3
+ // By default it is rendering catalog/sms_success and catalog/email_succcess.
4
+ // These templates deliver a payload of the format used by turbo-streams.
5
+ // See https://turbo.hotwired.dev/handbook/streams
6
+ // That format allows a downstream application to override the template to define
7
+ // multiple customizable areas of the page to get updated.
8
+ export default class {
9
+ constructor(errorHandler, badRequestHandler, hideModal) {
10
+ this.errorHandler = errorHandler
11
+ this.badRequestHandler = badRequestHandler
12
+ this.hideModal = hideModal
13
+ }
14
+
15
+ get triggerFormSelector() {
16
+ return 'form[data-blacklight-modal~=trigger]'
17
+ }
18
+
19
+ bind() {
20
+ document.addEventListener('submit', (e) => {
21
+ if (e.target.matches(this.triggerFormSelector))
22
+ this.onSubmit(e)
23
+ })
24
+ }
25
+
26
+ // This is like a light-weight version of turbo that only supports append presently.
27
+ updateTurboStream(data) {
28
+ this.hideModal()
29
+ const domparser = new DOMParser();
30
+ const dom = domparser.parseFromString(data, "text/html")
31
+ dom.querySelectorAll("turbo-stream[action='append']").forEach((node) => {
32
+ const target = node.getAttribute('target')
33
+ const element = document.getElementById(target)
34
+ if (element)
35
+ element.append(node.querySelector('template').content.cloneNode(true))
36
+ else
37
+ console.error(`Unable to find an element on the page with and ID of "${target}""`)
38
+ })
39
+ }
40
+
41
+ onSubmit(e) {
42
+ e.preventDefault();
43
+ const form = e.target
44
+ fetch(form.action, {
45
+ body: new FormData(form),
46
+ headers: { "X-Requested-With": "XMLHttpRequest" }, // Ensures rails will return true when checking request.xhr?
47
+ method: form.method,
48
+ })
49
+ .then(response => {
50
+ if (response.status === 422) {
51
+ return response.text().then(content => this.badRequestHandler(content) )
52
+ }
53
+ if (!response.ok) {
54
+ throw new TypeError("Request failed");
55
+ }
56
+ response.text().then(content => this.updateTurboStream(content))
57
+ })
58
+ .catch(error => this.errorHandler(error))
59
+ }
60
+ }