Skip to content

Commit 98a077f

Browse files
sandbergjaishasinha1christinach
authored
Add a way to filter facets in the facets "more" modal (#3367)
* Facet typeahead: back end This creates a new endpoint at /catalog/facet/<facet_name>/<query_fragment> Co-authored-by: Isha Sinha <[email protected]> * Facet typeahead: front end Co-authored-by: Christina Chortaria <[email protected]> * bundle exec i18n-tasks add-missing * Incorporate feedback from review Rather than two very similar controller actions for facets vs. facet_suggest, this commit combines them into a single controller action. --------- Co-authored-by: Isha Sinha <[email protected]> Co-authored-by: Christina Chortaria <[email protected]>
1 parent d27a1d1 commit 98a077f

31 files changed

+387
-115
lines changed

.rubocop.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Metrics/BlockLength:
4343

4444
Metrics/ClassLength:
4545
Exclude:
46+
- "app/services/blacklight/search_service.rb"
4647
- "lib/blacklight/configuration.rb"
4748
- "lib/blacklight/search_builder.rb"
4849
- "lib/blacklight/search_state.rb"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<label for="facet-suggest-<%= facet.key %>">
2+
<%= I18n.t('blacklight.search.facets.suggest.label', field_label: presenter&.label) %>
3+
</label>
4+
<input class="facet-suggest form-control"
5+
id="facet-suggest-<%= facet.key %>"
6+
data-facet-field="<%= facet.key %>"
7+
name="facet_suggest_<%= facet.key %>"
8+
placeholder="<%= I18n.t('blacklight.search.form.search.placeholder') %>">
9+
</input>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# frozen_string_literal: true
2+
3+
module Blacklight
4+
module Search
5+
class FacetSuggestInput < Blacklight::Component
6+
def initialize(facet:, presenter:)
7+
@facet = facet
8+
@presenter = presenter
9+
end
10+
11+
private
12+
13+
attr_accessor :facet, :presenter
14+
end
15+
end
16+
end

app/controllers/concerns/blacklight/catalog.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,12 @@ def facet
8383
@facet = blacklight_config.facet_fields[params[:id]]
8484
raise ActionController::RoutingError, 'Not Found' unless @facet
8585

86-
@response = search_service.facet_field_response(@facet.key)
86+
query_fragment = params[:query_fragment] || ''
87+
@response = if query_fragment.present?
88+
search_service.facet_suggest_response(@facet.key, params[:query_fragment])
89+
else
90+
@response = search_service.facet_field_response(@facet.key)
91+
end
8792
@display_facet = @response.aggregations[@facet.field]
8893

8994
@presenter = @facet.presenter.new(@facet, @display_facet, view_context)
@@ -92,6 +97,8 @@ def facet
9297
format.html do
9398
# Draw the partial for the "more" facet modal window:
9499
return render layout: false if request.xhr?
100+
# Only show the facet names and their values:
101+
return render 'facet_values', layout: false if params[:only_values]
95102
# Otherwise draw the facet selector for users who have javascript disabled.
96103
end
97104
format.json
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Usage:
2+
// ```
3+
// const basicFunction = (entry) => console.log(entry)
4+
// const debounced = debounce(basicFunction("I should only be called once"));
5+
//
6+
// debounced // does NOT print to the screen becase it is invoked again less than 200 milliseconds later
7+
// debounced // does print to the screen
8+
// ```
9+
export default function debounce(func, timeout = 200) {
10+
let timer;
11+
return (...args) => {
12+
clearTimeout(timer);
13+
timer = setTimeout(() => { func.apply(this, args); }, timeout);
14+
};
15+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import debounce from "blacklight/debounce";
2+
3+
const FacetSuggest = async (e) => {
4+
if (e.target.matches('.facet-suggest')) {
5+
const queryFragment = e.target.value?.trim();
6+
const facetField = e.target.dataset.facetField;
7+
if (!facetField) { return; }
8+
9+
const urlToFetch = `/catalog/facet_suggest/${facetField}/${queryFragment}`
10+
const response = await fetch(urlToFetch);
11+
if (response.ok) {
12+
const blob = await response.blob()
13+
const text = await blob.text()
14+
15+
const facetArea = document.querySelector('.facet-extended-list');
16+
17+
if (text && facetArea) {
18+
facetArea.innerHTML = text
19+
}
20+
}
21+
}
22+
};
23+
24+
document.addEventListener('input', debounce(FacetSuggest));
25+
26+
export default FacetSuggest

app/javascript/blacklight/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import BookmarkToggle from 'blacklight/bookmark_toggle'
22
import ButtonFocus from 'blacklight/button_focus'
3+
import FacetSuggest from 'blacklight/facet_suggest'
34
import Modal from 'blacklight/modal'
45
import SearchContext from 'blacklight/search_context'
56
import Core from 'blacklight/core'
67

78
export default {
89
BookmarkToggle,
910
ButtonFocus,
11+
FacetSuggest,
1012
Modal,
1113
SearchContext,
1214
Core,

app/presenters/blacklight/facet_field_presenter.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ def in_advanced_search?
3131
end
3232

3333
def in_modal?
34-
search_state.params[:action] == "facet"
34+
modal_like_actions = %w[facet facet_suggest]
35+
modal_like_actions.include? search_state.params[:action]
3536
end
3637

3738
def modal_path

app/services/blacklight/search_service.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ def facet_field_response(facet_field, extra_controller_params = {})
6262
repository.search(query.merge(extra_controller_params))
6363
end
6464

65+
def facet_suggest_response(facet_field, facet_suggestion_query, extra_controller_params = {})
66+
query = search_builder.with(search_state).facet(facet_field).facet_suggestion_query(facet_suggestion_query)
67+
repository.search(query.merge(extra_controller_params))
68+
end
69+
6570
# Get the previous and next document from a search result
6671
# @return [Blacklight::Solr::Response, Array<Blacklight::SolrDocument>] the solr response and a list of the first and last document
6772
def previous_and_next_documents_for_search(index, request_params, extra_controller_params = {})

app/views/catalog/facet.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<% end %>
77

88
<% component.with_title { facet_field_label(@facet.key) } %>
9+
<%= render Blacklight::Search::FacetSuggestInput.new(facet: @facet, presenter: @presenter) %>
910

1011
<%= render partial: 'facet_index_navigation' if @facet.index_range && @display_facet.index? %>
1112

0 commit comments

Comments
 (0)