Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HYC-1005 - Skip Ahead in Dept Browse #1136

Merged
merged 37 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
732374d
move override file
davidcam-src Dec 12, 2024
c6956af
override ruby component
davidcam-src Dec 12, 2024
d93702c
comments
davidcam-src Dec 16, 2024
c766b9f
paginate without katamari gem
davidcam-src Dec 16, 2024
7f6903b
kanamari
davidcam-src Dec 16, 2024
113fc24
update component override
davidcam-src Dec 16, 2024
aef7a02
rubocop
davidcam-src Dec 16, 2024
0e59dd6
error handling
davidcam-src Dec 16, 2024
91032ef
add default for link_to_specific_page, type cast
davidcam-src Dec 16, 2024
0fb3857
argument order
davidcam-src Dec 16, 2024
44b6a64
reorder arguments
davidcam-src Dec 16, 2024
fe39396
logging
davidcam-src Dec 16, 2024
16076cf
syntax
davidcam-src Dec 17, 2024
0733838
syntax
davidcam-src Dec 17, 2024
8b7e3e2
manually calculate total pages
davidcam-src Dec 17, 2024
80eca6a
delete file
davidcam-src Dec 17, 2024
7e1f60f
generate skip ahead urls based on facet total
davidcam-src Jan 3, 2025
d6ba7e9
temp log change
davidcam-src Jan 3, 2025
4533f2d
total count instance variable
davidcam-src Jan 3, 2025
d027a51
log update
davidcam-src Jan 3, 2025
3c31474
update FacetFieldPaginationComponent override
davidcam-src Jan 3, 2025
77f57aa
remove log
davidcam-src Jan 3, 2025
d34ef21
syntax
davidcam-src Jan 3, 2025
92f9bf0
cleanup
davidcam-src Jan 3, 2025
2e84dc6
move typecast into helper
davidcam-src Jan 3, 2025
b53c24b
syntax
davidcam-src Jan 3, 2025
dacff3d
remove log, typecasting
davidcam-src Jan 3, 2025
abae3cb
argument adjustment
davidcam-src Jan 3, 2025
b3e1afd
syntax
davidcam-src Jan 4, 2025
9ce5cbe
arguments, nil returns
davidcam-src Jan 4, 2025
0e55d5f
Merge branch 'main' into hyc-1005
davidcam-src Jan 6, 2025
51b6064
pagination component tests
davidcam-src Jan 6, 2025
9f27743
update error handling, tests
davidcam-src Jan 6, 2025
5079533
modified error handling
davidcam-src Jan 6, 2025
cec941c
styling
davidcam-src Jan 7, 2025
b49d514
Only calculate total unique facets for facets in the target list
davidcam-src Jan 8, 2025
b4e8d83
valid link test
davidcam-src Jan 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<!-- https://github.com/projectblacklight/blacklight/blob/v8.7.0/app/components/blacklight/facet_field_pagination_component.html.erb -->
<div class="prev_next_links btn-group">
<%= helpers.link_to_previous_page @facet_field.paginator, raw(t('views.pagination.previous')), params: @facet_field.search_state.to_h, param_name: param_name, class: 'btn btn-link', data: { blacklight_modal: "preserve" } do %>
<%= content_tag :span, raw(t('views.pagination.previous')), class: 'disabled btn' %>
<% end %>

<%= helpers.link_to_next_page @facet_field.paginator, raw(t('views.pagination.next')), params: @facet_field.search_state.to_h, param_name: param_name, class: 'btn btn-link', data: { blacklight_modal: "preserve" } do %>
<%= content_tag :span, raw(t('views.pagination.next')), class: 'disabled btn' %>
<% end %>

<!-- [hyc-override] Pagination using `Blacklight::Solr::FacetPaginator` -->
<ul class="pagination">
<% limit = @facet_field.paginator.instance_variable_get(:@limit) %>
<% offset = @facet_field.paginator.instance_variable_get(:@offset) %>
<% current_page = (offset / limit) + 1 %>
<% total_pages = (@total_unique_facets.to_f / limit).ceil %>
<% first_pages_count = 2 %>
<% last_pages_count = 2 %>
<% surrounding_pages = 4 %>
<!-- Page Numbers -->
<% (1..total_pages).each do |page| %>
bbpennel marked this conversation as resolved.
Show resolved Hide resolved
<!-- Display pages within +/-4 of the current page, always show the first and last two pages -->
<% within_range = (page >= current_page - surrounding_pages && page <= current_page + surrounding_pages) %>
<% show_page = (
page <= first_pages_count ||
page > total_pages - last_pages_count ||
within_range
) %>

<% if show_page %>
<li class="page-item <%= 'active' if page == current_page %>">
<% if page == current_page %>
<span class="page-link" aria-label="Current Page, Page <%= page %>" aria-current="true"><%= page %></span>
<% else %>
<!-- Typecasting not effective in helper method, using hash as a workaround -->
<%= helpers.link_to_specific_page(
@facet_field.paginator,
page,
@total_unique_facets,
params: @facet_field.search_state.to_h.merge(page: page, 'facet.page' => page),
class: "page-link"
) %>
<% end %>
</li>
<!-- Render ellipsis for pages that would be just outside of the range (3,last page - 2) if they aren't show pages -->
<% elsif page == first_pages_count + 1 || page == total_pages - last_pages_count %>
<li class="page-item disabled"><span class="page-link">…</span></li>
<% end %>
<% end %>
</ul>
</div>

<div class="sort-options btn-group" style="height: 40px;">
<% if @facet_field.paginator.sort == 'index' -%>
<span class="active az btn btn-outline-secondary"><%= t('blacklight.search.facets.sort.index') %></span>
<%= helpers.link_to(t('blacklight.search.facets.sort.count'), sort_facet_url('count'), class: "sort_change numeric btn btn-outline-secondary", data: { blacklight_modal: "preserve" }) %>
<% elsif @facet_field.paginator.sort == 'count' -%>
<%= helpers.link_to(t('blacklight.search.facets.sort.index'), sort_facet_url('index'), class: "sort_change az btn btn-outline-secondary", data: { blacklight_modal: "preserve" }) %>
<span class="active numeric btn btn-outline-secondary"><%= t('blacklight.search.facets.sort.count') %></span>
<% end -%>
</div>
39 changes: 39 additions & 0 deletions app/controllers/catalog_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,45 @@ def single_item_search_builder(id)
single_item_search_builder_class.new(self, id).with(params.except(:q, :page))
end

def facet_total_count(field_name)
query = '*:*'
params = {
facet: true,
'facet.field': field_name,
rows: 0,
'facet.limit': -1, # Retrieve all facets
wt: 'json'
}

# Perform the Solr query using Hyrax::SolrService
# Using spread operator to convert object into keyword arguments
response = Hyrax::SolrService.get(query, **params)

# Parse the response to extract the total unique facets
facet_values = response.dig('facet_counts', 'facet_fields', field_name)
# Facet counts are included with names in the list, so divide by 2
total_unique_facets = facet_values ? (facet_values.length / 2) : 0
total_unique_facets
rescue StandardError => e
Rails.logger.error("Error retrieving facets for '#{field_name}': #{e.message}")
Rails.logger.error(e.backtrace.join("\n"))
0
end

def facet
bbpennel marked this conversation as resolved.
Show resolved Hide resolved
begin
facet_field_name = params[:id]
super
# Calculate the total unique facet count
@total_unique_facets = facet_total_count(facet_field_name)
rescue StandardError => e
# Capture any errors that occur and log them
Rails.logger.error("Error during facet action: #{e.message}")
Rails.logger.error(e.backtrace.join("\n"))
end
end


configure_blacklight do |config|
# Advanced search configuration
config.advanced_search ||= Blacklight::OpenStructWithHashAccess.new
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true
# [hyc-override] https://github.com/projectblacklight/blacklight/blob/v7.33.1/app/components/blacklight/facet_field_pagination_component.rb
Blacklight::FacetFieldPaginationComponent.class_eval do
attr_reader :facet_field, :total_unique_facets
def initialize(facet_field:, total_unique_facets: nil)
@facet_field = facet_field
# Integrate total unique facets as an attribute for pagination
@total_unique_facets = total_unique_facets
end
end
44 changes: 44 additions & 0 deletions app/overrides/lib/kanamari/helpers/helper_methods_override.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true
# [hyc-override] https://github.com/kaminari/kaminari/blob/v1.2.2/kaminari-core/lib/kaminari/helpers/helper_methods.rb
Kaminari::Helpers::HelperMethods.module_eval do
# Helper to generate a link to a specific page
def link_to_specific_page(scope, page, total_entries, **options)
specific_page_path = path_to_specific_page(scope, page.to_i, total_entries, options)

# Remove unnecessary keys :params and :param_name from the options hash before generating the link
options.except! :params, :param_name

# Setting aria instead of rel for accessibility
options[:aria] ||= { label: "Go to page #{page.to_i}" }

if specific_page_path
link_to("#{page.to_i}", specific_page_path, options)
else
Rails.logger.warn "Specific page path could not be generated for page: #{page.to_i}"
nil
end
end

# Helper to generate the path for a specific page
def path_to_specific_page(scope, page_integer, total_entries, options = {})
begin
# Calculate total pages manually
limit = scope.instance_variable_get(:@limit)
total_pages = (total_entries.to_f / limit).ceil

Rails.logger.debug "path_to_specific_page: total_entries=#{total_entries}, limit=#{limit}, calculated total_pages=#{total_pages}, page=#{page_integer}"

# Validate inputs
raise ArgumentError, 'Page number must be a positive integer' unless page_integer.positive?
raise ArgumentError, "Page number exceeds total pages (#{total_pages})" if page_integer > total_pages
# Generate URL using Kaminari's Page helper
Kaminari::Helpers::Page.new(self, **options.reverse_merge(page: page_integer)).url
rescue ArgumentError => e
Rails.logger.error "Error in path_to_specific_page: #{e.message}"
nil
rescue => e
Rails.logger.error "Unexpected error in path_to_specific_page: #{e.message}\n#{e.backtrace.join("\n")}"
nil
end
end
end
1 change: 1 addition & 0 deletions app/views/catalog/_facet_pagination.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= render(Blacklight::FacetFieldPaginationComponent.new(facet_field: facet_field_presenter(@facet, @display_facet), total_unique_facets: @total_unique_facets)) %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true
require 'rails_helper'

RSpec.describe Blacklight::FacetFieldPaginationComponent, type: :component do
let(:facet_field) { double('facet_field') }
let(:total_unique_facets) { 42 }

describe '#initialize' do
context 'when total_unique_facets is provided' do
it 'sets the facet_field and total_unique_facets attributes' do
component = Blacklight::FacetFieldPaginationComponent.new(facet_field: facet_field, total_unique_facets: total_unique_facets)

expect(component.facet_field).to eq(facet_field)
expect(component.total_unique_facets).to eq(total_unique_facets)
end
end

context 'when total_unique_facets is not provided' do
it 'sets the total_unique_facets attribute to nil' do
component = Blacklight::FacetFieldPaginationComponent.new(facet_field: facet_field)

expect(component.facet_field).to eq(facet_field)
expect(component.total_unique_facets).to be_nil
end
end
end
end
50 changes: 50 additions & 0 deletions spec/lib/kanamari/helpers/helper_methods_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true
require 'rails_helper'

RSpec.describe Kaminari::Helpers::HelperMethods do
let(:dummy_class) { Class.new { extend Kaminari::Helpers::HelperMethods } }
let(:scope) { double('FacetPaginator', instance_variable_get: 20) }
let(:valid_page) { 1 }
let(:total_entries) { 100 }
let(:options) { { some_option: 'value' } }

describe '#link_to_specific_page' do
before do
# Stub Kaminari::Helpers::Page.new to return a mock URL
allow(Kaminari::Helpers::Page).to receive(:new).and_return(double(url: '/some_path'))
end

# it 'generates a valid link for correct input' do
bbpennel marked this conversation as resolved.
Show resolved Hide resolved
# expect(Rails.logger).to_not receive(:error)
# allow(dummy_class).to receive(:link_to).and_return('link')
# # Mock link_to to check its arguments
# dummy_class.link_to_specific_page(scope, valid_page, total_entries, **options)
# # Expect the method to return the mocked URL
# end


it 'logs and returns nil for invalid page input' do
invalid_page = -1
expect(Rails.logger).to receive(:error).with(/Page number must be a positive integer/)
expect(Rails.logger).to receive(:warn).with(/Specific page path could not be generated for page/)
expect(dummy_class.link_to_specific_page(scope, invalid_page, total_entries, **options))
.to be_nil
end

it 'logs and returns nil if page exceeds total pages' do
invalid_page = 999
expect(Rails.logger).to receive(:error).with(/Page number exceeds total pages/)
expect(Rails.logger).to receive(:warn).with(/Specific page path could not be generated for page/)
expect(dummy_class.link_to_specific_page(scope, invalid_page, total_entries, **options))
.to be_nil
end

it 'logs and returns nil if an unexpected error occurs' do
allow(Kaminari::Helpers::Page).to receive(:new).and_raise(StandardError, 'Simulated Kaminari Error')
expect(Rails.logger).to receive(:error).with(/Unexpected error in path_to_specific_page/)
expect(Rails.logger).to receive(:warn).with(/Specific page path could not be generated for page/)
expect(dummy_class.link_to_specific_page(scope, valid_page, total_entries, **options))
.to be_nil
end
end
end
Loading