Merge "search: Optionally support load-more events"

This commit is contained in:
jenkins-bot 2022-12-07 23:58:27 +00:00 committed by Gerrit Code Review
commit eb9040b859
5 changed files with 94 additions and 8 deletions

View file

@ -13,7 +13,7 @@
},
{
"resourceModule": "skins.vector.search",
"maxSize": "3.1 kB"
"maxSize": "3.3 kB"
},
{
"resourceModule": "skins.vector.icons",

View file

@ -18,6 +18,8 @@
:auto-expand-width="autoExpandWidth"
:search-results="suggestions"
:search-footer-url="searchFooterUrl"
:visible-item-limit="visibleItemLimit"
@load-more="onLoadMore"
@input="onInput"
@search-result-click="instrumentation.onSuggestionClick"
@submit="onSubmit"
@ -44,7 +46,7 @@
</template>
<script>
/* global SearchSubmitEvent */
/* global AbortableSearchFetch, SearchSubmitEvent */
const { CdxTypeaheadSearch } = require( '@wikimedia/codex-search' ),
{ defineComponent, nextTick } = require( 'vue' ),
client = require( './restSearchClient.js' ),
@ -153,6 +155,12 @@ module.exports = exports = defineComponent( {
'vector-search-box-disable-transitions': this.disableTransitions,
'vector-typeahead-search--active': this.isFocused
};
},
visibleItemLimit() {
// if the search client supports loading more results,
// show 7 out of 10 results at first (arbitrary number),
// so that scroll events are fired and trigger onLoadMore()
return restClient.loadMore ? 7 : null;
}
},
methods: {
@ -172,15 +180,55 @@ module.exports = exports = defineComponent( {
return;
}
this.updateUIWithSearchClientResult(
restClient.fetchByTitle( query, 10, this.showDescription ),
true
);
},
/**
* Fetch additional suggestions.
*
* This should only be called if visibleItemLimit is non-null,
* i.e. if the search client supports loading more results.
*/
onLoadMore() {
if ( !restClient.loadMore ) {
mw.log.warn( 'onLoadMore() should not have been called for this search client' );
return;
}
this.updateUIWithSearchClientResult(
restClient.loadMore(
this.currentSearchQuery,
this.suggestions.length,
10,
this.showDescription
),
false
);
},
/**
* @param {AbortableSearchFetch} search
* @param {boolean} replaceResults
*/
updateUIWithSearchClientResult( search, replaceResults ) {
const query = this.currentSearchQuery;
instrumentation.listeners.onFetchStart();
restClient.fetchByTitle( query, 10, this.showDescription ).fetch
search.fetch
.then( ( data ) => {
// Only use these results if they're still relevant
// If currentSearchQuery !== query, these results are for a previous search
// and we shouldn't show them.
if ( this.currentSearchQuery === query ) {
this.suggestions = instrumentation.addWprovToSearchResultUrls( data.results );
if ( replaceResults ) {
this.suggestions = [];
}
this.suggestions.push(
...instrumentation.addWprovToSearchResultUrls( data.results, this.suggestions.length )
);
this.searchFooterUrl = urlGenerator.generateUrl( query );
}

View file

@ -156,13 +156,14 @@ function generateUrl( suggestion, meta ) {
* with the `wprov` parameter added to each result's url (if any).
*
* @param {SearchResultPartial[]} results Not modified.
* @param {number} offset Offset to add to the index of each result.
* @return {SearchResultPartial[]}
*/
function addWprovToSearchResultUrls( results ) {
function addWprovToSearchResultUrls( results, offset ) {
return results.map( ( result, index ) => {
if ( result.url ) {
const uri = new mw.Uri( result.url );
uri.query.wprov = getWprovFromResultIndex( index );
uri.query.wprov = getWprovFromResultIndex( index + offset );
result = Object.assign( {}, result, { url: uri.toString() } );
}
return result;

View file

@ -71,9 +71,19 @@ function adaptApiResponse( config, query, restResponse, showDescription ) {
* @return {AbortableSearchFetch}
*/
/**
* @callback loadMore
* @param {string} query The search term.
* @param {number} offset The number of search results that were already loaded.
* @param {number} [limit] How many further search results to load (at most).
* @param {boolean} [showDescription] Whether descriptions should be added to the results.
* @return {AbortableSearchFetch}
*/
/**
* @typedef {Object} SearchClient
* @property {fetchByTitle} fetchByTitle
* @property {loadMore} [loadMore]
*/
/**

View file

@ -21,7 +21,7 @@ describe( 'instrumentation', () => {
} );
} );
test( 'addWprovToSearchResultUrls', () => {
test( 'addWprovToSearchResultUrls without offset', () => {
const url1 = 'https://host/?title=Special%3ASearch&search=Aa',
url2Base = 'https://host/?title=Special%3ASearch&search=Ab',
url3 = 'https://host/Ac';
@ -43,7 +43,7 @@ describe( 'instrumentation', () => {
}
];
expect( instrumentation.addWprovToSearchResultUrls( results ) )
expect( instrumentation.addWprovToSearchResultUrls( results, 0 ) )
.toStrictEqual( [
{
title: 'Aa',
@ -63,4 +63,31 @@ describe( 'instrumentation', () => {
] );
expect( results[ 0 ].url ).toStrictEqual( url1 );
} );
test( 'addWprovToSearchResultUrls with offset', () => {
const url1 = 'https://host/?title=Special%3ASearch&search=Ae',
url2 = 'https://host/?title=Special%3ASearch&search=Af';
const results = [
{
title: 'Ae',
url: url1
},
{
title: 'Af',
url: url2
}
];
expect( instrumentation.addWprovToSearchResultUrls( results, 4 ) )
.toStrictEqual( [
{
title: 'Ae',
url: `${url1}&wprov=acrw1_4`
},
{
title: 'Af',
url: `${url2}&wprov=acrw1_5`
}
] );
} );
} );