blacklight 7.19.2 → 7.21.1

Sign up to get free protection for your applications and to get access to all the features.
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