diff --git a/core/lib/Drupal/Core/Render/Element/Link.php b/core/lib/Drupal/Core/Render/Element/Link.php index e93deb23d7f8b8bcd5e61bbc27c024a9edd4f6a0..2a00e0526b6872640f18057a2e67585e00298df5 100644 --- a/core/lib/Drupal/Core/Render/Element/Link.php +++ b/core/lib/Drupal/Core/Render/Element/Link.php @@ -68,9 +68,12 @@ public static function preRenderLink($element) { $element += ['#options' => []]; // However, within the scope of renderable elements, #attributes is a valid // way to specify attributes, too. Take them into account, but do not - // override attributes from #options. + // override attributes from #options. Merge class as a string or array. if (isset($element['#attributes'])) { $element['#options'] += ['attributes' => []]; + $element_class = $element['#attributes']['class'] ?? []; + $option_class = $element['#options']['attributes']['class'] ?? []; + $element['#options']['attributes']['class'] = array_merge((array) $option_class, (array) $element_class); $element['#options']['attributes'] += $element['#attributes']; } diff --git a/core/tests/Drupal/KernelTests/Core/Render/Element/RenderElementTypesTest.php b/core/tests/Drupal/KernelTests/Core/Render/Element/RenderElementTypesTest.php index b381db560e9a8d7dfa1eb4a343cbf66fb94a1879..0af94a8d8dc34f39c8a5ed73d4c1e2e6bc79cb84 100644 --- a/core/tests/Drupal/KernelTests/Core/Render/Element/RenderElementTypesTest.php +++ b/core/tests/Drupal/KernelTests/Core/Render/Element/RenderElementTypesTest.php @@ -237,4 +237,99 @@ public function testSystemCompactLink(): void { $this->assertNotEmpty($result, '"' . $element['name'] . '" is rendered correctly.'); } + /** + * Tests system #type 'link'. + */ + public function testLink(): void { + $elements = [ + [ + 'name' => "#type 'link' simple anchor tag", + 'value' => [ + '#type' => 'link', + '#title' => 'title', + '#url' => Url::fromUri('https://www.drupal.org'), + ], + 'expected' => '//a[@href="https://www.drupal.org" and text()="title"]', + ], + [ + 'name' => "#type 'link' anchor tag with extra classes in ['#attributes']", + 'value' => [ + '#type' => 'link', + '#title' => 'title', + '#url' => Url::fromUri('https://www.drupal.org'), + '#attributes' => [ + 'class' => ['attributes-class'], + ], + '#options' => [], + ], + 'expected' => '//a[@href="https://www.drupal.org" and @class="attributes-class" and text()="title"]', + ], + [ + 'name' => "#type 'link' anchor tag with extra classes in ['#options']['attributes']", + 'value' => [ + '#type' => 'link', + '#title' => 'title', + '#url' => Url::fromUri('https://www.drupal.org'), + '#attributes' => [], + '#options' => [ + 'attributes' => [ + 'class' => ['options-attributes-class'], + ], + ], + ], + 'expected' => '//a[@href="https://www.drupal.org" and @class="options-attributes-class" and text()="title"]', + ], + [ + 'name' => "#type 'link' anchor tag with extra classes in both ['#attributes'] and ['#options']['attributes']", + 'value' => [ + '#type' => 'link', + '#title' => 'title', + '#url' => Url::fromUri('https://www.drupal.org'), + '#attributes' => [ + 'class' => ['attributes-class'], + ], + '#options' => [ + 'attributes' => [ + 'class' => ['options-attributes-class'], + ], + ], + ], + 'expected' => '//a[@href="https://www.drupal.org" and @class="options-attributes-class attributes-class" and text()="title"]', + ], + [ + 'name' => "#type 'link' anchor tag with extra classes in both ['#attributes'] and ['#options']['attributes'] as strings", + 'value' => [ + '#type' => 'link', + '#title' => 'title', + '#url' => Url::fromUri('https://www.drupal.org'), + '#attributes' => [ + 'class' => 'attributes-class', + ], + '#options' => [ + 'attributes' => [ + 'class' => 'options-attributes-class', + ], + ], + ], + 'expected' => '//a[@href="https://www.drupal.org" and contains(@class,"options-attributes-class") and contains(@class,"attributes-class") and text()="title"]', + ], + [ + 'name' => "#type 'link' anchor tag with extra classes in Url object ['#options']['attributes'] which are ignored", + 'value' => [ + '#type' => 'link', + '#title' => 'title', + '#url' => Url::fromUri('https://www.drupal.org')->setOption('attributes', ['class' => 'url-options-attributes-class']), + '#attributes' => [], + '#options' => [], + ], + 'expected' => '//a[@href="https://www.drupal.org" and not(@class) and text()="title"]', + ], + ]; + foreach ($elements as $element) { + $xml = new \SimpleXMLElement((string) \Drupal::service('renderer')->renderRoot($element['value'])); + $result = $xml->xpath($element['expected']); + $this->assertNotEmpty($result, '"' . $element['name'] . '" input rendered correctly.'); + } + } + }