diff --git a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php index 25222b7897600b0568bf01be511bee9f3cdec350..2dc8ed6760033213a92d67c0db3ee903ee760072 100644 --- a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php +++ b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php @@ -257,6 +257,29 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen // title of the 'uri' element. if ($this->getFieldSetting('title') == DRUPAL_DISABLED) { $element['uri']['#title'] = $element['#title']; + // By default the field description is added to the title field. Since + // the title field is disabled, we add the description, if given, to the + // uri element instead. + if (!empty($element['#description'])) { + if (empty($element['uri']['#description'])) { + $element['uri']['#description'] = $element['#description']; + } + else { + // If we have the description of the type of field together with + // the user provided description, we want to make a distinction + // between "core help text" and "user entered help text". To make + // this distinction more clear, we put them in an unordered list. + $element['uri']['#description'] = [ + '#theme' => 'item_list', + '#items' => [ + // Assume the user-specified description has the most relevance, + // so place it first. + $element['#description'], + $element['uri']['#description'], + ], + ]; + } + } } // Otherwise wrap everything in a details element. else { diff --git a/core/modules/link/tests/src/Functional/LinkFieldUITest.php b/core/modules/link/tests/src/Functional/LinkFieldUITest.php index 0da393834b7497dc1e9a43b25c28d6a2226d78ca..bd2433f903d1aa2a3936450d9ff0ca7c3220a351 100644 --- a/core/modules/link/tests/src/Functional/LinkFieldUITest.php +++ b/core/modules/link/tests/src/Functional/LinkFieldUITest.php @@ -2,7 +2,10 @@ namespace Drupal\Tests\link\Functional; -use Drupal\Component\Utility\Unicode; +use Drupal\Component\Utility\Html; +use Drupal\Core\Entity\Entity\EntityFormDisplay; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; use Drupal\field_ui\Tests\FieldUiTestTrait; use Drupal\link\LinkItemInterface; use Drupal\Tests\BrowserTestBase; @@ -30,14 +33,37 @@ class LinkFieldUITest extends BrowserTestBase { */ protected $adminUser; + /** + * A user that should see the help texts. + * + * @var \Drupal\user\Entity\User + */ + protected $helpTextUser; + + /** + * The first content type to add fields to. + * + * @var \Drupal\node\Entity\NodeType + */ + protected $firstContentType; + + /** + * The second content type to add fields to. + * + * @var \Drupal\node\Entity\NodeType + */ + protected $secondContentType; + /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); + $this->firstContentType = $this->drupalCreateContentType(); + $this->secondContentType = $this->drupalCreateContentType(); $this->adminUser = $this->drupalCreateUser(['administer content types', 'administer node fields', 'administer node display']); - $this->drupalLogin($this->adminUser); + $this->helpTextUser = $this->drupalCreateUser(['create ' . $this->secondContentType->id() . ' content']); $this->drupalPlaceBlock('system_breadcrumb_block'); } @@ -45,16 +71,91 @@ protected function setUp() { * Tests the link field UI. */ public function testFieldUI() { - // Add a content type. - $type = $this->drupalCreateContentType(); - $type_path = 'admin/structure/types/manage/' . $type->id(); - $add_path = 'node/add/' . $type->id(); + foreach ($this->providerTestFieldUI() as $item) { + list($cardinality, $link_type, $title, $label, $field_name) = $item; + $this->runFieldUIItem($cardinality, $link_type, $title, $label, $field_name); + } + } + + /** + * Provides test data for ::testFieldUI(). + */ + protected function providerTestFieldUI() { + // There are many combinations of field settings: where the description + // should show: variation on internal, external, both; cardinality (where + // the fieldset is hidden or used); and link text shown (required or + // optional) or disabled. There are two descriptions: field and URL help + // text. + $cardinalities = [1, 2]; + $title_settings = [ + DRUPAL_DISABLED, + DRUPAL_OPTIONAL, + ]; + $link_types = [ + LinkItemInterface::LINK_EXTERNAL, + LinkItemInterface::LINK_GENERIC, + LinkItemInterface::LINK_INTERNAL, + ]; + + // Test all variations of link types on all cardinalities. + foreach ($cardinalities as $cardinality) { + foreach ($link_types as $link_type) { + // Now, test this with both the title enabled and disabled. + foreach ($title_settings as $title_setting) { + // Test both empty and non-empty labels. + foreach ([TRUE, FALSE] as $label_provided) { + // Generate a unique machine name for the field so it can be + // identified in the test. + $id = implode('_', [ + 'link', + $cardinality, + $link_type, + $title_setting, + (int) $label_provided, + ]); + + // Use a unique label that contains some HTML. + $label = '' . $id; + + yield [ + $cardinality, + $link_type, + $title_setting, + $label_provided ? $label : '', + $id, + ]; + } + } + } + } + } + + /** + * Tests one link field UI item. + * + * @param int $cardinality + * The field cardinality. + * @param int $link_type + * Determine if the link is external, internal or both. + * @param int $title + * Determine if the field will display the link text field. + * @param string $label + * The field label. + * @param string $field_name + * The unique machine name for the field. + */ + public function runFieldUIItem($cardinality, $link_type, $title, $label, $field_name) { + $this->drupalLogin($this->adminUser); + $type_path = 'admin/structure/types/manage/' . $this->firstContentType->id(); // Add a link field to the newly-created type. It defaults to allowing both // internal and external links. - $label = $this->randomMachineName(); - $field_name = Unicode::strtolower($label); - $this->fieldUIAddNewField($type_path, $field_name, $label, 'link'); + $field_label = str_replace('_', ' ', $field_name); + $description = 'link field description'; + $field_edit = [ + 'description' => $description, + ]; + $this->fieldUIAddNewField($type_path, $field_name, $field_label, 'link', [], $field_edit); // Load the formatter page to check that the settings summary does not // generate warnings. @@ -62,31 +163,94 @@ public function testFieldUI() { $this->drupalGet("$type_path/display"); $this->assertText(t('Link text trimmed to @limit characters', ['@limit' => 80])); - // Test the help text displays when the link field allows both internal and - // external links. - $this->drupalLogin($this->drupalCreateUser(['create ' . $type->id() . ' content'])); - $this->drupalGet($add_path); - $this->assertRaw('You can also enter an internal path such as /node/add or an external URL such as http://example.com.'); + $storage = FieldStorageConfig::create([ + 'field_name' => $field_name, + 'entity_type' => 'node', + 'type' => 'link', + 'cardinality' => $cardinality, + ]); + $storage->save(); - // Log in an admin to set up the next content type. - $this->drupalLogin($this->adminUser); + FieldConfig::create([ + 'field_storage' => $storage, + 'label' => $label, + 'bundle' => $this->secondContentType->id(), + 'settings' => [ + 'title' => $title, + 'link_type' => $link_type, + ], + ])->save(); - // Add a different content type. - $type = $this->drupalCreateContentType(); - $type_path = 'admin/structure/types/manage/' . $type->id(); - $add_path = 'node/add/' . $type->id(); + // Make the fields visible in the form display. + $form_display_id = implode('.', ['node', $this->secondContentType->id(), 'default']); + $form_display = EntityFormDisplay::load($form_display_id); + $form_display->setComponent($field_name, ['region' => 'content']); + $form_display->save(); - // Add a link field to the newly-created type. Specify it must allow - // external only links. - $label = $this->randomMachineName(); - $field_name = Unicode::strtolower($label); - $field_edit = ['settings[link_type]' => LinkItemInterface::LINK_EXTERNAL]; - $this->fieldUIAddNewField($type_path, $field_name, $label, 'link', [], $field_edit); + // Log in a user that is allowed to create this content type, see if + // the user can see the expected help text. + $this->drupalLogin($this->helpTextUser); - // Test the help text displays when link allows only external links. - $this->drupalLogin($this->drupalCreateUser(['create ' . $type->id() . ' content'])); + $add_path = 'node/add/' . $this->secondContentType->id(); $this->drupalGet($add_path); - $this->assertRaw('This must be an external URL such as http://example.com.'); + + $expected_help_texts = [ + LinkItemInterface::LINK_EXTERNAL => 'This must be an external URL such as http://example.com.', + LinkItemInterface::LINK_GENERIC => 'You can also enter an internal path such as /node/add or an external URL such as http://example.com. Enter <front> to link to the front page.', + LinkItemInterface::LINK_INTERNAL => rtrim(\Drupal::url('', [], ['absolute' => TRUE]), '/'), + ]; + + // Check that the help texts we assume should be there, is there. + $this->assertFieldContainsRawText($field_name, $expected_help_texts[$link_type]); + if ($link_type === LinkItemInterface::LINK_INTERNAL) { + // Internal links have no "system" description. Test that none + // of the other help texts show here. + $this->assertNoFieldContainsRawText($field_name, $expected_help_texts[LinkItemInterface::LINK_EXTERNAL]); + $this->assertNoFieldContainsRawText($field_name, $expected_help_texts[LinkItemInterface::LINK_GENERIC]); + } + // Also assert that the description we made is here, no matter what the + // cardinality or link setting. + if (!empty($label)) { + $this->assertFieldContainsRawText($field_name, $label); + } + } + + /** + * Checks that given field contains the given raw text. + * + * @param string $field_name + * The name of the field to check. + * @param string $text + * The text to check. + */ + protected function assertFieldContainsRawText($field_name, $text) { + $this->assertTrue((bool) preg_match('/' . preg_quote($text, '/') . '/ui', $this->getFieldHtml($field_name))); + } + + /** + * Checks that given field does not contain the given raw text. + * + * @param string $field_name + * The name of the field to check. + * @param string $text + * The text to check. + */ + protected function assertNoFieldContainsRawText($field_name, $text) { + $this->assertFalse((bool) preg_match('/' . preg_quote($text, '/') . '/ui', $this->getFieldHtml($field_name))); + } + + /** + * Returns the raw HTML for the given field. + * + * @param $field_name + * The name of the field for which to return the HTML. + * + * @return string + * The raw HTML. + */ + protected function getFieldHtml($field_name) { + $css_id = Html::cleanCssIdentifier('edit-' . $field_name . '-wrapper'); + return $this->xpath('//*[@id=:id]', [':id' => $css_id])[0]->getHtml(); } } diff --git a/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentFormTest.php b/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentFormTest.php index 464b86c3972d4d66cbcc770b9cd5d23e7a297119..8796bc7488751282e87b206abd0f649dc73cfb54 100644 --- a/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentFormTest.php +++ b/core/modules/menu_link_content/tests/src/Functional/MenuLinkContentFormTest.php @@ -78,6 +78,8 @@ public function testMenuLinkContentForm() { $element = $this->xpath('//select[@id = :id]/option[@selected]', [':id' => 'edit-menu-parent']); $this->assertTrue($element, 'A default menu parent was found.'); $this->assertEqual('admin:', $element[0]->getValue(), ' menu is the parent.'); + // Test that the field description is present. + $this->assertRaw('The location this menu link points to.'); $this->drupalPostForm( NULL, diff --git a/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php b/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php index e39f652e55e0d4ea3b28df9628067395c1bb0bbb..77e88959b970b618372a4f7c5c8000f4d4f11ee5 100644 --- a/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php +++ b/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php @@ -65,6 +65,11 @@ public function testShortcutLinkAdd() { '/admin/config/system/site-information', ]; + // Test the add shortcut form UI. Test that the base field description is + // there. + $this->drupalGet('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link'); + $this->assertRaw('The location this shortcut points to.'); + // Check that each new shortcut links where it should. foreach ($test_cases as $test_path) { $title = $this->randomMachineName();