Skip to content

Commit d9201ba

Browse files
committed
1 parent 320cd0f commit d9201ba

File tree

2 files changed

+141
-46
lines changed

2 files changed

+141
-46
lines changed

src/wp-includes/block-supports/block-visibility.php

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
/**
10-
* Render nothing if the block is hidden.
10+
* Render nothing if the block is hidden, or add viewport visibility styles.
1111
*
1212
* @since 6.9.0
1313
* @since 7.0.0 Added support for breakpoint visibility.
@@ -36,17 +36,26 @@ function wp_render_block_visibility_support( $block_content, $block ) {
3636
* The following are taken from: https://github.com/WordPress/gutenberg/blob/trunk/packages/base-styles/_breakpoints.scss
3737
* The array is in a future, potential JSON format, and will be centralized
3838
* as the feature is developed.
39+
*
40+
* Breakpoints as array items are defined sequentially. The first item's size is the max value.
41+
* Each subsequent item's min is calc(previous size + 1px), and its size is the max.
42+
* The last item's min is previous size plus 1px, and it has no max.
3943
*/
4044
$breakpoints = array(
41-
'mobile' => array(
42-
'max' => '599px',
45+
array(
46+
'name' => 'Mobile',
47+
'slug' => 'mobile',
48+
'size' => '599px',
4349
),
44-
'tablet' => array(
45-
'min' => '600px',
46-
'max' => '959px',
50+
array(
51+
'name' => 'Tablet',
52+
'slug' => 'tablet',
53+
'size' => '959px',
4754
),
48-
'desktop' => array(
49-
'min' => '960px',
55+
array(
56+
'name' => 'Desktop',
57+
'slug' => 'desktop',
58+
'size' => '960px',
5059
),
5160
);
5261

@@ -56,17 +65,29 @@ function wp_render_block_visibility_support( $block_content, $block ) {
5665
* as well as classname building, and declaration of the display property, if required.
5766
*/
5867
$breakpoint_queries = array();
59-
foreach ( $breakpoints as $name => $values ) {
68+
$previous_size = null;
69+
foreach ( $breakpoints as $index => $breakpoint ) {
70+
$slug = $breakpoint['slug'];
71+
$size = $breakpoint['size'];
6072
$query_parts = array();
61-
if ( isset( $values['min'] ) ) {
62-
$query_parts[] = '(min-width: ' . $values['min'] . ')';
63-
}
64-
if ( isset( $values['max'] ) ) {
65-
$query_parts[] = '(max-width: ' . $values['max'] . ')';
73+
74+
// First item: max = size.
75+
if ( 0 === $index ) {
76+
$query_parts[] = '(max-width: ' . $size . ')';
77+
} elseif ( count( $breakpoints ) - 1 === $index ) {
78+
// Last item: min = calc(previous size + 1px), no max.
79+
$query_parts[] = '(min-width: calc(' . $previous_size . ' + 1px))';
80+
} else {
81+
// Middle items: min = calc(previous size + 1px), max = size.
82+
$query_parts[] = '(min-width: calc(' . $previous_size . ' + 1px))';
83+
$query_parts[] = '(max-width: ' . $size . ')';
6684
}
85+
6786
if ( ! empty( $query_parts ) ) {
68-
$breakpoint_queries[ $name ] = '@media ' . implode( ' and ', $query_parts );
87+
$breakpoint_queries[ $slug ] = '@media ' . implode( ' and ', $query_parts );
6988
}
89+
90+
$previous_size = $size;
7091
}
7192

7293
$hidden_on = array();
@@ -83,19 +104,30 @@ function wp_render_block_visibility_support( $block_content, $block ) {
83104
return $block_content;
84105
}
85106

86-
// If the block is hidden on all breakpoints, do not render the block.
107+
/*
108+
* If the block is hidden on all breakpoints,
109+
* do not render the block. If these values ever become user-defined,
110+
* we might need to output the CSS regardless of the breakpoint count.
111+
* For example, if there is one breakpoint defined and it's hidden.
112+
*/
87113
if ( count( $hidden_on ) === count( $breakpoint_queries ) ) {
88114
return '';
89115
}
90116

91117
// Maintain consistent order of breakpoints for class name generation.
92118
sort( $hidden_on );
93119

94-
$visibility_class = 'wp-block-hidden-' . implode( '-', $hidden_on );
95-
$css_rules = array();
120+
$css_rules = array();
121+
$class_names = array();
96122

97123
foreach ( $hidden_on as $breakpoint ) {
98-
$css_rules[] = array(
124+
/*
125+
* If these values ever become user-defined,
126+
* they should be sanitized and kebab-cased.
127+
*/
128+
$visibility_class = 'wp-block-hidden-' . $breakpoint;
129+
$class_names[] = $visibility_class;
130+
$css_rules[] = array(
99131
'selector' => '.' . $visibility_class,
100132
'declarations' => array(
101133
'display' => 'none !important',
@@ -116,7 +148,7 @@ function wp_render_block_visibility_support( $block_content, $block ) {
116148
if ( ! empty( $block_content ) ) {
117149
$processor = new WP_HTML_Tag_Processor( $block_content );
118150
if ( $processor->next_tag() ) {
119-
$processor->add_class( $visibility_class );
151+
$processor->add_class( implode( ' ', $class_names ) );
120152
$block_content = $processor->get_updated_html();
121153
}
122154
}

tests/phpunit/tests/block-supports/block-visibility.php

Lines changed: 89 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -125,20 +125,20 @@ public function test_block_visibility_support_no_visibility_attribute() {
125125
$block_content = '<div>Test content</div>';
126126
$result = wp_render_block_visibility_support( $block_content, $block );
127127

128-
$this->assertSame( $block_content, $result );
128+
$this->assertSame( $block_content, $result, 'Block content should remain unchanged when no visibility attribute is present.' );
129129
}
130130

131131
/*
132132
* @ticket 64414
133133
*/
134134
public function test_block_visibility_support_generated_css_with_mobile_breakpoint() {
135135
$this->register_visibility_block_with_support(
136-
'test/responsive-mobile',
136+
'test/viewport-mobile',
137137
array( 'visibility' => true )
138138
);
139139

140140
$block = array(
141-
'blockName' => 'test/responsive-mobile',
141+
'blockName' => 'test/viewport-mobile',
142142
'attrs' => array(
143143
'metadata' => array(
144144
'blockVisibility' => array(
@@ -152,23 +152,64 @@ public function test_block_visibility_support_generated_css_with_mobile_breakpoi
152152
$result = wp_render_block_visibility_support( $block_content, $block );
153153

154154
$this->assertStringContainsString( 'wp-block-hidden-mobile', $result, 'Block should have the visibility class for the mobile breakpoint.' );
155+
156+
$actual_stylesheet = gutenberg_style_engine_get_stylesheet_from_context( 'block-supports' );
157+
158+
$this->assertSame(
159+
'@media (max-width: 599px){.wp-block-hidden-mobile{display:none !important;}}',
160+
$actual_stylesheet,
161+
'CSS should contain mobile visibility rule'
162+
);
155163
}
156164

157165
/*
158166
* @ticket 64414
159167
*/
160-
public function test_block_visibility_support_generated_css_with_multiple_breakpoints() {
168+
public function test_block_visibility_support_generated_css_with_tablet_breakpoint() {
161169
$this->register_visibility_block_with_support(
162-
'test/responsive-multiple',
170+
'test/viewport-tablet',
163171
array( 'visibility' => true )
164172
);
165173

166174
$block = array(
167-
'blockName' => 'test/responsive-multiple',
175+
'blockName' => 'test/viewport-tablet',
176+
'attrs' => array(
177+
'metadata' => array(
178+
'blockVisibility' => array(
179+
'tablet' => false,
180+
),
181+
),
182+
),
183+
);
184+
185+
$block_content = '<div class="existing-class">Test content</div>';
186+
$result = wp_render_block_visibility_support( $block_content, $block );
187+
188+
$this->assertStringContainsString( 'class="existing-class wp-block-hidden-tablet"', $result, 'Block should have the existing class and the visibility class for the tablet breakpoint in the class attribute.' );
189+
190+
$actual_stylesheet = gutenberg_style_engine_get_stylesheet_from_context( 'block-supports' );
191+
192+
$this->assertSame(
193+
'@media (min-width: calc(599px + 1px)) and (max-width: 959px){.wp-block-hidden-tablet{display:none !important;}}',
194+
$actual_stylesheet,
195+
'CSS should contain tablet visibility rule'
196+
);
197+
}
198+
199+
/*
200+
* @ticket 64414
201+
*/
202+
public function test_block_visibility_support_generated_css_with_desktop_breakpoint() {
203+
$this->register_visibility_block_with_support(
204+
'test/viewport-desktop',
205+
array( 'visibility' => true )
206+
);
207+
208+
$block = array(
209+
'blockName' => 'test/viewport-desktop',
168210
'attrs' => array(
169211
'metadata' => array(
170212
'blockVisibility' => array(
171-
'mobile' => false,
172213
'desktop' => false,
173214
),
174215
),
@@ -178,47 +219,67 @@ public function test_block_visibility_support_generated_css_with_multiple_breakp
178219
$block_content = '<div>Test content</div>';
179220
$result = wp_render_block_visibility_support( $block_content, $block );
180221

181-
$this->assertStringContainsString( 'wp-block-hidden-desktop-mobile', $result, 'Block should have the visibility class for both breakpoints (sorted alphabetically).' );
222+
$this->assertStringContainsString( 'class="wp-block-hidden-desktop"', $result, 'Block should have the visibility class for the desktop breakpoint in the class attribute.' );
223+
224+
$actual_stylesheet = gutenberg_style_engine_get_stylesheet_from_context( 'block-supports' );
225+
226+
$this->assertSame(
227+
'@media (min-width: calc(959px + 1px)){.wp-block-hidden-desktop{display:none !important;}}',
228+
$actual_stylesheet,
229+
'CSS should contain desktop visibility rule'
230+
);
182231
}
183232

184233
/*
185234
* @ticket 64414
186235
*/
187-
public function test_block_visibility_support_generated_css_with_tablet_breakpoint() {
236+
public function test_block_visibility_support_generated_css_with_multiple_breakpoints() {
188237
$this->register_visibility_block_with_support(
189-
'test/responsive-tablet',
238+
'test/viewport-multiple',
190239
array( 'visibility' => true )
191240
);
192241

193242
$block = array(
194-
'blockName' => 'test/responsive-tablet',
243+
'blockName' => 'test/viewport-multiple',
195244
'attrs' => array(
196245
'metadata' => array(
197246
'blockVisibility' => array(
198-
'tablet' => false,
247+
'mobile' => false,
248+
'desktop' => false,
199249
),
200250
),
201251
),
202252
);
203253

204-
$block_content = '<div class="existing-class">Test content</div>';
254+
$block_content = '<div>Test content</div>';
205255
$result = wp_render_block_visibility_support( $block_content, $block );
206256

207-
$this->assertStringContainsString( 'existing-class', $result, 'Block should have the existing class.' );
208-
$this->assertStringContainsString( 'wp-block-hidden-tablet', $result, 'Block should have the visibility class for the tablet breakpoint.' );
257+
$this->assertStringContainsString(
258+
'class="wp-block-hidden-desktop wp-block-hidden-mobile"',
259+
$result,
260+
'Block should have both visibility classes in the class attribute'
261+
);
262+
263+
$actual_stylesheet = gutenberg_style_engine_get_stylesheet_from_context( 'block-supports' );
264+
265+
$this->assertSame(
266+
'@media (min-width: calc(959px + 1px)){.wp-block-hidden-desktop{display:none !important;}}@media (max-width: 599px){.wp-block-hidden-mobile{display:none !important;}}',
267+
$actual_stylesheet,
268+
'CSS should contain both visibility rules'
269+
);
209270
}
210271

211272
/*
212273
* @ticket 64414
213274
*/
214275
public function test_block_visibility_support_generated_css_with_all_breakpoints_visible() {
215276
$this->register_visibility_block_with_support(
216-
'test/responsive-all-visible',
277+
'test/viewport-all-visible',
217278
array( 'visibility' => true )
218279
);
219280

220281
$block = array(
221-
'blockName' => 'test/responsive-all-visible',
282+
'blockName' => 'test/viewport-all-visible',
222283
'attrs' => array(
223284
'metadata' => array(
224285
'blockVisibility' => array(
@@ -269,12 +330,12 @@ public function test_block_visibility_support_generated_css_with_all_breakpoints
269330
*/
270331
public function test_block_visibility_support_generated_css_with_empty_object() {
271332
$this->register_visibility_block_with_support(
272-
'test/responsive-empty',
333+
'test/viewport-empty',
273334
array( 'visibility' => true )
274335
);
275336

276337
$block = array(
277-
'blockName' => 'test/responsive-empty',
338+
'blockName' => 'test/viewport-empty',
278339
'attrs' => array(
279340
'metadata' => array(
280341
'blockVisibility' => array(),
@@ -293,12 +354,12 @@ public function test_block_visibility_support_generated_css_with_empty_object()
293354
*/
294355
public function test_block_visibility_support_generated_css_with_unknown_breakpoints_ignored() {
295356
$this->register_visibility_block_with_support(
296-
'test/responsive-unknown-breakpoints',
357+
'test/viewport-unknown-breakpoints',
297358
array( 'visibility' => true )
298359
);
299360

300361
$block = array(
301-
'blockName' => 'test/responsive-unknown-breakpoints',
362+
'blockName' => 'test/viewport-unknown-breakpoints',
302363
'attrs' => array(
303364
'metadata' => array(
304365
'blockVisibility' => array(
@@ -313,22 +374,24 @@ public function test_block_visibility_support_generated_css_with_unknown_breakpo
313374
$block_content = '<div>Test content</div>';
314375
$result = wp_render_block_visibility_support( $block_content, $block );
315376

316-
$this->assertStringContainsString( 'wp-block-hidden-mobile', $result, 'Block should have the visibility class for the mobile breakpoint.' );
317-
$this->assertStringNotContainsString( 'unknownBreak', $result, 'Unknown breakpoints should not appear in the class name.' );
318-
$this->assertStringNotContainsString( 'largeScreen', $result, 'Large screen breakpoints should not appear in the class name.' );
377+
$this->assertStringContainsString(
378+
'class="wp-block-hidden-mobile"',
379+
$result,
380+
'Block should have the visibility class for the mobile breakpoint in the class attribute'
381+
);
319382
}
320383

321384
/*
322385
* @ticket 64414
323386
*/
324387
public function test_block_visibility_support_generated_css_with_empty_content() {
325388
$this->register_visibility_block_with_support(
326-
'test/empty-content',
389+
'test/viewport-empty-content',
327390
array( 'visibility' => true )
328391
);
329392

330393
$block = array(
331-
'blockName' => 'test/empty-content',
394+
'blockName' => 'test/viewport-empty-content',
332395
'attrs' => array(
333396
'metadata' => array(
334397
'blockVisibility' => array(

0 commit comments

Comments
 (0)