blacklight 7.1.0 → 7.2.0

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