diff --git a/oe_theme.theme b/oe_theme.theme index c7d8837e4..01c92e254 100644 --- a/oe_theme.theme +++ b/oe_theme.theme @@ -9,6 +9,7 @@ declare(strict_types = 1); use Drupal\block\BlockInterface; use Drupal\Component\Utility\Html; +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Entity\ContentEntityInterface; @@ -1710,6 +1711,13 @@ function oe_theme_theme_suggestions_oe_corporate_blocks_eu_footer_alter(array &$ $suggestions[] = 'oe_corporate_blocks_eu_footer__' . $branding; } +/** + * Implements hook_preprocess_HOOK(). + */ +function oe_theme_preprocess_oe_corporate_blocks_ec_footer(array &$variables) { + _oe_theme_preprocess_site_specific_footer_links($variables); +} + /** * Implements hook_preprocess_HOOK(). */ @@ -1728,6 +1736,41 @@ function oe_theme_preprocess_oe_corporate_blocks_eu_footer(array &$variables) { $site_logo_href .= '/index_' . EuropeanUnionLanguages::getInternalLanguageCode($variables['current_language_id']); } $variables['path'] = $site_logo_href; + + _oe_theme_preprocess_site_specific_footer_links($variables); +} + +/** + * Helper function to preprocess footer links and set external flag to them. + * + * @param array $variables + * The variables array from the preprocess hook. + */ +function _oe_theme_preprocess_site_specific_footer_links(array &$variables): void { + if (empty($variables['site_specific_footer']['other_links'])) { + return; + } + + foreach ($variables['site_specific_footer']['other_links'] as $section => $links) { + if (empty($links['links'])) { + continue; + } + + foreach ($links['links'] as $index => $link) { + if (!$link['href'] instanceof Url) { + continue; + } + + if ($link['href']->isExternal()) { + $parts = UrlHelper::parse($link['href']->toString()); + if (strpos($parts['path'], 'europa.eu')) { + // We consider links with europa.eu domains as internal. + continue; + } + $variables['site_specific_footer']['other_links'][$section]['links'][$index]['external'] = TRUE; + } + } + } } /** diff --git a/tests/src/Functional/CorporateFooterRenderTest.php b/tests/src/Functional/CorporateFooterRenderTest.php index 3dcb565e7..67c694c33 100644 --- a/tests/src/Functional/CorporateFooterRenderTest.php +++ b/tests/src/Functional/CorporateFooterRenderTest.php @@ -339,7 +339,7 @@ public function testCorporateFooterRendering(): void { $this->assertListLink($actual, 'standardised', $expected); } - // Add a few custom footer links, one by one to assert section distripution. + // Add a few custom footer links, one by one to assert section distribution. $this->createGeneralLink('Custom contact 1', 'contact_us'); $this->drupalGet(''); @@ -356,7 +356,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom contact 1', 'href' => 'http://example.com/custom-contact-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external svg icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); $assert->elementNotExists('css', 'footer.ecl-footer-standardised div.ecl-footer-standardised__row:nth-child(1) div.ecl-footer-standardised__column:nth-child(3) div.ecl-footer-standardised__section:nth-child(1)'); @@ -375,7 +378,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom contact 1', 'href' => 'http://example.com/custom-contact-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); $column = $assert->elementExists('css', 'footer.ecl-footer-standardised div.ecl-footer-standardised__row:nth-child(1) div.ecl-footer-standardised__column:nth-child(3)'); $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(1)', $column); @@ -388,7 +394,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom about 1', 'href' => 'http://example.com/custom-about-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); // Add one more, assert odd goes into column 2, even goes into column 3. $this->createGeneralLink('Custom related 1', 'related_sites'); @@ -405,7 +414,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom contact 1', 'href' => 'http://example.com/custom-contact-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(2)', $column); @@ -417,7 +429,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom related 1', 'href' => 'http://example.com/custom-related-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); $column = $assert->elementExists('css', 'footer.ecl-footer-standardised div.ecl-footer-standardised__row:nth-child(1) div.ecl-footer-standardised__column:nth-child(3)'); $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(1)', $column); @@ -430,7 +445,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom about 1', 'href' => 'http://example.com/custom-about-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); // Add the follow us, assert it goes last, into the centre column. // We have 3 sub-sections plus follow, distribution should be 2 and 2. @@ -449,7 +467,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom contact 1', 'href' => 'http://example.com/custom-contact-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(2)', $column); @@ -485,7 +506,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom about 1', 'href' => 'http://example.com/custom-about-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); // Assert previous last in section 2 moved to the right into section 3. $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(2)', $column); @@ -498,7 +522,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom related 1', 'href' => 'http://example.com/custom-related-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); // Assert adding a custom section in backend appears, // and is placed in the footer on the right column. @@ -517,7 +544,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom contact 1', 'href' => 'http://example.com/custom-contact-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); // Since we have an even number there is no switch, // assert Related is back in section 2. @@ -531,7 +561,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom related 1', 'href' => 'http://example.com/custom-related-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); // Here we assert Follow us is still on the left in section 2. $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(3)', $column); @@ -568,7 +601,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom about 1', 'href' => 'http://example.com/custom-about-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(2)', $column); @@ -580,7 +616,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom link 1', 'href' => 'http://example.com/custom-link-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); // Assert updating a general link also changes the footer content. $this->updateGeneralLink('custom-link-1', [ @@ -600,7 +639,32 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom link altered', 'href' => 'http://example.com/custom-link-altered', ]; + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); + + // Update the link with local path, so the external icon won't be present. + $this->updateGeneralLink('custom-link-1', [ + 'label' => 'Custom link altered', + 'url' => '', + ]); + $this->drupalGet(''); + + $column = $assert->elementExists('css', 'footer.ecl-footer-standardised div.ecl-footer-standardised__row:nth-child(1) div.ecl-footer-standardised__column:nth-child(3)'); + $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(2)', $column); + + $actual = $assert->elementExists('css', '.ecl-footer-standardised__title', $subsection); + $this->assertEquals('Section 1', $actual->getText()); + + $actual = $subsection->find('css', 'ul li:nth-child(1) > a'); + $expected = [ + 'label' => 'Custom link altered', + 'href' => '/build/', + ]; $this->assertListLink($actual, 'standardised', $expected); + // We should not have any icon present. + $assert->elementNotExists('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon', $actual); // Assert updating a section also changes the footer content. $this->updateSection('section_1', [ @@ -635,7 +699,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom contact 1', 'href' => 'http://example.com/custom-contact-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(2)', $column); @@ -647,7 +714,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom about 1', 'href' => 'http://example.com/custom-about-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(3)', $column); @@ -681,9 +751,11 @@ public function testCorporateFooterRendering(): void { $actual = $subsection->find('css', 'ul li:nth-child(1) > a'); $expected = [ 'label' => 'Custom link altered', - 'href' => 'http://example.com/custom-link-altered', + 'href' => '/build/', ]; $this->assertListLink($actual, 'standardised', $expected); + // We should not have any icon present. + $assert->elementNotExists('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon', $actual); $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(2)', $column); @@ -695,7 +767,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom related 1', 'href' => 'http://example.com/custom-related-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); // Assert deleting links removes the section. $this->deleteEntity('footer_link_general', 'custom-about-1'); @@ -712,7 +787,10 @@ public function testCorporateFooterRendering(): void { 'label' => 'Custom contact 1', 'href' => 'http://example.com/custom-contact-1', ]; - $this->assertListLink($actual, 'standardised', $expected); + $this->assertListLink($actual, 'standardised', $expected, TRUE); + // We should have the external icon present. + $icon = $actual->find('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon use'); + $this->assertStringContainsString('external', $icon->getAttribute('xlink:href')); $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(2)', $column); @@ -739,6 +817,25 @@ public function testCorporateFooterRendering(): void { ]; $this->assertSocialLink($social_label, $social_link, $expected); + // Update the link with europa.eu path, so the external icon won't be + // present. + $this->updateGeneralLink('custom-link-1', [ + 'label' => 'Custom link altered', + 'url' => 'http://ec.europa.eu/info', + ]); + $this->drupalGet(''); + + $column = $assert->elementExists('css', 'footer.ecl-footer-standardised div.ecl-footer-standardised__row:nth-child(1) div.ecl-footer-standardised__column:nth-child(3)'); + $subsection = $assert->elementExists('css', '.ecl-footer-standardised__section:nth-child(1)', $column); + $actual = $subsection->find('css', 'ul li:nth-child(1) > a'); + $expected = [ + 'label' => 'Custom link altered', + 'href' => 'http://ec.europa.eu/info', + ]; + $this->assertListLink($actual, 'standardised', $expected); + // We should not have any icon present. + $assert->elementNotExists('css', 'svg.ecl-icon.ecl-icon--xs.ecl-link__icon', $actual); + // Assert deleting sections in backend is reflected in the footer. $this->deleteEntity('footer_link_section', 'about_us'); $this->deleteEntity('footer_link_section', 'contact_us'); @@ -802,11 +899,14 @@ protected function assertFooterPresence(string $branding, int $expected_section_ * Ecl branding, core/standardised. * @param array $expected * The expected data. + * @param bool $icon + * If the link should have an icon, defaults to FALSE. */ - protected function assertListLink(NodeElement $actual, string $branding, array $expected): void { + protected function assertListLink(NodeElement $actual, string $branding, array $expected, bool $icon = FALSE): void { $this->assertEquals($expected['label'], $actual->getText()); $this->assertEquals($expected['href'], $actual->getAttribute('href')); - $this->assertEquals("ecl-link ecl-link--standalone ecl-footer-{$branding}__link", $actual->getAttribute('class')); + $icon_class = $icon ? 'ecl-link--icon ecl-link--icon-after ' : ''; + $this->assertEquals("ecl-link ecl-link--standalone {$icon_class}ecl-footer-{$branding}__link", $actual->getAttribute('class')); } /** @@ -868,7 +968,7 @@ protected function updateSiteSettings(string $site_owner, string $site_name): vo */ protected function createGeneralLink(string $label, string $section = ''): void { $id = Html::getId($label); - $link = \Drupal::entityTypeManager()->getStorage('footer_link_general')->create([ + \Drupal::entityTypeManager()->getStorage('footer_link_general')->create([ 'id' => $id, 'label' => $label, 'url' => 'http://example.com/' . $id, @@ -905,7 +1005,7 @@ protected function updateGeneralLink(string $id, array $data): void { */ protected function createSocialLink(string $label, string $network): void { $id = Html::getId($label); - $link = \Drupal::entityTypeManager()->getStorage('footer_link_social')->create([ + \Drupal::entityTypeManager()->getStorage('footer_link_social')->create([ 'id' => $id, 'label' => $label, 'url' => 'http://example.com/' . $id, @@ -923,7 +1023,7 @@ protected function createSocialLink(string $label, string $network): void { * The section label. */ protected function createSection(string $id, string $label): void { - $section = \Drupal::entityTypeManager()->getStorage('footer_link_section')->create([ + \Drupal::entityTypeManager()->getStorage('footer_link_section')->create([ 'id' => $id, 'label' => $label, 'weight' => 0,