diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index df2a2fd..b89bc26 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -362,3 +362,19 @@ field.formatter.settings.entity_reference_label:
type: boolean
label: 'Link label to the referenced entity'
+field.formatter.settings.entity_reference_field:
+ type: mapping
+ label: 'Rendered field from referenced entity settings'
+ mapping:
+ field_name:
+ label: 'Machine name of the field'
+ type: string
+ type:
+ label: 'Machine name of the formatter'
+ type: string
+ settings:
+ label: 'Formatter settings'
+ type: field.formatter.settings.[type]
+ label:
+ label: 'Target field label visibility setting'
+ type: string
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceFieldFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceFieldFormatter.php
new file mode 100644
index 0000000..defc70a
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/EntityReferenceFieldFormatter.php
@@ -0,0 +1,451 @@
+ label).
+ *
+ * @var array
+ */
+ protected $availableFieldOptions;
+
+ /**
+ * The list of available formatters, keyed by field type.
+ *
+ * @var array
+ */
+ protected $availableFormatterOptions;
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+ return new static(
+ $plugin_id,
+ $plugin_definition,
+ $configuration['field_definition'],
+ $configuration['settings'],
+ $configuration['label'],
+ $configuration['view_mode'],
+ $configuration['third_party_settings'],
+ $container->get('entity_field.manager'),
+ $container->get('plugin.manager.field.formatter'),
+ $container->get('entity_type.manager'),
+ $container->get('entity_type.bundle.info')
+ );
+ }
+
+ /**
+ * Constructs a EntityReferenceReferencedEntityFieldFormatter object.
+ *
+ * @param string $plugin_id
+ * The plugin_id for the formatter.
+ * @param mixed $plugin_definition
+ * The plugin implementation definition.
+ * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+ * The definition of the field to which the formatter is associated.
+ * @param array $settings
+ * The formatter settings.
+ * @param string $label
+ * The formatter label display setting.
+ * @param string $view_mode
+ * The view mode.
+ * @param array $third_party_settings
+ * Any third party settings.
+ * @param \Drupal\Core\Entity\EntityFieldManager $entity_field_manager
+ * The entity field manager.
+ * @param \Drupal\Core\Field\FormatterPluginManager $formatter_plugin_manager
+ * The formatter plugin manager.
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * The entity type manager.
+ * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
+ * The entity type bundle info.
+ */
+ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, EntityFieldManager $entity_field_manager, FormatterPluginManager $formatter_plugin_manager, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info) {
+ parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
+ $this->entityFieldManager = $entity_field_manager;
+ $this->formatterPluginManager = $formatter_plugin_manager;
+ $this->entityTypeManager = $entity_type_manager;
+ $this->entityTypeBundleInfo = $entity_type_bundle_info;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function defaultSettings() {
+ return [
+ 'field_name' => '',
+ 'type' => '',
+ 'settings' => [],
+ 'label' => 'hidden',
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function viewElements(FieldItemListInterface $items, $langcode) {
+ /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */
+ $entities = $this->getEntitiesToView($items, $langcode);
+
+ $field_name = $this->getFieldName()['machine_name'];
+ $formatter_type = $this->getFormatterType($field_name)['machine_name'];
+ $formatter_settings = [
+ 'type' => $formatter_type,
+ 'settings' => $this->getSetting('settings'),
+ 'label' => $this->getSetting('label'),
+ ];
+
+ $build = [];
+ foreach ($entities as $delta => $entity) {
+ if ($entity->hasField($field_name)) {
+ $build[$delta] = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId())->viewField($entity->get($field_name), $formatter_settings);
+ }
+ }
+ return $build;
+ }
+
+ /**
+ * Gets a list of supported fields.
+ *
+ * @return array
+ * An associative array of supported fields, where keys are machine names
+ * and values are human-readable field labels.
+ */
+ protected function getAvailableFieldOptions() {
+ if (isset($this->availableFieldOptions)) {
+ return $this->availableFieldOptions;
+ }
+
+ $entity_type_id = $this->fieldDefinition->getSetting('target_type');
+ $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
+
+ // If possible, initialize the options array with the target entity's label,
+ // because we always want the label to be a selectable option for this
+ // formatter.
+ if ($entity_type->hasKey('label')) {
+ $label_key = $entity_type->getKey('label');
+ $field_names = [
+ // Example: 'title' => 'Title'.
+ $label_key => $this->entityFieldManager->getBaseFieldDefinitions($entity_type_id)[$label_key]->getLabel(),
+ ];
+ }
+ else {
+ $field_names = [];
+ }
+
+ $target_bundles = empty($this->fieldDefinition->getSetting('handler_settings')['target_bundles']) ? array_keys($this->entityTypeBundleInfo->getBundleInfo($entity_type_id)) : $this->fieldDefinition->getSetting('handler_settings')['target_bundles'];
+ foreach ($target_bundles as $bundle) {
+ // Fetch all fields present on each target bundle.
+ $bundle_field_names = [];
+ /** @var \Drupal\Core\Field\FieldDefinitionInterface $field_definition */
+ foreach ($this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle) as $field_machine_name => $field_definition) {
+ // We use ::isDisplayConfigurable() to filter out the base fields that
+ // should not be exposed to the user. The only exception to this is the
+ // entity label, which was added earlier and will always be an option.
+ if ($field_definition->isDisplayConfigurable('view')) {
+ $bundle_field_names[$field_machine_name] = $field_definition->getLabel();
+ }
+ }
+ $field_names = array_merge($field_names, $bundle_field_names);
+ }
+
+ $this->availableFieldOptions = $field_names;
+ return $field_names;
+ }
+
+ /**
+ * Get all available formatters for a field storage definition.
+ *
+ * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition
+ * The field storage definition.
+ *
+ * @return string[]
+ * The field formatter labels, keyed by plugin ID.
+ */
+ protected function getAvailableFormatterOptions(FieldStorageDefinitionInterface $field_storage_definition) {
+ $field_type = $field_storage_definition->getType();
+ if (isset($this->availableFormatterOptions[$field_type])) {
+ return $this->availableFormatterOptions[$field_type];
+ }
+
+ $field_definition = BaseFieldDefinition::createFromFieldStorageDefinition($field_storage_definition);
+ $formatters = $this->formatterPluginManager->getOptions($field_type);
+ $options = [];
+ foreach ($formatters as $formatter_id => $formatter_options) {
+ $formatter_definition = $this->formatterPluginManager->getDefinition($formatter_id);
+ /** @var \Drupal\Core\Field\FormatterInterface $formatter_class */
+ $formatter_class = $formatter_definition['class'];
+ if ($formatter_class::isApplicable($field_definition)) {
+ $options[$formatter_id] = $formatter_definition['label'];
+ }
+ }
+
+ $this->availableFormatterOptions[$field_type] = $options;
+ return $options;
+ }
+
+ /**
+ * Ajax callback for field name change.
+ */
+ public static function onFieldNameChange(array $form, FormStateInterface $form_state) {
+ return $form['fields'][$form_state->get('plugin_settings_edit')]['plugin']['settings_edit_form']['settings'];
+ }
+
+ /**
+ * Ajax callback for formatter type change.
+ */
+ public static function onFormatterTypeChange(array $form, FormStateInterface $form_state) {
+ return $form['fields'][$form_state->get('plugin_settings_edit')]['plugin']['settings_edit_form']['settings']['settings'];
+ }
+
+ /**
+ * Rebuilds the form on select submit.
+ */
+ public static function rebuildOnSubmit(array &$form, FormStateInterface $form_state) {
+ $form_state->setRebuild(TRUE);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsForm(array $form, FormStateInterface $form_state) {
+ $form = parent::settingsForm($form, $form_state);
+ $target_entity_type_id = $this->fieldDefinition->getSetting('target_type');
+ $field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($target_entity_type_id);
+ // Field on the target entity this formatter is currently displaying.
+ $selected_field_name = $this->getFieldName($form_state)['machine_name'];
+
+ $form['#prefix'] = '
';
+ $form['#suffix'] = '
';
+
+ $form['field_name'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Field name'),
+ '#default_value' => $selected_field_name,
+ '#options' => $this->getAvailableFieldOptions(),
+ '#ajax' => [
+ 'callback' => [static::class, 'onFieldNameChange'],
+ 'wrapper' => 'field-formatter-ajax',
+ 'method' => 'replace',
+ ],
+ '#submit' => [[static::class, 'rebuildOnSubmit']],
+ '#executes_submit_callback' => TRUE,
+ ];
+
+ $form['label'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Label'),
+ '#options' => EntityViewDisplayEditForm::getFieldLabelOptions(),
+ '#default_value' => $this->getSetting('label', $form_state),
+ ];
+
+ if ($selected_field_name) {
+ $field_storage = $field_storage_definitions[$selected_field_name];
+ $formatter_options = $this->getAvailableFormatterOptions($field_storage);
+ $formatter_type = $this->getFormatterType($selected_field_name, $form_state);
+ $settings = $this->getSetting('settings', $form_state) ?: [];
+
+ $form['type'] = [
+ '#type' => 'select',
+ '#title' => $this->t('Formatter'),
+ '#options' => $formatter_options,
+ '#default_value' => $formatter_type,
+ '#ajax' => [
+ 'callback' => [static::class, 'onFormatterTypeChange'],
+ 'wrapper' => 'field-formatter-settings-ajax',
+ 'method' => 'replace',
+ ],
+ '#submit' => [[static::class, 'rebuildOnSubmit']],
+ '#executes_submit_callback' => TRUE,
+ ];
+
+ $options = [
+ 'field_definition' => BaseFieldDefinition::createFromFieldStorageDefinition($field_storage),
+ 'configuration' => [
+ 'type' => $formatter_type,
+ 'settings' => $settings,
+ 'label' => '',
+ 'weight' => 0,
+ ],
+ 'view_mode' => EntityDisplayBase::CUSTOM_MODE,
+ ];
+
+ // Get the formatter settings form.
+ $settings_form = ['#value' => []];
+ if ($formatter = $this->formatterPluginManager->getInstance($options)) {
+ $settings_form = $formatter->settingsForm([], $form_state);
+ }
+ else {
+ drupal_set_message('Could not instantiate the formatter plugin, please choose a different formatter.', 'error');
+ }
+ $form['settings'] = $settings_form;
+ $form['settings']['#prefix'] = '';
+ $form['settings']['#suffix'] = '
';
+ }
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function settingsSummary() {
+ $summary = parent::settingsSummary();
+
+ $field_name = $this->getFieldName();
+ $formatter_type = $this->getFormatterType($field_name['machine_name']);
+ $summary[] = $this->t('Displaying the field "%field_name", using the formatter "%type".', [
+ '%field_name' => $field_name['label'],
+ '%type' => $formatter_type['label'],
+ ]);
+
+ return $summary;
+ }
+
+ /**
+ * Wrapper around ::getSetting() to carry over values from the form state.
+ *
+ * @param string $key
+ * The setting name.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * (optional) The form state object. If absent, this method is equivalent to
+ * parent::getSetting().
+ *
+ * @return mixed|null
+ * The value of the setting, or NULL if absent.
+ */
+ public function getSetting($key, FormStateInterface $form_state = NULL) {
+ if (!$form_state) {
+ return parent::getSetting($key);
+ }
+
+ $field_name = $this->fieldDefinition->getName();
+ $form_state_key = [
+ 'fields',
+ $field_name,
+ 'settings_edit_form',
+ 'settings',
+ $key,
+ ];
+ if ($form_state->hasValue($form_state_key)) {
+ return $form_state->getValue($form_state_key);
+ }
+
+ return parent::getSetting($key);
+ }
+
+ /**
+ * Gets the field to be used in this formatter.
+ *
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * (optional) The form state object.
+ *
+ * @return array
+ * An associative array containing:
+ * - machine_name: The field machine name.
+ * - label: The field's human-readable label.
+ * If no field was configured yet, will try to return the first field
+ * available on the target entity.
+ *
+ * @see \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceReferencedEntityFieldFormatter::getAvailableFieldOptions()
+ */
+ protected function getFieldName(FormStateInterface $form_state = NULL) {
+ $field_name_options = $this->getAvailableFieldOptions();
+ if (!$field_name = $this->getSetting('field_name', $form_state)) {
+ $field_name = array_keys($field_name_options)[0];
+ }
+ $field['machine_name'] = $field_name;
+ $field['label'] = $field_name_options[$field_name];
+ return $field;
+ }
+
+ /**
+ * Gets the target formatter to be used in this formatter.
+ *
+ * @param string $field_name
+ * The machine name of the target field.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * (optional) The form state object.
+ *
+ * @return array
+ * An associative array containing:
+ * - machine_name: The formatter machine name.
+ * - label: The formatter's human-readable label
+ * If nothing was configured yet, will try to return the first formatter
+ * available on the target entity.
+ *
+ * @see \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceReferencedEntityFieldFormatter::getAvailableFormatterOptions()
+ */
+ protected function getFormatterType($field_name, FormStateInterface $form_state = NULL) {
+ $formatter = [
+ 'machine_name' => NULL,
+ 'label' => NULL,
+ ];
+ if (!$formatter_type = $this->getSetting('type', $form_state)) {
+ $target_entity_type_id = $this->fieldDefinition->getSetting('target_type');
+ $field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($target_entity_type_id);
+ $formatter_options = $this->getAvailableFormatterOptions($field_storage_definitions[$field_name]);
+ $formatter_type = array_keys($formatter_options)[0];
+ }
+ $formatter['machine_name'] = $formatter_type;
+ $formatter['label'] = $this->formatterPluginManager->getDefinition($formatter_type)['label'];
+ return $formatter;
+ }
+
+}
diff --git a/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php b/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php
index fa68cb6..d3d3bc0 100644
--- a/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php
+++ b/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php
@@ -373,6 +373,7 @@ public function testAvailableFormatters() {
$this->assertFieldSelectOptions('fields[field_' . $taxonomy_term_field_name . '][type]', [
'entity_reference_label',
'entity_reference_entity_id',
+ 'entity_reference_field',
'entity_reference_rss_category',
'entity_reference_entity_view',
]);
@@ -384,6 +385,7 @@ public function testAvailableFormatters() {
'author',
'entity_reference_entity_id',
'entity_reference_entity_view',
+ 'entity_reference_field',
'entity_reference_label',
]);
@@ -393,6 +395,7 @@ public function testAvailableFormatters() {
'entity_reference_label',
'entity_reference_entity_id',
'entity_reference_entity_view',
+ 'entity_reference_field',
]);
// Test if Date Format Reference Field has the correct formatters.
@@ -401,6 +404,7 @@ public function testAvailableFormatters() {
$this->assertFieldSelectOptions('fields[field_' . $date_format_field_name . '][type]', [
'entity_reference_label',
'entity_reference_entity_id',
+ 'entity_reference_field',
]);
}
diff --git a/core/modules/field/src/Tests/EntityReference/EntityReferenceTestTrait.php b/core/modules/field/src/Tests/EntityReference/EntityReferenceTestTrait.php
index 568cd4d..80d38bc 100644
--- a/core/modules/field/src/Tests/EntityReference/EntityReferenceTestTrait.php
+++ b/core/modules/field/src/Tests/EntityReference/EntityReferenceTestTrait.php
@@ -11,15 +11,15 @@
trait EntityReferenceTestTrait {
/**
- * Creates a field of an entity reference field storage on the specified bundle.
+ * Creates a field of an entity reference field storage on a given bundle.
*
* @param string $entity_type
* The type of entity the field will be attached to.
* @param string $bundle
* The bundle name of the entity the field will be attached to.
* @param string $field_name
- * The name of the field; if it already exists, a new instance of the existing
- * field will be created.
+ * The name of the field; if it already exists, a new instance of the
+ * existing field will be created.
* @param string $field_label
* The label of the field.
* @param string $target_entity_type
diff --git a/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php b/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php
index 1473375..8a0a91d 100644
--- a/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php
+++ b/core/modules/field/tests/src/Kernel/EntityReference/EntityReferenceFormatterTest.php
@@ -4,8 +4,10 @@
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter;
+use Drupal\entity_test\Entity\EntityTest;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
@@ -60,6 +62,9 @@ class EntityReferenceFormatterTest extends EntityKernelTestBase {
*/
protected $unsavedReferencedEntity;
+ /**
+ * {@inheritdoc}
+ */
protected function setUp() {
parent::setUp();
@@ -402,6 +407,125 @@ public function testLabelFormatter() {
}
/**
+ * Tests entity_reference_field formatter output.
+ *
+ * @param string|null $label_option
+ * The value for the "label" formatter option.
+ * @param string $expected_output
+ * The expected output.
+ *
+ * @dataProvider providerTestRenderFieldFormatter
+ */
+ public function testRenderFieldFormatter($label_option, $expected_output) {
+ FieldStorageConfig::create([
+ 'field_name' => 'test_er_field',
+ 'entity_type' => 'entity_test',
+ 'type' => 'entity_reference',
+ 'settings' => [
+ 'target_type' => 'entity_test',
+ ],
+ ])->save();
+
+ FieldConfig::create([
+ 'field_name' => 'test_er_field',
+ 'entity_type' => 'entity_test',
+ 'bundle' => 'entity_test',
+ ])->save();
+
+ $parent_entity_view_display = EntityViewDisplay::load('entity_test.entity_test.default');
+ $parent_entity_view_display->setComponent('test_er_field', [
+ 'type' => 'entity_reference_field',
+ 'settings' => [
+ 'field_name' => 'name',
+ 'type' => 'string',
+ 'settings' => [],
+ 'label' => $label_option,
+ ],
+ ])->save();
+
+ $child_entity = EntityTest::create([
+ 'name' => ['child name'],
+ ]);
+ $child_entity->save();
+
+ $entity = EntityTest::create([
+ 'test_er_field' => [[
+ 'target_id' => $child_entity->id(),
+ ],
+ ],
+ ]);
+ $entity->save();
+
+ $build = $parent_entity_view_display->build($entity);
+
+ $this->container->get('renderer')->renderRoot($build);
+
+ $this->assertEquals($expected_output, $build['test_er_field']['#markup']);
+ }
+
+ /**
+ * Data provider for ::testRenderFieldFormatter().
+ */
+ public function providerTestRenderFieldFormatter() {
+ $output_with_label = <<
+ test_er_field
+
+
+
+EXPECTED;
+ $output_with_label_inline = <<
+ test_er_field
+
+
+
+EXPECTED;
+ $output_label_hidden = <<
+ test_er_field
+
+
+
+EXPECTED;
+ $output_label_visually_hidden = <<
+ test_er_field
+
+
+
+EXPECTED;
+
+ return [
+ ['above', $output_with_label],
+ ['inline', $output_with_label_inline],
+ ['hidden', $output_label_hidden],
+ ['visually_hidden', $output_label_visually_hidden],
+ ];
+ }
+
+ /**
* Sets field values and returns a render array as built by
* \Drupal\Core\Field\FieldItemListInterface::view().
*
diff --git a/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php b/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php
index c27f3d4..6b970d2 100644
--- a/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php
+++ b/core/modules/field_ui/src/Form/EntityViewDisplayEditForm.php
@@ -44,7 +44,7 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, arr
'#type' => 'select',
'#title' => $this->t('Label display for @title', ['@title' => $field_definition->getLabel()]),
'#title_display' => 'invisible',
- '#options' => $this->getFieldLabelOptions(),
+ '#options' => static::getFieldLabelOptions(),
'#default_value' => $display_options ? $display_options['label'] : 'above',
],
];
@@ -149,12 +149,12 @@ protected function getOverviewUrl($mode) {
* @return array
* An array of visibility options.
*/
- protected function getFieldLabelOptions() {
+ public static function getFieldLabelOptions() {
return [
- 'above' => $this->t('Above'),
- 'inline' => $this->t('Inline'),
- 'hidden' => '- ' . $this->t('Hidden') . ' -',
- 'visually_hidden' => '- ' . $this->t('Visually Hidden') . ' -',
+ 'above' => t('Above'),
+ 'inline' => t('Inline'),
+ 'hidden' => '- ' . t('Hidden') . ' -',
+ 'visually_hidden' => '- ' . t('Visually Hidden') . ' -',
];
}
diff --git a/core/modules/media/media.module b/core/modules/media/media.module
index eeb2ac1..706b5cb 100644
--- a/core/modules/media/media.module
+++ b/core/modules/media/media.module
@@ -6,12 +6,12 @@
*/
use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Session\AccountInterface;
-use Drupal\field\FieldConfigInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
+use Drupal\field\FieldConfigInterface;
/**
* Implements hook_help().
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceFieldFormatterTest.php b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceFieldFormatterTest.php
new file mode 100644
index 0000000..16058db
--- /dev/null
+++ b/core/tests/Drupal/FunctionalJavascriptTests/EntityReference/EntityReferenceFieldFormatterTest.php
@@ -0,0 +1,143 @@
+drupalCreateUser([
+ 'bypass node access',
+ 'administer node display',
+ ]);
+ $this->drupalLogin($user);
+
+ $this->createContentType(['type' => 'page']);
+ $field_name = 'field_related_content';
+ $this->createEntityReferenceField('node', 'page', $field_name, 'Related Content', 'node', 'default', ['target_bundles' => ['page']]);
+ EntityViewDisplay::load('node.page.default')
+ ->setComponent($field_name, [
+ 'type' => 'entity_reference_label',
+ ])->save();
+
+ $this->testChildNode = $this->createNode([
+ 'title' => 'Child test node',
+ 'body' => [
+ [
+ 'value' => 'Lorem ipsum dolor sit',
+ 'format' => filter_default_format(),
+ ],
+ ],
+ ]);
+
+ $this->testParentNode = $this->createNode([
+ 'title' => 'Parent test node',
+ 'field_related_content' => [[
+ 'target_id' => $this->testChildNode->id(),
+ ],
+ ],
+ ]);
+ }
+
+ /**
+ * Tests the Referenced Entity Field formatter configuration.
+ */
+ public function testEntityReferenceFieldFormatter() {
+ $page = $this->getSession()->getPage();
+ $assert_session = $this->assertSession();
+
+ // Visit the display config page.
+ $this->drupalGet('/admin/structure/types/manage/page/display');
+ $page->selectFieldOption('fields[field_related_content][type]', 'entity_reference_field');
+ $result = $assert_session->waitForElementVisible('css', '.field-plugin-summary-cell .ajax-new-content');
+ $this->assertNotEmpty($result);
+ $assert_session->pageTextContains('Displaying the field "Title", using the formatter "Plain text".');
+
+ // Save without choosing anything and verify that the child title is shown
+ // by default.
+ $page->pressButton('Save');
+ $this->drupalGet('/node/' . $this->testParentNode->id());
+ $assert_session->elementTextContains('css', '.field--name-field-related-content .field__item .field--name-title.field--label-hidden', 'Child test node');
+ $assert_session->pageTextNotContains('Lorem ipsum dolor sit');
+
+ // Open the formatter settings, really choose the title field and save.
+ $this->drupalGet('/admin/structure/types/manage/page/display');
+ $page->find('css', 'input[data-drupal-selector="edit-fields-field-related-content-settings-edit"]')->click();
+ $assert_session->assertWaitOnAjaxRequest();
+ $assert_session->selectExists('fields[field_related_content][settings_edit_form][settings][field_name]');
+ $assert_session->selectExists('fields[field_related_content][settings_edit_form][settings][label]');
+ $assert_session->selectExists('fields[field_related_content][settings_edit_form][settings][type]');
+ $assert_session->optionNotExists('fields[field_related_content][settings_edit_form][settings][type]', 'entity_reference_entity_id');
+ $assert_session->checkboxNotChecked('fields[field_related_content][settings_edit_form][settings][settings][link_to_entity]');
+ $page->pressButton('Update');
+ $assert_session->assertWaitOnAjaxRequest();
+ $page->pressButton('Save');
+ $this->drupalGet('/node/' . $this->testParentNode->id());
+ $assert_session->elementTextContains('css', '.field--name-field-related-content .field__item .field--name-title.field--label-hidden', 'Child test node');
+ $assert_session->pageTextNotContains('Lorem ipsum dolor sit');
+
+ // Test the body field of the child entity.
+ $this->drupalGet('/admin/structure/types/manage/page/display');
+ $page->find('css', 'input[data-drupal-selector="edit-fields-field-related-content-settings-edit"]')->click();
+ $assert_session->assertWaitOnAjaxRequest();
+ $page->selectFieldOption('fields[field_related_content][settings_edit_form][settings][field_name]', 'body');
+ $assert_session->assertWaitOnAjaxRequest();
+ $page->selectFieldOption('fields[field_related_content][settings_edit_form][settings][type]', 'text_default');
+ $assert_session->assertWaitOnAjaxRequest();
+ $page->pressButton('Update');
+ $result = $assert_session->waitForElementVisible('css', '.field-plugin-summary-cell .ajax-new-content');
+ $this->assertNotEmpty($result);
+ $assert_session->pageTextContains('Displaying the field "Body", using the formatter "Default".');
+ $page->pressButton('Save');
+
+ $this->drupalGet('/node/' . $this->testParentNode->id());
+ $assert_session->elementTextContains('css', '.field--name-field-related-content .field__item .field--name-body.field--label-hidden', 'Lorem ipsum dolor sit');
+ $assert_session->pageTextNotContains('Child test node');
+ }
+
+}