Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
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
19namespace 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;
38foreach ( $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;
52if ( $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>