Allow multiple search components on the same page

Styling should not depend on IDs to allow us to have multiple
searches in the page.

Precursor for wiring up search in the sticky header.

This also tweaks performance metrics to track separate metrics
for the sticky header search

Change-Id: I5b4192a8f5a9f95af26c1faf904f7cc994323518
This commit is contained in:
jdlrobson 2021-09-16 11:00:05 -07:00 committed by Jdlrobson
parent 67cd5b7db3
commit caed16e26f
17 changed files with 206 additions and 120 deletions

View File

@ -5,7 +5,7 @@
},
{
"resourceModule": "skins.vector.styles",
"maxSize": "9.8 kB"
"maxSize": "10.1 kB"
},
{
"resourceModule": "skins.vector.legacy.js",

View File

@ -411,7 +411,9 @@ class SkinVector extends SkinMustache {
$commonSkinData['data-search-box'] = $this->getSearchData(
$commonSkinData['data-search-box'],
!$this->isLegacy()
!$this->isLegacy(),
true,
'searchform'
);
return $commonSkinData;
@ -422,22 +424,31 @@ class SkinVector extends SkinMustache {
*
* @param array $searchBoxData
* @param bool $isCollapsible
* @param bool $isPrimary
* @param string $formId
* @return array modified version of $searchBoxData
*/
private function getSearchData( array $searchBoxData, bool $isCollapsible ) {
$searchClass = 'vector-search-box';
private function getSearchData( array $searchBoxData, bool $isCollapsible, bool $isPrimary, string $formId ) {
$searchClass = '';
// Determine the search widget treatment to send to the user
if ( VectorServices::getFeatureManager()->isFeatureEnabled( Constants::FEATURE_USE_WVUI_SEARCH ) ) {
$searchClass .= 'vector-search-box-vue ';
}
if ( $isCollapsible ) {
$searchClass .= ' vector-search-box-collapses';
$searchClass .= ' vector-search-box-collapses ';
}
if ( $this->shouldSearchExpand() ) {
$searchClass .= " " . self::SEARCH_EXPANDING_CLASS;
$searchClass .= ' ' . self::SEARCH_EXPANDING_CLASS;
}
// Annotate search box with a component class.
$searchBoxData['class'] = $searchClass;
$searchBoxData['class'] = trim( $searchClass );
$searchBoxData['is-collapsible'] = $isCollapsible;
$searchBoxData['is-primary'] = $isPrimary;
$searchBoxData['form-id'] = $formId;
// At lower resolutions the search input is hidden search and only the submit button is shown.
// It should behave like a form submit link (e.g. submit the form with no input value).

View File

@ -1,16 +1,20 @@
{{!
See @typedef SearchData
}}
<div id="p-search" role="search" class="{{class}}">
<div {{#is-primary}}id="p-search"{{/is-primary}} role="search" class="{{class}} vector-search-box">
<div>
{{#is-legacy}}
<h3 {{{html-user-language-attributes}}}>
<label for="searchInput">{{msg-search}}</label>
<label {{#is-primary}}for="searchInput"{{/is-primary}}>{{msg-search}}</label>
</h3>
{{/is-legacy}}
<form action="{{form-action}}" id="searchform">
<div id="simpleSearch"{{#input-location}} data-search-loc="{{.}}"{{/input-location}}>
{{{html-input}}}
<form action="{{form-action}}" id="{{form-id}}"
class="vector-search-box-form">
<div {{#is-primary}}id="simpleSearch"{{/is-primary}}
class="vector-search-box-inner"
{{#input-location}} data-search-loc="{{.}}"{{/input-location}}>
<input class="vector-search-box-input"
{{{html-input-attributes}}} {{#is-primary}}id="searchInput"{{/is-primary}} />
<input type="hidden" name="title" value="{{page-title}}"/>
{{! We construct two buttons (for 'go' and 'fulltext' search modes), but only one will be
visible and actionable at a time (they are overlaid on top of each other in CSS).
@ -20,8 +24,10 @@
* The mediawiki.searchSuggest module, after doing tests for the broken browsers, removes
the 'fulltext' button and handles 'fulltext' search itself; this will reveal the 'go'
button and cause it to be used. !}}
{{{html-button-search-fallback}}}
{{{html-button-search}}}
<input {{#is-primary}}id="mw-searchButton"{{/is-primary}}
{{{html-button-fulltext-attributes}}} value="{{msg-searchbutton}}" />
<input {{#is-primary}}id="searchButton"{{/is-primary}}
{{{html-button-go-attributes}}} value="{{msg-searcharticle}}" />
</div>
</form>
</div>

View File

@ -4,13 +4,20 @@
// Defined as `div`.
// Provide extra element for gadgets due to `form` already carrying an `id`.
// FIXME: Remove #simpleSearch when cache has cleared
.vector-search-box-inner,
#simpleSearch {
position: relative;
height: 100%;
}
// The search input.
#searchInput {
// Note that these rules only apply to the non-Vue enabled search input field.
// When Vue.js has loaded this element will no longer be in the page and subsituted with
// a WVUI element.
// FIXME: Remove searchInput selector when cache has cleared.
#searchInput,
.vector-search-box-input {
background-color: rgba( 255, 255, 255, 0.5 );
color: @color-base--emphasized;
width: 100%;
@ -34,11 +41,15 @@
// Support: Firefox.
-moz-appearance: textfield;
// FIXME: Remove #simpleSearch when cache has cleared
.vector-search-box-inner:hover &,
#simpleSearch:hover & {
border-color: @colorGray7;
}
// FIXME: #simpleSearch can be removed when cache has cleared.
&:focus,
.vector-search-box-inner:hover &:focus,
#simpleSearch:hover &:focus {
outline: 0;
border-color: @border-color-base--focus;
@ -60,8 +71,7 @@
// The search buttons. Fallback and search button are displayed in the same position,
// and if both are present the fulltext search one obscures the 'Go' one.
#searchButton,
#mw-searchButton {
.searchButton {
background-color: transparent;
position: absolute;
top: @border-width-base;
@ -85,7 +95,7 @@
z-index: @z-index-search-button;
}
#searchButton {
.searchButton[ name='go' ] {
background: no-repeat center/unit( 16 / @font-size-browser / @font-size-search-input, em ) url( images/search.svg );
opacity: 0.67;
}

View File

@ -23,7 +23,6 @@ var /** @type {VectorResourceLoaderVirtualConfig} */
LOAD_START_MARK = 'mwVectorVueSearchLoadStart',
LOAD_END_MARK = 'mwVectorVueSearchLoadEnd',
LOAD_MEASURE = 'mwVectorVueSearchLoadStartToLoadEnd',
SEARCH_FORM_ID = 'simpleSearch',
SEARCH_INPUT_ID = 'searchInput',
SEARCH_LOADING_CLASS = 'search-form__loader';
@ -34,18 +33,22 @@ var /** @type {VectorResourceLoaderVirtualConfig} */
* After the search module is loaded, executes a function to remove
* the loading indicator.
*
* @param {HTMLElement} element search input.
* @param {Element} element search input.
* @param {string} moduleName resourceLoader module to load.
* @param {function(): void} afterLoadFn function to execute after search module loads.
* @param {string|null} startMarker
* @param {null|function(): void} afterLoadFn function to execute after search module loads.
*/
function loadSearchModule( element, moduleName, afterLoadFn ) {
var SHOULD_TEST_SEARCH = CAN_TEST_SEARCH && moduleName === 'skins.vector.search';
function loadSearchModule( element, moduleName, startMarker, afterLoadFn ) {
var SHOULD_TEST_SEARCH = CAN_TEST_SEARCH &&
moduleName === 'skins.vector.search';
function requestSearchModule() {
if ( SHOULD_TEST_SEARCH ) {
performance.mark( LOAD_START_MARK );
if ( SHOULD_TEST_SEARCH && startMarker !== null && afterLoadFn !== null ) {
performance.mark( startMarker );
mw.loader.using( moduleName, afterLoadFn );
} else {
mw.loader.load( moduleName );
}
mw.loader.using( moduleName, afterLoadFn );
element.removeEventListener( 'focus', requestSearchModule );
}
@ -96,7 +99,7 @@ function renderSearchLoadingIndicator( event ) {
* Attaches or detaches the event listeners responsible for activating
* the loading indicator.
*
* @param {HTMLElement} element
* @param {Element} element
* @param {boolean} attach
* @param {function(Event): void} eventCallback
*/
@ -116,11 +119,15 @@ function setLoadingIndicatorListeners( element, attach, eventCallback ) {
/**
* Marks when the lazy load has completed.
*
* @param {string} startMarker
* @param {string} endMarker
* @param {string} measureMarker
*/
function markLoadEnd() {
if ( performance.getEntriesByName( LOAD_START_MARK ).length ) {
performance.mark( LOAD_END_MARK );
performance.measure( LOAD_MEASURE, LOAD_START_MARK, LOAD_END_MARK );
function markLoadEnd( startMarker, endMarker, measureMarker ) {
if ( performance.getEntriesByName( startMarker ).length ) {
performance.mark( endMarker );
performance.measure( measureMarker, startMarker, endMarker );
}
}
@ -131,8 +138,7 @@ function markLoadEnd() {
* @param {Document} document
*/
function initSearchLoader( document ) {
var searchForm = document.getElementById( SEARCH_FORM_ID ),
searchInput = document.getElementById( SEARCH_INPUT_ID ),
var searchBoxes = document.querySelectorAll( '.vector-search-box' ),
shouldUseCoreSearch;
// Allow developers to defined $wgVectorSearchHost in LocalSettings to target different APIs
@ -140,7 +146,7 @@ function initSearchLoader( document ) {
mw.config.set( 'wgVectorSearchHost', config.wgVectorSearchHost );
}
if ( !searchForm || !searchInput ) {
if ( !searchBoxes.length ) {
return;
}
@ -155,27 +161,46 @@ function initSearchLoader( document ) {
* before the search module loads.
**/
if ( shouldUseCoreSearch || !window.fetch ) {
loadSearchModule( searchInput, 'mediawiki.searchSuggest', function () {} );
} else {
searchBoxes.forEach( function ( searchBox ) {
var input = searchBox.querySelector( 'input[name="search"]' );
if ( input ) {
loadSearchModule(
input,
'mediawiki.searchSuggest',
null,
null
);
}
} );
return;
}
searchBoxes.forEach( function ( searchBox ) {
var searchInner = searchBox.querySelector( 'form > div' ),
searchInput = searchBox.querySelector( 'input[name="search"]' ),
isPrimarySearch = searchInput && searchInput.getAttribute( 'id' ) === 'searchInput';
if ( !searchInput || !searchInner ) {
return;
}
// Remove tooltips while Vue search is still loading
searchInput.setAttribute( 'autocomplete', 'off' );
searchInput.removeAttribute( 'title' );
setLoadingIndicatorListeners( searchForm, true, renderSearchLoadingIndicator );
setLoadingIndicatorListeners( searchInner, true, renderSearchLoadingIndicator );
loadSearchModule(
searchInput,
'skins.vector.search',
function () {
markLoadEnd();
isPrimarySearch ? LOAD_START_MARK : null,
isPrimarySearch ? function () {
markLoadEnd( LOAD_START_MARK, LOAD_END_MARK, LOAD_MEASURE );
setLoadingIndicatorListeners(
/** @type {HTMLElement} */ ( searchForm ),
// @ts-ignore
searchInner,
false,
renderSearchLoadingIndicator
);
}
} : null
);
}
} );
}
module.exports = {

View File

@ -42,7 +42,7 @@ function bindSearchBoxHandler( searchBox, header ) {
*
* @param {HTMLElement} searchBox
* @param {HTMLElement} header
* @param {HTMLElement} searchToggle
* @param {Element} searchToggle
*/
function bindToggleClickHandler( searchBox, header, searchToggle ) {
/**
@ -88,7 +88,7 @@ function bindToggleClickHandler( searchBox, header, searchToggle ) {
* elements. When the user clicks outside of SEARCH_BOX_SELECTOR, the class will
* be removed.
*
* @param {HTMLElement|null} searchToggle
* @param {HTMLElement|null|Element} searchToggle
*/
module.exports = function initSearchToggle( searchToggle ) {
// Check if .closest API is available (IE11 does not support it).

View File

@ -90,8 +90,8 @@ function makeStickyHeaderFunctional(
userMenu,
userMenuStickyContainer
) {
/* eslint-disable-next-line compat/compat */
var
/* eslint-disable-next-line compat/compat */
stickyObserver = new IntersectionObserver( function ( entries ) {
if ( !entries[ 0 ].isIntersecting && entries[ 0 ].boundingClientRect.top < 0 ) {
// Viewport has crossed the bottom edge of firstHeading so show sticky header.
@ -106,7 +106,8 @@ function makeStickyHeaderFunctional(
// Type declaration needed because of https://github.com/Microsoft/TypeScript/issues/3734#issuecomment-118934518
userMenuClone = /** @type {HTMLElement} */( userMenu.cloneNode( true ) ),
userMenuStickyElementsWithIds = userMenuClone.querySelectorAll( '[ id ], [ data-event-name ]' ),
userMenuStickyContainerInner = userMenuStickyContainer.querySelector( VECTOR_USER_LINKS_SELECTOR );
userMenuStickyContainerInner = userMenuStickyContainer
.querySelector( VECTOR_USER_LINKS_SELECTOR );
// Update all ids of the cloned user menu to make them unique.
makeNodeTrackable( userMenuClone );

View File

@ -5,53 +5,52 @@ var
config = require( './config.json' );
/**
* @param {HTMLElement} searchForm
* @param {NodeList} secondarySearchElements
* @param {HTMLInputElement} search
* @param {string|null} searchPageTitle title of page used for searching e.g. Special:Search
* If null then this will default to Special:Search.
* @param {Function} createElement
* @param {Element} searchForm
* @return {Vue.VNode}
* @throws {Error} if the searchForm does not
* contain input[name=title] and input[name="search"] elements.
*/
function renderFn( createElement, searchForm ) {
var
titleInput = /** @type {HTMLInputElement|null} */ (
searchForm.querySelector( 'input[name=title]' )
),
search = /** @type {HTMLInputElement|null} */ ( searchForm.querySelector( 'input[name="search"]' ) ),
searchPageTitle = titleInput && titleInput.value;
if ( !search || !titleInput ) {
throw new Error( 'Attempted to create Vue search element from an incompatible element.' );
}
return createElement( App, {
props: $.extend( {
id: searchForm.id,
autofocusInput: search === document.activeElement,
action: searchForm.getAttribute( 'action' ),
searchAccessKey: search.getAttribute( 'accessKey' ),
searchPageTitle: searchPageTitle,
searchTitle: search.getAttribute( 'title' ),
searchPlaceholder: search.getAttribute( 'placeholder' ),
searchQuery: search.value
},
// Pass additional config from server.
config
)
} );
}
/**
* @param {NodeList} searchForms
* @return {void}
*/
function initApp( searchForm, secondarySearchElements, search, searchPageTitle ) {
/**
*
* @ignore
* @param {Function} createElement
* @param {string} id
* @return {Vue.VNode}
*/
var renderFn = function ( createElement, id ) {
return createElement( App, {
props: $.extend( {
id: id,
autofocusInput: search === document.activeElement,
action: searchForm.getAttribute( 'action' ),
searchAccessKey: search.getAttribute( 'accessKey' ),
searchPageTitle: searchPageTitle,
searchTitle: search.getAttribute( 'title' ),
searchPlaceholder: search.getAttribute( 'placeholder' ),
searchQuery: search.value
},
// Pass additional config from server.
config
)
} );
};
// eslint-disable-next-line no-new
new Vue( {
el: searchForm,
render: function ( createElement ) {
return renderFn( createElement, 'searchform' );
}
} );
// Initialize secondary search elements like the search in the sticky header.
Array.prototype.forEach.call( secondarySearchElements, function ( secondarySearchElement ) {
function initApp( searchForms ) {
searchForms.forEach( function ( searchForm ) {
// eslint-disable-next-line no-new
new Vue( {
el: secondarySearchElement,
el: /** @type {Element} */ ( searchForm ),
render: function ( createElement ) {
return renderFn( createElement, secondarySearchElement.id );
return renderFn( createElement, /** @type {Element} */ ( searchForm ) );
}
} );
} );
@ -62,16 +61,9 @@ function initApp( searchForm, secondarySearchElements, search, searchPageTitle )
*/
function main( document ) {
var
searchForm = /** @type {HTMLElement} */ ( document.querySelector( '#searchform' ) ),
titleInput = /** @type {HTMLInputElement|null} */ (
searchForm.querySelector( 'input[name=title]' )
),
search = /** @type {HTMLInputElement|null} */ ( document.getElementById( 'searchInput' ) ),
// Since App.vue requires a unique id prop, only query elements with an id attribute.
secondarySearchElements = document.querySelectorAll( '.vector-secondary-search[id]' );
// FIXME: Use .vector-search-box-form instead when cache allows.
searchForms = document.querySelectorAll( '.vector-search-box form' );
if ( search && searchForm ) {
initApp( searchForm, secondarySearchElements, search, titleInput && titleInput.value );
}
initApp( searchForms );
}
main( document );

View File

@ -1,6 +1,6 @@
@import 'mediawiki.mixins.less';
// Search portlet.
#p-search h3 {
.vector-search-box h3 {
.mixin-screen-reader-text();
}

View File

@ -128,8 +128,10 @@ body {
// Defined as `div`.
// Provide extra element for gadgets due to `form` already carrying an `id`.
// FIXME: This selector requires knowledge of the internals of the search component
// FIXME: #simpleSearch selector can be removed when cache has cleared.
// and should not be used here.
#simpleSearch {
#simpleSearch,
.vector-search-box-inner {
min-width: 5em;
// Support: IE 8, Firefox 18-, Chrome 19-, Safari 5.1-, Opera 19-, Android 4.4.4-.
width: 13.2em;
@ -180,7 +182,9 @@ body {
padding-left: 0.5em;
}
#p-search {
// FIXME: p-search is for cached HTML only. Can be removed in 1 week.
#p-search,
.vector-search-box {
margin-right: 1em;
}

View File

@ -45,7 +45,9 @@
min-width: @min-width-search-desktop;
flex-basis: @min-width-search;
> div > #searchform,
// FIXME: Modify to use .vector-search-box-form when cache allows.
// When changing check the specificity is strong enough so that is still applies.
> div > form,
.wvui-typeahead-search {
max-width: @max-width-search;
}

View File

@ -21,7 +21,7 @@
// https://gerrit.wikimedia.org/r/plugins/gitiles/wvui/+/e32b54f3b8d1118b6a25cdc46b5638d6d048533e/src/themes/wikimedia-ui.less#27
@padding-vertical-typeahead-suggestion: 8px;
#simpleSearch.search-form__loader:after {
.search-form__loader:after {
// Set the i18n message.
content: attr( data-loading-msg );
//

View File

@ -27,25 +27,33 @@
}
// Typeahead search elements
// FIXME: remove ID selectors when cache has cleared.
#searchInput,
#searchButton,
#mw-searchButton {
#mw-searchButton,
.vector-search-box-vue .vector-search-box-input,
.vector-search-box-vue .searchButton {
// Overrides #mw-searchButton in resources/skins.vector.styles/SearchBox.less
font-size: inherit;
}
// FIXME: remove #searchInput selector when cache has cleared.
.vector-search-box-vue .vector-search-box-input,
#searchInput {
height: @size-base;
}
#searchButton,
#mw-searchButton {
// FIXME: Remove searchButton when cache has cleared.
.vector-search-box-vue .searchButton,
#searchButton {
background-size: @background-size-x-search-button auto;
}
// Only apply the following WVUI-related rules to clients who have js enabled.
// TODO: .skin-vector-search-vue class can be removed when $wgVectorUseWvuiSearch is no longer supported.
.client-js .skin-vector-search-vue {
// TODO: .skin-vector-search-vue class can be removed when $wgVectorUseWvuiSearch is no longer supported
// OR .vector-search-box-vue is in cached HTML.
.client-js .skin-vector-search-vue,
.client-js .vector-search-box-vue {
// Derived from @size-search-figure in WVUI
// https://gerrit.wikimedia.org/r/plugins/gitiles/wvui/+/e32b54f3b8d1118b6a25cdc46b5638d6d048533e/src/themes/wikimedia-ui.less#21
@size-search-figure: unit( 36px / @font-size-browser / @font-size-base, em );
@ -56,11 +64,13 @@
text-decoration: none;
}
#searchform-suggestions li {
.wvui-typeahead-search__suggestions li {
// Remove margin-bottom on li elements that is applied by mediawiki.skinning/elements.css.
margin-bottom: 0;
}
// FIXME: Remove #searchInput selector when cache has cleared.
.vector-search-box-input,
#searchInput {
padding-left: @size-search-figure;
// Derived from @padding-input-text in WVUI's Input component.
@ -68,8 +78,7 @@
}
// Move & resize search icon to match WVUI.
#searchButton,
#mw-searchButton {
.searchButton {
// T270202: Act like a an inert element instead of a submit button before
// WVUI loads to discourage people clicking on it since it is a submit
// button styled to look like WVUI's inert start icon. Note, ideally these
@ -97,6 +106,8 @@
.p-search--show-thumbnail,
.vector-search-box-show-thumbnail {
// Recreate WVUI expanding input.
// FIXME: Remove #searchInput selector when cache has cleared.
.vector-search-box-input:focus,
#searchInput:focus {
position: relative;
// Use ~ and fixed values to disable the LESS transformation in ResourceLoader LESS implementation.
@ -106,6 +117,8 @@
}
// Reposition search icon for expanded input.
// FIXME: Remove #searchInput selectors when cache has cleared.
.vector-search-box-input:focus ~ .searchButton,
#searchInput:focus ~ #searchButton,
#searchInput:focus ~ #mw-searchButton {
// Derived from
@ -116,7 +129,9 @@
}
// Update search loader to match width and position of WVUI expanding input.
#simpleSearch.search-form__loader:after {
// FIXME: Remove #simpleSearch selector when cache has cleared.
#simpleSearch.search-form__loader:after,
.vector-search-box-inner.search-form__loader:after {
width: ~'calc( 100% + @{size-search-expand} )';
left: ~'calc( -1 * @{size-search-expand} )';
padding-left: @size-search-expand;

View File

@ -12,7 +12,7 @@
"license-name": "GPL-2.0-or-later",
"type": "skin",
"requires": {
"MediaWiki": ">= 1.37.0"
"MediaWiki": ">= 1.38.0"
},
"ValidSkinNames": {
"vector": {
@ -51,6 +51,8 @@
"vector-jumptosearch",
"vector-jumptocontent",
"search",
"searchbutton",
"searcharticle",
"sitesubtitle",
"sitetitle",
"tagline"

View File

@ -6,18 +6,29 @@ import searchBoxTemplate from '!!raw-loader!../includes/templates/SearchBox.must
import Button from '!!raw-loader!../includes/templates/Button.mustache';
import { htmlUserLanguageAttributes } from './utils';
const INPUT_ATTRIBUTES = 'type="search" name="search" placeholder="Search Wikipedia" title="Search Wikipedia [⌃⌥f]" accesskey="f" id="searchInput" autocomplete="off"';
const FULL_TEXT_ATTRIBUTES = 'name="fulltext" title="Search pages for this text" id="mw-searchButton" class="searchButton mw-fallbackSearchButton"';
const GO_ATTRIBUTES = 'name="go" title="Go to a page with this exact name if it exists" id="searchButton" class="searchButton"';
/**
* @type {SearchData}
*/
const searchBoxData = {
'form-action': '/w/index.php',
class: 'vector-search-box vector-search-show-thumbnail',
'form-id': 'searchform',
'is-primary': false,
class: 'vector-search-show-thumbnail',
'html-user-language-attributes': htmlUserLanguageAttributes,
'msg-search': 'Search',
'html-input': '<input type="search" name="search" placeholder="Search Wikipedia" title="Search Wikipedia [⌃⌥f]" accesskey="f" id="searchInput" autocomplete="off">',
'html-input': `<input ${INPUT_ATTRIBUTES}>`,
'page-title': 'Special:Search',
'html-button-search-fallback': '<input type="submit" name="fulltext" value="Search" title="Search pages for this text" id="mw-searchButton" class="searchButton mw-fallbackSearchButton"/>',
'html-button-search': '<input type="submit" name="go" value="Go" title="Go to a page with this exact name if it exists" id="searchButton" class="searchButton">'
'html-input-attributes': INPUT_ATTRIBUTES,
'html-button-fulltext-attributes': FULL_TEXT_ATTRIBUTES,
'msg-searchbutton': 'Search',
'msg-searcharticle': 'Go',
'html-button-go-attributes': GO_ATTRIBUTES,
'html-button-search-fallback': `<input type="submit" ${FULL_TEXT_ATTRIBUTES} value="Search" />`,
'html-button-search': `<input type="submit" ${GO_ATTRIBUTES} value="Go">`
};
/**

View File

@ -1,6 +1,6 @@
import mustache from 'mustache';
import '../resources/skins.vector.styles/SearchBox.less';
import '../resources/skins.vector.styles/layouts/screen.less';
import { searchBoxData, searchBoxDataWithCollapsing, searchBoxTemplate,
SEARCH_TEMPLATE_PARTIALS
} from './SearchBox.stories.data';

View File

@ -39,11 +39,18 @@
/**
* @typedef {Object} SearchData
* @property {string|null} msg-search
* @property {string|null} msg-searchbutton
* @property {string|null} msg-searcharticle
* @property {string} [html-user-language-attributes]
* @property {boolean} is-primary is this the primary method of search?
* @property {string} form-action URL
* @property {string} form-id
* @property {string|null} html-input
* @property {string|null} [class] of the menu
* @property {string|null} page-title the title of the search page
* @property {string} html-input-attributes
* @property {string} html-button-fulltext-attributes
* @property {string} html-button-go-attributes
* @property {string|null} html-button-search-fallback
* @property {string|null} html-button-search
* @property {string} [input-location] An identifier corresponding the position of the search