diff --git a/src/wp-admin/css/customize-nav-menus.css b/src/wp-admin/css/customize-nav-menus.css index c5dc29c614..c6f7944bb4 100644 --- a/src/wp-admin/css/customize-nav-menus.css +++ b/src/wp-admin/css/customize-nav-menus.css @@ -590,7 +590,8 @@ bottom: 0; left: -301px; visibility: hidden; - overflow: hidden; + overflow-x: hidden; + overflow-y: auto; width: 300px; margin: 0; z-index: 4; @@ -600,8 +601,13 @@ border-right: 1px solid #ddd; } -#available-menu-items.allow-scroll { - overflow-y: auto; +#available-menu-items.opening { + overflow-y: hidden; /* avoid scrollbar jitter with animating heights */ +} + +#available-menu-items #available-menu-items-search.open { + height: 100%; + border-bottom: none; } #available-menu-items .accordion-section-title { @@ -610,7 +616,8 @@ background: #fff; } -#available-menu-items .open .accordion-section-title { +#available-menu-items .open .accordion-section-title, +#available-menu-items #available-menu-items-search .accordion-section-title { background: #eee; } @@ -702,6 +709,15 @@ button.not-a-button { max-height: 290px; } +#available-menu-items #available-menu-items-search .accordion-section-content { + position: absolute; + left: 1px; + top: 60px; /* below title div / search input */ + bottom: 0px; /* 100% height that still triggers lazy load */ + max-height: none; + width: 270px; +} + #available-menu-items .menu-item-tpl { margin: 0; } @@ -802,6 +818,43 @@ button.not-a-button { margin: 0 20px; } +#available-menu-items-search .clear-results { + position: absolute; + top: 20px; + right: 20px; + width: 20px; + height: 20px; + cursor: pointer; + color: #a00; + text-decoration: none; +} + +#available-menu-items-search .clear-results, +#available-menu-items-search.loading .clear-results.is-visible { + display: none; +} + +#available-menu-items-search .clear-results.is-visible { + display: block; +} + +#available-menu-items-search .clear-results:before { + content: "\f335"; + font: normal 20px/1 dashicons; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +#available-menu-items-search .clear-results:hover, +#available-menu-items-search .clear-results:focus { + color: #f00; +} + +#available-menu-items-search .clear-results:focus { + -webkit-box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8); + box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8); +} + #available-menu-items-search .spinner { position: absolute; top: 20px; diff --git a/src/wp-admin/js/accordion.js b/src/wp-admin/js/accordion.js index 990a2421b6..af6281546f 100644 --- a/src/wp-admin/js/accordion.js +++ b/src/wp-admin/js/accordion.js @@ -54,7 +54,8 @@ function accordionSwitch ( el ) { var section = el.closest( '.accordion-section' ), sectionToggleControl = section.find( '[aria-expanded]' ).first(), - siblings = section.closest( '.accordion-container' ).find( '.open' ), + container = section.closest( '.accordion-container' ), + siblings = container.find( '.open' ), siblingsToggleControl = siblings.find( '[aria-expanded]' ).first(), content = section.find( '.accordion-section-content' ); @@ -63,6 +64,10 @@ return; } + // Add a class to the container to let us know something is happening inside. + // This helps in cases such as hiding a scrollbar while animations are executing. + container.addClass( 'opening' ); + if ( section.hasClass( 'open' ) ) { section.toggleClass( 'open' ); content.toggle( true ).slideToggle( 150 ); @@ -74,6 +79,11 @@ section.toggleClass( 'open' ); } + // We have to wait for the animations to finish + setTimeout(function(){ + container.removeClass( 'opening' ); + }, 150); + // If there's an element with an aria-expanded attribute, assume it's a toggle control and toggle the aria-expanded value. if ( sectionToggleControl ) { sectionToggleControl.attr( 'aria-expanded', String( sectionToggleControl.attr( 'aria-expanded' ) === 'false' ) ); diff --git a/src/wp-admin/js/customize-nav-menus.js b/src/wp-admin/js/customize-nav-menus.js index 76a1d60157..e0899f4d7d 100644 --- a/src/wp-admin/js/customize-nav-menus.js +++ b/src/wp-admin/js/customize-nav-menus.js @@ -96,7 +96,6 @@ events: { 'input #menu-items-search': 'debounceSearch', 'keyup #menu-items-search': 'debounceSearch', - 'click #menu-items-search': 'debounceSearch', 'focus .menu-item-tpl': 'focus', 'click .menu-item-tpl': '_submit', 'click #custom-menu-item-submit': '_submitLink', @@ -138,6 +137,20 @@ } } ); + // Clear the search results. + $( '.clear-results' ).on( 'click keydown', function( event ) { + console.log(event); + if ( event.type === 'keydown' && ( 13 !== event.which && 32 !== event.which ) ) { // "return" or "space" keys only + return; + } + + event.preventDefault(); + + $( '#menu-items-search' ).val( '' ).focus(); + event.target.value = ''; + self.search( event ); + } ); + this.$el.on( 'input', '#custom-menu-item-name.invalid, #custom-menu-item-url.invalid', function() { $( this ).removeClass( 'invalid' ); }); @@ -176,25 +189,31 @@ // Search input change handler. search: function( event ) { var $searchSection = $( '#available-menu-items-search' ), - $openSections = $( '#available-menu-items .accordion-section.open' ); + $otherSections = $( '#available-menu-items .accordion-section' ).not( $searchSection ); if ( ! event ) { return; } - // Manual accordion-opening behavior. - if ( this.searchTerm && ! $searchSection.hasClass( 'open' ) ) { - $openSections.find( '.accordion-section-content' ).slideUp( 'fast' ); - $searchSection.find( '.accordion-section-content' ).slideDown( 'fast' ); - $openSections.find( '[aria-expanded]' ).first().attr( 'aria-expanded', 'false' ); - $openSections.removeClass( 'open' ); - $searchSection.addClass( 'open' ); - } - if ( '' === event.target.value ) { - $searchSection.removeClass( 'open' ); - } + if ( this.searchTerm === event.target.value ) { return; } + + if ( '' !== event.target.value && ! $searchSection.hasClass( 'open' ) ) { + $otherSections.fadeOut( 100 ); + $searchSection.find( '.accordion-section-content' ).slideDown( 'fast' ); + $searchSection.addClass( 'open' ); + $searchSection.find( '.clear-results' ) + .prop( 'tabIndex', 0 ) + .addClass( 'is-visible' ); + } else if ( '' === event.target.value ) { + $searchSection.removeClass( 'open' ); + $otherSections.show(); + $searchSection.find( '.clear-results' ) + .prop( 'tabIndex', -1 ) + .removeClass( 'is-visible' ); + } + this.searchTerm = event.target.value; this.pages.search = 1; this.doSearch( 1 ); @@ -351,8 +370,6 @@ diff = totalHeight - accordionHeight; if ( 120 < diff && 290 > diff ) { sections.css( 'max-height', diff ); - } else if ( 120 >= diff ) { - this.$el.addClass( 'allow-scroll' ); } }, diff --git a/src/wp-includes/class-wp-customize-nav-menus.php b/src/wp-includes/class-wp-customize-nav-menus.php index 030336fb51..00696004e2 100644 --- a/src/wp-includes/class-wp-customize-nav-menus.php +++ b/src/wp-includes/class-wp-customize-nav-menus.php @@ -217,7 +217,7 @@ final class WP_Customize_Nav_Menus { $items = $this->search_available_items_query( array( 'pagenum' => $p, 's' => $s ) ); if ( empty( $items ) ) { - wp_send_json_error( array( 'message' => __( 'No menu items found.' ) ) ); + wp_send_json_error( array( 'message' => __( 'No results found.' ) ) ); } else { wp_send_json_success( array( 'items' => $items ) ); } @@ -718,6 +718,7 @@ final class WP_Customize_Nav_Menus { + diff --git a/tests/phpunit/tests/ajax/CustomizeMenus.php b/tests/phpunit/tests/ajax/CustomizeMenus.php index abf630093e..135929756c 100644 --- a/tests/phpunit/tests/ajax/CustomizeMenus.php +++ b/tests/phpunit/tests/ajax/CustomizeMenus.php @@ -520,7 +520,7 @@ class Tests_Ajax_CustomizeMenus extends WP_Ajax_UnitTestCase { array( 'success' => false, 'data' => array( - 'message' => 'No menu items found.', + 'message' => 'No results found.', ), ), ),