Merge "Allow multiple search components on the same page"

This commit is contained in:
jenkins-bot 2021-09-24 17:04:14 +00:00 committed by Gerrit Code Review
commit 84023ff39c
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

@ -89,8 +89,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.
@ -105,7 +105,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