Wire up sticky header search feature

Bug: T289724
Change-Id: I784ea5eb12b6f43d19769ff48a14d3fd4627853c
This commit is contained in:
jdlrobson 2021-09-16 12:27:10 -07:00
parent caed16e26f
commit 125ea5dea9
9 changed files with 71 additions and 41 deletions

View File

@ -87,3 +87,10 @@
.vector-user-menu-legacy #pt-userpage a {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAx0lEQVQ4jdXSzQmEQAwFYEuYUixhSwgkA8mQgKXYgS3YgXZgCZagHWgHuxf14t8osssGcv145CVJvjk+hBRFK2TrkK1D0cqHkN7CUBRI7L21KAqXMIDModiwD9oAkLlH0i3L+ooGiTWPAPPfJQTIHLGOB9h46YZnKS+3PI8PISW2GkV7FO2Jrb79h4+ODyElsYJYm437NSRWRCWdylgj++U0u+UAZI5E22hsWW03UWQtr2NT66zlCjz8uzNQbFiDN7F5/xB8aj57Ynp2FKI0bAAAAABJRU5ErkJggg==") !important;
}
.mw-ui-icon-wikimedia-speechBubbles:before {
background-image: linear-gradient(transparent, transparent), url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2220%22 height=%2220%22 viewBox=%220 0 20 20%22%3E%3Ctitle%3Espeech bubbles%3C/title%3E%3Cg fill=%22%23000%22%3E%3Cpath d=%22M17 4v7a2 2 0 01-2 2H4v1a2 2 0 002 2h10l4 4V6a2 2 0 00-2-2zM6 10H0v6z%22/%3E%3Crect width=%2216%22 height=%2212%22 rx=%222%22/%3E%3C/g%3E%3C/svg%3E");
}
.mw-ui-icon-wikimedia-history:before {
background-image: linear-gradient(transparent, transparent), url("data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2220%22 height=%2220%22 viewBox=%220 0 20 20%22%3E%3Ctitle%3Ehistory%3C/title%3E%3Cg fill=%22%23000%22%3E%3Cpath d=%22M9 6v5h.06l2.48 2.47 1.41-1.41L11 10.11V6z%22/%3E%3Cpath d=%22M10 1a9 9 0 00-7.85 13.35L.5 16H6v-5.5l-2.38 2.38A7 7 0 1110 17v2a9 9 0 000-18z%22/%3E%3C/g%3E%3C/svg%3E");
}

View File

@ -329,21 +329,19 @@ class SkinVector extends SkinMustache {
/**
* Generate data needed to generate the sticky header.
* Lack of i18n is intentional and will be done as part of follow up work.
* @param array $searchBoxData
* @return array
*/
private function getStickyHeaderData() {
private function getStickyHeaderData( $searchBoxData ) {
return [
'data-primary-action' => !$this->shouldHideLanguages() ? $this->getULSButtonData() : null,
'data-button-start' => [
'href' => '#p-search',
'label' => $this->msg( 'search' ),
'icon' => 'wikimedia-search',
'is-quiet' => true,
'class' => 'vector-sticky-header-search-toggle',
],
'data-search' => [
'class' => $this->shouldSearchExpand() ? self::SEARCH_EXPANDING_CLASS : '',
],
'data-search' => $searchBoxData,
'data-buttons' => [
self::TALK_ICON, self::HISTORY_ICON, self::NO_ICON, self::NO_ICON
]
@ -385,9 +383,24 @@ class SkinVector extends SkinMustache {
'is-language-in-header' => $this->isLanguagesInHeader(),
'data-search-box' => $this->getSearchData(
$parentData['data-search-box'],
!$this->isLegacy(),
// is primary mode of search
true,
'searchform'
),
'data-vector-sticky-header' => VectorServices::getFeatureManager()->isFeatureEnabled(
Constants::FEATURE_STICKY_HEADER
) ? $this->getStickyHeaderData() : false,
) ? $this->getStickyHeaderData(
$this->getSearchData(
$parentData['data-search-box'],
// Collapse inside search box is disabled.
false,
false,
'vector-sticky-search-form'
)
) : false,
] );
if ( $skin->getUser()->isRegistered() ) {
@ -409,13 +422,6 @@ class SkinVector extends SkinMustache {
);
}
$commonSkinData['data-search-box'] = $this->getSearchData(
$commonSkinData['data-search-box'],
!$this->isLegacy(),
true,
'searchform'
);
return $commonSkinData;
}

View File

@ -14,7 +14,12 @@
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}} />
{{#is-primary}}{{{html-input-attributes}}} id="searchInput"{{/is-primary}}
{{^is-primary}}
type="search" name="search"
placeholder="{{msg-searchsuggest-search}}"
{{/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).

View File

@ -7,9 +7,7 @@
{{/data-button-start}}
</div>
{{#data-search}}
<div class="vector-search-box {{class}}">
<div class="vector-secondary-search" id="vector-sticky-header-search"></div>
</div>
{{>SearchBox}}
{{/data-search}}
<div class="vector-sticky-header-context-bar">
<div class="vector-sticky-header-context-bar-primary">{{html-title}}</div>

View File

@ -23,7 +23,6 @@ var /** @type {VectorResourceLoaderVirtualConfig} */
LOAD_START_MARK = 'mwVectorVueSearchLoadStart',
LOAD_END_MARK = 'mwVectorVueSearchLoadEnd',
LOAD_MEASURE = 'mwVectorVueSearchLoadStartToLoadEnd',
SEARCH_INPUT_ID = 'searchInput',
SEARCH_LOADING_CLASS = 'search-form__loader';
/**
@ -75,8 +74,8 @@ function renderSearchLoadingIndicator( event ) {
if (
!( event.currentTarget instanceof HTMLElement ) ||
!( event.target instanceof HTMLInputElement ) ||
!( input.id === SEARCH_INPUT_ID ) ) {
!( event.target instanceof HTMLInputElement )
) {
return;
}
@ -177,6 +176,14 @@ function initSearchLoader( document ) {
searchBoxes.forEach( function ( searchBox ) {
var searchInner = searchBox.querySelector( 'form > div' ),
searchInput = searchBox.querySelector( 'input[name="search"]' ),
clearLoadingIndicators = function () {
setLoadingIndicatorListeners(
// @ts-ignore
searchInner,
false,
renderSearchLoadingIndicator
);
},
isPrimarySearch = searchInput && searchInput.getAttribute( 'id' ) === 'searchInput';
if ( !searchInput || !searchInner ) {
@ -190,15 +197,12 @@ function initSearchLoader( document ) {
searchInput,
'skins.vector.search',
isPrimarySearch ? LOAD_START_MARK : null,
// Make sure we clearLoadingIndicators so that event listeners are removed.
// Note, loading Vue.js will remove the element from the DOM.
isPrimarySearch ? function () {
markLoadEnd( LOAD_START_MARK, LOAD_END_MARK, LOAD_MEASURE );
setLoadingIndicatorListeners(
// @ts-ignore
searchInner,
false,
renderSearchLoadingIndicator
);
} : null
clearLoadingIndicators();
} : clearLoadingIndicators
);
} );
}

View File

@ -1,5 +1,6 @@
var
STICKY_HEADER_ID = 'vector-sticky-header',
initSearchToggle = require( './searchToggle.js' ),
STICKY_HEADER_APPENDED_ID = '-sticky-header',
STICKY_HEADER_VISIBLE_CLASS = 'vector-sticky-header-visible',
STICKY_HEADER_USER_MENU_CONTAINER_CLASS = 'vector-sticky-header-icon-end',
@ -158,14 +159,7 @@ function setupSearchIfNeeded( header ) {
return;
}
// Load the `skins.vector.search` module here or setup an event handler to
// load it depending on the outcome of T289718. After it loads, initialize the
// search toggle.
//
// Example:
// mw.loader.using( 'skins.vector.search', function () {
// initSearchToggle( searchToggle );
// } );
initSearchToggle( searchToggle );
}
/**

View File

@ -36,6 +36,11 @@
display: none;
}
// Hide any open menus/search results unless sticky header is visible
&:not( .vector-sticky-header-visible ) > div {
display: none;
}
&-visible {
transform: translateY( 0% );
}

View File

@ -53,6 +53,7 @@
"search",
"searchbutton",
"searcharticle",
"searchsuggest-search",
"sitesubtitle",
"sitetitle",
"tagline"

View File

@ -1,5 +1,6 @@
import template from '!!raw-loader!../includes/templates/StickyHeader.mustache';
import Button from '!!raw-loader!../includes/templates/Button.mustache';
import { searchBoxData } from './SearchBox.stories.data';
const NO_ICON = {
icon: 'none',
@ -7,6 +8,18 @@ const NO_ICON = {
class: 'sticky-header-icon'
};
const TALK_ICON = {
icon: 'none',
'is-quiet': true,
class: 'sticky-header-icon mw-ui-icon-wikimedia-speechBubbles'
};
const HISTORY_ICON = {
icon: 'none',
'is-quiet': true,
class: 'sticky-header-icon mw-ui-icon-wikimedia-history'
};
const data = {
title: 'Audre Lorde',
heading: 'Introduction',
@ -18,19 +31,16 @@ const data = {
label: '196 languages',
'html-vector-button-icon': `<span class="mw-ui-icon mw-ui-icon-wikimedia-language"></span>`
},
'data-search': {
class: ''
},
'data-search': searchBoxData,
'data-button-start': {
icon: 'wikimedia-search',
href: '#',
class: 'search-toggle',
class: 'vector-sticky-header-search-toggle',
'is-quiet': true,
label: 'Search'
},
'data-button-end': NO_ICON,
'data-buttons': [
NO_ICON, NO_ICON, NO_ICON, NO_ICON
TALK_ICON, HISTORY_ICON, NO_ICON, NO_ICON
]
};