From 3b99370aac66ab1cd1b182cf6a5a0450bf2ae867 Mon Sep 17 00:00:00 2001
From: Tobias Skarhed <1438972+tskarhed@users.noreply.github.com>
Date: Mon, 3 Nov 2025 13:09:58 +0100
Subject: [PATCH] Scopes: Fix icon lookup for scope navigation (#113313)
Fix icon lookup for scope navigation
---
.../ScopesNavigationTreeLink.test.tsx | 28 ++++++++++++++++
.../dashboards/ScopesNavigationTreeLink.tsx | 33 +++++++++++--------
2 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/public/app/features/scopes/dashboards/ScopesNavigationTreeLink.test.tsx b/public/app/features/scopes/dashboards/ScopesNavigationTreeLink.test.tsx
index b8775a3c21b..3aba827d4ef 100644
--- a/public/app/features/scopes/dashboards/ScopesNavigationTreeLink.test.tsx
+++ b/public/app/features/scopes/dashboards/ScopesNavigationTreeLink.test.tsx
@@ -105,4 +105,32 @@ describe('ScopesNavigationTreeLink', () => {
expect(matchingLink).toHaveAttribute('aria-current', 'page');
expect(otherLink).not.toHaveAttribute('aria-current');
});
+
+ it('shows correct icon for grafana-metricsdrilldown-app with query parameters', () => {
+ renderWithRouter(
+
+ );
+
+ const link = screen.getByTestId('scopes-dashboards-metrics-drilldown');
+ // Icon should be rendered (check for SVG element which is how Icon renders)
+ const icon = link.querySelector('svg');
+ expect(icon).toBeInTheDocument();
+ // The link should contain the title text
+ expect(link).toHaveTextContent('Metrics Drilldown');
+ });
+
+ it('shows correct icon for grafana-metricsdrilldown-app without trailing slash', () => {
+ renderWithRouter(
+
+ );
+
+ const link = screen.getByTestId('scopes-dashboards-metrics-drilldown');
+ const icon = link.querySelector('svg');
+ expect(icon).toBeInTheDocument();
+ expect(link).toHaveTextContent('Metrics Drilldown');
+ });
});
diff --git a/public/app/features/scopes/dashboards/ScopesNavigationTreeLink.tsx b/public/app/features/scopes/dashboards/ScopesNavigationTreeLink.tsx
index 41481ca0446..859b6059059 100644
--- a/public/app/features/scopes/dashboards/ScopesNavigationTreeLink.tsx
+++ b/public/app/features/scopes/dashboards/ScopesNavigationTreeLink.tsx
@@ -5,7 +5,7 @@ import { Link, useLocation } from 'react-router-dom-v5-compat';
import { GrafanaTheme2, IconName, locationUtil } from '@grafana/data';
import { Icon, useStyles2 } from '@grafana/ui';
-import { isCurrentPath } from './scopeNavgiationUtils';
+import { isCurrentPath, normalizePath } from './scopeNavgiationUtils';
export interface ScopesNavigationTreeLinkProps {
to: string;
@@ -36,25 +36,30 @@ export function ScopesNavigationTreeLink({ to, title, id }: ScopesNavigationTree
}
function getLinkIcon(to: string) {
- // Strip base URL and normalize path
- const normalizedPath = locationUtil.stripBaseFromUrl(to);
- for (const [key, value] of linkMap.entries()) {
- if (normalizedPath.startsWith(key)) {
- return value;
- }
+ // Check for external links before stripping base (stripBaseFromUrl removes http:// for same-origin URLs)
+ if (to.startsWith('http')) {
+ return 'external-link-alt';
}
- return 'link';
+ // Strip base URL and normalize path (remove query params and hash)
+ const baseStripped = locationUtil.stripBaseFromUrl(to);
+ const normalizedPath = normalizePath(baseStripped);
+
+ // Check for dashboard paths with startsWith (e.g., /d/dashboard-id)
+ if (normalizedPath.startsWith('/d')) {
+ return 'apps';
+ }
+
+ // Use direct Map lookup for exact path matches
+ return linkMap.get(normalizedPath) ?? 'link';
}
const linkMap = new Map([
- ['http', 'external-link-alt'],
- ['/d', 'apps'],
['/explore/metrics', 'drilldown'],
- ['/a/grafana-metricsdrilldown-app/', 'drilldown'],
- ['/a/grafana-lokiexplore-app/', 'drilldown'],
- ['/a/grafana-exploretraces-app/', 'drilldown'],
- ['/a/grafana-pyroscope-app/', 'drilldown'],
+ ['/a/grafana-metricsdrilldown-app', 'drilldown'],
+ ['/a/grafana-lokiexplore-app', 'drilldown'],
+ ['/a/grafana-exploretraces-app', 'drilldown'],
+ ['/a/grafana-pyroscope-app', 'drilldown'],
]);
const getStyles = (theme: GrafanaTheme2) => {