Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
100.00% |
61 / 61 |
n/a |
0 / 0 |
CRAP | n/a |
0 / 0 |
||
| 1 | <?php |
| 2 | /** |
| 3 | * No Results block render. |
| 4 | * |
| 5 | * Empty-state region for the search results, modelled on |
| 6 | * `core/query-no-results`: the author fills it with any blocks they like and |
| 7 | * this renderer emits them. Core decides visibility server-side by re-running |
| 8 | * the query; the Search blocks fetch results client-side, so visibility is a |
| 9 | * `data-wp-bind--hidden` binding on the store instead. |
| 10 | * |
| 11 | * A container of `no-results-slot` variants, one per condition. Left empty — |
| 12 | * which is how the shipped templates carry it — it renders the same |
| 13 | * filter-aware default pair `results-list` always emitted, so a stock install |
| 14 | * reads exactly as before. |
| 15 | * |
| 16 | * @package automattic/jetpack-search |
| 17 | */ |
| 18 | |
| 19 | namespace Automattic\Jetpack\Search; |
| 20 | |
| 21 | // phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UndefinedVariable |
| 22 | |
| 23 | // @phan-suppress-next-line PhanUndeclaredGlobalVariable -- $content is provided by WP at block render. |
| 24 | $variants = trim( $content ); |
| 25 | |
| 26 | // Inner blocks that aren't variants — hand-edited markup, or content saved |
| 27 | // before the container took variants — are treated as one unscoped variant |
| 28 | // rather than emitted bare, which would put them behind the region-level |
| 29 | // binding and show them on an error too. `allowedBlocks` keeps the editor from |
| 30 | // producing this, so it is a robustness floor, not a migration: a legacy |
| 31 | // per-condition scope is not recovered, only a defined condition. The document |
| 32 | // Code Editor bypasses `allowedBlocks`, so stray blocks can arrive alongside |
| 33 | // variants and not just instead of them. |
| 34 | // @phan-suppress-next-line PhanUndeclaredGlobalVariable -- $block is provided by WP at block render. |
| 35 | $inner_blocks = ( $block instanceof \WP_Block ) ? $block->parsed_block['innerBlocks'] ?? array() : array(); |
| 36 | $has_variants = false; |
| 37 | $has_strays = false; |
| 38 | foreach ( $inner_blocks as $inner_block ) { |
| 39 | $inner_name = $inner_block['blockName'] ?? ''; |
| 40 | if ( 'jetpack-search/no-results-slot' === $inner_name ) { |
| 41 | $has_variants = true; |
| 42 | } elseif ( ! empty( $inner_name ) || '' !== trim( (string) ( $inner_block['innerHTML'] ?? '' ) ) ) { |
| 43 | // `parse_blocks()` yields whitespace between blocks as nameless |
| 44 | // entries; those aren't content and mustn't force the repair path. |
| 45 | $has_strays = true; |
| 46 | } |
| 47 | } |
| 48 | |
| 49 | // Without variants the container stands in for an unscoped one, so it seeds |
| 50 | // the same coverage and wraps whatever it holds in that condition's region. |
| 51 | $is_default = ! $has_variants; |
| 52 | if ( $is_default ) { |
| 53 | No_Results::seed_coverage( 'any' ); |
| 54 | } |
| 55 | ?> |
| 56 | <div |
| 57 | <?php echo wp_kses_data( get_block_wrapper_attributes( array( 'class' => 'jetpack-search-no-results' ) ) ); ?> |
| 58 | data-wp-interactive="jetpack-search" |
| 59 | <?php |
| 60 | // The container spans every condition its variants cover, so it binds to |
| 61 | // the region-level getter and each variant hides itself. That leaves it |
| 62 | // revealed with no visible child when the live condition is uncovered — an |
| 63 | // author's background and padding would paint an empty band — so |
| 64 | // `style.scss` collapses a container whose children are all hidden. |
| 65 | ?> |
| 66 | data-wp-bind--hidden="!state.showEmptyStateRegion" |
| 67 | hidden |
| 68 | > |
| 69 | <?php |
| 70 | if ( $is_default ) { |
| 71 | $default_class = 'jetpack-search-no-results__variant'; |
| 72 | if ( '' === $variants ) { |
| 73 | $default_class .= ' jetpack-search-no-results--default'; |
| 74 | } |
| 75 | ?> |
| 76 | <div |
| 77 | class="<?php echo esc_attr( $default_class ); ?>" |
| 78 | data-wp-bind--hidden="!<?php echo esc_attr( No_Results::visibility_getter( 'any' ) ); ?>" |
| 79 | <?php echo wp_kses_data( No_Results::live_region_attribute( 'any' ) ); ?> |
| 80 | hidden |
| 81 | > |
| 82 | <?php |
| 83 | if ( '' === $variants ) { |
| 84 | No_Results::render_default_copy( 'any' ); |
| 85 | } else { |
| 86 | // @phan-suppress-next-line PhanUndeclaredGlobalVariable -- $content is provided by WP at block render. |
| 87 | echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Inner block HTML is already escaped by each child block's renderer. |
| 88 | } |
| 89 | ?> |
| 90 | </div> |
| 91 | <?php |
| 92 | } elseif ( ! $has_strays ) { |
| 93 | // @phan-suppress-next-line PhanUndeclaredGlobalVariable -- $content is provided by WP at block render. |
| 94 | echo $content; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Inner block HTML is already escaped by each child block's renderer. |
| 95 | } else { |
| 96 | // Variants and stray blocks together. `$content` is one concatenated |
| 97 | // string, so the strays can't be lifted out of it — re-render each |
| 98 | // child in document order instead, gathering runs of strays into an |
| 99 | // unscoped variant. Re-rendering re-seeds coverage, which is |
| 100 | // idempotent. |
| 101 | $stray_run = ''; |
| 102 | /** |
| 103 | * Emit a run of stray blocks inside an unscoped variant wrapper. |
| 104 | * |
| 105 | * @param string $html Rendered stray markup. |
| 106 | * @return void |
| 107 | */ |
| 108 | $flush_strays = static function ( $html ) { |
| 109 | if ( '' === trim( $html ) ) { |
| 110 | return; |
| 111 | } |
| 112 | No_Results::seed_coverage( 'any' ); |
| 113 | printf( |
| 114 | '<div class="jetpack-search-no-results__variant" data-wp-bind--hidden="!%s" %s hidden>', |
| 115 | esc_attr( No_Results::visibility_getter( 'any' ) ), |
| 116 | wp_kses_data( No_Results::live_region_attribute( 'any' ) ) |
| 117 | ); |
| 118 | echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Inner block HTML is already escaped by each child block's renderer. |
| 119 | echo '</div>'; |
| 120 | }; |
| 121 | foreach ( $inner_blocks as $inner_block ) { |
| 122 | if ( 'jetpack-search/no-results-slot' === ( $inner_block['blockName'] ?? '' ) ) { |
| 123 | $flush_strays( $stray_run ); |
| 124 | $stray_run = ''; |
| 125 | echo render_block( $inner_block ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Inner block HTML is already escaped by each child block's renderer. |
| 126 | continue; |
| 127 | } |
| 128 | $stray_run .= render_block( $inner_block ); |
| 129 | } |
| 130 | $flush_strays( $stray_run ); |
| 131 | } |
| 132 | ?> |
| 133 | </div> |