diff --git a/packages/block-library/src/search/block.json b/packages/block-library/src/search/block.json index b96f14f53c778c..ba1d4324e882fe 100644 --- a/packages/block-library/src/search/block.json +++ b/packages/block-library/src/search/block.json @@ -21,10 +21,12 @@ "__experimentalRole": "content" }, "width": { - "type": "number" + "type": "number", + "default": 100 }, "widthUnit": { - "type": "string" + "type": "string", + "default": "%" }, "buttonText": { "type": "string", @@ -37,6 +39,14 @@ "buttonUseIcon": { "type": "boolean", "default": false + }, + "buttonBehavior": { + "type": "string", + "default": "expand-searchfield" + }, + "isSearchFieldHidden": { + "type": "boolean", + "default": false } }, "supports": { diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js index ec794a60e44aca..bd92a02c36bc3f 100644 --- a/packages/block-library/src/search/edit.js +++ b/packages/block-library/src/search/edit.js @@ -29,6 +29,7 @@ import { import { useInstanceId } from '@wordpress/compose'; import { search } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; +import { useRef, useEffect } from '@wordpress/element'; /** * Internal dependencies @@ -51,6 +52,7 @@ import { // Used to calculate border radius adjustment to avoid "fat" corners when // button is placed inside wrapper. const DEFAULT_INNER_PADDING = 4; +const BUTTON_BEHAVIOR_EXPAND = 'expand-searchfield'; export default function SearchEdit( { className, @@ -69,18 +71,40 @@ export default function SearchEdit( { buttonText, buttonPosition, buttonUseIcon, + buttonBehavior, + isSearchFieldHidden, style, } = attributes; const borderRadius = style?.border?.radius; const unitControlInstanceId = useInstanceId( UnitControl ); const unitControlInputId = `wp-block-search__width-${ unitControlInstanceId }`; + const searchFieldRef = useRef(); + const buttonRef = useRef(); const units = useCustomUnits( { availableUnits: [ '%', 'px' ], defaultValues: { '%': PC_WIDTH_DEFAULT, px: PX_WIDTH_DEFAULT }, } ); + useEffect( () => { + if ( 'button-only' === buttonPosition && ! isSelected ) { + setAttributes( { + isSearchFieldHidden: true, + } ); + } + }, [ isSelected ] ); + + useEffect( () => { + if ( 'button-only' !== buttonPosition || ! isSelected ) { + return; + } + + setAttributes( { + isSearchFieldHidden: false, + } ); + }, [ width ] ); + const getBlockClassNames = () => { return classnames( className, @@ -96,6 +120,13 @@ export default function SearchEdit( { 'button-only' === buttonPosition ? 'wp-block-search__button-only' : undefined, + 'button-only' === buttonPosition && + BUTTON_BEHAVIOR_EXPAND === buttonBehavior + ? 'wp-block-search__button-behavior-expand' + : undefined, + 'button-only' === buttonPosition && isSearchFieldHidden + ? 'wp-block-search__searchfield-hidden' + : undefined, ! buttonUseIcon && 'no-button' !== buttonPosition ? 'wp-block-search__text-button' : undefined, @@ -119,7 +150,10 @@ export default function SearchEdit( { }; const getResizableSides = () => { - if ( 'button-only' === buttonPosition ) { + if ( + 'button-only' === buttonPosition && + ( 'search-page-link' === buttonBehavior || isSearchFieldHidden ) + ) { return {}; } @@ -145,6 +179,7 @@ export default function SearchEdit( { onChange={ ( event ) => setAttributes( { placeholder: event.target.value } ) } + ref={ searchFieldRef } /> ); }; @@ -157,6 +192,17 @@ export default function SearchEdit( { icon={ search } className="wp-block-search__button" style={ { borderRadius } } + onClick={ () => { + if ( + 'button-only' === buttonPosition && + BUTTON_BEHAVIOR_EXPAND === buttonBehavior + ) { + setAttributes( { + isSearchFieldHidden: ! isSearchFieldHidden, + } ); + } + } } + ref={ buttonRef } /> ) } @@ -171,6 +217,17 @@ export default function SearchEdit( { onChange={ ( html ) => setAttributes( { buttonText: html } ) } + onClick={ () => { + if ( + 'button-only' === buttonPosition && + BUTTON_BEHAVIOR_EXPAND === buttonBehavior + ) { + setAttributes( { + isSearchFieldHidden: ! isSearchFieldHidden, + } ); + } + } } + ref={ buttonRef } /> ) } > @@ -202,6 +259,7 @@ export default function SearchEdit( { onClick={ () => { setAttributes( { buttonPosition: 'no-button', + isSearchFieldHidden: false, } ); onClose(); } } @@ -213,6 +271,7 @@ export default function SearchEdit( { onClick={ () => { setAttributes( { buttonPosition: 'button-outside', + isSearchFieldHidden: false, } ); onClose(); } } @@ -224,12 +283,26 @@ export default function SearchEdit( { onClick={ () => { setAttributes( { buttonPosition: 'button-inside', + isSearchFieldHidden: false, } ); onClose(); } } > { __( 'Button Inside' ) } + ) } @@ -241,6 +314,7 @@ export default function SearchEdit( { onClick={ () => { setAttributes( { buttonUseIcon: ! buttonUseIcon, + isSearchFieldHidden: true, } ); } } className={ @@ -375,14 +449,18 @@ export default function SearchEdit( { showHandle={ isSelected } > { ( 'button-inside' === buttonPosition || - 'button-outside' === buttonPosition ) && ( + 'button-outside' === buttonPosition || + ( 'button-only' === buttonPosition && + BUTTON_BEHAVIOR_EXPAND === buttonBehavior ) ) && ( <> { renderTextField() } { renderButton() } > ) } - { 'button-only' === buttonPosition && renderButton() } + { 'button-only' === buttonPosition && + BUTTON_BEHAVIOR_EXPAND !== buttonBehavior && + renderButton() } { 'no-button' === buttonPosition && renderTextField() } diff --git a/packages/block-library/src/search/editor.scss b/packages/block-library/src/search/editor.scss index 565afc644c9cb8..55eb03565de954 100644 --- a/packages/block-library/src/search/editor.scss +++ b/packages/block-library/src/search/editor.scss @@ -22,7 +22,6 @@ // This needs high specificity because it otherwise inherits styles from `components-button`. // stylelint-disable-line no-duplicate-selectors &.wp-block-search__button.wp-block-search__button { - padding: 6px 10px; display: flex; align-items: center; } @@ -31,4 +30,10 @@ &__components-button-group { margin-top: 10px; } + + &.wp-block-search__button-behavior-expand { + .wp-block-search__input { + transition-duration: 300ms; + } + } } diff --git a/packages/block-library/src/search/frontend.js b/packages/block-library/src/search/frontend.js new file mode 100644 index 00000000000000..9c795e2f372197 --- /dev/null +++ b/packages/block-library/src/search/frontend.js @@ -0,0 +1,63 @@ +// eslint-disable-next-line @wordpress/no-global-event-listener +document.addEventListener( 'DOMContentLoaded', () => { + const transitionDuration = 300; + const hiddenClass = 'wp-block-search__searchfield-hidden'; + const wrapperClass = '.wp-block-search__inside-wrapper'; + const buttonClass = '.wp-block-search__button'; + + Array.from( + document.getElementsByClassName( + 'wp-block-search__button-behavior-expand' + ) + ).forEach( ( block ) => { + + const wrapper = block.querySelector( wrapperClass ); + const searchField = block.querySelector( '.wp-block-search__input' ); + const button = block.querySelector( buttonClass ); + + // Hide search on init. + block.classList.add( hiddenClass ); + setTimeout( + () => + ( searchField.style.transitionDuration = `${ transitionDuration }ms` ), + transitionDuration + ); + + const toggleSearchField = ( e ) => { + if ( e.target !== button && ! e.target.closest( buttonClass ) ) { + return false; + } + + e.preventDefault(); + + return block.classList.contains( hiddenClass ) + ? doShowSearchField() + : doHideSearchField(); + }; + + const doShowSearchField = () => { + block.classList.remove( hiddenClass ); + searchField.focus(); + + wrapper.removeEventListener( 'click', toggleSearchField ); + document.body.addEventListener( 'click', doSearch ); + }; + + const doHideSearchField = () => { + block.classList.add( hiddenClass ); + }; + + const doSearch = ( e ) => { + if ( e.target.closest( wrapperClass ) ) { + return false; + } + + doHideSearchField(); + + document.body.removeEventListener( 'click', doSearch ); + wrapper.addEventListener( 'click', toggleSearchField ); + }; + + wrapper.addEventListener( 'click', toggleSearchField ); + } ); +} ); diff --git a/packages/block-library/src/search/index.php b/packages/block-library/src/search/index.php index eba67deeecdce6..6f2a5c7518e863 100644 --- a/packages/block-library/src/search/index.php +++ b/packages/block-library/src/search/index.php @@ -31,7 +31,6 @@ function render_block_core_search( $attributes ) { $classnames = classnames_for_block_core_search( $attributes ); $show_label = ( ! empty( $attributes['showLabel'] ) ) ? true : false; $use_icon_button = ( ! empty( $attributes['buttonUseIcon'] ) ) ? true : false; - $show_input = ( ! empty( $attributes['buttonPosition'] ) && 'button-only' === $attributes['buttonPosition'] ) ? false : true; $show_button = ( ! empty( $attributes['buttonPosition'] ) && 'no-button' === $attributes['buttonPosition'] ) ? false : true; $label_markup = ''; $input_markup = ''; @@ -54,15 +53,13 @@ function render_block_core_search( $attributes ) { } } - if ( $show_input ) { - $input_markup = sprintf( - '', - $input_id, - esc_attr( get_search_query() ), - esc_attr( $attributes['placeholder'] ), - $inline_styles['shared'] - ); - } + $input_markup = sprintf( + '', + $input_id, + esc_attr( get_search_query() ), + esc_attr( $attributes['placeholder'] ), + $inline_styles['shared'] + ); if ( $show_button ) { $button_internal_markup = ''; @@ -95,6 +92,12 @@ function render_block_core_search( $attributes ) { ); $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => $classnames ) ); + if ( ! empty( $attributes['buttonPosition'] ) && ! empty( $attributes['buttonBehavior'] ) ) { + if ( 'button-only' === $attributes['buttonPosition'] && 'expand-searchfield' === $attributes['buttonBehavior'] ) { + wp_enqueue_script( 'wp-block-library-search', plugins_url( 'search/frontend.js', __FILE__ ) ); + } + } + return sprintf( '
', esc_url( home_url( '/' ) ), @@ -141,6 +144,16 @@ function classnames_for_block_core_search( $attributes ) { if ( 'button-only' === $attributes['buttonPosition'] ) { $classnames[] = 'wp-block-search__button-only'; + + if ( ! empty( $attributes['buttonBehavior'] ) ) { + if ( 'expand-searchfield' === $attributes['buttonBehavior'] ) { + $classnames[] = 'wp-block-search__button-behavior-expand'; + } + + if ( 'search-page-link' === $attributes['buttonBehavior'] ) { + $classnames[] = 'wp-block-search__button-behavior-link'; + } + } } } @@ -176,7 +189,7 @@ function styles_for_block_core_search( $attributes ) { $has_width = ! empty( $attributes['width'] ) && ! empty( $attributes['widthUnit'] ); $button_only = ! empty( $attributes['buttonPosition'] ) && 'button-only' === $attributes['buttonPosition']; - if ( $has_width && ! $button_only ) { + if ( $has_width ) { $wrapper_styles[] = sprintf( 'width: %d%s;', esc_attr( $attributes['width'] ), diff --git a/packages/block-library/src/search/style.scss b/packages/block-library/src/search/style.scss index 9959c4545ace41..930845f6ffe236 100644 --- a/packages/block-library/src/search/style.scss +++ b/packages/block-library/src/search/style.scss @@ -1,5 +1,4 @@ .wp-block-search { - .wp-block-search__button { background: #f7f7f7; border: 1px solid #ccc; @@ -7,6 +6,9 @@ color: #32373c; margin-left: 0.625em; word-break: normal; + flex-shrink: 0; + font-size: inherit; + line-height: inherit; &.has-icon { line-height: 0; @@ -60,8 +62,47 @@ } } + &.wp-block-search__button-behavior-expand { + .wp-block-search__inside-wrapper { + transition-property: width; + min-width: 0 !important; + } + + .wp-block-search__input { + transition-property: all; + transition-duration: 0; + flex-basis: 100%; + } + + // !important here to override inline styles on button only deselected view. + &.wp-block-search__searchfield-hidden { + overflow: hidden; + + .wp-block-search__inside-wrapper { + overflow: hidden; + } + + .wp-block-search__input { + width: 0 !important; + min-width: 0 !important; + padding-left: 0 !important; + padding-right: 0 !important; + border-left-width: 0 !important; + border-right-width: 0 !important; + flex-grow: 0; + margin: 0; + flex-basis: 0 !important; + } + } + } + &.aligncenter .wp-block-search__inside-wrapper { margin: auto; } } +.wp-block[data-align="right"] .wp-block-search__button-behavior-expand { + .wp-block-search__inside-wrapper { + float: right; + } +} diff --git a/packages/e2e-tests/fixtures/blocks/core__search.json b/packages/e2e-tests/fixtures/blocks/core__search.json index c3764ecb5f793f..b1929f5ecf42f0 100644 --- a/packages/e2e-tests/fixtures/blocks/core__search.json +++ b/packages/e2e-tests/fixtures/blocks/core__search.json @@ -6,8 +6,12 @@ "attributes": { "showLabel": true, "placeholder": "", + "width": 100, + "widthUnit": "%", "buttonPosition": "button-outside", - "buttonUseIcon": false + "buttonUseIcon": false, + "buttonBehavior": "expand-searchfield", + "isSearchFieldHidden": false }, "innerBlocks": [], "originalContent": "" diff --git a/packages/e2e-tests/fixtures/blocks/core__search__custom-text.json b/packages/e2e-tests/fixtures/blocks/core__search__custom-text.json index 7d7e970a6c56d7..6b89e2e5d35087 100644 --- a/packages/e2e-tests/fixtures/blocks/core__search__custom-text.json +++ b/packages/e2e-tests/fixtures/blocks/core__search__custom-text.json @@ -7,9 +7,13 @@ "label": "Custom label", "showLabel": true, "placeholder": "Custom placeholder", + "width": 100, + "widthUnit": "%", "buttonText": "Custom button text", "buttonPosition": "button-outside", - "buttonUseIcon": false + "buttonUseIcon": false, + "buttonBehavior": "expand-searchfield", + "isSearchFieldHidden": false }, "innerBlocks": [], "originalContent": ""