From ccb2e5d6aa1b6bc9ffe62a0bd1e1494a4c38a0bb Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Fri, 2 Oct 2026 09:24:21 -0500 Subject: [PATCH 1/2] Render plugin nav icons supplied as a file path --- src/Cp/Navigation.php | 46 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/Cp/Navigation.php b/src/Cp/Navigation.php index a2cbbb9e933..c48ac74373f 100644 --- a/src/Cp/Navigation.php +++ b/src/Cp/Navigation.php @@ -533,8 +533,8 @@ private function settingNavItem(string $handle, array $setting): NavItem } /** - * Gives an item its id and an absolute URL, and does the same for anything - * beneath it. + * Gives an item its id and an absolute URL, renders an icon it can't name, + * and does the same for anything beneath it. */ private function normalize(NavItem $item): NavItem { @@ -544,6 +544,8 @@ private function normalize(NavItem $item): NavItem $item->href = Url::url($item->href); } + $this->resolveIcon($item); + if (is_array($item->subnav)) { $item->subnav = array_map( $this->normalize(...), @@ -554,6 +556,46 @@ private function normalize(NavItem $item): NavItem return $item; } + /** + * Renders an icon the control panel can't look up by name. + * + * The nav draws a named icon through `craft-nav-item`'s `icon` attribute + * and anything else as inline markup, so those are the only two shapes it + * understands. A plugin supplies neither: `cpNavIconPath()` returns the + * path to its own `icon-mask.svg`, which went into `icon` and was then + * looked up as though it were a system icon's name — so no plugin's icon + * ever appeared. + * + * Rendering it here covers plugins written for Craft 6 and those coming + * through the Yii adapter alike, since both arrive as a path in the same + * field, and neither has to change. + * + * Deliberately narrow: only a value naming an SVG file is rendered, so the + * named icons the rest of the nav uses stay names. Inlining every icon + * would weigh down a tree that is cached and shared across every control + * panel page. + */ + private function resolveIcon(NavItem $item): void + { + if ($item->iconSvg !== null || $item->icon === null) { + return; + } + + if (! str_ends_with(strtolower($item->icon), '.svg')) { + return; + } + + $svg = Icons::svg($item->icon); + + // `Icons::svg()` logs and returns an empty string for anything it + // can't read, so a missing or unreadable file leaves the item as it + // was rather than giving it a blank icon. + if ($svg !== null && $svg !== '') { + $item->iconSvg = $svg; + $item->icon = null; + } + } + /** The stable id an item is known by, in the tree and in the badge map. */ private function itemId(string $href): string { From 3cfe11c7449cee1cfca59f6f12e2d1c27706efc3 Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Fri, 2 Oct 2026 09:25:21 -0500 Subject: [PATCH 2/2] Reference plugin nav icons PR in changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 447d42c7a2a..cd1bd543c2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Brought back users’ gradient-based default avatars. - Fixed a bug where users’ breadcrumb chips weren’t getting hyperlinked. +- Fixed a bug where plugins’ control panel nav items weren’t showing their icons. ([#19794](https://github.com/craftcms/cms/pull/19794)) - Added `CraftCms\Cms\Element\Events\ElementActionMenuDescriptorsResolving`, allowing plugins to add action menu items to the Inertia element editor and element chips. ([#19783](https://github.com/craftcms/cms/pull/19783)) - Action menu items added via `craft\base\Element::EVENT_DEFINE_ACTION_MENU_ITEMS`, or by plugin element types’ `safeActionMenuItems()` and `destructiveActionMenuItems()` overrides, are now shown in the Inertia element editor and element chips through the Yii adapter, if they define a `url` or `action`. Items that rely on JavaScript are left out, and deprecation warnings point to the code to update. ([#19783](https://github.com/craftcms/cms/pull/19783)) - The global `axios` is now only loaded in the control panel when `craftcms/yii2-adapter` is installed. `Craft.sendActionRequest()` or `actionClient` from `@craftcms/ui` should be used instead.