From 7917b2bd53d64126ef542e5f952bcff6d0bcf970 Mon Sep 17 00:00:00 2001 From: "paulina.shakirova" Date: Fri, 22 May 2026 16:56:54 +0200 Subject: [PATCH 1/8] [SideNav] Make solutions navigation respect 'visibleIn': ['sideNav'] --- .../application/browser/src/application.ts | 4 +- .../project_navigation_service.test.ts | 50 ++++++- .../services/project_navigation/utils.test.ts | 2 +- .../src/services/project_navigation/utils.ts | 4 +- .../plugins/shared/dev_tools/public/plugin.ts | 1 + .../shared/management/public/plugin.tsx | 5 +- .../public/plugin_deep_links.test.ts | 125 ++++++++++++++++++ .../shared/fleet/public/deep_links.test.ts | 107 +++++++++++++++ .../plugins/shared/fleet/public/deep_links.ts | 10 +- .../search_deep_links.test.ts | 80 +++++++++++ .../search_deep_links.ts | 22 ++- .../triggers_actions_ui/public/plugin.ts | 2 +- .../plugins/apm/public/plugin.ts | 8 +- .../plugins/infra/public/pages/logs/routes.ts | 4 + .../plugins/infra/public/plugin.test.ts | 40 ++++++ .../plugins/infra/public/plugin.ts | 102 +++++++------- .../plugins/observability/public/plugin.ts | 12 +- .../public/plugin.tsx | 2 +- .../observability_onboarding/public/plugin.ts | 2 +- .../plugins/profiling/public/plugin.ts | 3 + .../plugins/synthetics/public/plugin.ts | 2 + .../plugins/uptime/public/plugin.ts | 7 +- .../plugins/search_synonyms/public/plugin.ts | 2 +- 23 files changed, 513 insertions(+), 83 deletions(-) create mode 100644 src/platform/plugins/shared/management/public/plugin_deep_links.test.ts create mode 100644 x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts create mode 100644 x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts create mode 100644 x-pack/solutions/observability/plugins/infra/public/plugin.test.ts diff --git a/src/core/packages/application/browser/src/application.ts b/src/core/packages/application/browser/src/application.ts index 710740e5aa210..d0b21a9e02606 100644 --- a/src/core/packages/application/browser/src/application.ts +++ b/src/core/packages/application/browser/src/application.ts @@ -150,9 +150,9 @@ export interface App extends AppNavOptions { * * start() { * // later, when the navlink needs to be updated - * appUpdater.next(() => { + * appUpdater.next(() => ({ * visibleIn: ['globalSearch'], - * }) + * })) * } * ``` */ diff --git a/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts b/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts index 5a2f50f5773e3..487fa55a64556 100644 --- a/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts @@ -42,7 +42,7 @@ const getNavLink = (partial: Partial = {}): ChromeNavLink => ({ baseUrl: '/app', url: `/app/${partial.id ?? 'kibana'}`, href: `/app/${partial.id ?? 'kibana'}`, - visibleIn: ['globalSearch'], + visibleIn: ['globalSearch', 'sideNav'], ...partial, }); @@ -208,6 +208,48 @@ describe('initNavigation()', () => { expect(node.children?.[0].href).toBe('https://elastic.co'); }); + test('should filter out deepLinks that exclude sideNav from visibleIn', async () => { + const { projectNavigation: svc, navLinksService: nls } = setup({ + navLinkIds: ['management:genAiSettings'], + }); + + // Add a second link that is registered but explicitly excludes sideNav + const evalsNoSideNav: ChromeNavLink = getNavLink({ + id: 'management:evals', + title: 'MANAGEMENT:EVALS', + visibleIn: ['globalSearch'], + }); + const existing = nls.getAll(); + nls.getNavLinks$.mockReturnValue(of([...existing, evalsNoSideNav])); + nls.getAll.mockReturnValue([...existing, evalsNoSideNav]); + + svc.initNavigation( + 'es', + of({ + body: [ + { + id: 'group1', + type: 'navGroup', + children: [ + { link: 'management:genAiSettings' }, + { link: 'management:evals' }, // registered but no sideNav → removed + ], + }, + ], + }) + ); + + const treeDefinition = await lastValueFrom( + svc.getNavigation$().pipe( + take(1), + map((nav) => nav.navigationTree) + ) + ); + + const [node] = treeDefinition.body as [ChromeProjectNavigationNode]; + expect(node.children?.map((c) => c.id)).toEqual(['management:genAiSettings']); + }); + test('should filter out missing deepLinks (e.g. evals) from the navigation tree', async () => { const { projectNavigation: projectNavigationService } = setup({ navLinkIds: ['management:genAiSettings'], @@ -289,7 +331,7 @@ describe('initNavigation()', () => { id: 'foo', title: 'FOO', url: '/app/foo', - visibleIn: ['globalSearch'], + visibleIn: ['globalSearch', 'sideNav'], }, href: '/app/foo', id: 'foo', @@ -838,7 +880,7 @@ describe('getNavigation$() active nodes', () => { baseUrl: '/app', url: '/app/item1', href: '/app/item1', - visibleIn: ['globalSearch'], + visibleIn: ['globalSearch', 'sideNav'], }, }, ], @@ -892,7 +934,7 @@ describe('getNavigation$() active nodes', () => { baseUrl: '/app', url: '/app/item1', href: '/app/item1', - visibleIn: ['globalSearch'], + visibleIn: ['globalSearch', 'sideNav'], }, getIsActive: expect.any(Function), }, diff --git a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts index 27b89970fed64..d5554ab69679b 100644 --- a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts @@ -21,7 +21,7 @@ const getDeepLink = (id: string, path: string, title = ''): ChromeNavLink => ({ href: `http://mocked/kibana/foo/${path}`, title, baseUrl: '', - visibleIn: ['globalSearch'], + visibleIn: ['globalSearch', 'sideNav'], }); describe('flattenNav', () => { diff --git a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts index 8403cddacb938..2a4d0f6156043 100644 --- a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts +++ b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts @@ -177,8 +177,8 @@ function getNodeStatus( }, { cloudLinks }: { cloudLinks: CloudLinks } ): SideNavNodeStatus | 'remove' { - if (link && !deepLink) { - // If a link is provided, but no deepLink is found, don't render anything + if (link && (!deepLink || !deepLink.visibleIn.includes('sideNav'))) { + // If a link is provided but no deepLink found, or the app explicitly excluded sideNav return 'remove'; } diff --git a/src/platform/plugins/shared/dev_tools/public/plugin.ts b/src/platform/plugins/shared/dev_tools/public/plugin.ts index 64df73726f9b8..6be9b409ade8b 100644 --- a/src/platform/plugins/shared/dev_tools/public/plugin.ts +++ b/src/platform/plugins/shared/dev_tools/public/plugin.ts @@ -130,6 +130,7 @@ export class DevToolsPlugin implements Plugin { id: tool.id, title: tool.title as string, path: `#/${tool.id}`, + visibleIn: ['globalSearch', 'sideNav'], }; if (!devtoolsDeeplinkIds.some((id) => id === deepLink.id)) { throw new Error('Deeplink must be registered in package.'); diff --git a/src/platform/plugins/shared/management/public/plugin.tsx b/src/platform/plugins/shared/management/public/plugin.tsx index a274b43946774..c50c4fca84c2c 100644 --- a/src/platform/plugins/shared/management/public/plugin.tsx +++ b/src/platform/plugins/shared/management/public/plugin.tsx @@ -83,7 +83,10 @@ export class ManagementPlugin title: mgmtApp.title, path: mgmtApp.basePath, keywords: mgmtApp.keywords, - ...(mgmtApp.visibleIn ? { visibleIn: mgmtApp.visibleIn } : {}), + // Default includes 'sideNav' so all management sections appear in solution nav. + // Sections that explicitly set visibleIn (e.g. to hide from globalSearch) must + // also include 'sideNav' if they want to remain visible in solution nav trees. + visibleIn: mgmtApp.visibleIn ?? ['globalSearch', 'sideNav'], })), })); diff --git a/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts b/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts new file mode 100644 index 0000000000000..2e06e187b4fde --- /dev/null +++ b/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { coreMock } from '@kbn/core/public/mocks'; +import { ManagementPlugin } from './plugin'; +import type { AppUpdater, AppDeepLink } from '@kbn/core/public'; + +const mockShare = { + url: { + locators: { + create: jest.fn().mockReturnValue({ id: 'mock-locator' }), + }, + }, +} as any; + +function createPlugin() { + const context = coreMock.createPluginInitializerContext(); + return new ManagementPlugin(context); +} + +function getDeepLinksFromUpdater(plugin: ManagementPlugin): AppDeepLink[] { + const updaterFn: AppUpdater = (plugin as any).appUpdater.getValue(); + const result = updaterFn({} as any); + return (result?.deepLinks as AppDeepLink[]) ?? []; +} + +describe('ManagementPlugin appUpdater deep link visibleIn', () => { + it('defaults visibleIn to globalSearch and sideNav for apps without an explicit visibleIn', () => { + const plugin = createPlugin(); + const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); + + setup.sections.section.kibana.registerApp({ + id: 'test-no-visible-in', + title: 'Test App No visibleIn', + mount: jest.fn(), + }); + + const deepLinks = getDeepLinksFromUpdater(plugin); + const kibana = deepLinks.find((s) => s.id === 'kibana'); + const app = kibana?.deepLinks?.find((a) => a.id === 'test-no-visible-in'); + + expect(app).toBeDefined(); + expect(app?.visibleIn).toEqual(['globalSearch', 'sideNav']); + }); + + it('preserves explicit visibleIn when set to sideNav-only', () => { + const plugin = createPlugin(); + const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); + + setup.sections.section.kibana.registerApp({ + id: 'test-sidenav-only', + title: 'Test App sideNav only', + mount: jest.fn(), + visibleIn: ['sideNav'], + }); + + const deepLinks = getDeepLinksFromUpdater(plugin); + const kibana = deepLinks.find((s) => s.id === 'kibana'); + const app = kibana?.deepLinks?.find((a) => a.id === 'test-sidenav-only'); + + expect(app?.visibleIn).toEqual(['sideNav']); + }); + + it('preserves explicit visibleIn when set to globalSearch-only', () => { + const plugin = createPlugin(); + const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); + + setup.sections.section.kibana.registerApp({ + id: 'test-global-only', + title: 'Test App globalSearch only', + mount: jest.fn(), + visibleIn: ['globalSearch'], + }); + + const deepLinks = getDeepLinksFromUpdater(plugin); + const kibana = deepLinks.find((s) => s.id === 'kibana'); + const app = kibana?.deepLinks?.find((a) => a.id === 'test-global-only'); + + expect(app?.visibleIn).toEqual(['globalSearch']); + }); + + it('excludes apps with hideFromGlobalSearch:true and no explicit visibleIn', () => { + const plugin = createPlugin(); + const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); + + setup.sections.section.kibana.registerApp({ + id: 'test-hidden', + title: 'Hidden App', + mount: jest.fn(), + hideFromGlobalSearch: true, + }); + + const deepLinks = getDeepLinksFromUpdater(plugin); + const kibana = deepLinks.find((s) => s.id === 'kibana'); + const app = kibana?.deepLinks?.find((a) => a.id === 'test-hidden'); + + expect(app).toBeUndefined(); + }); + + it('includes apps with hideFromGlobalSearch:true when explicit visibleIn is set', () => { + const plugin = createPlugin(); + const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); + + setup.sections.section.kibana.registerApp({ + id: 'test-hidden-with-visible-in', + title: 'Hidden App With visibleIn', + mount: jest.fn(), + hideFromGlobalSearch: true, + visibleIn: ['sideNav'], + }); + + const deepLinks = getDeepLinksFromUpdater(plugin); + const kibana = deepLinks.find((s) => s.id === 'kibana'); + const app = kibana?.deepLinks?.find((a) => a.id === 'test-hidden-with-visible-in'); + + expect(app).toBeDefined(); + expect(app?.visibleIn).toEqual(['sideNav']); + }); +}); diff --git a/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts b/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts new file mode 100644 index 0000000000000..0b27fc477e01f --- /dev/null +++ b/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts @@ -0,0 +1,107 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { allowedExperimentalValues } from '../common/experimental_features'; +import type { FleetAuthz } from '../common'; + +import { getFleetDeepLinks, FleetDeepLinkId } from './deep_links'; + +const authorizedAuthz: FleetAuthz = { + fleet: { + all: true, + setup: true, + readEnrollmentTokens: true, + readAgentPolicies: true, + allAgentPolicies: true, + readAgents: true, + allAgents: true, + readSettings: true, + allSettings: true, + generateAgentReports: true, + addAgents: true, + addFleetServers: true, + }, + integrations: { + all: true, + readPackageInfo: true, + readInstalledPackages: true, + installPackages: true, + upgradePackages: true, + removePackages: true, + uploadPackages: true, + readPackageSettings: true, + writePackageSettings: true, + readIntegrationPolicies: true, + writeIntegrationPolicies: true, + }, +}; + +const unauthorizedAuthz: FleetAuthz = { + fleet: { + all: false, + setup: false, + readEnrollmentTokens: false, + readAgentPolicies: false, + allAgentPolicies: false, + readAgents: false, + allAgents: false, + readSettings: false, + allSettings: false, + generateAgentReports: false, + addAgents: false, + addFleetServers: false, + }, + integrations: { + all: false, + readPackageInfo: false, + readInstalledPackages: false, + installPackages: false, + upgradePackages: false, + removePackages: false, + uploadPackages: false, + readPackageSettings: false, + writePackageSettings: false, + readIntegrationPolicies: false, + writeIntegrationPolicies: false, + }, +}; + +const authzGatedIds = [ + FleetDeepLinkId.agents, + FleetDeepLinkId.policies, + FleetDeepLinkId.enrollmentTokens, + FleetDeepLinkId.uninstallTokens, + FleetDeepLinkId.settings, +]; + +describe('getFleetDeepLinks', () => { + it('includes sideNav in visibleIn for all authz-gated links when authorized', () => { + const links = getFleetDeepLinks(allowedExperimentalValues, authorizedAuthz); + for (const id of authzGatedIds) { + expect(links.find((l) => l.id === id)?.visibleIn).toEqual(['globalSearch', 'sideNav']); + } + }); + + it('returns empty visibleIn for all authz-gated links when unauthorized', () => { + const links = getFleetDeepLinks(allowedExperimentalValues, unauthorizedAuthz); + for (const id of authzGatedIds) { + expect(links.find((l) => l.id === id)?.visibleIn).toEqual([]); + } + }); + + it('returns empty visibleIn for all authz-gated links when authz is undefined', () => { + const links = getFleetDeepLinks(allowedExperimentalValues, undefined); + for (const id of authzGatedIds) { + expect(links.find((l) => l.id === id)?.visibleIn).toEqual([]); + } + }); + + it('dataStreams link has no visibleIn restriction', () => { + const links = getFleetDeepLinks(allowedExperimentalValues, undefined); + expect(links.find((l) => l.id === FleetDeepLinkId.dataStreams)?.visibleIn).toBeUndefined(); + }); +}); diff --git a/x-pack/platform/plugins/shared/fleet/public/deep_links.ts b/x-pack/platform/plugins/shared/fleet/public/deep_links.ts index 27a75fcddbbfb..8b28a6f527dd2 100644 --- a/x-pack/platform/plugins/shared/fleet/public/deep_links.ts +++ b/x-pack/platform/plugins/shared/fleet/public/deep_links.ts @@ -31,7 +31,7 @@ export const getFleetDeepLinks: ( id: FleetDeepLinkId.agents, title: i18n.translate('xpack.fleet.deepLinks.agents.title', { defaultMessage: 'Agents' }), path: FLEET_ROUTING_PATHS.agents, - visibleIn: !authz?.fleet.readAgents ? [] : ['globalSearch'], + visibleIn: !authz?.fleet.readAgents ? [] : ['globalSearch', 'sideNav'], }, { id: FleetDeepLinkId.policies, @@ -39,7 +39,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Agent policies', }), path: FLEET_ROUTING_PATHS.policies, - visibleIn: !authz?.fleet.readAgentPolicies ? [] : ['globalSearch'], + visibleIn: !authz?.fleet.readAgentPolicies ? [] : ['globalSearch', 'sideNav'], }, { id: FleetDeepLinkId.enrollmentTokens, @@ -47,7 +47,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Enrollment tokens', }), path: FLEET_ROUTING_PATHS.enrollment_tokens, - visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch'], + visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch', 'sideNav'], }, { id: FleetDeepLinkId.uninstallTokens, @@ -55,7 +55,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Uninstall tokens', }), path: FLEET_ROUTING_PATHS.uninstall_tokens, - visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch'], + visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch', 'sideNav'], }, { id: FleetDeepLinkId.dataStreams, @@ -70,7 +70,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Settings', }), path: FLEET_ROUTING_PATHS.settings, - visibleIn: !authz?.fleet.readSettings ? [] : ['globalSearch'], + visibleIn: !authz?.fleet.readSettings ? [] : ['globalSearch', 'sideNav'], }, ]; }; diff --git a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts new file mode 100644 index 0000000000000..8a2c15072df69 --- /dev/null +++ b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getDefaultMlCapabilities } from '@kbn/ml-common-types/capabilities'; +import type { MlCapabilities } from '@kbn/ml-common-types/capabilities'; +import { getDeepLinks } from './search_deep_links'; + +const fullCapabilities: MlCapabilities = { + ...getDefaultMlCapabilities(), + canUseAiops: true, +}; + +describe('getDeepLinks', () => { + it('visible nav links include both globalSearch and sideNav', () => { + const links = getDeepLinks(true, fullCapabilities, false); + const anomalyDetection = links.find((l) => l.id === 'anomalyDetection'); + const dfa = links.find((l) => l.id === 'dataFrameAnalytics'); + const aiOps = links.find((l) => l.id === 'aiOps'); + + expect(links.find((l) => l.id === 'overview')?.visibleIn).toEqual(['globalSearch', 'sideNav']); + expect(links.find((l) => l.id === 'dataVisualizer')?.visibleIn).toEqual([ + 'globalSearch', + 'sideNav', + ]); + expect(anomalyDetection?.deepLinks?.find((l) => l.id === 'anomalyExplorer')?.visibleIn).toEqual( + ['globalSearch', 'sideNav'] + ); + expect( + anomalyDetection?.deepLinks?.find((l) => l.id === 'singleMetricViewer')?.visibleIn + ).toEqual(['globalSearch', 'sideNav']); + expect(dfa?.deepLinks?.find((l) => l.id === 'resultExplorer')?.visibleIn).toEqual([ + 'globalSearch', + 'sideNav', + ]); + expect(aiOps?.deepLinks?.find((l) => l.id === 'logRateAnalysis')?.visibleIn).toEqual([ + 'globalSearch', + 'sideNav', + ]); + }); + + it('hidden nav nodes (breadcrumb-only) use sideNav-only visibleIn', () => { + const links = getDeepLinks(true, fullCapabilities, false); + const aiOps = links.find((l) => l.id === 'aiOps'); + + expect(links.find((l) => l.id === 'fileUpload')?.visibleIn).toEqual(['sideNav']); + expect(links.find((l) => l.id === 'indexDataVisualizer')?.visibleIn).toEqual(['sideNav']); + expect(links.find((l) => l.id === 'indexDataVisualizerPage')?.visibleIn).toEqual(['sideNav']); + expect(links.find((l) => l.id === 'dataDrift')?.visibleIn).toEqual(['sideNav']); + expect(links.find((l) => l.id === 'dataDriftPage')?.visibleIn).toEqual(['sideNav']); + expect(aiOps?.deepLinks?.find((l) => l.id === 'logRateAnalysisPage')?.visibleIn).toEqual([ + 'sideNav', + ]); + expect(aiOps?.deepLinks?.find((l) => l.id === 'logPatternAnalysisPage')?.visibleIn).toEqual([ + 'sideNav', + ]); + expect(aiOps?.deepLinks?.find((l) => l.id === 'changePointDetectionsPage')?.visibleIn).toEqual([ + 'sideNav', + ]); + }); + + it('omits links when capabilities or license are insufficient', () => { + expect( + getDeepLinks(false, fullCapabilities, false).find((l) => l.id === 'overview') + ).toBeUndefined(); + expect( + getDeepLinks(true, { ...fullCapabilities, isADEnabled: false }, false).find( + (l) => l.id === 'anomalyDetection' + ) + ).toBeUndefined(); + expect( + getDeepLinks(true, { ...fullCapabilities, canUseAiops: false }, false).find( + (l) => l.id === 'aiOps' + ) + ).toBeUndefined(); + }); +}); diff --git a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts index 8810503e07c59..d0e1534861d4d 100644 --- a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts +++ b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts @@ -28,6 +28,7 @@ function createDeepLinks( defaultMessage: 'Overview', }), path: `/${ML_PAGES.OVERVIEW}`, + visibleIn: ['globalSearch', 'sideNav'], }; }, @@ -46,6 +47,7 @@ function createDeepLinks( defaultMessage: 'Anomaly explorer', }), path: `/${ML_PAGES.ANOMALY_EXPLORER}`, + visibleIn: ['globalSearch', 'sideNav'], }, { id: 'singleMetricViewer', @@ -53,6 +55,7 @@ function createDeepLinks( defaultMessage: 'Single metric viewer', }), path: `/${ML_PAGES.SINGLE_METRIC_VIEWER}`, + visibleIn: ['globalSearch', 'sideNav'], }, { id: 'suppliedConfigurations', @@ -82,6 +85,7 @@ function createDeepLinks( defaultMessage: 'Results explorer', }), path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION}`, + visibleIn: ['globalSearch', 'sideNav'], }, { id: 'analyticsMap', @@ -89,6 +93,7 @@ function createDeepLinks( defaultMessage: 'Analytics map', }), path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_MAP}`, + visibleIn: ['globalSearch', 'sideNav'], }, ], }; @@ -140,6 +145,7 @@ function createDeepLinks( defaultMessage: 'Log rate analysis', }), path: `/${ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT}`, + visibleIn: ['globalSearch', 'sideNav'], }, { id: 'logRateAnalysisPage', @@ -147,7 +153,7 @@ function createDeepLinks( defaultMessage: 'Log rate analysis', }), path: `/${ML_PAGES.AIOPS_LOG_RATE_ANALYSIS}`, - visibleIn: [], + visibleIn: ['sideNav'], }, { id: 'logPatternAnalysis', @@ -155,6 +161,7 @@ function createDeepLinks( defaultMessage: 'Log pattern analysis', }), path: `/${ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT}`, + visibleIn: ['globalSearch', 'sideNav'], }, { id: 'logPatternAnalysisPage', @@ -162,7 +169,7 @@ function createDeepLinks( defaultMessage: 'Log pattern analysis', }), path: `/${ML_PAGES.AIOPS_LOG_CATEGORIZATION}`, - visibleIn: [], + visibleIn: ['sideNav'], }, { id: 'changePointDetections', @@ -170,6 +177,7 @@ function createDeepLinks( defaultMessage: 'Change point detection', }), path: `/${ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT}`, + visibleIn: ['globalSearch', 'sideNav'], }, { id: 'changePointDetectionsPage', @@ -177,7 +185,7 @@ function createDeepLinks( defaultMessage: 'Change point detection', }), path: `/${ML_PAGES.AIOPS_CHANGE_POINT_DETECTION}`, - visibleIn: [], + visibleIn: ['sideNav'], }, ], }; @@ -203,6 +211,7 @@ function createDeepLinks( defaultMessage: 'Data visualizer', }), path: `/${ML_PAGES.DATA_VISUALIZER}`, + visibleIn: ['globalSearch', 'sideNav'], }; }, @@ -214,6 +223,7 @@ function createDeepLinks( }), keywords: ['CSV', 'JSON'], path: `/${ML_PAGES.DATA_VISUALIZER_FILE}`, + visibleIn: ['sideNav'], }; }, @@ -224,6 +234,7 @@ function createDeepLinks( defaultMessage: 'Index data visualizer', }), path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_SELECT}`, + visibleIn: ['sideNav'], }; }, getIndexDataVisualizerPageDeepLink: (): AppDeepLink => { @@ -233,7 +244,7 @@ function createDeepLinks( defaultMessage: 'Index data visualizer', }), path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER}`, - visibleIn: [], + visibleIn: ['sideNav'], }; }, @@ -255,6 +266,7 @@ function createDeepLinks( defaultMessage: 'Data drift', }), path: `/${ML_PAGES.DATA_DRIFT_INDEX_SELECT}`, + visibleIn: ['sideNav'], }; }, getDataDriftPageDeepLink: (): AppDeepLink => { @@ -264,7 +276,7 @@ function createDeepLinks( defaultMessage: 'Data drift', }), path: `/${ML_PAGES.DATA_DRIFT}`, - visibleIn: [], + visibleIn: ['sideNav'], }; }, }; diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts index bec787c2627ec..36e873af14d99 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts @@ -299,7 +299,7 @@ export class Plugin title: i18n.translate('xpack.triggersActionsUI.rulesPage.title', { defaultMessage: 'Rules', }), - visibleIn: ['globalSearch'], + visibleIn: ['globalSearch', 'sideNav'], category: DEFAULT_APP_CATEGORIES.management, async mount(params: AppMountParameters) { const [coreStart, pluginsStart] = (await core.getStartServices()) as [ diff --git a/x-pack/solutions/observability/plugins/apm/public/plugin.ts b/x-pack/solutions/observability/plugins/apm/public/plugin.ts index 9313f3d1b4160..bd966f1eb5547 100644 --- a/x-pack/solutions/observability/plugins/apm/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/apm/public/plugin.ts @@ -464,24 +464,28 @@ export class ApmPlugin implements Plugin { id: 'service-groups-list', title: serviceGroupsTitle, path: '/service-groups', + visibleIn: ['sideNav'], }, { id: 'services', title: servicesTitle, path: '/services', + visibleIn: ['globalSearch', 'sideNav'], }, { id: 'traces', title: tracesTitle, path: '/traces', + visibleIn: ['globalSearch', 'sideNav'], }, - { id: 'service-map', title: serviceMapTitle, path: '/service-map' }, + { id: 'service-map', title: serviceMapTitle, path: '/service-map', visibleIn: ['sideNav'] }, { id: 'dependencies', title: dependenciesTitle, path: '/dependencies/inventory', + visibleIn: ['globalSearch', 'sideNav'], }, - { id: 'settings', title: apmSettingsTitle, path: '/settings' }, + { id: 'settings', title: apmSettingsTitle, path: '/settings', visibleIn: ['sideNav'] }, { id: 'storage-explorer', title: apmStorageExplorerTitle, diff --git a/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts b/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts index c575492a63e37..65470583d8c05 100644 --- a/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts +++ b/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts @@ -5,12 +5,14 @@ * 2.0. */ +import type { AppDeepLinkLocations } from '@kbn/core/public'; import { logsAnomaliesTitle, logCategoriesTitle } from '../../translations'; export interface LogsRoute { id: string; title: string; path: string; + visibleIn?: AppDeepLinkLocations[]; } export interface LogsAppRoutes { @@ -24,11 +26,13 @@ export const getLogsAppRoutes = () => { id: 'anomalies', title: logsAnomaliesTitle, path: '/anomalies', + visibleIn: ['globalSearch', 'sideNav'], }, logsCategories: { id: 'log-categories', title: logCategoriesTitle, path: '/log-categories', + visibleIn: ['globalSearch', 'sideNav'], }, }; diff --git a/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts b/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts new file mode 100644 index 0000000000000..a15ee7d0c0c41 --- /dev/null +++ b/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getInfraDeepLinks } from './plugin'; + +describe('getInfraDeepLinks', () => { + it('visible nav links include both globalSearch and sideNav', () => { + const links = getInfraDeepLinks({ metricsExplorerEnabled: false }); + + expect(links.find((l) => l.id === 'inventory')?.visibleIn).toEqual(['globalSearch', 'sideNav']); + expect(links.find((l) => l.id === 'hosts')?.visibleIn).toEqual(['globalSearch', 'sideNav']); + }); + + it('settings is sideNav-only (hidden breadcrumb node)', () => { + const links = getInfraDeepLinks({ metricsExplorerEnabled: false }); + expect(links.find((l) => l.id === 'settings')?.visibleIn).toEqual(['sideNav']); + }); + + it('assetDetails is hidden from all nav locations', () => { + const links = getInfraDeepLinks({ metricsExplorerEnabled: false }); + expect(links.find((l) => l.id === 'assetDetails')?.visibleIn).toEqual([]); + }); + + it('includes metrics-explorer with globalSearch and sideNav when feature flag is enabled', () => { + const links = getInfraDeepLinks({ metricsExplorerEnabled: true }); + expect(links.find((l) => l.id === 'metrics-explorer')?.visibleIn).toEqual([ + 'globalSearch', + 'sideNav', + ]); + }); + + it('omits metrics-explorer when feature flag is disabled', () => { + const links = getInfraDeepLinks({ metricsExplorerEnabled: false }); + expect(links.find((l) => l.id === 'metrics-explorer')).toBeUndefined(); + }); +}); diff --git a/x-pack/solutions/observability/plugins/infra/public/plugin.ts b/x-pack/solutions/observability/plugins/infra/public/plugin.ts index 18bb5f92828f6..6a87488d90092 100644 --- a/x-pack/solutions/observability/plugins/infra/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/infra/public/plugin.ts @@ -68,6 +68,58 @@ import { import type { LogsAppRoutes, LogsRoute } from './pages/logs/routes'; import { getLogsAppRoutes } from './pages/logs/routes'; +// !! Need to be kept in sync with the routes in x-pack/solutions/observability/plugins/infra/public/pages/metrics/index.tsx +export const getInfraDeepLinks = ({ + metricsExplorerEnabled, +}: { + metricsExplorerEnabled: boolean; +}): AppDeepLink[] => { + const visibleIn: AppDeepLinkLocations[] = ['globalSearch', 'sideNav']; + + return [ + { + id: 'inventory', + title: inventoryTitle, + path: '/inventory', + visibleIn, + }, + { + id: 'hosts', + title: i18n.translate('xpack.infra.homePage.metricsHostsTabTitle', { + defaultMessage: 'Hosts', + }), + path: '/hosts', + visibleIn, + }, + ...(metricsExplorerEnabled + ? [ + { + id: 'metrics-explorer', + title: i18n.translate('xpack.infra.homePage.metricsExplorerTabTitle', { + defaultMessage: 'Metrics Explorer', + }), + path: '/explorer', + visibleIn: ['globalSearch', 'sideNav'] as AppDeepLinkLocations[], + }, + ] + : []), + { + id: 'settings', + title: i18n.translate('xpack.infra.homePage.settingsTabTitle', { + defaultMessage: 'Settings', + }), + path: '/settings', + visibleIn: ['sideNav'], + }, + { + id: 'assetDetails', + title: '', // Internal deep link, not shown in the UI. Title is dynamically set in the app. + path: '/detail', + visibleIn: [], + }, + ]; +}; + export class Plugin implements InfraClientPluginClass { public config: InfraPublicConfig; private inventoryViews: InventoryViewsService; @@ -215,56 +267,6 @@ export class Plugin implements InfraClientPluginClass { }, }); - // !! Need to be kept in sync with the routes in x-pack/solutions/observability/plugins/infra/public/pages/metrics/index.tsx - const getInfraDeepLinks = ({ - metricsExplorerEnabled, - }: { - metricsExplorerEnabled: boolean; - }): AppDeepLink[] => { - const visibleIn: AppDeepLinkLocations[] = ['globalSearch']; - - return [ - { - id: 'inventory', - title: inventoryTitle, - path: '/inventory', - visibleIn, - }, - { - id: 'hosts', - title: i18n.translate('xpack.infra.homePage.metricsHostsTabTitle', { - defaultMessage: 'Hosts', - }), - path: '/hosts', - visibleIn, - }, - ...(metricsExplorerEnabled - ? [ - { - id: 'metrics-explorer', - title: i18n.translate('xpack.infra.homePage.metricsExplorerTabTitle', { - defaultMessage: 'Metrics Explorer', - }), - path: '/explorer', - }, - ] - : []), - { - id: 'settings', - title: i18n.translate('xpack.infra.homePage.settingsTabTitle', { - defaultMessage: 'Settings', - }), - path: '/settings', - }, - { - id: 'assetDetails', - title: '', // Internal deep link, not shown in the UI. Title is dynamically set in the app. - path: '/detail', - visibleIn: [], - }, - ]; - }; - core.application.register({ id: 'metrics', title: i18n.translate('xpack.infra.metrics.pluginTitle', { diff --git a/x-pack/solutions/observability/plugins/observability/public/plugin.ts b/x-pack/solutions/observability/plugins/observability/public/plugin.ts index 45a4bd19f637f..650958ff9e239 100644 --- a/x-pack/solutions/observability/plugins/observability/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/observability/public/plugin.ts @@ -218,7 +218,7 @@ export class Plugin }), order: 8001, path: ALERTS_PATH, - visibleIn: [], + visibleIn: ['sideNav'], keywords: ['alerts', 'rules'], }, { @@ -258,13 +258,13 @@ export class Plugin extend: { [CasesDeepLinkId.cases]: { order: 8003, - visibleIn: [], + visibleIn: ['sideNav'], }, [CasesDeepLinkId.casesCreate]: { - visibleIn: [], + visibleIn: ['sideNav'], }, [CasesDeepLinkId.casesConfigure]: { - visibleIn: [], + visibleIn: ['sideNav'], }, }, }) @@ -360,8 +360,8 @@ export class Plugin 'experience', ], visibleIn: Boolean(pluginsSetup.serverless) - ? ['home', 'kibanaOverview'] - : ['globalSearch', 'home', 'kibanaOverview', 'sideNav'], + ? ['sideNav', 'home', 'kibanaOverview'] + : ['globalSearch', 'sideNav', 'home', 'kibanaOverview'], }; coreSetup.application.register(app); diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx index 87ec409261a73..728e87cd3cf85 100644 --- a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx +++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx @@ -67,7 +67,7 @@ export class ObservabilityAIAssistantAppPlugin euiIconType: 'logoObservability', appRoute: '/app/observabilityAIAssistant', category: DEFAULT_APP_CATEGORIES.observability, - visibleIn: [], + visibleIn: ['sideNav'], deepLinks: [ { id: 'conversations', diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts index 11b696d9ff5a5..1dfc4e3aac8d0 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts @@ -123,7 +123,7 @@ export class ObservabilityOnboardingPlugin }, }); }, - visibleIn: ['globalSearch'], + visibleIn: ['globalSearch', 'sideNav'], }); this.locators = { diff --git a/x-pack/solutions/observability/plugins/profiling/public/plugin.ts b/x-pack/solutions/observability/plugins/profiling/public/plugin.ts index bdb69f2a11b5b..9cdc5c82ee0c2 100644 --- a/x-pack/solutions/observability/plugins/profiling/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/profiling/public/plugin.ts @@ -47,6 +47,7 @@ export class ProfilingPlugin defaultMessage: 'Stacktraces', }), path: '/stacktraces', + visibleIn: ['globalSearch', 'sideNav'], }, { id: 'flamegraphs', @@ -54,6 +55,7 @@ export class ProfilingPlugin defaultMessage: 'Flamegraphs', }), path: '/flamegraphs', + visibleIn: ['globalSearch', 'sideNav'], }, { id: 'functions', @@ -61,6 +63,7 @@ export class ProfilingPlugin defaultMessage: 'Functions', }), path: '/functions', + visibleIn: ['globalSearch', 'sideNav'], }, ]; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts b/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts index 5ed02fa5f4be4..e6875e694de23 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts @@ -213,6 +213,7 @@ export class SyntheticsPlugin defaultMessage: 'Monitors', }), path: '/', + visibleIn: ['globalSearch', 'sideNav'], }, { id: 'certificates', @@ -220,6 +221,7 @@ export class SyntheticsPlugin defaultMessage: 'TLS Certificates', }), path: '/certificates', + visibleIn: ['globalSearch', 'sideNav'], }, ], mount: async (params: AppMountParameters) => { diff --git a/x-pack/solutions/observability/plugins/uptime/public/plugin.ts b/x-pack/solutions/observability/plugins/uptime/public/plugin.ts index 16f41837ab601..5fa5f00e65aae 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/uptime/public/plugin.ts @@ -206,7 +206,12 @@ export class UptimePlugin keywords: appKeywords, deepLinks: [ { id: 'Down monitors', title: 'Down monitors', path: '/?statusFilter=down' }, - { id: 'Certificates', title: 'TLS Certificates', path: '/certificates' }, + { + id: 'Certificates', + title: 'TLS Certificates', + path: '/certificates', + visibleIn: ['globalSearch', 'sideNav'], + }, { id: 'Settings', title: 'Settings', path: '/settings' }, ], mount: async (params: AppMountParameters) => { diff --git a/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts b/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts index 162d7c08da9b7..4dead1fd2cbca 100644 --- a/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts @@ -40,7 +40,7 @@ export class SearchSynonymsPlugin id: 'synonyms', path: '/', title: PLUGIN_TITLE, - visibleIn: ['globalSearch'], + visibleIn: ['globalSearch', 'sideNav'], }, ], async mount({ element, history }: AppMountParameters) { From d8fc6fe4cbe3a9a18239fcff60f2be31e6c17575 Mon Sep 17 00:00:00 2001 From: "paulina.shakirova" Date: Fri, 22 May 2026 17:32:41 +0200 Subject: [PATCH 2/8] Enhance type safety for deep link locations in ProfilingPlugin --- .../observability/plugins/profiling/public/plugin.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/x-pack/solutions/observability/plugins/profiling/public/plugin.ts b/x-pack/solutions/observability/plugins/profiling/public/plugin.ts index 9cdc5c82ee0c2..cc1c718387e23 100644 --- a/x-pack/solutions/observability/plugins/profiling/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/profiling/public/plugin.ts @@ -6,6 +6,7 @@ */ import type { + AppDeepLinkLocations, AppMountParameters, AppUpdater, CoreSetup, @@ -47,7 +48,7 @@ export class ProfilingPlugin defaultMessage: 'Stacktraces', }), path: '/stacktraces', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'sideNav'] as AppDeepLinkLocations[], }, { id: 'flamegraphs', @@ -55,7 +56,7 @@ export class ProfilingPlugin defaultMessage: 'Flamegraphs', }), path: '/flamegraphs', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'sideNav'] as AppDeepLinkLocations[], }, { id: 'functions', @@ -63,7 +64,7 @@ export class ProfilingPlugin defaultMessage: 'Functions', }), path: '/functions', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'sideNav'] as AppDeepLinkLocations[], }, ]; From 663745d0588b19690884a64d5faa2c3fb9c1a6c0 Mon Sep 17 00:00:00 2001 From: "paulina.shakirova" Date: Mon, 25 May 2026 18:11:26 +0200 Subject: [PATCH 3/8] [Navigation] Update deep link visibility to include 'classicSideNav' and 'solutionSideNav' This commit modifies the visibility settings for various applications and deep links to replace 'sideNav' with 'classicSideNav' and 'solutionSideNav'. The changes ensure that links are appropriately categorized for the new navigation structure, enhancing user experience across different navigation styles. Affected files include application registration, deep link definitions, and test cases to reflect the updated visibility settings. --- .../browser-internal/src/utils/constants.ts | 6 +- .../src/utils/get_app_info.test.ts | 20 +++---- .../application/browser/src/application.ts | 16 ++++-- .../src/classic/collapsible_nav.test.tsx | 2 +- .../src/classic/collapsible_nav.tsx | 2 +- .../navigation/to_navigation_items.test.tsx | 2 +- .../nav_links/nav_links_service.test.ts | 2 +- .../services/nav_links/to_nav_link.test.ts | 4 +- .../project_navigation_service.test.ts | 55 ++++++++++++++++--- .../services/project_navigation/utils.test.ts | 2 +- .../src/services/project_navigation/utils.ts | 4 +- .../private/kibana_overview/public/plugin.ts | 2 +- .../plugins/shared/dev_tools/public/plugin.ts | 2 +- .../plugins/shared/discover/public/plugin.tsx | 2 +- .../shared/management/public/plugin.tsx | 8 +-- .../public/plugin_deep_links.test.ts | 20 +++---- .../shared/visualizations/public/plugin.ts | 2 +- .../public/plugin.test.ts | 22 +++++--- .../workflows_management/public/plugin.ts | 8 +-- .../core_plugins/application_status.ts | 2 +- .../shared/agent_builder/public/register.ts | 2 +- .../shared/fleet/public/deep_links.test.ts | 7 ++- .../plugins/shared/fleet/public/deep_links.ts | 10 ++-- .../shared/inbox/public/plugin.test.ts | 4 +- .../plugins/shared/inbox/public/plugin.tsx | 2 +- .../shared/ingest_hub/public/plugin.test.tsx | 2 +- .../shared/ingest_hub/public/plugin.tsx | 2 +- .../search_deep_links.test.ts | 39 +++++++------ .../search_deep_links.ts | 34 ++++++------ .../shared/streams_app/public/plugin.tsx | 2 +- .../triggers_actions_ui/public/plugin.ts | 2 +- .../plugins/apm/public/plugin.ts | 22 ++++++-- .../plugins/infra/public/pages/logs/routes.ts | 4 +- .../plugins/infra/public/plugin.test.ts | 20 ++++--- .../plugins/infra/public/plugin.ts | 6 +- .../plugins/observability/public/plugin.ts | 12 ++-- .../public/plugin.tsx | 2 +- .../observability_onboarding/public/plugin.ts | 2 +- .../update_global_navigation.test.tsx | 40 +++++++++++--- .../services/update_global_navigation.tsx | 8 +-- .../plugins/profiling/public/plugin.ts | 10 ++-- .../plugins/synthetics/public/plugin.ts | 4 +- .../plugins/uptime/public/plugin.ts | 2 +- .../search_getting_started/public/plugin.ts | 2 +- .../plugins/search_homepage/public/plugin.ts | 2 +- .../search_playground/public/plugin.ts | 2 +- .../search_query_rules/public/plugin.ts | 2 +- .../plugins/search_synonyms/public/plugin.ts | 4 +- .../public/app/links/deep_links.test.ts | 16 +++--- .../public/app/links/deep_links.ts | 6 +- .../public/onboarding/links.ts | 2 +- .../public/application/register.ts | 2 +- 52 files changed, 286 insertions(+), 173 deletions(-) diff --git a/src/core/packages/application/browser-internal/src/utils/constants.ts b/src/core/packages/application/browser-internal/src/utils/constants.ts index 5b3e582b40623..e08141c5df71a 100644 --- a/src/core/packages/application/browser-internal/src/utils/constants.ts +++ b/src/core/packages/application/browser-internal/src/utils/constants.ts @@ -9,6 +9,10 @@ import type { AppDeepLinkLocations } from '@kbn/core-application-browser/src/application'; -export const DEFAULT_APP_VISIBILITY: AppDeepLinkLocations[] = ['globalSearch', 'sideNav']; +export const DEFAULT_APP_VISIBILITY: AppDeepLinkLocations[] = [ + 'globalSearch', + 'classicSideNav', + 'solutionSideNav', +]; export const DEFAULT_LINK_VISIBILITY: AppDeepLinkLocations[] = ['globalSearch']; diff --git a/src/core/packages/application/browser-internal/src/utils/get_app_info.test.ts b/src/core/packages/application/browser-internal/src/utils/get_app_info.test.ts index 02235f316ad3a..17bf113f1c352 100644 --- a/src/core/packages/application/browser-internal/src/utils/get_app_info.test.ts +++ b/src/core/packages/application/browser-internal/src/utils/get_app_info.test.ts @@ -41,7 +41,7 @@ describe('getAppInfo', () => { id: 'some-id', title: 'some-title', status: AppStatus.accessible, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], appRoute: `/app/some-id`, keywords: [], deepLinks: [], @@ -70,7 +70,7 @@ describe('getAppInfo', () => { id: 'some-id', title: 'some-title', status: AppStatus.accessible, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], appRoute: `/app/some-id`, keywords: [], deepLinks: [ @@ -98,7 +98,7 @@ describe('getAppInfo', () => { expect( getAppInfo( createApp({ - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], status: AppStatus.inaccessible, }) ) @@ -110,25 +110,25 @@ describe('getAppInfo', () => { expect( getAppInfo( createApp({ - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], status: AppStatus.accessible, }) ) ).toEqual( expect.objectContaining({ - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }) ); expect( getAppInfo( createApp({ // status is not set, default to accessible - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }) ) ).toEqual( expect.objectContaining({ - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }) ); }); @@ -159,7 +159,7 @@ describe('getAppInfo', () => { id: 'some-id', title: 'some-title', status: AppStatus.accessible, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], appRoute: `/app/some-id`, keywords: [], order: 3, @@ -192,7 +192,7 @@ describe('getAppInfo', () => { createApp({ deepLinks: [ createDeepLink({ - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }), ], }) @@ -201,7 +201,7 @@ describe('getAppInfo', () => { expect.objectContaining({ deepLinks: [ expect.objectContaining({ - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }), ], }) diff --git a/src/core/packages/application/browser/src/application.ts b/src/core/packages/application/browser/src/application.ts index d0b21a9e02606..9174d5e10599e 100644 --- a/src/core/packages/application/browser/src/application.ts +++ b/src/core/packages/application/browser/src/application.ts @@ -108,10 +108,13 @@ export interface App extends AppNavOptions { * - "globalSearch": the link will appear in the global search bar * - "home": the link will appear on the Kibana home page * - "kibanaOverview": the link will appear in the Kibana overview page - * - "sideNav": the link will appear in the side navigation. - * Note: "sideNav" will be deprecated when we change the navigation to "solutions" style. + * - "classicSideNav": the link will appear in the classic (hamburger) side navigation only. + * - "solutionSideNav": the link will appear in solution navs only, not in the classic side navigation. + * Use this when the link should be accessible from a solution nav but hidden from the classic hamburger menu. * - * @default ['globalSearch', 'sideNav'] + * To appear in both classic and solution navs, use `['classicSideNav', 'solutionSideNav']`. + * + * @default ['globalSearch', 'classicSideNav', 'solutionSideNav'] * unless the status is marked as `inaccessible`. * @note Set to `[]` (empty array) to hide this link */ @@ -254,7 +257,12 @@ export type PublicAppDeepLinkInfo = Omit) { return { diff --git a/src/core/packages/chrome/browser-components/src/classic/collapsible_nav.tsx b/src/core/packages/chrome/browser-components/src/classic/collapsible_nav.tsx index 60823d94c2daf..4bd52e8cd48d2 100644 --- a/src/core/packages/chrome/browser-components/src/classic/collapsible_nav.tsx +++ b/src/core/packages/chrome/browser-components/src/classic/collapsible_nav.tsx @@ -141,7 +141,7 @@ export function CollapsibleNav({ allLinks.filter( (link) => // Filterting out hidden links, - link.visibleIn.includes('sideNav') && + link.visibleIn.includes('classicSideNav') && // and non-data overview pages !overviewIDsToHide.includes(link.id) ), diff --git a/src/core/packages/chrome/browser-components/src/project/sidenav/navigation/to_navigation_items.test.tsx b/src/core/packages/chrome/browser-components/src/project/sidenav/navigation/to_navigation_items.test.tsx index fa275ed319988..22c3dd1faca4b 100644 --- a/src/core/packages/chrome/browser-components/src/project/sidenav/navigation/to_navigation_items.test.tsx +++ b/src/core/packages/chrome/browser-components/src/project/sidenav/navigation/to_navigation_items.test.tsx @@ -254,7 +254,7 @@ describe('hidden panel link', () => { baseUrl: '/', href: '/app/management', url: '/app/management', - visibleIn: ['sideNav'], + visibleIn: ['classicSideNav', 'solutionSideNav'], }, sideNavStatus: 'hidden', id: 'stack_management', diff --git a/src/core/packages/chrome/browser-internal/src/services/nav_links/nav_links_service.test.ts b/src/core/packages/chrome/browser-internal/src/services/nav_links/nav_links_service.test.ts index ff70f15dfaf5a..c45b6d09dd821 100644 --- a/src/core/packages/chrome/browser-internal/src/services/nav_links/nav_links_service.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/nav_links/nav_links_service.test.ts @@ -28,7 +28,7 @@ const availableApps: ReadonlyMap = new Map([ order: 50, title: 'Deep App 1', path: '/deepapp1', - visibleIn: ['sideNav'], + visibleIn: ['classicSideNav', 'solutionSideNav'], deepLinks: [ { id: 'deepApp2', diff --git a/src/core/packages/chrome/browser-internal/src/services/nav_links/to_nav_link.test.ts b/src/core/packages/chrome/browser-internal/src/services/nav_links/to_nav_link.test.ts index 27da0918c4847..4b64a6687aae6 100644 --- a/src/core/packages/chrome/browser-internal/src/services/nav_links/to_nav_link.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/nav_links/to_nav_link.test.ts @@ -47,7 +47,7 @@ describe('toNavLink', () => { order: 12, tooltip: 'tooltip', euiIconType: 'my-icon', - visibleIn: ['sideNav'], + visibleIn: ['classicSideNav', 'solutionSideNav'], }), basePath ); @@ -58,7 +58,7 @@ describe('toNavLink', () => { order: 12, tooltip: 'tooltip', euiIconType: 'my-icon', - visibleIn: ['sideNav'], + visibleIn: ['classicSideNav', 'solutionSideNav'], }) ); }); diff --git a/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts b/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts index 487fa55a64556..de1c6e21c74f8 100644 --- a/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts @@ -42,7 +42,7 @@ const getNavLink = (partial: Partial = {}): ChromeNavLink => ({ baseUrl: '/app', url: `/app/${partial.id ?? 'kibana'}`, href: `/app/${partial.id ?? 'kibana'}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], ...partial, }); @@ -208,12 +208,12 @@ describe('initNavigation()', () => { expect(node.children?.[0].href).toBe('https://elastic.co'); }); - test('should filter out deepLinks that exclude sideNav from visibleIn', async () => { + test('should filter out deepLinks that exclude solutionSideNav from visibleIn', async () => { const { projectNavigation: svc, navLinksService: nls } = setup({ navLinkIds: ['management:genAiSettings'], }); - // Add a second link that is registered but explicitly excludes sideNav + // Add a second link that is registered but explicitly excludes solutionSideNav const evalsNoSideNav: ChromeNavLink = getNavLink({ id: 'management:evals', title: 'MANAGEMENT:EVALS', @@ -232,7 +232,48 @@ describe('initNavigation()', () => { type: 'navGroup', children: [ { link: 'management:genAiSettings' }, - { link: 'management:evals' }, // registered but no sideNav → removed + { link: 'management:evals' }, // registered but no solutionSideNav → removed + ], + }, + ], + }) + ); + + const treeDefinition = await lastValueFrom( + svc.getNavigation$().pipe( + take(1), + map((nav) => nav.navigationTree) + ) + ); + + const [node] = treeDefinition.body as [ChromeProjectNavigationNode]; + expect(node.children?.map((c) => c.id)).toEqual(['management:genAiSettings']); + }); + + test('should filter out deepLinks that only include classicSideNav from visibleIn', async () => { + const { projectNavigation: svc, navLinksService: nls } = setup({ + navLinkIds: ['management:genAiSettings'], + }); + + const classicOnlyLink: ChromeNavLink = getNavLink({ + id: 'management:evals', + title: 'MANAGEMENT:EVALS', + visibleIn: ['globalSearch', 'classicSideNav'], + }); + const existing = nls.getAll(); + nls.getNavLinks$.mockReturnValue(of([...existing, classicOnlyLink])); + nls.getAll.mockReturnValue([...existing, classicOnlyLink]); + + svc.initNavigation( + 'es', + of({ + body: [ + { + id: 'group1', + type: 'navGroup', + children: [ + { link: 'management:genAiSettings' }, + { link: 'management:evals' }, // classicSideNav only → removed from solution nav ], }, ], @@ -331,7 +372,7 @@ describe('initNavigation()', () => { id: 'foo', title: 'FOO', url: '/app/foo', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }, href: '/app/foo', id: 'foo', @@ -880,7 +921,7 @@ describe('getNavigation$() active nodes', () => { baseUrl: '/app', url: '/app/item1', href: '/app/item1', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }, }, ], @@ -934,7 +975,7 @@ describe('getNavigation$() active nodes', () => { baseUrl: '/app', url: '/app/item1', href: '/app/item1', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }, getIsActive: expect.any(Function), }, diff --git a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts index d5554ab69679b..19dbd9d49bd8c 100644 --- a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts @@ -21,7 +21,7 @@ const getDeepLink = (id: string, path: string, title = ''): ChromeNavLink => ({ href: `http://mocked/kibana/foo/${path}`, title, baseUrl: '', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }); describe('flattenNav', () => { diff --git a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts index 2a4d0f6156043..16b3c6c932e90 100644 --- a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts +++ b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts @@ -177,8 +177,8 @@ function getNodeStatus( }, { cloudLinks }: { cloudLinks: CloudLinks } ): SideNavNodeStatus | 'remove' { - if (link && (!deepLink || !deepLink.visibleIn.includes('sideNav'))) { - // If a link is provided but no deepLink found, or the app explicitly excluded sideNav + if (link && (!deepLink || !deepLink.visibleIn.includes('solutionSideNav'))) { + // If a link is provided but no deepLink found, or the app excluded solutionSideNav return 'remove'; } diff --git a/src/platform/plugins/private/kibana_overview/public/plugin.ts b/src/platform/plugins/private/kibana_overview/public/plugin.ts index 022b3d8e5b1cd..f60a9edfc9665 100644 --- a/src/platform/plugins/private/kibana_overview/public/plugin.ts +++ b/src/platform/plugins/private/kibana_overview/public/plugin.ts @@ -67,7 +67,7 @@ export class KibanaOverviewPlugin order: 1, updater$: appUpdater$, appRoute: PLUGIN_PATH, - visibleIn: ['globalSearch', 'home', 'sideNav'], + visibleIn: ['globalSearch', 'home', 'classicSideNav', 'solutionSideNav'], async mount(params: AppMountParameters) { // Load application bundle const { renderApp } = await import('./application'); diff --git a/src/platform/plugins/shared/dev_tools/public/plugin.ts b/src/platform/plugins/shared/dev_tools/public/plugin.ts index 6be9b409ade8b..2c8bcfe7a2b79 100644 --- a/src/platform/plugins/shared/dev_tools/public/plugin.ts +++ b/src/platform/plugins/shared/dev_tools/public/plugin.ts @@ -130,7 +130,7 @@ export class DevToolsPlugin implements Plugin { id: tool.id, title: tool.title as string, path: `#/${tool.id}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }; if (!devtoolsDeeplinkIds.some((id) => id === deepLink.id)) { throw new Error('Deeplink must be registered in package.'); diff --git a/src/platform/plugins/shared/discover/public/plugin.tsx b/src/platform/plugins/shared/discover/public/plugin.tsx index 083d11db5cadd..dc56ffd7061df 100644 --- a/src/platform/plugins/shared/discover/public/plugin.tsx +++ b/src/platform/plugins/shared/discover/public/plugin.tsx @@ -184,7 +184,7 @@ export class DiscoverPlugin euiIconType: 'logoKibana', defaultPath: '#/', category: DEFAULT_APP_CATEGORIES.kibana, - visibleIn: ['globalSearch', 'sideNav', 'kibanaOverview'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav', 'kibanaOverview'], mount: async (params: AppMountParameters) => { const [[coreStart, discoverStartPlugins], historyService, ebtManager, { renderApp }] = await Promise.all([ diff --git a/src/platform/plugins/shared/management/public/plugin.tsx b/src/platform/plugins/shared/management/public/plugin.tsx index c50c4fca84c2c..d1d913b39aa37 100644 --- a/src/platform/plugins/shared/management/public/plugin.tsx +++ b/src/platform/plugins/shared/management/public/plugin.tsx @@ -83,10 +83,10 @@ export class ManagementPlugin title: mgmtApp.title, path: mgmtApp.basePath, keywords: mgmtApp.keywords, - // Default includes 'sideNav' so all management sections appear in solution nav. - // Sections that explicitly set visibleIn (e.g. to hide from globalSearch) must - // also include 'sideNav' if they want to remain visible in solution nav trees. - visibleIn: mgmtApp.visibleIn ?? ['globalSearch', 'sideNav'], + // Default includes both 'classicSideNav' and 'solutionSideNav' so all management sections + // appear in both navigation modes. Sections that explicitly set visibleIn + // (e.g. to hide from globalSearch) must also include the relevant nav flags. + visibleIn: mgmtApp.visibleIn ?? ['globalSearch', 'classicSideNav', 'solutionSideNav'], })), })); diff --git a/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts b/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts index 2e06e187b4fde..1a34c162a8eba 100644 --- a/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts +++ b/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts @@ -31,7 +31,7 @@ function getDeepLinksFromUpdater(plugin: ManagementPlugin): AppDeepLink[] { } describe('ManagementPlugin appUpdater deep link visibleIn', () => { - it('defaults visibleIn to globalSearch and sideNav for apps without an explicit visibleIn', () => { + it('defaults visibleIn to globalSearch and classicSideNav/solutionSideNav for apps without an explicit visibleIn', () => { const plugin = createPlugin(); const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); @@ -46,25 +46,25 @@ describe('ManagementPlugin appUpdater deep link visibleIn', () => { const app = kibana?.deepLinks?.find((a) => a.id === 'test-no-visible-in'); expect(app).toBeDefined(); - expect(app?.visibleIn).toEqual(['globalSearch', 'sideNav']); + expect(app?.visibleIn).toEqual(['globalSearch', 'classicSideNav', 'solutionSideNav']); }); - it('preserves explicit visibleIn when set to sideNav-only', () => { + it('preserves explicit visibleIn when set to classicSideNav/solutionSideNav-only', () => { const plugin = createPlugin(); const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); setup.sections.section.kibana.registerApp({ - id: 'test-sidenav-only', - title: 'Test App sideNav only', + id: 'test-both-nav-only', + title: 'Test App both nav only', mount: jest.fn(), - visibleIn: ['sideNav'], + visibleIn: ['classicSideNav', 'solutionSideNav'], }); const deepLinks = getDeepLinksFromUpdater(plugin); const kibana = deepLinks.find((s) => s.id === 'kibana'); - const app = kibana?.deepLinks?.find((a) => a.id === 'test-sidenav-only'); + const app = kibana?.deepLinks?.find((a) => a.id === 'test-both-nav-only'); - expect(app?.visibleIn).toEqual(['sideNav']); + expect(app?.visibleIn).toEqual(['classicSideNav', 'solutionSideNav']); }); it('preserves explicit visibleIn when set to globalSearch-only', () => { @@ -112,7 +112,7 @@ describe('ManagementPlugin appUpdater deep link visibleIn', () => { title: 'Hidden App With visibleIn', mount: jest.fn(), hideFromGlobalSearch: true, - visibleIn: ['sideNav'], + visibleIn: ['classicSideNav', 'solutionSideNav'], }); const deepLinks = getDeepLinksFromUpdater(plugin); @@ -120,6 +120,6 @@ describe('ManagementPlugin appUpdater deep link visibleIn', () => { const app = kibana?.deepLinks?.find((a) => a.id === 'test-hidden-with-visible-in'); expect(app).toBeDefined(); - expect(app?.visibleIn).toEqual(['sideNav']); + expect(app?.visibleIn).toEqual(['classicSideNav', 'solutionSideNav']); }); }); diff --git a/src/platform/plugins/shared/visualizations/public/plugin.ts b/src/platform/plugins/shared/visualizations/public/plugin.ts index ff32d23a20b72..670fb144d4e84 100644 --- a/src/platform/plugins/shared/visualizations/public/plugin.ts +++ b/src/platform/plugins/shared/visualizations/public/plugin.ts @@ -582,7 +582,7 @@ export class VisualizationsPlugin const isServerless = Boolean(serverless); const isSolutionView = space.solution && space.solution !== 'classic'; const visibleIn: AppDeepLinkLocations[] = - isServerless || isSolutionView ? [] : ['globalSearch', 'sideNav']; + isServerless || isSolutionView ? [] : ['globalSearch', 'classicSideNav']; this.visibilityUpdater.next(() => ({ visibleIn })); }); } diff --git a/src/platform/plugins/shared/workflows_management/public/plugin.test.ts b/src/platform/plugins/shared/workflows_management/public/plugin.test.ts index 5bfa8dc2f4c73..73c540e92e0b6 100644 --- a/src/platform/plugins/shared/workflows_management/public/plugin.test.ts +++ b/src/platform/plugins/shared/workflows_management/public/plugin.test.ts @@ -195,11 +195,12 @@ describe('WorkflowsPlugin', () => { 'globalSearch', 'home', 'kibanaOverview', - 'sideNav', + 'classicSideNav', + 'solutionSideNav', ]); }); - it('should hide the app from sideNav when the user lacks read capability', () => { + it('should hide the app from solutionSideNav when the user lacks read capability', () => { setReadCapability(false); setLicenseValid(true); const updates = captureAppUpdates(); @@ -209,10 +210,15 @@ describe('WorkflowsPlugin', () => { const visibleInUpdates = updates.filter((u) => u.visibleIn !== undefined); expect(visibleInUpdates.length).toBeGreaterThanOrEqual(1); expect(visibleInUpdates[visibleInUpdates.length - 1].visibleIn).toEqual(['globalSearch']); - expect(visibleInUpdates[visibleInUpdates.length - 1].visibleIn).not.toContain('sideNav'); + expect(visibleInUpdates[visibleInUpdates.length - 1].visibleIn).not.toContain( + 'classicSideNav' + ); + expect(visibleInUpdates[visibleInUpdates.length - 1].visibleIn).not.toContain( + 'solutionSideNav' + ); }); - it('should keep the app in sideNav and globalSearch when authorized but unavailable', () => { + it('should keep the app in classicSideNav and solutionSideNav and globalSearch when authorized but unavailable', () => { setReadCapability(true); setLicenseValid(false); const updates = captureAppUpdates(); @@ -223,11 +229,12 @@ describe('WorkflowsPlugin', () => { expect(visibleInUpdates.length).toBeGreaterThanOrEqual(1); expect(visibleInUpdates[visibleInUpdates.length - 1].visibleIn).toEqual([ 'globalSearch', - 'sideNav', + 'classicSideNav', + 'solutionSideNav', ]); }); - it('should hide the app from sideNav for unauthorized users even when unavailable', () => { + it('should hide the app from classicSideNav and solutionSideNav for unauthorized users even when unavailable', () => { setReadCapability(false); setLicenseValid(false); const updates = captureAppUpdates(); @@ -238,7 +245,8 @@ describe('WorkflowsPlugin', () => { expect(visibleInUpdates.length).toBeGreaterThanOrEqual(1); expect(visibleInUpdates[visibleInUpdates.length - 1].visibleIn).toEqual([ 'globalSearch', - 'sideNav', + 'classicSideNav', + 'solutionSideNav', ]); }); }); diff --git a/src/platform/plugins/shared/workflows_management/public/plugin.ts b/src/platform/plugins/shared/workflows_management/public/plugin.ts index 39744b8c6ef24..57ea271c5caa2 100644 --- a/src/platform/plugins/shared/workflows_management/public/plugin.ts +++ b/src/platform/plugins/shared/workflows_management/public/plugin.ts @@ -205,14 +205,14 @@ export class WorkflowsPlugin }): AppDeepLinkLocations[] { // Not available takes precedence over authorized. if (!params.isAvailable) { - // Remove generic locations, but keep in sideNav and globalSearch to make users aware of the feature. - return ['globalSearch', 'sideNav']; + // Remove generic locations, but keep in 'classicSideNav', 'solutionSideNav' and globalSearch to make users aware of the feature. + return ['globalSearch', 'classicSideNav', 'solutionSideNav']; } if (!params.isAuthorized) { - // Remove from sideNav so it does not use nav real estate, but keep in globalSearch to make it discoverable + // Remove from 'classicSideNav', 'solutionSideNav' so it does not use nav real estate, but keep in globalSearch to make it discoverable return ['globalSearch']; } - return ['globalSearch', 'home', 'kibanaOverview', 'sideNav']; + return ['globalSearch', 'home', 'kibanaOverview', 'classicSideNav', 'solutionSideNav']; } /** diff --git a/src/platform/test/plugin_functional/test_suites/core_plugins/application_status.ts b/src/platform/test/plugin_functional/test_suites/core_plugins/application_status.ts index dbaa0d468c843..5562da75676b4 100644 --- a/src/platform/test/plugin_functional/test_suites/core_plugins/application_status.ts +++ b/src/platform/test/plugin_functional/test_suites/core_plugins/application_status.ts @@ -51,7 +51,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide it('can change the visibleIn array at runtime', async () => { await setAppStatus({ - visibleIn: ['sideNav'], + visibleIn: ['classicSideNav', 'solutionSideNav'], }); let link = await appsMenu.getLink('App Status'); expect(link).not.to.eql(undefined); diff --git a/x-pack/platform/plugins/shared/agent_builder/public/register.ts b/x-pack/platform/plugins/shared/agent_builder/public/register.ts index 99c344af0da00..856027239791b 100644 --- a/x-pack/platform/plugins/shared/agent_builder/public/register.ts +++ b/x-pack/platform/plugins/shared/agent_builder/public/register.ts @@ -76,7 +76,7 @@ export const registerApp = ({ category: DEFAULT_APP_CATEGORIES.enterpriseSearch, title: AGENT_BUILDER_SHORT_TITLE, euiIconType: 'logoElasticsearch', - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], keywords: ['agent builder', 'ai agent', 'chat agent'], updater$: appUpdater$, deepLinks: buildAgentBuilderDeepLinks(false), diff --git a/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts b/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts index 0b27fc477e01f..b76e2fee632c8 100644 --- a/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts +++ b/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts @@ -79,10 +79,13 @@ const authzGatedIds = [ ]; describe('getFleetDeepLinks', () => { - it('includes sideNav in visibleIn for all authz-gated links when authorized', () => { + it('includes solutionSideNav in visibleIn for all authz-gated links when authorized', () => { const links = getFleetDeepLinks(allowedExperimentalValues, authorizedAuthz); for (const id of authzGatedIds) { - expect(links.find((l) => l.id === id)?.visibleIn).toEqual(['globalSearch', 'sideNav']); + expect(links.find((l) => l.id === id)?.visibleIn).toEqual([ + 'globalSearch', + 'solutionSideNav', + ]); } }); diff --git a/x-pack/platform/plugins/shared/fleet/public/deep_links.ts b/x-pack/platform/plugins/shared/fleet/public/deep_links.ts index 8b28a6f527dd2..71014481ace8f 100644 --- a/x-pack/platform/plugins/shared/fleet/public/deep_links.ts +++ b/x-pack/platform/plugins/shared/fleet/public/deep_links.ts @@ -31,7 +31,7 @@ export const getFleetDeepLinks: ( id: FleetDeepLinkId.agents, title: i18n.translate('xpack.fleet.deepLinks.agents.title', { defaultMessage: 'Agents' }), path: FLEET_ROUTING_PATHS.agents, - visibleIn: !authz?.fleet.readAgents ? [] : ['globalSearch', 'sideNav'], + visibleIn: !authz?.fleet.readAgents ? [] : ['globalSearch', 'solutionSideNav'], }, { id: FleetDeepLinkId.policies, @@ -39,7 +39,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Agent policies', }), path: FLEET_ROUTING_PATHS.policies, - visibleIn: !authz?.fleet.readAgentPolicies ? [] : ['globalSearch', 'sideNav'], + visibleIn: !authz?.fleet.readAgentPolicies ? [] : ['globalSearch', 'solutionSideNav'], }, { id: FleetDeepLinkId.enrollmentTokens, @@ -47,7 +47,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Enrollment tokens', }), path: FLEET_ROUTING_PATHS.enrollment_tokens, - visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch', 'sideNav'], + visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch', 'solutionSideNav'], }, { id: FleetDeepLinkId.uninstallTokens, @@ -55,7 +55,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Uninstall tokens', }), path: FLEET_ROUTING_PATHS.uninstall_tokens, - visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch', 'sideNav'], + visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch', 'solutionSideNav'], }, { id: FleetDeepLinkId.dataStreams, @@ -70,7 +70,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Settings', }), path: FLEET_ROUTING_PATHS.settings, - visibleIn: !authz?.fleet.readSettings ? [] : ['globalSearch', 'sideNav'], + visibleIn: !authz?.fleet.readSettings ? [] : ['globalSearch', 'solutionSideNav'], }, ]; }; diff --git a/x-pack/platform/plugins/shared/inbox/public/plugin.test.ts b/x-pack/platform/plugins/shared/inbox/public/plugin.test.ts index 583dcb925451c..593f82e669841 100644 --- a/x-pack/platform/plugins/shared/inbox/public/plugin.test.ts +++ b/x-pack/platform/plugins/shared/inbox/public/plugin.test.ts @@ -56,7 +56,9 @@ describe('InboxPublicPlugin', () => { plugin.setup(coreSetup, noopSetupDeps); const [registration] = coreSetup.application.register.mock.calls[0]; - expect(registration.visibleIn).toEqual(expect.arrayContaining(['sideNav', 'globalSearch'])); + expect(registration.visibleIn).toEqual( + expect.arrayContaining(['classicSideNav', 'solutionSideNav', 'globalSearch']) + ); }); it('exposes registerActionDetailRenderer regardless of enabled state', () => { diff --git a/x-pack/platform/plugins/shared/inbox/public/plugin.tsx b/x-pack/platform/plugins/shared/inbox/public/plugin.tsx index b2a1e09b9178f..cf3e0ff9425de 100644 --- a/x-pack/platform/plugins/shared/inbox/public/plugin.tsx +++ b/x-pack/platform/plugins/shared/inbox/public/plugin.tsx @@ -66,7 +66,7 @@ export class InboxPublicPlugin category: DEFAULT_APP_CATEGORIES.security, euiIconType: 'email', status: AppStatus.accessible, - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], order: 100, mount: async (params) => { const [coreStart, startDeps] = await coreSetup.getStartServices(); diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/plugin.test.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/plugin.test.tsx index d49f5f1ce18a5..a97d892f1940d 100644 --- a/x-pack/platform/plugins/shared/ingest_hub/public/plugin.test.tsx +++ b/x-pack/platform/plugins/shared/ingest_hub/public/plugin.test.tsx @@ -144,7 +144,7 @@ describe('IngestHubPlugin', () => { const { updater$ } = coreSetup.application.register.mock.calls[0][0]; const updater = await firstValueFrom(updater$!); expect((updater as AppUpdater)!({} as unknown as Parameters[0])).toEqual({ - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], }); }); diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/plugin.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/plugin.tsx index 31d484e3afe03..43c4c1b825f23 100644 --- a/x-pack/platform/plugins/shared/ingest_hub/public/plugin.tsx +++ b/x-pack/platform/plugins/shared/ingest_hub/public/plugin.tsx @@ -82,7 +82,7 @@ export class IngestHubPlugin coreStart.featureFlags.getBooleanValue$(INGEST_HUB_ENABLED_FLAG, false).pipe( map((enabled): AppUpdater => { return () => ({ - visibleIn: enabled ? ['sideNav', 'globalSearch'] : [], + visibleIn: enabled ? ['classicSideNav', 'solutionSideNav', 'globalSearch'] : [], }); }) ) diff --git a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts index 8a2c15072df69..ee6b06dd91a03 100644 --- a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts +++ b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts @@ -15,50 +15,57 @@ const fullCapabilities: MlCapabilities = { }; describe('getDeepLinks', () => { - it('visible nav links include both globalSearch and sideNav', () => { + it('visible nav links include both globalSearch and solutionSideNav', () => { const links = getDeepLinks(true, fullCapabilities, false); const anomalyDetection = links.find((l) => l.id === 'anomalyDetection'); const dfa = links.find((l) => l.id === 'dataFrameAnalytics'); const aiOps = links.find((l) => l.id === 'aiOps'); - expect(links.find((l) => l.id === 'overview')?.visibleIn).toEqual(['globalSearch', 'sideNav']); + expect(links.find((l) => l.id === 'overview')?.visibleIn).toEqual([ + 'globalSearch', + 'solutionSideNav', + ]); expect(links.find((l) => l.id === 'dataVisualizer')?.visibleIn).toEqual([ 'globalSearch', - 'sideNav', + 'solutionSideNav', ]); expect(anomalyDetection?.deepLinks?.find((l) => l.id === 'anomalyExplorer')?.visibleIn).toEqual( - ['globalSearch', 'sideNav'] + ['globalSearch', 'solutionSideNav'] ); expect( anomalyDetection?.deepLinks?.find((l) => l.id === 'singleMetricViewer')?.visibleIn - ).toEqual(['globalSearch', 'sideNav']); + ).toEqual(['globalSearch', 'solutionSideNav']); expect(dfa?.deepLinks?.find((l) => l.id === 'resultExplorer')?.visibleIn).toEqual([ 'globalSearch', - 'sideNav', + 'solutionSideNav', ]); expect(aiOps?.deepLinks?.find((l) => l.id === 'logRateAnalysis')?.visibleIn).toEqual([ 'globalSearch', - 'sideNav', + 'solutionSideNav', ]); }); - it('hidden nav nodes (breadcrumb-only) use sideNav-only visibleIn', () => { + it('hidden nav nodes (breadcrumb-only) use solutionSideNav-only visibleIn', () => { const links = getDeepLinks(true, fullCapabilities, false); const aiOps = links.find((l) => l.id === 'aiOps'); - expect(links.find((l) => l.id === 'fileUpload')?.visibleIn).toEqual(['sideNav']); - expect(links.find((l) => l.id === 'indexDataVisualizer')?.visibleIn).toEqual(['sideNav']); - expect(links.find((l) => l.id === 'indexDataVisualizerPage')?.visibleIn).toEqual(['sideNav']); - expect(links.find((l) => l.id === 'dataDrift')?.visibleIn).toEqual(['sideNav']); - expect(links.find((l) => l.id === 'dataDriftPage')?.visibleIn).toEqual(['sideNav']); + expect(links.find((l) => l.id === 'fileUpload')?.visibleIn).toEqual(['solutionSideNav']); + expect(links.find((l) => l.id === 'indexDataVisualizer')?.visibleIn).toEqual([ + 'solutionSideNav', + ]); + expect(links.find((l) => l.id === 'indexDataVisualizerPage')?.visibleIn).toEqual([ + 'solutionSideNav', + ]); + expect(links.find((l) => l.id === 'dataDrift')?.visibleIn).toEqual(['solutionSideNav']); + expect(links.find((l) => l.id === 'dataDriftPage')?.visibleIn).toEqual(['solutionSideNav']); expect(aiOps?.deepLinks?.find((l) => l.id === 'logRateAnalysisPage')?.visibleIn).toEqual([ - 'sideNav', + 'solutionSideNav', ]); expect(aiOps?.deepLinks?.find((l) => l.id === 'logPatternAnalysisPage')?.visibleIn).toEqual([ - 'sideNav', + 'solutionSideNav', ]); expect(aiOps?.deepLinks?.find((l) => l.id === 'changePointDetectionsPage')?.visibleIn).toEqual([ - 'sideNav', + 'solutionSideNav', ]); }); diff --git a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts index d0e1534861d4d..a51795e0b23dd 100644 --- a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts +++ b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts @@ -28,7 +28,7 @@ function createDeepLinks( defaultMessage: 'Overview', }), path: `/${ML_PAGES.OVERVIEW}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }; }, @@ -47,7 +47,7 @@ function createDeepLinks( defaultMessage: 'Anomaly explorer', }), path: `/${ML_PAGES.ANOMALY_EXPLORER}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'singleMetricViewer', @@ -55,7 +55,7 @@ function createDeepLinks( defaultMessage: 'Single metric viewer', }), path: `/${ML_PAGES.SINGLE_METRIC_VIEWER}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'suppliedConfigurations', @@ -85,7 +85,7 @@ function createDeepLinks( defaultMessage: 'Results explorer', }), path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'analyticsMap', @@ -93,7 +93,7 @@ function createDeepLinks( defaultMessage: 'Analytics map', }), path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_MAP}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, ], }; @@ -145,7 +145,7 @@ function createDeepLinks( defaultMessage: 'Log rate analysis', }), path: `/${ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'logRateAnalysisPage', @@ -153,7 +153,7 @@ function createDeepLinks( defaultMessage: 'Log rate analysis', }), path: `/${ML_PAGES.AIOPS_LOG_RATE_ANALYSIS}`, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }, { id: 'logPatternAnalysis', @@ -161,7 +161,7 @@ function createDeepLinks( defaultMessage: 'Log pattern analysis', }), path: `/${ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'logPatternAnalysisPage', @@ -169,7 +169,7 @@ function createDeepLinks( defaultMessage: 'Log pattern analysis', }), path: `/${ML_PAGES.AIOPS_LOG_CATEGORIZATION}`, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }, { id: 'changePointDetections', @@ -177,7 +177,7 @@ function createDeepLinks( defaultMessage: 'Change point detection', }), path: `/${ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'changePointDetectionsPage', @@ -185,7 +185,7 @@ function createDeepLinks( defaultMessage: 'Change point detection', }), path: `/${ML_PAGES.AIOPS_CHANGE_POINT_DETECTION}`, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }, ], }; @@ -211,7 +211,7 @@ function createDeepLinks( defaultMessage: 'Data visualizer', }), path: `/${ML_PAGES.DATA_VISUALIZER}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }; }, @@ -223,7 +223,7 @@ function createDeepLinks( }), keywords: ['CSV', 'JSON'], path: `/${ML_PAGES.DATA_VISUALIZER_FILE}`, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }; }, @@ -234,7 +234,7 @@ function createDeepLinks( defaultMessage: 'Index data visualizer', }), path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_SELECT}`, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }; }, getIndexDataVisualizerPageDeepLink: (): AppDeepLink => { @@ -244,7 +244,7 @@ function createDeepLinks( defaultMessage: 'Index data visualizer', }), path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER}`, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }; }, @@ -266,7 +266,7 @@ function createDeepLinks( defaultMessage: 'Data drift', }), path: `/${ML_PAGES.DATA_DRIFT_INDEX_SELECT}`, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }; }, getDataDriftPageDeepLink: (): AppDeepLink => { @@ -276,7 +276,7 @@ function createDeepLinks( defaultMessage: 'Data drift', }), path: `/${ML_PAGES.DATA_DRIFT}`, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }; }, }; diff --git a/x-pack/platform/plugins/shared/streams_app/public/plugin.tsx b/x-pack/platform/plugins/shared/streams_app/public/plugin.tsx index 1531baa27eac8..4253ae3503efa 100644 --- a/x-pack/platform/plugins/shared/streams_app/public/plugin.tsx +++ b/x-pack/platform/plugins/shared/streams_app/public/plugin.tsx @@ -122,7 +122,7 @@ export class StreamsAppPlugin } return { - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], }; }; }) diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts index 36e873af14d99..29ae6f92dd07c 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts @@ -299,7 +299,7 @@ export class Plugin title: i18n.translate('xpack.triggersActionsUI.rulesPage.title', { defaultMessage: 'Rules', }), - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], category: DEFAULT_APP_CATEGORIES.management, async mount(params: AppMountParameters) { const [coreStart, pluginsStart] = (await core.getStartServices()) as [ diff --git a/x-pack/solutions/observability/plugins/apm/public/plugin.ts b/x-pack/solutions/observability/plugins/apm/public/plugin.ts index bd966f1eb5547..2b261f433f0cc 100644 --- a/x-pack/solutions/observability/plugins/apm/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/apm/public/plugin.ts @@ -464,28 +464,38 @@ export class ApmPlugin implements Plugin { id: 'service-groups-list', title: serviceGroupsTitle, path: '/service-groups', - visibleIn: ['sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'services', title: servicesTitle, path: '/services', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'traces', title: tracesTitle, path: '/traces', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], + }, + { + id: 'service-map', + title: serviceMapTitle, + path: '/service-map', + visibleIn: ['globalSearch', 'solutionSideNav'], }, - { id: 'service-map', title: serviceMapTitle, path: '/service-map', visibleIn: ['sideNav'] }, { id: 'dependencies', title: dependenciesTitle, path: '/dependencies/inventory', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], + }, + { + id: 'settings', + title: apmSettingsTitle, + path: '/settings', + visibleIn: ['globalSearch', 'solutionSideNav'], }, - { id: 'settings', title: apmSettingsTitle, path: '/settings', visibleIn: ['sideNav'] }, { id: 'storage-explorer', title: apmStorageExplorerTitle, diff --git a/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts b/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts index 65470583d8c05..cd566a014b010 100644 --- a/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts +++ b/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts @@ -26,13 +26,13 @@ export const getLogsAppRoutes = () => { id: 'anomalies', title: logsAnomaliesTitle, path: '/anomalies', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, logsCategories: { id: 'log-categories', title: logCategoriesTitle, path: '/log-categories', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, }; diff --git a/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts b/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts index a15ee7d0c0c41..4dfe4e9e93f92 100644 --- a/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts +++ b/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts @@ -8,16 +8,22 @@ import { getInfraDeepLinks } from './plugin'; describe('getInfraDeepLinks', () => { - it('visible nav links include both globalSearch and sideNav', () => { + it('visible nav links include globalSearch and solutionSideNav', () => { const links = getInfraDeepLinks({ metricsExplorerEnabled: false }); - expect(links.find((l) => l.id === 'inventory')?.visibleIn).toEqual(['globalSearch', 'sideNav']); - expect(links.find((l) => l.id === 'hosts')?.visibleIn).toEqual(['globalSearch', 'sideNav']); + expect(links.find((l) => l.id === 'inventory')?.visibleIn).toEqual([ + 'globalSearch', + 'solutionSideNav', + ]); + expect(links.find((l) => l.id === 'hosts')?.visibleIn).toEqual([ + 'globalSearch', + 'solutionSideNav', + ]); }); - it('settings is sideNav-only (hidden breadcrumb node)', () => { + it('settings is solutionSideNav (hidden breadcrumb node)', () => { const links = getInfraDeepLinks({ metricsExplorerEnabled: false }); - expect(links.find((l) => l.id === 'settings')?.visibleIn).toEqual(['sideNav']); + expect(links.find((l) => l.id === 'settings')?.visibleIn).toEqual(['solutionSideNav']); }); it('assetDetails is hidden from all nav locations', () => { @@ -25,11 +31,11 @@ describe('getInfraDeepLinks', () => { expect(links.find((l) => l.id === 'assetDetails')?.visibleIn).toEqual([]); }); - it('includes metrics-explorer with globalSearch and sideNav when feature flag is enabled', () => { + it('includes metrics-explorer with globalSearch and solutionSideNav when feature flag is enabled', () => { const links = getInfraDeepLinks({ metricsExplorerEnabled: true }); expect(links.find((l) => l.id === 'metrics-explorer')?.visibleIn).toEqual([ 'globalSearch', - 'sideNav', + 'solutionSideNav', ]); }); diff --git a/x-pack/solutions/observability/plugins/infra/public/plugin.ts b/x-pack/solutions/observability/plugins/infra/public/plugin.ts index 6a87488d90092..dcf5968868565 100644 --- a/x-pack/solutions/observability/plugins/infra/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/infra/public/plugin.ts @@ -74,7 +74,7 @@ export const getInfraDeepLinks = ({ }: { metricsExplorerEnabled: boolean; }): AppDeepLink[] => { - const visibleIn: AppDeepLinkLocations[] = ['globalSearch', 'sideNav']; + const visibleIn: AppDeepLinkLocations[] = ['globalSearch', 'solutionSideNav']; return [ { @@ -99,7 +99,7 @@ export const getInfraDeepLinks = ({ defaultMessage: 'Metrics Explorer', }), path: '/explorer', - visibleIn: ['globalSearch', 'sideNav'] as AppDeepLinkLocations[], + visibleIn: ['globalSearch', 'solutionSideNav'] as AppDeepLinkLocations[], }, ] : []), @@ -109,7 +109,7 @@ export const getInfraDeepLinks = ({ defaultMessage: 'Settings', }), path: '/settings', - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }, { id: 'assetDetails', diff --git a/x-pack/solutions/observability/plugins/observability/public/plugin.ts b/x-pack/solutions/observability/plugins/observability/public/plugin.ts index 650958ff9e239..83ea7eb0856aa 100644 --- a/x-pack/solutions/observability/plugins/observability/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/observability/public/plugin.ts @@ -218,7 +218,7 @@ export class Plugin }), order: 8001, path: ALERTS_PATH, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], keywords: ['alerts', 'rules'], }, { @@ -258,13 +258,13 @@ export class Plugin extend: { [CasesDeepLinkId.cases]: { order: 8003, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }, [CasesDeepLinkId.casesCreate]: { - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }, [CasesDeepLinkId.casesConfigure]: { - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }, }, }) @@ -360,8 +360,8 @@ export class Plugin 'experience', ], visibleIn: Boolean(pluginsSetup.serverless) - ? ['sideNav', 'home', 'kibanaOverview'] - : ['globalSearch', 'sideNav', 'home', 'kibanaOverview'], + ? ['solutionSideNav', 'home', 'kibanaOverview'] + : ['globalSearch', 'classicSideNav', 'solutionSideNav', 'home', 'kibanaOverview'], }; coreSetup.application.register(app); diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx index 728e87cd3cf85..e988045aebb0c 100644 --- a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx +++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx @@ -67,7 +67,7 @@ export class ObservabilityAIAssistantAppPlugin euiIconType: 'logoObservability', appRoute: '/app/observabilityAIAssistant', category: DEFAULT_APP_CATEGORIES.observability, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], deepLinks: [ { id: 'conversations', diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts index 1dfc4e3aac8d0..e6db9e2d59c56 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts @@ -123,7 +123,7 @@ export class ObservabilityOnboardingPlugin }, }); }, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }); this.locators = { diff --git a/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.test.tsx b/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.test.tsx index 6ebdfe824bf54..a30c606fc4654 100644 --- a/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.test.tsx +++ b/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.test.tsx @@ -58,7 +58,7 @@ describe('updateGlobalNavigation', () => { expect(callback).toHaveBeenCalledWith({ deepLinks, - visibleIn: ['sideNav', 'home', 'kibanaOverview', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'home', 'kibanaOverview', 'globalSearch'], }); }); @@ -91,10 +91,16 @@ describe('updateGlobalNavigation', () => { deepLinks: [ { ...caseRoute, - visibleIn: ['sideNav', 'globalSearch'], // visibility set + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], // visibility set }, ], - visibleIn: ['sideNav', 'home', 'kibanaOverview', 'globalSearch'], + visibleIn: [ + 'classicSideNav', + 'solutionSideNav', + 'home', + 'kibanaOverview', + 'globalSearch', + ], }); }); }); @@ -126,7 +132,13 @@ describe('updateGlobalNavigation', () => { expect(callback).toHaveBeenCalledWith({ deepLinks: [], // Deeplink has been filtered out - visibleIn: ['sideNav', 'home', 'kibanaOverview', 'globalSearch'], + visibleIn: [ + 'classicSideNav', + 'solutionSideNav', + 'home', + 'kibanaOverview', + 'globalSearch', + ], }); }); }); @@ -162,10 +174,16 @@ describe('updateGlobalNavigation', () => { title: 'Alerts', order: 8001, path: '/alerts', - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], }, ], - visibleIn: ['sideNav', 'home', 'kibanaOverview', 'globalSearch'], + visibleIn: [ + 'classicSideNav', + 'solutionSideNav', + 'home', + 'kibanaOverview', + 'globalSearch', + ], }); }); @@ -199,10 +217,16 @@ describe('updateGlobalNavigation', () => { title: 'Alerts', order: 8001, path: '/alerts', - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], }, ], - visibleIn: ['sideNav', 'home', 'kibanaOverview', 'globalSearch'], + visibleIn: [ + 'classicSideNav', + 'solutionSideNav', + 'home', + 'kibanaOverview', + 'globalSearch', + ], }); }); }); diff --git a/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.tsx b/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.tsx index fd7da3c20ecd1..81bd95d58fc35 100644 --- a/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.tsx +++ b/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.tsx @@ -51,7 +51,7 @@ export function updateGlobalNavigation({ if (capabilities[casesFeatureId].read_cases) { return { ...link, - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], }; } return null; @@ -59,7 +59,7 @@ export function updateGlobalNavigation({ if (someVisible) { return { ...link, - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], }; } return null; @@ -67,7 +67,7 @@ export function updateGlobalNavigation({ if (someVisible) { return { ...link, - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], }; } return null; @@ -79,7 +79,7 @@ export function updateGlobalNavigation({ updater$.next(() => { const visibleIn: AppDeepLinkLocations[] = someVisible - ? ['sideNav', 'home', 'kibanaOverview'] + ? ['classicSideNav', 'solutionSideNav', 'home', 'kibanaOverview'] : []; if (isCompleteOverviewEnabled && someVisible) { diff --git a/x-pack/solutions/observability/plugins/profiling/public/plugin.ts b/x-pack/solutions/observability/plugins/profiling/public/plugin.ts index cc1c718387e23..93127b87d56ef 100644 --- a/x-pack/solutions/observability/plugins/profiling/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/profiling/public/plugin.ts @@ -6,7 +6,7 @@ */ import type { - AppDeepLinkLocations, + AppDeepLink, AppMountParameters, AppUpdater, CoreSetup, @@ -48,7 +48,7 @@ export class ProfilingPlugin defaultMessage: 'Stacktraces', }), path: '/stacktraces', - visibleIn: ['globalSearch', 'sideNav'] as AppDeepLinkLocations[], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'flamegraphs', @@ -56,7 +56,7 @@ export class ProfilingPlugin defaultMessage: 'Flamegraphs', }), path: '/flamegraphs', - visibleIn: ['globalSearch', 'sideNav'] as AppDeepLinkLocations[], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'functions', @@ -64,9 +64,9 @@ export class ProfilingPlugin defaultMessage: 'Functions', }), path: '/functions', - visibleIn: ['globalSearch', 'sideNav'] as AppDeepLinkLocations[], + visibleIn: ['globalSearch', 'solutionSideNav'], }, - ]; + ] satisfies AppDeepLink[]; const kuerySubject = new BehaviorSubject(''); const appUpdater$ = new BehaviorSubject(() => ({})); diff --git a/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts b/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts index e6875e694de23..82d0afb8f2cf3 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts @@ -213,7 +213,7 @@ export class SyntheticsPlugin defaultMessage: 'Monitors', }), path: '/', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'certificates', @@ -221,7 +221,7 @@ export class SyntheticsPlugin defaultMessage: 'TLS Certificates', }), path: '/certificates', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, ], mount: async (params: AppMountParameters) => { diff --git a/x-pack/solutions/observability/plugins/uptime/public/plugin.ts b/x-pack/solutions/observability/plugins/uptime/public/plugin.ts index 5fa5f00e65aae..6417c506cd616 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/uptime/public/plugin.ts @@ -210,7 +210,7 @@ export class UptimePlugin id: 'Certificates', title: 'TLS Certificates', path: '/certificates', - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: 'Settings', title: 'Settings', path: '/settings' }, ], diff --git a/x-pack/solutions/search/plugins/search_getting_started/public/plugin.ts b/x-pack/solutions/search/plugins/search_getting_started/public/plugin.ts index 5fae875f209dc..d482c7b699e1a 100644 --- a/x-pack/solutions/search/plugins/search_getting_started/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_getting_started/public/plugin.ts @@ -67,7 +67,7 @@ export class SearchGettingStartedPlugin return renderApp(coreStart, services, element, queryClient, kibanaVersion); }, order: 1, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }); return {}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/plugin.ts b/x-pack/solutions/search/plugins/search_homepage/public/plugin.ts index 72d576b378fb1..f5fad31f419ec 100644 --- a/x-pack/solutions/search/plugins/search_homepage/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_homepage/public/plugin.ts @@ -70,7 +70,7 @@ export class SearchHomepagePlugin return renderApp(coreStart, startDeps, element, queryClient, kibanaVersion); }, order: 0, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }); return result; diff --git a/x-pack/solutions/search/plugins/search_playground/public/plugin.ts b/x-pack/solutions/search/plugins/search_playground/public/plugin.ts index 2d5657d7206b6..2a27d96ebb14c 100644 --- a/x-pack/solutions/search/plugins/search_playground/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_playground/public/plugin.ts @@ -67,7 +67,7 @@ export class SearchPlaygroundPlugin return renderApp(coreStart, startDeps, element); }, - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], order: 3, }); diff --git a/x-pack/solutions/search/plugins/search_query_rules/public/plugin.ts b/x-pack/solutions/search/plugins/search_query_rules/public/plugin.ts index 1907810ed3943..a65444ecf5c96 100644 --- a/x-pack/solutions/search/plugins/search_query_rules/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_query_rules/public/plugin.ts @@ -51,7 +51,7 @@ export class QueryRulesPlugin return renderApp(coreStart, startDeps, element); }, order: 5, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }); return {}; diff --git a/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts b/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts index 4dead1fd2cbca..cf3e442788a90 100644 --- a/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts @@ -40,7 +40,7 @@ export class SearchSynonymsPlugin id: 'synonyms', path: '/', title: PLUGIN_TITLE, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, ], async mount({ element, history }: AppMountParameters) { @@ -59,7 +59,7 @@ export class SearchSynonymsPlugin return renderApp(coreStart, startDeps, element); }, order: 4, - visibleIn: ['sideNav'], + visibleIn: ['classicSideNav', 'solutionSideNav'], }); return {}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.test.ts b/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.test.ts index e327e21c73b1e..bb31a97ff514d 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.test.ts @@ -37,7 +37,7 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, ]); }); @@ -59,7 +59,7 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, ]); }); @@ -82,12 +82,12 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, ]); }); - it('should include unavailable links with sideNav visibility only', () => { + it('should include unavailable links with solutionSideNav visibility only', () => { const id = 'page-1' as SecurityPageName; const id2 = 'page-2' as SecurityPageName; const tree: NavigationTreeDefinition = { @@ -105,13 +105,13 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, { id: id2, path: `/path/${id2}`, title: `Title for ${id2}`, - visibleIn: ['sideNav'], + visibleIn: ['solutionSideNav'], }, ]); }); @@ -137,7 +137,7 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, ]); }); @@ -164,7 +164,7 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }, ]); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.ts b/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.ts index 66db352062a8b..ecd1824d65925 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.ts @@ -25,7 +25,7 @@ const classicFormatter: (appLinks: AppLinkItems) => AppDeepLink[] = (appLinks) = visibleIn.add('globalSearch'); } if (appLink.globalNavPosition != null) { - visibleIn.add('sideNav'); + visibleIn.add('classicSideNav'); } const deepLink: AppDeepLink = { id: appLink.id, @@ -94,7 +94,7 @@ const solutionNodesFormatter = ( if (appLink && !appLink.unauthorized) { const deepLink = formatDeepLink(appLink); if (appLink.unavailable) { - deepLink.visibleIn = ['sideNav']; // Links marked as unavailable have an upselling page to display, show only in sideNav + deepLink.visibleIn = ['solutionSideNav']; // Links marked as unavailable have an upselling page to display, show only in solutionSideNav } if (node.children) { const childrenLinks = solutionNodesFormatter(node.children, normalizedLinks); @@ -121,7 +121,7 @@ const formatDeepLink = (appLink: LinkItem): AppDeepLink => { visibleIn.add('globalSearch'); } if (!appLink.sideNavDisabled) { - visibleIn.add('sideNav'); + visibleIn.add('solutionSideNav'); } const deepLink: AppDeepLink = { id: appLink.id, diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/links.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/links.ts index 00da83303e82c..105c3adb9cd2c 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/links.ts @@ -66,7 +66,7 @@ export const launchPadLinks: LinkItem = { sideNavFooter: true, skipUrlState: true, hideTimeline: true, - visibleIn: ['globalSearch', 'sideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], }; /** diff --git a/x-pack/solutions/workplaceai/plugins/workplace_ai_app/public/application/register.ts b/x-pack/solutions/workplaceai/plugins/workplace_ai_app/public/application/register.ts index 16837314ae9ed..2b506d644f5a6 100644 --- a/x-pack/solutions/workplaceai/plugins/workplace_ai_app/public/application/register.ts +++ b/x-pack/solutions/workplaceai/plugins/workplace_ai_app/public/application/register.ts @@ -28,7 +28,7 @@ export const registerApp = ({ defaultMessage: 'Workplace AI', }), updater$: undefined, - visibleIn: ['sideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], async mount({ element, history }) { const [coreStart, startPluginDeps] = await core.getStartServices(); const services = getServices(); From 865006c03fb197b130db1d6c6fe1d47fa26015f5 Mon Sep 17 00:00:00 2001 From: "paulina.shakirova" Date: Tue, 26 May 2026 00:29:27 +0200 Subject: [PATCH 4/8] [Navigation] Fix CI regressions from bridge commit Three FTR failures on the latest CI run trace back to the bridge commit that introduced 'sideNav' on deep links that previously had no explicit visibleIn: 1. Serverless ML search_bar_features (search, security, observability) File upload / Index data visualizer / Data drift had ['sideNav'] in the bridge commit, which dropped them from globalSearch. Restored ['globalSearch', 'solutionSideNav'] and reset their breadcrumb-only "*Page" variants back to visibleIn: [] (matching main). 2. data_views feature_controls/security (read-only privileges) 3. APM feature_controls/apm_security (all privileges) The Management plugin's appUpdater default of ['globalSearch', 'classicSideNav', 'solutionSideNav'] surfaced every Stack Management subapp (Data Views, Content Connectors, Alerts, ...) as a top-level entry in the classic hamburger nav. Main never did that because it omitted 'sideNav' by default. Dropped 'classicSideNav' from the default so the classic nav behavior matches main while keeping 'solutionSideNav' for solution nav trees that reference management deep links by id (e.g. 'management:dataViews'). Unit tests updated to match new expectations. --- .../register_search_links/search_deep_links.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts index a51795e0b23dd..724eaef5ab118 100644 --- a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts +++ b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts @@ -153,7 +153,7 @@ function createDeepLinks( defaultMessage: 'Log rate analysis', }), path: `/${ML_PAGES.AIOPS_LOG_RATE_ANALYSIS}`, - visibleIn: ['solutionSideNav'], + visibleIn: [], }, { id: 'logPatternAnalysis', @@ -169,7 +169,7 @@ function createDeepLinks( defaultMessage: 'Log pattern analysis', }), path: `/${ML_PAGES.AIOPS_LOG_CATEGORIZATION}`, - visibleIn: ['solutionSideNav'], + visibleIn: [], }, { id: 'changePointDetections', @@ -185,7 +185,7 @@ function createDeepLinks( defaultMessage: 'Change point detection', }), path: `/${ML_PAGES.AIOPS_CHANGE_POINT_DETECTION}`, - visibleIn: ['solutionSideNav'], + visibleIn: [], }, ], }; @@ -223,7 +223,7 @@ function createDeepLinks( }), keywords: ['CSV', 'JSON'], path: `/${ML_PAGES.DATA_VISUALIZER_FILE}`, - visibleIn: ['solutionSideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }; }, @@ -234,7 +234,7 @@ function createDeepLinks( defaultMessage: 'Index data visualizer', }), path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_SELECT}`, - visibleIn: ['solutionSideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }; }, getIndexDataVisualizerPageDeepLink: (): AppDeepLink => { @@ -244,7 +244,7 @@ function createDeepLinks( defaultMessage: 'Index data visualizer', }), path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_VIEWER}`, - visibleIn: ['solutionSideNav'], + visibleIn: [], }; }, @@ -266,7 +266,7 @@ function createDeepLinks( defaultMessage: 'Data drift', }), path: `/${ML_PAGES.DATA_DRIFT_INDEX_SELECT}`, - visibleIn: ['solutionSideNav'], + visibleIn: ['globalSearch', 'solutionSideNav'], }; }, getDataDriftPageDeepLink: (): AppDeepLink => { @@ -276,7 +276,7 @@ function createDeepLinks( defaultMessage: 'Data drift', }), path: `/${ML_PAGES.DATA_DRIFT}`, - visibleIn: ['solutionSideNav'], + visibleIn: [], }; }, }; From 3b5a518d883789826949df3a9852f8c53c1b5e8b Mon Sep 17 00:00:00 2001 From: "paulina.shakirova" Date: Tue, 26 May 2026 01:17:50 +0200 Subject: [PATCH 5/8] [Navigation] Update deep link visibility settings to exclude 'classicSideNav' This commit modifies the default visibility settings for management plugins to exclude 'classicSideNav', ensuring that management sub-apps do not appear as top-level entries in the classic hamburger navigation. The changes also update related unit tests to reflect the new expectations for deep link visibility, specifically for applications without an explicit 'visibleIn' setting. Additionally, the test cases for file upload, index data visualizer, and data drift have been adjusted to confirm their visibility in both 'globalSearch' and 'solutionSideNav', while breadcrumb-only page variants are now correctly set to be hidden everywhere. --- .../shared/management/public/plugin.tsx | 9 ++--- .../public/plugin_deep_links.test.ts | 4 +-- .../search_deep_links.test.ts | 34 +++++++++++-------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/platform/plugins/shared/management/public/plugin.tsx b/src/platform/plugins/shared/management/public/plugin.tsx index d1d913b39aa37..9f202d352ecdd 100644 --- a/src/platform/plugins/shared/management/public/plugin.tsx +++ b/src/platform/plugins/shared/management/public/plugin.tsx @@ -83,10 +83,11 @@ export class ManagementPlugin title: mgmtApp.title, path: mgmtApp.basePath, keywords: mgmtApp.keywords, - // Default includes both 'classicSideNav' and 'solutionSideNav' so all management sections - // appear in both navigation modes. Sections that explicitly set visibleIn - // (e.g. to hide from globalSearch) must also include the relevant nav flags. - visibleIn: mgmtApp.visibleIn ?? ['globalSearch', 'classicSideNav', 'solutionSideNav'], + // Default excludes 'classicSideNav' so management sub-apps do not surface as + // top-level entries in the classic hamburger nav (the "Stack Management" app + // is the entry point there). 'solutionSideNav' is included so solution + // navigation trees can keep referencing them by id (e.g. 'management:dataViews'). + visibleIn: mgmtApp.visibleIn ?? ['globalSearch', 'solutionSideNav'], })), })); diff --git a/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts b/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts index 1a34c162a8eba..deda95f04ee8e 100644 --- a/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts +++ b/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts @@ -31,7 +31,7 @@ function getDeepLinksFromUpdater(plugin: ManagementPlugin): AppDeepLink[] { } describe('ManagementPlugin appUpdater deep link visibleIn', () => { - it('defaults visibleIn to globalSearch and classicSideNav/solutionSideNav for apps without an explicit visibleIn', () => { + it('defaults visibleIn to globalSearch + solutionSideNav for apps without an explicit visibleIn (excludes classicSideNav to avoid duplicating Stack Management entries in the classic hamburger nav)', () => { const plugin = createPlugin(); const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); @@ -46,7 +46,7 @@ describe('ManagementPlugin appUpdater deep link visibleIn', () => { const app = kibana?.deepLinks?.find((a) => a.id === 'test-no-visible-in'); expect(app).toBeDefined(); - expect(app?.visibleIn).toEqual(['globalSearch', 'classicSideNav', 'solutionSideNav']); + expect(app?.visibleIn).toEqual(['globalSearch', 'solutionSideNav']); }); it('preserves explicit visibleIn when set to classicSideNav/solutionSideNav-only', () => { diff --git a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts index ee6b06dd91a03..0e16d3bd26cf3 100644 --- a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts +++ b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts @@ -45,30 +45,36 @@ describe('getDeepLinks', () => { ]); }); - it('hidden nav nodes (breadcrumb-only) use solutionSideNav-only visibleIn', () => { + it('file upload / index data visualizer / data drift remain in globalSearch + solutionSideNav', () => { const links = getDeepLinks(true, fullCapabilities, false); - const aiOps = links.find((l) => l.id === 'aiOps'); - expect(links.find((l) => l.id === 'fileUpload')?.visibleIn).toEqual(['solutionSideNav']); - expect(links.find((l) => l.id === 'indexDataVisualizer')?.visibleIn).toEqual([ - 'solutionSideNav', - ]); - expect(links.find((l) => l.id === 'indexDataVisualizerPage')?.visibleIn).toEqual([ - 'solutionSideNav', - ]); - expect(links.find((l) => l.id === 'dataDrift')?.visibleIn).toEqual(['solutionSideNav']); - expect(links.find((l) => l.id === 'dataDriftPage')?.visibleIn).toEqual(['solutionSideNav']); - expect(aiOps?.deepLinks?.find((l) => l.id === 'logRateAnalysisPage')?.visibleIn).toEqual([ + expect(links.find((l) => l.id === 'fileUpload')?.visibleIn).toEqual([ + 'globalSearch', 'solutionSideNav', ]); - expect(aiOps?.deepLinks?.find((l) => l.id === 'logPatternAnalysisPage')?.visibleIn).toEqual([ + expect(links.find((l) => l.id === 'indexDataVisualizer')?.visibleIn).toEqual([ + 'globalSearch', 'solutionSideNav', ]); - expect(aiOps?.deepLinks?.find((l) => l.id === 'changePointDetectionsPage')?.visibleIn).toEqual([ + expect(links.find((l) => l.id === 'dataDrift')?.visibleIn).toEqual([ + 'globalSearch', 'solutionSideNav', ]); }); + it('breadcrumb-only *Page variants are hidden everywhere (visibleIn: [])', () => { + const links = getDeepLinks(true, fullCapabilities, false); + const aiOps = links.find((l) => l.id === 'aiOps'); + + expect(links.find((l) => l.id === 'indexDataVisualizerPage')?.visibleIn).toEqual([]); + expect(links.find((l) => l.id === 'dataDriftPage')?.visibleIn).toEqual([]); + expect(aiOps?.deepLinks?.find((l) => l.id === 'logRateAnalysisPage')?.visibleIn).toEqual([]); + expect(aiOps?.deepLinks?.find((l) => l.id === 'logPatternAnalysisPage')?.visibleIn).toEqual([]); + expect(aiOps?.deepLinks?.find((l) => l.id === 'changePointDetectionsPage')?.visibleIn).toEqual( + [] + ); + }); + it('omits links when capabilities or license are insufficient', () => { expect( getDeepLinks(false, fullCapabilities, false).find((l) => l.id === 'overview') From 567865738575eeff14d08e092f0dd36f67986d6d Mon Sep 17 00:00:00 2001 From: "paulina.shakirova" Date: Tue, 26 May 2026 14:56:35 +0200 Subject: [PATCH 6/8] [Navigation] Refine deep link visibility settings to enhance user experience This commit updates the visibility settings for deep links across various applications, ensuring that 'classicSideNav' is excluded by default. The changes clarify the intended navigation structure, allowing deep links to be visible in 'globalSearch' and 'solutionSideNav' only. Additionally, related unit tests have been adjusted to reflect these updates, confirming that deep links are correctly categorized and displayed based on the new visibility rules. --- .../application/browser/src/application.ts | 23 ++++---- .../project_navigation_service.test.ts | 4 +- .../shared/management/public/plugin.tsx | 4 -- .../public/plugin_deep_links.test.ts | 58 +------------------ .../public/plugin.test.ts | 2 +- .../shared/fleet/public/deep_links.test.ts | 51 +--------------- .../search_deep_links.test.ts | 40 +------------ .../plugins/infra/public/plugin.test.ts | 26 ++------- .../plugins/infra/public/plugin.ts | 4 +- 9 files changed, 22 insertions(+), 190 deletions(-) diff --git a/src/core/packages/application/browser/src/application.ts b/src/core/packages/application/browser/src/application.ts index 9174d5e10599e..bcb9a02d300fa 100644 --- a/src/core/packages/application/browser/src/application.ts +++ b/src/core/packages/application/browser/src/application.ts @@ -102,21 +102,16 @@ export interface App extends AppNavOptions { status?: AppStatus; /** - * Optional list of locations where the app is visible. - * - * Accepts the following values: - * - "globalSearch": the link will appear in the global search bar - * - "home": the link will appear on the Kibana home page - * - "kibanaOverview": the link will appear in the Kibana overview page - * - "classicSideNav": the link will appear in the classic (hamburger) side navigation only. - * - "solutionSideNav": the link will appear in solution navs only, not in the classic side navigation. - * Use this when the link should be accessible from a solution nav but hidden from the classic hamburger menu. - * - * To appear in both classic and solution navs, use `['classicSideNav', 'solutionSideNav']`. + * Locations where the app is visible. Each value enables one surface: + * - `globalSearch`: top-bar search results + * - `classicSideNav`: classic (hamburger) side navigation + * - `solutionSideNav`: solution side navs (Observability, Security, Search) + * - `home`: Kibana home page + * - `kibanaOverview`: Kibana overview page * * @default ['globalSearch', 'classicSideNav', 'solutionSideNav'] * unless the status is marked as `inaccessible`. - * @note Set to `[]` (empty array) to hide this link + * @note Set to `[]` (empty array) to hide this link from every surface. */ visibleIn?: AppDeepLinkLocations[]; @@ -260,6 +255,7 @@ export type PublicAppDeepLinkInfo = Omit = { /** Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. */ keywords?: string[]; /** - * Optional list of locations where the deepLink is visible. By default the deepLink is visible in "globalSearch". + * Locations where the deep link is visible. See {@link App.visibleIn} for the list of surfaces. + * @default ['globalSearch'] */ visibleIn?: AppDeepLinkLocations[]; /** diff --git a/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts b/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts index de1c6e21c74f8..8c8b9a86bd843 100644 --- a/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts @@ -250,7 +250,7 @@ describe('initNavigation()', () => { expect(node.children?.map((c) => c.id)).toEqual(['management:genAiSettings']); }); - test('should filter out deepLinks that only include classicSideNav from visibleIn', async () => { + test('removes deepLinks from the solution nav tree when their visibleIn excludes solutionSideNav', async () => { const { projectNavigation: svc, navLinksService: nls } = setup({ navLinkIds: ['management:genAiSettings'], }); @@ -273,7 +273,7 @@ describe('initNavigation()', () => { type: 'navGroup', children: [ { link: 'management:genAiSettings' }, - { link: 'management:evals' }, // classicSideNav only → removed from solution nav + { link: 'management:evals' }, // visibleIn excludes solutionSideNav → removed ], }, ], diff --git a/src/platform/plugins/shared/management/public/plugin.tsx b/src/platform/plugins/shared/management/public/plugin.tsx index 9f202d352ecdd..6f05088ac61d7 100644 --- a/src/platform/plugins/shared/management/public/plugin.tsx +++ b/src/platform/plugins/shared/management/public/plugin.tsx @@ -83,10 +83,6 @@ export class ManagementPlugin title: mgmtApp.title, path: mgmtApp.basePath, keywords: mgmtApp.keywords, - // Default excludes 'classicSideNav' so management sub-apps do not surface as - // top-level entries in the classic hamburger nav (the "Stack Management" app - // is the entry point there). 'solutionSideNav' is included so solution - // navigation trees can keep referencing them by id (e.g. 'management:dataViews'). visibleIn: mgmtApp.visibleIn ?? ['globalSearch', 'solutionSideNav'], })), })); diff --git a/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts b/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts index deda95f04ee8e..8be87dab41d06 100644 --- a/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts +++ b/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts @@ -31,7 +31,7 @@ function getDeepLinksFromUpdater(plugin: ManagementPlugin): AppDeepLink[] { } describe('ManagementPlugin appUpdater deep link visibleIn', () => { - it('defaults visibleIn to globalSearch + solutionSideNav for apps without an explicit visibleIn (excludes classicSideNav to avoid duplicating Stack Management entries in the classic hamburger nav)', () => { + it('defaults to globalSearch + solutionSideNav (no classicSideNav, to avoid duplicating Stack Management entries in the classic hamburger)', () => { const plugin = createPlugin(); const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); @@ -49,42 +49,6 @@ describe('ManagementPlugin appUpdater deep link visibleIn', () => { expect(app?.visibleIn).toEqual(['globalSearch', 'solutionSideNav']); }); - it('preserves explicit visibleIn when set to classicSideNav/solutionSideNav-only', () => { - const plugin = createPlugin(); - const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); - - setup.sections.section.kibana.registerApp({ - id: 'test-both-nav-only', - title: 'Test App both nav only', - mount: jest.fn(), - visibleIn: ['classicSideNav', 'solutionSideNav'], - }); - - const deepLinks = getDeepLinksFromUpdater(plugin); - const kibana = deepLinks.find((s) => s.id === 'kibana'); - const app = kibana?.deepLinks?.find((a) => a.id === 'test-both-nav-only'); - - expect(app?.visibleIn).toEqual(['classicSideNav', 'solutionSideNav']); - }); - - it('preserves explicit visibleIn when set to globalSearch-only', () => { - const plugin = createPlugin(); - const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); - - setup.sections.section.kibana.registerApp({ - id: 'test-global-only', - title: 'Test App globalSearch only', - mount: jest.fn(), - visibleIn: ['globalSearch'], - }); - - const deepLinks = getDeepLinksFromUpdater(plugin); - const kibana = deepLinks.find((s) => s.id === 'kibana'); - const app = kibana?.deepLinks?.find((a) => a.id === 'test-global-only'); - - expect(app?.visibleIn).toEqual(['globalSearch']); - }); - it('excludes apps with hideFromGlobalSearch:true and no explicit visibleIn', () => { const plugin = createPlugin(); const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); @@ -102,24 +66,4 @@ describe('ManagementPlugin appUpdater deep link visibleIn', () => { expect(app).toBeUndefined(); }); - - it('includes apps with hideFromGlobalSearch:true when explicit visibleIn is set', () => { - const plugin = createPlugin(); - const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); - - setup.sections.section.kibana.registerApp({ - id: 'test-hidden-with-visible-in', - title: 'Hidden App With visibleIn', - mount: jest.fn(), - hideFromGlobalSearch: true, - visibleIn: ['classicSideNav', 'solutionSideNav'], - }); - - const deepLinks = getDeepLinksFromUpdater(plugin); - const kibana = deepLinks.find((s) => s.id === 'kibana'); - const app = kibana?.deepLinks?.find((a) => a.id === 'test-hidden-with-visible-in'); - - expect(app).toBeDefined(); - expect(app?.visibleIn).toEqual(['classicSideNav', 'solutionSideNav']); - }); }); diff --git a/src/platform/plugins/shared/workflows_management/public/plugin.test.ts b/src/platform/plugins/shared/workflows_management/public/plugin.test.ts index 73c540e92e0b6..c7a9a4bb5ab2f 100644 --- a/src/platform/plugins/shared/workflows_management/public/plugin.test.ts +++ b/src/platform/plugins/shared/workflows_management/public/plugin.test.ts @@ -200,7 +200,7 @@ describe('WorkflowsPlugin', () => { ]); }); - it('should hide the app from solutionSideNav when the user lacks read capability', () => { + it('should hide the app from classicSideNav and solutionSideNav when the user lacks read capability', () => { setReadCapability(false); setLicenseValid(true); const updates = captureAppUpdates(); diff --git a/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts b/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts index b76e2fee632c8..967686408d08c 100644 --- a/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts +++ b/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts @@ -40,36 +40,6 @@ const authorizedAuthz: FleetAuthz = { }, }; -const unauthorizedAuthz: FleetAuthz = { - fleet: { - all: false, - setup: false, - readEnrollmentTokens: false, - readAgentPolicies: false, - allAgentPolicies: false, - readAgents: false, - allAgents: false, - readSettings: false, - allSettings: false, - generateAgentReports: false, - addAgents: false, - addFleetServers: false, - }, - integrations: { - all: false, - readPackageInfo: false, - readInstalledPackages: false, - installPackages: false, - upgradePackages: false, - removePackages: false, - uploadPackages: false, - readPackageSettings: false, - writePackageSettings: false, - readIntegrationPolicies: false, - writeIntegrationPolicies: false, - }, -}; - const authzGatedIds = [ FleetDeepLinkId.agents, FleetDeepLinkId.policies, @@ -79,7 +49,7 @@ const authzGatedIds = [ ]; describe('getFleetDeepLinks', () => { - it('includes solutionSideNav in visibleIn for all authz-gated links when authorized', () => { + it('authz-gated links include solutionSideNav when authorized', () => { const links = getFleetDeepLinks(allowedExperimentalValues, authorizedAuthz); for (const id of authzGatedIds) { expect(links.find((l) => l.id === id)?.visibleIn).toEqual([ @@ -88,23 +58,4 @@ describe('getFleetDeepLinks', () => { ]); } }); - - it('returns empty visibleIn for all authz-gated links when unauthorized', () => { - const links = getFleetDeepLinks(allowedExperimentalValues, unauthorizedAuthz); - for (const id of authzGatedIds) { - expect(links.find((l) => l.id === id)?.visibleIn).toEqual([]); - } - }); - - it('returns empty visibleIn for all authz-gated links when authz is undefined', () => { - const links = getFleetDeepLinks(allowedExperimentalValues, undefined); - for (const id of authzGatedIds) { - expect(links.find((l) => l.id === id)?.visibleIn).toEqual([]); - } - }); - - it('dataStreams link has no visibleIn restriction', () => { - const links = getFleetDeepLinks(allowedExperimentalValues, undefined); - expect(links.find((l) => l.id === FleetDeepLinkId.dataStreams)?.visibleIn).toBeUndefined(); - }); }); diff --git a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts index 0e16d3bd26cf3..c8e3361a71d5a 100644 --- a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts +++ b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts @@ -15,34 +15,12 @@ const fullCapabilities: MlCapabilities = { }; describe('getDeepLinks', () => { - it('visible nav links include both globalSearch and solutionSideNav', () => { + it('primary visible nav links include globalSearch and solutionSideNav', () => { const links = getDeepLinks(true, fullCapabilities, false); - const anomalyDetection = links.find((l) => l.id === 'anomalyDetection'); - const dfa = links.find((l) => l.id === 'dataFrameAnalytics'); - const aiOps = links.find((l) => l.id === 'aiOps'); - expect(links.find((l) => l.id === 'overview')?.visibleIn).toEqual([ 'globalSearch', 'solutionSideNav', ]); - expect(links.find((l) => l.id === 'dataVisualizer')?.visibleIn).toEqual([ - 'globalSearch', - 'solutionSideNav', - ]); - expect(anomalyDetection?.deepLinks?.find((l) => l.id === 'anomalyExplorer')?.visibleIn).toEqual( - ['globalSearch', 'solutionSideNav'] - ); - expect( - anomalyDetection?.deepLinks?.find((l) => l.id === 'singleMetricViewer')?.visibleIn - ).toEqual(['globalSearch', 'solutionSideNav']); - expect(dfa?.deepLinks?.find((l) => l.id === 'resultExplorer')?.visibleIn).toEqual([ - 'globalSearch', - 'solutionSideNav', - ]); - expect(aiOps?.deepLinks?.find((l) => l.id === 'logRateAnalysis')?.visibleIn).toEqual([ - 'globalSearch', - 'solutionSideNav', - ]); }); it('file upload / index data visualizer / data drift remain in globalSearch + solutionSideNav', () => { @@ -74,20 +52,4 @@ describe('getDeepLinks', () => { [] ); }); - - it('omits links when capabilities or license are insufficient', () => { - expect( - getDeepLinks(false, fullCapabilities, false).find((l) => l.id === 'overview') - ).toBeUndefined(); - expect( - getDeepLinks(true, { ...fullCapabilities, isADEnabled: false }, false).find( - (l) => l.id === 'anomalyDetection' - ) - ).toBeUndefined(); - expect( - getDeepLinks(true, { ...fullCapabilities, canUseAiops: false }, false).find( - (l) => l.id === 'aiOps' - ) - ).toBeUndefined(); - }); }); diff --git a/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts b/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts index 4dfe4e9e93f92..57e3a879a16dd 100644 --- a/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts +++ b/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts @@ -8,39 +8,21 @@ import { getInfraDeepLinks } from './plugin'; describe('getInfraDeepLinks', () => { - it('visible nav links include globalSearch and solutionSideNav', () => { - const links = getInfraDeepLinks({ metricsExplorerEnabled: false }); + it('primary nav links use the shared globalSearch + solutionSideNav value', () => { + const links = getInfraDeepLinks({ metricsExplorerEnabled: true }); expect(links.find((l) => l.id === 'inventory')?.visibleIn).toEqual([ 'globalSearch', 'solutionSideNav', ]); - expect(links.find((l) => l.id === 'hosts')?.visibleIn).toEqual([ - 'globalSearch', - 'solutionSideNav', - ]); - }); - - it('settings is solutionSideNav (hidden breadcrumb node)', () => { - const links = getInfraDeepLinks({ metricsExplorerEnabled: false }); - expect(links.find((l) => l.id === 'settings')?.visibleIn).toEqual(['solutionSideNav']); - }); - - it('assetDetails is hidden from all nav locations', () => { - const links = getInfraDeepLinks({ metricsExplorerEnabled: false }); - expect(links.find((l) => l.id === 'assetDetails')?.visibleIn).toEqual([]); - }); - - it('includes metrics-explorer with globalSearch and solutionSideNav when feature flag is enabled', () => { - const links = getInfraDeepLinks({ metricsExplorerEnabled: true }); expect(links.find((l) => l.id === 'metrics-explorer')?.visibleIn).toEqual([ 'globalSearch', 'solutionSideNav', ]); }); - it('omits metrics-explorer when feature flag is disabled', () => { + it('settings is globalSearch-only (matches main: searchable, not in any nav)', () => { const links = getInfraDeepLinks({ metricsExplorerEnabled: false }); - expect(links.find((l) => l.id === 'metrics-explorer')).toBeUndefined(); + expect(links.find((l) => l.id === 'settings')?.visibleIn).toEqual(['globalSearch']); }); }); diff --git a/x-pack/solutions/observability/plugins/infra/public/plugin.ts b/x-pack/solutions/observability/plugins/infra/public/plugin.ts index dcf5968868565..deb0478a0f7ba 100644 --- a/x-pack/solutions/observability/plugins/infra/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/infra/public/plugin.ts @@ -99,7 +99,7 @@ export const getInfraDeepLinks = ({ defaultMessage: 'Metrics Explorer', }), path: '/explorer', - visibleIn: ['globalSearch', 'solutionSideNav'] as AppDeepLinkLocations[], + visibleIn, }, ] : []), @@ -109,7 +109,7 @@ export const getInfraDeepLinks = ({ defaultMessage: 'Settings', }), path: '/settings', - visibleIn: ['solutionSideNav'], + visibleIn: ['globalSearch'], }, { id: 'assetDetails', From ebde11ec98457f8e0c93dcb999018b050f937e13 Mon Sep 17 00:00:00 2001 From: "paulina.shakirova" Date: Fri, 29 May 2026 12:12:24 +0200 Subject: [PATCH 7/8] replace 'solutionSideNav' with 'projectSideNav' --- .../browser-internal/src/utils/constants.ts | 2 +- .../src/utils/get_app_info.test.ts | 20 +++++----- .../application/browser/src/application.ts | 6 +-- .../src/classic/collapsible_nav.test.tsx | 2 +- .../navigation/to_navigation_items.test.tsx | 2 +- .../nav_links/nav_links_service.test.ts | 2 +- .../services/nav_links/to_nav_link.test.ts | 4 +- .../project_navigation_service.test.ts | 18 ++++----- .../services/project_navigation/utils.test.ts | 2 +- .../src/services/project_navigation/utils.ts | 4 +- .../private/kibana_overview/public/plugin.ts | 2 +- .../plugins/shared/dev_tools/public/plugin.ts | 2 +- .../plugins/shared/discover/public/plugin.tsx | 2 +- .../shared/management/public/plugin.tsx | 2 +- .../public/plugin_deep_links.test.ts | 4 +- .../public/plugin.test.ts | 14 +++---- .../workflows_management/public/plugin.ts | 8 ++-- .../core_plugins/application_status.ts | 2 +- .../shared/agent_builder/public/register.ts | 2 +- .../shared/fleet/public/deep_links.test.ts | 7 +--- .../plugins/shared/fleet/public/deep_links.ts | 10 ++--- .../shared/inbox/public/plugin.test.ts | 2 +- .../plugins/shared/inbox/public/plugin.tsx | 2 +- .../shared/ingest_hub/public/plugin.test.tsx | 2 +- .../shared/ingest_hub/public/plugin.tsx | 2 +- .../search_deep_links.test.ts | 12 +++--- .../search_deep_links.ts | 24 +++++------ .../shared/streams_app/public/plugin.tsx | 2 +- .../triggers_actions_ui/public/plugin.ts | 2 +- .../plugins/apm/public/plugin.ts | 12 +++--- .../plugins/infra/public/pages/logs/routes.ts | 4 +- .../plugins/infra/public/plugin.test.ts | 6 +-- .../plugins/infra/public/plugin.ts | 2 +- .../plugins/observability/public/plugin.ts | 12 +++--- .../public/plugin.tsx | 2 +- .../observability_onboarding/public/plugin.ts | 2 +- .../update_global_navigation.test.tsx | 40 ++++--------------- .../services/update_global_navigation.tsx | 8 ++-- .../plugins/profiling/public/plugin.ts | 6 +-- .../plugins/synthetics/public/plugin.ts | 4 +- .../plugins/uptime/public/plugin.ts | 2 +- .../search_getting_started/public/plugin.ts | 2 +- .../plugins/search_homepage/public/plugin.ts | 2 +- .../search_playground/public/plugin.ts | 2 +- .../search_query_rules/public/plugin.ts | 2 +- .../plugins/search_synonyms/public/plugin.ts | 4 +- .../public/app/links/deep_links.test.ts | 16 ++++---- .../public/app/links/deep_links.ts | 4 +- .../public/onboarding/links.ts | 2 +- .../public/application/register.ts | 2 +- 50 files changed, 137 insertions(+), 164 deletions(-) diff --git a/src/core/packages/application/browser-internal/src/utils/constants.ts b/src/core/packages/application/browser-internal/src/utils/constants.ts index e08141c5df71a..be1f9108dc36f 100644 --- a/src/core/packages/application/browser-internal/src/utils/constants.ts +++ b/src/core/packages/application/browser-internal/src/utils/constants.ts @@ -12,7 +12,7 @@ import type { AppDeepLinkLocations } from '@kbn/core-application-browser/src/app export const DEFAULT_APP_VISIBILITY: AppDeepLinkLocations[] = [ 'globalSearch', 'classicSideNav', - 'solutionSideNav', + 'projectSideNav', ]; export const DEFAULT_LINK_VISIBILITY: AppDeepLinkLocations[] = ['globalSearch']; diff --git a/src/core/packages/application/browser-internal/src/utils/get_app_info.test.ts b/src/core/packages/application/browser-internal/src/utils/get_app_info.test.ts index 17bf113f1c352..07869260de236 100644 --- a/src/core/packages/application/browser-internal/src/utils/get_app_info.test.ts +++ b/src/core/packages/application/browser-internal/src/utils/get_app_info.test.ts @@ -41,7 +41,7 @@ describe('getAppInfo', () => { id: 'some-id', title: 'some-title', status: AppStatus.accessible, - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], appRoute: `/app/some-id`, keywords: [], deepLinks: [], @@ -70,7 +70,7 @@ describe('getAppInfo', () => { id: 'some-id', title: 'some-title', status: AppStatus.accessible, - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], appRoute: `/app/some-id`, keywords: [], deepLinks: [ @@ -98,7 +98,7 @@ describe('getAppInfo', () => { expect( getAppInfo( createApp({ - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], status: AppStatus.inaccessible, }) ) @@ -110,25 +110,25 @@ describe('getAppInfo', () => { expect( getAppInfo( createApp({ - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], status: AppStatus.accessible, }) ) ).toEqual( expect.objectContaining({ - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }) ); expect( getAppInfo( createApp({ // status is not set, default to accessible - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }) ) ).toEqual( expect.objectContaining({ - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }) ); }); @@ -159,7 +159,7 @@ describe('getAppInfo', () => { id: 'some-id', title: 'some-title', status: AppStatus.accessible, - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], appRoute: `/app/some-id`, keywords: [], order: 3, @@ -192,7 +192,7 @@ describe('getAppInfo', () => { createApp({ deepLinks: [ createDeepLink({ - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }), ], }) @@ -201,7 +201,7 @@ describe('getAppInfo', () => { expect.objectContaining({ deepLinks: [ expect.objectContaining({ - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }), ], }) diff --git a/src/core/packages/application/browser/src/application.ts b/src/core/packages/application/browser/src/application.ts index bcb9a02d300fa..b1c84856d2a41 100644 --- a/src/core/packages/application/browser/src/application.ts +++ b/src/core/packages/application/browser/src/application.ts @@ -105,11 +105,11 @@ export interface App extends AppNavOptions { * Locations where the app is visible. Each value enables one surface: * - `globalSearch`: top-bar search results * - `classicSideNav`: classic (hamburger) side navigation - * - `solutionSideNav`: solution side navs (Observability, Security, Search) + * - `projectSideNav`: project side navs (Observability, Security, Search) * - `home`: Kibana home page * - `kibanaOverview`: Kibana overview page * - * @default ['globalSearch', 'classicSideNav', 'solutionSideNav'] + * @default ['globalSearch', 'classicSideNav', 'projectSideNav'] * unless the status is marked as `inaccessible`. * @note Set to `[]` (empty array) to hide this link from every surface. */ @@ -256,7 +256,7 @@ export type AppDeepLinkLocations = | 'globalSearch' | 'classicSideNav' // TODO: rename to 'sideNav' when 'classicSideNav' is removed (classic nav deprecation). - | 'solutionSideNav' + | 'projectSideNav' | 'home' | 'kibanaOverview'; diff --git a/src/core/packages/chrome/browser-components/src/classic/collapsible_nav.test.tsx b/src/core/packages/chrome/browser-components/src/classic/collapsible_nav.test.tsx index 48a51aab036b7..3df45fce224c5 100644 --- a/src/core/packages/chrome/browser-components/src/classic/collapsible_nav.test.tsx +++ b/src/core/packages/chrome/browser-components/src/classic/collapsible_nav.test.tsx @@ -22,7 +22,7 @@ import { CollapsibleNav } from './collapsible_nav'; const { kibana, observability, security, management } = DEFAULT_APP_CATEGORIES; -const visibleIn = ['globalSearch' as const, 'classicSideNav' as const, 'solutionSideNav' as const]; +const visibleIn = ['globalSearch' as const, 'classicSideNav' as const, 'projectSideNav' as const]; function mockLink({ title = 'discover', category }: Partial) { return { diff --git a/src/core/packages/chrome/browser-components/src/project/sidenav/navigation/to_navigation_items.test.tsx b/src/core/packages/chrome/browser-components/src/project/sidenav/navigation/to_navigation_items.test.tsx index 22c3dd1faca4b..a078526d00465 100644 --- a/src/core/packages/chrome/browser-components/src/project/sidenav/navigation/to_navigation_items.test.tsx +++ b/src/core/packages/chrome/browser-components/src/project/sidenav/navigation/to_navigation_items.test.tsx @@ -254,7 +254,7 @@ describe('hidden panel link', () => { baseUrl: '/', href: '/app/management', url: '/app/management', - visibleIn: ['classicSideNav', 'solutionSideNav'], + visibleIn: ['classicSideNav', 'projectSideNav'], }, sideNavStatus: 'hidden', id: 'stack_management', diff --git a/src/core/packages/chrome/browser-internal/src/services/nav_links/nav_links_service.test.ts b/src/core/packages/chrome/browser-internal/src/services/nav_links/nav_links_service.test.ts index c45b6d09dd821..75fed1bf96d0c 100644 --- a/src/core/packages/chrome/browser-internal/src/services/nav_links/nav_links_service.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/nav_links/nav_links_service.test.ts @@ -28,7 +28,7 @@ const availableApps: ReadonlyMap = new Map([ order: 50, title: 'Deep App 1', path: '/deepapp1', - visibleIn: ['classicSideNav', 'solutionSideNav'], + visibleIn: ['classicSideNav', 'projectSideNav'], deepLinks: [ { id: 'deepApp2', diff --git a/src/core/packages/chrome/browser-internal/src/services/nav_links/to_nav_link.test.ts b/src/core/packages/chrome/browser-internal/src/services/nav_links/to_nav_link.test.ts index 4b64a6687aae6..1787e857cbc0d 100644 --- a/src/core/packages/chrome/browser-internal/src/services/nav_links/to_nav_link.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/nav_links/to_nav_link.test.ts @@ -47,7 +47,7 @@ describe('toNavLink', () => { order: 12, tooltip: 'tooltip', euiIconType: 'my-icon', - visibleIn: ['classicSideNav', 'solutionSideNav'], + visibleIn: ['classicSideNav', 'projectSideNav'], }), basePath ); @@ -58,7 +58,7 @@ describe('toNavLink', () => { order: 12, tooltip: 'tooltip', euiIconType: 'my-icon', - visibleIn: ['classicSideNav', 'solutionSideNav'], + visibleIn: ['classicSideNav', 'projectSideNav'], }) ); }); diff --git a/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts b/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts index 8c8b9a86bd843..864114d907419 100644 --- a/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/project_navigation/project_navigation_service.test.ts @@ -42,7 +42,7 @@ const getNavLink = (partial: Partial = {}): ChromeNavLink => ({ baseUrl: '/app', url: `/app/${partial.id ?? 'kibana'}`, href: `/app/${partial.id ?? 'kibana'}`, - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], ...partial, }); @@ -208,12 +208,12 @@ describe('initNavigation()', () => { expect(node.children?.[0].href).toBe('https://elastic.co'); }); - test('should filter out deepLinks that exclude solutionSideNav from visibleIn', async () => { + test('should filter out deepLinks that exclude projectSideNav from visibleIn', async () => { const { projectNavigation: svc, navLinksService: nls } = setup({ navLinkIds: ['management:genAiSettings'], }); - // Add a second link that is registered but explicitly excludes solutionSideNav + // Add a second link that is registered but explicitly excludes projectSideNav const evalsNoSideNav: ChromeNavLink = getNavLink({ id: 'management:evals', title: 'MANAGEMENT:EVALS', @@ -232,7 +232,7 @@ describe('initNavigation()', () => { type: 'navGroup', children: [ { link: 'management:genAiSettings' }, - { link: 'management:evals' }, // registered but no solutionSideNav → removed + { link: 'management:evals' }, // registered but no projectSideNav → removed ], }, ], @@ -250,7 +250,7 @@ describe('initNavigation()', () => { expect(node.children?.map((c) => c.id)).toEqual(['management:genAiSettings']); }); - test('removes deepLinks from the solution nav tree when their visibleIn excludes solutionSideNav', async () => { + test('removes deepLinks from the solution nav tree when their visibleIn excludes projectSideNav', async () => { const { projectNavigation: svc, navLinksService: nls } = setup({ navLinkIds: ['management:genAiSettings'], }); @@ -273,7 +273,7 @@ describe('initNavigation()', () => { type: 'navGroup', children: [ { link: 'management:genAiSettings' }, - { link: 'management:evals' }, // visibleIn excludes solutionSideNav → removed + { link: 'management:evals' }, // visibleIn excludes projectSideNav → removed ], }, ], @@ -372,7 +372,7 @@ describe('initNavigation()', () => { id: 'foo', title: 'FOO', url: '/app/foo', - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }, href: '/app/foo', id: 'foo', @@ -921,7 +921,7 @@ describe('getNavigation$() active nodes', () => { baseUrl: '/app', url: '/app/item1', href: '/app/item1', - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }, }, ], @@ -975,7 +975,7 @@ describe('getNavigation$() active nodes', () => { baseUrl: '/app', url: '/app/item1', href: '/app/item1', - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }, getIsActive: expect.any(Function), }, diff --git a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts index 19dbd9d49bd8c..b4d9c1c9b6efc 100644 --- a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts +++ b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.test.ts @@ -21,7 +21,7 @@ const getDeepLink = (id: string, path: string, title = ''): ChromeNavLink => ({ href: `http://mocked/kibana/foo/${path}`, title, baseUrl: '', - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }); describe('flattenNav', () => { diff --git a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts index 16b3c6c932e90..f11b98588fd44 100644 --- a/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts +++ b/src/core/packages/chrome/browser-internal/src/services/project_navigation/utils.ts @@ -177,8 +177,8 @@ function getNodeStatus( }, { cloudLinks }: { cloudLinks: CloudLinks } ): SideNavNodeStatus | 'remove' { - if (link && (!deepLink || !deepLink.visibleIn.includes('solutionSideNav'))) { - // If a link is provided but no deepLink found, or the app excluded solutionSideNav + if (link && (!deepLink || !deepLink.visibleIn.includes('projectSideNav'))) { + // If a link is provided but no deepLink found, or the app excluded projectSideNav return 'remove'; } diff --git a/src/platform/plugins/private/kibana_overview/public/plugin.ts b/src/platform/plugins/private/kibana_overview/public/plugin.ts index f60a9edfc9665..843b5c24ea255 100644 --- a/src/platform/plugins/private/kibana_overview/public/plugin.ts +++ b/src/platform/plugins/private/kibana_overview/public/plugin.ts @@ -67,7 +67,7 @@ export class KibanaOverviewPlugin order: 1, updater$: appUpdater$, appRoute: PLUGIN_PATH, - visibleIn: ['globalSearch', 'home', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'home', 'classicSideNav', 'projectSideNav'], async mount(params: AppMountParameters) { // Load application bundle const { renderApp } = await import('./application'); diff --git a/src/platform/plugins/shared/dev_tools/public/plugin.ts b/src/platform/plugins/shared/dev_tools/public/plugin.ts index 2c8bcfe7a2b79..8f3758fb44b2b 100644 --- a/src/platform/plugins/shared/dev_tools/public/plugin.ts +++ b/src/platform/plugins/shared/dev_tools/public/plugin.ts @@ -130,7 +130,7 @@ export class DevToolsPlugin implements Plugin { id: tool.id, title: tool.title as string, path: `#/${tool.id}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }; if (!devtoolsDeeplinkIds.some((id) => id === deepLink.id)) { throw new Error('Deeplink must be registered in package.'); diff --git a/src/platform/plugins/shared/discover/public/plugin.tsx b/src/platform/plugins/shared/discover/public/plugin.tsx index b60c15120af5c..b5a4c6a97473c 100644 --- a/src/platform/plugins/shared/discover/public/plugin.tsx +++ b/src/platform/plugins/shared/discover/public/plugin.tsx @@ -184,7 +184,7 @@ export class DiscoverPlugin euiIconType: 'logoKibana', defaultPath: '#/', category: DEFAULT_APP_CATEGORIES.kibana, - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav', 'kibanaOverview'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav', 'kibanaOverview'], mount: async (params: AppMountParameters) => { const [[coreStart, discoverStartPlugins], historyService, ebtManager, { renderApp }] = await Promise.all([ diff --git a/src/platform/plugins/shared/management/public/plugin.tsx b/src/platform/plugins/shared/management/public/plugin.tsx index 6f05088ac61d7..80056e9bc5c85 100644 --- a/src/platform/plugins/shared/management/public/plugin.tsx +++ b/src/platform/plugins/shared/management/public/plugin.tsx @@ -83,7 +83,7 @@ export class ManagementPlugin title: mgmtApp.title, path: mgmtApp.basePath, keywords: mgmtApp.keywords, - visibleIn: mgmtApp.visibleIn ?? ['globalSearch', 'solutionSideNav'], + visibleIn: mgmtApp.visibleIn ?? ['globalSearch', 'projectSideNav'], })), })); diff --git a/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts b/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts index 8be87dab41d06..d63342d4b3ca8 100644 --- a/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts +++ b/src/platform/plugins/shared/management/public/plugin_deep_links.test.ts @@ -31,7 +31,7 @@ function getDeepLinksFromUpdater(plugin: ManagementPlugin): AppDeepLink[] { } describe('ManagementPlugin appUpdater deep link visibleIn', () => { - it('defaults to globalSearch + solutionSideNav (no classicSideNav, to avoid duplicating Stack Management entries in the classic hamburger)', () => { + it('defaults to globalSearch + projectSideNav (no classicSideNav, to avoid duplicating Stack Management entries in the classic hamburger)', () => { const plugin = createPlugin(); const setup = plugin.setup(coreMock.createSetup(), { share: mockShare }); @@ -46,7 +46,7 @@ describe('ManagementPlugin appUpdater deep link visibleIn', () => { const app = kibana?.deepLinks?.find((a) => a.id === 'test-no-visible-in'); expect(app).toBeDefined(); - expect(app?.visibleIn).toEqual(['globalSearch', 'solutionSideNav']); + expect(app?.visibleIn).toEqual(['globalSearch', 'projectSideNav']); }); it('excludes apps with hideFromGlobalSearch:true and no explicit visibleIn', () => { diff --git a/src/platform/plugins/shared/workflows_management/public/plugin.test.ts b/src/platform/plugins/shared/workflows_management/public/plugin.test.ts index c7a9a4bb5ab2f..c274d6ef958cc 100644 --- a/src/platform/plugins/shared/workflows_management/public/plugin.test.ts +++ b/src/platform/plugins/shared/workflows_management/public/plugin.test.ts @@ -196,11 +196,11 @@ describe('WorkflowsPlugin', () => { 'home', 'kibanaOverview', 'classicSideNav', - 'solutionSideNav', + 'projectSideNav', ]); }); - it('should hide the app from classicSideNav and solutionSideNav when the user lacks read capability', () => { + it('should hide the app from classicSideNav and projectSideNav when the user lacks read capability', () => { setReadCapability(false); setLicenseValid(true); const updates = captureAppUpdates(); @@ -214,11 +214,11 @@ describe('WorkflowsPlugin', () => { 'classicSideNav' ); expect(visibleInUpdates[visibleInUpdates.length - 1].visibleIn).not.toContain( - 'solutionSideNav' + 'projectSideNav' ); }); - it('should keep the app in classicSideNav and solutionSideNav and globalSearch when authorized but unavailable', () => { + it('should keep the app in classicSideNav and projectSideNav and globalSearch when authorized but unavailable', () => { setReadCapability(true); setLicenseValid(false); const updates = captureAppUpdates(); @@ -230,11 +230,11 @@ describe('WorkflowsPlugin', () => { expect(visibleInUpdates[visibleInUpdates.length - 1].visibleIn).toEqual([ 'globalSearch', 'classicSideNav', - 'solutionSideNav', + 'projectSideNav', ]); }); - it('should hide the app from classicSideNav and solutionSideNav for unauthorized users even when unavailable', () => { + it('should hide the app from classicSideNav and projectSideNav for unauthorized users even when unavailable', () => { setReadCapability(false); setLicenseValid(false); const updates = captureAppUpdates(); @@ -246,7 +246,7 @@ describe('WorkflowsPlugin', () => { expect(visibleInUpdates[visibleInUpdates.length - 1].visibleIn).toEqual([ 'globalSearch', 'classicSideNav', - 'solutionSideNav', + 'projectSideNav', ]); }); }); diff --git a/src/platform/plugins/shared/workflows_management/public/plugin.ts b/src/platform/plugins/shared/workflows_management/public/plugin.ts index 57ea271c5caa2..fe1b9410cbe01 100644 --- a/src/platform/plugins/shared/workflows_management/public/plugin.ts +++ b/src/platform/plugins/shared/workflows_management/public/plugin.ts @@ -205,14 +205,14 @@ export class WorkflowsPlugin }): AppDeepLinkLocations[] { // Not available takes precedence over authorized. if (!params.isAvailable) { - // Remove generic locations, but keep in 'classicSideNav', 'solutionSideNav' and globalSearch to make users aware of the feature. - return ['globalSearch', 'classicSideNav', 'solutionSideNav']; + // Remove generic locations, but keep in 'classicSideNav', 'projectSideNav' and globalSearch to make users aware of the feature. + return ['globalSearch', 'classicSideNav', 'projectSideNav']; } if (!params.isAuthorized) { - // Remove from 'classicSideNav', 'solutionSideNav' so it does not use nav real estate, but keep in globalSearch to make it discoverable + // Remove from 'classicSideNav', 'projectSideNav' so it does not use nav real estate, but keep in globalSearch to make it discoverable return ['globalSearch']; } - return ['globalSearch', 'home', 'kibanaOverview', 'classicSideNav', 'solutionSideNav']; + return ['globalSearch', 'home', 'kibanaOverview', 'classicSideNav', 'projectSideNav']; } /** diff --git a/src/platform/test/plugin_functional/test_suites/core_plugins/application_status.ts b/src/platform/test/plugin_functional/test_suites/core_plugins/application_status.ts index 5562da75676b4..73ed49a27852f 100644 --- a/src/platform/test/plugin_functional/test_suites/core_plugins/application_status.ts +++ b/src/platform/test/plugin_functional/test_suites/core_plugins/application_status.ts @@ -51,7 +51,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide it('can change the visibleIn array at runtime', async () => { await setAppStatus({ - visibleIn: ['classicSideNav', 'solutionSideNav'], + visibleIn: ['classicSideNav', 'projectSideNav'], }); let link = await appsMenu.getLink('App Status'); expect(link).not.to.eql(undefined); diff --git a/x-pack/platform/plugins/shared/agent_builder/public/register.ts b/x-pack/platform/plugins/shared/agent_builder/public/register.ts index 856027239791b..ab355bdadf5ce 100644 --- a/x-pack/platform/plugins/shared/agent_builder/public/register.ts +++ b/x-pack/platform/plugins/shared/agent_builder/public/register.ts @@ -76,7 +76,7 @@ export const registerApp = ({ category: DEFAULT_APP_CATEGORIES.enterpriseSearch, title: AGENT_BUILDER_SHORT_TITLE, euiIconType: 'logoElasticsearch', - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], keywords: ['agent builder', 'ai agent', 'chat agent'], updater$: appUpdater$, deepLinks: buildAgentBuilderDeepLinks(false), diff --git a/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts b/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts index 967686408d08c..7600ccf18d2e9 100644 --- a/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts +++ b/x-pack/platform/plugins/shared/fleet/public/deep_links.test.ts @@ -49,13 +49,10 @@ const authzGatedIds = [ ]; describe('getFleetDeepLinks', () => { - it('authz-gated links include solutionSideNav when authorized', () => { + it('authz-gated links include projectSideNav when authorized', () => { const links = getFleetDeepLinks(allowedExperimentalValues, authorizedAuthz); for (const id of authzGatedIds) { - expect(links.find((l) => l.id === id)?.visibleIn).toEqual([ - 'globalSearch', - 'solutionSideNav', - ]); + expect(links.find((l) => l.id === id)?.visibleIn).toEqual(['globalSearch', 'projectSideNav']); } }); }); diff --git a/x-pack/platform/plugins/shared/fleet/public/deep_links.ts b/x-pack/platform/plugins/shared/fleet/public/deep_links.ts index 71014481ace8f..c931ce7767ad1 100644 --- a/x-pack/platform/plugins/shared/fleet/public/deep_links.ts +++ b/x-pack/platform/plugins/shared/fleet/public/deep_links.ts @@ -31,7 +31,7 @@ export const getFleetDeepLinks: ( id: FleetDeepLinkId.agents, title: i18n.translate('xpack.fleet.deepLinks.agents.title', { defaultMessage: 'Agents' }), path: FLEET_ROUTING_PATHS.agents, - visibleIn: !authz?.fleet.readAgents ? [] : ['globalSearch', 'solutionSideNav'], + visibleIn: !authz?.fleet.readAgents ? [] : ['globalSearch', 'projectSideNav'], }, { id: FleetDeepLinkId.policies, @@ -39,7 +39,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Agent policies', }), path: FLEET_ROUTING_PATHS.policies, - visibleIn: !authz?.fleet.readAgentPolicies ? [] : ['globalSearch', 'solutionSideNav'], + visibleIn: !authz?.fleet.readAgentPolicies ? [] : ['globalSearch', 'projectSideNav'], }, { id: FleetDeepLinkId.enrollmentTokens, @@ -47,7 +47,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Enrollment tokens', }), path: FLEET_ROUTING_PATHS.enrollment_tokens, - visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch', 'solutionSideNav'], + visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch', 'projectSideNav'], }, { id: FleetDeepLinkId.uninstallTokens, @@ -55,7 +55,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Uninstall tokens', }), path: FLEET_ROUTING_PATHS.uninstall_tokens, - visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch', 'solutionSideNav'], + visibleIn: !authz?.fleet.allAgents ? [] : ['globalSearch', 'projectSideNav'], }, { id: FleetDeepLinkId.dataStreams, @@ -70,7 +70,7 @@ export const getFleetDeepLinks: ( defaultMessage: 'Settings', }), path: FLEET_ROUTING_PATHS.settings, - visibleIn: !authz?.fleet.readSettings ? [] : ['globalSearch', 'solutionSideNav'], + visibleIn: !authz?.fleet.readSettings ? [] : ['globalSearch', 'projectSideNav'], }, ]; }; diff --git a/x-pack/platform/plugins/shared/inbox/public/plugin.test.ts b/x-pack/platform/plugins/shared/inbox/public/plugin.test.ts index 593f82e669841..78ba917480ac6 100644 --- a/x-pack/platform/plugins/shared/inbox/public/plugin.test.ts +++ b/x-pack/platform/plugins/shared/inbox/public/plugin.test.ts @@ -57,7 +57,7 @@ describe('InboxPublicPlugin', () => { const [registration] = coreSetup.application.register.mock.calls[0]; expect(registration.visibleIn).toEqual( - expect.arrayContaining(['classicSideNav', 'solutionSideNav', 'globalSearch']) + expect.arrayContaining(['classicSideNav', 'projectSideNav', 'globalSearch']) ); }); diff --git a/x-pack/platform/plugins/shared/inbox/public/plugin.tsx b/x-pack/platform/plugins/shared/inbox/public/plugin.tsx index cf3e0ff9425de..4635c4837efcc 100644 --- a/x-pack/platform/plugins/shared/inbox/public/plugin.tsx +++ b/x-pack/platform/plugins/shared/inbox/public/plugin.tsx @@ -66,7 +66,7 @@ export class InboxPublicPlugin category: DEFAULT_APP_CATEGORIES.security, euiIconType: 'email', status: AppStatus.accessible, - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], order: 100, mount: async (params) => { const [coreStart, startDeps] = await coreSetup.getStartServices(); diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/plugin.test.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/plugin.test.tsx index a97d892f1940d..5192cafa847c9 100644 --- a/x-pack/platform/plugins/shared/ingest_hub/public/plugin.test.tsx +++ b/x-pack/platform/plugins/shared/ingest_hub/public/plugin.test.tsx @@ -144,7 +144,7 @@ describe('IngestHubPlugin', () => { const { updater$ } = coreSetup.application.register.mock.calls[0][0]; const updater = await firstValueFrom(updater$!); expect((updater as AppUpdater)!({} as unknown as Parameters[0])).toEqual({ - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], }); }); diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/plugin.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/plugin.tsx index 1e24a13f9b399..f77ac9985c1ea 100644 --- a/x-pack/platform/plugins/shared/ingest_hub/public/plugin.tsx +++ b/x-pack/platform/plugins/shared/ingest_hub/public/plugin.tsx @@ -83,7 +83,7 @@ export class IngestHubPlugin coreStart.featureFlags.getBooleanValue$(INGEST_HUB_ENABLED_FLAG, false).pipe( map((enabled): AppUpdater => { return () => ({ - visibleIn: enabled ? ['classicSideNav', 'solutionSideNav', 'globalSearch'] : [], + visibleIn: enabled ? ['classicSideNav', 'projectSideNav', 'globalSearch'] : [], }); }) ) diff --git a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts index c8e3361a71d5a..a2f008e82591e 100644 --- a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts +++ b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.test.ts @@ -15,28 +15,28 @@ const fullCapabilities: MlCapabilities = { }; describe('getDeepLinks', () => { - it('primary visible nav links include globalSearch and solutionSideNav', () => { + it('primary visible nav links include globalSearch and projectSideNav', () => { const links = getDeepLinks(true, fullCapabilities, false); expect(links.find((l) => l.id === 'overview')?.visibleIn).toEqual([ 'globalSearch', - 'solutionSideNav', + 'projectSideNav', ]); }); - it('file upload / index data visualizer / data drift remain in globalSearch + solutionSideNav', () => { + it('file upload / index data visualizer / data drift remain in globalSearch + projectSideNav', () => { const links = getDeepLinks(true, fullCapabilities, false); expect(links.find((l) => l.id === 'fileUpload')?.visibleIn).toEqual([ 'globalSearch', - 'solutionSideNav', + 'projectSideNav', ]); expect(links.find((l) => l.id === 'indexDataVisualizer')?.visibleIn).toEqual([ 'globalSearch', - 'solutionSideNav', + 'projectSideNav', ]); expect(links.find((l) => l.id === 'dataDrift')?.visibleIn).toEqual([ 'globalSearch', - 'solutionSideNav', + 'projectSideNav', ]); }); diff --git a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts index 724eaef5ab118..6593715d33771 100644 --- a/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts +++ b/x-pack/platform/plugins/shared/ml/public/register_helper/register_search_links/search_deep_links.ts @@ -28,7 +28,7 @@ function createDeepLinks( defaultMessage: 'Overview', }), path: `/${ML_PAGES.OVERVIEW}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }; }, @@ -47,7 +47,7 @@ function createDeepLinks( defaultMessage: 'Anomaly explorer', }), path: `/${ML_PAGES.ANOMALY_EXPLORER}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'singleMetricViewer', @@ -55,7 +55,7 @@ function createDeepLinks( defaultMessage: 'Single metric viewer', }), path: `/${ML_PAGES.SINGLE_METRIC_VIEWER}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'suppliedConfigurations', @@ -85,7 +85,7 @@ function createDeepLinks( defaultMessage: 'Results explorer', }), path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'analyticsMap', @@ -93,7 +93,7 @@ function createDeepLinks( defaultMessage: 'Analytics map', }), path: `/${ML_PAGES.DATA_FRAME_ANALYTICS_MAP}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, ], }; @@ -145,7 +145,7 @@ function createDeepLinks( defaultMessage: 'Log rate analysis', }), path: `/${ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'logRateAnalysisPage', @@ -161,7 +161,7 @@ function createDeepLinks( defaultMessage: 'Log pattern analysis', }), path: `/${ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'logPatternAnalysisPage', @@ -177,7 +177,7 @@ function createDeepLinks( defaultMessage: 'Change point detection', }), path: `/${ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'changePointDetectionsPage', @@ -211,7 +211,7 @@ function createDeepLinks( defaultMessage: 'Data visualizer', }), path: `/${ML_PAGES.DATA_VISUALIZER}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }; }, @@ -223,7 +223,7 @@ function createDeepLinks( }), keywords: ['CSV', 'JSON'], path: `/${ML_PAGES.DATA_VISUALIZER_FILE}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }; }, @@ -234,7 +234,7 @@ function createDeepLinks( defaultMessage: 'Index data visualizer', }), path: `/${ML_PAGES.DATA_VISUALIZER_INDEX_SELECT}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }; }, getIndexDataVisualizerPageDeepLink: (): AppDeepLink => { @@ -266,7 +266,7 @@ function createDeepLinks( defaultMessage: 'Data drift', }), path: `/${ML_PAGES.DATA_DRIFT_INDEX_SELECT}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }; }, getDataDriftPageDeepLink: (): AppDeepLink => { diff --git a/x-pack/platform/plugins/shared/streams_app/public/plugin.tsx b/x-pack/platform/plugins/shared/streams_app/public/plugin.tsx index 68184566c3fda..c3faeec3a7966 100644 --- a/x-pack/platform/plugins/shared/streams_app/public/plugin.tsx +++ b/x-pack/platform/plugins/shared/streams_app/public/plugin.tsx @@ -173,7 +173,7 @@ export class StreamsAppPlugin } return { - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], deepLinks: (app.deepLinks ?? []).map((link) => { if (significantEventsDeepLinkIds.includes(link.id as SigEventsLinkId)) { return { diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts b/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts index 86e7a240a8761..f08649292de95 100644 --- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts +++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/plugin.ts @@ -298,7 +298,7 @@ export class Plugin title: i18n.translate('xpack.triggersActionsUI.rulesPage.title', { defaultMessage: 'Rules', }), - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], category: DEFAULT_APP_CATEGORIES.management, async mount(params: AppMountParameters) { const [coreStart] = (await core.getStartServices()) as [CoreStart, PluginsStart, unknown]; diff --git a/x-pack/solutions/observability/plugins/apm/public/plugin.ts b/x-pack/solutions/observability/plugins/apm/public/plugin.ts index 2b261f433f0cc..1e0548b93b1f9 100644 --- a/x-pack/solutions/observability/plugins/apm/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/apm/public/plugin.ts @@ -464,37 +464,37 @@ export class ApmPlugin implements Plugin { id: 'service-groups-list', title: serviceGroupsTitle, path: '/service-groups', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'services', title: servicesTitle, path: '/services', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'traces', title: tracesTitle, path: '/traces', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'service-map', title: serviceMapTitle, path: '/service-map', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'dependencies', title: dependenciesTitle, path: '/dependencies/inventory', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'settings', title: apmSettingsTitle, path: '/settings', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'storage-explorer', diff --git a/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts b/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts index cd566a014b010..48706c0b2010f 100644 --- a/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts +++ b/x-pack/solutions/observability/plugins/infra/public/pages/logs/routes.ts @@ -26,13 +26,13 @@ export const getLogsAppRoutes = () => { id: 'anomalies', title: logsAnomaliesTitle, path: '/anomalies', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, logsCategories: { id: 'log-categories', title: logCategoriesTitle, path: '/log-categories', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, }; diff --git a/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts b/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts index 57e3a879a16dd..db4a4b78caa49 100644 --- a/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts +++ b/x-pack/solutions/observability/plugins/infra/public/plugin.test.ts @@ -8,16 +8,16 @@ import { getInfraDeepLinks } from './plugin'; describe('getInfraDeepLinks', () => { - it('primary nav links use the shared globalSearch + solutionSideNav value', () => { + it('primary nav links use the shared globalSearch + projectSideNav value', () => { const links = getInfraDeepLinks({ metricsExplorerEnabled: true }); expect(links.find((l) => l.id === 'inventory')?.visibleIn).toEqual([ 'globalSearch', - 'solutionSideNav', + 'projectSideNav', ]); expect(links.find((l) => l.id === 'metrics-explorer')?.visibleIn).toEqual([ 'globalSearch', - 'solutionSideNav', + 'projectSideNav', ]); }); diff --git a/x-pack/solutions/observability/plugins/infra/public/plugin.ts b/x-pack/solutions/observability/plugins/infra/public/plugin.ts index deb0478a0f7ba..406a4b5919faa 100644 --- a/x-pack/solutions/observability/plugins/infra/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/infra/public/plugin.ts @@ -74,7 +74,7 @@ export const getInfraDeepLinks = ({ }: { metricsExplorerEnabled: boolean; }): AppDeepLink[] => { - const visibleIn: AppDeepLinkLocations[] = ['globalSearch', 'solutionSideNav']; + const visibleIn: AppDeepLinkLocations[] = ['globalSearch', 'projectSideNav']; return [ { diff --git a/x-pack/solutions/observability/plugins/observability/public/plugin.ts b/x-pack/solutions/observability/plugins/observability/public/plugin.ts index 83ea7eb0856aa..e4566c7f43be3 100644 --- a/x-pack/solutions/observability/plugins/observability/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/observability/public/plugin.ts @@ -218,7 +218,7 @@ export class Plugin }), order: 8001, path: ALERTS_PATH, - visibleIn: ['solutionSideNav'], + visibleIn: ['projectSideNav'], keywords: ['alerts', 'rules'], }, { @@ -258,13 +258,13 @@ export class Plugin extend: { [CasesDeepLinkId.cases]: { order: 8003, - visibleIn: ['solutionSideNav'], + visibleIn: ['projectSideNav'], }, [CasesDeepLinkId.casesCreate]: { - visibleIn: ['solutionSideNav'], + visibleIn: ['projectSideNav'], }, [CasesDeepLinkId.casesConfigure]: { - visibleIn: ['solutionSideNav'], + visibleIn: ['projectSideNav'], }, }, }) @@ -360,8 +360,8 @@ export class Plugin 'experience', ], visibleIn: Boolean(pluginsSetup.serverless) - ? ['solutionSideNav', 'home', 'kibanaOverview'] - : ['globalSearch', 'classicSideNav', 'solutionSideNav', 'home', 'kibanaOverview'], + ? ['projectSideNav', 'home', 'kibanaOverview'] + : ['globalSearch', 'classicSideNav', 'projectSideNav', 'home', 'kibanaOverview'], }; coreSetup.application.register(app); diff --git a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx index e988045aebb0c..b35d8a138cf20 100644 --- a/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx +++ b/x-pack/solutions/observability/plugins/observability_ai_assistant_app/public/plugin.tsx @@ -67,7 +67,7 @@ export class ObservabilityAIAssistantAppPlugin euiIconType: 'logoObservability', appRoute: '/app/observabilityAIAssistant', category: DEFAULT_APP_CATEGORIES.observability, - visibleIn: ['solutionSideNav'], + visibleIn: ['projectSideNav'], deepLinks: [ { id: 'conversations', diff --git a/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts b/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts index e6db9e2d59c56..a7755c74b463f 100644 --- a/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/observability_onboarding/public/plugin.ts @@ -123,7 +123,7 @@ export class ObservabilityOnboardingPlugin }, }); }, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }); this.locators = { diff --git a/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.test.tsx b/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.test.tsx index a30c606fc4654..0d14eb8a17049 100644 --- a/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.test.tsx +++ b/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.test.tsx @@ -58,7 +58,7 @@ describe('updateGlobalNavigation', () => { expect(callback).toHaveBeenCalledWith({ deepLinks, - visibleIn: ['classicSideNav', 'solutionSideNav', 'home', 'kibanaOverview', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'home', 'kibanaOverview', 'globalSearch'], }); }); @@ -91,16 +91,10 @@ describe('updateGlobalNavigation', () => { deepLinks: [ { ...caseRoute, - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], // visibility set + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], // visibility set }, ], - visibleIn: [ - 'classicSideNav', - 'solutionSideNav', - 'home', - 'kibanaOverview', - 'globalSearch', - ], + visibleIn: ['classicSideNav', 'projectSideNav', 'home', 'kibanaOverview', 'globalSearch'], }); }); }); @@ -132,13 +126,7 @@ describe('updateGlobalNavigation', () => { expect(callback).toHaveBeenCalledWith({ deepLinks: [], // Deeplink has been filtered out - visibleIn: [ - 'classicSideNav', - 'solutionSideNav', - 'home', - 'kibanaOverview', - 'globalSearch', - ], + visibleIn: ['classicSideNav', 'projectSideNav', 'home', 'kibanaOverview', 'globalSearch'], }); }); }); @@ -174,16 +162,10 @@ describe('updateGlobalNavigation', () => { title: 'Alerts', order: 8001, path: '/alerts', - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], }, ], - visibleIn: [ - 'classicSideNav', - 'solutionSideNav', - 'home', - 'kibanaOverview', - 'globalSearch', - ], + visibleIn: ['classicSideNav', 'projectSideNav', 'home', 'kibanaOverview', 'globalSearch'], }); }); @@ -217,16 +199,10 @@ describe('updateGlobalNavigation', () => { title: 'Alerts', order: 8001, path: '/alerts', - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], }, ], - visibleIn: [ - 'classicSideNav', - 'solutionSideNav', - 'home', - 'kibanaOverview', - 'globalSearch', - ], + visibleIn: ['classicSideNav', 'projectSideNav', 'home', 'kibanaOverview', 'globalSearch'], }); }); }); diff --git a/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.tsx b/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.tsx index 81bd95d58fc35..d3050044299b4 100644 --- a/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.tsx +++ b/x-pack/solutions/observability/plugins/observability_shared/public/services/update_global_navigation.tsx @@ -51,7 +51,7 @@ export function updateGlobalNavigation({ if (capabilities[casesFeatureId].read_cases) { return { ...link, - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], }; } return null; @@ -59,7 +59,7 @@ export function updateGlobalNavigation({ if (someVisible) { return { ...link, - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], }; } return null; @@ -67,7 +67,7 @@ export function updateGlobalNavigation({ if (someVisible) { return { ...link, - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], }; } return null; @@ -79,7 +79,7 @@ export function updateGlobalNavigation({ updater$.next(() => { const visibleIn: AppDeepLinkLocations[] = someVisible - ? ['classicSideNav', 'solutionSideNav', 'home', 'kibanaOverview'] + ? ['classicSideNav', 'projectSideNav', 'home', 'kibanaOverview'] : []; if (isCompleteOverviewEnabled && someVisible) { diff --git a/x-pack/solutions/observability/plugins/profiling/public/plugin.ts b/x-pack/solutions/observability/plugins/profiling/public/plugin.ts index 93127b87d56ef..3ebba625cef90 100644 --- a/x-pack/solutions/observability/plugins/profiling/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/profiling/public/plugin.ts @@ -48,7 +48,7 @@ export class ProfilingPlugin defaultMessage: 'Stacktraces', }), path: '/stacktraces', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'flamegraphs', @@ -56,7 +56,7 @@ export class ProfilingPlugin defaultMessage: 'Flamegraphs', }), path: '/flamegraphs', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'functions', @@ -64,7 +64,7 @@ export class ProfilingPlugin defaultMessage: 'Functions', }), path: '/functions', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, ] satisfies AppDeepLink[]; diff --git a/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts b/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts index 82d0afb8f2cf3..a2c868d9c977a 100644 --- a/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/synthetics/public/plugin.ts @@ -213,7 +213,7 @@ export class SyntheticsPlugin defaultMessage: 'Monitors', }), path: '/', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'certificates', @@ -221,7 +221,7 @@ export class SyntheticsPlugin defaultMessage: 'TLS Certificates', }), path: '/certificates', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, ], mount: async (params: AppMountParameters) => { diff --git a/x-pack/solutions/observability/plugins/uptime/public/plugin.ts b/x-pack/solutions/observability/plugins/uptime/public/plugin.ts index 6417c506cd616..8be398bc6097a 100644 --- a/x-pack/solutions/observability/plugins/uptime/public/plugin.ts +++ b/x-pack/solutions/observability/plugins/uptime/public/plugin.ts @@ -210,7 +210,7 @@ export class UptimePlugin id: 'Certificates', title: 'TLS Certificates', path: '/certificates', - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: 'Settings', title: 'Settings', path: '/settings' }, ], diff --git a/x-pack/solutions/search/plugins/search_getting_started/public/plugin.ts b/x-pack/solutions/search/plugins/search_getting_started/public/plugin.ts index d482c7b699e1a..9e9bebe973324 100644 --- a/x-pack/solutions/search/plugins/search_getting_started/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_getting_started/public/plugin.ts @@ -67,7 +67,7 @@ export class SearchGettingStartedPlugin return renderApp(coreStart, services, element, queryClient, kibanaVersion); }, order: 1, - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }); return {}; diff --git a/x-pack/solutions/search/plugins/search_homepage/public/plugin.ts b/x-pack/solutions/search/plugins/search_homepage/public/plugin.ts index f5fad31f419ec..f861e5d6b7b07 100644 --- a/x-pack/solutions/search/plugins/search_homepage/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_homepage/public/plugin.ts @@ -70,7 +70,7 @@ export class SearchHomepagePlugin return renderApp(coreStart, startDeps, element, queryClient, kibanaVersion); }, order: 0, - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }); return result; diff --git a/x-pack/solutions/search/plugins/search_playground/public/plugin.ts b/x-pack/solutions/search/plugins/search_playground/public/plugin.ts index 2a27d96ebb14c..db9cf598eba03 100644 --- a/x-pack/solutions/search/plugins/search_playground/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_playground/public/plugin.ts @@ -67,7 +67,7 @@ export class SearchPlaygroundPlugin return renderApp(coreStart, startDeps, element); }, - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], order: 3, }); diff --git a/x-pack/solutions/search/plugins/search_query_rules/public/plugin.ts b/x-pack/solutions/search/plugins/search_query_rules/public/plugin.ts index a65444ecf5c96..8af8f89dbe4a5 100644 --- a/x-pack/solutions/search/plugins/search_query_rules/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_query_rules/public/plugin.ts @@ -51,7 +51,7 @@ export class QueryRulesPlugin return renderApp(coreStart, startDeps, element); }, order: 5, - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }); return {}; diff --git a/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts b/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts index cf3e442788a90..359702ef2d925 100644 --- a/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts +++ b/x-pack/solutions/search/plugins/search_synonyms/public/plugin.ts @@ -40,7 +40,7 @@ export class SearchSynonymsPlugin id: 'synonyms', path: '/', title: PLUGIN_TITLE, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, ], async mount({ element, history }: AppMountParameters) { @@ -59,7 +59,7 @@ export class SearchSynonymsPlugin return renderApp(coreStart, startDeps, element); }, order: 4, - visibleIn: ['classicSideNav', 'solutionSideNav'], + visibleIn: ['classicSideNav', 'projectSideNav'], }); return {}; diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.test.ts b/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.test.ts index bb31a97ff514d..5dde642c8879f 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.test.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.test.ts @@ -37,7 +37,7 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, ]); }); @@ -59,7 +59,7 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, ]); }); @@ -82,12 +82,12 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, ]); }); - it('should include unavailable links with solutionSideNav visibility only', () => { + it('should include unavailable links with projectSideNav visibility only', () => { const id = 'page-1' as SecurityPageName; const id2 = 'page-2' as SecurityPageName; const tree: NavigationTreeDefinition = { @@ -105,13 +105,13 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, { id: id2, path: `/path/${id2}`, title: `Title for ${id2}`, - visibleIn: ['solutionSideNav'], + visibleIn: ['projectSideNav'], }, ]); }); @@ -137,7 +137,7 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, ]); }); @@ -164,7 +164,7 @@ describe('solutionFormatter', () => { id, path: `/path/${id}`, title: `Title for ${id}`, - visibleIn: ['globalSearch', 'solutionSideNav'], + visibleIn: ['globalSearch', 'projectSideNav'], }, ]); }); diff --git a/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.ts b/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.ts index ecd1824d65925..07856c759c8f2 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/app/links/deep_links.ts @@ -94,7 +94,7 @@ const solutionNodesFormatter = ( if (appLink && !appLink.unauthorized) { const deepLink = formatDeepLink(appLink); if (appLink.unavailable) { - deepLink.visibleIn = ['solutionSideNav']; // Links marked as unavailable have an upselling page to display, show only in solutionSideNav + deepLink.visibleIn = ['projectSideNav']; // Links marked as unavailable have an upselling page to display, show only in projectSideNav } if (node.children) { const childrenLinks = solutionNodesFormatter(node.children, normalizedLinks); @@ -121,7 +121,7 @@ const formatDeepLink = (appLink: LinkItem): AppDeepLink => { visibleIn.add('globalSearch'); } if (!appLink.sideNavDisabled) { - visibleIn.add('solutionSideNav'); + visibleIn.add('projectSideNav'); } const deepLink: AppDeepLink = { id: appLink.id, diff --git a/x-pack/solutions/security/plugins/security_solution/public/onboarding/links.ts b/x-pack/solutions/security/plugins/security_solution/public/onboarding/links.ts index 105c3adb9cd2c..21a895b747ec4 100644 --- a/x-pack/solutions/security/plugins/security_solution/public/onboarding/links.ts +++ b/x-pack/solutions/security/plugins/security_solution/public/onboarding/links.ts @@ -66,7 +66,7 @@ export const launchPadLinks: LinkItem = { sideNavFooter: true, skipUrlState: true, hideTimeline: true, - visibleIn: ['globalSearch', 'classicSideNav', 'solutionSideNav'], + visibleIn: ['globalSearch', 'classicSideNav', 'projectSideNav'], }; /** diff --git a/x-pack/solutions/workplaceai/plugins/workplace_ai_app/public/application/register.ts b/x-pack/solutions/workplaceai/plugins/workplace_ai_app/public/application/register.ts index 2b506d644f5a6..889d99107df9c 100644 --- a/x-pack/solutions/workplaceai/plugins/workplace_ai_app/public/application/register.ts +++ b/x-pack/solutions/workplaceai/plugins/workplace_ai_app/public/application/register.ts @@ -28,7 +28,7 @@ export const registerApp = ({ defaultMessage: 'Workplace AI', }), updater$: undefined, - visibleIn: ['classicSideNav', 'solutionSideNav', 'globalSearch'], + visibleIn: ['classicSideNav', 'projectSideNav', 'globalSearch'], async mount({ element, history }) { const [coreStart, startPluginDeps] = await core.getStartServices(); const services = getServices(); From 3cc553ed1f6b43b47d2cbe95b60a8c37487cb004 Mon Sep 17 00:00:00 2001 From: "paulina.shakirova" Date: Fri, 29 May 2026 12:17:14 +0200 Subject: [PATCH 8/8] Update ingestHub asset size limit in kbn-optimizer configuration from 13422 to 14788 --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index f47843b187235..a3c8547f1f174 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -92,7 +92,7 @@ pageLoadAssetSize: indexManagement: 39694 inference: 10368 infra: 56302 - ingestHub: 13422 + ingestHub: 14788 ingestPipelines: 17866 inputControlVis: 7638 inspectComponent: 4590