Verified Commit ee78cfa7 authored by Dave Long's avatar Dave Long
Browse files

Issue #3494015 by vidorado, danchadwick, smustgrave: Link render element overwrites CSS class

parent c742c881
Loading
Loading
Loading
Loading
Loading
+4 −1
Changes for core/lib/Drupal/Core/Render/Element/Link.php: 4 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -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'];
    }

+95 −0
Changes for core/tests/Drupal/KernelTests/Core/Render/Element/RenderElementTypesTest.php: 95 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -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.');
    }
  }

}