Explore: Move drilldown apps from Explore to a new navbar item "Drilldown" (#100409)

* Move drilldown apps from Explore to a new navbar item "Drilldown"

* Commit make i18n-extract

* Update drilldown icon

* Added alert to explore with call out to drilldown apps

* Add isNew field for nav item which shows a "New!" badge on the navbar and expands it by default

* Fix e2e test
This commit is contained in:
Andre Pereira
2025-02-20 17:56:55 +00:00
committed by GitHub
parent 2313eca636
commit e54149e551
18 changed files with 104 additions and 15 deletions
@@ -175,6 +175,7 @@ describe('Loki Query Editor', () => {
// One row with two cells
cy.get('[role="cell"]').should('have.length', 2);
cy.contains('label', 'targetLabelName').scrollIntoView();
cy.contains('label', 'targetLabelName').should('be.visible');
cy.contains('label', 'targetLabelName').click();
cy.contains('label', 'targetLabelName').within(() => {
+1
View File
@@ -93,6 +93,7 @@ export const availableIconsIndex = {
'document-layout-left': true,
'download-alt': true,
draggabledots: true,
drilldown: true,
edit: true,
'ellipsis-v': true,
enter: true,
@@ -14,6 +14,7 @@ export interface NavLinkDTO {
sortWeight?: number;
hideFromTabs?: boolean;
roundIcon?: boolean;
isNew?: boolean;
/**
* This is true for some sections that have no children (but is still a section)
**/
+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="49" viewBox="0 0 48 49" fill="currentColor">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.0044 5.21777C17.0044 3.56092 18.3476 2.21777 20.0044 2.21777H27.9148C29.5716 2.21777 30.9148 3.56092 30.9148 5.21777V9.12282L31.892 8.79869C32.8093 8.49441 33.7996 8.99139 34.1039 9.90874C34.4082 10.8261 33.9112 11.8164 32.9939 12.1207L30.9148 12.8103V19.2526L31.8924 18.9283C32.8098 18.6241 33.8001 19.121 34.1044 20.0384C34.4087 20.9557 33.9117 21.9461 32.9943 22.2503L30.9148 22.9401V28.9144C30.9148 29.4299 30.7819 29.9367 30.5291 30.3859L26.9845 36.6835C25.7386 38.9239 23.1565 38.741 21.8985 36.9123L17.5328 30.566C17.1887 30.0658 17.0044 29.4729 17.0044 28.8658V27.5542L16.0266 27.8785C15.1092 28.1828 14.1189 27.6858 13.8146 26.7685C13.5103 25.8511 14.0073 24.8608 14.9246 24.5565L17.0044 23.8667V17.4244L16.0261 17.7489C15.1087 18.0532 14.1184 17.5562 13.8141 16.6388C13.5098 15.7215 14.0068 14.7312 14.9242 14.4269L17.0044 13.7369V5.21777ZM21.0044 26.2274V28.3996C21.0044 28.5008 21.0351 28.5996 21.0925 28.683L23.8308 32.6636C24.0405 32.9684 24.497 32.9479 24.6785 32.6255L26.8505 28.7665C26.8926 28.6916 26.9148 28.6072 26.9148 28.5213V24.2669L21.0044 26.2274ZM26.9148 20.5794L21.0044 22.5399V16.0976L26.9148 14.1371V20.5794ZM26.9148 10.4496L21.0044 12.4101V6.71777C21.0044 6.44163 21.2283 6.21777 21.5044 6.21777H26.4148C26.6909 6.21777 26.9148 6.44163 26.9148 6.71777V10.4496Z"/>
<path d="M42.3721 30.8303C42.8016 29.9645 42.4479 28.9144 41.5821 28.4849C40.7163 28.0554 39.6662 28.4091 39.2367 29.2749L35.448 36.9123C35.0185 37.7782 36.3722 38.8282 37.238 39.2577C38.1038 39.6872 38.1539 39.3335 38.5834 38.4677L42.3721 30.8303Z"/>
<path d="M8.75767 29.296C8.32292 28.4328 7.27072 28.0855 6.40752 28.5202C5.54433 28.955 5.19701 30.0072 5.63176 30.8704L9.46537 38.482C9.90012 39.3452 10.9523 39.6925 11.8155 39.2577C12.6787 38.823 13.026 37.7708 12.5913 36.9076L8.75767 29.296Z"/>
<path d="M10.3193 45.0314C10.3193 44.0649 11.1028 43.2814 12.0693 43.2814H36.1498C37.1163 43.2814 37.8998 44.0649 37.8998 45.0314C37.8998 45.9979 37.1163 46.7814 36.1498 46.7814H12.0693C11.1028 46.7814 10.3193 45.9979 10.3193 45.0314Z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+4 -1
View File
@@ -60,12 +60,15 @@ func (hs *HTTPServer) declareFixedRoles() error {
Role: ac.RoleDTO{
Name: "fixed:datasources:explorer",
DisplayName: "Explorer",
Description: "Enable the Explore feature. Data source permissions still apply; you can only query data sources for which you have query permissions.",
Description: "Enable the Explore and Drilldown features. Data source permissions still apply; you can only query data sources for which you have query permissions.",
Group: "Data sources",
Permissions: []ac.Permission{
{
Action: ac.ActionDatasourcesExplore,
},
{
Action: ac.ActionDatasourcesDrilldown,
},
},
},
Grants: []string{string(org.RoleEditor)},
+1
View File
@@ -183,6 +183,7 @@ func (hs *HTTPServer) registerRoutes() {
}
r.Get("/explore", authorize(ac.EvalPermission(ac.ActionDatasourcesExplore)), hs.Index)
r.Get("/drilldown", authorize(ac.EvalPermission(ac.ActionDatasourcesDrilldown)), hs.Index)
r.Get("/playlists/", reqSignedIn, hs.Index)
r.Get("/playlists/*", reqSignedIn, hs.Index)
+2 -1
View File
@@ -386,7 +386,8 @@ const (
ActionSettingsWrite = "settings:write"
// Datasources actions
ActionDatasourcesExplore = "datasources:explore"
ActionDatasourcesExplore = "datasources:explore"
ActionDatasourcesDrilldown = "datasources:drilldown"
// Global Scopes
ScopeGlobalUsersAll = "global.users:*"
+3
View File
@@ -16,6 +16,7 @@ const (
WeightSavedItems
WeightDashboard
WeightExplore
WeightDrilldown
WeightAlerting
WeightAlertsAndIncidents
WeightTestingAndSynthetics
@@ -37,6 +38,7 @@ const (
NavIDRoot = "root"
NavIDDashboards = "dashboards/browse"
NavIDExplore = "explore"
NavIDDrilldown = "drilldown"
NavIDCfg = "cfg" // NavIDCfg is the id for org configuration navigation node
NavIDAlertsAndIncidents = "alerts-and-incidents"
NavIDTestingAndSynthetics = "testing-and-synthetics"
@@ -70,6 +72,7 @@ type NavLink struct {
EmptyMessageId string `json:"emptyMessageId,omitempty"`
PluginID string `json:"pluginId,omitempty"` // (Optional) The ID of the plugin that registered nav link (e.g. as a standalone plugin page)
IsCreateAction bool `json:"isCreateAction,omitempty"`
IsNew bool `json:"isNew,omitempty"` // (Optional) Adds "New!" badge to the nav link and expands it by default
Keywords []string `json:"keywords,omitempty"`
ParentItem *NavLink `json:"parentItem,omitempty"` // (Optional) The parent item of the nav link
}
+4 -4
View File
@@ -286,9 +286,9 @@ func (s *ServiceImpl) readNavigationSettings() {
"grafana-k8s-app": {SectionID: navtree.NavIDInfrastructure, SortWeight: 1, Text: "Kubernetes"},
"grafana-dbo11y-app": {SectionID: navtree.NavIDInfrastructure, SortWeight: 2, Text: "Databases"},
"grafana-app-observability-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightApplication, Text: "Application", Icon: "graph-bar"},
"grafana-lokiexplore-app": {SectionID: navtree.NavIDExplore, SortWeight: 2, Text: "Logs"},
"grafana-exploretraces-app": {SectionID: navtree.NavIDExplore, SortWeight: 3, Text: "Traces"},
"grafana-pyroscope-app": {SectionID: navtree.NavIDExplore, SortWeight: 4, Text: "Profiles"},
"grafana-lokiexplore-app": {SectionID: navtree.NavIDDrilldown, SortWeight: 2, Text: "Logs"},
"grafana-exploretraces-app": {SectionID: navtree.NavIDDrilldown, SortWeight: 3, Text: "Traces"},
"grafana-pyroscope-app": {SectionID: navtree.NavIDDrilldown, SortWeight: 4, Text: "Profiles"},
"grafana-kowalski-app": {SectionID: navtree.NavIDRoot, SortWeight: navtree.WeightFrontend, Text: "Frontend", Icon: "frontend-observability"},
"grafana-synthetic-monitoring-app": {SectionID: navtree.NavIDTestingAndSynthetics, SortWeight: 2, Text: "Synthetics"},
"grafana-irm-app": {SectionID: navtree.NavIDAlertsAndIncidents, SortWeight: 1, Text: "IRM"},
@@ -310,7 +310,7 @@ func (s *ServiceImpl) readNavigationSettings() {
}
if s.features.IsEnabledGlobally(featuremgmt.FlagExploreMetricsUseExternalAppPlugin) {
s.navigationAppConfig["grafana-metricsdrilldown-app"] = NavigationAppConfig{SectionID: navtree.NavIDExplore, SortWeight: 1, Text: "Metrics"}
s.navigationAppConfig["grafana-metricsdrilldown-app"] = NavigationAppConfig{SectionID: navtree.NavIDDrilldown, SortWeight: 1, Text: "Metrics"}
}
if s.features.IsEnabledGlobally(featuremgmt.FlagGrafanaAdvisor) {
+18 -6
View File
@@ -120,7 +120,6 @@ func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, prefs *pref.Prefere
}
if s.cfg.ExploreEnabled && hasAccess(ac.EvalPermission(ac.ActionDatasourcesExplore)) {
exploreChildNavLinks := s.buildExploreNavLinks(c)
treeRoot.AddSection(&navtree.NavLink{
Text: "Explore",
Id: navtree.NavIDExplore,
@@ -128,7 +127,20 @@ func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, prefs *pref.Prefere
Icon: "compass",
SortWeight: navtree.WeightExplore,
Url: s.cfg.AppSubURL + "/explore",
Children: exploreChildNavLinks,
})
}
if hasAccess(ac.EvalPermission(ac.ActionDatasourcesDrilldown)) {
drilldownChildNavLinks := s.buildDrilldownNavLinks(c)
treeRoot.AddSection(&navtree.NavLink{
Text: "Drilldown",
Id: navtree.NavIDDrilldown,
SubTitle: "Drill down into your data using Grafana's powerful queryless apps",
Icon: "drilldown",
IsNew: true,
SortWeight: navtree.WeightDrilldown,
Url: s.cfg.AppSubURL + "/drilldown",
Children: drilldownChildNavLinks,
})
}
@@ -560,10 +572,10 @@ func (s *ServiceImpl) buildDataConnectionsNavLink(c *contextmodel.ReqContext) *n
return nil
}
func (s *ServiceImpl) buildExploreNavLinks(c *contextmodel.ReqContext) []*navtree.NavLink {
exploreChildNavs := []*navtree.NavLink{}
func (s *ServiceImpl) buildDrilldownNavLinks(c *contextmodel.ReqContext) []*navtree.NavLink {
drilldownChildNavs := []*navtree.NavLink{}
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagExploreMetrics) && !s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagExploreMetricsUseExternalAppPlugin) {
exploreChildNavs = append(exploreChildNavs, &navtree.NavLink{
drilldownChildNavs = append(drilldownChildNavs, &navtree.NavLink{
Text: "Metrics",
SubTitle: "Queryless exploration of your metrics",
Id: "explore/metrics",
@@ -571,5 +583,5 @@ func (s *ServiceImpl) buildExploreNavLinks(c *contextmodel.ReqContext) []*navtre
Icon: "code-branch",
})
}
return exploreChildNavs
return drilldownChildNavs
}
@@ -5,8 +5,9 @@ import { useLocation } from 'react-router-dom-v5-compat';
import { useLocalStorage } from 'react-use';
import { GrafanaTheme2, NavModelItem, toIconName } from '@grafana/data';
import { useStyles2, Text, IconButton, Icon, Stack } from '@grafana/ui';
import { useStyles2, Text, IconButton, Icon, Stack, Badge } from '@grafana/ui';
import { useGrafana } from 'app/core/context/GrafanaContext';
import { t } from 'app/core/internationalization';
import { Indent } from '../../Indent/Indent';
@@ -35,7 +36,7 @@ export function MegaMenuItem({ link, activeItem, level = 0, onClick, onPin, isPi
const isActive = link === activeItem || (level === MAX_DEPTH && hasActiveChild);
const [sectionExpanded, setSectionExpanded] = useLocalStorage(
`grafana.navigation.expanded[${link.text}]`,
Boolean(hasActiveChild)
Boolean(hasActiveChild || link.isNew)
);
const showExpandButton = level < MAX_DEPTH && Boolean(linkHasChildren(link) || link.emptyMessage);
const item = useRef<HTMLLIElement>(null);
@@ -107,6 +108,7 @@ export function MegaMenuItem({ link, activeItem, level = 0, onClick, onPin, isPi
>
{level === 0 && iconElement && <FeatureHighlightWrapper>{iconElement}</FeatureHighlightWrapper>}
<Text truncate>{link.text}</Text>
{link.isNew && <Badge text={t('navigation.megamenu.new', 'New!')} color={'blue'} />}
</div>
</MegaMenuItemText>
</div>
@@ -114,7 +114,7 @@ const getStyles = (theme: GrafanaTheme2, isActive: Props['isActive']) => ({
position: 'relative',
width: '100%',
'&:hover, &:focus-visible': {
'&:hover span, &:focus-visible span': {
color: theme.colors.text.primary,
textDecoration: 'underline',
},
@@ -61,6 +61,8 @@ export function getNavTitle(navId: string | undefined) {
return t('nav.scenes.title', 'Scenes');
case 'explore':
return t('nav.explore.title', 'Explore');
case 'drilldown':
return t('nav.drilldown.title', 'Drilldown');
case 'alerting':
return t('nav.alerting.title', 'Alerting');
case 'plugin-page-grafana-oncall-app':
+28
View File
@@ -23,14 +23,18 @@ import { getDataSourceSrv, reportInteraction } from '@grafana/runtime';
import { DataQuery } from '@grafana/schema';
import {
AdHocFilterItem,
Alert,
ErrorBoundaryAlert,
LinkButton,
PanelContainer,
ScrollContainer,
Stack,
Themeable2,
withTheme2,
} from '@grafana/ui';
import { FILTER_FOR_OPERATOR, FILTER_OUT_OPERATOR } from '@grafana/ui/src/components/Table/types';
import { supportedFeatures } from 'app/core/history/richHistoryStorageProvider';
import { t, Trans } from 'app/core/internationalization';
import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource';
import { StoreState } from 'app/types';
@@ -564,6 +568,9 @@ export class Explore extends PureComponent<Props, ExploreState> {
if (showCorrelationHelper && correlationEditorHelperData !== undefined) {
correlationsBox = <CorrelationHelper exploreId={exploreId} correlations={correlationEditorHelperData} />;
}
const isDsCompatibleWithDrilldown = ['prometheus', 'loki', 'tempo', 'grafana-pyroscope-datasource'].includes(
datasourceInstance?.type || ''
);
return (
<ContentOutlineContextProvider refreshDependencies={this.props.queries}>
@@ -593,6 +600,27 @@ export class Explore extends PureComponent<Props, ExploreState> {
<>
<ContentOutlineItem panelId="Queries" title="Queries" icon="arrow" mergeSingleChild={true}>
<PanelContainer className={styles.queryContainer}>
{isDsCompatibleWithDrilldown && (
<Alert
severity={'info'}
title={t(
'explore.drilldownInfo.title',
'Explore Metrics, Logs, Traces and Profiles have moved!'
)}
>
<Stack gap={1} alignItems="flex-end" justifyContent={'space-between'}>
<span>
<Trans i18nKey={'explore.drilldownInfo.description'}>
Looking for the Grafana Explore apps? They are now called the Grafana Drilldown apps
and can be found under <b>Menu &gt; Drilldown</b>
</Trans>
</span>
<LinkButton variant={'secondary'} href="/drilldown">
<Trans i18nKey={'explore.drilldownInfo.action'}>Go to Grafana Drilldown</Trans>
</LinkButton>
</Stack>
</Alert>
)}
{correlationsBox}
<QueryRows exploreId={exploreId} />
<SecondaryActions
+4
View File
@@ -162,6 +162,10 @@ export function getAppRoutes(): RouteDescriptor[] {
: import(/* webpackChunkName: "explore-feature-toggle-page" */ 'app/features/explore/FeatureTogglePage')
),
},
{
path: '/drilldown',
component: () => <NavLandingPage navId="drilldown" />,
},
{
path: '/apps',
component: () => <NavLandingPage navId="apps" />,
+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="49" viewBox="0 0 48 49" fill="currentColor">
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.0044 5.21777C17.0044 3.56092 18.3476 2.21777 20.0044 2.21777H27.9148C29.5716 2.21777 30.9148 3.56092 30.9148 5.21777V9.12282L31.892 8.79869C32.8093 8.49441 33.7996 8.99139 34.1039 9.90874C34.4082 10.8261 33.9112 11.8164 32.9939 12.1207L30.9148 12.8103V19.2526L31.8924 18.9283C32.8098 18.6241 33.8001 19.121 34.1044 20.0384C34.4087 20.9557 33.9117 21.9461 32.9943 22.2503L30.9148 22.9401V28.9144C30.9148 29.4299 30.7819 29.9367 30.5291 30.3859L26.9845 36.6835C25.7386 38.9239 23.1565 38.741 21.8985 36.9123L17.5328 30.566C17.1887 30.0658 17.0044 29.4729 17.0044 28.8658V27.5542L16.0266 27.8785C15.1092 28.1828 14.1189 27.6858 13.8146 26.7685C13.5103 25.8511 14.0073 24.8608 14.9246 24.5565L17.0044 23.8667V17.4244L16.0261 17.7489C15.1087 18.0532 14.1184 17.5562 13.8141 16.6388C13.5098 15.7215 14.0068 14.7312 14.9242 14.4269L17.0044 13.7369V5.21777ZM21.0044 26.2274V28.3996C21.0044 28.5008 21.0351 28.5996 21.0925 28.683L23.8308 32.6636C24.0405 32.9684 24.497 32.9479 24.6785 32.6255L26.8505 28.7665C26.8926 28.6916 26.9148 28.6072 26.9148 28.5213V24.2669L21.0044 26.2274ZM26.9148 20.5794L21.0044 22.5399V16.0976L26.9148 14.1371V20.5794ZM26.9148 10.4496L21.0044 12.4101V6.71777C21.0044 6.44163 21.2283 6.21777 21.5044 6.21777H26.4148C26.6909 6.21777 26.9148 6.44163 26.9148 6.71777V10.4496Z"/>
<path d="M42.3721 30.8303C42.8016 29.9645 42.4479 28.9144 41.5821 28.4849C40.7163 28.0554 39.6662 28.4091 39.2367 29.2749L35.448 36.9123C35.0185 37.7782 36.3722 38.8282 37.238 39.2577C38.1038 39.6872 38.1539 39.3335 38.5834 38.4677L42.3721 30.8303Z"/>
<path d="M8.75767 29.296C8.32292 28.4328 7.27072 28.0855 6.40752 28.5202C5.54433 28.955 5.19701 30.0072 5.63176 30.8704L9.46537 38.482C9.90012 39.3452 10.9523 39.6925 11.8155 39.2577C12.6787 38.823 13.026 37.7708 12.5913 36.9076L8.75767 29.296Z"/>
<path d="M10.3193 45.0314C10.3193 44.0649 11.1028 43.2814 12.0693 43.2814H36.1498C37.1163 43.2814 37.8998 44.0649 37.8998 45.0314C37.8998 45.9979 37.1163 46.7814 36.1498 46.7814H12.0693C11.1028 46.7814 10.3193 45.9979 10.3193 45.0314Z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+9
View File
@@ -1419,6 +1419,11 @@
},
"explore": {
"add-to-dashboard": "Add to dashboard",
"drilldownInfo": {
"action": "Go to Grafana Drilldown",
"description": "Looking for the Grafana Explore apps? They are now called the Grafana Drilldown apps and can be found under <1>Menu > Drilldown</1>",
"title": "Explore Metrics, Logs, Traces and Profiles have moved!"
},
"logs": {
"logs-volume": {
"add-filters": "Add more labels to your query to narrow down your search.",
@@ -2499,6 +2504,9 @@
"detect": {
"title": "Detect"
},
"drilldown": {
"title": "Drilldown"
},
"explore": {
"title": "Explore"
},
@@ -2714,6 +2722,7 @@
"close": "Close menu",
"dock": "Dock menu",
"list-label": "Navigation",
"new": "New!",
"open": "Open menu",
"undock": "Undock menu"
},
@@ -1419,6 +1419,11 @@
},
"explore": {
"add-to-dashboard": "Åđđ ŧő đäşĥþőäřđ",
"drilldownInfo": {
"action": "Ğő ŧő Ğřäƒäʼnä Đřįľľđőŵʼn",
"description": "Ŀőőĸįʼnģ ƒőř ŧĥę Ğřäƒäʼnä Ēχpľőřę äppş? Ŧĥęy äřę ʼnőŵ čäľľęđ ŧĥę Ğřäƒäʼnä Đřįľľđőŵʼn äppş äʼnđ čäʼn þę ƒőūʼnđ ūʼnđęř <1>Męʼnū > Đřįľľđőŵʼn</1>",
"title": "Ēχpľőřę Męŧřįčş, Ŀőģş, Ŧřäčęş äʼnđ Přőƒįľęş ĥävę mővęđ!"
},
"logs": {
"logs-volume": {
"add-filters": "Åđđ mőřę ľäþęľş ŧő yőūř qūęřy ŧő ʼnäřřőŵ đőŵʼn yőūř şęäřčĥ.",
@@ -2499,6 +2504,9 @@
"detect": {
"title": "Đęŧęčŧ"
},
"drilldown": {
"title": "Đřįľľđőŵʼn"
},
"explore": {
"title": "Ēχpľőřę"
},
@@ -2714,6 +2722,7 @@
"close": "Cľőşę męʼnū",
"dock": "Đőčĸ męʼnū",
"list-label": "Ńävįģäŧįőʼn",
"new": "Ńęŵ!",
"open": "Øpęʼn męʼnū",
"undock": "Ůʼnđőčĸ męʼnū"
},