blacklight 7.19.2 → 7.21.1

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.babelrc +11 -0
  3. data/.env +1 -1
  4. data/.github/workflows/ruby.yml +21 -3
  5. data/.rubocop.yml +4 -0
  6. data/Gemfile +1 -1
  7. data/README.md +2 -2
  8. data/VERSION +1 -1
  9. data/app/assets/javascripts/blacklight/blacklight.js +50 -27
  10. data/app/assets/stylesheets/blacklight/_constraints.scss +7 -4
  11. data/app/assets/stylesheets/blacklight/_controls.scss +8 -0
  12. data/app/assets/stylesheets/blacklight/_facets.scss +2 -2
  13. data/app/assets/stylesheets/blacklight/_pagination.scss +1 -1
  14. data/app/assets/stylesheets/blacklight/_twitter_typeahead.scss +1 -0
  15. data/app/components/blacklight/constraints_component.rb +1 -1
  16. data/app/components/blacklight/document_component.rb +4 -4
  17. data/app/components/blacklight/facet_item_component.rb +2 -2
  18. data/app/components/blacklight/metadata_field_layout_component.rb +1 -1
  19. data/app/components/blacklight/response/pagination_component.html.erb +1 -1
  20. data/app/components/blacklight/system/modal_component.html.erb +2 -2
  21. data/app/controllers/concerns/blacklight/bookmarks.rb +0 -3
  22. data/app/controllers/concerns/blacklight/catalog.rb +5 -2
  23. data/app/controllers/concerns/blacklight/controller.rb +9 -5
  24. data/app/controllers/concerns/blacklight/search_context.rb +1 -1
  25. data/app/helpers/blacklight/catalog_helper_behavior.rb +1 -3
  26. data/app/javascript/blacklight/core.js +14 -3
  27. data/app/javascript/blacklight/modal.js +1 -1
  28. data/app/javascript/blacklight/search_context.js +5 -2
  29. data/app/models/concerns/blacklight/document/email.rb +18 -6
  30. data/app/models/concerns/blacklight/document/sms.rb +16 -4
  31. data/app/models/record_mailer.rb +9 -1
  32. data/app/presenters/blacklight/facet_item_presenter.rb +2 -0
  33. data/app/views/catalog/_home_text.html.erb +2 -2
  34. data/app/views/catalog/_paginate_compact.html.erb +1 -0
  35. data/app/views/record_mailer/sms_record.text.erb +1 -1
  36. data/app/views/shared/_header_navbar.html.erb +2 -2
  37. data/blacklight.gemspec +4 -2
  38. data/config/locales/blacklight.de.yml +4 -2
  39. data/config/locales/blacklight.en.yml +6 -2
  40. data/config/locales/blacklight.es.yml +3 -1
  41. data/config/locales/blacklight.fr.yml +3 -1
  42. data/config/locales/blacklight.it.yml +3 -1
  43. data/lib/blacklight/configuration.rb +9 -0
  44. data/lib/blacklight/engine.rb +2 -0
  45. data/lib/blacklight/search_state/filter_field.rb +35 -11
  46. data/lib/blacklight/solr/response/facets.rb +2 -1
  47. data/lib/blacklight/solr/response/group_response.rb +3 -2
  48. data/lib/blacklight/solr/response/pagination_methods.rb +1 -1
  49. data/lib/blacklight/solr/search_builder_behavior.rb +2 -0
  50. data/lib/blacklight.rb +5 -1
  51. data/lib/generators/blacklight/assets_generator.rb +4 -2
  52. data/lib/generators/blacklight/install_generator.rb +4 -1
  53. data/lib/generators/blacklight/user_generator.rb +1 -1
  54. data/package.json +5 -3
  55. data/spec/components/blacklight/facet_item_component_spec.rb +6 -2
  56. data/spec/controllers/catalog_controller_spec.rb +14 -2
  57. data/spec/features/axe_spec.rb +33 -0
  58. data/spec/features/facet_missing_spec.rb +67 -0
  59. data/spec/features/facets_spec.rb +1 -1
  60. data/spec/helpers/blacklight/facets_helper_behavior_spec.rb +2 -2
  61. data/spec/lib/blacklight/search_state/filter_field_spec.rb +13 -0
  62. data/spec/models/blacklight/configuration_spec.rb +92 -0
  63. data/spec/models/blacklight/document/email_spec.rb +32 -0
  64. data/spec/models/blacklight/document/sms_spec.rb +33 -0
  65. data/spec/models/blacklight/solr/response/facets_spec.rb +1 -1
  66. data/spec/models/blacklight/solr/response/group_response_spec.rb +3 -2
  67. data/spec/models/record_mailer_spec.rb +30 -2
  68. data/spec/spec_helper.rb +18 -12
  69. data/spec/test_app_templates/lib/generators/test_app_generator.rb +0 -3
  70. metadata +44 -11
@@ -2,13 +2,25 @@
2
2
  # This module provides the body of an email export based on the document's semantic values
3
3
  module Blacklight::Document::Email
4
4
  # Return a text string that will be the body of the email
5
- def to_email_text
6
- semantics = to_semantic_values
5
+ def to_email_text(config = nil)
7
6
  body = []
8
- body << I18n.t('blacklight.email.text.title', value: semantics[:title].join(" ")) if semantics[:title].present?
9
- body << I18n.t('blacklight.email.text.author', value: semantics[:author].join(" ")) if semantics[:author].present?
10
- body << I18n.t('blacklight.email.text.format', value: semantics[:format].join(" ")) if semantics[:format].present?
11
- body << I18n.t('blacklight.email.text.language', value: semantics[:language].join(" ")) if semantics[:language].present?
7
+
8
+ if config
9
+ body = config.email_fields.map do |name, field|
10
+ values = [self[name]].flatten
11
+ "#{field.label} #{values.join(' ')}" if self[name].present?
12
+ end
13
+ end
14
+
15
+ # Use to_semantic_values for backwards compatibility
16
+ if body.empty?
17
+ semantics = to_semantic_values
18
+ body << I18n.t('blacklight.email.text.title', value: semantics[:title].join(" ")) if semantics[:title].present?
19
+ body << I18n.t('blacklight.email.text.author', value: semantics[:author].join(" ")) if semantics[:author].present?
20
+ body << I18n.t('blacklight.email.text.format', value: semantics[:format].join(" ")) if semantics[:format].present?
21
+ body << I18n.t('blacklight.email.text.language', value: semantics[:language].join(" ")) if semantics[:language].present?
22
+ end
23
+
12
24
  return body.join("\n") unless body.empty?
13
25
  end
14
26
  end
@@ -2,11 +2,23 @@
2
2
  # This module provides the body of an email export based on the document's semantic values
3
3
  module Blacklight::Document::Sms
4
4
  # Return a text string that will be the body of the email
5
- def to_sms_text
6
- semantics = to_semantic_values
5
+ def to_sms_text(config = nil)
7
6
  body = []
8
- body << I18n.t('blacklight.sms.text.title', value: semantics[:title].first) if semantics[:title].present?
9
- body << I18n.t('blacklight.sms.text.author', value: semantics[:author].first) if semantics[:author].present?
7
+
8
+ if config
9
+ body = config.sms_fields.map do |name, field|
10
+ values = [self[name]].flatten
11
+ "#{field.label} #{values.first}" if self[name].present?
12
+ end
13
+ end
14
+
15
+ # Use to_semantic_values for backwards compatibility
16
+ if body.empty?
17
+ semantics = to_semantic_values
18
+ body << I18n.t('blacklight.sms.text.title', value: semantics[:title].first) if semantics[:title].present?
19
+ body << I18n.t('blacklight.sms.text.author', value: semantics[:author].first) if semantics[:author].present?
20
+ end
21
+
10
22
  return body.join unless body.empty?
11
23
  end
12
24
  end
@@ -3,7 +3,12 @@
3
3
  class RecordMailer < ActionMailer::Base
4
4
  def email_record(documents, details, url_gen_params)
5
5
  title = begin
6
- documents.first.to_semantic_values[:title]
6
+ title_field = details[:config].email.title_field
7
+ if title_field
8
+ [documents.first[title_field]].flatten.first
9
+ else
10
+ documents.first.to_semantic_values[:title]
11
+ end
7
12
  rescue
8
13
  I18n.t('blacklight.email.text.default_title')
9
14
  end
@@ -14,6 +19,7 @@ class RecordMailer < ActionMailer::Base
14
19
 
15
20
  @documents = documents
16
21
  @message = details[:message]
22
+ @config = details[:config]
17
23
  @url_gen_params = url_gen_params
18
24
 
19
25
  mail(to: details[:to], subject: subject)
@@ -21,7 +27,9 @@ class RecordMailer < ActionMailer::Base
21
27
 
22
28
  def sms_record(documents, details, url_gen_params)
23
29
  @documents = documents
30
+ @config = details[:config]
24
31
  @url_gen_params = url_gen_params
32
+
25
33
  mail(to: details[:to], subject: "")
26
34
  end
27
35
  end
@@ -56,6 +56,8 @@ module Blacklight
56
56
  view_context.public_send(facet_config.helper_method, label_value)
57
57
  elsif facet_config.query && facet_config.query[label_value]
58
58
  facet_config.query[label_value][:label]
59
+ elsif value == Blacklight::SearchState::FilterField::MISSING
60
+ I18n.t("blacklight.search.facets.missing")
59
61
  elsif facet_config.date
60
62
  localization_options = facet_config.date == true ? {} : facet_config.date
61
63
  I18n.l(Time.zone.parse(label_value), **localization_options)
@@ -1,11 +1,11 @@
1
- <div class="jumbotron text-center">
1
+ <div class="jumbotron text-center p-5 mb-4 bg-light rounded-3">
2
2
  <h1 class="jumbotron-heading"><%= t('blacklight.welcome') %></h1>
3
3
 
4
4
  <p class="lead">Blacklight is a multi-institutional open-source collaboration building a better discovery platform framework.</p>
5
5
 
6
6
  <p>
7
7
  <%= link_to 'Read the Documentation', 'https://github.com/projectblacklight/blacklight/wiki', class: 'btn btn-primary' %>
8
- <%= link_to 'See Examples', 'http://projectblacklight.org', class: 'btn btn-outline-secondary' %>
8
+ <%= link_to 'See Examples', 'http://projectblacklight.org', class: 'btn btn-light' %>
9
9
  </p>
10
10
  </div>
11
11
 
@@ -2,5 +2,6 @@
2
2
  response: paginate_compact,
3
3
  theme: :blacklight_compact,
4
4
  page_entries_info: page_entries_info(paginate_compact),
5
+ role: nil,
5
6
  html: { aria: {} })
6
7
  %>
@@ -1,4 +1,4 @@
1
1
  <% @documents.each do |document| %>
2
- <%= document.to_sms_text %>
2
+ <%= document.to_sms_text(@config) %>
3
3
  <%= t('blacklight.sms.text.url', :url => polymorphic_url(document, @url_gen_params) ) %>
4
4
  <% end %>
@@ -11,8 +11,8 @@
11
11
  </div>
12
12
  </nav>
13
13
 
14
- <div class="navbar-search navbar navbar-light bg-light" role="navigation">
14
+ <%= content_tag :div, class: 'navbar-search navbar navbar-light bg-light', role: 'navigation', aria: { label: t('blacklight.search.header') } do %>
15
15
  <div class="<%= container_classes %>">
16
16
  <%= render_search_bar %>
17
17
  </div>
18
- </div>
18
+ <% end %>
data/blacklight.gemspec CHANGED
@@ -32,14 +32,16 @@ Gem::Specification.new do |s|
32
32
  s.add_dependency "deprecation"
33
33
  s.add_dependency "i18n", '>= 1.7.0' # added named parameters
34
34
  s.add_dependency "ostruct", '>= 0.3.2'
35
- s.add_dependency "view_component", '>= 2.28.0'
35
+ s.add_dependency "view_component", '~> 2.42'
36
36
 
37
37
  s.add_development_dependency "rsolr", ">= 1.0.6", "< 3" # Library for interacting with rSolr.
38
38
  s.add_development_dependency "rspec-rails", "~> 4.0.0.beta2"
39
39
  s.add_development_dependency "rspec-its"
40
40
  s.add_development_dependency "rspec-collection_matchers", ">= 1.0"
41
+ s.add_development_dependency 'axe-core-rspec'
41
42
  s.add_development_dependency "capybara", '~> 3'
42
- s.add_development_dependency 'apparition'
43
+ s.add_development_dependency 'webdrivers'
44
+ s.add_development_dependency 'selenium-webdriver'
43
45
  s.add_development_dependency 'engine_cart', '~> 2.1'
44
46
  s.add_development_dependency "equivalent-xml"
45
47
  s.add_development_dependency "simplecov"
@@ -169,7 +169,7 @@ de:
169
169
  pagination:
170
170
  title: 'Ergebnisse Navigation'
171
171
  pagination_info:
172
- no_items_found: 'Kein %{entry_name} gefunden'
172
+ no_items_found: 'Keine %{entry_name} gefunden'
173
173
  single_item_found: '<strong>1</strong> %{entry_name} gefunden'
174
174
  pages:
175
175
  one: '<strong>%{start_num}</strong> - <strong>%{end_num}</strong> von <strong>%{total_num}</strong>'
@@ -230,7 +230,9 @@ de:
230
230
  list: "Liste"
231
231
 
232
232
  entry_name:
233
- default: 'Eintrag'
233
+ default:
234
+ one: Eintrag
235
+ other: Einträge
234
236
  grouped:
235
237
  default: 'gruppiertes Ergebnis'
236
238
 
@@ -230,9 +230,13 @@ en:
230
230
  list: "List"
231
231
 
232
232
  entry_name:
233
- default: 'entry'
233
+ default:
234
+ one: 'entry'
235
+ other: 'entries'
234
236
  grouped:
235
- default: 'grouped result'
237
+ default:
238
+ one: 'grouped result'
239
+ other: 'grouped results'
236
240
 
237
241
  did_you_mean: 'Did you mean to type: %{options}?'
238
242
 
@@ -230,7 +230,9 @@ es:
230
230
  list: "Lista"
231
231
 
232
232
  entry_name:
233
- default: 'entrada'
233
+ default:
234
+ one: 'entrada'
235
+ other: 'entradas'
234
236
  grouped:
235
237
  default: 'resultado agrupado'
236
238
 
@@ -234,7 +234,9 @@ fr:
234
234
 
235
235
 
236
236
  entry_name:
237
- default: 'résultat'
237
+ default:
238
+ one: 'résultat'
239
+ other: 'résultats'
238
240
  grouped:
239
241
  default: 'résultat groupé'
240
242
 
@@ -230,7 +230,9 @@ it:
230
230
  list: "Lista"
231
231
 
232
232
  entry_name:
233
- default: 'termine di ricerca'
233
+ default:
234
+ one: 'termine di ricerca'
235
+ other: 'termini di ricerca'
234
236
  grouped:
235
237
  default: 'risultato raggruppato'
236
238
 
@@ -101,6 +101,9 @@ module Blacklight
101
101
  show: { top_level_config: :show },
102
102
  citation: { parent_config: :show }
103
103
  ),
104
+ # SMS and Email configurations.
105
+ sms: ViewConfig.new,
106
+ email: ViewConfig.new,
104
107
  # Configurations for specific types of index views
105
108
  view: NestedOpenStructWithHashAccess.new(ViewConfig,
106
109
  list: {},
@@ -167,6 +170,12 @@ module Blacklight
167
170
  # solr fields to use for sorting results
168
171
  define_field_access :sort_field
169
172
 
173
+ # solr fields to use in text message
174
+ define_field_access :sms_field
175
+
176
+ # solr fields to use in email message
177
+ define_field_access :email_field
178
+
170
179
  def initialize(hash = {})
171
180
  super(self.class.default_values.deep_dup.merge(hash))
172
181
  yield(self) if block_given?
@@ -48,5 +48,7 @@ module Blacklight
48
48
  config.action_dispatch.rescue_responses["Blacklight::Exceptions::RecordNotFound"] = :not_found
49
49
 
50
50
  config.enable_search_bar_autofocus = false
51
+
52
+ config.facet_missing_param = '[* TO *]'
51
53
  end
52
54
  end
@@ -4,6 +4,8 @@ module Blacklight
4
4
  class SearchState
5
5
  # Modeling access to filter query parameters
6
6
  class FilterField
7
+ MISSING = { missing: true }.freeze
8
+
7
9
  # @param [Blacklight::Configuration::FacetField] config
8
10
  attr_reader :config
9
11
 
@@ -25,7 +27,9 @@ module Blacklight
25
27
  def add(item)
26
28
  new_state = search_state.reset_search
27
29
 
28
- if item.respond_to?(:fq)
30
+ if item.try(:missing)
31
+ # if this is a 'missing' facet value, the :fq is only for backwards compatibility
32
+ elsif item.respond_to?(:fq)
29
33
  Array(item.fq).each do |f, v|
30
34
  new_state = new_state.filter(f).add(v)
31
35
  end
@@ -35,21 +39,28 @@ module Blacklight
35
39
  return new_state.filter(item.field).add(item)
36
40
  end
37
41
 
42
+ url_key = key
38
43
  params = new_state.params
39
44
  param = :f
40
45
  value = as_url_parameter(item)
46
+
47
+ if value == Blacklight::SearchState::FilterField::MISSING
48
+ url_key = "-#{key}"
49
+ value = Blacklight::Engine.config.facet_missing_param
50
+ end
51
+
41
52
  param = :f_inclusive if value.is_a?(Array)
42
53
 
43
54
  # value could be a string
44
55
  params[param] = (params[param] || {}).dup
45
56
 
46
57
  if value.is_a? Array
47
- params[param][key] = value
58
+ params[param][url_key] = value
48
59
  elsif config.single
49
- params[param][key] = [value]
60
+ params[param][url_key] = [value]
50
61
  else
51
- params[param][key] = Array(params[param][key] || []).dup
52
- params[param][key].push(value)
62
+ params[param][url_key] = Array(params[param][url_key] || []).dup
63
+ params[param][url_key].push(value)
53
64
  end
54
65
 
55
66
  new_state.reset(params)
@@ -63,19 +74,26 @@ module Blacklight
63
74
  return new_state.filter(item.field).remove(item)
64
75
  end
65
76
 
77
+ url_key = config.key
66
78
  params = new_state.params
67
79
 
68
80
  param = :f
69
81
  value = as_url_parameter(item)
82
+
83
+ if value == Blacklight::SearchState::FilterField::MISSING
84
+ url_key = "-#{key}"
85
+ value = Blacklight::Engine.config.facet_missing_param
86
+ end
87
+
70
88
  param = :f_inclusive if value.is_a?(Array)
71
89
 
72
90
  # need to dup the facet values too,
73
91
  # if the values aren't dup'd, then the values
74
92
  # from the session will get remove in the show view...
75
93
  params[param] = (params[param] || {}).dup
76
- params[param][key] = (params[param][key] || []).dup
94
+ params[param][url_key] = (params[param][url_key] || []).dup
77
95
 
78
- collection = params[param][key]
96
+ collection = params[param][url_key]
79
97
  # collection should be an array, because we link to ?f[key][]=value,
80
98
  # however, Facebook (and maybe some other PHP tools) tranform that parameters
81
99
  # into ?f[key][0]=value, which Rails interprets as a Hash.
@@ -83,8 +101,9 @@ module Blacklight
83
101
  Deprecation.warn(self, 'Normalizing parameters in FilterField#remove is deprecated')
84
102
  collection = collection.values
85
103
  end
86
- params[param][key] = collection - Array(value)
87
- params[param].delete(key) if params[param][key].empty?
104
+
105
+ params[param][url_key] = collection - Array(value)
106
+ params[param].delete(url_key) if params[param][url_key].empty?
88
107
  params.delete(param) if params[param].empty?
89
108
 
90
109
  new_state.reset(params)
@@ -95,8 +114,9 @@ module Blacklight
95
114
  params = search_state.params
96
115
  f = Array(params.dig(:f, key))
97
116
  f_inclusive = [params.dig(:f_inclusive, key)] if params.dig(:f_inclusive, key).present?
117
+ f_missing = [Blacklight::SearchState::FilterField::MISSING] if params.dig(:f, "-#{key}")&.any? { |v| v == Blacklight::Engine.config.facet_missing_param }
98
118
 
99
- f + (f_inclusive || [])
119
+ f + (f_inclusive || []) + (f_missing || [])
100
120
  end
101
121
  delegate :any?, to: :values
102
122
 
@@ -112,6 +132,8 @@ module Blacklight
112
132
 
113
133
  if value.is_a?(Array)
114
134
  (params.dig(:f_inclusive, key) || []).to_set == value.to_set
135
+ elsif value == Blacklight::SearchState::FilterField::MISSING
136
+ (params.dig(:f, "-#{key}") || []).include?(Blacklight::Engine.config.facet_missing_param)
115
137
  else
116
138
  (params.dig(:f, key) || []).include?(value)
117
139
  end
@@ -121,7 +143,9 @@ module Blacklight
121
143
 
122
144
  # TODO: this code is duplicated in Blacklight::FacetsHelperBehavior
123
145
  def as_url_parameter(item)
124
- if item.respond_to? :value
146
+ if item.respond_to?(:missing) && item.missing
147
+ Blacklight::SearchState::FilterField::MISSING
148
+ elsif item.respond_to? :value
125
149
  item.value
126
150
  else
127
151
  item
@@ -171,7 +171,8 @@ module Blacklight::Solr::Response::Facets
171
171
  # legacy solr facet.missing serialization
172
172
  if value.nil?
173
173
  i.label = I18n.t(:"blacklight.search.fields.facet.missing.#{facet_field_name}", default: [:"blacklight.search.facets.missing"])
174
- i.fq = "-#{facet_field_name}:[* TO *]"
174
+ i.fq = "-#{facet_field_name}:[* TO *]" # this explicit fq is deprecated; the missing attribute below is a better thing to check for this case
175
+ i.value = Blacklight::SearchState::FilterField::MISSING
175
176
  i.missing = true
176
177
  end
177
178
 
@@ -45,8 +45,9 @@ class Blacklight::Solr::Response::GroupResponse
45
45
  def entry_name(options)
46
46
  I18n.t(
47
47
  "blacklight.entry_name.grouped.#{key}",
48
- default: :'blacklight.entry_name.grouped.default'
49
- ).pluralize(options[:count])
48
+ default: :'blacklight.entry_name.grouped.default',
49
+ count: options[:count]
50
+ )
50
51
  end
51
52
 
52
53
  def method_missing meth, *args, &block
@@ -24,6 +24,6 @@ module Blacklight::Solr::Response::PaginationMethods
24
24
  ##
25
25
  # Meant to have the same signature as Kaminari::PaginatableArray#entry_name
26
26
  def entry_name(options)
27
- I18n.t('blacklight.entry_name.default').pluralize(options[:count])
27
+ I18n.t('blacklight.entry_name.default', count: options[:count])
28
28
  end
29
29
  end
@@ -370,6 +370,8 @@ module Blacklight::Solr
370
370
  elsif value.is_a?(Range)
371
371
  prefix = "{!#{local_params.join(' ')}}" unless local_params.empty?
372
372
  "#{prefix}#{solr_field}:[#{value.first} TO #{value.last}]"
373
+ elsif value == Blacklight::SearchState::FilterField::MISSING
374
+ "-#{solr_field}:[* TO *]"
373
375
  else
374
376
  "{!term f=#{solr_field}#{(' ' + local_params.join(' ')) unless local_params.empty?}}#{convert_to_term_value(value)}"
375
377
  end
data/lib/blacklight.rb CHANGED
@@ -96,7 +96,11 @@ module Blacklight
96
96
  end
97
97
 
98
98
  begin
99
- @blacklight_yml = YAML.safe_load(blacklight_erb)
99
+ @blacklight_yml = if RUBY_VERSION > '2.6'
100
+ YAML.safe_load(blacklight_erb, aliases: true)
101
+ else
102
+ YAML.safe_load(blacklight_erb, [], [], true)
103
+ end
100
104
  rescue => e
101
105
  raise("#{blacklight_config_file} was found, but could not be parsed.\n#{e.inspect}")
102
106
  end
@@ -3,9 +3,11 @@ module Blacklight
3
3
  class Assets < Rails::Generators::Base
4
4
  source_root File.expand_path('../templates', __FILE__)
5
5
 
6
+ class_option :'bootstrap-version', type: :string, default: ENV.fetch('BOOTSTRAP_VERSION', '~> 4.0'), desc: "Set the generated app's bootstrap version"
7
+
6
8
  # This could be skipped if you want to use webpacker
7
9
  def add_javascript_dependencies
8
- gem 'bootstrap', '~> 4.0'
10
+ gem 'bootstrap', options[:'bootstrap-version']
9
11
  gem 'twitter-typeahead-rails', '0.11.1.pre.corejavascript'
10
12
  end
11
13
 
@@ -15,7 +17,7 @@ module Blacklight
15
17
 
16
18
  create_file 'app/assets/javascripts/application.js' do
17
19
  <<~CONTENT
18
- //= require jquery
20
+ //= require jquery3
19
21
  //= require rails-ujs
20
22
  //= require turbolinks
21
23
  CONTENT
@@ -10,6 +10,7 @@ module Blacklight
10
10
 
11
11
  class_option :devise, type: :boolean, default: false, aliases: "-d", desc: "Use Devise as authentication logic."
12
12
  class_option :marc, type: :boolean, default: false, aliases: "-m", desc: "Generate MARC-based demo."
13
+ class_option :'bootstrap-version', type: :string, default: nil, desc: "Set the generated app's bootstrap version"
13
14
  class_option :'skip-assets', type: :boolean, default: !defined?(Sprockets), desc: "Skip generating javascript and css assets into the application"
14
15
  class_option :'skip-solr', type: :boolean, default: false, desc: "Skip generating solr configurations."
15
16
 
@@ -33,7 +34,9 @@ module Blacklight
33
34
  # Call external generator in AssetsGenerator, so we can
34
35
  # leave that callable seperately too.
35
36
  def copy_public_assets
36
- generate "blacklight:assets" unless options[:'skip-assets']
37
+ generated_options = "--bootstrap-version #{options[:'bootstrap-version']}" if options[:'bootstrap-version']
38
+
39
+ generate "blacklight:assets", generated_options unless options[:'skip-assets']
37
40
  end
38
41
 
39
42
  def bundle_install
@@ -22,7 +22,7 @@ module Blacklight
22
22
  return unless options[:devise]
23
23
 
24
24
  gem "devise"
25
- gem "devise-guests", "~> 0.6"
25
+ gem "devise-guests", "~> 0.8"
26
26
 
27
27
  inside destination_root do
28
28
  Bundler.with_clean_env do
data/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "blacklight-frontend",
3
- "version": "7.10.0",
4
- "description": "[![Build Status](https://travis-ci.com/projectblacklight/blacklight.png?branch=master)](https://travis-ci.com/projectblacklight/blacklight) [![Gem Version](https://badge.fury.io/rb/blacklight.png)](http://badge.fury.io/rb/blacklight) [![Coverage Status](https://coveralls.io/repos/github/projectblacklight/blacklight/badge.svg?branch=master)](https://coveralls.io/github/projectblacklight/blacklight?branch=master)",
3
+ "version": "7.21.1",
4
+ "description": "[![Build Status](https://travis-ci.com/projectblacklight/blacklight.png?branch=main)](https://travis-ci.com/projectblacklight/blacklight) [![Gem Version](https://badge.fury.io/rb/blacklight.png)](http://badge.fury.io/rb/blacklight) [![Coverage Status](https://coveralls.io/repos/github/projectblacklight/blacklight/badge.svg?branch=main)](https://coveralls.io/github/projectblacklight/blacklight?branch=main)",
5
5
  "main": "app/assets/javascripts/blacklight",
6
6
  "scripts": {
7
7
  "js-compile-bundle": "shx cat app/javascript/blacklight/core.js app/javascript/blacklight/autocomplete.js app/javascript/blacklight/bookmark_toggle.js app/javascript/blacklight/button_focus.js app/javascript/blacklight/checkbox_submit.js app/javascript/blacklight/facet_load.js app/javascript/blacklight/modal.js app/javascript/blacklight/search_context.js | shx sed \"s/^(import|export).*//\" | babel --filename app/javascript/blacklight/blacklight.js > app/assets/javascripts/blacklight/blacklight.js"
@@ -23,11 +23,13 @@
23
23
  "devDependencies": {
24
24
  "@babel/cli": "^7.2.3",
25
25
  "@babel/core": "^7.2.3",
26
+ "@babel/preset-env": "^7.16.0",
26
27
  "shx": "^0.3.2"
27
28
  },
29
+ "browserslist": "> 0.25%, not dead",
28
30
  "dependencies": {
29
31
  "bloodhound-js": "^1.2.3",
30
- "bootstrap": "^4.3.1",
32
+ "bootstrap": ">=4.3.1 <6.0.0",
31
33
  "jquery": "^3.5.1",
32
34
  "typeahead.js": "^0.11.1"
33
35
  }
@@ -20,7 +20,9 @@ RSpec.describe Blacklight::FacetItemComponent, type: :component do
20
20
 
21
21
  it 'links to the facet and shows the number of hits' do
22
22
  expect(rendered).to have_selector 'li'
23
- expect(rendered).to have_link 'x', href: '/catalog?f=x'
23
+ expect(rendered).to have_link 'x', href: '/catalog?f=x' do |link|
24
+ link['rel'] == 'nofollow'
25
+ end
24
26
  expect(rendered).to have_selector '.facet-count', text: '10'
25
27
  end
26
28
 
@@ -39,7 +41,9 @@ RSpec.describe Blacklight::FacetItemComponent, type: :component do
39
41
  it 'links to the facet and shows the number of hits' do
40
42
  expect(rendered).to have_selector 'li'
41
43
  expect(rendered).to have_selector '.selected', text: 'x'
42
- expect(rendered).to have_link '[remove]', href: '/catalog'
44
+ expect(rendered).to have_link '[remove]', href: '/catalog' do |link|
45
+ link['rel'] == 'nofollow'
46
+ end
43
47
  expect(rendered).to have_selector '.selected.facet-count', text: '10'
44
48
  end
45
49
  end
@@ -501,6 +501,12 @@ RSpec.describe CatalogController, api: true do
501
501
  end
502
502
 
503
503
  describe "email", api: false do
504
+ let(:config) { Blacklight::Configuration.new }
505
+
506
+ before do
507
+ allow(controller).to receive(:blacklight_config).and_return(config)
508
+ end
509
+
504
510
  it "gives error if no TO parameter" do
505
511
  post :email, params: { id: doc_id }
506
512
  expect(request.flash[:error]).to eq "You must enter a recipient in order to send this message"
@@ -518,7 +524,7 @@ RSpec.describe CatalogController, api: true do
518
524
 
519
525
  it "redirects back to the record upon success" do
520
526
  allow(RecordMailer).to receive(:email_record)
521
- .with(anything, { to: 'test_email@projectblacklight.org', message: 'xyz' }, hash_including(host: 'test.host'))
527
+ .with(anything, { to: 'test_email@projectblacklight.org', message: 'xyz', config: config }, hash_including(host: 'test.host'))
522
528
  .and_return double(deliver: nil)
523
529
  post :email, params: { id: doc_id, to: 'test_email@projectblacklight.org', message: 'xyz' }
524
530
  expect(request.flash[:error]).to be_nil
@@ -533,6 +539,12 @@ RSpec.describe CatalogController, api: true do
533
539
  end
534
540
 
535
541
  describe "sms", api: false do
542
+ let(:config) { Blacklight::Configuration.new }
543
+
544
+ before do
545
+ allow(controller).to receive(:blacklight_config).and_return(config)
546
+ end
547
+
536
548
  it "gives error if no phone number is given" do
537
549
  post :sms, params: { id: doc_id, carrier: 'att' }
538
550
  expect(request.flash[:error]).to eq "You must enter a recipient's phone number in order to send this message"
@@ -562,7 +574,7 @@ RSpec.describe CatalogController, api: true do
562
574
  it "sends to the appropriate carrier email address" do
563
575
  expect(RecordMailer)
564
576
  .to receive(:sms_record)
565
- .with(anything, { to: '5555555555@txt.att.net' }, hash_including(host: 'test.host'))
577
+ .with(anything, { to: '5555555555@txt.att.net', config: config }, hash_including(host: 'test.host'))
566
578
  .and_return double(deliver: nil)
567
579
  post :sms, params: { id: doc_id, to: '5555555555', carrier: 'txt.att.net' }
568
580
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe 'Accessibility testing', api: false, js: true do
4
+ it 'validates the home page' do
5
+ visit root_path
6
+ expect(page).to be_accessible
7
+ end
8
+
9
+ it 'validates the catalog page' do
10
+ visit root_path
11
+ fill_in "q", with: 'history'
12
+ click_button 'search'
13
+
14
+ expect(page).to be_accessible
15
+
16
+ within '.card.blacklight-language_ssim' do
17
+ click_button 'Language'
18
+ click_link "Tibetan"
19
+ end
20
+
21
+ expect(page).to be_accessible
22
+ end
23
+
24
+ it 'validates the single results page' do
25
+ visit solr_document_path('2007020969')
26
+ expect(page).to be_accessible
27
+ end
28
+
29
+ def be_accessible(skipping: [])
30
+ # typeahead does funny things with the search bar
31
+ be_axe_clean.excluding('.tt-hint').skipping(skipping + [('color-contrast' if Bootstrap::VERSION < '5')].compact)
32
+ end
33
+ end