diff --git a/core/modules/layout_builder/layout_builder.module b/core/modules/layout_builder/layout_builder.module
index f03e73afd0..b05382b94d 100644
--- a/core/modules/layout_builder/layout_builder.module
+++ b/core/modules/layout_builder/layout_builder.module
@@ -405,3 +405,15 @@ function layout_builder_preprocess_language_content_settings_table(&$variables)
}
}
}
+
+/**
+ * Implements hook_theme_suggestions_HOOK_alter().
+ */
+function layout_builder_theme_suggestions_field_alter(&$suggestions, array $variables) {
+ $element = $variables['element'];
+ if (isset($element['#third_party_settings']['layout_builder']['view_mode'])) {
+ // See system_theme_suggestions_field().
+ $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle'] . '__' . $element['#third_party_settings']['layout_builder']['view_mode'];
+ }
+ return $suggestions;
+}
diff --git a/core/modules/layout_builder/src/LayoutEntityHelperTrait.php b/core/modules/layout_builder/src/LayoutEntityHelperTrait.php
index e1c4216ce7..2c0d9d7547 100644
--- a/core/modules/layout_builder/src/LayoutEntityHelperTrait.php
+++ b/core/modules/layout_builder/src/LayoutEntityHelperTrait.php
@@ -109,6 +109,7 @@ protected function getSectionStorageForEntity(EntityInterface $entity) {
$view_mode = 'full';
if ($entity instanceof LayoutEntityDisplayInterface) {
$contexts['display'] = EntityContext::fromEntity($entity);
+ $contexts['view_mode'] = new Context(new ContextDefinition('string'), $entity->getMode());
}
else {
$contexts['entity'] = EntityContext::fromEntity($entity);
diff --git a/core/modules/layout_builder/src/Plugin/Block/FieldBlock.php b/core/modules/layout_builder/src/Plugin/Block/FieldBlock.php
index e8a21d12dc..493ed5dee5 100644
--- a/core/modules/layout_builder/src/Plugin/Block/FieldBlock.php
+++ b/core/modules/layout_builder/src/Plugin/Block/FieldBlock.php
@@ -156,6 +156,7 @@ protected function getEntity() {
*/
public function build() {
$display_settings = $this->getConfiguration()['formatter'];
+ $display_settings['third_party_settings']['layout_builder']['view_mode'] = $this->getContextValue('view_mode');
$entity = $this->getEntity();
try {
$build = $entity->get($this->fieldName)->view($display_settings);
diff --git a/core/modules/layout_builder/src/Plugin/Derivative/FieldBlockDeriver.php b/core/modules/layout_builder/src/Plugin/Derivative/FieldBlockDeriver.php
index 97ac811409..bc2b32cc17 100644
--- a/core/modules/layout_builder/src/Plugin/Derivative/FieldBlockDeriver.php
+++ b/core/modules/layout_builder/src/Plugin/Derivative/FieldBlockDeriver.php
@@ -9,6 +9,7 @@
use Drupal\Core\Field\FieldConfigInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\Field\FormatterPluginManager;
+use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\EntityContextDefinition;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -123,6 +124,7 @@ public function getDerivativeDefinitions($base_plugin_definition) {
$context_definition->addConstraint('Bundle', [$bundle]);
$derivative['context_definitions'] = [
'entity' => $context_definition,
+ 'view_mode' => (new ContextDefinition('string'))->setDefaultValue('default'),
];
$derivative_id = $entity_type_id . PluginBase::DERIVATIVE_SEPARATOR . $bundle . PluginBase::DERIVATIVE_SEPARATOR . $field_name;
diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php b/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php
index 8c6e8afb28..de8f1bfc45 100644
--- a/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php
+++ b/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php
@@ -2,6 +2,7 @@
namespace Drupal\layout_builder\Plugin\SectionStorage;
+use Drupal\Component\Plugin\Context\ContextInterface as ComponentContextInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
@@ -33,6 +34,7 @@
* weight = 20,
* context_definitions = {
* "display" = @ContextDefinition("entity:entity_view_display"),
+ * "view_mode" = @ContextDefinition("string", default_value = "default"),
* },
* )
*
@@ -432,4 +434,15 @@ public function isApplicable(RefinableCacheableDependencyInterface $cacheability
return $this->isLayoutBuilderEnabled();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function setContext($name, ComponentContextInterface $context) {
+ // Set the view mode context based on the display context.
+ if ($name === 'display') {
+ $this->setContextValue('view_mode', $context->getContextValue()->getMode());
+ }
+ parent::setContext($name, $context);
+ }
+
}
diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
index cde6f497c5..eb3e12ef9b 100644
--- a/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
+++ b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
@@ -39,7 +39,7 @@
* "entity" = @ContextDefinition("entity", constraints = {
* "EntityHasField" = \Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage::FIELD_NAME,
* }),
- * "view_mode" = @ContextDefinition("string"),
+ * "view_mode" = @ContextDefinition("string", default_value = "default"),
* }
* )
*
diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php b/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php
index 8c6d506c1d..0f8543336f 100644
--- a/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php
+++ b/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php
@@ -2,6 +2,8 @@
namespace Drupal\layout_builder\Plugin\SectionStorage;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\ContextAwarePluginBase;
use Drupal\layout_builder\Routing\LayoutBuilderRoutesTrait;
use Drupal\layout_builder\Section;
@@ -109,7 +111,16 @@ public function removeAllSections($set_blank = FALSE) {
* {@inheritdoc}
*/
public function getContextsDuringPreview() {
- return $this->getContexts();
+ $contexts = $this->getContexts();
+
+ // view_mode is a required context, but SectionStorage plugins are not
+ // required to return it (for example, the layout_library plugin provided
+ // in the Layout Library module. In these instances, explicitly create a
+ // view_mode context with the value "default".
+ if (!isset($contexts['view_mode']) || $contexts['view_mode']->validate()->count() || !$contexts['view_mode']->getContextValue()) {
+ $contexts['view_mode'] = new Context(new ContextDefinition('string'), 'default');
+ }
+ return $contexts;
}
/**
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_field_block_theme_suggestions_test/layout_builder_field_block_theme_suggestions_test.info.yml b/core/modules/layout_builder/tests/modules/layout_builder_field_block_theme_suggestions_test/layout_builder_field_block_theme_suggestions_test.info.yml
new file mode 100644
index 0000000000..589c256103
--- /dev/null
+++ b/core/modules/layout_builder/tests/modules/layout_builder_field_block_theme_suggestions_test/layout_builder_field_block_theme_suggestions_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Layout Builder Field Block Theme Suggestions Test'
+type: module
+description: 'Support module for testing.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_field_block_theme_suggestions_test/layout_builder_field_block_theme_suggestions_test.module b/core/modules/layout_builder/tests/modules/layout_builder_field_block_theme_suggestions_test/layout_builder_field_block_theme_suggestions_test.module
new file mode 100644
index 0000000000..729f3a3331
--- /dev/null
+++ b/core/modules/layout_builder/tests/modules/layout_builder_field_block_theme_suggestions_test/layout_builder_field_block_theme_suggestions_test.module
@@ -0,0 +1,19 @@
+ [
+ 'base hook' => 'field',
+ ],
+ ];
+}
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_field_block_theme_suggestions_test/templates/field--node--body--bundle-with-section-field--default.html.twig b/core/modules/layout_builder/tests/modules/layout_builder_field_block_theme_suggestions_test/templates/field--node--body--bundle-with-section-field--default.html.twig
new file mode 100644
index 0000000000..d48ff34267
--- /dev/null
+++ b/core/modules/layout_builder/tests/modules/layout_builder_field_block_theme_suggestions_test/templates/field--node--body--bundle-with-section-field--default.html.twig
@@ -0,0 +1 @@
+
I am a field template for a specific view mode!
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_fieldblock_test/layout_builder_fieldblock_test.services.yml b/core/modules/layout_builder/tests/modules/layout_builder_fieldblock_test/layout_builder_fieldblock_test.services.yml
new file mode 100644
index 0000000000..ed7b1f92cf
--- /dev/null
+++ b/core/modules/layout_builder/tests/modules/layout_builder_fieldblock_test/layout_builder_fieldblock_test.services.yml
@@ -0,0 +1,5 @@
+services:
+ layout_builder_fieldblock_test.fake_view_mode_context:
+ class: Drupal\layout_builder_fieldblock_test\ContextProvider\FakeViewModeContext
+ tags:
+ - { name: 'context_provider' }
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_fieldblock_test/src/ContextProvider/FakeViewModeContext.php b/core/modules/layout_builder/tests/modules/layout_builder_fieldblock_test/src/ContextProvider/FakeViewModeContext.php
new file mode 100644
index 0000000000..1dfbf8f296
--- /dev/null
+++ b/core/modules/layout_builder/tests/modules/layout_builder_fieldblock_test/src/ContextProvider/FakeViewModeContext.php
@@ -0,0 +1,30 @@
+ new Context(new ContextDefinition('string'), 'default')];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAvailableContexts() {
+ return $this->getRuntimeContexts([]);
+ }
+
+}
diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderFieldBlockThemeSuggestionsTest.php b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderFieldBlockThemeSuggestionsTest.php
new file mode 100644
index 0000000000..4d47586dff
--- /dev/null
+++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderFieldBlockThemeSuggestionsTest.php
@@ -0,0 +1,68 @@
+createContentType([
+ 'type' => 'bundle_with_section_field',
+ 'name' => 'Bundle with section field',
+ ]);
+ $this->createNode([
+ 'type' => 'bundle_with_section_field',
+ 'title' => 'A node title',
+ 'body' => [
+ [
+ 'value' => 'This is content that the template should not render',
+ ],
+ ],
+ ]);
+
+ $this->drupalLogin($this->drupalCreateUser([
+ 'configure any layout',
+ 'administer node display',
+ ]));
+
+ $this->drupalGet('admin/structure/types/manage/bundle_with_section_field/display/default');
+ $this->drupalPostForm(NULL, ['layout[enabled]' => TRUE], 'Save');
+ }
+
+ /**
+ * Tests that of view mode specific field templates are suggested.
+ */
+ public function testFieldBlockViewModeTemplates() {
+ $assert_session = $this->assertSession();
+
+ $this->drupalGet('node/1');
+ // Confirm that content is displayed by layout builder.
+ $assert_session->elementExists('css', '.block-layout-builder');
+ // Text that only appears in the view mode specific template.
+ $assert_session->pageTextContains('I am a field template for a specific view mode!');
+ // The content of the body field should not be visible because it is
+ // displayed via a template that does not render it.
+ $assert_session->pageTextNotContains('This is content that the template should not render');
+ }
+
+}
diff --git a/core/modules/layout_builder/tests/src/Kernel/DefaultsSectionStorageTest.php b/core/modules/layout_builder/tests/src/Kernel/DefaultsSectionStorageTest.php
index 637da29947..1d854972f5 100644
--- a/core/modules/layout_builder/tests/src/Kernel/DefaultsSectionStorageTest.php
+++ b/core/modules/layout_builder/tests/src/Kernel/DefaultsSectionStorageTest.php
@@ -2,7 +2,10 @@
namespace Drupal\Tests\layout_builder\Kernel;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\EntityContext;
+use Drupal\Core\Plugin\Context\EntityContextDefinition;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\KernelTests\KernelTestBase;
use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
@@ -51,7 +54,10 @@ protected function setUp() {
$this->installEntitySchema('user');
$this->installConfig(['layout_builder_defaults_test']);
- $this->plugin = DefaultsSectionStorage::create($this->container, [], 'defaults', new SectionStorageDefinition());
+ $definition = (new SectionStorageDefinition())
+ ->addContextDefinition('display', EntityContextDefinition::fromEntityTypeId('entity_view_display'))
+ ->addContextDefinition('view_mode', new ContextDefinition('string'));
+ $this->plugin = DefaultsSectionStorage::create($this->container, [], 'defaults', $definition);
}
/**
@@ -141,8 +147,9 @@ public function testGetContexts() {
$context = EntityContext::fromEntity($display);
$this->plugin->setContext('display', $context);
- $expected = ['display' => $context];
- $this->assertSame($expected, $this->plugin->getContexts());
+ $result = $this->plugin->getContexts();
+ $this->assertSame(['view_mode', 'display'], array_keys($result));
+ $this->assertSame($context, $result['display']);
}
/**
@@ -161,7 +168,7 @@ public function testGetContextsDuringPreview() {
$this->plugin->setContext('display', $context);
$result = $this->plugin->getContextsDuringPreview();
- $this->assertEquals(['display', 'layout_builder.entity'], array_keys($result));
+ $this->assertSame(['view_mode', 'display', 'layout_builder.entity'], array_keys($result));
$this->assertSame($context, $result['display']);
@@ -169,6 +176,10 @@ public function testGetContextsDuringPreview() {
$result_value = $result['layout_builder.entity']->getContextValue();
$this->assertInstanceOf(EntityTest::class, $result_value);
$this->assertSame('entity_test', $result_value->bundle());
+
+ $this->assertInstanceOf(Context::class, $result['view_mode']);
+ $result_value = $result['view_mode']->getContextValue();
+ $this->assertSame('default', $result_value);
}
/**
@@ -202,4 +213,24 @@ public function testGetTempstoreKey() {
$this->assertSame('entity_test.entity_test.default', $result);
}
+ /**
+ * Tests loading given a display.
+ */
+ public function testLoadFromDisplay() {
+ $display = LayoutBuilderEntityViewDisplay::create([
+ 'targetEntityType' => 'entity_test',
+ 'bundle' => 'entity_test',
+ 'mode' => 'default',
+ 'status' => TRUE,
+ ]);
+ $display->save();
+ $contexts = [
+ 'display' => EntityContext::fromEntity($display),
+ ];
+
+ $section_storage_manager = $this->container->get('plugin.manager.layout_builder.section_storage');
+ $section_storage = $section_storage_manager->load('defaults', $contexts);
+ $this->assertInstanceOf(DefaultsSectionStorage::class, $section_storage);
+ }
+
}
diff --git a/core/modules/layout_builder/tests/src/Kernel/FieldBlockTest.php b/core/modules/layout_builder/tests/src/Kernel/FieldBlockTest.php
index 4675046a89..d08a65bad9 100644
--- a/core/modules/layout_builder/tests/src/Kernel/FieldBlockTest.php
+++ b/core/modules/layout_builder/tests/src/Kernel/FieldBlockTest.php
@@ -10,6 +10,7 @@
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterPluginManager;
+use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Form\EnforcedResponseException;
use Drupal\Core\Plugin\Context\EntityContextDefinition;
use Drupal\Core\Session\AccountInterface;
@@ -210,6 +211,7 @@ protected function getTestBlock(ProphecyInterface $entity_prophecy, array $confi
'bundles' => ['entity_test'],
'context_definitions' => [
'entity' => EntityContextDefinition::fromEntityTypeId('entity_test')->setLabel('Test'),
+ 'view_mode' => new ContextDefinition('string'),
],
];
$formatter_manager = $this->prophesize(FormatterPluginManager::class);
@@ -225,6 +227,7 @@ protected function getTestBlock(ProphecyInterface $entity_prophecy, array $confi
$this->logger->reveal()
);
$block->setContextValue('entity', $entity_prophecy->reveal());
+ $block->setContextValue('view_mode', 'default');
return $block;
}
diff --git a/core/modules/layout_builder/tests/src/Kernel/LayoutEntityHelperTraitTest.php b/core/modules/layout_builder/tests/src/Kernel/LayoutEntityHelperTraitTest.php
index 54cc87d5b1..dc9c412ddb 100644
--- a/core/modules/layout_builder/tests/src/Kernel/LayoutEntityHelperTraitTest.php
+++ b/core/modules/layout_builder/tests/src/Kernel/LayoutEntityHelperTraitTest.php
@@ -64,7 +64,7 @@ public function providerTestGetSectionStorageForEntity() {
],
],
],
- ['display'],
+ ['display', 'view_mode'],
];
$data['fieldable entity'] = [
'entity_test',
diff --git a/core/modules/layout_builder/tests/src/Unit/DefaultsSectionStorageTest.php b/core/modules/layout_builder/tests/src/Unit/DefaultsSectionStorageTest.php
index f36b8fbc94..9d20508dee 100644
--- a/core/modules/layout_builder/tests/src/Unit/DefaultsSectionStorageTest.php
+++ b/core/modules/layout_builder/tests/src/Unit/DefaultsSectionStorageTest.php
@@ -2,13 +2,17 @@
namespace Drupal\Tests\layout_builder\Unit;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityType;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextInterface;
+use Drupal\Core\Plugin\Context\EntityContextDefinition;
+use Drupal\Core\TypedData\TypedDataManagerInterface;
use Drupal\layout_builder\Entity\LayoutEntityDisplayInterface;
use Drupal\layout_builder\Entity\SampleEntityGeneratorInterface;
use Drupal\layout_builder\Plugin\SectionStorage\DefaultsSectionStorage;
@@ -67,6 +71,17 @@ protected function setUp() {
* @covers ::setThirdPartySetting
*/
public function testThirdPartySettings() {
+ $this->entityTypeManager->getDefinition('entity_view_display')->willReturn(new EntityType(['id' => 'entity_view_display']));
+
+ $container = new ContainerBuilder();
+ $container->set('typed_data_manager', $this->prophesize(TypedDataManagerInterface::class)->reveal());
+ $container->set('entity_type.manager', $this->entityTypeManager->reveal());
+ \Drupal::setContainer($container);
+
+ $this->plugin->getPluginDefinition()
+ ->addContextDefinition('display', EntityContextDefinition::fromEntityTypeId('entity_view_display'))
+ ->addContextDefinition('view_mode', new ContextDefinition('string'));
+
// Set an initial value on the section list.
$section_list = $this->prophesize(LayoutEntityDisplayInterface::class);