Skip to content

Commit 900b6e7

Browse files
Merge pull request #2004 from WordPress/feature/1511-incorporate-layout-constraints-from-ancestors
Accurate Sizes: Incorporate layout constraints in image sizes calculations
2 parents d1d72ed + a7c72d0 commit 900b6e7

File tree

11 files changed

+944
-47
lines changed

11 files changed

+944
-47
lines changed

.github/workflows/e2e-test.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: End-to-End Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- trunk
7+
- 'release/**'
8+
- 'add/setup-e2e-tests'
9+
paths:
10+
- '.github/workflows/e2e-test.yml'
11+
- 'plugins/auto-sizes/**'
12+
- '**/package.json'
13+
- 'package-lock.json'
14+
- 'composer.json'
15+
- 'composer.lock'
16+
pull_request:
17+
paths:
18+
- '.github/workflows/e2e-test.yml'
19+
- 'plugins/auto-sizes/**'
20+
- '**/package.json'
21+
- 'package-lock.json'
22+
- 'composer.json'
23+
- 'composer.lock'
24+
types:
25+
- opened
26+
- reopened
27+
- synchronize
28+
29+
jobs:
30+
e2e-test:
31+
name: E2E Tests
32+
runs-on: ubuntu-latest
33+
timeout-minutes: 20
34+
steps:
35+
- name: Checkout
36+
uses: actions/checkout@v4
37+
38+
- name: Setup Node.js (.nvmrc)
39+
uses: actions/setup-node@v4
40+
with:
41+
node-version-file: '.nvmrc'
42+
cache: npm
43+
44+
- name: Install npm dependencies
45+
run: npm ci
46+
47+
- name: Build assets
48+
run: npm run build
49+
50+
- name: Install Playwright dependencies
51+
run: npx playwright install chromium --with-deps
52+
53+
- name: Install WordPress
54+
run: npm run wp-env start
55+
56+
- name: Run tests
57+
env:
58+
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
59+
run: npm run test-e2e

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
############
44

55
plugins/*/tests/**/actual.html
6+
artifacts
7+
test-results
68

79
############
810
## IDEs

package-lock.json

Lines changed: 2 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
},
1313
"devDependencies": {
1414
"@octokit/rest": "^21.1.1",
15+
"@playwright/test": "^1.51.1",
16+
"@wordpress/e2e-test-utils-playwright": "^1.21.0",
1517
"@wordpress/env": "^10.23.0",
1618
"@wordpress/prettier-config": "^4.23.0",
1719
"@wordpress/scripts": "^30.16.0",
@@ -52,6 +54,9 @@
5254
"tsc": "tsc",
5355
"format-php": "composer format:all",
5456
"phpstan": "composer phpstan",
57+
"test-e2e": "wp-scripts test-playwright --config tools/e2e/playwright.config.ts",
58+
"test-e2e:debug": "wp-scripts test-playwright --config tools/e2e/playwright.config.ts --ui",
59+
"test-e2e:auto-sizes": "wp-scripts test-playwright --config tools/e2e/playwright.config.ts --project=auto-sizes",
5560
"lint-php": "composer lint:all",
5661
"test-php": "wp-env run tests-cli --env-cwd=/var/www/html/wp-content/plugins/performance composer test:plugins",
5762
"test-php-watch": "./bin/test-php-watch.sh",

plugins/auto-sizes/auto-sizes.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Plugin Name: Enhanced Responsive Images
44
* Plugin URI: https://github.com/WordPress/performance/tree/trunk/plugins/auto-sizes
55
* Description: Improves responsive images with better sizes calculations and auto-sizes for lazy-loaded images.
6-
* Requires at least: 6.6
6+
* Requires at least: 6.8
77
* Requires PHP: 7.2
88
* Version: 1.4.0
99
* Author: WordPress Performance Team

plugins/auto-sizes/hooks.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,4 @@ function auto_sizes_render_generator(): void {
4141
add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 3 );
4242
add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 );
4343
add_filter( 'get_block_type_uses_context', 'auto_sizes_filter_uses_context', 10, 2 );
44-
add_filter( 'render_block_context', 'auto_sizes_filter_render_block_context', 10, 2 );
44+
add_filter( 'render_block_context', 'auto_sizes_filter_render_block_context', 10, 3 );

plugins/auto-sizes/includes/improve-calculate-sizes.php

Lines changed: 101 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@
66
* @since 1.4.0
77
*/
88

9+
/*
10+
* Map alignment values to a weighting value so they can be compared.
11+
* Note that 'left' and 'right' alignments are only constrained by max alignment.
12+
*/
13+
const AUTO_SIZES_CONSTRAINTS = array(
14+
'full' => 0,
15+
'wide' => 1,
16+
'left' => 2,
17+
'right' => 2,
18+
'default' => 3,
19+
'center' => 3,
20+
);
21+
922
/**
1023
* Primes attachment into the cache with a single database query.
1124
*
@@ -82,10 +95,11 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b
8295
*/
8396
$filter = static function ( $sizes, $size ) use ( $block ) {
8497

85-
$id = isset( $block->attributes['id'] ) ? (int) $block->attributes['id'] : 0;
86-
$alignment = $block->attributes['align'] ?? '';
87-
$width = isset( $block->attributes['width'] ) ? (int) $block->attributes['width'] : 0;
88-
$max_alignment = $block->context['max_alignment'] ?? '';
98+
$id = isset( $block->attributes['id'] ) ? (int) $block->attributes['id'] : 0;
99+
$alignment = $block->attributes['align'] ?? '';
100+
$width = isset( $block->attributes['width'] ) ? (int) $block->attributes['width'] : 0;
101+
$max_alignment = $block->context['max_alignment'] ?? '';
102+
$container_relative_width = $block->context['container_relative_width'] ?? 1.0;
89103

90104
/*
91105
* Update width for cover block.
@@ -95,7 +109,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b
95109
$size = array( 420, 420 );
96110
}
97111

98-
$better_sizes = auto_sizes_calculate_better_sizes( $id, $size, $alignment, $width, $max_alignment );
112+
$better_sizes = auto_sizes_calculate_better_sizes( $id, $size, $alignment, $width, $max_alignment, $container_relative_width );
99113

100114
// If better sizes can't be calculated, use the default sizes.
101115
return false !== $better_sizes ? $better_sizes : $sizes;
@@ -132,14 +146,15 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b
132146
*
133147
* @since 1.4.0
134148
*
135-
* @param int $id The image attachment post ID.
136-
* @param string|array{int, int} $size Image size name or array of width and height.
137-
* @param string $align The image alignment.
138-
* @param int $resize_width Resize image width.
139-
* @param string $max_alignment The maximum usable layout alignment.
149+
* @param int $id The image attachment post ID.
150+
* @param string|array{int, int} $size Image size name or array of width and height.
151+
* @param string $align The image alignment.
152+
* @param int $resize_width Resize image width.
153+
* @param string $max_alignment The maximum usable layout alignment.
154+
* @param float $container_relative_width Container relative width.
140155
* @return string|false An improved sizes attribute or false if a better size cannot be calculated.
141156
*/
142-
function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $resize_width, string $max_alignment ) {
157+
function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $resize_width, string $max_alignment, float $container_relative_width ) {
143158
// Bail early if not a block theme.
144159
if ( ! wp_is_block_theme() ) {
145160
return false;
@@ -172,18 +187,8 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $
172187
// Normalize default alignment values.
173188
$align = '' !== $align ? $align : 'default';
174189

175-
/*
176-
* Map alignment values to a weighting value so they can be compared.
177-
* Note that 'left' and 'right' alignments are only constrained by max alignment.
178-
*/
179-
$constraints = array(
180-
'full' => 0,
181-
'wide' => 1,
182-
'left' => 2,
183-
'right' => 2,
184-
'default' => 3,
185-
'center' => 3,
186-
);
190+
// Use the defined constant for constraints.
191+
$constraints = AUTO_SIZES_CONSTRAINTS;
187192

188193
$alignment = $constraints[ $align ] > $constraints[ $max_alignment ] ? $align : $max_alignment;
189194

@@ -195,6 +200,18 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $
195200

196201
case 'wide':
197202
$layout_width = auto_sizes_get_layout_width( 'wide' );
203+
// TODO: Add support for em, rem, vh, and vw.
204+
if (
205+
str_ends_with( $layout_width, 'px' ) &&
206+
( $container_relative_width > 0.0 ||
207+
$container_relative_width < 1.0 )
208+
) {
209+
// First remove 'px' from width.
210+
$layout_width = str_replace( 'px', '', $layout_width );
211+
// Convert to float for better precision.
212+
$layout_width = (float) $layout_width * $container_relative_width;
213+
$layout_width = sprintf( '%dpx', (int) $layout_width );
214+
}
198215
break;
199216

200217
case 'left':
@@ -207,8 +224,18 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $
207224
/*
208225
* If the layout width is in pixels, we can compare against the image width
209226
* on the server. Otherwise, we need to rely on CSS functions.
227+
*
228+
* TODO: Add support for em, rem, vh, and vw.
210229
*/
211-
if ( str_ends_with( $layout_width, 'px' ) ) {
230+
if (
231+
str_ends_with( $layout_width, 'px' ) &&
232+
( $container_relative_width > 0.0 ||
233+
$container_relative_width < 1.0 )
234+
) {
235+
// First remove 'px' from width.
236+
$layout_width = str_replace( 'px', '', $layout_width );
237+
// Convert to float for better precision.
238+
$layout_width = (float) $layout_width * $container_relative_width;
212239
$layout_width = sprintf( '%dpx', min( (int) $layout_width, $image_width ) );
213240
} else {
214241
$layout_width = sprintf( 'min(%1$s, %2$spx)', $layout_width, $image_width );
@@ -255,15 +282,18 @@ function auto_sizes_get_layout_width( string $alignment ): string {
255282
* @return string[] The filtered context keys used by the block type.
256283
*/
257284
function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $block_type ): array {
258-
// The list of blocks that can consume outer layout context.
259-
$consumer_blocks = array(
260-
'core/cover',
261-
'core/image',
285+
// Define block-specific context usage.
286+
$block_specific_context = array(
287+
'core/cover' => array( 'max_alignment', 'container_relative_width' ),
288+
'core/image' => array( 'max_alignment', 'container_relative_width' ),
289+
'core/group' => array( 'max_alignment' ),
290+
'core/columns' => array( 'max_alignment', 'container_relative_width' ),
291+
'core/column' => array( 'max_alignment', 'column_count' ),
262292
);
263293

264-
if ( in_array( $block_type->name, $consumer_blocks, true ) ) {
265-
// Use array_values to reset the array keys after merging.
266-
return array_values( array_unique( array_merge( $uses_context, array( 'max_alignment' ) ) ) );
294+
if ( isset( $block_specific_context[ $block_type->name ] ) ) {
295+
// Use array_values to reset array keys after merging.
296+
return array_values( array_unique( array_merge( $uses_context, $block_specific_context[ $block_type->name ] ) ) );
267297
}
268298
return $uses_context;
269299
}
@@ -273,11 +303,12 @@ function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $blo
273303
*
274304
* @since 1.4.0
275305
*
276-
* @param array<string, mixed> $context Current block context.
277-
* @param array<string, mixed> $block The block being rendered.
306+
* @param array<string, mixed> $context Current block context.
307+
* @param array<string, mixed> $block The block being rendered.
308+
* @param WP_Block|null $parent_block If this is a nested block, a reference to the parent block.
278309
* @return array<string, mixed> Modified block context.
279310
*/
280-
function auto_sizes_filter_render_block_context( array $context, array $block ): array {
311+
function auto_sizes_filter_render_block_context( array $context, array $block, ?WP_Block $parent_block ): array {
281312
// When no max alignment is set, the maximum is assumed to be 'full'.
282313
$context['max_alignment'] = $context['max_alignment'] ?? 'full';
283314

@@ -288,15 +319,44 @@ function auto_sizes_filter_render_block_context( array $context, array $block ):
288319
);
289320

290321
if ( in_array( $block['blockName'], $provider_blocks, true ) ) {
291-
$alignment = $block['attrs']['align'] ?? '';
322+
// Normalize default alignment values.
323+
$alignment = isset( $block['attrs']['align'] ) && '' !== $block['attrs']['align'] ? $block['attrs']['align'] : 'default';
324+
// Use the defined constant for constraints.
325+
$constraints = AUTO_SIZES_CONSTRAINTS;
292326

293-
// If the container block doesn't have alignment, it's assumed to be 'default'.
294-
if ( '' === $alignment ) {
295-
$context['max_alignment'] = 'default';
296-
} elseif ( 'wide' === $alignment ) {
297-
$context['max_alignment'] = 'wide';
298-
}
327+
$context['max_alignment'] = $constraints[ $context['max_alignment'] ] > $constraints[ $alignment ] ? $context['max_alignment'] : $alignment;
328+
}
329+
330+
if ( 'core/columns' === $block['blockName'] ) {
331+
// This is a special context key just to pass to the child 'core/column' block.
332+
$context['column_count'] = count( $block['innerBlocks'] );
299333
}
300334

335+
if ( 'core/column' === $block['blockName'] ) {
336+
$found_image_block = wp_get_first_block( $block['innerBlocks'], 'core/image' );
337+
$found_cover_block = wp_get_first_block( $block['innerBlocks'], 'core/cover' );
338+
if ( count( $found_image_block ) > 0 || count( $found_cover_block ) > 0 ) {
339+
// Get column width, if explicitly set.
340+
if ( isset( $block['attrs']['width'] ) && '' !== $block['attrs']['width'] ) {
341+
$current_width = floatval( rtrim( $block['attrs']['width'], '%' ) ) / 100;
342+
} elseif ( isset( $parent_block->context['column_count'] ) && $parent_block->context['column_count'] ) {
343+
// Default to equally divided width if not explicitly set.
344+
$current_width = 1.0 / $parent_block->context['column_count'];
345+
} else {
346+
// Full width fallback.
347+
$current_width = 1.0;
348+
}
349+
350+
// Multiply with parent's width if available.
351+
if (
352+
isset( $parent_block->context['container_relative_width'] ) &&
353+
( $current_width > 0.0 || $current_width < 1.0 )
354+
) {
355+
$context['container_relative_width'] = $parent_block->context['container_relative_width'] * $current_width;
356+
} else {
357+
$context['container_relative_width'] = $current_width;
358+
}
359+
}
360+
}
301361
return $context;
302362
}

0 commit comments

Comments
 (0)