Skip to content

Commit adc9daf

Browse files
committed
Block support: Add server-side processing for anchor.
Adds server-side registration for `anchor` block support and its required fields. Props westonruter, wildworks. Fixes #64449. git-svn-id: https://develop.svn.wordpress.org/trunk@61437 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 6d60f73 commit adc9daf

File tree

3 files changed

+264
-0
lines changed

3 files changed

+264
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
/**
3+
* Anchor block support flag.
4+
*
5+
* @package WordPress
6+
* @since 7.0.0
7+
*/
8+
9+
/**
10+
* Registers the anchor block attribute for block types that support it.
11+
*
12+
* @since 7.0.0
13+
* @access private
14+
*
15+
* @param WP_Block_Type $block_type Block Type.
16+
*/
17+
function wp_register_anchor_support( WP_Block_Type $block_type ) {
18+
if ( ! block_has_support( $block_type, array( 'anchor' ) ) ) {
19+
return;
20+
}
21+
22+
if ( ! isset( $block_type->attributes ) ) {
23+
$block_type->attributes = array();
24+
}
25+
26+
if ( ! array_key_exists( 'anchor', $block_type->attributes ) ) {
27+
$block_type->attributes['anchor'] = array(
28+
'type' => 'string',
29+
);
30+
}
31+
}
32+
33+
/**
34+
* Add the anchor id to the output.
35+
*
36+
* @since 7.0.0
37+
* @access private
38+
*
39+
* @param WP_Block_Type $block_type Block Type.
40+
* @param array<string, mixed> $block_attributes Block attributes.
41+
* @return array<string, string> Attributes with block anchor id.
42+
*/
43+
function wp_apply_anchor_support( WP_Block_Type $block_type, array $block_attributes ): array {
44+
if ( empty( $block_attributes ) ) {
45+
return array();
46+
}
47+
48+
if ( ! block_has_support( $block_type, array( 'anchor' ) ) ) {
49+
return array();
50+
}
51+
52+
if ( ! isset( $block_attributes['anchor'] ) || ! is_string( $block_attributes['anchor'] ) || '' === $block_attributes['anchor'] ) {
53+
return array();
54+
}
55+
56+
return array( 'id' => $block_attributes['anchor'] );
57+
}
58+
59+
// Register the block support.
60+
WP_Block_Supports::get_instance()->register(
61+
'anchor',
62+
array(
63+
'register_attribute' => 'wp_register_anchor_support',
64+
'apply' => 'wp_apply_anchor_support',
65+
)
66+
);

src/wp-settings.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@
404404
require ABSPATH . WPINC . '/block-supports/background.php';
405405
require ABSPATH . WPINC . '/block-supports/block-style-variations.php';
406406
require ABSPATH . WPINC . '/block-supports/aria-label.php';
407+
require ABSPATH . WPINC . '/block-supports/anchor.php';
407408
require ABSPATH . WPINC . '/block-supports/block-visibility.php';
408409
require ABSPATH . WPINC . '/style-engine.php';
409410
require ABSPATH . WPINC . '/style-engine/class-wp-style-engine.php';
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
<?php
2+
/**
3+
* @group block-supports
4+
*/
5+
class Tests_Block_Supports_Anchor extends WP_UnitTestCase {
6+
/**
7+
* @var string
8+
*/
9+
const TEST_BLOCK_NAME = 'test/anchor-block';
10+
11+
public function tear_down() {
12+
unregister_block_type( self::TEST_BLOCK_NAME );
13+
parent::tear_down();
14+
}
15+
16+
/**
17+
* Tests that anchor block support attribute registration works as expected.
18+
*
19+
* @covers ::wp_register_anchor_support
20+
*
21+
* @dataProvider data_wp_register_anchor_support
22+
*
23+
* @param bool $support Anchor block support configuration.
24+
* @param array<string, array<string, string>>|null $value Attributes array for the block.
25+
* @param array<string, array<string, string>> $expected Expected attributes for the block.
26+
*/
27+
public function test_wp_register_anchor_support( bool $support, ?array $value, array $expected ) {
28+
register_block_type(
29+
self::TEST_BLOCK_NAME,
30+
array(
31+
'api_version' => 3,
32+
'supports' => array( 'anchor' => $support ),
33+
'attributes' => $value,
34+
)
35+
);
36+
$registry = WP_Block_Type_Registry::get_instance();
37+
$block_type = $registry->get_registered( self::TEST_BLOCK_NAME );
38+
$this->assertInstanceOf( WP_Block_Type::class, $block_type );
39+
wp_register_anchor_support( $block_type );
40+
$actual = $block_type->attributes;
41+
$this->assertIsArray( $actual );
42+
$expected = array_merge( WP_Block_Type::GLOBAL_ATTRIBUTES, $expected );
43+
$this->assertSameSetsWithIndex( $expected, $actual );
44+
}
45+
46+
/**
47+
* Tests that anchor block support is applied as expected.
48+
*
49+
* @covers ::wp_apply_anchor_support
50+
*
51+
* @dataProvider data_wp_apply_anchor_support
52+
*
53+
* @param bool $support Anchor block support configuration.
54+
* @param mixed $value Anchor value for attribute object.
55+
* @param array<string, array<string, string>> $expected Expected anchor block support output.
56+
*/
57+
public function test_wp_apply_anchor_support( bool $support, $value, array $expected ) {
58+
register_block_type(
59+
self::TEST_BLOCK_NAME,
60+
array(
61+
'api_version' => 3,
62+
'supports' => array( 'anchor' => $support ),
63+
)
64+
);
65+
$registry = WP_Block_Type_Registry::get_instance();
66+
$block_type = $registry->get_registered( self::TEST_BLOCK_NAME );
67+
$this->assertInstanceOf( WP_Block_Type::class, $block_type );
68+
$block_attrs = array( 'anchor' => $value );
69+
$actual = wp_apply_anchor_support( $block_type, $block_attrs );
70+
$this->assertSame( $expected, $actual );
71+
}
72+
73+
/**
74+
* Data provider for test_wp_register_anchor_support().
75+
*
76+
* @return array<string, array<string, mixed>>
77+
*/
78+
public function data_wp_register_anchor_support(): array {
79+
return array(
80+
'anchor attribute is registered when block supports anchor' => array(
81+
'support' => true,
82+
'value' => null,
83+
'expected' => array(
84+
'anchor' => array(
85+
'type' => 'string',
86+
),
87+
),
88+
),
89+
'anchor attribute is not registered when block does not support anchor' => array(
90+
'support' => false,
91+
'value' => null,
92+
'expected' => array(),
93+
),
94+
'anchor attribute is added to existing attributes' => array(
95+
'support' => true,
96+
'value' => array(
97+
'foo' => array(
98+
'type' => 'string',
99+
),
100+
),
101+
'expected' => array(
102+
'foo' => array(
103+
'type' => 'string',
104+
),
105+
'anchor' => array(
106+
'type' => 'string',
107+
),
108+
),
109+
),
110+
'existing anchor attribute is not overwritten' => array(
111+
'support' => true,
112+
'value' => array(
113+
'anchor' => array(
114+
'type' => 'string',
115+
'default' => 'default-anchor',
116+
),
117+
),
118+
'expected' => array(
119+
'anchor' => array(
120+
'type' => 'string',
121+
'default' => 'default-anchor',
122+
),
123+
),
124+
),
125+
);
126+
}
127+
128+
/**
129+
* Data provider for test_wp_apply_anchor_support().
130+
*
131+
* @return array<string, array<string, mixed>>
132+
*/
133+
public function data_wp_apply_anchor_support(): array {
134+
return array(
135+
'anchor id attribute is applied' => array(
136+
'support' => true,
137+
'value' => 'my-anchor',
138+
'expected' => array( 'id' => 'my-anchor' ),
139+
),
140+
'anchor id attribute is not applied if block does not support it' => array(
141+
'support' => false,
142+
'value' => 'my-anchor',
143+
'expected' => array(),
144+
),
145+
'empty anchor value returns empty array' => array(
146+
'support' => true,
147+
'value' => '',
148+
'expected' => array(),
149+
),
150+
'null anchor value returns empty array' => array(
151+
'support' => true,
152+
'value' => null,
153+
'expected' => array(),
154+
),
155+
'whitespace-only anchor value is applied' => array(
156+
'support' => true,
157+
'value' => ' ',
158+
'expected' => array( 'id' => ' ' ),
159+
),
160+
'anchor with hyphen and numbers' => array(
161+
'support' => true,
162+
'value' => 'section-123',
163+
'expected' => array( 'id' => 'section-123' ),
164+
),
165+
'anchor with underscore' => array(
166+
'support' => true,
167+
'value' => 'my_anchor_id',
168+
'expected' => array( 'id' => 'my_anchor_id' ),
169+
),
170+
'anchor with colon (valid in HTML5)' => array(
171+
'support' => true,
172+
'value' => 'my:anchor',
173+
'expected' => array( 'id' => 'my:anchor' ),
174+
),
175+
'anchor with period (valid in HTML5)' => array(
176+
'support' => true,
177+
'value' => 'my.anchor',
178+
'expected' => array( 'id' => 'my.anchor' ),
179+
),
180+
'numeric anchor value' => array(
181+
'support' => true,
182+
'value' => '123',
183+
'expected' => array( 'id' => '123' ),
184+
),
185+
'zero string anchor value is applied' => array(
186+
'support' => true,
187+
'value' => '0',
188+
'expected' => array( 'id' => '0' ),
189+
),
190+
'false value is treated as empty' => array(
191+
'support' => true,
192+
'value' => false,
193+
'expected' => array(),
194+
),
195+
);
196+
}
197+
}

0 commit comments

Comments
 (0)