From 93a525e4fe7613fffb1324a32ec2535913328884 Mon Sep 17 00:00:00 2001 From: ashokaditya Date: Tue, 14 Apr 2026 17:13:02 +0200 Subject: [PATCH] enable `securityClassicNavUpdate` logic and remove feature flag --- .../security/packages/side-nav/index.ts | 6 +- .../security/packages/side-nav/src/index.tsx | 4 +- .../side-nav/src/solution_side_nav.test.tsx | 243 +++--------------- .../side-nav/src/solution_side_nav.tsx | 70 +---- .../common/experimental_features.ts | 6 - .../public/app/links/app_links.ts | 6 +- .../app/links/get_filtered_links.test.ts | 10 +- .../breadcrumbs/use_breadcrumbs_nav.test.ts | 24 +- .../breadcrumbs/use_breadcrumbs_nav.ts | 23 +- .../security_side_nav/categories.ts | 17 +- .../security_side_nav.test.tsx | 178 +++++-------- .../security_side_nav/security_side_nav.tsx | 96 ++----- .../rules/translated_rules_page.cy.ts | 2 +- 13 files changed, 151 insertions(+), 534 deletions(-) diff --git a/x-pack/solutions/security/packages/side-nav/index.ts b/x-pack/solutions/security/packages/side-nav/index.ts index fe2e1fc88deb8..acc05924544fc 100644 --- a/x-pack/solutions/security/packages/side-nav/index.ts +++ b/x-pack/solutions/security/packages/side-nav/index.ts @@ -5,10 +5,6 @@ * 2.0. */ -export { - SolutionSideNav, - type SolutionSideNavProps, - type SolutionSideNavInteractionVariant, -} from './src'; +export { SolutionSideNav, type SolutionSideNavProps } from './src'; export { SolutionSideNavItemPosition } from './src/types'; export type { SolutionSideNavItem, Tracker } from './src/types'; diff --git a/x-pack/solutions/security/packages/side-nav/src/index.tsx b/x-pack/solutions/security/packages/side-nav/src/index.tsx index b24d4f856dfcc..055c45a1a8519 100644 --- a/x-pack/solutions/security/packages/side-nav/src/index.tsx +++ b/x-pack/solutions/security/packages/side-nav/src/index.tsx @@ -7,9 +7,9 @@ import React, { lazy, Suspense } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; -import type { SolutionSideNavInteractionVariant, SolutionSideNavProps } from './solution_side_nav'; +import type { SolutionSideNavProps } from './solution_side_nav'; -export type { SolutionSideNavProps, SolutionSideNavInteractionVariant }; +export type { SolutionSideNavProps }; const SolutionSideNavLazy = lazy(() => import('./solution_side_nav')); diff --git a/x-pack/solutions/security/packages/side-nav/src/solution_side_nav.test.tsx b/x-pack/solutions/security/packages/side-nav/src/solution_side_nav.test.tsx index 3cadad7ec78a2..d860fd136df4c 100644 --- a/x-pack/solutions/security/packages/side-nav/src/solution_side_nav.test.tsx +++ b/x-pack/solutions/security/packages/side-nav/src/solution_side_nav.test.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { userEvent } from '@testing-library/user-event'; import { SolutionSideNav } from './solution_side_nav'; import type { SolutionSideNavProps } from './solution_side_nav'; @@ -35,6 +35,19 @@ const mockItems: SolutionSideNavItem[] = [ label: 'Alerts', href: '/alerts', }, + { + id: 'rulesLanding', + label: 'Rules', + href: '/rules', + items: [ + { + id: 'rulesManagement', + label: 'Rules Management', + href: '/rules-management', + description: 'Rules Management description', + }, + ], + }, ]; const renderNav = (props: Partial = {}) => @@ -58,7 +71,7 @@ describe('SolutionSideNav', () => { const result = renderNav(); expect( result.getByTestId(`solutionSideNavItemLink-${'dashboardsLanding'}`).getAttribute('href') - ).toBe('/dashboards'); + ).toBe('/overview'); expect(result.getByTestId(`solutionSideNavItemLink-${'alerts'}`).getAttribute('href')).toBe( '/alerts' ); @@ -82,7 +95,16 @@ describe('SolutionSideNav', () => { expect(mockOnClick).toHaveBeenCalled(); }); - it('should send telemetry if link clicked', async () => { + it('renders right arrow hint when item has children', () => { + const result = renderNav({ + items: mockItems, + selectedId: 'rules', + }); + + expect(result.getByTestId('solutionSideNavItemPanelHint-rulesLanding')).toBeInTheDocument(); + }); + + it('should send telemetry when a link without children is clicked', async () => { const items = [ ...mockItems, { @@ -98,43 +120,33 @@ describe('SolutionSideNav', () => { `${TELEMETRY_EVENT.NAVIGATION}${'exploreLanding'}` ); }); - }); - describe('panel button toggle', () => { - it('should render the panel button only for nav items', () => { - const result = renderNav(); - expect( - result.getByTestId(`solutionSideNavItemButton-${'dashboardsLanding'}`) - ).toBeInTheDocument(); - expect(result.queryByTestId(`solutionSideNavItemButton-${'alerts'}`)).not.toBeInTheDocument(); - }); - - it('should render the panel when button is clicked', async () => { + it('should send telemetry when a parent link is clicked', async () => { const result = renderNav(); expect(result.queryByTestId('solutionSideNavPanel')).not.toBeInTheDocument(); - await userEvent.click(result.getByTestId(`solutionSideNavItemButton-${'dashboardsLanding'}`)); - expect(result.getByTestId('solutionSideNavPanel')).toBeInTheDocument(); - expect(result.getByText('Overview')).toBeInTheDocument(); + await userEvent.click(result.getByTestId(`solutionSideNavItemLink-${'dashboardsLanding'}`)); + expect(mockTrack).toHaveBeenCalledWith( + METRIC_TYPE.CLICK, + `${TELEMETRY_EVENT.PANEL_NAVIGATION_TOGGLE}${'dashboardsLanding'}` + ); }); - it('should telemetry when button is clicked', async () => { + it('should render the sub-nav panel when link is clicked', async () => { const result = renderNav(); expect(result.queryByTestId('solutionSideNavPanel')).not.toBeInTheDocument(); - await userEvent.click(result.getByTestId(`solutionSideNavItemButton-${'dashboardsLanding'}`)); - expect(mockTrack).toHaveBeenCalledWith( - METRIC_TYPE.CLICK, - `${TELEMETRY_EVENT.PANEL_NAVIGATION_TOGGLE}${'dashboardsLanding'}` - ); + await userEvent.click(result.getByTestId(`solutionSideNavItemLink-${'dashboardsLanding'}`)); + expect(result.getByTestId('solutionSideNavPanel')).toBeInTheDocument(); + expect(result.getByText('Overview')).toBeInTheDocument(); }); - it('should close the panel when the same button is clicked', async () => { + it('should close the sub-nav panel when the same link is clicked', async () => { const result = renderNav(); - await userEvent.click(result.getByTestId(`solutionSideNavItemButton-${'dashboardsLanding'}`)); + await userEvent.click(result.getByTestId(`solutionSideNavItemLink-${'dashboardsLanding'}`)); expect(result.getByTestId('solutionSideNavPanel')).toBeInTheDocument(); - await userEvent.click(result.getByTestId(`solutionSideNavItemButton-${'dashboardsLanding'}`)); + await userEvent.click(result.getByTestId(`solutionSideNavItemLink-${'dashboardsLanding'}`)); // add check at the end of the event loop to ensure the panel is removed setTimeout(() => { @@ -142,7 +154,7 @@ describe('SolutionSideNav', () => { }); }); - it('should open other panel when other button is clicked while open', async () => { + it('should open relevant sub-nav panel when another link is clicked while sub-nav is open', async () => { const items = [ ...mockItems, { @@ -161,186 +173,13 @@ describe('SolutionSideNav', () => { ]; const result = renderNav({ items }); - await userEvent.click(result.getByTestId(`solutionSideNavItemButton-${'dashboardsLanding'}`)); + await userEvent.click(result.getByTestId(`solutionSideNavItemLink-${'dashboardsLanding'}`)); expect(result.getByTestId('solutionSideNavPanel')).toBeInTheDocument(); expect(result.getByText('Overview')).toBeInTheDocument(); - await userEvent.click(result.getByTestId(`solutionSideNavItemButton-${'exploreLanding'}`)); + await userEvent.click(result.getByTestId(`solutionSideNavItemLink-${'exploreLanding'}`)); expect(result.queryByTestId('solutionSideNavPanel')).toBeInTheDocument(); expect(result.getByText('Users')).toBeInTheDocument(); }); }); - - describe('unifiedRow interaction variant', () => { - const firstChildOnClick = jest.fn((ev: { preventDefault: () => void }) => { - ev.preventDefault(); - }); - const panelItems: SolutionSideNavItem[] = [ - { - id: 'dashboardsLanding', - label: 'Dashboards', - href: '/dashboards', - items: [ - { - id: 'overview', - label: 'Overview', - href: '/overview-first', - onClick: firstChildOnClick, - description: 'Overview description', - }, - ], - }, - { - id: 'alerts', - label: 'Alerts', - href: '/alerts', - }, - { - id: 'Rules', - label: 'Rules', - href: '/rules', - items: [ - { - id: 'rulesManagement', - label: 'Rules Management', - href: '/rules-management', - description: 'Rules Management description', - }, - ], - }, - ]; - - beforeEach(() => { - firstChildOnClick.mockClear(); - }); - - it('renders arrow hint instead of split panel button when item has children', () => { - const result = renderNav({ - items: panelItems, - navLinkInteractionVariant: 'unifiedRow', - selectedId: 'alerts', - }); - expect( - result.queryByTestId('solutionSideNavItemButton-dashboardsLanding') - ).not.toBeInTheDocument(); - expect( - result.getByTestId('solutionSideNavItemPanelHint-dashboardsLanding') - ).toBeInTheDocument(); - }); - - it('uses first child href on the parent row link', () => { - const result = renderNav({ - items: panelItems, - navLinkInteractionVariant: 'unifiedRow', - selectedId: 'alerts', - }); - expect( - result.getByTestId('solutionSideNavItemLink-dashboardsLanding').getAttribute('href') - ).toBe('/overview-first'); - }); - - it('clicking the parent row opens the panel', async () => { - const result = renderNav({ - items: panelItems, - navLinkInteractionVariant: 'unifiedRow', - selectedId: 'alerts', - }); - await userEvent.click(result.getByTestId('solutionSideNavItemLink-dashboardsLanding')); - expect(result.getByTestId('solutionSideNavPanel')).toBeInTheDocument(); - expect(result.getByText('Overview')).toBeInTheDocument(); - expect(mockTrack).toHaveBeenCalledWith( - METRIC_TYPE.CLICK, - `${TELEMETRY_EVENT.PANEL_NAVIGATION_TOGGLE}dashboardsLanding` - ); - }); - - it('should not show panel button in unifiedRow variant', () => { - const result = renderNav({ - items: panelItems, - navLinkInteractionVariant: 'unifiedRow', - selectedId: 'alerts', - }); - // Panel button should not exist for items with children in unifiedRow mode - expect( - result.queryByTestId('solutionSideNavItemButton-dashboardsLanding') - ).not.toBeInTheDocument(); - expect(result.queryByTestId('solutionSideNavItemButton-Rules')).not.toBeInTheDocument(); - }); - - it('should navigate directly when clicking item without children in unifiedRow mode', async () => { - const mockOnClick = jest.fn((ev) => { - ev.preventDefault(); - }); - const itemsWithoutChildren: SolutionSideNavItem[] = [ - { - id: 'alerts', - label: 'Alerts', - href: '/alerts', - onClick: mockOnClick, - }, - ]; - const result = renderNav({ - items: itemsWithoutChildren, - navLinkInteractionVariant: 'unifiedRow', - selectedId: 'alerts', - }); - await userEvent.click(result.getByTestId('solutionSideNavItemLink-alerts')); - expect(mockOnClick).toHaveBeenCalled(); - }); - }); - - describe('navLinkInteractionVariant attribute', () => { - it('should use splitButton as default variant', () => { - const result = renderNav(); - // In splitButton mode, panel button should be visible for items with children - expect(result.getByTestId('solutionSideNavItemButton-dashboardsLanding')).toBeInTheDocument(); - }); - - it('should apply unifiedRow variant correctly', () => { - const result = renderNav({ - items: mockItems, - navLinkInteractionVariant: 'unifiedRow', - selectedId: 'alerts', - }); - // In unifiedRow mode, arrow hint should show instead of button - expect( - result.getByTestId('solutionSideNavItemPanelHint-dashboardsLanding') - ).toBeInTheDocument(); - expect( - result.queryByTestId('solutionSideNavItemButton-dashboardsLanding') - ).not.toBeInTheDocument(); - }); - - it('should switch between variants correctly', () => { - const { rerender } = render( - - ); - - // Check splitButton mode - expect(screen.getByTestId('solutionSideNavItemButton-dashboardsLanding')).toBeInTheDocument(); - - // Re-render with unifiedRow - rerender( - - ); - - // Check unifiedRow mode - expect( - screen.getByTestId('solutionSideNavItemPanelHint-dashboardsLanding') - ).toBeInTheDocument(); - expect( - screen.queryByTestId('solutionSideNavItemButton-dashboardsLanding') - ).not.toBeInTheDocument(); - }); - }); }); diff --git a/x-pack/solutions/security/packages/side-nav/src/solution_side_nav.tsx b/x-pack/solutions/security/packages/side-nav/src/solution_side_nav.tsx index 928a15c8a0bf5..9eb92878c1449 100644 --- a/x-pack/solutions/security/packages/side-nav/src/solution_side_nav.tsx +++ b/x-pack/solutions/security/packages/side-nav/src/solution_side_nav.tsx @@ -7,7 +7,6 @@ import React, { useCallback, useMemo, useRef, useState } from 'react'; import { - EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -35,12 +34,6 @@ export const TOGGLE_PANEL_LABEL = i18n.translate('securitySolutionPackages.sideN defaultMessage: 'Toggle panel nav', }); -/** - * - `splitButton`: separate link row and spaces button to open the secondary panel (default) - * - `unifiedRow`: single row with trailing chevron; primary click navigates to the first child and toggles the panel - */ -export type SolutionSideNavInteractionVariant = 'splitButton' | 'unifiedRow'; - export interface SolutionSideNavProps { /** All the items to display in the side navigation */ items: SolutionSideNavItem[]; @@ -52,10 +45,6 @@ export interface SolutionSideNavProps { panelBottomOffset?: string; /** Css value for the top offset of the secondary panel. defaults to the generic kibana header height */ panelTopOffset?: string; - /** - * Presentation and interaction for panel opener rows. When `unifiedRow`, selected rows use primary background. - */ - navLinkInteractionVariant?: SolutionSideNavInteractionVariant; /** * The tracker function to enable navigation Telemetry, this has to be bound with the plugin `appId` * e.g.: usageCollection?.reportUiCounter?.bind(null, appId) @@ -72,7 +61,6 @@ export const SolutionSideNav: React.FC = React.memo(functi selectedId, panelBottomOffset, panelTopOffset, - navLinkInteractionVariant = 'splitButton', tracker, }) { const isMobileSize = useIsWithinBreakpoints(['xs', 's']); @@ -133,7 +121,6 @@ export const SolutionSideNav: React.FC = React.memo(functi isMobileSize={isMobileSize} onOpenPanelNav={openPanelNav} onClosePanelNav={onClosePanelNav} - navLinkInteractionVariant={navLinkInteractionVariant} /> @@ -145,7 +132,6 @@ export const SolutionSideNav: React.FC = React.memo(functi isMobileSize={isMobileSize} onOpenPanelNav={openPanelNav} onClosePanelNav={onClosePanelNav} - navLinkInteractionVariant={navLinkInteractionVariant} /> @@ -171,7 +157,6 @@ interface SolutionSideNavItemsProps { isMobileSize: boolean; onOpenPanelNav: (id: string) => void; onClosePanelNav: () => void; - navLinkInteractionVariant: SolutionSideNavInteractionVariant; categories?: SeparatorLinkCategory[]; } /** @@ -188,7 +173,6 @@ const SolutionSideNavItems: React.FC = React.memo( isMobileSize, onOpenPanelNav, onClosePanelNav, - navLinkInteractionVariant, }) { if (!categories?.length) { return ( @@ -202,7 +186,6 @@ const SolutionSideNavItems: React.FC = React.memo( isMobileSize={isMobileSize} onOpenPanelNav={onOpenPanelNav} onClosePanelNav={onClosePanelNav} - navLinkInteractionVariant={navLinkInteractionVariant} /> ))} @@ -236,7 +219,6 @@ const SolutionSideNavItems: React.FC = React.memo( isMobileSize={isMobileSize} onOpenPanelNav={onOpenPanelNav} onClosePanelNav={onClosePanelNav} - navLinkInteractionVariant={navLinkInteractionVariant} /> ))} @@ -255,7 +237,6 @@ interface SolutionSideNavItemProps { onOpenPanelNav: (id: string) => void; onClosePanelNav: () => void; isMobileSize: boolean; - navLinkInteractionVariant: SolutionSideNavInteractionVariant; } /** * The Solution side navigation item component. @@ -263,14 +244,7 @@ interface SolutionSideNavItemProps { * and it adds a button to open the item secondary panel if needed. */ const SolutionSideNavItem: React.FC = React.memo( - function SolutionSideNavItem({ - item, - isSelected, - isActive, - isMobileSize, - onOpenPanelNav, - navLinkInteractionVariant, - }) { + function SolutionSideNavItem({ item, isSelected, isActive, isMobileSize, onOpenPanelNav }) { const { euiTheme } = useEuiTheme(); const { tracker } = useTelemetryContext(); @@ -290,19 +264,14 @@ const SolutionSideNavItem: React.FC = React.memo( [childItems] ); - const effectiveHref = - navLinkInteractionVariant === 'unifiedRow' && firstPanelChild ? firstPanelChild.href : href; + const effectiveHref = firstPanelChild ? firstPanelChild.href : href; - const solutionSideNavItemStyles = SolutionSideNavItemStyles( - euiTheme, - navLinkInteractionVariant === 'unifiedRow' && isSelected ? 'primary' : 'default' - ); + const solutionSideNavItemStyles = SolutionSideNavItemStyles(euiTheme, 'primary'); const itemClassNames = classNames( 'solutionSideNavItem', { 'solutionSideNavItem--isSelected': isSelected }, solutionSideNavItemStyles ); - const buttonClassNames = classNames('solutionSideNavItemButton'); const hasPanelNav = useMemo( () => !isMobileSize && childItems != null && childItems.length > 0, @@ -310,7 +279,7 @@ const SolutionSideNavItem: React.FC = React.memo( ); const listItemLabel = useMemo(() => { - if (hasPanelNav && navLinkInteractionVariant === 'unifiedRow') { + if (hasPanelNav) { return ( = React.memo( ); } return label; - }, [hasPanelNav, navLinkInteractionVariant, label, id, euiTheme.size.s]); + }, [hasPanelNav, label, id, euiTheme.size.s]); const onLinkClicked: React.MouseEventHandler = useCallback( (ev) => { @@ -343,21 +312,21 @@ const SolutionSideNavItem: React.FC = React.memo( [tracker, id, onClick] ); - const onButtonClick: React.MouseEventHandler = useCallback(() => { + const onSubNavPanelOpen: React.MouseEventHandler = useCallback(() => { tracker?.(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.PANEL_NAVIGATION_TOGGLE}${id}`); onOpenPanelNav(id); }, [id, onOpenPanelNav, tracker]); - const onSingleRowLinkClick: React.MouseEventHandler = useCallback( + const onLinkClick: React.MouseEventHandler = useCallback( (ev) => { - ev.preventDefault(); if (!hasPanelNav) { onLinkClicked(ev); return; } - onButtonClick(ev); + ev.preventDefault(); + onSubNavPanelOpen(ev); }, - [hasPanelNav, onButtonClick, onLinkClicked] + [hasPanelNav, onSubNavPanelOpen, onLinkClicked] ); return ( @@ -371,9 +340,7 @@ const SolutionSideNavItem: React.FC = React.memo( label={listItemLabel} href={effectiveHref} wrapText - onClick={ - navLinkInteractionVariant === 'unifiedRow' ? onSingleRowLinkClick : onLinkClicked - } + onClick={onLinkClick} className={itemClassNames} color="text" size="s" @@ -382,21 +349,6 @@ const SolutionSideNavItem: React.FC = React.memo( /> - {hasPanelNav && navLinkInteractionVariant === 'splitButton' && ( - - - - )} {appendSeparator ? : } diff --git a/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts b/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts index 0667f44bb6826..d1c2fb0a7a025 100644 --- a/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/solutions/security/plugins/security_solution/common/experimental_features.ts @@ -252,12 +252,6 @@ export const allowedExperimentalValues = Object.freeze({ * Release: 9.4 */ prebuiltRulesDeprecationUIEnabled: false, - - /** - * Classic chrome only: refreshed Security side nav (Launchpad, Manage footer; unified row + panel behavior). - * Release: 9.4 - */ - securityClassicNavUpdate: true, }); type ExperimentalConfigKeys = Array; diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/links/app_links.ts b/x-pack/solutions/security/plugins/security_solution/public/app/links/app_links.ts index 670e84cc48d38..8993e4fed37bb 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/links/app_links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/links/app_links.ts @@ -70,7 +70,6 @@ export const getFilteredLinks = async ( const chatExperience: AIChatExperience = await firstValueFrom(chatExperience$); const filteredConfigurationsLinks = getConfigurationsLinks(chatExperience); - const isClassicNavUpdateEnabled = experimentalFeatures?.securityClassicNavUpdate ?? false; return Object.freeze([ dashboardsLinks, core.uiSettings.get(ENABLE_ALERTS_AND_ATTACKS_ALIGNMENT_SETTING, false) @@ -88,10 +87,7 @@ export const getFilteredLinks = async ( assetInventoryLinks, rulesLinks, siemMigrationsLinks, - onboardingLinks, managementFilteredLinks, - siemReadinessLinks, - aiValueLinks, - ...(isClassicNavUpdateEnabled ? [launchPadLinks] : []), + launchPadLinks, ]); }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/links/get_filtered_links.test.ts b/x-pack/solutions/security/plugins/security_solution/public/app/links/get_filtered_links.test.ts index e4f755628b167..c9b2a7c2bb3f4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/links/get_filtered_links.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/links/get_filtered_links.test.ts @@ -48,13 +48,13 @@ describe('getFilteredLinks', () => { mockCore.uiSettings.get$ = jest.fn().mockReturnValue(of(AIChatExperience.Classic)); }); - it('returns filtered links including AI Value links', async () => { + it('returns filtered links including launchpad', async () => { mockGetManagementFilteredLinks.mockResolvedValue(mockManagementLinks); const result = await getFilteredLinks(mockCore, mockPlugins, mockExperimentalFeatures); - expect(result).toContainEqual(expect.objectContaining({ id: SecurityPageName.aiValue })); expect(result).toContainEqual(mockManagementLinks); + expect(result).toContainEqual(expect.objectContaining({ id: SecurityPageName.launchpad })); expect(mockGetManagementFilteredLinks).toHaveBeenCalledWith( mockCore, mockPlugins, @@ -76,7 +76,7 @@ describe('getFilteredLinks', () => { expect(resultIds).toContain('alerts'); expect(resultIds).toContain('cases'); expect(resultIds).toContain('configurations'); - expect(resultIds).toContain('ai_value'); // AI Value is now included statically + expect(resultIds).toContain('launchpad'); }); it('returns a frozen array', async () => { @@ -117,7 +117,7 @@ describe('getFilteredLinks', () => { expect(resultIds).toContain('attack_discovery'); expect(resultIds).toContain('cases'); expect(resultIds).toContain('configurations'); - expect(resultIds).toContain('ai_value'); // AI Value is now included statically + expect(resultIds).toContain('launchpad'); }); it('includes all base links in the result when setting is enabled', async () => { @@ -136,7 +136,7 @@ describe('getFilteredLinks', () => { expect(resultIds).toContain('attack_discovery'); expect(resultIds).toContain('cases'); expect(resultIds).toContain('configurations'); - expect(resultIds).toContain('ai_value'); // AI Value is now included statically + expect(resultIds).toContain('launchpad'); }); }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.test.ts b/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.test.ts index d451bc0c139e7..8dc47fca6b791 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.test.ts @@ -13,7 +13,6 @@ import type { LinkInfo, LinkItem } from '../../../links'; import { useBreadcrumbsNav } from './use_breadcrumbs_nav'; import type { BreadcrumbsNav } from '../../../breadcrumbs'; import * as kibanaLib from '../../../lib/kibana'; -import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; jest.mock('../../../lib/kibana'); @@ -23,9 +22,6 @@ jest.mock('react-redux', () => ({ useDispatch: () => mockDispatch, })); -jest.mock('../../../hooks/use_experimental_features'); -const mockUseIsExperimentalFeatureEnabled = useIsExperimentalFeatureEnabled as jest.Mock; - const link1Id = 'link-1' as SecurityPageName; const link2Id = 'link-2' as SecurityPageName; const link3Id = 'link-3' as SecurityPageName; @@ -74,14 +70,13 @@ jest.mock('./trailing_breadcrumbs', () => ({ })); const landingBreadcrumb = { - href: 'get_started', + href: 'launchpad', text: 'Security', onClick: expect.any(Function), }; describe('useBreadcrumbsNav', () => { beforeEach(() => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); jest.clearAllMocks(); }); @@ -163,22 +158,7 @@ describe('useBreadcrumbsNav', () => { expect(reportEventMock).toHaveBeenCalled(); }); - it('should use SecurityPageName.landing when isClassicNavUpdateEnabled is false', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); - - renderHook(useBreadcrumbsNav); - - const calls = (mockSecuritySolutionUrl as jest.Mock).mock.calls; - const landingBreadcrumbCall = calls.find( - (call) => call[0].deepLinkId === SecurityPageName.landing - ); - - expect(landingBreadcrumbCall).toBeDefined(); - }); - - it('should use SecurityPageName.launchpad when isClassicNavUpdateEnabled is true', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); - + it('should use SecurityPageName.launchpad', () => { renderHook(useBreadcrumbsNav); const calls = (mockSecuritySolutionUrl as jest.Mock).mock.calls; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts b/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts index 95a744dd7b3be..ff43f0e9cb9b1 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts @@ -23,7 +23,6 @@ import type { LinkInfo } from '../../../links'; import { APP_NAME } from '../../../../../common/constants'; import { getTrailingBreadcrumbs } from './trailing_breadcrumbs'; import { useParentLinks } from '../../../links/links_hooks'; -import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; export const useBreadcrumbsNav = () => { const dispatch = useDispatch(); @@ -32,7 +31,6 @@ export const useBreadcrumbsNav = () => { const { navigateTo } = useNavigateTo(); const getSecuritySolutionUrl = useGetSecuritySolutionUrl(); const parentLinks = useParentLinks(routeProps.pageName); - const isClassicNavUpdateEnabled = useIsExperimentalFeatureEnabled('securityClassicNavUpdate'); useEffect(() => { // cases manages its own breadcrumbs @@ -40,37 +38,24 @@ export const useBreadcrumbsNav = () => { return; } - const leadingBreadcrumbs = getLeadingBreadcrumbs( - parentLinks, - getSecuritySolutionUrl, - isClassicNavUpdateEnabled - ); + const leadingBreadcrumbs = getLeadingBreadcrumbs(parentLinks, getSecuritySolutionUrl); const trailingBreadcrumbs = getTrailingBreadcrumbs(routeProps, getSecuritySolutionUrl); updateBreadcrumbsNav({ leading: addOnClicksHandlers(leadingBreadcrumbs, dispatch, navigateTo, telemetry), trailing: addOnClicksHandlers(trailingBreadcrumbs, dispatch, navigateTo, telemetry), }); - }, [ - routeProps, - parentLinks, - getSecuritySolutionUrl, - dispatch, - navigateTo, - telemetry, - isClassicNavUpdateEnabled, - ]); + }, [routeProps, parentLinks, getSecuritySolutionUrl, dispatch, navigateTo, telemetry]); }; const getLeadingBreadcrumbs = ( parentLinks: LinkInfo[], - getSecuritySolutionUrl: GetSecuritySolutionUrl, - isClassicNavUpdateEnabled: boolean + getSecuritySolutionUrl: GetSecuritySolutionUrl ): ChromeBreadcrumb[] => { const landingBreadcrumb: ChromeBreadcrumb = { text: APP_NAME, href: getSecuritySolutionUrl({ - deepLinkId: isClassicNavUpdateEnabled ? SecurityPageName.launchpad : SecurityPageName.landing, + deepLinkId: SecurityPageName.launchpad, }), }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts b/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts index 876d2fb9724f5..ddd4e7b733f27 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/categories.ts @@ -9,8 +9,7 @@ import { LinkCategoryType, type SeparatorLinkCategory } from '@kbn/security-solu import { SecurityPageName } from '../../../../../common'; export const getNavCategories = ( - enableAlertsAndAttacksAlignment?: boolean, - securityClassicNavUpdate?: boolean + enableAlertsAndAttacksAlignment?: boolean ): SeparatorLinkCategory[] => { const categories: SeparatorLinkCategory[] = [ { @@ -41,17 +40,5 @@ export const getNavCategories = ( }, ]; - return securityClassicNavUpdate - ? categories - : [ - ...categories, - { - type: LinkCategoryType.separator, - linkIds: [ - SecurityPageName.siemReadiness, - SecurityPageName.aiValue, - SecurityPageName.siemMigrationsLanding, - ], - }, - ]; + return categories; }; diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.test.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.test.tsx index 624db3246260c..e1b894f8cff99 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.test.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.test.tsx @@ -77,17 +77,6 @@ jest.mock('../../../../management/pages/policy/view/policy_hooks', () => ({ useIsPolicySettingsBarVisible: () => mockUseIsPolicySettingsBarVisible(), })); -const mockUseIsExperimentalFeatureEnabled = jest.fn((featureName: string) => { - if (featureName === 'securityClassicNavUpdate') { - return false; - } - return false; -}); -jest.mock('../../../hooks/use_experimental_features', () => ({ - useIsExperimentalFeatureEnabled: (featureName: string) => - mockUseIsExperimentalFeatureEnabled(featureName), -})); - const renderNav = (options?: { store?: ReturnType }) => render(, { wrapper: ({ children }) => {children}, @@ -101,7 +90,6 @@ describe('SecuritySideNav', () => { new BehaviorSubject(false).asObservable() ); useKibana().services.serverless = undefined; - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); }); it('should render main items', () => { @@ -118,7 +106,7 @@ describe('SecuritySideNav', () => { position: 'top', }, ], - categories: getNavCategories(false, false), + categories: getNavCategories(false), tracker: track, }) ); @@ -181,20 +169,18 @@ describe('SecuritySideNav', () => { ); }); - it('should render get started item', () => { + it('should render launchpad item', () => { mockUseNavLinks.mockReturnValue([ - { id: SecurityPageName.landing, title: 'Get started', sideNavIcon: 'rocket' }, + { id: SecurityPageName.launchpad, title: 'Launchpad', sideNavIcon: 'rocket' }, ]); renderNav(); expect(mockSolutionSideNav).toHaveBeenCalledWith( expect.objectContaining({ items: [ expect.objectContaining({ - id: SecurityPageName.landing, - label: 'Get started', - position: 'bottom', - iconType: 'rocket', - appendSeparator: true, + id: SecurityPageName.launchpad, + label: 'Launchpad', + position: 'top', }), ], }) @@ -265,7 +251,7 @@ describe('SecuritySideNav', () => { renderNav(); expect(mockSolutionSideNav).toHaveBeenCalledWith( expect.objectContaining({ - categories: getNavCategories(true, false), + categories: getNavCategories(true), }) ); }); @@ -275,109 +261,67 @@ describe('SecuritySideNav', () => { renderNav(); expect(mockSolutionSideNav).toHaveBeenCalledWith( expect.objectContaining({ - categories: getNavCategories(false, false), + categories: getNavCategories(false), }) ); }); }); - describe('securityClassicNavUpdate feature flag', () => { - beforeEach(() => { - useKibana().services.serverless = undefined; - }); - - it('should use splitButton interaction variant when classic nav update is disabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(false); - renderNav(); - expect(mockSolutionSideNav).toHaveBeenCalledWith( - expect.objectContaining({ - navLinkInteractionVariant: 'splitButton', - }) - ); - }); - - it('should use unifiedRow interaction variant when classic nav update is enabled and not serverless', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); - useKibana().services.serverless = undefined; - renderNav(); - expect(mockSolutionSideNav).toHaveBeenCalledWith( - expect.objectContaining({ - navLinkInteractionVariant: 'unifiedRow', - }) - ); - }); - - it('should place administration item in footer when classic nav update is enabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); - mockUseNavLinks.mockReturnValue([alertsNavLink, settingsNavLink]); - renderNav(); - expect(mockSolutionSideNav).toHaveBeenCalledWith( - expect.objectContaining({ - items: expect.arrayContaining([ - expect.objectContaining({ - id: SecurityPageName.administration, - position: 'bottom', - }), - ]), - }) - ); - }); - - it('should not include administration item in body when classic nav update is enabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); - mockUseNavLinks.mockReturnValue([settingsNavLink, alertsNavLink]); - renderNav(); - const calls = mockSolutionSideNav.mock.calls; - const lastCall = calls[calls.length - 1]; - const items = lastCall[0].items; - const administrationItemsInBody = items.filter( - (item) => item.id === SecurityPageName.administration && item.position !== 'bottom' - ); - expect(administrationItemsInBody).toHaveLength(0); - }); + it('should place administration item in footer', () => { + mockUseNavLinks.mockReturnValue([alertsNavLink, settingsNavLink]); + renderNav(); + expect(mockSolutionSideNav).toHaveBeenCalledWith( + expect.objectContaining({ + items: expect.arrayContaining([ + expect.objectContaining({ + id: SecurityPageName.administration, + position: 'bottom', + }), + ]), + }) + ); + }); - it('should pass isClassicNavUpdateLayout true to getNavCategories when enabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); - useKibana().services.uiSettings.get = jest.fn().mockReturnValue(false); - renderNav(); - expect(mockSolutionSideNav).toHaveBeenCalledWith( - expect.objectContaining({ - categories: getNavCategories(false, true), - }) - ); - }); + it('should not include administration item in body', () => { + mockUseNavLinks.mockReturnValue([settingsNavLink, alertsNavLink]); + renderNav(); + const calls = mockSolutionSideNav.mock.calls; + const lastCall = calls[calls.length - 1]; + const items = lastCall[0].items; + const administrationItemsInBody = items.filter( + (item) => item.id === SecurityPageName.administration && item.position !== 'bottom' + ); + expect(administrationItemsInBody).toHaveLength(0); + }); - it('should select launchpad when landing page is selected in classic nav layout', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); - mockUseRouteSpy.mockReturnValue([{ pageName: SecurityPageName.landing }]); - const landingNavLink: NavigationLink = { - id: SecurityPageName.landing, - title: 'Get started', - description: 'Get started description', - }; - mockUseNavLinks.mockReturnValue([landingNavLink, alertsNavLink, settingsNavLink]); - renderNav(); - expect(mockSolutionSideNav).toHaveBeenCalledWith( - expect.objectContaining({ - selectedId: 'securityGroup:launchpad', - }) - ); - }); + it('should select launchpad when landing page is selected', () => { + mockUseRouteSpy.mockReturnValue([{ pageName: SecurityPageName.landing }]); + const landingNavLink: NavigationLink = { + id: SecurityPageName.landing, + title: 'Get started', + description: 'Get started description', + }; + mockUseNavLinks.mockReturnValue([landingNavLink, alertsNavLink, settingsNavLink]); + renderNav(); + expect(mockSolutionSideNav).toHaveBeenCalledWith( + expect.objectContaining({ + selectedId: 'securityGroup:launchpad', + }) + ); + }); - it('should maintain top position for most items when classic nav update is enabled', () => { - mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); - mockUseNavLinks.mockReturnValue([alertsNavLink]); - renderNav(); - expect(mockSolutionSideNav).toHaveBeenCalledWith( - expect.objectContaining({ - items: [ - expect.objectContaining({ - id: SecurityPageName.alerts, - position: 'top', - }), - ], - }) - ); - }); + it('should maintain top position for most items', () => { + mockUseNavLinks.mockReturnValue([alertsNavLink]); + renderNav(); + expect(mockSolutionSideNav).toHaveBeenCalledWith( + expect.objectContaining({ + items: [ + expect.objectContaining({ + id: SecurityPageName.alerts, + position: 'top', + }), + ], + }) + ); }); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx b/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx index a72a3dffdf7ea..24e861b1024b0 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx +++ b/x-pack/solutions/security/plugins/security_solution/public/common/components/navigation/security_side_nav/security_side_nav.tsx @@ -15,13 +15,11 @@ import { import { i18nStrings } from '@kbn/security-solution-navigation/links'; import { SolutionSideNav, - type SolutionSideNavInteractionVariant, type SolutionSideNavItem, SolutionSideNavItemPosition, } from '@kbn/security-solution-side-nav'; import useObservable from 'react-use/lib/useObservable'; import { ENABLE_ALERTS_AND_ATTACKS_ALIGNMENT_SETTING } from '../../../../../common/constants'; -import { useIsExperimentalFeatureEnabled } from '../../../hooks/use_experimental_features'; import { useRouteSpy } from '../../../utils/route/use_route_spy'; import { type GetSecuritySolutionLinkProps, useGetSecuritySolutionLinkProps } from '../../links'; import { useNavLinks } from '../../../links/nav_links'; @@ -43,21 +41,12 @@ const flattenNavigationLinks = (links: NavigationLink[]): NavigationLink[] => ]); export const BOTTOM_BAR_HEIGHT = '50px'; -const getNavItemPosition = ( - id: SecurityPageName, - isClassicNavUpdateLayout: boolean -): SolutionSideNavItemPosition => { - if (isClassicNavUpdateLayout) { - return SolutionSideNavItemPosition.top; - } - return id === SecurityPageName.landing || id === SecurityPageName.administration +const getNavItemPosition = (id: SecurityPageName): SolutionSideNavItemPosition => { + return id === SecurityPageName.administration ? SolutionSideNavItemPosition.bottom : SolutionSideNavItemPosition.top; }; -const isGetStartedNavItem = (id: SecurityPageName, isClassicNavUpdateLayout: boolean): boolean => - !isClassicNavUpdateLayout && id === SecurityPageName.landing; - const LAUNCHPAD_PAGES: ReadonlySet = new Set([ SecurityPageName.landing, SecurityPageName.siemReadiness, @@ -72,16 +61,14 @@ const LAUNCHPAD_PAGES: ReadonlySet = new Set([ */ const formatLink = ( navLink: NavigationLink, - getSecuritySolutionLinkProps: GetSecuritySolutionLinkProps, - options: { isClassicNavUpdateLayout: boolean } + getSecuritySolutionLinkProps: GetSecuritySolutionLinkProps ): SolutionSideNavItem => { - const stripDashboardsPanel = - options.isClassicNavUpdateLayout && navLink.id === SecurityPageName.dashboards; + const stripDashboardsPanel = navLink.id === SecurityPageName.dashboards; return { id: navLink.id, label: navLink.title, - position: getNavItemPosition(navLink.id, options.isClassicNavUpdateLayout), + position: getNavItemPosition(navLink.id), ...getSecuritySolutionLinkProps({ deepLinkId: navLink.id }), ...(navLink.sideNavIcon && { iconType: navLink.sideNavIcon }), ...(navLink.categories?.length && !stripDashboardsPanel && { categories: navLink.categories }), @@ -104,30 +91,11 @@ const formatLink = ( }; }; -/** - * Formats the get started navigation links into the shape expected by the `SolutionSideNav` - */ -const formatGetStartedLink = ( - navLink: NavigationLink, - getSecuritySolutionLinkProps: GetSecuritySolutionLinkProps -): SolutionSideNavItem => ({ - id: navLink.id, - label: navLink.title, - iconType: navLink.sideNavIcon, - position: SolutionSideNavItemPosition.bottom, - appendSeparator: true, - ...getSecuritySolutionLinkProps({ deepLinkId: navLink.id }), -}); - -const useSolutionSideNavItems = (isClassicNavUpdateLayout: boolean) => { +const useSolutionSideNavItems = (): SolutionSideNavItem[] | undefined => { const navLinks = useNavLinks(); const getSecuritySolutionLinkProps = useGetSecuritySolutionLinkProps(); // adds href and onClick props const classicFooterItems = useMemo((): SolutionSideNavItem[] | null => { - if (!isClassicNavUpdateLayout) { - return null; - } - const flatNavLinks = flattenNavigationLinks(navLinks); const authorizedNavById = new Map( flatNavLinks @@ -209,9 +177,7 @@ const useSolutionSideNavItems = (isClassicNavUpdateLayout: boolean) => { const administrationFooter: SolutionSideNavItem | null = administrationNavLink != null && !administrationNavLink.disabled ? { - ...formatLink(administrationNavLink, getSecuritySolutionLinkProps, { - isClassicNavUpdateLayout, - }), + ...formatLink(administrationNavLink, getSecuritySolutionLinkProps), iconType: undefined, position: SolutionSideNavItemPosition.bottom, } @@ -221,14 +187,14 @@ const useSolutionSideNavItems = (isClassicNavUpdateLayout: boolean) => { ...(launchpad != null ? [launchpad] : []), ...(administrationFooter != null ? [administrationFooter] : []), ]; - }, [isClassicNavUpdateLayout, navLinks, getSecuritySolutionLinkProps]); + }, [navLinks, getSecuritySolutionLinkProps]); const sideNavItems = useMemo(() => { if (!navLinks?.length) { return undefined; } - const excluded = isClassicNavUpdateLayout ? new Set(LAUNCHPAD_PAGES) : undefined; + const excluded = new Set(LAUNCHPAD_PAGES); const bodyItems = navLinks.reduce((navItems, navLink) => { if (navLink.disabled) { @@ -238,38 +204,25 @@ const useSolutionSideNavItems = (isClassicNavUpdateLayout: boolean) => { return navItems; } - if (isClassicNavUpdateLayout && navLink.id === SecurityPageName.administration) { + if (navLink.id === SecurityPageName.administration) { return navItems; } - if (isGetStartedNavItem(navLink.id, isClassicNavUpdateLayout)) { - navItems.push(formatGetStartedLink(navLink, getSecuritySolutionLinkProps)); - } else { - navItems.push( - formatLink(navLink, getSecuritySolutionLinkProps, { isClassicNavUpdateLayout }) - ); - } + navItems.push(formatLink(navLink, getSecuritySolutionLinkProps)); + return navItems; }, []); - if (isClassicNavUpdateLayout && classicFooterItems) { - return [...bodyItems, ...classicFooterItems]; - } - - return bodyItems; - }, [navLinks, getSecuritySolutionLinkProps, isClassicNavUpdateLayout, classicFooterItems]); + return [...bodyItems, ...(classicFooterItems ? classicFooterItems : [])]; + }, [navLinks, getSecuritySolutionLinkProps, classicFooterItems]); return sideNavItems; }; -const useSelectedId = (isClassicNavUpdateLayout: boolean): string => { +const useSelectedId = (): string => { const [{ pageName }] = useRouteSpy(); const [rootLinkInfo] = useParentLinks(pageName); - if (!isClassicNavUpdateLayout) { - return rootLinkInfo?.id ?? ''; - } - if (LAUNCHPAD_PAGES.has(pageName)) { return SecurityGroupName.launchpad; } @@ -297,18 +250,10 @@ const usePanelBottomOffset = (): string | undefined => { * It takes the links to render from the generic application `links` configs. */ export const SecuritySideNav: React.FC = () => { - const { uiSettings, serverless } = useKibana().services; - const isSecurityClassicNavUpdateEnabled = useIsExperimentalFeatureEnabled( - 'securityClassicNavUpdate' - ); - const isClassicNavUpdateLayout = isSecurityClassicNavUpdateEnabled && serverless == null; - - const navLinkInteractionVariant: SolutionSideNavInteractionVariant = isClassicNavUpdateLayout - ? 'unifiedRow' - : 'splitButton'; + const { uiSettings } = useKibana().services; - const items = useSolutionSideNavItems(isClassicNavUpdateLayout); - const selectedId = useSelectedId(isClassicNavUpdateLayout); + const items = useSolutionSideNavItems(); + const selectedId = useSelectedId(); const panelTopOffset = usePanelTopOffset(); const panelBottomOffset = usePanelBottomOffset(); @@ -317,8 +262,8 @@ export const SecuritySideNav: React.FC = () => { ENABLE_ALERTS_AND_ATTACKS_ALIGNMENT_SETTING, false ); - return getNavCategories(enableAlertsAndAttacksAlignment, isClassicNavUpdateLayout); - }, [uiSettings, isClassicNavUpdateLayout]); + return getNavCategories(enableAlertsAndAttacksAlignment); + }, [uiSettings]); if (!items) { return ; @@ -331,7 +276,6 @@ export const SecuritySideNav: React.FC = () => { selectedId={selectedId} panelTopOffset={panelTopOffset} panelBottomOffset={panelBottomOffset} - navLinkInteractionVariant={navLinkInteractionVariant} tracker={track} /> ); diff --git a/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/investigations/siem_migrations/rules/translated_rules_page.cy.ts b/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/investigations/siem_migrations/rules/translated_rules_page.cy.ts index 9a81d62d82f44..6b391879af928 100644 --- a/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/investigations/siem_migrations/rules/translated_rules_page.cy.ts +++ b/x-pack/solutions/security/test/security_solution_cypress/cypress/e2e/investigations/siem_migrations/rules/translated_rules_page.cy.ts @@ -30,7 +30,7 @@ import { role } from '../common/role'; // TODO: https://github.com/elastic/kibana/issues/228940 remove @skipInServerlessMKI tag when privileges issue is fixed describe( - 'Rule Migrations - Translated Rules Page (securityClassicNavUpdate enabled)', + 'Rule Migrations - Translated Rules Page', { tags: ['@ess', '@serverless', '@skipInServerlessMKI'], },