Skip to content

Commit ae329a7

Browse files
committed
Issue #3441137 by catch, mherchel: BigPipe injecting Local Actions block creates large janky layout shift in Claro
1 parent 8667b88 commit ae329a7

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

lib/Drupal/Core/Render/Renderer.php

+1
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
310310
$pre_bubbling_elements = array_intersect_key($elements, [
311311
'#cache' => TRUE,
312312
'#lazy_builder' => TRUE,
313+
'#lazy_builder_preview' => TRUE,
313314
'#create_placeholder' => TRUE,
314315
]);
315316

modules/block/block.module

+20
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use Drupal\Core\Link;
1212
use Drupal\Core\Url;
1313
use Drupal\language\ConfigurableLanguageInterface;
1414
use Drupal\system\Entity\Menu;
15+
use Drupal\Core\Block\BlockPluginInterface;
1516
use Drupal\block\Entity\Block;
1617

1718
/**
@@ -316,3 +317,22 @@ function block_configurable_language_delete(ConfigurableLanguageInterface $langu
316317
}
317318
}
318319
}
320+
321+
/**
322+
* Implements hook_block_build_BASE_BLOCK_ID_alter().
323+
*/
324+
function block_block_build_local_actions_block_alter(array &$build, BlockPluginInterface $block) {
325+
$build['#lazy_builder_preview'] = [
326+
'#type' => 'container',
327+
'#attributes' => [
328+
'class' => ['invisible'],
329+
],
330+
'actions' => [
331+
'#theme' => 'menu_local_action',
332+
'#link' => [
333+
'title' => t('Add'),
334+
'url' => Url::fromUserInput('#'),
335+
],
336+
],
337+
];
338+
}

tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php

+61
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
use Drupal\Core\Cache\Cache;
1010
use Drupal\Core\Cache\CacheableMetadata;
1111
use Drupal\Core\Render\Markup;
12+
use Drupal\Core\Render\PlaceholderingRenderCache;
1213
use Drupal\Core\Render\RenderContext;
14+
use Drupal\Core\Render\Renderer;
1315
use Drupal\Core\Security\TrustedCallbackInterface;
1416

1517
/**
@@ -1047,6 +1049,50 @@ public function testRenderChildrenPlaceholdersDifferentArguments() {
10471049
$this->assertSame($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; both the original JavaScript setting and the ones added by each #lazy_builder callback exist.');
10481050
}
10491051

1052+
/**
1053+
* Tests the creation of an element with a lazy_builder_preview.
1054+
*
1055+
* @covers ::render
1056+
* @covers ::doRender
1057+
* @covers \Drupal\Core\Render\RenderCache::get
1058+
* @covers ::replacePlaceholders
1059+
*/
1060+
public function testRenderLazyBuilderPreview() {
1061+
$this->setUpRequest();
1062+
$this->setupMemoryCache();
1063+
$this->renderCache = new TestPlaceholderingRenderCache($this->requestStack, $this->cacheFactory, $this->cacheContextsManager, $this->placeholderGenerator);
1064+
$this->renderer = new Renderer($this->callableResolver, $this->themeManager, $this->elementInfo, $this->placeholderGenerator, $this->renderCache, $this->requestStack, $this->rendererConfig);
1065+
1066+
$this->cacheContextsManager->expects($this->any())
1067+
->method('convertTokensToKeys')
1068+
->willReturnArgument(0);
1069+
$this->callableResolver->expects($this->any())
1070+
->method('getCallableFromDefinition')
1071+
->willReturnArgument(0);
1072+
1073+
$test_element = $this->generatePlaceholderWithLazyBuilderPreview();
1074+
1075+
$element1 = $element2 = $test_element;
1076+
// Render the element twice so that it is in the render cache.
1077+
$result = $this->renderer->renderRoot($element1);
1078+
$result = $this->renderer->renderRoot($element2);
1079+
$placeholder_string = (string) $this->renderCache->placeholderElements[0]['#markup'];
1080+
$this->assertSame($this->renderCache->placeholderElements[0]['#attached']['placeholders'][$placeholder_string]['#preview'], ['#markup' => 'Lazy Builder Preview']);
1081+
}
1082+
1083+
/**
1084+
* Generates an element with a lazy builder and preview.
1085+
*/
1086+
public function generatePlaceholderWithLazyBuilderPreview(): array {
1087+
return [
1088+
'#cache' => [
1089+
'keys' => ['test_render'],
1090+
],
1091+
'#lazy_builder' => [__namespace__ . '\\PlaceholdersTest::callbackPerUser', ['foo']],
1092+
'#lazy_builder_preview' => ['#markup' => 'Lazy Builder Preview'],
1093+
];
1094+
}
1095+
10501096
/**
10511097
* Generates an element with placeholders at 3 levels.
10521098
*
@@ -1156,3 +1202,18 @@ public static function trustedCallbacks() {
11561202
}
11571203

11581204
}
1205+
1206+
class TestPlaceholderingRenderCache extends PlaceholderingRenderCache {
1207+
1208+
/**
1209+
* The placeholder elements created during rendering.
1210+
*/
1211+
public array $placeholderElements = [];
1212+
1213+
protected function createPlaceholderAndRemember(array $rendered_elements, array $pre_bubbling_elements) {
1214+
$placeholder_element = parent::createPlaceholderAndRemember($rendered_elements, $pre_bubbling_elements);
1215+
$this->placeholderElements[] = $placeholder_element;
1216+
return $placeholder_element;
1217+
}
1218+
1219+
}

0 commit comments

Comments
 (0)