blacklight 7.1.0 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.jshintrc +3 -1
  3. data/.rubocop_todo.yml +8 -8
  4. data/.travis.yml +13 -13
  5. data/CONTRIBUTING.md +3 -1
  6. data/Gemfile +2 -0
  7. data/VERSION +1 -1
  8. data/app/assets/images/blacklight/list.svg +1 -1
  9. data/app/assets/images/blacklight/search.svg +1 -1
  10. data/app/assets/javascripts/blacklight/blacklight.js +82 -70
  11. data/app/assets/stylesheets/blacklight/_bootstrap_overrides.scss +17 -13
  12. data/app/assets/stylesheets/blacklight/_facets.scss +3 -0
  13. data/app/assets/stylesheets/blacklight/_header.scss +26 -0
  14. data/app/assets/stylesheets/blacklight/_icons.scss +6 -6
  15. data/app/assets/stylesheets/blacklight/_mixins.scss +3 -3
  16. data/app/controllers/concerns/blacklight/facet.rb +1 -7
  17. data/app/helpers/blacklight/catalog_helper_behavior.rb +16 -17
  18. data/app/helpers/blacklight/layout_helper_behavior.rb +2 -2
  19. data/app/helpers/blacklight/url_helper_behavior.rb +1 -1
  20. data/app/javascript/blacklight/button_focus.js +9 -0
  21. data/app/javascript/blacklight/facet_load.js +18 -19
  22. data/app/javascript/blacklight/search_context.js +67 -49
  23. data/app/models/blacklight/icon.rb +26 -5
  24. data/app/presenters/blacklight/search_bar_presenter.rb +3 -1
  25. data/app/views/catalog/_bookmark_control.html.erb +11 -13
  26. data/app/views/catalog/_facet_group.html.erb +1 -1
  27. data/app/views/catalog/_facet_layout.html.erb +9 -2
  28. data/app/views/catalog/_facets.html.erb +1 -1
  29. data/app/views/catalog/_per_page_widget.html.erb +1 -1
  30. data/app/views/catalog/_search_form.html.erb +1 -1
  31. data/app/views/catalog/_search_results.html.erb +4 -0
  32. data/app/views/catalog/_sort_widget.html.erb +1 -1
  33. data/app/views/layouts/blacklight.html.erb +2 -2
  34. data/app/views/layouts/blacklight/base.html.erb +14 -8
  35. data/config/locales/blacklight.de.yml +17 -2
  36. data/config/locales/blacklight.en.yml +17 -2
  37. data/config/locales/blacklight.es.yml +17 -2
  38. data/config/locales/blacklight.fr.yml +17 -2
  39. data/config/locales/blacklight.hu.yml +17 -3
  40. data/config/locales/blacklight.it.yml +17 -2
  41. data/config/locales/blacklight.nl.yml +17 -3
  42. data/config/locales/blacklight.pt-BR.yml +17 -2
  43. data/config/locales/blacklight.sq.yml +17 -2
  44. data/config/locales/blacklight.zh.yml +17 -3
  45. data/lib/blacklight/configuration.rb +6 -0
  46. data/lib/blacklight/engine.rb +5 -4
  47. data/lib/blacklight/solr.rb +2 -0
  48. data/{app/models/concerns → lib}/blacklight/solr/document.rb +0 -0
  49. data/{app/models → lib}/blacklight/solr/facet_paginator.rb +0 -0
  50. data/lib/blacklight/solr/response/group_response.rb +10 -0
  51. data/lib/blacklight/solr/response/pagination_methods.rb +12 -0
  52. data/lib/generators/blacklight/assets_generator.rb +4 -1
  53. data/package-lock.json +1 -1
  54. data/package.json +2 -2
  55. data/spec/features/facets_spec.rb +22 -1
  56. data/spec/features/search_filters_spec.rb +25 -5
  57. data/spec/helpers/blacklight/layout_helper_behavior_spec.rb +4 -4
  58. data/spec/helpers/catalog_helper_spec.rb +0 -9
  59. data/spec/models/blacklight/icon_spec.rb +25 -1
  60. data/spec/models/blacklight/solr/response/group_response_spec.rb +13 -0
  61. data/spec/models/blacklight/solr/response_spec.rb +3 -0
  62. data/spec/presenters/blacklight/search_bar_presenter_spec.rb +15 -0
  63. data/spec/views/catalog/_facet_group.html.erb_spec.rb +82 -0
  64. data/spec/views/catalog/_facet_layout.html.erb_spec.rb +1 -1
  65. data/spec/views/catalog/_facets.html.erb_spec.rb +5 -66
  66. metadata +7 -5
  67. data/app/javascript/blacklight/collapsable.js +0 -9
@@ -13,22 +13,22 @@
13
13
 
14
14
  @each $color, $value in $theme-colors {
15
15
  .btn-#{$color} {
16
- .blacklight-icons g {
17
- @include stroke-yiq(theme-color($color));
16
+ .blacklight-icons svg {
17
+ @include fill-yiq(theme-color($color));
18
18
  }
19
19
  }
20
20
 
21
21
  .btn-outline-#{$color} {
22
- .blacklight-icons g {
23
- stroke: $value;
22
+ .blacklight-icons svg {
23
+ fill: $value;
24
24
  }
25
25
 
26
26
  &.hover,
27
27
  &:hover,
28
28
  &:active,
29
29
  &.active {
30
- .blacklight-icons g {
31
- @include stroke-yiq(theme-color($color));
30
+ .blacklight-icons svg {
31
+ @include fill-yiq(theme-color($color));
32
32
  }
33
33
  }
34
34
  }
@@ -1,6 +1,6 @@
1
1
  // Stroke contrast
2
2
  // Ripped off from https://github.com/twbs/bootstrap/commit/c31d52499811d5c68d122db806ce27a112b489bd
3
- @mixin stroke-yiq($color) {
3
+ @mixin fill-yiq($color) {
4
4
  $r: red($color);
5
5
  $g: green($color);
6
6
  $b: blue($color);
@@ -8,8 +8,8 @@
8
8
  $yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
9
9
 
10
10
  @if ($yiq >= 150) {
11
- stroke: #111;
11
+ fill: #111;
12
12
  } @else {
13
- stroke: #fff;
13
+ fill: #fff;
14
14
  }
15
15
  }
@@ -4,7 +4,7 @@ module Blacklight
4
4
  # They are only dependent on `blacklight_config` and `@response`
5
5
  #
6
6
  module Facet
7
- delegate :facet_configuration_for_field, to: :blacklight_config
7
+ delegate :facet_configuration_for_field, :facet_field_names, to: :blacklight_config
8
8
 
9
9
  def facet_paginator(field_config, response_data)
10
10
  blacklight_config.facet_paginator_class.new(
@@ -22,12 +22,6 @@ module Blacklight
22
22
  fields.map { |field| facet_by_field_name(field) }.compact
23
23
  end
24
24
 
25
- # @param group [String] a group name of facet fields
26
- # @return [Array<String>] a list of the facet field names from the configuration
27
- def facet_field_names(group = nil)
28
- blacklight_config.facet_fields.select { |_facet, opts| group == opts[:group] }.values.map(&:field)
29
- end
30
-
31
25
  def facet_group_names
32
26
  blacklight_config.facet_fields.map { |_facet, opts| opts[:group] }.uniq
33
27
  end
@@ -3,13 +3,13 @@ module Blacklight::CatalogHelperBehavior
3
3
  extend Deprecation
4
4
  self.deprecation_horizon = 'blacklight 8.0'
5
5
 
6
- include ConfigurationHelperBehavior
7
- include ComponentHelperBehavior
8
- include FacetsHelperBehavior
9
- include RenderConstraintsHelperBehavior
10
- include RenderPartialsHelperBehavior
11
- include SearchHistoryConstraintsHelperBehavior
12
- include SuggestHelperBehavior
6
+ include Blacklight::ConfigurationHelperBehavior
7
+ include Blacklight::ComponentHelperBehavior
8
+ include Blacklight::FacetsHelperBehavior
9
+ include Blacklight::RenderConstraintsHelperBehavior
10
+ include Blacklight::RenderPartialsHelperBehavior
11
+ include Blacklight::SearchHistoryConstraintsHelperBehavior
12
+ include Blacklight::SuggestHelperBehavior
13
13
 
14
14
  # @param [Hash] options
15
15
  # @option options :route_set the route scope to use when constructing the link
@@ -31,22 +31,21 @@ module Blacklight::CatalogHelperBehavior
31
31
 
32
32
  ##
33
33
  # Override the Kaminari page_entries_info helper with our own, blacklight-aware
34
- # implementation.
35
- # Displays the "showing X through Y of N" message.
34
+ # implementation. Why do we have to do this?
35
+ # - We need custom counting information for grouped results
36
+ # - We need to provide number_with_delimiter strings to i18n keys
37
+ # If we didn't have to do either one of these, we could get away with removing
38
+ # this entirely.
36
39
  #
37
40
  # @param [RSolr::Resource] collection (or other Kaminari-compatible objects)
38
41
  # @return [String]
39
- def page_entries_info(collection, options = {})
42
+ def page_entries_info(collection, entry_name: nil)
40
43
  return unless show_pagination? collection
41
44
 
42
- entry_name = if options[:entry_name]
43
- options[:entry_name]
44
- elsif collection.respond_to? :model # DataMapper
45
- collection.model.model_name.human.downcase
46
- elsif collection.respond_to?(:model_name) && !collection.model_name.nil? # AR, Blacklight::PaginationMethods
47
- collection.model_name.human.downcase
45
+ entry_name = if entry_name
46
+ entry_name.pluralize(collection.size, I18n.locale)
48
47
  else
49
- t('blacklight.entry_name.default')
48
+ collection.entry_name(count: collection.size).downcase
50
49
  end
51
50
 
52
51
  entry_name = entry_name.pluralize unless collection.total_count == 1
@@ -22,14 +22,14 @@ module Blacklight
22
22
  # Classes used for sizing the main content of a Blacklight page
23
23
  # @return [String]
24
24
  def main_content_classes
25
- 'col-md-9'
25
+ 'col-lg-9'
26
26
  end
27
27
 
28
28
  ##
29
29
  # Classes used for sizing the sidebar content of a Blacklight page
30
30
  # @return [String]
31
31
  def sidebar_classes
32
- 'page-sidebar col-md-3'
32
+ 'page-sidebar col-lg-3'
33
33
  end
34
34
 
35
35
  ##
@@ -66,7 +66,7 @@ module Blacklight::UrlHelperBehavior
66
66
  # @param [Integer] counter
67
67
  # @example
68
68
  # session_tracking_params(SolrDocument.new(id: 123), 7)
69
- # => { data: { :'tracker-href' => '/catalog/123/track?counter=7&search_id=999' } }
69
+ # => { data: { :'context-href' => '/catalog/123/track?counter=7&search_id=999' } }
70
70
  def session_tracking_params document, counter
71
71
  path = session_tracking_path(document, per_page: params.fetch(:per_page, search_session['per_page']), counter: counter, search_id: current_search_session.try(:id))
72
72
 
@@ -0,0 +1,9 @@
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
+ document.querySelectorAll('button.collapse-toggle').forEach((button) => {
5
+ button.addEventListener('click', () => {
6
+ event.target.focus();
7
+ });
8
+ });
9
+ });
@@ -1,23 +1,22 @@
1
1
  /*global Blacklight */
2
2
 
3
- (function($) {
4
- 'use strict';
5
-
6
- Blacklight.doResizeFacetLabelsAndCounts = function() {
7
- // adjust width of facet columns to fit their contents
8
- function longer (a,b){ return b.textContent.length - a.textContent.length; }
3
+ 'use strict';
9
4
 
10
- $('ul.facet-values, ul.pivot-facet').each(function(){
11
- var longest = $(this).find('span.facet-count').sort(longer)[0];
12
-
13
- if (longest && longest.textContent) {
14
- var width = longest.textContent.length + 1 + 'ch';
15
- $(this).find('.facet-count').first().width(width);
16
- }
17
- });
18
- };
5
+ Blacklight.doResizeFacetLabelsAndCounts = function() {
6
+ // adjust width of facet columns to fit their contents
7
+ function longer (a,b) { return b.textContent.length - a.textContent.length }
19
8
 
20
- Blacklight.onLoad(function() {
21
- Blacklight.doResizeFacetLabelsAndCounts();
22
- });
23
- })(jQuery);
9
+ document.querySelectorAll('.facet-values, .pivot-facet').forEach(function(elem){
10
+ const nodes = elem.querySelectorAll('.facet-count')
11
+ // TODO: when we drop ie11 support, this can become the spread operator:
12
+ const longest = Array.from(nodes).sort(longer)[0]
13
+ if (longest && longest.textContent) {
14
+ const width = longest.textContent.length + 1 + 'ch'
15
+ elem.querySelector('.facet-count').style.width = width
16
+ }
17
+ })
18
+ }
19
+
20
+ Blacklight.onLoad(function() {
21
+ Blacklight.doResizeFacetLabelsAndCounts()
22
+ })
@@ -1,49 +1,67 @@
1
- (function($) {
2
- Blacklight.doSearchContextBehavior = function() {
3
- if (typeof Blacklight.do_search_context_behavior == 'function') {
4
- console.warn("do_search_context_behavior is deprecated. Use doSearchContextBehavior instead.");
5
- return Blacklight.do_search_context_behavior();
6
- }
7
- $('a[data-context-href]').on('click.search-context', Blacklight.handleSearchContextMethod);
8
- };
9
-
10
- // this is the $.rails.handleMethod with a couple adjustments, described inline:
11
- // first, we're attaching this directly to the event handler, so we can check for meta-keys
12
- Blacklight.handleSearchContextMethod = function(event) {
13
- if (typeof Blacklight.handle_search_context_method == 'function') {
14
- console.warn("handle_search_context_method is deprecated. Use handleSearchContextMethod instead.");
15
- return Blacklight.handle_search_context_method(event);
16
- }
17
- var link = $(this);
18
-
19
- // instead of using the normal href, we need to use the context href instead
20
- var href = link.data('context-href'),
21
- method = 'post',
22
- target = link.attr('target'),
23
- csrfToken = $('meta[name=csrf-token]').attr('content'),
24
- csrfParam = $('meta[name=csrf-param]').attr('content'),
25
- form = $('<form method="post" action="' + href + '"></form>'),
26
- metadataInput = '<input name="_method" value="' + method + '" type="hidden" />',
27
- redirectHref = '<input name="redirect" value="' + link.attr('href') + '" type="hidden" />';
28
-
29
- // check for meta keys.. if set, we should open in a new tab
30
- if(event.metaKey || event.ctrlKey) {
31
- target = '_blank';
32
- }
33
-
34
- if (csrfParam !== undefined && csrfToken !== undefined) {
35
- metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />';
36
- }
37
-
38
- if (target) { form.attr('target', target); }
39
-
40
- form.hide().append(metadataInput).append(redirectHref).appendTo('body');
41
- form.submit();
42
-
43
- return false;
44
- };
45
-
46
- Blacklight.onLoad(function() {
47
- Blacklight.doSearchContextBehavior();
48
- });
49
- })(jQuery);
1
+ Blacklight.doSearchContextBehavior = function() {
2
+ if (typeof Blacklight.do_search_context_behavior == 'function') {
3
+ console.warn("do_search_context_behavior is deprecated. Use doSearchContextBehavior instead.");
4
+ return Blacklight.do_search_context_behavior();
5
+ }
6
+
7
+ const elements = document.querySelectorAll('a[data-context-href]')
8
+ // Equivalent to Array.from(), but supports ie11
9
+ const nodes = Array.prototype.slice.call(elements)
10
+
11
+ nodes.forEach(function(element) {
12
+ element.addEventListener('click', function(e) {
13
+ Blacklight.handleSearchContextMethod.call(e.target, e)
14
+ })
15
+ })
16
+ };
17
+
18
+ // this is the Rails.handleMethod with a couple adjustments, described inline:
19
+ // first, we're attaching this directly to the event handler, so we can check for meta-keys
20
+ Blacklight.handleSearchContextMethod = function(event) {
21
+ if (typeof Blacklight.handle_search_context_method == 'function') {
22
+ console.warn("handle_search_context_method is deprecated. Use handleSearchContextMethod instead.");
23
+ return Blacklight.handle_search_context_method(event);
24
+ }
25
+ var link = this
26
+
27
+ // instead of using the normal href, we need to use the context href instead
28
+ let href = link.getAttribute('data-context-href')
29
+ let target = link.getAttribute('target')
30
+ let csrfToken = Rails.csrfToken()
31
+ let csrfParam = Rails.csrfParam()
32
+ let form = document.createElement('form')
33
+ form.method = 'post'
34
+ form.action = href
35
+
36
+
37
+ let formContent = `<input name="_method" value="post" type="hidden" />
38
+ <input name="redirect" value="${link.getAttribute('href')}" type="hidden" />`
39
+
40
+ // check for meta keys.. if set, we should open in a new tab
41
+ if(event.metaKey || event.ctrlKey) {
42
+ target = '_blank';
43
+ }
44
+
45
+ if (csrfParam !== undefined && csrfToken !== undefined) {
46
+ formContent += `<input name="${csrfParam}" value="${csrfToken}" type="hidden" />`
47
+ }
48
+
49
+ // Must trigger submit by click on a button, else "submit" event handler won't work!
50
+ // https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit
51
+ formContent += '<input type="submit" />'
52
+
53
+ if (target) { form.setAttribute('target', target); }
54
+
55
+ form.style.display = 'none'
56
+ form.innerHTML = formContent
57
+ document.body.appendChild(form)
58
+ form.querySelector('[type="submit"]').click()
59
+
60
+ event.preventDefault()
61
+ event.stopPropagation()
62
+ event.stopImmediatePropagation()
63
+ };
64
+
65
+ Blacklight.onLoad(function() {
66
+ Blacklight.doSearchContextBehavior();
67
+ });
@@ -2,22 +2,39 @@
2
2
 
3
3
  module Blacklight
4
4
  class Icon
5
- attr_reader :icon_name
5
+ attr_reader :icon_name, :aria_hidden, :label, :role
6
6
  ##
7
7
  # @param [String, Symbol] icon_name
8
8
  # @param [Hash] options
9
9
  # @param [String] classes additional classes separated by a string
10
- def initialize(icon_name, classes: '', aria_hidden: false)
10
+ # @param [Boolean] aria_hidden include aria_hidden attribute
11
+ # @param [Boolean] label include <title> and aria-label as part of svg
12
+ # @param [String] role role attribute to be included in svg
13
+ def initialize(icon_name, classes: '', aria_hidden: false, label: true, role: 'image')
11
14
  @icon_name = icon_name
12
15
  @classes = classes
13
16
  @aria_hidden = aria_hidden
17
+ @label = label
18
+ @role = role
14
19
  end
15
20
 
16
21
  ##
17
- # Returns the raw source, but you could extend this to add additional attributes
22
+ # Returns an updated version of the svg source
18
23
  # @return [String]
19
24
  def svg
20
- file_source
25
+ svg = ng_xml.at_xpath('svg')
26
+ svg['role'] = role
27
+ svg['aria-labelled-by'] = unique_id if label
28
+ svg.add_child("<title id='#{unique_id}'>#{icon_label}</title>") if label
29
+ ng_xml.to_xml
30
+ end
31
+
32
+ def icon_label
33
+ I18n.translate("blacklight.icon.#{icon_name}", default: "#{icon_name} icon")
34
+ end
35
+
36
+ def unique_id
37
+ @unique_id ||= "bl-icon-#{icon_name}-#{SecureRandom.hex(8)}"
21
38
  end
22
39
 
23
40
  ##
@@ -25,7 +42,7 @@ module Blacklight
25
42
  def options
26
43
  {
27
44
  class: classes,
28
- "aria-hidden": (true if @aria_hidden)
45
+ "aria-hidden": (true if aria_hidden)
29
46
  }
30
47
  end
31
48
 
@@ -43,6 +60,10 @@ module Blacklight
43
60
  file.source.force_encoding('UTF-8')
44
61
  end
45
62
 
63
+ def ng_xml
64
+ @ng_xml ||= Nokogiri::XML(file_source).remove_namespaces!
65
+ end
66
+
46
67
  private
47
68
 
48
69
  def file
@@ -31,7 +31,9 @@ module Blacklight
31
31
  #
32
32
  # @return [Boolean]
33
33
  def autofocus?
34
- controller.is_a?(Blacklight::Catalog) &&
34
+ configuration.enable_search_bar_autofocus.present? &&
35
+ configuration.enable_search_bar_autofocus &&
36
+ controller.is_a?(Blacklight::Catalog) &&
35
37
  controller.action_name == "index" &&
36
38
  !controller.has_search_parameters?
37
39
  end
@@ -4,35 +4,33 @@
4
4
  # but it was simpler to leave them seperate instead of DRYing them, got confusing trying that.
5
5
  # the data-doc-id attribute is used by our JS that converts to a checkbox/label.
6
6
  -%>
7
- <% unless bookmarked? document %>
7
+ <% if bookmarked? document %>
8
8
  <%= form_tag(bookmark_path(document),
9
- method: :put,
10
- class: 'bookmark-toggle',
9
+ method: :delete,
10
+ class: "bookmark-toggle",
11
11
  data: {
12
12
  'doc-id' => document.id,
13
13
  present: t('blacklight.search.bookmarks.present'),
14
14
  absent: t('blacklight.search.bookmarks.absent'),
15
15
  inprogress: t('blacklight.search.bookmarks.inprogress')
16
16
  }) do %>
17
- <%= submit_tag(t('blacklight.bookmarks.add.button'),
17
+ <%= submit_tag(t('blacklight.bookmarks.remove.button'),
18
18
  id: "bookmark_toggle_#{document.id.to_s.parameterize}",
19
- class: "bookmark-add btn btn-outline-secondary") %>
19
+ class: "bookmark-remove btn btn-outline-secondary") %>
20
20
  <% end %>
21
21
  <% else %>
22
22
  <%= form_tag(bookmark_path(document),
23
- method: :delete,
24
- class: "bookmark-toggle",
23
+ method: :put,
24
+ class: 'bookmark-toggle',
25
25
  data: {
26
26
  'doc-id' => document.id,
27
27
  present: t('blacklight.search.bookmarks.present'),
28
28
  absent: t('blacklight.search.bookmarks.absent'),
29
29
  inprogress: t('blacklight.search.bookmarks.inprogress')
30
- }) do %>
31
- <%= submit_tag(t('blacklight.bookmarks.remove.button'),
32
- id: "bookmark_toggle_#{document.id.to_s.parameterize}",
33
- class: "bookmark-remove btn btn-outline-secondary") %>
30
+ }) do %>
31
+ <%= submit_tag(t('blacklight.bookmarks.add.button'),
32
+ id: "bookmark_toggle_#{document.id.to_s.parameterize}",
33
+ class: "bookmark-add btn btn-outline-secondary") %>
34
34
  <% end %>
35
35
  <% end %>
36
- <% else %>
37
- &nbsp;
38
36
  <% end %>
@@ -1,6 +1,6 @@
1
1
  <% # main container for facets/limits menu -%>
2
2
  <% if has_facet_values? facet_field_names(groupname) %>
3
- <div id="facets<%= "-#{groupname}" unless groupname.nil? %>" class="facets sidenav facets-toggleable-sm">
3
+ <div id="facets<%= "-#{groupname}" unless groupname.nil? %>" class="facets sidenav facets-toggleable-md">
4
4
 
5
5
  <div class="navbar">
6
6
  <h2 class="facets-heading">