From 3028a4f9d882b4f62abbd8ef16ba4664c0fb96b0 Mon Sep 17 00:00:00 2001 From: Nicholas Ray Date: Wed, 15 Dec 2021 20:13:56 -0700 Subject: [PATCH] Refactor search component expand behavior and add auto-expand-width prop to search component In preparation for I30c670e3f195f77a27715c6b494a3088b7a55712, refactor the search component expand behavior so that it can accomodate the new changes in WVUI while maintaining backwards compatibility with the status quo. Additionally, pass/enable the `auto-expand-width` prop to the main header's search. This will be inert until the new changes in WVUI have landed. Bug: T297531 Change-Id: Id8d3bd4aa74113b91ecaf66cb58cf5625db8a302 --- includes/SkinVector.php | 32 +++++++++------ resources/skins.vector.search/App.vue | 5 +++ .../skins.vector.search.js | 22 +++++----- .../components/Header.less | 40 ++++++++++++++----- .../components/StickyHeader.less | 28 ++++++++----- .../components/VueEnhancedSearchBox.less | 29 ++++++++------ 6 files changed, 101 insertions(+), 55 deletions(-) diff --git a/includes/SkinVector.php b/includes/SkinVector.php index e3ef812..c94c1f4 100644 --- a/includes/SkinVector.php +++ b/includes/SkinVector.php @@ -105,7 +105,8 @@ class SkinVector extends SkinMustache { 'tabindex' => '-1', 'class' => 'sticky-header-icon' ]; - private const SEARCH_EXPANDING_CLASS = 'vector-search-box-show-thumbnail'; + private const SEARCH_SHOW_THUMBNAIL_CLASS = 'vector-search-box-show-thumbnail'; + private const SEARCH_AUTO_EXPAND_WIDTH_CLASS = 'vector-search-box-auto-expand-width'; private const STICKY_HEADER_ENABLED_CLASS = 'vector-sticky-header-enabled'; private const CLASS_QUIET_BUTTON = 'mw-ui-button mw-ui-quiet'; private const CLASS_PROGRESSIVE = 'mw-ui-progressive'; @@ -583,7 +584,8 @@ class SkinVector extends SkinMustache { !$this->isLegacy(), // is primary mode of search true, - 'searchform' + 'searchform', + true ), 'data-vector-sticky-header' => VectorServices::getFeatureManager()->isFeatureEnabled( Constants::FEATURE_STICKY_HEADER @@ -593,7 +595,8 @@ class SkinVector extends SkinMustache { // Collapse inside search box is disabled. false, false, - 'vector-sticky-search-form' + 'vector-sticky-search-form', + false ), VectorServices::getFeatureManager()->isFeatureEnabled( Constants::FEATURE_STICKY_HEADER_EDIT @@ -657,9 +660,16 @@ class SkinVector extends SkinMustache { * @param bool $isCollapsible * @param bool $isPrimary * @param string $formId + * @param bool $autoExpandWidth * @return array modified version of $searchBoxData */ - private function getSearchData( array $searchBoxData, bool $isCollapsible, bool $isPrimary, string $formId ) { + private function getSearchData( + array $searchBoxData, + bool $isCollapsible, + bool $isPrimary, + string $formId, + bool $autoExpandWidth + ) { $searchClass = ''; // Determine the search widget treatment to send to the user @@ -671,8 +681,9 @@ class SkinVector extends SkinMustache { $searchClass .= ' vector-search-box-collapses '; } - if ( $this->shouldSearchExpand() ) { - $searchClass .= ' ' . self::SEARCH_EXPANDING_CLASS; + if ( $this->doesSearchHaveThumbnails() ) { + $searchClass .= ' ' . self::SEARCH_SHOW_THUMBNAIL_CLASS . + ( $autoExpandWidth ? ' ' . self::SEARCH_AUTO_EXPAND_WIDTH_CLASS : '' ); } // Annotate search box with a component class. @@ -728,15 +739,12 @@ class SkinVector extends SkinMustache { } /** - * Determines whether or not the search input should expand when focused - * before WVUI search is loaded. In WVUI, the search input expands to - * accomodate thumbnails in the suggestion list. When thumbnails are - * disabled, the input should not expand. Note this is only relevant for WVUI - * search (not legacy search). + * Returns `true` if WVUI is enabled to show thumbnails and `false` otherwise. + * Note this is only relevant for WVUI search (not legacy search). * * @return bool */ - private function shouldSearchExpand(): bool { + private function doesSearchHaveThumbnails(): bool { $featureManager = VectorServices::getFeatureManager(); return $featureManager->isFeatureEnabled( Constants::FEATURE_USE_WVUI_SEARCH ) && diff --git a/resources/skins.vector.search/App.vue b/resources/skins.vector.search/App.vue index cbd3af2..2c22386 100644 --- a/resources/skins.vector.search/App.vue +++ b/resources/skins.vector.search/App.vue @@ -17,6 +17,7 @@ :show-thumbnail="showThumbnail" :show-description="showDescription" :highlight-query="highlightQuery" + :auto-expand-width="autoExpandWidth" @fetch-start="instrumentation.onFetchStart" @fetch-end="instrumentation.onFetchEnd" @suggestion-click="instrumentation.onSuggestionClick" @@ -121,6 +122,10 @@ module.exports = { highlightQuery: { type: Boolean, default: true + }, + autoExpandWidth: { + type: Boolean, + default: false } }, data: function () { diff --git a/resources/skins.vector.search/skins.vector.search.js b/resources/skins.vector.search/skins.vector.search.js index c337007..e51ea7c 100644 --- a/resources/skins.vector.search/skins.vector.search.js +++ b/resources/skins.vector.search/skins.vector.search.js @@ -6,18 +6,18 @@ var config = require( './config.json' ); /** - * @param {Element} searchForm + * @param {Element} searchBox * @return {void} */ -function initApp( searchForm ) { - var +function initApp( searchBox ) { + var searchForm = searchBox.querySelector( '.vector-search-box-form' ), titleInput = /** @type {HTMLInputElement|null} */ ( - searchForm.querySelector( 'input[name=title]' ) + searchBox.querySelector( 'input[name=title]' ) ), - search = /** @type {HTMLInputElement|null} */ ( searchForm.querySelector( 'input[name="search"]' ) ), + search = /** @type {HTMLInputElement|null} */ ( searchBox.querySelector( 'input[name="search"]' ) ), searchPageTitle = titleInput && titleInput.value; - if ( !search || !titleInput ) { + if ( !searchForm || !search || !titleInput ) { throw new Error( 'Attempted to create Vue search element from an incompatible element.' ); } @@ -31,7 +31,8 @@ function initApp( searchForm ) { searchPageTitle: searchPageTitle, searchTitle: search.getAttribute( 'title' ), searchPlaceholder: search.getAttribute( 'placeholder' ), - searchQuery: search.value + searchQuery: search.value, + autoExpandWidth: searchBox ? searchBox.classList.contains( 'vector-search-box-auto-expand-width' ) : false // Pass additional config from server. }, config ) ) @@ -42,11 +43,10 @@ function initApp( searchForm ) { * @return {void} */ function main( document ) { - var - searchForms = document.querySelectorAll( '.vector-search-box-form' ); + var searchBoxes = document.querySelectorAll( '.vector-search-box' ); - searchForms.forEach( function ( searchForm ) { - initApp( searchForm ); + searchBoxes.forEach( function ( searchBox ) { + initApp( searchBox ); } ); } main( document ); diff --git a/resources/skins.vector.styles/components/Header.less b/resources/skins.vector.styles/components/Header.less index 5c0b535..f345d72 100644 --- a/resources/skins.vector.styles/components/Header.less +++ b/resources/skins.vector.styles/components/Header.less @@ -32,9 +32,20 @@ flex-grow: 1; } + // Allocate space for the extra width of the input when the search component + // has thumbnails. + // + // FIXME: This can be removed when WVUI in core has been upgraded to use the + // `.wvui-typeahead-search--auto-expand-width` class. + .wvui-typeahead-search--show-thumbnail:not( .wvui-typeahead-search--auto-expand-width ) { + margin-left: @size-search-expand; + } + @media ( min-width: @width-breakpoint-desktop ) { + @margin-start-search: 40px; + .vector-search-box { - margin-left: unit( 40px / @font-size-browser / @font-size-base, em ); // 2.85714286em @ 16 & 0.875em + margin-left: unit( @margin-start-search / @font-size-browser / @font-size-base, em ); // 2.85714286em @ 16 & 0.875em margin-right: @margin-end-search; // Support: IE 8, Firefox 18-, Chrome 19-, Safari 5.1-, Opera 19-, Android 4.4.4-. @@ -45,10 +56,17 @@ min-width: @min-width-search-desktop; flex-basis: @min-width-search; - &-form, - .wvui-typeahead-search { + > div { max-width: @max-width-search; } + + &.vector-search-box-show-thumbnail { + margin-left: unit( ( @margin-start-search - @size-search-expand ) / @font-size-browser / @font-size-base, em ); // 1.14285714em @ 16 & 0.875em + + > div { + max-width: @max-width-search + unit( @size-search-expand / @font-size-browser / @font-size-base, em ); // 37.42857143em @ 16 & 0.875em + } + } } } @@ -75,15 +93,19 @@ margin-right: @margin-end-search; } - // Increase the start margin of the search box to account for the input - // expanding on focus. - .vector-search-box-show-thumbnail { - margin-left: @size-search-expand + @padding-horizontal-tabs; - } - .wvui-typeahead-search__wrapper { position: static; } + + // Position the start of suggestion list at the start of the input. + // + // FIXME: This can be removed when WVUI in core has been upgraded to use + // the `.wvui-typeahead-search--auto-expand-width` class. + .wvui-typeahead-search--show-thumbnail:not( .wvui-typeahead-search--auto-expand-width ) { + .wvui-typeahead-search__suggestions { + left: 0; + } + } } } } diff --git a/resources/skins.vector.styles/components/StickyHeader.less b/resources/skins.vector.styles/components/StickyHeader.less index f250eb2..d77f5aa 100644 --- a/resources/skins.vector.styles/components/StickyHeader.less +++ b/resources/skins.vector.styles/components/StickyHeader.less @@ -101,6 +101,11 @@ } &.vector-header-search-toggled { + // .wvui-input__input left padding (36px) - the .wvui-icon svg width (20px) + // - the icon left padding (12px [1]) = 4px + // [1] see .wvui-typeahead-search--show-thumbnail .wvui-input__input:focus) + @margin-start-search-box: 4px; + .vector-sticky-header-search-toggle, .vector-sticky-header-context-bar { display: none; @@ -109,24 +114,27 @@ .vector-search-box { display: block; flex-basis: unit( 500px / @font-size-browser / @font-size-base, em ); - - .wvui-typeahead-search { - // .wvui-input__input left padding (36px) - the .wvui-icon svg width (20px) - // - the icon left padding (12px [1]) = 4px - // [1] see .wvui-typeahead-search--show-thumbnail .wvui-input__input:focus) - margin-left: 4px; - } + margin-left: @margin-start-search-box; } - // T296318 Increase the start margin of the search box to account for the input - // expanding on focus. + // T296318 Decrease the start margin of the search box to account for the + // icon's increased start position when the search component has thumbnails. .vector-search-box-show-thumbnail { - margin-left: @margin-end-search; + margin-left: @margin-start-search-box - ( @size-search-expand / 2 ); .wvui-input__start-icon { color: @colorGray2; } } + + // Allocate space for the extra width of the input when the search component + // has thumbnails. + // + // FIXME: This can be removed when WVUI in core has been upgraded + // to use the `.wvui-typeahead-search--full-width` class. + .wvui-typeahead-search--show-thumbnail:not( .wvui-typeahead-search--full-width ) { + margin-left: @size-search-expand; + } } } diff --git a/resources/skins.vector.styles/components/VueEnhancedSearchBox.less b/resources/skins.vector.styles/components/VueEnhancedSearchBox.less index 286c22e..19ee08a 100644 --- a/resources/skins.vector.styles/components/VueEnhancedSearchBox.less +++ b/resources/skins.vector.styles/components/VueEnhancedSearchBox.less @@ -81,7 +81,7 @@ bottom: 0; // Accounts for the 1px input border. Derived from // https://gerrit.wikimedia.org/g/wvui/+/refs/changes/93/650593/10/src/components/input/Input.vue#163 - left: 1px; + left: @border-width-base; // Increase size to match WVUI. width: @size-search-figure; } @@ -93,13 +93,23 @@ } .vector-search-box-show-thumbnail { + .searchButton { + // Accounts for the margin that allocates space for the input expanding and + // 1px input border. + left: @size-search-expand + @border-width-base; + } + + .vector-search-box-input { + margin-left: @size-search-expand; + width: ~'calc( 100% - @{size-search-expand} )'; + } + // Recreate WVUI expanding input. .vector-search-box-input:focus { - position: relative; + margin-left: 0; // Use ~ and fixed values to disable the LESS transformation in ResourceLoader LESS implementation. padding-left: ~'calc( @{size-search-figure} + @{size-search-expand} )'; - width: ~'calc( 100% + @{size-search-expand} )'; - left: ~'calc( -1 * @{size-search-expand} )'; + width: 100%; } // Reposition search icon for expanded input. @@ -107,15 +117,8 @@ // Derived from // https://gerrit.wikimedia.org/g/wvui/+/refs/changes/93/650593/10/src/components/typeahead-search/TypeaheadSearch.vue#655 // (12px of space between the border and the icon) with 1px to account for the focused input border. - @space-icon-start: 12px; - left: -@size-search-expand + @space-icon-start + @border-width-base; - } - - // Update search loader to match width and position of WVUI expanding input. - .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; + @space-icon-start: @size-search-expand / 2; + left: @space-icon-start + @border-width-base; } } }