mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/Vector.git
synced 2024-11-26 16:40:33 +00:00
d34fae74f1
This ensures that the wprov parameter is included when users follow the link as a link (click, middle-click, etc.), and also prepares us for a future where pressing Enter after selecting a search result navigates to that result’s URL instead of submitting the form. It also matches the behavior of the legacy search form. We put this in App.vue + instrumentation.js, not in urlGenerator.js, because we also want wprov to be added when custom URL generators or search clients are used. (The reason for instrumentation.js instead of purely App.vue is just that it’s easier to test there.) In the tests, we need to update @wikimedia/mw-node-qunit so that we have a sufficiently functional mw.Uri() mock. Bug: T317682 Change-Id: I765d3bbf89b2253add7b50305c362e4bbc9ecceb
214 lines
5 KiB
Vue
214 lines
5 KiB
Vue
<template>
|
|
<cdx-typeahead-search
|
|
:id="id"
|
|
ref="searchForm"
|
|
class="vector-typeahead-search"
|
|
:class="rootClasses"
|
|
:search-results-label="$i18n( 'searchresults' ).text()"
|
|
:accesskey="searchAccessKey"
|
|
:title="searchTitle"
|
|
:placeholder="searchPlaceholder"
|
|
:aria-label="searchPlaceholder"
|
|
:initial-input-value="searchQuery"
|
|
:button-label="$i18n( 'searchbutton' ).text()"
|
|
:form-action="action"
|
|
:show-thumbnail="showThumbnail"
|
|
:highlight-query="highlightQuery"
|
|
:auto-expand-width="autoExpandWidth"
|
|
:search-results="suggestions"
|
|
:search-footer-url="searchFooterUrl"
|
|
@input="onInput"
|
|
@search-result-click="instrumentation.onSuggestionClick"
|
|
@submit="onSubmit"
|
|
@focus="onFocus"
|
|
@blur="onBlur"
|
|
>
|
|
<template #default>
|
|
<input
|
|
type="hidden"
|
|
name="title"
|
|
:value="searchPageTitle"
|
|
>
|
|
<input
|
|
type="hidden"
|
|
name="wprov"
|
|
:value="wprov"
|
|
>
|
|
</template>
|
|
<!-- eslint-disable-next-line vue/no-template-shadow -->
|
|
<template #search-footer-text="{ searchQuery }">
|
|
<span v-i18n-html:vector-searchsuggest-containing="[ searchQuery ]"></span>
|
|
</template>
|
|
</cdx-typeahead-search>
|
|
</template>
|
|
|
|
<script>
|
|
/* global SearchSubmitEvent */
|
|
const { CdxTypeaheadSearch } = require( '@wikimedia/codex-search' ),
|
|
{ defineComponent, nextTick } = require( 'vue' ),
|
|
client = require( './restSearchClient.js' ),
|
|
restClient = client( mw.config ),
|
|
urlGenerator = require( './urlGenerator.js' )( mw.config ),
|
|
instrumentation = require( './instrumentation.js' );
|
|
|
|
// @vue/component
|
|
module.exports = exports = defineComponent( {
|
|
name: 'App',
|
|
compatConfig: {
|
|
MODE: 3
|
|
},
|
|
compilerOptions: {
|
|
whitespace: 'condense'
|
|
},
|
|
components: { CdxTypeaheadSearch },
|
|
props: {
|
|
id: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
searchPageTitle: {
|
|
type: String,
|
|
default: 'Special:Search'
|
|
},
|
|
autofocusInput: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
action: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
/** The keyboard shortcut to focus search. */
|
|
// eslint-disable-next-line vue/require-default-prop
|
|
searchAccessKey: {
|
|
type: String
|
|
},
|
|
/** The access key informational tip for search. */
|
|
// eslint-disable-next-line vue/require-default-prop
|
|
searchTitle: {
|
|
type: String
|
|
},
|
|
/** The ghost text shown when no search query is entered. */
|
|
// eslint-disable-next-line vue/require-default-prop
|
|
searchPlaceholder: {
|
|
type: String
|
|
},
|
|
/**
|
|
* The search query string taken from the server-side rendered input immediately before
|
|
* client render.
|
|
*/
|
|
// eslint-disable-next-line vue/require-default-prop
|
|
searchQuery: {
|
|
type: String
|
|
},
|
|
showThumbnail: {
|
|
type: Boolean,
|
|
// eslint-disable-next-line vue/no-boolean-default
|
|
default: true
|
|
},
|
|
showDescription: {
|
|
type: Boolean,
|
|
// eslint-disable-next-line vue/no-boolean-default
|
|
default: true
|
|
},
|
|
highlightQuery: {
|
|
type: Boolean,
|
|
// eslint-disable-next-line vue/no-boolean-default
|
|
default: true
|
|
},
|
|
autoExpandWidth: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
// -1 here is the default "active suggestion index".
|
|
wprov: instrumentation.getWprovFromResultIndex( -1 ),
|
|
|
|
// Suggestions to be shown in the TypeaheadSearch menu.
|
|
suggestions: [],
|
|
|
|
// Link to the search page for the current search query.
|
|
searchFooterUrl: '',
|
|
|
|
// Whether to apply a CSS class that disables the CSS transitions on the text input
|
|
disableTransitions: this.autofocusInput,
|
|
|
|
instrumentation: instrumentation.listeners,
|
|
|
|
isFocused: false
|
|
};
|
|
},
|
|
computed: {
|
|
rootClasses() {
|
|
return {
|
|
'vector-search-box-disable-transitions': this.disableTransitions,
|
|
'vector-typeahead-search--active': this.isFocused
|
|
};
|
|
}
|
|
},
|
|
methods: {
|
|
/**
|
|
* Fetch suggestions when new input is received.
|
|
*
|
|
* @param {string} value
|
|
*/
|
|
onInput: function ( value ) {
|
|
const searchApiUrl = mw.config.get( 'wgVectorSearchApiUrl',
|
|
mw.config.get( 'wgScriptPath' ) + '/rest.php'
|
|
),
|
|
query = value.trim();
|
|
|
|
if ( query === '' ) {
|
|
this.suggestions = [];
|
|
this.searchFooterUrl = '';
|
|
return;
|
|
}
|
|
|
|
instrumentation.listeners.onFetchStart();
|
|
|
|
restClient.fetchByTitle( query, searchApiUrl, 10, this.showDescription ).fetch
|
|
.then( ( data ) => {
|
|
this.suggestions = instrumentation.addWprovToSearchResultUrls( data.results );
|
|
this.searchFooterUrl = urlGenerator.generateUrl( query );
|
|
|
|
const event = {
|
|
numberOfResults: data.results.length,
|
|
query: query
|
|
};
|
|
instrumentation.listeners.onFetchEnd( event );
|
|
} )
|
|
.catch( () => {
|
|
// TODO: error handling
|
|
} );
|
|
},
|
|
|
|
/**
|
|
* @param {SearchSubmitEvent} event
|
|
*/
|
|
onSubmit( event ) {
|
|
this.wprov = instrumentation.getWprovFromResultIndex( event.index );
|
|
|
|
instrumentation.listeners.onSubmit( event );
|
|
},
|
|
|
|
onFocus() {
|
|
this.isFocused = true;
|
|
},
|
|
|
|
onBlur() {
|
|
this.isFocused = false;
|
|
}
|
|
},
|
|
mounted() {
|
|
if ( this.autofocusInput ) {
|
|
this.$refs.searchForm.focus();
|
|
nextTick( () => {
|
|
this.disableTransitions = false;
|
|
} );
|
|
}
|
|
}
|
|
} );
|
|
</script>
|