mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/Vector.git
synced 2024-12-01 02:37:05 +00:00
Merge "search: Optionally support load-more events"
This commit is contained in:
commit
eb9040b859
|
@ -13,7 +13,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resourceModule": "skins.vector.search",
|
"resourceModule": "skins.vector.search",
|
||||||
"maxSize": "3.1 kB"
|
"maxSize": "3.3 kB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"resourceModule": "skins.vector.icons",
|
"resourceModule": "skins.vector.icons",
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
:auto-expand-width="autoExpandWidth"
|
:auto-expand-width="autoExpandWidth"
|
||||||
:search-results="suggestions"
|
:search-results="suggestions"
|
||||||
:search-footer-url="searchFooterUrl"
|
:search-footer-url="searchFooterUrl"
|
||||||
|
:visible-item-limit="visibleItemLimit"
|
||||||
|
@load-more="onLoadMore"
|
||||||
@input="onInput"
|
@input="onInput"
|
||||||
@search-result-click="instrumentation.onSuggestionClick"
|
@search-result-click="instrumentation.onSuggestionClick"
|
||||||
@submit="onSubmit"
|
@submit="onSubmit"
|
||||||
|
@ -44,7 +46,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* global SearchSubmitEvent */
|
/* global AbortableSearchFetch, SearchSubmitEvent */
|
||||||
const { CdxTypeaheadSearch } = require( '@wikimedia/codex-search' ),
|
const { CdxTypeaheadSearch } = require( '@wikimedia/codex-search' ),
|
||||||
{ defineComponent, nextTick } = require( 'vue' ),
|
{ defineComponent, nextTick } = require( 'vue' ),
|
||||||
client = require( './restSearchClient.js' ),
|
client = require( './restSearchClient.js' ),
|
||||||
|
@ -153,6 +155,12 @@ module.exports = exports = defineComponent( {
|
||||||
'vector-search-box-disable-transitions': this.disableTransitions,
|
'vector-search-box-disable-transitions': this.disableTransitions,
|
||||||
'vector-typeahead-search--active': this.isFocused
|
'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: {
|
methods: {
|
||||||
|
@ -172,15 +180,55 @@ module.exports = exports = defineComponent( {
|
||||||
return;
|
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();
|
instrumentation.listeners.onFetchStart();
|
||||||
|
|
||||||
restClient.fetchByTitle( query, 10, this.showDescription ).fetch
|
search.fetch
|
||||||
.then( ( data ) => {
|
.then( ( data ) => {
|
||||||
// Only use these results if they're still relevant
|
// Only use these results if they're still relevant
|
||||||
// If currentSearchQuery !== query, these results are for a previous search
|
// If currentSearchQuery !== query, these results are for a previous search
|
||||||
// and we shouldn't show them.
|
// and we shouldn't show them.
|
||||||
if ( this.currentSearchQuery === query ) {
|
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 );
|
this.searchFooterUrl = urlGenerator.generateUrl( query );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,13 +156,14 @@ function generateUrl( suggestion, meta ) {
|
||||||
* with the `wprov` parameter added to each result's url (if any).
|
* with the `wprov` parameter added to each result's url (if any).
|
||||||
*
|
*
|
||||||
* @param {SearchResultPartial[]} results Not modified.
|
* @param {SearchResultPartial[]} results Not modified.
|
||||||
|
* @param {number} offset Offset to add to the index of each result.
|
||||||
* @return {SearchResultPartial[]}
|
* @return {SearchResultPartial[]}
|
||||||
*/
|
*/
|
||||||
function addWprovToSearchResultUrls( results ) {
|
function addWprovToSearchResultUrls( results, offset ) {
|
||||||
return results.map( ( result, index ) => {
|
return results.map( ( result, index ) => {
|
||||||
if ( result.url ) {
|
if ( result.url ) {
|
||||||
const uri = new mw.Uri( 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() } );
|
result = Object.assign( {}, result, { url: uri.toString() } );
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -71,9 +71,19 @@ function adaptApiResponse( config, query, restResponse, showDescription ) {
|
||||||
* @return {AbortableSearchFetch}
|
* @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
|
* @typedef {Object} SearchClient
|
||||||
* @property {fetchByTitle} fetchByTitle
|
* @property {fetchByTitle} fetchByTitle
|
||||||
|
* @property {loadMore} [loadMore]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,7 +21,7 @@ describe( 'instrumentation', () => {
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'addWprovToSearchResultUrls', () => {
|
test( 'addWprovToSearchResultUrls without offset', () => {
|
||||||
const url1 = 'https://host/?title=Special%3ASearch&search=Aa',
|
const url1 = 'https://host/?title=Special%3ASearch&search=Aa',
|
||||||
url2Base = 'https://host/?title=Special%3ASearch&search=Ab',
|
url2Base = 'https://host/?title=Special%3ASearch&search=Ab',
|
||||||
url3 = 'https://host/Ac';
|
url3 = 'https://host/Ac';
|
||||||
|
@ -43,7 +43,7 @@ describe( 'instrumentation', () => {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
expect( instrumentation.addWprovToSearchResultUrls( results ) )
|
expect( instrumentation.addWprovToSearchResultUrls( results, 0 ) )
|
||||||
.toStrictEqual( [
|
.toStrictEqual( [
|
||||||
{
|
{
|
||||||
title: 'Aa',
|
title: 'Aa',
|
||||||
|
@ -63,4 +63,31 @@ describe( 'instrumentation', () => {
|
||||||
] );
|
] );
|
||||||
expect( results[ 0 ].url ).toStrictEqual( url1 );
|
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`
|
||||||
|
}
|
||||||
|
] );
|
||||||
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
Loading…
Reference in a new issue