Skip to content

Commit b99f359

Browse files
authored
Search Blocks: register filter-checkbox variations via get_block_type_variations filter (SEARCH-176) (#48589)
* Search Blocks: register filter-checkbox variations via filter register_block_variation() is a JavaScript-only API; no matching PHP function exists in WordPress core. The function_exists() guard always returned false, so register_variations() returned early and zero variations were exposed to the inserter — Category, Tag, Post Type, Author, and Custom Taxonomy presets never appeared. Switch to the get_block_type_variations filter (WP 6.5+) — the supported PHP-side path — and merge the variations onto the block type's getter when the editor or REST endpoint queries them. Add Search_Blocks_Test coverage for the injector's positive and negative paths so the next rename or signature change can't regress this silently. * Address review: dedupe variations by name (comment #3198558520) If a variation with one of our preset names is already on the block type — registered via block.json or a higher-priority filter — the prior `array_merge` would have appended a duplicate, producing two inserter cards for the same name and ambiguous `isActive` resolution. Index the incoming variations by name and skip additions whose name already exists, so an upstream registration wins. Add a focused unit test covering the collision case alongside the existing pass-through and add-shapes tests. * Address review: document WP 6.5+ behavior in docblock (claude review)
1 parent 517e366 commit b99f359

3 files changed

Lines changed: 161 additions & 13 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: patch
2+
Type: fixed
3+
4+
Search Blocks: Category, Tag, Post Type, Author, and Custom Taxonomy filter presets now appear in the block inserter.

projects/packages/search/src/search-blocks/class-search-blocks.php

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -212,24 +212,35 @@ public static function register_blocks() {
212212
}
213213
}
214214

215-
static::register_variations();
215+
add_filter( 'get_block_type_variations', array( static::class, 'inject_filter_checkbox_variations' ), 10, 2 );
216216
static::register_patterns();
217217
}
218218

219219
/**
220-
* Register named block variations for the filter-checkbox block.
220+
* Inject named block variations for the filter-checkbox block.
221221
*
222-
* PHP-side registration keeps the editor-only JS bundle out of the ESM
223-
* pipeline. Variation names and default `taxonomy` / `filterType`
224-
* attributes intentionally mirror the filter types exposed by the
225-
* instant-search overlay so the two surfaces describe the same filters.
222+
* Hooks `get_block_type_variations` (WP 6.5+) rather than calling
223+
* `register_block_variation()` because the latter is a JS-only API; no
224+
* matching PHP function exists in WordPress core. Filtering on the block
225+
* type's own variations getter is the supported PHP-side path and keeps
226+
* the editor-only JS bundle out of the ESM pipeline. On WP < 6.5 the
227+
* filter doesn't fire and no variations are registered — same end-state
228+
* as the prior code path, so this is not a version regression.
229+
*
230+
* Variation names and default `taxonomy` / `filterType` attributes
231+
* intentionally mirror the filter types exposed by the instant-search
232+
* overlay so the two surfaces describe the same filters.
233+
*
234+
* @param array $variations Variations registered on the block type.
235+
* @param \WP_Block_Type $block_type Block type the filter is being applied to.
236+
* @return array
226237
*/
227-
protected static function register_variations() {
228-
if ( ! function_exists( 'register_block_variation' ) ) {
229-
return;
238+
public static function inject_filter_checkbox_variations( $variations, $block_type ) {
239+
if ( ! isset( $block_type->name ) || 'jetpack-search/filter-checkbox' !== $block_type->name ) {
240+
return $variations;
230241
}
231242

232-
$variations = array(
243+
$additions = array(
233244
array(
234245
'name' => 'category',
235246
'title' => __( 'Filter by Category', 'jetpack-search-pkg' ),
@@ -292,10 +303,18 @@ protected static function register_variations() {
292303
),
293304
);
294305

295-
foreach ( $variations as $variation ) {
296-
// @phan-suppress-next-line PhanUndeclaredFunction -- Guarded by function_exists() above; stub missing from wordpress-stubs.
297-
register_block_variation( 'jetpack-search/filter-checkbox', $variation );
306+
// Merge by `name` so a variation already registered upstream (block.json
307+
// or a higher-priority filter) wins over our preset of the same name —
308+
// `array_merge` would otherwise append duplicates and the inserter
309+
// would render two cards for the same variation.
310+
$variations = (array) $variations;
311+
$existing_keys = array_flip( array_column( $variations, 'name' ) );
312+
foreach ( $additions as $variation ) {
313+
if ( ! isset( $existing_keys[ $variation['name'] ] ) ) {
314+
$variations[] = $variation;
315+
}
298316
}
317+
return $variations;
299318
}
300319

301320
/**

projects/packages/search/tests/php/Search_Blocks_Test.php

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,131 @@ public function test_build_initial_state_reserves_both_s_and_q_from_active_filte
435435
}
436436
}
437437

438+
/**
439+
* The filter-checkbox inserter cards come from
440+
* Search_Blocks::inject_filter_checkbox_variations(); if these names or
441+
* seeded attributes drift, the editor stops offering the expected filter
442+
* presets or inserts them with the wrong defaults.
443+
*/
444+
public function test_inject_filter_checkbox_variations_adds_expected_shapes() {
445+
$variations = Search_Blocks::inject_filter_checkbox_variations(
446+
array(
447+
array(
448+
'name' => 'existing',
449+
'title' => 'Existing variation',
450+
),
451+
),
452+
new \WP_Block_Type( 'jetpack-search/filter-checkbox' )
453+
);
454+
455+
$variations_by_name = array_column( $variations, null, 'name' );
456+
457+
$this->assertArrayHasKey( 'existing', $variations_by_name );
458+
$this->assertSame(
459+
array(
460+
'filterType' => 'taxonomy',
461+
'taxonomy' => 'category',
462+
'label' => 'Category',
463+
),
464+
$variations_by_name['category']['attributes']
465+
);
466+
$this->assertSame( array( 'filterType', 'taxonomy' ), $variations_by_name['category']['isActive'] );
467+
468+
$this->assertSame(
469+
array(
470+
'filterType' => 'taxonomy',
471+
'taxonomy' => 'post_tag',
472+
'label' => 'Tag',
473+
),
474+
$variations_by_name['post_tag']['attributes']
475+
);
476+
$this->assertSame( array( 'filterType', 'taxonomy' ), $variations_by_name['post_tag']['isActive'] );
477+
478+
$this->assertSame(
479+
array(
480+
'filterType' => 'post_type',
481+
'label' => 'Post Type',
482+
),
483+
$variations_by_name['post_type']['attributes']
484+
);
485+
$this->assertSame( array( 'filterType' ), $variations_by_name['post_type']['isActive'] );
486+
487+
$this->assertSame(
488+
array(
489+
'filterType' => 'author',
490+
'label' => 'Author',
491+
),
492+
$variations_by_name['author']['attributes']
493+
);
494+
$this->assertSame( array( 'filterType' ), $variations_by_name['author']['isActive'] );
495+
496+
$this->assertSame(
497+
array(
498+
'filterType' => 'taxonomy',
499+
'taxonomy' => '',
500+
'label' => '',
501+
),
502+
$variations_by_name['custom_taxonomy']['attributes']
503+
);
504+
$this->assertSame( array( 'filterType' ), $variations_by_name['custom_taxonomy']['isActive'] );
505+
}
506+
507+
/**
508+
* The injector must be scoped to jetpack-search/filter-checkbox so it
509+
* can't leak Search-specific presets onto unrelated blocks.
510+
*/
511+
public function test_inject_filter_checkbox_variations_ignores_other_block_types() {
512+
$variations = array(
513+
array(
514+
'name' => 'existing',
515+
'title' => 'Existing variation',
516+
),
517+
);
518+
519+
$this->assertSame(
520+
$variations,
521+
Search_Blocks::inject_filter_checkbox_variations( $variations, new \WP_Block_Type( 'core/paragraph' ) )
522+
);
523+
}
524+
525+
/**
526+
* If a variation with one of our preset names is already registered (via
527+
* block.json or a higher-priority filter), the existing entry must win —
528+
* otherwise `array_merge` would emit two inserter cards under the same
529+
* variation name and the editor would resolve `isActive` ambiguously.
530+
*/
531+
public function test_inject_filter_checkbox_variations_skips_name_collisions() {
532+
$existing_category = array(
533+
'name' => 'category',
534+
'title' => 'Site-customized Category filter',
535+
'attributes' => array(
536+
'filterType' => 'taxonomy',
537+
'taxonomy' => 'category',
538+
'label' => 'Topics',
539+
),
540+
);
541+
$variations = Search_Blocks::inject_filter_checkbox_variations(
542+
array( $existing_category ),
543+
new \WP_Block_Type( 'jetpack-search/filter-checkbox' )
544+
);
545+
546+
$category_entries = array();
547+
foreach ( $variations as $v ) {
548+
if ( 'category' === $v['name'] ) {
549+
$category_entries[] = $v;
550+
}
551+
}
552+
$this->assertCount( 1, $category_entries );
553+
554+
$by_name = array_column( $variations, null, 'name' );
555+
$this->assertSame( 'Site-customized Category filter', $by_name['category']['title'] );
556+
// Other presets are still added, only the colliding name is skipped.
557+
$this->assertArrayHasKey( 'post_tag', $by_name );
558+
$this->assertArrayHasKey( 'post_type', $by_name );
559+
$this->assertArrayHasKey( 'author', $by_name );
560+
$this->assertArrayHasKey( 'custom_taxonomy', $by_name );
561+
}
562+
438563
/**
439564
* Invoke a protected static on Search_Blocks from test code. Reflection
440565
* is the cheapest way to cover this logic without leaking visibility

0 commit comments

Comments
 (0)