diff --git a/core/modules/navigation/components/toolbar-button/toolbar-button.component.yml b/core/modules/navigation/components/toolbar-button/toolbar-button.component.yml index 0a7e9853f5cca9de387f566e6fb4fac50e2642c7..87e29a14bf447b81c96f8df387c638222d561a1f 100644 --- a/core/modules/navigation/components/toolbar-button/toolbar-button.component.yml +++ b/core/modules/navigation/components/toolbar-button/toolbar-button.component.yml @@ -57,7 +57,23 @@ props: default: button icon: title: Icon - type: string + type: object + properties: + pack_id: + title: Icon Pack + type: string + default: navigation + icon_id: + title: Icon ID + type: string + settings: + title: Icon Settings + type: object + default: + class: toolbar-button__icon + size: 20 + required: + - icon_id text: title: Text description: Text of button. diff --git a/core/modules/navigation/components/toolbar-button/toolbar-button.twig b/core/modules/navigation/components/toolbar-button/toolbar-button.twig index a4e061986ac8ff0952f7d37f19b5720e0edfb382..c220d1008a6ab91445d990e2321a75e934f05bb1 100644 --- a/core/modules/navigation/components/toolbar-button/toolbar-button.twig +++ b/core/modules/navigation/components/toolbar-button/toolbar-button.twig @@ -3,7 +3,7 @@ appear after main classes #} {% set classes = [ 'toolbar-button', - icon ? 'toolbar-button--icon--' ~ icon : '', + icon.icon_id ? 'toolbar-button--icon--' ~ icon.icon_id : '', ] %} @@ -24,8 +24,8 @@ appear after main classes #} <{{ html_tag|default('button') }} {{ attributes.addClass(classes) }}> - {% if icon %} - {{ icon('navigation', icon, { class: 'toolbar-button__icon', size: 20 }) }} + {% if icon.icon_id %} + {{ icon(icon.pack_id|default('navigation'), icon.icon_id, icon.settings|default({ class: 'toolbar-button__icon', size: 20 })) }} {% endif %} {% if action %} diff --git a/core/modules/navigation/layouts/navigation.html.twig b/core/modules/navigation/layouts/navigation.html.twig index 699d14f72c02e01262bb633103991ba6e2963711..6f9804e332dd370b14f57945236060f63158c96b 100644 --- a/core/modules/navigation/layouts/navigation.html.twig +++ b/core/modules/navigation/layouts/navigation.html.twig @@ -22,7 +22,7 @@ <div class="admin-toolbar-control-bar__content"> {% include 'navigation:toolbar-button' with { attributes: create_attribute({'aria-expanded': 'false', 'aria-controls': 'admin-toolbar', 'type': 'button'}), - icon: 'burger', + icon: { icon_id: 'burger' }, text: 'Expand sidebar'|t, modifiers: ['small-offset'], extra_classes: [ @@ -59,14 +59,14 @@ {% include 'navigation:toolbar-button' with { attributes: create_attribute({ 'data-toolbar-back-control': true, 'tabindex': '-1' }), extra_classes: ['admin-toolbar__back-button'], - icon: 'arrow-left', + icon: { icon_id: 'arrow-left' }, text: 'Back'|t, } only %} {% include 'navigation:toolbar-button' with { action: 'Collapse sidebar'|t, attributes: create_attribute({ 'aria-controls': 'admin-toolbar', 'tabindex': '-1', 'type': 'button' }), extra_classes: ['admin-toolbar__close-button'], - icon: 'cross', + icon: { icon_id: 'cross' }, } only %} </div> diff --git a/core/modules/navigation/src/Menu/NavigationMenuLinkTree.php b/core/modules/navigation/src/Menu/NavigationMenuLinkTree.php index ca45eb581d94a6aae837dd65b48bf70195103062..68ce42fc02b366628215785cd297917474387f00 100644 --- a/core/modules/navigation/src/Menu/NavigationMenuLinkTree.php +++ b/core/modules/navigation/src/Menu/NavigationMenuLinkTree.php @@ -4,6 +4,8 @@ namespace Drupal\navigation\Menu; +use Drupal\Component\Utility\Html; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Menu\MenuActiveTrailInterface; use Drupal\Core\Menu\MenuLinkManagerInterface; @@ -72,8 +74,18 @@ public function build(array $tree): array { foreach ($tree as $item) { if ($item->access->isAllowed()) { $plugin_id = $item->link->getPluginId(); - $plugin_class = str_replace('.', '_', $plugin_id); + $plugin_class = Html::getClass(str_replace('.', '_', $plugin_id)); $build['#items'][$plugin_id]['class'] = $plugin_class; + $url = $build['#items'][$plugin_id]['url']; + $icon_defaults = [ + 'pack_id' => 'navigation', + 'icon_id' => $plugin_class, + 'settings' => [ + 'class' => 'toolbar-button__icon', + 'size' => 20, + ], + ]; + $build['#items'][$plugin_id]['icon'] = NestedArray::mergeDeep($icon_defaults, $url->getOption('icon') ?? []); } } diff --git a/core/modules/navigation/src/Plugin/Block/NavigationLinkBlock.php b/core/modules/navigation/src/Plugin/Block/NavigationLinkBlock.php index 7a73de500d8e3b261bb1f28f58ab6ea6f911cb33..2f96c7615bfd07d7c1621eca17fb777cb46a55f8 100644 --- a/core/modules/navigation/src/Plugin/Block/NavigationLinkBlock.php +++ b/core/modules/navigation/src/Plugin/Block/NavigationLinkBlock.php @@ -274,6 +274,9 @@ public function build(): array { 'title' => $config['title'], 'class' => $config['icon_class'], 'url' => $url, + 'icon' => [ + 'icon_id' => $config['icon_class'], + ], ], ], ]; diff --git a/core/modules/navigation/src/Plugin/Block/NavigationShortcutsBlock.php b/core/modules/navigation/src/Plugin/Block/NavigationShortcutsBlock.php index e5e2f7f4526db35a2a43e96ff8b4b99059f276d0..a74ac0c472e46254c40263bfa7c6c9f26a0b5ce4 100644 --- a/core/modules/navigation/src/Plugin/Block/NavigationShortcutsBlock.php +++ b/core/modules/navigation/src/Plugin/Block/NavigationShortcutsBlock.php @@ -89,6 +89,9 @@ public function build(): array { [ 'title' => $this->configuration['label'], 'class' => 'shortcuts', + 'icon' => [ + 'icon_id' => 'shortcuts', + ], ], ], ], diff --git a/core/modules/navigation/src/Plugin/TopBarItem/PageActions.php b/core/modules/navigation/src/Plugin/TopBarItem/PageActions.php index c759676bc9e73dfdb8929a6b8c4c8b5379429fb5..eb9e88015d72d9a67765cc077c3b53852011d05a 100644 --- a/core/modules/navigation/src/Plugin/TopBarItem/PageActions.php +++ b/core/modules/navigation/src/Plugin/TopBarItem/PageActions.php @@ -119,7 +119,9 @@ protected function getFeaturedPageActions(array $page_actions): ?array { if (isset($page_actions['page_actions'][$edit_route]) && $page_actions['page_actions'][$edit_route]['#access']?->isAllowed()) { $featured_page_actions[$edit_route] = [ 'page_action' => $page_actions['page_actions'][$edit_route], - 'icon' => 'pencil', + 'icon' => [ + 'icon_id' => 'pencil', + ], ]; } } diff --git a/core/modules/navigation/src/ShortcutLazyBuilder.php b/core/modules/navigation/src/ShortcutLazyBuilder.php index a229c2674cee11f3e631aee3ad774771bb0a5735..121c29f7e865b21666597e5d1e13446967b949d7 100644 --- a/core/modules/navigation/src/ShortcutLazyBuilder.php +++ b/core/modules/navigation/src/ShortcutLazyBuilder.php @@ -53,6 +53,9 @@ public function lazyLinks(string $label = 'Shortcuts') { [ 'title' => $label, 'class' => 'shortcuts', + 'icon' => [ + 'icon_id' => 'shortcuts', + ], 'below' => $shortcut_links['shortcuts']['#links'], ], ]; diff --git a/core/modules/navigation/templates/navigation-menu.html.twig b/core/modules/navigation/templates/navigation-menu.html.twig index 48de2f9604e2a98fd72aef11394ca8d472e81ddf..f5b905caf1af1380d2c4702531046428ff634d1d 100644 --- a/core/modules/navigation/templates/navigation-menu.html.twig +++ b/core/modules/navigation/templates/navigation-menu.html.twig @@ -31,7 +31,7 @@ <li id="{{ item_id }}" class="toolbar-block__list-item"> {% include 'navigation:toolbar-button' with { attributes: item_link_attributes.setAttribute('href', item.url|render|default(null)).setAttribute('data-drupal-tooltip', item.title).setAttribute('data-drupal-tooltip-class', 'admin-toolbar__tooltip'), - icon: item.class|clean_class, + icon: item.icon, html_tag: item_link_tag, text: item.title, modifiers: [ @@ -50,7 +50,7 @@ 'data-toolbar-popover-control': true, 'data-has-safe-triangle': true, }), - icon: item.class|clean_class, + icon: item.icon, text: item.title, modifiers: [ 'expand--side', diff --git a/core/modules/navigation/templates/top-bar-page-actions.html.twig b/core/modules/navigation/templates/top-bar-page-actions.html.twig index 2fb8edce610058e35103ecb22f0e71c13ad68e25..b412e3353b367d84498ed53f22ef6ecc4a4f536a 100644 --- a/core/modules/navigation/templates/top-bar-page-actions.html.twig +++ b/core/modules/navigation/templates/top-bar-page-actions.html.twig @@ -22,7 +22,7 @@ {% endfor %} {% include 'navigation:toolbar-button' with { - icon: 'dots', + icon: { icon_id: 'dots' }, action: 'More actions'|t, attributes: create_attribute( { diff --git a/core/modules/navigation/tests/navigation_test/assets/icons/radioactive.svg b/core/modules/navigation/tests/navigation_test/assets/icons/radioactive.svg new file mode 100644 index 0000000000000000000000000000000000000000..466281e1cc0965baebca644ddd036bef1e2d3088 --- /dev/null +++ b/core/modules/navigation/tests/navigation_test/assets/icons/radioactive.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M92,136H40a16,16,0,0,1-11.76-5.21,16.21,16.21,0,0,1-4.17-12.37A103.83,103.83,0,0,1,67.65,42.93,16,16,0,0,1,90.75,48l26,45a8,8,0,1,1-13.86,8L76.89,56A87.83,87.83,0,0,0,40,119.86a.19.19,0,0,0,.07.16L92,120a8,8,0,0,1,0,16Zm139.93-17.58a103.83,103.83,0,0,0-43.58-75.49A16,16,0,0,0,165.25,48L139.3,93a8,8,0,0,0,13.86,8l26-45A87.87,87.87,0,0,1,216,119.86c0,.07,0,.12,0,.14H164a8,8,0,0,0,0,16h52a16,16,0,0,0,11.76-5.21A16.21,16.21,0,0,0,231.93,118.42Zm-79,36.76a8,8,0,1,0-13.86,8l25.84,44.73a88.22,88.22,0,0,1-73.81,0l25.83-44.73a8,8,0,1,0-13.86-8L77.25,199.91a16,16,0,0,0,7.12,22.52,104.24,104.24,0,0,0,87.26,0,16,16,0,0,0,7.12-22.52ZM128,140a12,12,0,1,0-12-12A12,12,0,0,0,128,140Z"></path></svg> \ No newline at end of file diff --git a/core/modules/navigation/tests/navigation_test/assets/icons/star.svg b/core/modules/navigation/tests/navigation_test/assets/icons/star.svg new file mode 100644 index 0000000000000000000000000000000000000000..093da7c72f467e37013f80e316f4399638be58cb --- /dev/null +++ b/core/modules/navigation/tests/navigation_test/assets/icons/star.svg @@ -0,0 +1,3 @@ +<svg width="22" height="21" viewBox="0 0 22 21" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M21.4251 8.121C21.3342 7.84104 21.1631 7.594 20.9328 7.41063C20.7026 7.22726 20.4236 7.11566 20.1304 7.08975L14.5626 6.60974L12.3801 1.41974C12.2664 1.14742 12.0748 0.914795 11.8292 0.751174C11.5836 0.587552 11.2952 0.500244 11.0001 0.500244C10.705 0.500244 10.4165 0.587552 10.1709 0.751174C9.92537 0.914795 9.7337 1.14742 9.62007 1.41974L7.44413 6.60974L1.86976 7.09256C1.57542 7.11729 1.29493 7.22838 1.06347 7.41188C0.832008 7.59539 0.65988 7.84315 0.568668 8.1241C0.477457 8.40504 0.471222 8.70666 0.550747 8.99113C0.630272 9.2756 0.792015 9.53027 1.01569 9.72318L5.24476 13.4188L3.97726 18.9069C3.91023 19.1941 3.92936 19.4947 4.03224 19.7711C4.13512 20.0475 4.31719 20.2874 4.55569 20.4609C4.79419 20.6344 5.07853 20.7337 5.37317 20.7464C5.66781 20.7592 5.95967 20.6848 6.21226 20.5326L10.9935 17.6263L15.7851 20.5326C16.0377 20.6848 16.3295 20.7592 16.6242 20.7464C16.9188 20.7337 17.2031 20.6344 17.4416 20.4609C17.6801 20.2874 17.8622 20.0475 17.9651 19.7711C18.068 19.4947 18.0871 19.1941 18.0201 18.9069L16.7535 13.4132L20.9816 9.72318C21.2053 9.5296 21.3667 9.27421 21.4456 8.98914C21.5245 8.70406 21.5174 8.40202 21.4251 8.121ZM19.9982 8.58975L15.7701 12.2797C15.5643 12.4587 15.4112 12.6905 15.3273 12.95C15.2434 13.2095 15.2318 13.487 15.2938 13.7526L16.5641 19.2501L11.7763 16.3438C11.5427 16.2016 11.2745 16.1263 11.001 16.1263C10.7275 16.1263 10.4593 16.2016 10.2257 16.3438L5.44444 19.2501L6.70632 13.7563C6.76834 13.4907 6.75676 13.2132 6.67285 12.9537C6.58893 12.6942 6.43585 12.4625 6.23007 12.2835L2.00007 8.59537C1.99973 8.59257 1.99973 8.58973 2.00007 8.58693L7.57257 8.10506C7.84463 8.08108 8.10499 7.98327 8.32555 7.82219C8.54611 7.6611 8.7185 7.44286 8.82413 7.191L11.0001 2.00756L13.1751 7.191C13.2807 7.44286 13.4531 7.6611 13.6737 7.82219C13.8942 7.98327 14.1546 8.08108 14.4266 8.10506L20.0001 8.58693C20.0001 8.58693 20.0001 8.59256 20.0001 8.59349L19.9982 8.58975Z" fill="currentColor"/> +</svg> diff --git a/core/modules/navigation/tests/navigation_test/navigation_test.icons.yml b/core/modules/navigation/tests/navigation_test/navigation_test.icons.yml new file mode 100644 index 0000000000000000000000000000000000000000..832c2ec5b777c47a8dd6fd2db526737f93af881c --- /dev/null +++ b/core/modules/navigation/tests/navigation_test/navigation_test.icons.yml @@ -0,0 +1,36 @@ +navigation_test: + enabled: true + label: "Drupal Navigation Test" + description: "Icons available within Drupal Navigation Test module." + version: 11.x + license: + name: GPL2-or-later + url: https://api.drupal.org/api/drupal/core%21LICENSE.txt/11.x + gpl-compatible: true + extractor: svg + config: + sources: + - assets/icons/*.svg + settings: + size: + title: "Size" + description: "Set a size for this icon." + type: "integer" + default: 20 + class: + title: "Class" + description: "Set a class for this icon." + type: "string" + default: "" + template: > + <svg + {{ attributes + .setAttribute('viewBox', attributes.viewBox|default('0 0 24 24')) + .setAttribute('class', class) + .setAttribute('width', size|default('20')) + .setAttribute('height', size|default('20')) + .setAttribute('aria-hidden', 'true') + }} + > + {{ content }} + </svg> diff --git a/core/modules/navigation/tests/navigation_test/navigation_test.links.menu.yml b/core/modules/navigation/tests/navigation_test/navigation_test.links.menu.yml new file mode 100644 index 0000000000000000000000000000000000000000..7c6eb33ded879d369f0a457e0f4efd6a69954b11 --- /dev/null +++ b/core/modules/navigation/tests/navigation_test/navigation_test.links.menu.yml @@ -0,0 +1,37 @@ +navigation_test.navigation__custom_item: + title: "Test Custom Icon" + weight: 0 + menu_name: admin + parent: system.admin + route_name: "<front>" + options: + icon: + pack_id: navigation_test + icon_id: star + settings: + class: 'toolbar-button__icon foo' + size: 25 + +navigation_test.navigation__default_item: + title: "Test Default Icon" + weight: 1 + menu_name: admin + parent: system.admin + route_name: "<front>" + options: + icon: + icon_id: pencil + +navigation_test.navigation__no_icon: + title: "Test No Icon" + weight: 2 + menu_name: admin + parent: system.admin + route_name: "<front>" + +navigation.media: + title: "Test Default Logic Icon" + weight: 3 + menu_name: admin + parent: system.admin + route_name: "<front>" diff --git a/core/modules/navigation/tests/navigation_test/src/Hook/NavigationTestHooks.php b/core/modules/navigation/tests/navigation_test/src/Hook/NavigationTestHooks.php index d0c4006e6662bb91aad30faf7177f17b1266f959..b61a694e80f44b7489dcdf8ad99e8d5913d45ecd 100644 --- a/core/modules/navigation/tests/navigation_test/src/Hook/NavigationTestHooks.php +++ b/core/modules/navigation/tests/navigation_test/src/Hook/NavigationTestHooks.php @@ -104,4 +104,21 @@ public function navigationMenuLinkTreeAlter(array &$tree): void { } } + /** + * Implements hook_menu_links_discovered_alter(). + */ + #[Hook('menu_links_discovered_alter')] + public function menuLinksDiscoveredAlter(array &$links): void { + if (\Drupal::keyValue('navigation_test')->get('menu_links_discovered_alter')) { + $links['navigation_test.navigation__no_icon']['options']['icon'] = [ + 'icon_id' => 'radioactive', + 'pack_id' => 'navigation_test', + ]; + $links['navigation_test.navigation__default_item']['options']['icon'] = [ + 'icon_id' => 'foo', + 'pack_id' => 'bar', + ]; + } + } + } diff --git a/core/modules/navigation/tests/navigation_test_block/src/Plugin/Block/NavigationTestBlock.php b/core/modules/navigation/tests/navigation_test_block/src/Plugin/Block/NavigationTestBlock.php index eb34725d1900e110eb54ce0ec02ba34366d0b064..05d1f1b55053f398b13e36e4a81ac9d92bce7dd1 100644 --- a/core/modules/navigation/tests/navigation_test_block/src/Plugin/Block/NavigationTestBlock.php +++ b/core/modules/navigation/tests/navigation_test_block/src/Plugin/Block/NavigationTestBlock.php @@ -35,6 +35,9 @@ public function build(): array { [ 'title' => 'Test Navigation Block', 'class' => 'test-block', + 'icon' => [ + 'icon_id' => 'test-block', + ], 'url' => Url::fromRoute('<front>'), ], ], diff --git a/core/modules/navigation/tests/src/Functional/NavigationIconTest.php b/core/modules/navigation/tests/src/Functional/NavigationIconTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e4cd508bb67bbd83bd8f85aede4741e95d6f620f --- /dev/null +++ b/core/modules/navigation/tests/src/Functional/NavigationIconTest.php @@ -0,0 +1,66 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\navigation\Functional; + +use Drupal\Core\Menu\MenuLinkManagerInterface; +use Drupal\Tests\BrowserTestBase; + +/** + * Tests Navigation Icon behavior. + * + * @group navigation + */ +class NavigationIconTest extends BrowserTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = ['navigation', 'navigation_test', 'test_page_test']; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->drupalLogin($this->createUser([ + 'access navigation', + ])); + } + + /** + * Tests the behavior of custom icons. + */ + public function testNavigationIcon(): void { + $this->drupalGet('/test-page'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--star svg', 'width', '25'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--star svg', 'class', 'toolbar-button__icon foo'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--pencil svg', 'width', '20'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--pencil svg', 'class', 'toolbar-button__icon'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--navigation-media svg', 'width', '20'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--navigation-media svg', 'class', 'toolbar-button__icon'); + $this->assertSession()->elementExists('css', 'a.toolbar-button--icon--navigation-test-navigation__no-icon'); + $this->assertSession()->elementNotExists('css', 'a.toolbar-button--icon--navigation-test-navigation__no-icon svg'); + + // Rebuild menu with alterations and reload the page to check them. + \Drupal::keyValue('navigation_test')->set('menu_links_discovered_alter', 1); + \Drupal::service(MenuLinkManagerInterface::class)->rebuild(); + + $this->drupalGet('/test-page'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--star svg', 'width', '25'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--star svg', 'class', 'toolbar-button__icon foo'); + $this->assertSession()->elementNotExists('css', 'a.toolbar-button--icon--pencil svg'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--navigation-media svg', 'width', '20'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--navigation-media svg', 'class', 'toolbar-button__icon'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--radioactive svg', 'width', '20'); + $this->assertSession()->elementAttributeContains('css', 'a.toolbar-button--icon--radioactive svg', 'class', 'toolbar-button__icon'); + } + +}