From 47a0e695991b5b719e92a6c2362c560e8d2efcb2 Mon Sep 17 00:00:00 2001 From: Justin Coyne Date: Wed, 8 Jul 2020 11:33:22 -0500 Subject: [PATCH] Componentize hierarchy rendering --- .../hierarchy/facet_field_component.html.erb | 18 +++++ .../hierarchy/facet_field_component.rb | 33 ++++++++++ .../facet_field_list_component.html.erb | 4 +- .../hierarchy/facet_field_list_component.rb | 65 ++++++++++++++++++- .../hierarchy/qfacet_value_component.html.erb | 1 + .../hierarchy/qfacet_value_component.rb | 26 ++++++++ .../selected_qfacet_value_component.html.erb | 4 ++ .../selected_qfacet_value_component.rb | 19 ++++++ app/helpers/blacklight/hierarchy_helper.rb | 16 ++++- app/models/hierarchical_facet_item.rb | 1 + blacklight-hierarchy.gemspec | 1 + spec/helpers/hierarchy_helper_spec.rb | 1 + 12 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 app/components/blacklight/hierarchy/facet_field_component.html.erb create mode 100644 app/components/blacklight/hierarchy/facet_field_component.rb create mode 100644 app/components/blacklight/hierarchy/qfacet_value_component.html.erb create mode 100644 app/components/blacklight/hierarchy/qfacet_value_component.rb create mode 100644 app/components/blacklight/hierarchy/selected_qfacet_value_component.html.erb create mode 100644 app/components/blacklight/hierarchy/selected_qfacet_value_component.rb create mode 100644 app/models/hierarchical_facet_item.rb diff --git a/app/components/blacklight/hierarchy/facet_field_component.html.erb b/app/components/blacklight/hierarchy/facet_field_component.html.erb new file mode 100644 index 0000000..403e877 --- /dev/null +++ b/app/components/blacklight/hierarchy/facet_field_component.html.erb @@ -0,0 +1,18 @@ +
  • + <%= helpers.facet_toggle_button(field_name, id) if subset.any? %> + <% if item.nil? %> + <%= key %> + <% elsif qfacet_selected? %> + <%= render Blacklight::Hierarchy::SelectedQfacetValueComponent.new(field_name: field_name, item: item) %> + <% else %> + <%= render Blacklight::Hierarchy::QfacetValueComponent.new(field_name: field_name, item: item, id: id) %> + <% end %> + + <% unless subset.empty? %> + + <% end %> +
  • diff --git a/app/components/blacklight/hierarchy/facet_field_component.rb b/app/components/blacklight/hierarchy/facet_field_component.rb new file mode 100644 index 0000000..7b7a157 --- /dev/null +++ b/app/components/blacklight/hierarchy/facet_field_component.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Blacklight + module Hierarchy + class FacetFieldComponent < ::ViewComponent::Base + def initialize(field_name:, tree:, key:) + @field_name = field_name + @tree = tree + @key = key + @id = SecureRandom.uuid + end + + attr_reader :field_name, :tree, :key, :id + + def subset + @subset ||= tree.reject { |k, _v| !k.is_a?(String) } + end + + def li_class + subset.empty? ? 'h-leaf' : 'h-node' + end + + def item + tree[:_] + end + + def qfacet_selected? + config = helpers.facet_configuration_for_field(field_name) + helpers.search_state.has_facet?(config, value: item.qvalue) + end + end + end +end diff --git a/app/components/blacklight/hierarchy/facet_field_list_component.html.erb b/app/components/blacklight/hierarchy/facet_field_list_component.html.erb index 2ac58f1..182c76d 100644 --- a/app/components/blacklight/hierarchy/facet_field_list_component.html.erb +++ b/app/components/blacklight/hierarchy/facet_field_list_component.html.erb @@ -4,7 +4,9 @@ <% end %> <% component.with(:body) do %> <% end %> <% end %> diff --git a/app/components/blacklight/hierarchy/facet_field_list_component.rb b/app/components/blacklight/hierarchy/facet_field_list_component.rb index 700478f..aaf8c96 100644 --- a/app/components/blacklight/hierarchy/facet_field_list_component.rb +++ b/app/components/blacklight/hierarchy/facet_field_list_component.rb @@ -3,8 +3,69 @@ module Blacklight module Hierarchy class FacetFieldListComponent < Blacklight::FacetFieldListComponent - def render_hierarchy - helpers.render_hierarchy(@facet_field.facet_field) + DELIMETER = '_' + + # @param [Blacklight::Configuration::FacetField] as defined in controller with config.add_facet_field (and with :partial => 'blacklight/hierarchy/facet_hierarchy') + # @return [String] html for the facet tree + def tree + @tree ||= begin + facet_tree_for_prefix = facet_tree + facet_tree_for_prefix ? facet_tree_for_prefix[field_name] : nil + end + end + + def field_name + @facet_field.facet_field.field + end + + # @return [String] a key to access the rest of the hierarchy tree, as defined in controller config.facet_display[:hierarchy] declaration. + # e.g. if you had this in controller: + # config.facet_display = { + # :hierarchy => { + # 'wf' => [['wps','wsp','swp'], ':'], + # 'callnum_top' => [['facet'], '/'], + # 'exploded_tag' => [['ssim'], ':'] + # } + # } + # then possible hkey values would be 'wf', 'callnum_top', and 'exploded_tag'. + # + # the key in the :hierarchy hash is the "prefix" for the solr field with the hierarchy info. the value + # in the hash is a list, where the first element is a list of suffixes, and the second element is the delimiter + # used to break up the sections of hierarchical data in the solr field being read. when joined, the prefix and + # suffix should form the field name. so, for example, 'wf_wps', 'wf_wsp', 'wf_swp', 'callnum_top_facet', and + # 'exploded_tag_ssim' would be the solr fields with blacklight-hierarchy related configuration according to the + # hash above. ':' would be the delimiter used in all of those fields except for 'callnum_top_facet', which would + # use '/'. exploded_tag_ssim might contain values like ['Book', 'Book : Multi-Volume Work'], and callnum_top_facet + # might contain values like ['LB', 'LB/2395', 'LB/2395/.C65', 'LB/2395/.C65/1991']. + # note: the suffixes (e.g. 'ssim' for 'exploded_tag' in the above example) can't have underscores, otherwise things break. + def prefix + @prefix ||= field_name.gsub("#{DELIMETER}#{field_name.split(/#{DELIMETER}/).last}", '') + end + + + delegate :blacklight_config, to: :helpers + + def facet_tree + @facet_tree ||= {} + return @facet_tree[prefix] unless @facet_tree[prefix].nil? + return @facet_tree[prefix] unless blacklight_config.facet_display[:hierarchy] && blacklight_config.facet_display[:hierarchy][prefix] + @facet_tree[prefix] = {} + facet_config = blacklight_config.facet_display[:hierarchy][prefix] + split_regex = Regexp.new("\s*#{Regexp.escape(facet_config.length >= 2 ? facet_config[1] : ':')}\s*") + facet_config.first.each do |key| + # TODO: remove baked in notion of underscores being part of the blacklight facet field names + facet_field = [prefix, key].compact.join('_') + @facet_tree[prefix][facet_field] ||= {} + data = @facet_field.display_facet + next if data.nil? + data.items.each do |facet_item| + path = facet_item.value.split(split_regex) + loc = @facet_tree[prefix][facet_field] + loc = loc[path.shift] ||= {} while path.length > 0 + loc[:_] = HierarchicalFacetItem.new(facet_item.value, facet_item.value.split(split_regex).last, facet_item.hits) + end + end + @facet_tree[prefix] end end end diff --git a/app/components/blacklight/hierarchy/qfacet_value_component.html.erb b/app/components/blacklight/hierarchy/qfacet_value_component.html.erb new file mode 100644 index 0000000..a531fb4 --- /dev/null +++ b/app/components/blacklight/hierarchy/qfacet_value_component.html.erb @@ -0,0 +1 @@ +<%= link_to_unless suppress_link, item.value, path_for_facet, id: id, class: 'facet_select' %> <%= render_facet_count %> diff --git a/app/components/blacklight/hierarchy/qfacet_value_component.rb b/app/components/blacklight/hierarchy/qfacet_value_component.rb new file mode 100644 index 0000000..dc4bfc6 --- /dev/null +++ b/app/components/blacklight/hierarchy/qfacet_value_component.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Blacklight + module Hierarchy + class QfacetValueComponent < ::ViewComponent::Base + def initialize(field_name:, item:, id: nil, suppress_link: false) + @field_name = field_name + @item = item + @id = id + @suppress_link = suppress_link + end + + attr_reader :field_name, :item, :id, :suppress_link + + def path_for_facet + facet_config = helpers.facet_configuration_for_field(field_name) + Blacklight::FacetItemPresenter.new(item.qvalue, facet_config, helpers, field_name).href + end + + def render_facet_count + classes = "facet-count" + content_tag("span", t('blacklight.search.facets.count', number: number_with_delimiter(item.hits)), class: classes) + end + end + end +end diff --git a/app/components/blacklight/hierarchy/selected_qfacet_value_component.html.erb b/app/components/blacklight/hierarchy/selected_qfacet_value_component.html.erb new file mode 100644 index 0000000..02bee6e --- /dev/null +++ b/app/components/blacklight/hierarchy/selected_qfacet_value_component.html.erb @@ -0,0 +1,4 @@ +<%= render Blacklight::Hierarchy::QfacetValueComponent.new(field_name: field_name, item: item, suppress_link: true) %> +<%= link_to remove_href, class: 'remove' do %> + [remove] +<% end %> diff --git a/app/components/blacklight/hierarchy/selected_qfacet_value_component.rb b/app/components/blacklight/hierarchy/selected_qfacet_value_component.rb new file mode 100644 index 0000000..62244de --- /dev/null +++ b/app/components/blacklight/hierarchy/selected_qfacet_value_component.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Blacklight + module Hierarchy + # Standard display of a SELECTED facet value, no link, special span with class, and 'remove' button. + class SelectedQfacetValueComponent < ::ViewComponent::Base + def initialize(field_name:, item:) + @field_name = field_name + @item = item + end + + attr_reader :field_name, :item + + def remove_href + helpers.search_action_path(helpers.search_state.remove_facet_params(field_name, item.qvalue)) + end + end + end +end diff --git a/app/helpers/blacklight/hierarchy_helper.rb b/app/helpers/blacklight/hierarchy_helper.rb index 7be3404..55bf750 100644 --- a/app/helpers/blacklight/hierarchy_helper.rb +++ b/app/helpers/blacklight/hierarchy_helper.rb @@ -1,3 +1,5 @@ +require 'deprecation' + module Blacklight::HierarchyHelper # Putting bare HTML strings in a helper sucks. But in this case, with a # lot of recursive tree-walking going on, it's an order of magnitude faster @@ -28,6 +30,7 @@ def render_facet_hierarchy_item(field_name, data, key) %(
  • #{li.html_safe}#{ul.html_safe}
  • ).html_safe end + deprecation_deprecate :render_facet_hierarchy_item def qfacet_selected?(field_name, item) config = facet_configuration_for_field(field_name) @@ -48,6 +51,7 @@ def render_hierarchy(bl_facet_field, delim = '_') render_facet_hierarchy_item(field_name, tree[key], key) end.join("\n").html_safe end + deprecation_deprecate :render_hierarchy def render_qfacet_value(facet_solr_field, item, options = {}) id = options.delete(:id) @@ -55,6 +59,7 @@ def render_qfacet_value(facet_solr_field, item, options = {}) path_for_facet = facet_item_presenter(facet_config, item.qvalue, facet_solr_field).href (link_to_unless(options[:suppress_link], item.value, path_for_facet, id: id, class: 'facet_select') + ' ' + render_facet_count(item.hits)).html_safe end + deprecation_deprecate :render_qfacet_value # Standard display of a SELECTED facet value, no link, special span with class, and 'remove' button. def render_selected_qfacet_value(facet_solr_field, item) @@ -66,8 +71,7 @@ def render_selected_qfacet_value(facet_solr_field, item) class: 'remove' ) end - - HierarchicalFacetItem = Struct.new :qvalue, :value, :hits + deprecation_deprecate :render_selected_qfacet_value # @param [String] hkey - a key to access the rest of the hierarchy tree, as defined in controller config.facet_display[:hierarchy] declaration. # e.g. if you had this in controller: @@ -111,6 +115,7 @@ def facet_tree(hkey) end @facet_tree[hkey] end + deprecation_deprecate :facet_tree def facet_toggle_button(field_name, described_by) aria_label = I18n.t( @@ -139,11 +144,13 @@ def is_hierarchical?(field_name) (prefix, order) = field_name.split(/_/, 2) (list = blacklight_config.facet_display[:hierarchy][prefix]) && list.include?(order) end + deprecation_deprecate :is_hierarchical? def facet_order(prefix) param_name = "#{prefix}_facet_order".to_sym params[param_name] || blacklight_config.facet_display[:hierarchy][prefix].first end + deprecation_deprecate :facet_order def facet_after(prefix, order) orders = blacklight_config.facet_display[:hierarchy][prefix] @@ -156,6 +163,8 @@ def hide_facet?(field_name) prefix = field_name.split(/_/).first field_name != "#{prefix}_#{facet_order(prefix)}" end + deprecation_deprecate :hide_facet? + # FIXME: remove baked in colon separator def rotate_facet_value(val, from, to) @@ -165,6 +174,7 @@ def rotate_facet_value(val, from, to) return nil if new_values.include?(nil) new_values.compact.join(':') end + deprecation_deprecate :rotate_facet_value # FIXME: remove baked in underscore separator in field name def rotate_facet_params(prefix, from, to, p = params.dup) @@ -180,6 +190,7 @@ def rotate_facet_params(prefix, from, to, p = params.dup) p[:f].delete(to_field) if p[:f][to_field].empty? p end + deprecation_deprecate :rotate_facet_params # FIXME: remove baked in underscore separator in field name def render_facet_rotate(field_name) @@ -191,4 +202,5 @@ def render_facet_rotate(field_name) new_params["#{prefix}_facet_order"] = new_order link_to image_tag('icons/rotate.png', title: new_order.upcase).html_safe, new_params, class: 'no-underline' end + deprecation_deprecate :render_facet_rotate end diff --git a/app/models/hierarchical_facet_item.rb b/app/models/hierarchical_facet_item.rb new file mode 100644 index 0000000..356bbe9 --- /dev/null +++ b/app/models/hierarchical_facet_item.rb @@ -0,0 +1 @@ +HierarchicalFacetItem = Struct.new :qvalue, :value, :hits diff --git a/blacklight-hierarchy.gemspec b/blacklight-hierarchy.gemspec index b058cda..eed04cb 100644 --- a/blacklight-hierarchy.gemspec +++ b/blacklight-hierarchy.gemspec @@ -21,6 +21,7 @@ Gem::Specification.new do |s| # A version of blacklight with view_component is required s.add_dependency 'blacklight', '~> 7.9' s.add_dependency 'rails', '>= 5.1', '< 7' + s.add_dependency 'deprecation' s.add_development_dependency 'rsolr' s.add_development_dependency 'rspec-rails' diff --git a/spec/helpers/hierarchy_helper_spec.rb b/spec/helpers/hierarchy_helper_spec.rb index 496b239..d9d3680 100644 --- a/spec/helpers/hierarchy_helper_spec.rb +++ b/spec/helpers/hierarchy_helper_spec.rb @@ -3,6 +3,7 @@ describe Blacklight::HierarchyHelper do describe '#render_hierarchy' do it 'should remove the _suffix from the field name' do + expect(Deprecation).to receive(:warn) field = OpenStruct.new(field: 'the_field_name_facet') expect(helper).to receive(:facet_tree).with('the_field_name').and_return({}) helper.render_hierarchy(field)