Bugzilla – Attachment 114567 Details for
Bug 27200
"Browse search" is broken
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 27200: (follow-up) Browse search interface update
Bug-27200-follow-up-Browse-search-interface-update.patch (text/plain), 23.98 KB, created by
Nick Clemens (kidclamp)
on 2020-12-21 16:20:27 UTC
(
hide
)
Description:
Bug 27200: (follow-up) Browse search interface update
Filename:
MIME Type:
Creator:
Nick Clemens (kidclamp)
Created:
2020-12-21 16:20:27 UTC
Size:
23.98 KB
patch
obsolete
>From a7052f37a3a90bee2a9d1ae66b71a2febeb95755 Mon Sep 17 00:00:00 2001 >From: Owen Leonard <oleonard@myacpl.org> >Date: Wed, 16 Dec 2020 18:38:11 +0000 >Subject: [PATCH] Bug 27200: (follow-up) Browse search interface update > >This patch updates the browse search interface to confirm to new >interface patterns in the Bootstrap 4 framework, especially in the form >markup. > >The results list has been changed to a Bootstrap "Collapse" component >configured as an accordion >(https://getbootstrap.com/docs/4.5/components/collapse/#accordion-example). > >Instead of loading the bibliographic record results below the list of >terms returned, the bibliographic results are now displayed in the >"panel" expanded below the selected term. Subtitle has been added to the >information displayed about the bibliographic record. > >To test you must be using ElasticSearch and the OpacBrowseSearch >preference must be enabled. Apply the patch and rebuild the OPAC CSS >(https://wiki.koha-community.org/wiki/Working_with_SCSS_in_the_OPAC_and_staff_client). > >- Open the "Browse search" page in the OPAC. >- Test various searches: Author, Subject, and Title. >- When results are found, the should be displayed as a Bootstrap-styled > accordion widget. Clicking any individual result should expand the > panel containing the corresponding records. >- Clicking the record link should open the bibliographic detail page in > a new window. >- When no results are found, a Bootstrap-style "alert" box should > appear. > >Signed-off-by: Victor Grousset/tuxayo <victor@tuxayo.net> >--- > koha-tmpl/opac-tmpl/bootstrap/css/src/_common.scss | 2 +- > koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss | 139 +++++++--------- > .../opac-tmpl/bootstrap/en/modules/opac-browse.tt | 88 ++++++---- > koha-tmpl/opac-tmpl/bootstrap/js/browse.js | 179 +++++++++------------ > opac/opac-browse.pl | 1 + > 5 files changed, 195 insertions(+), 214 deletions(-) > >diff --git a/koha-tmpl/opac-tmpl/bootstrap/css/src/_common.scss b/koha-tmpl/opac-tmpl/bootstrap/css/src/_common.scss >index 3df3488625..f0a0fd2be3 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/css/src/_common.scss >+++ b/koha-tmpl/opac-tmpl/bootstrap/css/src/_common.scss >@@ -44,7 +44,7 @@ $sci-heading-color: #727272; > @import "../../../../../node_modules/bootstrap/scss/breadcrumb"; > @import "../../../../../node_modules/bootstrap/scss/button-group"; > @import "../../../../../node_modules/bootstrap/scss/buttons"; >-// @import "../../../../../node_modules/bootstrap/scss/card"; >+@import "../../../../../node_modules/bootstrap/scss/card"; > // @import "../../../../../node_modules/bootstrap/scss/carousel"; > @import "../../../../../node_modules/bootstrap/scss/close"; > // @import "../../../../../node_modules/bootstrap/scss/code"; >diff --git a/koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss b/koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss >index 24f095c819..b946cc193b 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss >+++ b/koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss >@@ -2513,94 +2513,35 @@ $star-selected: #EDB867; > } > > /*opac browse search*/ >-#browse-search { >- >- form { >- label { >- display: inline-block; >- margin-right:5px; >- } >- >- [type=submit] { >- margin-top: 10px; >- } >- } >- >- #browse-resultswrapper { >- margin-top: 4em; >- >- @media (min-width: 768px) and (max-width: 984px) { >- margin-top: 2em; >- } >- >- @media (max-width: 767px) { >- margin-top: 1em; >- } >- } >- #browse-searchresults, #browse-selectionsearch { >- border: 1px solid #E3E3E3; >- border-radius: 4px; >- padding: 0; >- overflow-y: auto; >- max-height: 31em; >- margin-bottom: 2em; >- } >- #browse-searchresults { >- max-height: 31em; >- list-style: none; >- padding: 10px; > >- a { >- display: block; >- margin-bottom: 5px; >- >- &.selected { >- background-color:#EEE; >- } >- } >+#browse-resultswrapper { >+ margin-top: 15px; >+} >+#browse-searchfuzziness { >+ padding: 15px 0; >+} > >- li:last-child a { >- margin-bottom: 0; >- } >+#browse-searchresults, #browse-selectionsearch { >+ border: 1px solid #E3E3E3; >+ border-radius: 4px; >+ padding: 0; >+ margin-bottom: 2em; >+} > >- @media (max-width: 767px) { >- max-height: 13em; >- } >- } >- #browse-selection { >- margin-top: -40px; >- padding-top: 0; >+#browse-selectionsearch p.subjects { >+ font-size: 0.9em; >+ margin-bottom: 0; >+} > >- @media (max-width: 767px) { >- margin-top: 0; >- } >- } >- #browse-selectionsearch ol { >- list-style: none; >- margin: 0; >+#browse-selectionsearch h4 { >+ margin: 0; >+} > >- li { >- padding: 1em; >+#browse-suggestionserror { >+ margin-top: 1rem; >+} > >- &:nth-child(odd) { >- background-color: #F4F4F4; >- } >- } >- } >- #browse-selectionsearch p.subjects { >- font-size: 0.9em; >- margin-bottom: 0; >- } >- #browse-selectionsearch h4 { >- margin: 0; >- } >- .error, .no-results { >- background-color: #EEE; >- border: 1px solid #E8E8E8; >- text-align: left; >- padding: 0.5em; >- border-radius: 3px; >- } >+#browse-search { > .loading { > text-align: center; > >@@ -2611,6 +2552,15 @@ $star-selected: #EDB867; > } > } > } >+ >+#card_template { >+ display: none; >+} >+ >+.result-title { >+ margin-bottom: .4rem; >+ margin-left: 1rem; >+} > /*end browse search*/ > > /* Skip to content link. CSS adapted from https://webaim.org/ */ >@@ -2638,4 +2588,29 @@ $star-selected: #EDB867; > } > } > >+.accordion { >+ .card-header { >+ padding: 0; >+ >+ a { >+ display: block; >+ padding: .75rem 1.25rem; >+ >+ &[aria-expanded="true"] { font-weight: bold; } >+ &[aria-expanded="false"] { font-weight: normal; } >+ >+ &:hover { >+ background-color: rgba(255, 255, 204, 0.8); >+ text-decoration: none; >+ } >+ } >+ } >+ >+ .collapse { >+ &.show { >+ border-top: 1px solid #888; >+ } >+ } >+} >+ > @import "responsive"; >diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt >index d8ad1b5921..11ff7f1efc 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt >+++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt >@@ -41,44 +41,70 @@ > <h1>Browse search</h1> > > <form> >- <label for="browse-searchterm">Search for:</label> >- <input type="search" id="browse-searchterm" name="searchterm" value=""> >- <label for="browse-searchfield" class="hide-text">Search type:</label> >- <select id="browse-searchfield" name="searchfield"> >- <option value="author">Author</option> >- <option value="subject">Subject</option> >- <option value="title">Title</option> >- </select> >- >- <div id="browse-searchfuzziness"> >- <label for="exact" class="radio inline"><input type="radio" name="browse-searchfuzziness" id="exact" value="0">Exact</label> >- <label for="fuzzy" class="radio inline"><input type="radio" name="browse-searchfuzziness" id="fuzzy" value="1" checked="checked"> Fuzzy</label> >- <label for="reallyfuzzy" class="radio inline"><input type="radio" name="browse-searchfuzziness" id="reallyfuzzy" value="2"> Really fuzzy</label> >+ <div class="form-row"> >+ <div class="col"> >+ <label for="browse-searchterm">Search for:</label> >+ <input type="search" id="browse-searchterm" class="form-control" name="searchterm" value="" /> >+ </div> <!-- /.col-12.col-sm-9 --> >+ <div class="col-auto"> >+ <label for="browse-searchfield">Search type:</label> >+ <select id="browse-searchfield" name="searchfield" class="form-control"> >+ <option value="author">Author</option> >+ <option value="subject">Subject</option> >+ <option value="title">Title</option> >+ </select> >+ </div> <!-- /.col-auto --> >+ </div> <!-- /.form-row --> >+ <div class="form-row"> >+ <div class="col"> >+ <div id="browse-searchfuzziness"> >+ <div class="form-check form-check-inline"> >+ <label for="exact" class="form-check-label"> >+ <input class="form-check-input" type="radio" name="browse-searchfuzziness" id="exact" value="0" />Exact >+ </label> >+ </div> <!-- /.form-check.form-check-inline --> >+ <div class="form-check form-check-inline"> >+ <label for="fuzzy" class="form-check-label"> >+ <input class="form-check-input" type="radio" name="browse-searchfuzziness" id="fuzzy" value="1" checked="checked" /> Fuzzy >+ </label> >+ </div> <!-- /.form-check.form-check-inline --> >+ <div class="form-check form-check-inline"> >+ <label for="reallyfuzzy" class="form-check-label"> >+ <input class="form-check-input" type="radio" name="browse-searchfuzziness" id="reallyfuzzy" value="2" /> Really fuzzy >+ </label> >+ </div> <!-- /.form-check.form-check-inline --> >+ </div> <!-- /#browse-searchfuzziness --> >+ </div> <!-- /.col --> >+ </div> <!-- /.form-row --> >+ <div class="form-row"> >+ <div class="col"> >+ <button class="btn btn-primary" type="submit" accesskey="s">Search</button> >+ </div> > </div> >- <button class="btn btn-success" type="submit" accesskey="s">Search</button> > </form> > >- <p id="browse-suggestionserror" class="error hidden"> >- An error occurred, please try again.</p> >+ <div id="browse-suggestionserror" class="alert alert-warning d-none" role="alert"> >+ An error occurred, please try again. >+ </div> > > <div id="browse-resultswrapper" class="d-none"> >- <ul id="browse-searchresults" start="-1" aria-live="polite"> >- <li class="loading hidden"><img src="[% interface | html %]/[% theme |html %]/images/loading.gif" alt=""> Loading</li> >- >- <li class="no-results hidden">Sorry, there are no results, try a different search term.</li> >- </ul> >+ <h2>Results</h2> > >- <h3 id="browse-selection"></h3> >+ <div class="loading d-none"><img src="[% interface | html %]/[% theme |html %]/images/loading.gif" alt=""> Loading</div> >+ <div class="alert alert-warning no-results d-none" role="alert">Sorry, there are no results. Try a different search term.</div> > >- <div id="browse-selectionsearch" class="d-none"> >- <div class="loading hidden"> >- <img src="[% interface | html %]/[% theme | html %]/images/loading.gif" alt=""> Loading >- </div> >- >- <div class="no-results hidden">No results</div> >- >- <ol aria-live="polite"></ol> >- </div> >+ <div class="accordion" id="browse-searchresults"> >+ <div id="card_template" class="card"> >+ <div class="card-header" id="heading"> >+ <a class="expand-result" href="#" data-toggle="collapse" aria-expanded="false" aria-controls="collapse"> >+ </a> >+ </div> <!-- /#heading.card-header --> >+ <div id="collapse" class="collapse" aria-labelledby="heading" data-parent="#browse-searchresults"> >+ <div class="card-body"> >+ </div> >+ </div> <!-- /#collapse.collapse --> >+ </div> <!-- /#card_template.card --> >+ </div> <!-- /#browse-searchresults.accordion --> > </div><!-- / #browse-resultswrapper --> > </div><!-- /#browse-search --> > [% ELSE %] >diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/browse.js b/koha-tmpl/opac-tmpl/bootstrap/js/browse.js >index e755e748b5..3a3bcc26a3 100644 >--- a/koha-tmpl/opac-tmpl/bootstrap/js/browse.js >+++ b/koha-tmpl/opac-tmpl/bootstrap/js/browse.js >@@ -16,8 +16,7 @@ $(document).ready(function(){ > var userInput = $('#browse-searchterm').val().trim(); > var userField = $('#browse-searchfield').val(); > var userFuzziness = $('input[name=browse-searchfuzziness]:checked', '#browse-searchfuzziness').val(); >- var leftPaneResults = $('#browse-searchresults li').not('.loading, .no-results'); >- var rightPaneResults = $('#browse-selectionsearch ol li'); >+ var card_template = $("#card_template"); > > event.preventDefault(); > >@@ -25,49 +24,50 @@ $(document).ready(function(){ > return; > } > >- // remove any error states and show the results area (except right pane) >+ /* return the browsing results to empty state in case previous results have been loaded */ >+ $("#browse-searchresults").empty().append( card_template ); >+ >+ // remove any error states and show the results area > $('#browse-suggestionserror').addClass('d-none'); >- $('#browse-searchresults .no-results').addClass('d-none'); >+ $('.no-results').addClass('d-none'); > $('#browse-resultswrapper').removeClass('d-none'); >- $('#browse-selection').addClass('d-none').text(""); >- $('#browse-selectionsearch').addClass('d-none'); >- >- // clear any results from left and right panes >- leftPaneResults.remove(); >- rightPaneResults.remove(); >+ /* Reset results browser to default state */ > >- // show the spinner in the left pane >- $('#browse-searchresults .loading').removeClass('d-none'); >+ // show the spinner >+ $('.loading').removeClass('d-none'); > > xhrGetSuggestions = $.get(window.location.pathname, {api: "GetSuggestions", field: userField, prefix: userInput, fuzziness: userFuzziness}) > .always(function() { > // hide spinner >- $('#browse-searchresults .loading').addClass('d-none'); >+ $('.loading').addClass('d-none'); > }) > .done(function(data) { > var fragment = document.createDocumentFragment(); > > if (data.length === 0) { >- $('#browse-searchresults .no-results').removeClass('d-none'); >- >+ $('.no-results').removeClass('d-none'); > return; > } > >- // scroll to top of container again >- $("#browse-searchresults").overflowScrollReset(); >- > // store the type of search that was performed as an attrib > $('#browse-searchresults').data('field', userField); > > $.each(data, function(index, object) { > // use a document fragment so we don't need to nest the elems > // or append during each iteration (which would be slow) >- var elem = document.createElement("li"); >- var link = document.createElement("a"); >- link.textContent = object.text; >- link.setAttribute("href", "#"); >- elem.appendChild(link); >- fragment.appendChild(elem); >+ var card = card_template.clone().removeAttr("id"); >+ // change card-header id >+ card >+ .find(".card-header") >+ .attr("id", "heading" + index) >+ .find("a").attr("data-target", "#collapse" + index) >+ .attr("aria-controls", "collapse" + index) >+ .text(object.text); >+ card >+ .find(".collapse") >+ .attr("id", "collapse" + index) >+ .attr("aria-labelledby", "heading" + index); >+ $(fragment).append(card); > }); > > $('#browse-searchresults').append(fragment.cloneNode(true)); >@@ -81,92 +81,71 @@ $(document).ready(function(){ > }); > }); > >- $('#browse-searchresults').on("click", 'a', function(event) { >+ $('#browse-searchresults').on("click", 'a.expand-result', function(event) { > // if there is an in progress request, abort it so we > // don't end up with a race condition > if(xhrGetResults && xhrGetResults.readyState != 4){ > xhrGetResults.abort(); > } > >- var term = $(this).text(); >+ var link = $(this); >+ var target = link.data("target"); >+ var term = link.text(); >+ > var field = $('#browse-searchresults').data('field'); >- var rightPaneResults = $('#browse-selectionsearch ol li'); > > event.preventDefault(); > >- // clear any current selected classes and add a new one >- $(this).parent().siblings().children().removeClass('selected'); >- $(this).addClass('selected'); >- >- // copy in the clicked text >- $('#browse-selection').removeClass('d-none').text(term); >- >- // show the right hand pane if it is not shown already >- $('#browse-selectionsearch').removeClass('d-none'); >- >- // hide the no results element >- $('#browse-selectionsearch .no-results').addClass('d-none'); >- >- // clear results >- rightPaneResults.remove(); >- >- // turn the spinner on >- $('#browse-selectionsearch .loading').removeClass('d-none'); >- >- // do the query for the term >- xhrGetResults = $.get(window.location.pathname, {api: "GetResults", field: field, term: term}) >- .always(function() { >- // hide spinner >- $('#browse-selectionsearch .loading').addClass('d-none'); >- }) >- .done(function(data) { >- var fragment = document.createDocumentFragment(); >- >- if (data.length === 0) { >- $('#browse-selectionsearch .no-results').removeClass('d-none'); >- >- return; >- } >- >- // scroll to top of container again >- $("#browse-selectionsearch").overflowScrollReset(); >- >- $.each(data, function(index, object) { >- // use a document fragment so we don't need to nest the elems >- // or append during each iteration (which would be slow) >- var elem = document.createElement("li"); >- var title = document.createElement("h4"); >- var link = document.createElement("a"); >- var author = document.createElement("p"); >- var destination = window.location.pathname; >- >- destination = destination.replace("browse", "detail"); >- destination = destination + "?biblionumber=" + object.id; >- >- author.className = "author"; >- >- link.setAttribute("href", destination); >- link.setAttribute("target", "_blank"); >- link.textContent = object.title; >- title.appendChild(link); >- >- author.textContent = object.author; >- >- elem.appendChild(title); >- elem.appendChild(author); >- fragment.appendChild(elem); >+ /* Don't load data via AJAX if it has already been loaded */ >+ if ($(target).find(".result-title").length == 0) { >+ // do the query for the term >+ xhrGetResults = $.get(window.location.pathname, {api: "GetResults", field: field, term: term}) >+ .done(function(data) { >+ var fragment = document.createDocumentFragment(); >+ >+ if (data.length === 0) { >+ $('#browse-selectionsearch .no-results').removeClass('d-none'); >+ return; >+ } >+ >+ >+ $.each(data, function(index, object) { >+ // use a document fragment so we don't need to nest the elems >+ // or append during each iteration (which would be slow) >+ var elem = document.createElement("div"); >+ elem.className = "result-title"; >+ >+ var destination = window.location.pathname; >+ destination = destination.replace("browse", "detail"); >+ destination = destination + "?biblionumber=" + object.id; >+ >+ var link = document.createElement("a"); >+ link.setAttribute("href", destination); >+ link.setAttribute("target", "_blank"); >+ link.textContent = object.title; >+ if( object.subtitle ){ >+ link.textContent += " " + object.subtitle; >+ } >+ elem.appendChild(link); >+ >+ if( object.author ){ >+ var author = document.createElement("span"); >+ author.className = "author"; >+ author.textContent = " " + object.author; >+ elem.appendChild(author); >+ } >+ fragment.appendChild(elem); >+ }); >+ >+ $( target ).find(".card-body").append(fragment.cloneNode(true)); >+ }) >+ .fail(function(jqXHR) { >+ //if 500 or 404 (abort is okay though) >+ if (jqXHR.statusText !== "abort") { >+ $('#browse-resultswrapper').addClass('d-none'); >+ $('#browse-suggestionserror').removeClass('d-none'); >+ } > }); >- >- $('#browse-selectionsearch ol').append(fragment.cloneNode(true)); >- }) >- .fail(function(jqXHR) { >- //if 500 or 404 (abort is okay though) >- if (jqXHR.statusText !== "abort") { >- $('#browse-resultswrapper').addClass('d-none'); >- $('#browse-suggestionserror').removeClass('d-none'); >- } >- }); >- >+ } > }); >- > }); >diff --git a/opac/opac-browse.pl b/opac/opac-browse.pl >index bbad0d4f51..84df8c83ed 100755 >--- a/opac/opac-browse.pl >+++ b/opac/opac-browse.pl >@@ -104,6 +104,7 @@ sub _filter_for_output { > { > id => $biblionumber, > title => $biblio->title, >+ subtitle => $biblio->subtitle, > author => $biblio->author, > }; > }; >-- >2.11.0
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 27200
:
114324
|
114462
|
114500
|
114501
|
114566
| 114567