From 661cd58bae33bb5ac0ad082274d967cf08384ff4 Mon Sep 17 00:00:00 2001 From: Alejandro Fraenkel Date: Mon, 12 Jan 2026 21:12:32 +0100 Subject: [PATCH] feat(alerting): remove alertingNavigationV2 feature flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the alertingNavigationV2 feature toggle and make V2 navigation the only option. Changes: - Remove feature flag from registry.go and regenerate toggle files - Remove shouldUseAlertingNavigationV2() function from featureToggles.ts - Clean up all navigation hooks (useNotificationConfigNav, useInsightsNav, useAlertRulesNav, useAlertActivityNav) - Remove legacy navigation logic from page components (TimeIntervalsPage, Templates) - Update MegaMenu to always use flattened alerting sidebar navigation - Simplify all test files by removing feature flag mocking and legacy test cases Net result: ~500 lines of code removed, simpler codebase with single navigation implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../src/types/featureToggles.gen.ts | 5 -- pkg/services/featuremgmt/registry.go | 8 -- pkg/services/featuremgmt/toggles_gen.csv | 1 - pkg/services/featuremgmt/toggles_gen.go | 4 - pkg/services/featuremgmt/toggles_gen.json | 3 +- .../AppChrome/MegaMenu/MegaMenu.tsx | 8 +- .../unified/NotificationPoliciesPage.test.tsx | 46 +++++----- .../features/alerting/unified/Templates.tsx | 23 ++--- .../unified/TimeIntervalsPage.test.tsx | 90 ++++++++----------- .../alerting/unified/TimeIntervalsPage.tsx | 19 ++-- .../contact-points/ContactPoints.test.tsx | 30 +++---- .../alerting/unified/featureToggles.ts | 5 -- .../navigation/useAlertActivityNav.test.tsx | 63 ++----------- .../unified/navigation/useAlertActivityNav.ts | 36 -------- .../navigation/useAlertRulesNav.test.tsx | 30 +------ .../unified/navigation/useAlertRulesNav.ts | 14 +-- .../navigation/useInsightsNav.test.tsx | 30 +------ .../unified/navigation/useInsightsNav.ts | 25 ------ .../useNotificationConfigNav.test.tsx | 39 +------- .../navigation/useNotificationConfigNav.ts | 30 +------ 20 files changed, 100 insertions(+), 409 deletions(-) diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 7d6174ecc81..5d9ad02dbc7 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -547,11 +547,6 @@ export interface FeatureToggles { */ alertingCentralAlertHistory?: boolean; /** - * Enable new grouped navigation structure for Alerting - * @default false - */ - alertingNavigationV2?: boolean; - /** * Preserve plugin proxy trailing slash. * @default false */ diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 094d8fefed0..5ec4bfb880b 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -907,14 +907,6 @@ var ( Owner: grafanaAlertingSquad, FrontendOnly: false, // changes navtree from backend }, - { - Name: "alertingNavigationV2", - Description: "Enable new grouped navigation structure for Alerting", - Stage: FeatureStageExperimental, - Owner: grafanaAlertingSquad, - FrontendOnly: false, // changes navtree from backend - Expression: "false", // Off by default - }, { Name: "pluginProxyPreserveTrailingSlash", Description: "Preserve plugin proxy trailing slash.", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 175258423bf..20009d3f30b 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -125,7 +125,6 @@ alertingSavedSearches,experimental,@grafana/alerting-squad,false,false,true alertingDisableSendAlertsExternal,experimental,@grafana/alerting-squad,false,false,false preserveDashboardStateWhenNavigating,experimental,@grafana/dashboards-squad,false,false,false alertingCentralAlertHistory,experimental,@grafana/alerting-squad,false,false,false -alertingNavigationV2,experimental,@grafana/alerting-squad,false,false,false pluginProxyPreserveTrailingSlash,GA,@grafana/plugins-platform-backend,false,false,false azureMonitorPrometheusExemplars,GA,@grafana/partner-datasources,false,false,false authZGRPCServer,experimental,@grafana/identity-access-team,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index f66898d31b6..062748b95df 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -379,10 +379,6 @@ const ( // Enables the new central alert history. FlagAlertingCentralAlertHistory = "alertingCentralAlertHistory" - // FlagAlertingNavigationV2 - // Enable new grouped navigation structure for Alerting - FlagAlertingNavigationV2 = "alertingNavigationV2" - // FlagPluginProxyPreserveTrailingSlash // Preserve plugin proxy trailing slash. FlagPluginProxyPreserveTrailingSlash = "pluginProxyPreserveTrailingSlash" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 13c46e3b514..2b89aa7b4be 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -352,7 +352,8 @@ "metadata": { "name": "alertingNavigationV2", "resourceVersion": "1767827323622", - "creationTimestamp": "2026-01-07T23:08:43Z" + "creationTimestamp": "2026-01-07T23:08:43Z", + "deletionTimestamp": "2026-01-12T18:34:54Z" }, "spec": { "description": "Enable new grouped navigation structure for Alerting", diff --git a/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx b/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx index e1b69963d67..713e55e4b94 100644 --- a/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx +++ b/public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx @@ -11,7 +11,6 @@ import { reportInteraction } from '@grafana/runtime'; import { ScrollContainer, useStyles2 } from '@grafana/ui'; import { useGrafana } from 'app/core/context/GrafanaContext'; import { setBookmark } from 'app/core/reducers/navBarTree'; -import { shouldUseAlertingNavigationV2 } from 'app/features/alerting/unified/featureToggles'; import { useDispatch, useSelector } from 'app/types/store'; import { MegaMenuExtensionPoint } from './MegaMenuExtensionPoint'; @@ -38,15 +37,14 @@ export const MegaMenu = memo( const pinnedItems = usePinnedItems(); // Remove profile + help from tree - // For Alerting V2 navigation, flatten the sidebar to show only top-level items (hide nested children/tabs) - const useV2Nav = shouldUseAlertingNavigationV2(); + // For Alerting navigation, flatten the sidebar to show only top-level items (hide nested children/tabs) const navItems = navTree .filter((item) => item.id !== 'profile' && item.id !== 'help') .map((item) => { const enriched = enrichWithInteractionTracking(item, state.megaMenuDocked); - // If this is Alerting section and V2 navigation is enabled, flatten children for sidebar display + // If this is Alerting section, flatten children for sidebar display // Children are still available in navIndex for breadcrumbs and page navigation - if (useV2Nav && item.id === 'alerting' && enriched.children) { + if (item.id === 'alerting' && enriched.children) { return { ...enriched, children: enriched.children.map((child) => ({ diff --git a/public/app/features/alerting/unified/NotificationPoliciesPage.test.tsx b/public/app/features/alerting/unified/NotificationPoliciesPage.test.tsx index e29be82b38b..dc948d1a6ff 100644 --- a/public/app/features/alerting/unified/NotificationPoliciesPage.test.tsx +++ b/public/app/features/alerting/unified/NotificationPoliciesPage.test.tsx @@ -140,37 +140,33 @@ const getRootRoute = async () => { }; describe('NotificationPolicies', () => { - describe('V2 Navigation Mode', () => { - testWithFeatureToggles({ enable: ['alertingNavigationV2'] }); + beforeEach(() => { + setupDataSources(dataSources.am); + grantUserPermissions([ + AccessControlAction.AlertingNotificationsRead, + AccessControlAction.AlertingNotificationsWrite, + ...PERMISSIONS_NOTIFICATION_POLICIES, + ]); + }); - beforeEach(() => { - setupDataSources(dataSources.am); - grantUserPermissions([ - AccessControlAction.AlertingNotificationsRead, - AccessControlAction.AlertingNotificationsWrite, - ...PERMISSIONS_NOTIFICATION_POLICIES, - ]); - }); + it('shows only notification policies without internal tabs', async () => { + renderNotificationPolicies(); - it('shows only notification policies without internal tabs', async () => { - renderNotificationPolicies(); + // Should show notification policies directly + expect(await ui.rootRouteContainer.find()).toBeInTheDocument(); - // Should show notification policies directly - expect(await ui.rootRouteContainer.find()).toBeInTheDocument(); + // Should not have tabs + expect(screen.queryByRole('tab')).not.toBeInTheDocument(); + }); - // Should not have tabs - expect(screen.queryByRole('tab')).not.toBeInTheDocument(); - }); + it('does not show time intervals tab', async () => { + renderNotificationPolicies(); - it('does not show time intervals tab in V2 mode', async () => { - renderNotificationPolicies(); + // Should show notification policies + expect(await ui.rootRouteContainer.find()).toBeInTheDocument(); - // Should show notification policies - expect(await ui.rootRouteContainer.find()).toBeInTheDocument(); - - // Should not show time intervals tab - expect(screen.queryByText(/time intervals/i)).not.toBeInTheDocument(); - }); + // Should not show time intervals tab + expect(screen.queryByText(/time intervals/i)).not.toBeInTheDocument(); }); // combobox hack :/ diff --git a/public/app/features/alerting/unified/Templates.tsx b/public/app/features/alerting/unified/Templates.tsx index 569cc430e22..86ae83f4759 100644 --- a/public/app/features/alerting/unified/Templates.tsx +++ b/public/app/features/alerting/unified/Templates.tsx @@ -8,7 +8,6 @@ import DuplicateMessageTemplate from './components/contact-points/DuplicateMessa import EditMessageTemplate from './components/contact-points/EditMessageTemplate'; import NewMessageTemplate from './components/contact-points/NewMessageTemplate'; import { NotificationTemplates } from './components/contact-points/NotificationTemplates'; -import { shouldUseAlertingNavigationV2 } from './featureToggles'; import { AlertmanagerAction, useAlertmanagerAbility } from './hooks/useAbilities'; import { useNotificationConfigNav } from './navigation/useNotificationConfigNav'; import { withPageErrorBoundary } from './withPageErrorBoundary'; @@ -45,12 +44,9 @@ const TemplatesList = () => { }; function NotificationTemplatesRoutes() { - const useV2Nav = shouldUseAlertingNavigationV2(); - return ( - {/* In V2 mode, show templates list on base route */} - {useV2Nav && } />} + } /> } /> } /> } /> @@ -59,20 +55,13 @@ function NotificationTemplatesRoutes() { } function NotificationTemplatesPage() { - const useV2Nav = shouldUseAlertingNavigationV2(); const { navId, pageNav } = useNotificationConfigNav(); - // In V2 mode, wrap with page wrapper for proper navigation - if (useV2Nav) { - return ( - - - - ); - } - - // In legacy mode, just render routes (templates are accessed via ContactPoints page tabs) - return ; + return ( + + + + ); } export default withPageErrorBoundary(NotificationTemplatesPage); diff --git a/public/app/features/alerting/unified/TimeIntervalsPage.test.tsx b/public/app/features/alerting/unified/TimeIntervalsPage.test.tsx index 0663d6445d3..a9637797d21 100644 --- a/public/app/features/alerting/unified/TimeIntervalsPage.test.tsx +++ b/public/app/features/alerting/unified/TimeIntervalsPage.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, testWithFeatureToggles } from 'test/test-utils'; +import { render, screen } from 'test/test-utils'; import { configureStore } from 'app/store/configureStore'; import { AccessControlAction } from 'app/types/accessControl'; @@ -20,62 +20,46 @@ const alertManager = mockDataSource({ }); describe('TimeIntervalsPage', () => { - describe('V2 Navigation Mode', () => { - testWithFeatureToggles({ enable: ['alertingNavigationV2'] }); + beforeEach(() => { + setupDataSources(alertManager); + setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, defaultConfig); + setTimeIntervalsListEmpty(); // Mock empty time intervals list so component renders + grantUserPermissions([ + AccessControlAction.AlertingNotificationsRead, + AccessControlAction.AlertingTimeIntervalsRead, + ]); + }); - beforeEach(() => { - setupDataSources(alertManager); - setAlertmanagerConfig(GRAFANA_RULES_SOURCE_NAME, defaultConfig); - setTimeIntervalsListEmpty(); // Mock empty time intervals list so component renders - grantUserPermissions([ - AccessControlAction.AlertingNotificationsRead, - AccessControlAction.AlertingTimeIntervalsRead, - ]); + it('renders time intervals table', async () => { + const mockNavIndex = { + 'notification-config': { + id: 'notification-config', + text: 'Notification configuration', + url: '/alerting/notifications', + }, + 'notification-config-time-intervals': { + id: 'notification-config-time-intervals', + text: 'Time intervals', + url: '/alerting/time-intervals', + }, + }; + const store = configureStore({ + navIndex: mockNavIndex, }); - it('renders time intervals table', async () => { - const mockNavIndex = { - 'notification-config': { - id: 'notification-config', - text: 'Notification configuration', - url: '/alerting/notifications', - }, - 'notification-config-time-intervals': { - id: 'notification-config-time-intervals', - text: 'Time intervals', - url: '/alerting/time-intervals', - }, - }; - const store = configureStore({ - navIndex: mockNavIndex, - }); - - render(, { - store, - historyOptions: { - initialEntries: ['/alerting/time-intervals'], - }, - }); - - // Should show time intervals content - // When empty, it shows "You haven't created any time intervals yet" - // When loading, it shows "Loading time intervals..." - // When error, it shows "Error loading time intervals" - // All contain "time intervals" - use getAllByText since there are multiple matches (tab, description, empty state) - const timeIntervalsTexts = await screen.findAllByText(/time intervals/i, {}, { timeout: 5000 }); - expect(timeIntervalsTexts.length).toBeGreaterThan(0); + render(, { + store, + historyOptions: { + initialEntries: ['/alerting/time-intervals'], + }, }); - it('returns null in legacy mode', () => { - // This test verifies that the component returns null when V2 is disabled - // The feature toggle is controlled by testWithFeatureToggles, so we test it separately - const { container } = render(, { - historyOptions: { - initialEntries: ['/alerting/time-intervals'], - }, - }); - // In V2 mode (enabled by testWithFeatureToggles), it should render content - expect(container).not.toBeEmptyDOMElement(); - }); + // Should show time intervals content + // When empty, it shows "You haven't created any time intervals yet" + // When loading, it shows "Loading time intervals..." + // When error, it shows "Error loading time intervals" + // All contain "time intervals" - use getAllByText since there are multiple matches (tab, description, empty state) + const timeIntervalsTexts = await screen.findAllByText(/time intervals/i, {}, { timeout: 5000 }); + expect(timeIntervalsTexts.length).toBeGreaterThan(0); }); }); diff --git a/public/app/features/alerting/unified/TimeIntervalsPage.tsx b/public/app/features/alerting/unified/TimeIntervalsPage.tsx index 319f96a0ac9..fe1ee0c42ed 100644 --- a/public/app/features/alerting/unified/TimeIntervalsPage.tsx +++ b/public/app/features/alerting/unified/TimeIntervalsPage.tsx @@ -1,7 +1,6 @@ import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper'; import { GrafanaAlertmanagerWarning } from './components/GrafanaAlertmanagerWarning'; import { TimeIntervalsTable } from './components/mute-timings/MuteTimingsTable'; -import { shouldUseAlertingNavigationV2 } from './featureToggles'; import { useNotificationConfigNav } from './navigation/useNotificationConfigNav'; import { useAlertmanager } from './state/AlertmanagerContext'; import { withPageErrorBoundary } from './withPageErrorBoundary'; @@ -20,21 +19,13 @@ function TimeIntervalsPageContent() { } function TimeIntervalsPage() { - const useV2Nav = shouldUseAlertingNavigationV2(); const { navId, pageNav } = useNotificationConfigNav(); - // In V2 mode, wrap with page wrapper for proper navigation - // AlertmanagerPageWrapper provides AlertmanagerContext, so TimeIntervalsPageContent can use useAlertmanager - if (useV2Nav) { - return ( - - - - ); - } - - // Legacy mode: not used (handled by NotificationPoliciesPage) - return null; + return ( + + + + ); } export default withPageErrorBoundary(TimeIntervalsPage); diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx index d75aef6e52e..5cdac0c6169 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoints.test.tsx @@ -178,28 +178,24 @@ describe('contact points', () => { }); }); - describe('V2 Navigation Mode', () => { - testWithFeatureToggles({ enable: ['alertingNavigationV2'] }); + test('shows only contact points without internal tabs', async () => { + renderWithProvider(); - test('shows only contact points without internal tabs', async () => { - renderWithProvider(); + // Should show contact points directly + expect(await screen.findByText(/create contact point/i)).toBeInTheDocument(); - // Should show contact points directly - expect(await screen.findByText(/create contact point/i)).toBeInTheDocument(); + // Should not have tabs + expect(screen.queryByRole('tab')).not.toBeInTheDocument(); + }); - // Should not have tabs - expect(screen.queryByRole('tab')).not.toBeInTheDocument(); - }); + test('does not show templates tab', async () => { + renderWithProvider(); - test('does not show templates tab in V2 mode', async () => { - renderWithProvider(); + // Should show contact points + expect(await screen.findByText(/create contact point/i)).toBeInTheDocument(); - // Should show contact points - expect(await screen.findByText(/create contact point/i)).toBeInTheDocument(); - - // Should not show templates tab - expect(screen.queryByText(/notification templates/i)).not.toBeInTheDocument(); - }); + // Should not show templates tab + expect(screen.queryByText(/notification templates/i)).not.toBeInTheDocument(); }); describe('templates tab', () => { diff --git a/public/app/features/alerting/unified/featureToggles.ts b/public/app/features/alerting/unified/featureToggles.ts index e584ac4211c..15fa8e11fd8 100644 --- a/public/app/features/alerting/unified/featureToggles.ts +++ b/public/app/features/alerting/unified/featureToggles.ts @@ -31,8 +31,3 @@ export const shouldUseFullyCompatibleBackendFilters = () => * Saved searches feature - allows users to save and apply search queries on the Alert Rules page. */ export const shouldUseSavedSearches = () => config.featureToggles.alertingSavedSearches ?? false; - -/** - * New grouped navigation structure for Alerting - */ -export const shouldUseAlertingNavigationV2 = () => config.featureToggles.alertingNavigationV2 ?? false; diff --git a/public/app/features/alerting/unified/navigation/useAlertActivityNav.test.tsx b/public/app/features/alerting/unified/navigation/useAlertActivityNav.test.tsx index 741fb1a1119..c2ddee422dc 100644 --- a/public/app/features/alerting/unified/navigation/useAlertActivityNav.test.tsx +++ b/public/app/features/alerting/unified/navigation/useAlertActivityNav.test.tsx @@ -1,7 +1,6 @@ import { renderHook } from '@testing-library/react'; import { getWrapper } from 'test/test-utils'; -import { config } from '@grafana/runtime'; import { configureStore } from 'app/store/configureStore'; import { useAlertActivityNav } from './useAlertActivityNav'; @@ -23,58 +22,13 @@ describe('useAlertActivityNav', () => { text: 'Active notifications', url: '/alerting/groups', }, - groups: { - id: 'groups', - text: 'Alert groups', - url: '/alerting/groups', - }, - 'alert-alerts': { - id: 'alert-alerts', - text: 'Alerts', - url: '/alerting/alerts', - }, }; const defaultPreloadedState = { navIndex: mockNavIndex, }; - beforeEach(() => { - config.featureToggles.alertingNavigationV2 = false; - }); - - it('should return legacy navId when feature flag is off for /alerting/groups', () => { - const wrapper = getWrapper({ - preloadedState: defaultPreloadedState, - renderWithRouter: true, - historyOptions: { - initialEntries: ['/alerting/groups'], - }, - }); - - const { result } = renderHook(() => useAlertActivityNav(), { wrapper }); - - expect(result.current.navId).toBe('groups'); - expect(result.current.pageNav).toBeUndefined(); - }); - - it('should return legacy navId when feature flag is off for /alerting/alerts', () => { - const wrapper = getWrapper({ - preloadedState: defaultPreloadedState, - renderWithRouter: true, - historyOptions: { - initialEntries: ['/alerting/alerts'], - }, - }); - - const { result } = renderHook(() => useAlertActivityNav(), { wrapper }); - - expect(result.current.navId).toBe('alert-alerts'); - expect(result.current.pageNav).toBeUndefined(); - }); - - it('should return V2 navigation when feature flag is on for Alerts tab', () => { - config.featureToggles.alertingNavigationV2 = true; + it('should return navigation with pageNav for Alerts tab', () => { const store = configureStore(defaultPreloadedState); const wrapper = getWrapper({ store, @@ -94,8 +48,7 @@ describe('useAlertActivityNav', () => { expect(result.current.pageNav?.text).toBe('Alert activity'); }); - it('should return V2 navigation when feature flag is on for Active notifications tab', () => { - config.featureToggles.alertingNavigationV2 = true; + it('should return navigation with pageNav for Active notifications tab', () => { const store = configureStore(defaultPreloadedState); const wrapper = getWrapper({ store, @@ -116,7 +69,6 @@ describe('useAlertActivityNav', () => { }); it('should set active tab based on current path', () => { - config.featureToggles.alertingNavigationV2 = true; const store = configureStore(defaultPreloadedState); const wrapper = getWrapper({ store, @@ -138,7 +90,6 @@ describe('useAlertActivityNav', () => { }); it('should filter tabs based on permissions', () => { - config.featureToggles.alertingNavigationV2 = true; const limitedNavIndex = { 'alert-activity': mockNavIndex['alert-activity'], 'alert-activity-alerts': mockNavIndex['alert-activity-alerts'], @@ -163,13 +114,9 @@ describe('useAlertActivityNav', () => { expect(result.current.pageNav?.children?.[0].id).toBe('alert-activity-alerts'); }); - it('should fallback to legacy when alert-activity nav is missing', () => { - config.featureToggles.alertingNavigationV2 = true; + it('should return undefined when alert-activity nav is missing', () => { const store = configureStore({ - navIndex: { - groups: mockNavIndex.groups, - 'alert-alerts': mockNavIndex['alert-alerts'], - }, + navIndex: {}, }); const wrapper = getWrapper({ store, @@ -181,7 +128,7 @@ describe('useAlertActivityNav', () => { const { result } = renderHook(() => useAlertActivityNav(), { wrapper }); - expect(result.current.navId).toBe('groups'); + expect(result.current.navId).toBeUndefined(); expect(result.current.pageNav).toBeUndefined(); }); }); diff --git a/public/app/features/alerting/unified/navigation/useAlertActivityNav.ts b/public/app/features/alerting/unified/navigation/useAlertActivityNav.ts index cfd84d11346..dc7baae644c 100644 --- a/public/app/features/alerting/unified/navigation/useAlertActivityNav.ts +++ b/public/app/features/alerting/unified/navigation/useAlertActivityNav.ts @@ -4,48 +4,12 @@ import { NavModelItem } from '@grafana/data'; import { t } from '@grafana/i18n'; import { useSelector } from 'app/types/store'; -import { shouldUseAlertingNavigationV2 } from '../featureToggles'; - export function useAlertActivityNav() { const location = useLocation(); const navIndex = useSelector((state) => state.navIndex); - const useV2Nav = shouldUseAlertingNavigationV2(); - - // If V2 navigation is not enabled, return legacy navId - if (!useV2Nav) { - if (location.pathname === '/alerting/groups') { - return { - navId: 'groups', - pageNav: undefined, - }; - } - if (location.pathname === '/alerting/alerts') { - return { - navId: 'alert-alerts', - pageNav: undefined, - }; - } - return { - navId: undefined, - pageNav: undefined, - }; - } const alertActivityNav = navIndex['alert-activity']; if (!alertActivityNav) { - // Fallback to legacy - if (location.pathname === '/alerting/groups') { - return { - navId: 'groups', - pageNav: undefined, - }; - } - if (location.pathname === '/alerting/alerts') { - return { - navId: 'alert-alerts', - pageNav: undefined, - }; - } return { navId: undefined, pageNav: undefined, diff --git a/public/app/features/alerting/unified/navigation/useAlertRulesNav.test.tsx b/public/app/features/alerting/unified/navigation/useAlertRulesNav.test.tsx index aa652a866da..f5dd670d161 100644 --- a/public/app/features/alerting/unified/navigation/useAlertRulesNav.test.tsx +++ b/public/app/features/alerting/unified/navigation/useAlertRulesNav.test.tsx @@ -1,7 +1,6 @@ import { renderHook } from '@testing-library/react'; import { getWrapper } from 'test/test-utils'; -import { config } from '@grafana/runtime'; import { configureStore } from 'app/store/configureStore'; import { useAlertRulesNav } from './useAlertRulesNav'; @@ -24,38 +23,13 @@ describe('useAlertRulesNav', () => { text: 'Recently deleted', url: '/alerting/recently-deleted', }, - 'alert-list': { - id: 'alert-list', - text: 'Alert rules', - url: '/alerting/list', - }, }; const defaultPreloadedState = { navIndex: mockNavIndex, }; - beforeEach(() => { - config.featureToggles.alertingNavigationV2 = false; - }); - - it('should return legacy navId when feature flag is off', () => { - const wrapper = getWrapper({ - preloadedState: defaultPreloadedState, - renderWithRouter: true, - historyOptions: { - initialEntries: ['/alerting/list'], - }, - }); - - const { result } = renderHook(() => useAlertRulesNav(), { wrapper }); - - expect(result.current.navId).toBe('alert-list'); - expect(result.current.pageNav).toBeUndefined(); - }); - - it('should return V2 navigation when feature flag is on', () => { - config.featureToggles.alertingNavigationV2 = true; + it('should return navigation with pageNav', () => { const store = configureStore(defaultPreloadedState); const wrapper = getWrapper({ store, @@ -76,7 +50,6 @@ describe('useAlertRulesNav', () => { }); it('should filter tabs based on permissions', () => { - config.featureToggles.alertingNavigationV2 = true; const limitedNavIndex = { 'alert-rules': mockNavIndex['alert-rules'], 'alert-rules-list': mockNavIndex['alert-rules-list'], @@ -102,7 +75,6 @@ describe('useAlertRulesNav', () => { }); it('should set active tab based on current path', () => { - config.featureToggles.alertingNavigationV2 = true; const store = configureStore(defaultPreloadedState); const wrapper = getWrapper({ store, diff --git a/public/app/features/alerting/unified/navigation/useAlertRulesNav.ts b/public/app/features/alerting/unified/navigation/useAlertRulesNav.ts index b10a3dddd69..00847d4fec6 100644 --- a/public/app/features/alerting/unified/navigation/useAlertRulesNav.ts +++ b/public/app/features/alerting/unified/navigation/useAlertRulesNav.ts @@ -4,26 +4,14 @@ import { NavModelItem } from '@grafana/data'; import { t } from '@grafana/i18n'; import { useSelector } from 'app/types/store'; -import { shouldUseAlertingNavigationV2 } from '../featureToggles'; - export function useAlertRulesNav() { const location = useLocation(); const navIndex = useSelector((state) => state.navIndex); - const useV2Nav = shouldUseAlertingNavigationV2(); - - // If V2 navigation is not enabled, return legacy navId - if (!useV2Nav) { - return { - navId: 'alert-list', - pageNav: undefined, - }; - } const alertRulesNav = navIndex['alert-rules']; if (!alertRulesNav) { - // Fallback to legacy if V2 nav doesn't exist return { - navId: 'alert-list', + navId: undefined, pageNav: undefined, }; } diff --git a/public/app/features/alerting/unified/navigation/useInsightsNav.test.tsx b/public/app/features/alerting/unified/navigation/useInsightsNav.test.tsx index 35122d5c0e9..a70accbc213 100644 --- a/public/app/features/alerting/unified/navigation/useInsightsNav.test.tsx +++ b/public/app/features/alerting/unified/navigation/useInsightsNav.test.tsx @@ -1,7 +1,6 @@ import { renderHook } from '@testing-library/react'; import { getWrapper } from 'test/test-utils'; -import { config } from '@grafana/runtime'; import { configureStore } from 'app/store/configureStore'; import { useInsightsNav } from './useInsightsNav'; @@ -23,38 +22,13 @@ describe('useInsightsNav', () => { text: 'Alert state history', url: '/alerting/history', }, - 'alerts-history': { - id: 'alerts-history', - text: 'History', - url: '/alerting/history', - }, }; const defaultPreloadedState = { navIndex: mockNavIndex, }; - beforeEach(() => { - config.featureToggles.alertingNavigationV2 = false; - }); - - it('should return legacy navId when feature flag is off', () => { - const wrapper = getWrapper({ - preloadedState: defaultPreloadedState, - renderWithRouter: true, - historyOptions: { - initialEntries: ['/alerting/history'], - }, - }); - - const { result } = renderHook(() => useInsightsNav(), { wrapper }); - - expect(result.current.navId).toBe('alerts-history'); - expect(result.current.pageNav).toBeUndefined(); - }); - - it('should return V2 navigation when feature flag is on', () => { - config.featureToggles.alertingNavigationV2 = true; + it('should return navigation with pageNav', () => { const store = configureStore(defaultPreloadedState); const wrapper = getWrapper({ store, @@ -73,7 +47,6 @@ describe('useInsightsNav', () => { }); it('should set active tab based on current path', () => { - config.featureToggles.alertingNavigationV2 = true; const store = configureStore(defaultPreloadedState); const wrapper = getWrapper({ store, @@ -91,7 +64,6 @@ describe('useInsightsNav', () => { }); it('should filter tabs based on permissions', () => { - config.featureToggles.alertingNavigationV2 = true; const limitedNavIndex = { insights: mockNavIndex.insights, 'insights-system': mockNavIndex['insights-system'], diff --git a/public/app/features/alerting/unified/navigation/useInsightsNav.ts b/public/app/features/alerting/unified/navigation/useInsightsNav.ts index 7d888c30d5c..f647f3bc441 100644 --- a/public/app/features/alerting/unified/navigation/useInsightsNav.ts +++ b/public/app/features/alerting/unified/navigation/useInsightsNav.ts @@ -4,37 +4,12 @@ import { NavModelItem } from '@grafana/data'; import { t } from '@grafana/i18n'; import { useSelector } from 'app/types/store'; -import { shouldUseAlertingNavigationV2 } from '../featureToggles'; - export function useInsightsNav() { const location = useLocation(); const navIndex = useSelector((state) => state.navIndex); - const useV2Nav = shouldUseAlertingNavigationV2(); - - // If V2 navigation is not enabled, return legacy navId - if (!useV2Nav) { - if (location.pathname === '/alerting/history') { - return { - navId: 'alerts-history', - pageNav: undefined, - }; - } - // For insights page, it doesn't exist in legacy, so return undefined - return { - navId: undefined, - pageNav: undefined, - }; - } const insightsNav = navIndex.insights; if (!insightsNav) { - // Fallback to legacy - if (location.pathname === '/alerting/history') { - return { - navId: 'alerts-history', - pageNav: undefined, - }; - } return { navId: undefined, pageNav: undefined, diff --git a/public/app/features/alerting/unified/navigation/useNotificationConfigNav.test.tsx b/public/app/features/alerting/unified/navigation/useNotificationConfigNav.test.tsx index fe434457a92..94d9c3da60f 100644 --- a/public/app/features/alerting/unified/navigation/useNotificationConfigNav.test.tsx +++ b/public/app/features/alerting/unified/navigation/useNotificationConfigNav.test.tsx @@ -1,7 +1,6 @@ import { renderHook } from '@testing-library/react'; import { getWrapper } from 'test/test-utils'; -import { config } from '@grafana/runtime'; import { configureStore } from 'app/store/configureStore'; import { useNotificationConfigNav } from './useNotificationConfigNav'; @@ -31,17 +30,7 @@ describe('useNotificationConfigNav', () => { 'notification-config-time-intervals': { id: 'notification-config-time-intervals', text: 'Time intervals', - url: '/alerting/routes?tab=time_intervals', - }, - receivers: { - id: 'receivers', - text: 'Contact points', - url: '/alerting/notifications', - }, - 'am-routes': { - id: 'am-routes', - text: 'Notification policies', - url: '/alerting/routes', + url: '/alerting/time-intervals', }, }; @@ -49,27 +38,7 @@ describe('useNotificationConfigNav', () => { navIndex: mockNavIndex, }; - beforeEach(() => { - config.featureToggles.alertingNavigationV2 = false; - }); - - it('should return legacy navId when feature flag is off', () => { - const wrapper = getWrapper({ - preloadedState: defaultPreloadedState, - renderWithRouter: true, - historyOptions: { - initialEntries: ['/alerting/notifications'], - }, - }); - - const { result } = renderHook(() => useNotificationConfigNav(), { wrapper }); - - expect(result.current.navId).toBe('receivers'); - expect(result.current.pageNav).toBeUndefined(); - }); - - it('should return V2 navigation when feature flag is on', () => { - config.featureToggles.alertingNavigationV2 = true; + it('should return navigation with pageNav', () => { const store = configureStore(defaultPreloadedState); const wrapper = getWrapper({ store, @@ -87,8 +56,7 @@ describe('useNotificationConfigNav', () => { expect(result.current.pageNav?.children).toBeDefined(); }); - it('should detect time intervals tab from V2 path', () => { - config.featureToggles.alertingNavigationV2 = true; + it('should detect time intervals tab from path', () => { const store = configureStore(defaultPreloadedState); const wrapper = getWrapper({ store, @@ -108,7 +76,6 @@ describe('useNotificationConfigNav', () => { }); it('should filter tabs based on permissions', () => { - config.featureToggles.alertingNavigationV2 = true; const limitedNavIndex = { 'notification-config': mockNavIndex['notification-config'], 'notification-config-contact-points': mockNavIndex['notification-config-contact-points'], diff --git a/public/app/features/alerting/unified/navigation/useNotificationConfigNav.ts b/public/app/features/alerting/unified/navigation/useNotificationConfigNav.ts index 4eb754e1f04..d945addd13e 100644 --- a/public/app/features/alerting/unified/navigation/useNotificationConfigNav.ts +++ b/public/app/features/alerting/unified/navigation/useNotificationConfigNav.ts @@ -4,32 +4,9 @@ import { NavModelItem } from '@grafana/data'; import { t } from '@grafana/i18n'; import { useSelector } from 'app/types/store'; -import { shouldUseAlertingNavigationV2 } from '../featureToggles'; - export function useNotificationConfigNav() { const location = useLocation(); const navIndex = useSelector((state) => state.navIndex); - const useV2Nav = shouldUseAlertingNavigationV2(); - - // If V2 navigation is not enabled, return legacy navId based on current path - if (!useV2Nav) { - if (location.pathname.includes('/alerting/notifications/templates')) { - return { - navId: 'receivers', - pageNav: undefined, - }; - } - if (location.pathname === '/alerting/routes') { - return { - navId: 'am-routes', - pageNav: undefined, - }; - } - return { - navId: 'receivers', - pageNav: undefined, - }; - } const notificationConfigNav = navIndex['notification-config']; if (!notificationConfigNav) { @@ -53,10 +30,7 @@ export function useNotificationConfigNav() { } // Check if we're on the time intervals page - // In V2 mode, check for dedicated route; in legacy mode, check for query param - const isTimeIntervalsTab = useV2Nav - ? location.pathname === '/alerting/time-intervals' - : location.pathname === '/alerting/routes' && location.search.includes('tab=time_intervals'); + const isTimeIntervalsTab = location.pathname === '/alerting/time-intervals'; // All available tabs const allTabs = [ @@ -87,7 +61,7 @@ export function useNotificationConfigNav() { { id: 'notification-config-time-intervals', text: t('alerting.navigation.time-intervals', 'Time intervals'), - url: useV2Nav ? '/alerting/time-intervals' : '/alerting/routes?tab=time_intervals', + url: '/alerting/time-intervals', active: isTimeIntervalsTab, icon: 'clock-nine', parentItem: notificationConfigNav,