[SharedUxChromeNavigation] Use deeplink id instead of href (#159125)

This commit is contained in:
Sébastien Loix 2023-06-13 18:10:10 +01:00 committed by GitHub
parent b1128ea67b
commit fb41ca56f9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
125 changed files with 2598 additions and 1627 deletions

10
.github/CODEOWNERS vendored
View file

@ -307,6 +307,16 @@ src/plugins/data_view_management @elastic/kibana-data-discovery
src/plugins/data_views @elastic/kibana-data-discovery
x-pack/plugins/data_visualizer @elastic/ml-ui
packages/kbn-datemath @elastic/kibana-data-discovery
packages/deeplinks/analytics @elastic/kibana-data-discovery
packages/deeplinks/devtools @elastic/platform-deployment-management
packages/deeplinks/management @elastic/platform-deployment-management @elastic/kibana-data-discovery
packages/deeplinks/ml @elastic/ml-ui
packages/deeplinks/observability @elastic/apm-ui
packages/deeplinks/search @elastic/enterprise-search-frontend
packages/default-nav/analytics @elastic/kibana-data-discovery
packages/default-nav/devtools @elastic/platform-deployment-management
packages/default-nav/management @elastic/platform-deployment-management @elastic/kibana-data-discovery
packages/default-nav/ml @elastic/ml-ui
packages/kbn-dev-cli-errors @elastic/kibana-operations
packages/kbn-dev-cli-runner @elastic/kibana-operations
packages/kbn-dev-proc-runner @elastic/kibana-operations

View file

@ -19,6 +19,7 @@
"data": "src/plugins/data",
"observabilityAlertDetails": "x-pack/packages/observability/alert_details",
"dataViews": "src/plugins/data_views",
"defaultNavigation": "packages/default-nav",
"devTools": "src/plugins/dev_tools",
"discover": "src/plugins/discover",
"savedSearch": "src/plugins/saved_search",

View file

@ -353,6 +353,16 @@
"@kbn/data-views-plugin": "link:src/plugins/data_views",
"@kbn/data-visualizer-plugin": "link:x-pack/plugins/data_visualizer",
"@kbn/datemath": "link:packages/kbn-datemath",
"@kbn/deeplinks-analytics": "link:packages/deeplinks/analytics",
"@kbn/deeplinks-devtools": "link:packages/deeplinks/devtools",
"@kbn/deeplinks-management": "link:packages/deeplinks/management",
"@kbn/deeplinks-ml": "link:packages/deeplinks/ml",
"@kbn/deeplinks-observability": "link:packages/deeplinks/observability",
"@kbn/deeplinks-search": "link:packages/deeplinks/search",
"@kbn/default-nav-analytics": "link:packages/default-nav/analytics",
"@kbn/default-nav-devtools": "link:packages/default-nav/devtools",
"@kbn/default-nav-management": "link:packages/default-nav/management",
"@kbn/default-nav-ml": "link:packages/default-nav/ml",
"@kbn/dev-tools-plugin": "link:src/plugins/dev_tools",
"@kbn/developer-examples-plugin": "link:examples/developer_examples",
"@kbn/discover-enhanced-plugin": "link:x-pack/plugins/discover_enhanced",

View file

@ -286,9 +286,9 @@ export type PublicAppDeepLinkInfo = Omit<
* user-accessible.
* @public
*/
export type AppDeepLink = {
export type AppDeepLink<Id extends string = string> = {
/** Identifier to represent this sublink, should be unique for this application */
id: string;
id: Id;
/** Title to label represent this deep link */
title: string;
/** Optional keywords to match with in deep links search. Omit if this part of the hierarchy does not have a page URL. */
@ -303,13 +303,13 @@ export type AppDeepLink = {
/** URL path to access this link, relative to the application's appRoute. */
path: string;
/** Optional array of links that are 'underneath' this section in the hierarchy */
deepLinks?: AppDeepLink[];
deepLinks?: Array<AppDeepLink<Id>>;
}
| {
/** Optional path to access this section. Omit if this part of the hierarchy does not have a page URL. */
path?: string;
/** Array links that are 'underneath' this section in this hierarchy. */
deepLinks: AppDeepLink[];
deepLinks: Array<AppDeepLink<Id>>;
}
);

View file

@ -7,6 +7,8 @@
*/
export type {
AppDeepLinkId,
AppId,
ChromeBadge,
ChromeBreadcrumb,
ChromeBreadcrumbsAppendExtension,
@ -29,11 +31,12 @@ export type {
ChromeStart,
ChromeStyle,
ChromeUserBanner,
ChromeProjectNavigationLink,
ChromeProjectNavigation,
ChromeProjectNavigationNode,
SideNavCompProps,
SideNavComponent,
ChromeProjectBreadcrumb,
ChromeSetProjectBreadcrumbsParams,
NodeDefinition,
NodeDefinitionWithChildren,
} from './src';

View file

@ -31,9 +31,12 @@ export type { ChromeBadge, ChromeUserBanner, ChromeStyle } from './types';
export type {
ChromeProjectNavigation,
ChromeProjectNavigationNode,
ChromeProjectNavigationLink,
AppDeepLinkId,
AppId,
SideNavCompProps,
SideNavComponent,
ChromeSetProjectBreadcrumbsParams,
ChromeProjectBreadcrumb,
NodeDefinition,
NodeDefinitionWithChildren,
} from './project_navigation';

View file

@ -5,26 +5,43 @@
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { ComponentType } from 'react';
import type { AppId as DevToolsApp, DeepLinkId as DevToolsLink } from '@kbn/deeplinks-devtools';
import type {
AppId as AnalyticsApp,
DeepLinkId as AnalyticsDeepLink,
} from '@kbn/deeplinks-analytics';
import type { AppId as MlApp, DeepLinkId as MlLink } from '@kbn/deeplinks-ml';
import type {
AppId as ManagementApp,
DeepLinkId as ManagementLink,
} from '@kbn/deeplinks-management';
import type { AppId as SearchApp, DeepLinkId as SearchLink } from '@kbn/deeplinks-search';
import type {
AppId as ObservabilityApp,
DeepLinkId as ObservabilityLink,
} from '@kbn/deeplinks-observability';
import type { ChromeBreadcrumb } from './breadcrumb';
import type { ChromeNavLink } from './nav_links';
/** @internal */
type AppId = string;
/** @public */
export type AppId =
| DevToolsApp
| AnalyticsApp
| MlApp
| ManagementApp
| SearchApp
| ObservabilityApp;
/** @internal */
type DeepLinkId = string;
/** @internal */
export type AppDeepLinkId = `${AppId}:${DeepLinkId}`;
/**
* @public
*
* App id or deeplink id
*/
export type ChromeProjectNavigationLink = AppId | AppDeepLinkId;
/** @public */
export type AppDeepLinkId =
| AnalyticsDeepLink
| DevToolsLink
| MlLink
| ManagementLink
| SearchLink
| ObservabilityLink;
/** @public */
export interface ChromeProjectNavigationNode {
@ -77,3 +94,48 @@ export type ChromeProjectBreadcrumb = ChromeBreadcrumb;
export interface ChromeSetProjectBreadcrumbsParams {
absolute: boolean;
}
type NonEmptyArray<T> = [T, ...T[]];
/**
* @public
*
* A navigation node definition with its unique id, title, path in the tree and optional
* deep link and children.
* This definition serves to build the full ChromeProjectNavigation.navigationTree, converting
* "link" to "deepLink" and adding the "path" property for each node.
*/
export interface NodeDefinition<
LinkId extends AppDeepLinkId = AppDeepLinkId,
Id extends string = string,
ChildrenId extends string = Id
> {
/** Optional id, if not passed a "link" must be provided. */
id?: Id;
/** Optional title. If not provided and a "link" is provided the title will be the Deep link title */
title?: string;
/** App id or deeplink id */
link?: LinkId;
/** Optional icon for the navigation node. Note: not all navigation depth will render the icon */
icon?: string;
/** Optional children of the navigation node */
children?: NonEmptyArray<NodeDefinition<LinkId, Id, ChildrenId>>;
/**
* Use href for absolute links only. Internal links should use "link".
*/
href?: string;
}
/**
* @public
*
* A navigation node definition with its unique id, title, path in the tree and optional
* deep link and children.
*/
export type NodeDefinitionWithChildren<
LinkId extends AppDeepLinkId = AppDeepLinkId,
Id extends string = string,
ChildrenID extends string = Id
> = NodeDefinition<LinkId, Id, ChildrenID> & {
children: Required<NodeDefinition<LinkId, Id, ChildrenID>>['children'];
};

View file

@ -13,7 +13,13 @@
],
"kbn_references": [
"@kbn/core-mount-utils-browser",
"@kbn/core-application-common"
"@kbn/core-application-common",
"@kbn/deeplinks-devtools",
"@kbn/deeplinks-analytics",
"@kbn/deeplinks-ml",
"@kbn/deeplinks-management",
"@kbn/deeplinks-search",
"@kbn/deeplinks-observability"
],
"exclude": [
"target/**/*",

View file

@ -0,0 +1,3 @@
# @kbn/deeplinks-analytics
Empty package generated by @kbn/generate

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export const DISCOVER_APP_ID = 'discover';
export const DASHBOARD_APP_ID = 'dashboards';
export const VISUALIZE_APP_ID = 'visualize';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { DASHBOARD_APP_ID, DISCOVER_APP_ID, VISUALIZE_APP_ID } from './constants';
export type AppId = typeof DISCOVER_APP_ID | typeof DASHBOARD_APP_ID | typeof VISUALIZE_APP_ID;
export type DeepLinkId = AppId;

View file

@ -0,0 +1,11 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export { DASHBOARD_APP_ID, DISCOVER_APP_ID, VISUALIZE_APP_ID } from './constants';
export type { AppId, DeepLinkId } from './deep_links';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/packages/deeplinks/analytics'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/deeplinks-analytics",
"owner": "@elastic/kibana-data-discovery"
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/deeplinks-analytics",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,20 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": [
]
}

View file

@ -0,0 +1,3 @@
# @kbn/deeplinks-devtools
Empty package generated by @kbn/generate

View file

@ -0,0 +1,12 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import type { LinkId } from './deep_links';
export const DEV_TOOLS_APP_ID = 'dev_tools';
export const deepLinkIds: LinkId[] = ['searchprofiler', 'painless_lab', 'grokdebugger', 'console'];

View file

@ -0,0 +1,15 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { DEV_TOOLS_APP_ID } from './constants';
export type AppId = typeof DEV_TOOLS_APP_ID;
export type LinkId = 'searchprofiler' | 'painless_lab' | 'grokdebugger' | 'console';
export type DeepLinkId = AppId | `${AppId}:${LinkId}`;

View file

@ -0,0 +1,11 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export { DEV_TOOLS_APP_ID, deepLinkIds } from './constants';
export type { AppId, LinkId, DeepLinkId } from './deep_links';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/packages/deeplinks/devtools'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/deeplinks-devtools",
"owner": "@elastic/platform-deployment-management"
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/deeplinks-devtools",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,20 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": [
]
}

View file

@ -0,0 +1,3 @@
# @kbn/deeplinks-management
Empty package generated by @kbn/generate

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export const MONITORING_APP_ID = 'monitoring';
export const INTEGRATIONS_APP_ID = 'integrations';
export const FLEET_APP_ID = 'fleet';
export const OSQUERY_APP_ID = 'osquery';
export const MANAGEMENT_APP_ID = 'management';

View file

@ -0,0 +1,57 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import {
MONITORING_APP_ID,
INTEGRATIONS_APP_ID,
FLEET_APP_ID,
OSQUERY_APP_ID,
MANAGEMENT_APP_ID,
} from './constants';
// Monitoring
export type MonitoringAppId = typeof MONITORING_APP_ID;
export type MonitoringDeepLinkId = MonitoringAppId;
// Integrations
export type IntegrationsAppId = typeof INTEGRATIONS_APP_ID;
export type FleetAppId = typeof FLEET_APP_ID;
export type OsQueryAppId = typeof OSQUERY_APP_ID;
export type IntegrationsDeepLinkId = IntegrationsAppId | FleetAppId | OsQueryAppId;
// Management
export type ManagementAppId = typeof MANAGEMENT_APP_ID;
export type ManagementId =
| 'api_keys'
| 'cases'
| 'cross_cluster_replication'
| 'dataViews'
| 'index_lifecycle_management'
| 'index_management'
| 'ingest_pipelines'
| 'jobsListLink'
| 'objects'
| 'pipelines'
| 'remote_clusters'
| 'reporting'
| 'rollup_jobs'
| 'settings'
| 'snapshot_restore'
| 'spaces'
| 'tags'
| 'transform'
| 'triggersActions'
| 'triggersActionsConnectors'
| 'watcher';
export type ManagementDeepLinkId = MonitoringAppId | `${ManagementAppId}:${ManagementId}`;
// Combined
export type AppId = MonitoringAppId | IntegrationsAppId | ManagementAppId;
export type LinkId = ManagementId;
export type DeepLinkId = MonitoringDeepLinkId | IntegrationsDeepLinkId | ManagementDeepLinkId;

View file

@ -0,0 +1,11 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export { MONITORING_APP_ID } from './constants';
export type { AppId, LinkId, DeepLinkId } from './deep_links';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/packages/deeplinks/management'],
};

View file

@ -0,0 +1,8 @@
{
"type": "shared-common",
"id": "@kbn/deeplinks-management",
"owner": [
"@elastic/platform-deployment-management",
"@elastic/kibana-data-discovery"
]
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/deeplinks-management",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,19 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": []
}

View file

@ -0,0 +1,3 @@
# @kbn/deeplinks-ml
Empty package generated by @kbn/generate

View file

@ -0,0 +1,38 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export const ML_APP_ID = 'ml';
export type AppId = typeof ML_APP_ID;
export type LinkId =
| 'overview'
| 'anomalyDetection'
| 'anomalyExplorer'
| 'singleMetricViewer'
| 'dataFrameAnalytics'
| 'resultExplorer'
| 'analyticsMap'
| 'aiOps'
| 'explainLogRateSpikes'
| 'logPatternAnalysis'
| 'changePointDetections'
| 'modelManagement'
| 'nodesOverview'
| 'nodes'
| 'memoryUsage'
| 'dataVisualizer'
| 'fileUpload'
| 'indexDataVisualizer'
| 'settings'
| 'calendarSettings'
| 'calendarSettings'
| 'filterListsSettings'
| 'notifications';
export type DeepLinkId = AppId | `${AppId}:${LinkId}`;

View file

@ -0,0 +1,9 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export type { LinkId, AppId, DeepLinkId } from './deep_links';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/packages/deeplinks/ml'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/deeplinks-ml",
"owner": "@elastic/ml-ui"
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/deeplinks-ml",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,19 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": []
}

View file

@ -0,0 +1,3 @@
# @kbn/deeplinks-observability
Empty package generated by @kbn/generate

View file

@ -0,0 +1,17 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export const LOGS_APP_ID = 'logs';
export const OBSERVABILITY_OVERVIEW_APP_ID = 'observability-overview';
export const METRICS_APP_ID = 'metrics';
export const APM_APP_ID = 'apm';
export const OBSERVABILITY_ONBOARDING_APP_ID = 'observabilityOnboarding';

View file

@ -0,0 +1,58 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import {
LOGS_APP_ID,
OBSERVABILITY_OVERVIEW_APP_ID,
METRICS_APP_ID,
APM_APP_ID,
OBSERVABILITY_ONBOARDING_APP_ID,
} from './constants';
type LogsApp = typeof LOGS_APP_ID;
type ObservabilityOverviewApp = typeof OBSERVABILITY_OVERVIEW_APP_ID;
type MetricsApp = typeof METRICS_APP_ID;
type ApmApp = typeof APM_APP_ID;
type ObservabilityOnboardingApp = typeof OBSERVABILITY_ONBOARDING_APP_ID;
export type AppId =
| LogsApp
| ObservabilityOverviewApp
| ObservabilityOnboardingApp
| ApmApp
| MetricsApp;
export type LogsLinkId = 'log-categories' | 'settings' | 'anomalies' | 'stream';
export type ObservabilityOverviewLinkId =
| 'alerts'
| 'cases'
| 'cases_configure'
| 'cases_create'
| 'rules'
| 'slos';
export type MetricsLinkId = 'inventory' | 'metrics-explorer' | 'metrics-hosts' | 'settings';
export type ApmLinkId =
| 'services'
| 'traces'
| 'service-groups-list'
| 'service-map'
| 'dependencies'
| 'settings'
| 'storage-explorer';
export type LinkId = LogsLinkId | ObservabilityOverviewLinkId | MetricsLinkId | ApmLinkId;
export type DeepLinkId =
| AppId
| `${LogsApp}:${LogsLinkId}`
| `${ObservabilityOverviewApp}:${ObservabilityOverviewLinkId}`
| `${MetricsApp}:${MetricsLinkId}`
| `${ApmApp}:${ApmLinkId}`;

View file

@ -0,0 +1,11 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export { OBSERVABILITY_ONBOARDING_APP_ID } from './constants';
export type { AppId, DeepLinkId } from './deep_links';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/packages/deeplinks/observability'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/deeplinks-observability",
"owner": "@elastic/apm-ui"
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/deeplinks-observability",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,19 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": []
}

View file

@ -0,0 +1,3 @@
# @kbn/deeplinks-search
Empty package generated by @kbn/generate

View file

@ -0,0 +1,9 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export const SERVERLESS_ES_APP_ID = 'serverlessElasticsearch';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { SERVERLESS_ES_APP_ID } from './constants';
export type AppId = typeof SERVERLESS_ES_APP_ID;
export type DeepLinkId = AppId;

View file

@ -0,0 +1,11 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export { SERVERLESS_ES_APP_ID } from './constants';
export type { AppId, DeepLinkId } from './deep_links';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/packages/deeplinks/search'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/deeplinks-search",
"owner": "@elastic/enterprise-search-frontend"
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/deeplinks-search",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,19 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": []
}

View file

@ -0,0 +1,3 @@
# @kbn/default-nav-analytics
Empty package generated by @kbn/generate

View file

@ -0,0 +1,38 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { i18n } from '@kbn/i18n';
import type { NodeDefinitionWithChildren } from '@kbn/core-chrome-browser';
import type { DeepLinkId } from '@kbn/deeplinks-analytics';
export type NavigationID = 'rootNav:analytics' | 'root';
export type AnalyticsNodeDefinition = NodeDefinitionWithChildren<DeepLinkId, NavigationID>;
export const defaultNavigation: AnalyticsNodeDefinition = {
id: 'rootNav:analytics',
title: i18n.translate('defaultNavigation.analytics.dataExploration', {
defaultMessage: 'Data exploration',
}),
icon: 'stats',
children: [
{
id: 'root',
children: [
{
link: 'discover',
},
{
link: 'dashboards',
},
{
link: 'visualize',
},
],
},
],
};

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export {
defaultNavigation,
type AnalyticsNodeDefinition,
type NavigationID,
} from './default_navigation';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/packages/default-nav/analytics'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/default-nav-analytics",
"owner": "@elastic/kibana-data-discovery"
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/default-nav-analytics",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,23 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": [
"@kbn/i18n",
"@kbn/core-chrome-browser",
"@kbn/deeplinks-analytics",
]
}

View file

@ -0,0 +1,3 @@
# @kbn/default-nav-devtools
Empty package generated by @kbn/generate

View file

@ -0,0 +1,41 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { i18n } from '@kbn/i18n';
import type { NodeDefinitionWithChildren } from '@kbn/core-chrome-browser';
import type { DeepLinkId } from '@kbn/deeplinks-devtools';
export type NavigationID = 'rootNav:devtools' | 'root';
export type DevToolsNodeDefinition = NodeDefinitionWithChildren<DeepLinkId, NavigationID>;
export const defaultNavigation: DevToolsNodeDefinition = {
title: i18n.translate('defaultNavigation.devTools.developerTools', {
defaultMessage: 'Developer tools',
}),
id: 'rootNav:devtools',
icon: 'editorCodeBlock',
children: [
{
id: 'root',
children: [
{
link: 'dev_tools:console',
},
{
link: 'dev_tools:searchprofiler',
},
{
link: 'dev_tools:grokdebugger',
},
{
link: 'dev_tools:painless_lab',
},
],
},
],
};

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export {
defaultNavigation,
type DevToolsNodeDefinition,
type NavigationID,
} from './default_navigation';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/packages/default-nav/devtools'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/default-nav-devtools",
"owner": "@elastic/platform-deployment-management"
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/default-nav-devtools",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,23 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": [
"@kbn/i18n",
"@kbn/core-chrome-browser",
"@kbn/deeplinks-devtools",
]
}

View file

@ -0,0 +1,3 @@
# @kbn/default-nav-management
Empty package generated by @kbn/generate

View file

@ -0,0 +1,140 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { i18n } from '@kbn/i18n';
import type { NodeDefinitionWithChildren } from '@kbn/core-chrome-browser';
import type { DeepLinkId } from '@kbn/deeplinks-management';
export type NavigationID =
| 'rootNav:management'
| 'root'
| 'integration_management'
| 'stack_management'
| 'ingest'
| 'data'
| 'alerts_and_insights'
| 'kibana';
export type ManagementNodeDefinition = NodeDefinitionWithChildren<DeepLinkId, NavigationID>;
export const defaultNavigation: ManagementNodeDefinition = {
id: 'rootNav:management',
title: i18n.translate('defaultNavigation.management.sectionLabel', {
defaultMessage: 'Management',
}),
icon: 'gear',
children: [
{
id: 'root',
title: '',
children: [
{
link: 'monitoring',
},
],
},
{
id: 'integration_management',
title: i18n.translate('defaultNavigation.management.integrationManagement', {
defaultMessage: 'Integration management',
}),
children: [
{
link: 'integrations',
},
{
link: 'fleet',
},
{
link: 'osquery',
},
],
},
{
id: 'stack_management',
title: i18n.translate('defaultNavigation.management.stackManagement', {
defaultMessage: 'Stack management',
}),
children: [
{
id: 'ingest',
title: i18n.translate('defaultNavigation.management.ingest', {
defaultMessage: 'Ingest',
}),
children: [
{
link: 'management:ingest_pipelines',
},
{
link: 'management:pipelines',
},
],
},
{
id: 'data',
title: i18n.translate('defaultNavigation.management.stackManagementData', {
defaultMessage: 'Data',
}),
children: [
{
link: 'management:index_management',
},
{
link: 'management:transform',
},
],
},
{
id: 'alerts_and_insights',
title: i18n.translate('defaultNavigation.management.alertAndInsights', {
defaultMessage: 'Alerts and insights',
}),
children: [
{
// Rules
link: 'management:triggersActions',
},
{
link: 'management:cases',
},
{
// Connectors
link: 'management:triggersActionsConnectors',
},
{
// Machine Learning
link: 'management:jobsListLink',
},
],
},
{
id: 'kibana',
title: 'Kibana',
children: [
{
link: 'management:dataViews',
},
{
// Saved objects
link: 'management:objects',
},
{
link: 'management:tags',
},
{
link: 'management:spaces',
},
{
// Advanced settings
link: 'management:settings',
},
],
},
],
},
],
};

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export {
defaultNavigation,
type ManagementNodeDefinition,
type NavigationID,
} from './default_navigation';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/packages/default-nav/management'],
};

View file

@ -0,0 +1,8 @@
{
"type": "shared-common",
"id": "@kbn/default-nav-management",
"owner": [
"@elastic/platform-deployment-management",
"@elastic/kibana-data-discovery"
]
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/default-nav-management",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,23 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": [
"@kbn/i18n",
"@kbn/core-chrome-browser",
"@kbn/deeplinks-management",
]
}

View file

@ -0,0 +1,3 @@
# @kbn/default-nav-ml
Empty package generated by @kbn/generate

View file

@ -0,0 +1,135 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { i18n } from '@kbn/i18n';
import type { NodeDefinitionWithChildren } from '@kbn/core-chrome-browser';
import type { DeepLinkId } from '@kbn/deeplinks-ml';
export type NavigationID =
| 'rootNav:ml'
| 'root'
| 'anomaly_detection'
| 'data_frame_analytics'
| 'model_management'
| 'data_visualizer'
| 'aiops_labs';
export type MlNodeDefinition = NodeDefinitionWithChildren<DeepLinkId, NavigationID>;
export const defaultNavigation: MlNodeDefinition = {
id: 'rootNav:ml',
title: i18n.translate('defaultNavigation.ml.machineLearning', {
defaultMessage: 'Machine learning',
}),
icon: 'indexMapping',
children: [
{
title: '',
id: 'root',
children: [
{
link: 'ml:overview',
},
{
link: 'ml:notifications',
},
],
},
{
title: i18n.translate('defaultNavigation.ml.anomalyDetection', {
defaultMessage: 'Anomaly Detection',
}),
id: 'anomaly_detection',
children: [
{
title: i18n.translate('defaultNavigation.ml.jobs', {
defaultMessage: 'Jobs',
}),
link: 'ml:anomalyDetection',
},
{
link: 'ml:anomalyExplorer',
},
{
link: 'ml:singleMetricViewer',
},
{
link: 'ml:settings',
},
],
},
{
id: 'data_frame_analytics',
title: i18n.translate('defaultNavigation.ml.dataFrameAnalytics', {
defaultMessage: 'Data frame analytics',
}),
children: [
{
title: 'Jobs',
link: 'ml:dataFrameAnalytics',
},
{
link: 'ml:resultExplorer',
},
{
link: 'ml:analyticsMap',
},
],
},
{
id: 'model_management',
title: i18n.translate('defaultNavigation.ml.modelManagement', {
defaultMessage: 'Model management',
}),
children: [
{
link: 'ml:nodesOverview',
},
{
link: 'ml:nodes',
},
],
},
{
id: 'data_visualizer',
title: i18n.translate('defaultNavigation.ml.dataVisualizer', {
defaultMessage: 'Data visualizer',
}),
children: [
{
title: i18n.translate('defaultNavigation.ml.file', {
defaultMessage: 'File',
}),
link: 'ml:fileUpload',
},
{
title: i18n.translate('defaultNavigation.ml.dataView', {
defaultMessage: 'Data view',
}),
link: 'ml:indexDataVisualizer',
},
],
},
{
id: 'aiops_labs',
title: i18n.translate('defaultNavigation.ml.aiopsLabs', {
defaultMessage: 'AIOps labs',
}),
children: [
{
title: i18n.translate('defaultNavigation.ml.explainLogRateSpikes', {
defaultMessage: 'Explain log rate spikes',
}),
link: 'ml:explainLogRateSpikes',
},
{
link: 'ml:logPatternAnalysis',
},
],
},
],
};

View file

@ -0,0 +1,9 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
export { defaultNavigation, type MlNodeDefinition, type NavigationID } from './default_navigation';

View file

@ -0,0 +1,13 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/packages/default-nav/ml'],
};

View file

@ -0,0 +1,5 @@
{
"type": "shared-common",
"id": "@kbn/default-nav-ml",
"owner": "@elastic/ml-ui"
}

View file

@ -0,0 +1,6 @@
{
"name": "@kbn/default-nav-ml",
"private": true,
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0"
}

View file

@ -0,0 +1,23 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types",
"types": [
"jest",
"node",
"react"
]
},
"include": [
"**/*.ts",
"**/*.tsx",
],
"exclude": [
"target/**/*"
],
"kbn_references": [
"@kbn/i18n",
"@kbn/core-chrome-browser",
"@kbn/deeplinks-ml",
]
}

View file

@ -15,7 +15,6 @@ export type {
GroupDefinition,
NavigationGroupPreset,
NavigationTreeDefinition,
NodeDefinition,
ProjectNavigationDefinition,
RecentlyAccessedDefinition,
RootNavigationItemDefinition,
@ -23,8 +22,8 @@ export type {
export type {
ChromeNavigation,
ChromeNavigationViewModel,
NavigationServices,
ChromeNavigationNode,
ChromeNavigationNodeViewModel,
ChromeNavigationViewModel,
NavigationServices,
} from './types';

View file

@ -19,3 +19,4 @@ export {
defaultManagementNavGroup,
defaultMlNavGroup,
} from './src/default_navigation.test.helpers';
export { navLinksMock } from './src/navlinks';

View file

@ -14,33 +14,51 @@
*/
export const defaultAnalyticsNavGroup = {
id: 'sharedux:analytics',
id: 'rootNav:analytics',
title: 'Data exploration',
icon: 'stats',
path: ['sharedux:analytics'],
path: ['rootNav:analytics'],
children: [
{
id: 'root',
path: ['sharedux:analytics', 'root'],
path: ['rootNav:analytics', 'root'],
title: '',
children: [
{
title: 'Discover',
id: 'discover',
href: '/app/discover',
path: ['sharedux:analytics', 'root', 'discover'],
path: ['rootNav:analytics', 'root', 'discover'],
title: 'Deeplink discover',
deepLink: {
id: 'discover',
title: 'Deeplink discover',
href: 'http://mocked/discover',
baseUrl: '/mocked',
url: '/mocked/discover',
},
},
{
title: 'Dashboard',
id: 'dashboard',
href: '/app/dashboards',
path: ['sharedux:analytics', 'root', 'dashboard'],
id: 'dashboards',
path: ['rootNav:analytics', 'root', 'dashboards'],
title: 'Deeplink dashboards',
deepLink: {
id: 'dashboards',
title: 'Deeplink dashboards',
href: 'http://mocked/dashboards',
baseUrl: '/mocked',
url: '/mocked/dashboards',
},
},
{
id: 'visualize_library',
title: 'Visualize Library',
href: '/app/visualize',
path: ['sharedux:analytics', 'root', 'visualize_library'],
id: 'visualize',
path: ['rootNav:analytics', 'root', 'visualize'],
title: 'Deeplink visualize',
deepLink: {
id: 'visualize',
title: 'Deeplink visualize',
href: 'http://mocked/visualize',
baseUrl: '/mocked',
url: '/mocked/visualize',
},
},
],
},
@ -48,140 +66,230 @@ export const defaultAnalyticsNavGroup = {
};
export const defaultMlNavGroup = {
id: 'sharedux:ml',
id: 'rootNav:ml',
title: 'Machine learning',
icon: 'indexMapping',
path: ['sharedux:ml'],
path: ['rootNav:ml'],
children: [
{
title: '',
id: 'root',
path: ['sharedux:ml', 'root'],
path: ['rootNav:ml', 'root'],
children: [
{
id: 'overview',
title: 'Overview',
href: '/app/ml/overview',
path: ['sharedux:ml', 'root', 'overview'],
id: 'ml:overview',
path: ['rootNav:ml', 'root', 'ml:overview'],
title: 'Deeplink ml:overview',
deepLink: {
id: 'ml:overview',
title: 'Deeplink ml:overview',
href: 'http://mocked/ml:overview',
baseUrl: '/mocked',
url: '/mocked/ml:overview',
},
},
{
id: 'notifications',
title: 'Notifications',
href: '/app/ml/notifications',
path: ['sharedux:ml', 'root', 'notifications'],
id: 'ml:notifications',
path: ['rootNav:ml', 'root', 'ml:notifications'],
title: 'Deeplink ml:notifications',
deepLink: {
id: 'ml:notifications',
title: 'Deeplink ml:notifications',
href: 'http://mocked/ml:notifications',
baseUrl: '/mocked',
url: '/mocked/ml:notifications',
},
},
],
},
{
title: 'Anomaly detection',
title: 'Anomaly Detection',
id: 'anomaly_detection',
path: ['sharedux:ml', 'anomaly_detection'],
path: ['rootNav:ml', 'anomaly_detection'],
children: [
{
id: 'jobs',
title: 'Jobs',
href: '/app/ml/jobs',
path: ['sharedux:ml', 'anomaly_detection', 'jobs'],
id: 'ml:anomalyDetection',
path: ['rootNav:ml', 'anomaly_detection', 'ml:anomalyDetection'],
deepLink: {
id: 'ml:anomalyDetection',
title: 'Deeplink ml:anomalyDetection',
href: 'http://mocked/ml:anomalyDetection',
baseUrl: '/mocked',
url: '/mocked/ml:anomalyDetection',
},
},
{
id: 'explorer',
title: 'Anomaly explorer',
href: '/app/ml/explorer',
path: ['sharedux:ml', 'anomaly_detection', 'explorer'],
id: 'ml:anomalyExplorer',
path: ['rootNav:ml', 'anomaly_detection', 'ml:anomalyExplorer'],
title: 'Deeplink ml:anomalyExplorer',
deepLink: {
id: 'ml:anomalyExplorer',
title: 'Deeplink ml:anomalyExplorer',
href: 'http://mocked/ml:anomalyExplorer',
baseUrl: '/mocked',
url: '/mocked/ml:anomalyExplorer',
},
},
{
id: 'single_metric_viewer',
title: 'Single metric viewer',
href: '/app/ml/timeseriesexplorer',
path: ['sharedux:ml', 'anomaly_detection', 'single_metric_viewer'],
id: 'ml:singleMetricViewer',
path: ['rootNav:ml', 'anomaly_detection', 'ml:singleMetricViewer'],
title: 'Deeplink ml:singleMetricViewer',
deepLink: {
id: 'ml:singleMetricViewer',
title: 'Deeplink ml:singleMetricViewer',
href: 'http://mocked/ml:singleMetricViewer',
baseUrl: '/mocked',
url: '/mocked/ml:singleMetricViewer',
},
},
{
id: 'settings',
title: 'Settings',
href: '/app/ml/settings',
path: ['sharedux:ml', 'anomaly_detection', 'settings'],
id: 'ml:settings',
path: ['rootNav:ml', 'anomaly_detection', 'ml:settings'],
title: 'Deeplink ml:settings',
deepLink: {
id: 'ml:settings',
title: 'Deeplink ml:settings',
href: 'http://mocked/ml:settings',
baseUrl: '/mocked',
url: '/mocked/ml:settings',
},
},
],
},
{
id: 'data_frame_analytics',
title: 'Data frame analytics',
path: ['sharedux:ml', 'data_frame_analytics'],
path: ['rootNav:ml', 'data_frame_analytics'],
children: [
{
id: 'jobs',
title: 'Jobs',
href: '/app/ml/data_frame_analytics',
path: ['sharedux:ml', 'data_frame_analytics', 'jobs'],
id: 'ml:dataFrameAnalytics',
path: ['rootNav:ml', 'data_frame_analytics', 'ml:dataFrameAnalytics'],
deepLink: {
id: 'ml:dataFrameAnalytics',
title: 'Deeplink ml:dataFrameAnalytics',
href: 'http://mocked/ml:dataFrameAnalytics',
baseUrl: '/mocked',
url: '/mocked/ml:dataFrameAnalytics',
},
},
{
id: 'results_explorer',
title: 'Results explorer',
href: '/app/ml/data_frame_analytics/exploration',
path: ['sharedux:ml', 'data_frame_analytics', 'results_explorer'],
id: 'ml:resultExplorer',
path: ['rootNav:ml', 'data_frame_analytics', 'ml:resultExplorer'],
title: 'Deeplink ml:resultExplorer',
deepLink: {
id: 'ml:resultExplorer',
title: 'Deeplink ml:resultExplorer',
href: 'http://mocked/ml:resultExplorer',
baseUrl: '/mocked',
url: '/mocked/ml:resultExplorer',
},
},
{
id: 'analytics_map',
title: 'Analytics map',
href: '/app/ml/data_frame_analytics/map',
path: ['sharedux:ml', 'data_frame_analytics', 'analytics_map'],
id: 'ml:analyticsMap',
path: ['rootNav:ml', 'data_frame_analytics', 'ml:analyticsMap'],
title: 'Deeplink ml:analyticsMap',
deepLink: {
id: 'ml:analyticsMap',
title: 'Deeplink ml:analyticsMap',
href: 'http://mocked/ml:analyticsMap',
baseUrl: '/mocked',
url: '/mocked/ml:analyticsMap',
},
},
],
},
{
id: 'model_management',
title: 'Model management',
path: ['sharedux:ml', 'model_management'],
path: ['rootNav:ml', 'model_management'],
children: [
{
id: 'trained_models',
title: 'Trained models',
href: '/app/ml/trained_models',
path: ['sharedux:ml', 'model_management', 'trained_models'],
id: 'ml:nodesOverview',
path: ['rootNav:ml', 'model_management', 'ml:nodesOverview'],
title: 'Deeplink ml:nodesOverview',
deepLink: {
id: 'ml:nodesOverview',
title: 'Deeplink ml:nodesOverview',
href: 'http://mocked/ml:nodesOverview',
baseUrl: '/mocked',
url: '/mocked/ml:nodesOverview',
},
},
{
id: 'nodes',
title: 'Nodes',
href: '/app/ml/nodes',
path: ['sharedux:ml', 'model_management', 'nodes'],
id: 'ml:nodes',
path: ['rootNav:ml', 'model_management', 'ml:nodes'],
title: 'Deeplink ml:nodes',
deepLink: {
id: 'ml:nodes',
title: 'Deeplink ml:nodes',
href: 'http://mocked/ml:nodes',
baseUrl: '/mocked',
url: '/mocked/ml:nodes',
},
},
],
},
{
id: 'data_visualizer',
title: 'Data visualizer',
path: ['sharedux:ml', 'data_visualizer'],
path: ['rootNav:ml', 'data_visualizer'],
children: [
{
id: 'file',
title: 'File',
href: '/app/ml/filedatavisualizer',
path: ['sharedux:ml', 'data_visualizer', 'file'],
id: 'ml:fileUpload',
path: ['rootNav:ml', 'data_visualizer', 'ml:fileUpload'],
deepLink: {
id: 'ml:fileUpload',
title: 'Deeplink ml:fileUpload',
href: 'http://mocked/ml:fileUpload',
baseUrl: '/mocked',
url: '/mocked/ml:fileUpload',
},
},
{
id: 'data_view',
title: 'Data view',
href: '/app/ml/datavisualizer_index_select',
path: ['sharedux:ml', 'data_visualizer', 'data_view'],
id: 'ml:indexDataVisualizer',
path: ['rootNav:ml', 'data_visualizer', 'ml:indexDataVisualizer'],
deepLink: {
id: 'ml:indexDataVisualizer',
title: 'Deeplink ml:indexDataVisualizer',
href: 'http://mocked/ml:indexDataVisualizer',
baseUrl: '/mocked',
url: '/mocked/ml:indexDataVisualizer',
},
},
],
},
{
id: 'aiops_labs',
title: 'AIOps labs',
path: ['sharedux:ml', 'aiops_labs'],
path: ['rootNav:ml', 'aiops_labs'],
children: [
{
id: 'explain_log_rate_spikes',
title: 'Explain log rate spikes',
href: '/app/ml/aiops/explain_log_rate_spikes_index_select',
path: ['sharedux:ml', 'aiops_labs', 'explain_log_rate_spikes'],
id: 'ml:explainLogRateSpikes',
path: ['rootNav:ml', 'aiops_labs', 'ml:explainLogRateSpikes'],
deepLink: {
id: 'ml:explainLogRateSpikes',
title: 'Deeplink ml:explainLogRateSpikes',
href: 'http://mocked/ml:explainLogRateSpikes',
baseUrl: '/mocked',
url: '/mocked/ml:explainLogRateSpikes',
},
},
{
id: 'log_pattern_analysis',
title: 'Log pattern analysis',
href: '/app/ml/aiops/log_categorization_index_select',
path: ['sharedux:ml', 'aiops_labs', 'log_pattern_analysis'],
id: 'ml:logPatternAnalysis',
path: ['rootNav:ml', 'aiops_labs', 'ml:logPatternAnalysis'],
title: 'Deeplink ml:logPatternAnalysis',
deepLink: {
id: 'ml:logPatternAnalysis',
title: 'Deeplink ml:logPatternAnalysis',
href: 'http://mocked/ml:logPatternAnalysis',
baseUrl: '/mocked',
url: '/mocked/ml:logPatternAnalysis',
},
},
],
},
@ -190,38 +298,62 @@ export const defaultMlNavGroup = {
export const defaultDevtoolsNavGroup = {
title: 'Developer tools',
id: 'sharedux:devtools',
id: 'rootNav:devtools',
icon: 'editorCodeBlock',
path: ['sharedux:devtools'],
path: ['rootNav:devtools'],
children: [
{
id: 'root',
path: ['sharedux:devtools', 'root'],
path: ['rootNav:devtools', 'root'],
title: '',
children: [
{
id: 'console',
title: 'Console',
href: '/app/dev_tools#/console',
path: ['sharedux:devtools', 'root', 'console'],
id: 'dev_tools:console',
path: ['rootNav:devtools', 'root', 'dev_tools:console'],
title: 'Deeplink dev_tools:console',
deepLink: {
id: 'dev_tools:console',
title: 'Deeplink dev_tools:console',
href: 'http://mocked/dev_tools:console',
baseUrl: '/mocked',
url: '/mocked/dev_tools:console',
},
},
{
id: 'search_profiler',
title: 'Search profiler',
href: '/app/dev_tools#/searchprofiler',
path: ['sharedux:devtools', 'root', 'search_profiler'],
id: 'dev_tools:searchprofiler',
path: ['rootNav:devtools', 'root', 'dev_tools:searchprofiler'],
title: 'Deeplink dev_tools:searchprofiler',
deepLink: {
id: 'dev_tools:searchprofiler',
title: 'Deeplink dev_tools:searchprofiler',
href: 'http://mocked/dev_tools:searchprofiler',
baseUrl: '/mocked',
url: '/mocked/dev_tools:searchprofiler',
},
},
{
id: 'grok_debugger',
title: 'Grok debugger',
href: '/app/dev_tools#/grokdebugger',
path: ['sharedux:devtools', 'root', 'grok_debugger'],
id: 'dev_tools:grokdebugger',
path: ['rootNav:devtools', 'root', 'dev_tools:grokdebugger'],
title: 'Deeplink dev_tools:grokdebugger',
deepLink: {
id: 'dev_tools:grokdebugger',
title: 'Deeplink dev_tools:grokdebugger',
href: 'http://mocked/dev_tools:grokdebugger',
baseUrl: '/mocked',
url: '/mocked/dev_tools:grokdebugger',
},
},
{
id: 'painless_lab',
title: 'Painless lab',
href: '/app/dev_tools#/painless_lab',
path: ['sharedux:devtools', 'root', 'painless_lab'],
id: 'dev_tools:painless_lab',
path: ['rootNav:devtools', 'root', 'dev_tools:painless_lab'],
title: 'Deeplink dev_tools:painless_lab',
deepLink: {
id: 'dev_tools:painless_lab',
title: 'Deeplink dev_tools:painless_lab',
href: 'http://mocked/dev_tools:painless_lab',
baseUrl: '/mocked',
url: '/mocked/dev_tools:painless_lab',
},
},
],
},
@ -229,257 +361,289 @@ export const defaultDevtoolsNavGroup = {
};
export const defaultManagementNavGroup = {
id: 'sharedux:management',
id: 'rootNav:management',
title: 'Management',
icon: 'gear',
path: ['sharedux:management'],
path: ['rootNav:management'],
children: [
{
id: 'root',
title: '',
path: ['sharedux:management', 'root'],
path: ['rootNav:management', 'root'],
children: [
{
id: 'stack_monitoring',
title: 'Stack monitoring',
href: '/app/monitoring',
path: ['sharedux:management', 'root', 'stack_monitoring'],
id: 'monitoring',
path: ['rootNav:management', 'root', 'monitoring'],
title: 'Deeplink monitoring',
deepLink: {
id: 'monitoring',
title: 'Deeplink monitoring',
href: 'http://mocked/monitoring',
baseUrl: '/mocked',
url: '/mocked/monitoring',
},
},
],
},
{
id: 'integration_management',
title: 'Integration management',
path: ['sharedux:management', 'integration_management'],
path: ['rootNav:management', 'integration_management'],
children: [
{
id: 'integrations',
title: 'Integrations',
href: '/app/integrations',
path: ['sharedux:management', 'integration_management', 'integrations'],
path: ['rootNav:management', 'integration_management', 'integrations'],
title: 'Deeplink integrations',
deepLink: {
id: 'integrations',
title: 'Deeplink integrations',
href: 'http://mocked/integrations',
baseUrl: '/mocked',
url: '/mocked/integrations',
},
},
{
id: 'fleet',
title: 'Fleet',
href: '/app/fleet',
path: ['sharedux:management', 'integration_management', 'fleet'],
path: ['rootNav:management', 'integration_management', 'fleet'],
title: 'Deeplink fleet',
deepLink: {
id: 'fleet',
title: 'Deeplink fleet',
href: 'http://mocked/fleet',
baseUrl: '/mocked',
url: '/mocked/fleet',
},
},
{
id: 'osquery',
title: 'Osquery',
href: '/app/osquery',
path: ['sharedux:management', 'integration_management', 'osquery'],
path: ['rootNav:management', 'integration_management', 'osquery'],
title: 'Deeplink osquery',
deepLink: {
id: 'osquery',
title: 'Deeplink osquery',
href: 'http://mocked/osquery',
baseUrl: '/mocked',
url: '/mocked/osquery',
},
},
],
},
{
id: 'stack_management',
title: 'Stack management',
path: ['sharedux:management', 'stack_management'],
path: ['rootNav:management', 'stack_management'],
children: [
{
id: 'upgrade_assistant',
title: 'Upgrade assistant',
href: '/app/management/stack/upgrade_assistant',
path: ['sharedux:management', 'stack_management', 'upgrade_assistant'],
},
{
id: 'ingest',
title: 'Ingest',
path: ['sharedux:management', 'stack_management', 'ingest'],
path: ['rootNav:management', 'stack_management', 'ingest'],
children: [
{
id: 'ingest_pipelines',
title: 'Ingest pipelines',
href: '/app/management/ingest/ingest_pipelines',
path: ['sharedux:management', 'stack_management', 'ingest', 'ingest_pipelines'],
id: 'management:ingest_pipelines',
path: [
'rootNav:management',
'stack_management',
'ingest',
'management:ingest_pipelines',
],
title: 'Deeplink management:ingest_pipelines',
deepLink: {
id: 'management:ingest_pipelines',
title: 'Deeplink management:ingest_pipelines',
href: 'http://mocked/management:ingest_pipelines',
baseUrl: '/mocked',
url: '/mocked/management:ingest_pipelines',
},
},
{
id: 'logstash_pipelines',
title: 'Logstash pipelines',
href: '/app/management/ingest/pipelines',
path: ['sharedux:management', 'stack_management', 'ingest', 'logstash_pipelines'],
id: 'management:pipelines',
path: ['rootNav:management', 'stack_management', 'ingest', 'management:pipelines'],
title: 'Deeplink management:pipelines',
deepLink: {
id: 'management:pipelines',
title: 'Deeplink management:pipelines',
href: 'http://mocked/management:pipelines',
baseUrl: '/mocked',
url: '/mocked/management:pipelines',
},
},
],
},
{
id: 'data',
title: 'Data',
path: ['sharedux:management', 'stack_management', 'data'],
path: ['rootNav:management', 'stack_management', 'data'],
children: [
{
id: 'index_management',
title: 'Index management',
href: '/app/management/data/index_management',
path: ['sharedux:management', 'stack_management', 'data', 'index_management'],
},
{
id: 'index_lifecycle_policies',
title: 'Index lifecycle policies',
href: '/app/management/data/index_lifecycle_management',
path: ['sharedux:management', 'stack_management', 'data', 'index_lifecycle_policies'],
},
{
id: 'snapshot_and_restore',
title: 'Snapshot and restore',
href: 'app/management/data/snapshot_restore',
path: ['sharedux:management', 'stack_management', 'data', 'snapshot_and_restore'],
},
{
id: 'rollup_jobs',
title: 'Rollup jobs',
href: '/app/management/data/rollup_jobs',
path: ['sharedux:management', 'stack_management', 'data', 'rollup_jobs'],
},
{
id: 'transforms',
title: 'Transforms',
href: '/app/management/data/transform',
path: ['sharedux:management', 'stack_management', 'data', 'transforms'],
},
{
id: 'cross_cluster_replication',
title: 'Cross-cluster replication',
href: '/app/management/data/cross_cluster_replication',
id: 'management:index_management',
path: [
'sharedux:management',
'rootNav:management',
'stack_management',
'data',
'cross_cluster_replication',
'management:index_management',
],
title: 'Deeplink management:index_management',
deepLink: {
id: 'management:index_management',
title: 'Deeplink management:index_management',
href: 'http://mocked/management:index_management',
baseUrl: '/mocked',
url: '/mocked/management:index_management',
},
},
{
id: 'remote_clusters',
title: 'Remote clusters',
href: '/app/management/data/remote_clusters',
path: ['sharedux:management', 'stack_management', 'data', 'remote_clusters'],
id: 'management:transform',
path: ['rootNav:management', 'stack_management', 'data', 'management:transform'],
title: 'Deeplink management:transform',
deepLink: {
id: 'management:transform',
title: 'Deeplink management:transform',
href: 'http://mocked/management:transform',
baseUrl: '/mocked',
url: '/mocked/management:transform',
},
},
],
},
{
id: 'alerts_and_insights',
title: 'Alerts and insights',
path: ['sharedux:management', 'stack_management', 'alerts_and_insights'],
path: ['rootNav:management', 'stack_management', 'alerts_and_insights'],
children: [
{
id: 'rules',
title: 'Rules',
href: '/app/management/insightsAndAlerting/triggersActions/rules',
path: ['sharedux:management', 'stack_management', 'alerts_and_insights', 'rules'],
},
{
id: 'cases',
title: 'Cases',
href: '/app/management/insightsAndAlerting/cases',
path: ['sharedux:management', 'stack_management', 'alerts_and_insights', 'cases'],
},
{
id: 'connectors',
title: 'Connectors',
href: '/app/management/insightsAndAlerting/triggersActionsConnectors/connectors',
id: 'management:triggersActions',
path: [
'sharedux:management',
'rootNav:management',
'stack_management',
'alerts_and_insights',
'connectors',
'management:triggersActions',
],
title: 'Deeplink management:triggersActions',
deepLink: {
id: 'management:triggersActions',
title: 'Deeplink management:triggersActions',
href: 'http://mocked/management:triggersActions',
baseUrl: '/mocked',
url: '/mocked/management:triggersActions',
},
},
{
id: 'reporting',
title: 'Reporting',
href: '/app/management/insightsAndAlerting/reporting',
path: ['sharedux:management', 'stack_management', 'alerts_and_insights', 'reporting'],
},
{
id: 'machine_learning',
title: 'Machine learning',
href: '/app/management/insightsAndAlerting/jobsListLink',
id: 'management:cases',
path: [
'sharedux:management',
'rootNav:management',
'stack_management',
'alerts_and_insights',
'machine_learning',
'management:cases',
],
title: 'Deeplink management:cases',
deepLink: {
id: 'management:cases',
title: 'Deeplink management:cases',
href: 'http://mocked/management:cases',
baseUrl: '/mocked',
url: '/mocked/management:cases',
},
},
{
id: 'watcher',
title: 'Watcher',
href: '/app/management/insightsAndAlerting/watcher',
path: ['sharedux:management', 'stack_management', 'alerts_and_insights', 'watcher'],
},
],
},
{
id: 'security',
title: 'Security',
path: ['sharedux:management', 'stack_management', 'security'],
children: [
{
id: 'users',
title: 'Users',
href: '/app/management/security/users',
path: ['sharedux:management', 'stack_management', 'security', 'users'],
id: 'management:triggersActionsConnectors',
path: [
'rootNav:management',
'stack_management',
'alerts_and_insights',
'management:triggersActionsConnectors',
],
title: 'Deeplink management:triggersActionsConnectors',
deepLink: {
id: 'management:triggersActionsConnectors',
title: 'Deeplink management:triggersActionsConnectors',
href: 'http://mocked/management:triggersActionsConnectors',
baseUrl: '/mocked',
url: '/mocked/management:triggersActionsConnectors',
},
},
{
id: 'roles',
title: 'Roles',
href: '/app/management/security/roles',
path: ['sharedux:management', 'stack_management', 'security', 'roles'],
},
{
id: 'role_mappings',
title: 'Role mappings',
href: '/app/management/security/role_mappings',
path: ['sharedux:management', 'stack_management', 'security', 'role_mappings'],
},
{
id: 'api_keys',
title: 'API keys',
href: '/app/management/security/api_keys',
path: ['sharedux:management', 'stack_management', 'security', 'api_keys'],
id: 'management:jobsListLink',
path: [
'rootNav:management',
'stack_management',
'alerts_and_insights',
'management:jobsListLink',
],
title: 'Deeplink management:jobsListLink',
deepLink: {
id: 'management:jobsListLink',
title: 'Deeplink management:jobsListLink',
href: 'http://mocked/management:jobsListLink',
baseUrl: '/mocked',
url: '/mocked/management:jobsListLink',
},
},
],
},
{
id: 'kibana',
title: 'Kibana',
path: ['sharedux:management', 'stack_management', 'kibana'],
path: ['rootNav:management', 'stack_management', 'kibana'],
children: [
{
id: 'data_views',
title: 'Data view',
href: '/app/management/kibana/dataViews',
path: ['sharedux:management', 'stack_management', 'kibana', 'data_views'],
id: 'management:dataViews',
path: ['rootNav:management', 'stack_management', 'kibana', 'management:dataViews'],
title: 'Deeplink management:dataViews',
deepLink: {
id: 'management:dataViews',
title: 'Deeplink management:dataViews',
href: 'http://mocked/management:dataViews',
baseUrl: '/mocked',
url: '/mocked/management:dataViews',
},
},
{
id: 'saved_objects',
title: 'Saved objects',
href: '/app/management/kibana/objects',
path: ['sharedux:management', 'stack_management', 'kibana', 'saved_objects'],
id: 'management:objects',
path: ['rootNav:management', 'stack_management', 'kibana', 'management:objects'],
title: 'Deeplink management:objects',
deepLink: {
id: 'management:objects',
title: 'Deeplink management:objects',
href: 'http://mocked/management:objects',
baseUrl: '/mocked',
url: '/mocked/management:objects',
},
},
{
id: 'tags',
title: 'Tags',
href: '/app/management/kibana/tags',
path: ['sharedux:management', 'stack_management', 'kibana', 'tags'],
id: 'management:tags',
path: ['rootNav:management', 'stack_management', 'kibana', 'management:tags'],
title: 'Deeplink management:tags',
deepLink: {
id: 'management:tags',
title: 'Deeplink management:tags',
href: 'http://mocked/management:tags',
baseUrl: '/mocked',
url: '/mocked/management:tags',
},
},
{
id: 'search_sessions',
title: 'Search sessions',
href: '/app/management/kibana/search_sessions',
path: ['sharedux:management', 'stack_management', 'kibana', 'search_sessions'],
id: 'management:spaces',
path: ['rootNav:management', 'stack_management', 'kibana', 'management:spaces'],
title: 'Deeplink management:spaces',
deepLink: {
id: 'management:spaces',
title: 'Deeplink management:spaces',
href: 'http://mocked/management:spaces',
baseUrl: '/mocked',
url: '/mocked/management:spaces',
},
},
{
id: 'spaces',
title: 'Spaces',
href: '/app/management/kibana/spaces',
path: ['sharedux:management', 'stack_management', 'kibana', 'spaces'],
},
{
id: 'advanced_settings',
title: 'Advanced settings',
href: '/app/management/kibana/settings',
path: ['sharedux:management', 'stack_management', 'kibana', 'advanced_settings'],
id: 'management:settings',
path: ['rootNav:management', 'stack_management', 'kibana', 'management:settings'],
title: 'Deeplink management:settings',
deepLink: {
id: 'management:settings',
title: 'Deeplink management:settings',
href: 'http://mocked/management:settings',
baseUrl: '/mocked',
url: '/mocked/management:settings',
},
},
],
},

View file

@ -6,15 +6,19 @@
* Side Public License, v 1.
*/
import { ChromeNavLink } from '@kbn/core-chrome-browser';
import { BehaviorSubject } from 'rxjs';
import { NavigationServices, ChromeNavigationNodeViewModel } from '../../types';
import { navLinksMock } from './navlinks';
export const getServicesMock = (): NavigationServices => {
export const getServicesMock = ({
navLinks = navLinksMock,
}: { navLinks?: ChromeNavLink[] } = {}): NavigationServices => {
const navigateToUrl = jest.fn().mockResolvedValue(undefined);
const basePath = { prepend: jest.fn((path: string) => `/base${path}`) };
const loadingCount$ = new BehaviorSubject(0);
const recentlyAccessed$ = new BehaviorSubject([]);
const navLinks$ = new BehaviorSubject([]);
const navLinks$ = new BehaviorSubject(navLinks);
return {
basePath,

View file

@ -0,0 +1,81 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import type { AppDeepLinkId, ChromeNavLink } from '@kbn/core-chrome-browser';
const getNavLink = (id: string): ChromeNavLink => {
return {
id,
title: `Deeplink ${id}`,
href: `http://mocked/${id}`,
baseUrl: `/mocked`,
url: `/mocked/${id}`,
};
};
const allNavLinks: AppDeepLinkId[] = [
'dashboards',
'dev_tools',
'dev_tools:console',
'dev_tools:grokdebugger',
'dev_tools:painless_lab',
'dev_tools:searchprofiler',
'discover',
'fleet',
'integrations',
'management:api_keys',
'management:cases',
'management:cross_cluster_replication',
'management:dataViews',
'management:index_lifecycle_management',
'management:index_management',
'management:ingest_pipelines',
'management:jobsListLink',
'management:objects',
'management:pipelines',
'management:reporting',
'management:rollup_jobs',
'management:settings',
'management:snapshot_restore',
'management:spaces',
'management:tags',
'management:transform',
'management:triggersActions',
'management:triggersActionsConnectors',
'management:watcher',
'ml',
'ml:aiOps',
'ml:analyticsMap',
'ml:anomalyDetection',
'ml:anomalyExplorer',
'ml:calendarSettings',
'ml:calendarSettings',
'ml:changePointDetections',
'ml:dataFrameAnalytics',
'ml:dataVisualizer',
'ml:explainLogRateSpikes',
'ml:fileUpload',
'ml:filterListsSettings',
'ml:indexDataVisualizer',
'ml:logPatternAnalysis',
'ml:memoryUsage',
'ml:modelManagement',
'ml:nodes',
'ml:nodesOverview',
'ml:notifications',
'ml:overview',
'ml:resultExplorer',
'ml:settings',
'ml:singleMetricViewer',
'monitoring',
'osquery',
'serverlessElasticsearch',
'visualize',
];
export const navLinksMock = allNavLinks.map(getNavLink);

View file

@ -1,47 +0,0 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import type { ChromeNavigationNodeViewModel, PlatformSectionConfig } from '../../types';
/**
* Navigation node parser. It filers out the nodes disabled through config and
* sets the `path` of each of the nodes.
*
* @param navItems Navigation nodes
* @param platformSectionConfig Configuration with flags to disable nodes in the navigation tree
*
* @returns The navigation tree filtered
*/
export const parseNavItems = (
parentIds: string[] = [],
navItems?: ChromeNavigationNodeViewModel[],
platformSectionConfig?: PlatformSectionConfig
): ChromeNavigationNodeViewModel[] | undefined => {
if (!navItems) {
return undefined;
}
return navItems.reduce<ChromeNavigationNodeViewModel[]>((accum, item) => {
const config = platformSectionConfig?.properties?.[item.id];
if (config?.enabled === false) {
// return accumulated set without the item that is not enabled
return accum;
}
const path = [...parentIds, item.id].filter(Boolean).join('.');
let filteredItems: ChromeNavigationNodeViewModel[] | undefined;
if (item.items) {
// recursion
const nextPlatformSectionConfig = platformSectionConfig?.properties?.[item.id];
filteredItems = parseNavItems([...parentIds, item.id], item.items, nextPlatformSectionConfig);
}
return [...accum, { ...item, path, items: filteredItems }];
}, []);
};

View file

@ -1,40 +0,0 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { BasePathService, NavigateToUrlFn } from '../../types/internal';
import { analyticsItemSet } from './platform_nav/analytics';
import { devtoolsItemSet } from './platform_nav/devtools';
import { mlItemSet } from './platform_nav/machine_learning';
import { managementItemSet } from './platform_nav/management';
export interface NavigationModelDeps {
basePath: BasePathService;
navigateToUrl: NavigateToUrlFn;
}
/**
* @public
*/
export enum Platform {
Analytics = 'analytics',
MachineLearning = 'ml',
DevTools = 'devTools',
Management = 'management',
}
/**
* @public
*/
export const navItemSet = {
[Platform.Analytics]: analyticsItemSet,
[Platform.MachineLearning]: mlItemSet,
[Platform.DevTools]: devtoolsItemSet,
[Platform.Management]: managementItemSet,
};
export { NavigationModel } from './model';

View file

@ -1,80 +0,0 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { navItemSet, Platform } from '.';
import type { ChromeNavigationNodeViewModel, PlatformId, PlatformConfigSet } from '../../types';
import { parseNavItems } from './create_side_nav';
/**
* @internal
*/
export class NavigationModel {
constructor(
private platformConfig: Partial<PlatformConfigSet> | undefined,
private solutions: ChromeNavigationNodeViewModel[]
) {}
public getPlatform(): Record<PlatformId, ChromeNavigationNodeViewModel> {
return {
[Platform.Analytics]: {
id: Platform.Analytics,
icon: 'stats',
title: 'Data exploration',
items: parseNavItems(
[Platform.Analytics],
navItemSet[Platform.Analytics],
this.platformConfig?.[Platform.Analytics]
),
},
[Platform.MachineLearning]: {
id: Platform.MachineLearning,
icon: 'indexMapping',
title: 'Machine learning',
items: parseNavItems(
[Platform.MachineLearning],
navItemSet[Platform.MachineLearning],
this.platformConfig?.[Platform.MachineLearning]
),
},
[Platform.DevTools]: {
id: Platform.DevTools,
icon: 'editorCodeBlock',
title: 'Developer tools',
items: parseNavItems(
[Platform.DevTools],
navItemSet[Platform.DevTools],
this.platformConfig?.[Platform.DevTools]
),
},
[Platform.Management]: {
id: Platform.Management,
icon: 'gear',
title: 'Management',
items: parseNavItems(
[Platform.Management],
navItemSet[Platform.Management],
this.platformConfig?.[Platform.Management]
),
},
};
}
public getSolutions(): ChromeNavigationNodeViewModel[] {
// Allow multiple solutions' collapsible nav buckets side-by-side
return this.solutions.map((s) => ({
id: s.id,
title: s.title,
icon: s.icon,
items: parseNavItems([s.id], s.items),
}));
}
public isEnabled(sectionId: PlatformId) {
return this.platformConfig?.[sectionId]?.enabled !== false;
}
}

View file

@ -1,37 +0,0 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { ChromeNavigationNodeViewModel } from '../../../types';
// TODO: Declare ChromeNavigationNode[] (with "link" to app id or deeplink id)
// and then call an api on the Chrome service to convert to ChromeNavigationNodeViewModel
// with its "href", "isActive"... metadata
export const analyticsItemSet: ChromeNavigationNodeViewModel[] = [
{
title: '',
id: 'root',
items: [
{
title: 'Discover',
id: 'discover',
href: '/app/discover',
},
{
title: 'Dashboard',
id: 'dashboard',
href: '/app/dashboards',
},
{
title: 'Visualize Library',
id: 'visualize_library',
href: '/app/visualize',
},
],
},
];

View file

@ -1,42 +0,0 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { ChromeNavigationNodeViewModel } from '../../../types';
// TODO: Declare ChromeNavigationNode[] (with "link" to app id or deeplink id)
// and then call an api on the Chrome service to convert to ChromeNavigationNodeViewModel
// with its "href", "isActive"... metadata
export const devtoolsItemSet: ChromeNavigationNodeViewModel[] = [
{
title: '',
id: 'root',
items: [
{
title: 'Console',
id: 'console',
href: '/app/dev_tools#/console',
},
{
title: 'Search profiler',
id: 'search_profiler',
href: '/app/dev_tools#/searchprofiler',
},
{
title: 'Grok debugger',
id: 'grok_debugger',
href: '/app/dev_tools#/grokdebugger',
},
{
title: 'Painless lab',
id: 'painless_lab',
href: '/app/dev_tools#/painless_lab',
},
],
},
];

View file

@ -1,127 +0,0 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { ChromeNavigationNodeViewModel } from '../../../types';
// TODO: Declare ChromeNavigationNode[] (with "link" to app id or deeplink id)
// and then call an api on the Chrome service to convert to ChromeNavigationNodeViewModel
// with its "href", "isActive"... metadata
export const mlItemSet: ChromeNavigationNodeViewModel[] = [
{
title: '',
id: 'root',
items: [
{
title: 'Overview',
id: 'overview',
href: '/app/ml/overview',
},
{
title: 'Notifications',
id: 'notifications',
href: '/app/ml/notifications',
},
],
},
{
title: 'Anomaly detection',
id: 'anomaly_detection',
items: [
{
title: 'Jobs',
id: 'jobs',
href: '/app/ml/jobs',
},
{
title: 'Anomaly explorer',
id: 'explorer',
href: '/app/ml/explorer',
},
{
title: 'Single metric viewer',
id: 'single_metric_viewer',
href: '/app/ml/timeseriesexplorer',
},
{
title: 'Settings',
id: 'settings',
href: '/app/ml/settings',
},
],
},
{
title: 'Data frame analytics',
id: 'data_frame_analytics',
items: [
{
title: 'Jobs',
id: 'jobs',
href: '/app/ml/data_frame_analytics',
},
{
title: 'Results explorer',
id: 'results_explorer',
href: '/app/ml/data_frame_analytics/exploration',
},
{
title: 'Analytics map',
id: 'analytics_map',
href: '/app/ml/data_frame_analytics/map',
},
],
},
{
title: 'Model management',
id: 'model_management',
items: [
{
title: 'Trained models',
id: 'trained_models',
href: '/app/ml/trained_models',
},
{
title: 'Nodes',
id: 'nodes',
href: '/app/ml/nodes',
},
],
},
{
title: 'Data visualizer',
id: 'data_visualizer',
items: [
{
title: 'File',
id: 'file',
href: '/app/ml/filedatavisualizer',
},
{
title: 'Data view',
id: 'data_view',
href: '/app/ml/datavisualizer_index_select',
},
],
},
{
title: 'AIOps labs',
id: 'aiops_labs',
items: [
{
title: 'Explain log rate spikes',
id: 'explain_log_rate_spikes',
href: '/app/ml/aiops/explain_log_rate_spikes_index_select',
},
{
title: 'Log pattern analysis',
id: 'log_pattern_analysis',
href: '/app/ml/aiops/log_categorization_index_select',
},
],
},
];

View file

@ -1,214 +0,0 @@
/*
* 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 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 or the Server
* Side Public License, v 1.
*/
import { ChromeNavigationNodeViewModel } from '../../../types';
// TODO: Declare ChromeNavigationNode[] (with "link" to app id or deeplink id)
// and then call an api on the Chrome service to convert to ChromeNavigationNodeViewModel
// with its "href", "isActive"... metadata
export const managementItemSet: ChromeNavigationNodeViewModel[] = [
{
title: '',
id: 'root',
items: [
{
title: 'Stack monitoring',
id: 'stack_monitoring',
href: '/app/monitoring',
},
],
},
{
title: 'Integration management',
id: 'integration_management',
items: [
{
title: 'Integrations',
id: 'integrations',
href: '/app/integrations',
},
{
title: 'Fleet',
id: 'fleet',
href: '/app/fleet',
},
{
title: 'Osquery',
id: 'osquery',
href: '/app/osquery',
},
],
},
{
title: 'Stack management',
id: 'stack_management',
items: [
{
title: 'Ingest',
id: 'ingest',
items: [
{
title: 'Ingest pipelines',
id: 'ingest_pipelines',
href: '/app/management/ingest/ingest_pipelines',
},
{
title: 'Logstash pipelines',
id: 'logstash_pipelines',
href: '/app/management/ingest/pipelines',
},
],
},
{
title: 'Data',
id: 'data',
items: [
{
title: 'Index management',
id: 'index_management',
href: '/app/management/data/index_management',
},
{
title: 'Index lifecycle policies',
id: 'index_lifecycle_policies',
href: '/app/management/data/index_lifecycle_management',
},
{
title: 'Snapshot and restore',
id: 'snapshot_and_restore',
href: 'app/management/data/snapshot_restore',
},
{
title: 'Rollup jobs',
id: 'rollup_jobs',
href: '/app/management/data/rollup_jobs',
},
{
title: 'Transforms',
id: 'transforms',
href: '/app/management/data/transform',
},
{
title: 'Cross-cluster replication',
id: 'cross_cluster_replication',
href: '/app/management/data/cross_cluster_replication',
},
{
title: 'Remote clusters',
id: 'remote_clusters',
href: '/app/management/data/remote_clusters',
},
],
},
{
title: 'Alerts and insights',
id: 'alerts_and_insights',
items: [
{
title: 'Rules',
id: 'rules',
href: '/app/management/insightsAndAlerting/triggersActions/rules',
},
{
title: 'Cases',
id: 'cases',
href: '/app/management/insightsAndAlerting/cases',
},
{
title: 'Connectors',
id: 'connectors',
href: '/app/management/insightsAndAlerting/triggersActionsConnectors/connectors',
},
{
title: 'Reporting',
id: 'reporting',
href: '/app/management/insightsAndAlerting/reporting',
},
{
title: 'Machine learning',
id: 'machine_learning',
href: '/app/management/insightsAndAlerting/jobsListLink',
},
{
title: 'Watcher',
id: 'watcher',
href: '/app/management/insightsAndAlerting/watcher',
},
],
},
{
title: 'Security',
id: 'security',
items: [
{
title: 'Users',
id: 'users',
href: '/app/management/security/users',
},
{
title: 'Roles',
id: 'roles',
href: '/app/management/security/roles',
},
{
title: 'Role mappings',
id: 'role_mappings',
href: '/app/management/security/role_mappings',
},
{
title: 'API keys',
id: 'api_keys',
href: '/app/management/security/api_keys',
},
],
},
{
title: 'Kibana',
id: 'kibana',
items: [
{
title: 'Data view',
id: 'data_views',
href: '/app/management/kibana/dataViews',
},
{
title: 'Saved objects',
id: 'saved_objects',
href: '/app/management/kibana/objects',
},
{
title: 'Tags',
id: 'tags',
href: '/app/management/kibana/tags',
},
{
title: 'Search sessions',
id: 'search_sessions',
href: '/app/management/kibana/search_sessions',
},
{
title: 'Spaces',
id: 'spaces',
href: '/app/management/kibana/spaces',
},
{
title: 'Advanced settings',
id: 'advanced_settings',
href: '/app/management/kibana/settings',
},
],
},
{
title: 'Upgrade assistant',
id: 'upgrade_assistant',
href: '/app/management/stack/upgrade_assistant',
},
],
},
];

View file

@ -32,12 +32,12 @@ describe('<Navigation />', () => {
<NavigationProvider {...services} onProjectNavigationChange={onProjectNavigationChange}>
<Navigation homeRef="https://elastic.co">
<Navigation.Group id="group1">
<Navigation.Item id="item1" title="Item 1" />
<Navigation.Item id="item2" title="Item 2" />
<Navigation.Item id="item1" title="Item 1" href="https://foo" />
<Navigation.Item id="item2" title="Item 2" href="https://foo" />
<Navigation.Group id="group1A" title="Group1A">
<Navigation.Item id="item1" title="Group 1A Item 1" />
<Navigation.Item id="item1" title="Group 1A Item 1" href="https://foo" />
<Navigation.Group id="group1A_1" title="Group1A_1">
<Navigation.Item id="item1" title="Group 1A_1 Item 1" />
<Navigation.Item id="item1" title="Group 1A_1 Item 1" href="https://foo" />
</Navigation.Group>
</Navigation.Group>
</Navigation.Group>
@ -72,11 +72,13 @@ describe('<Navigation />', () => {
{
id: 'item1',
title: 'Item 1',
href: 'https://foo',
path: ['group1', 'item1'],
},
{
id: 'item2',
title: 'Item 2',
href: 'https://foo',
path: ['group1', 'item2'],
},
{
@ -86,6 +88,7 @@ describe('<Navigation />', () => {
children: [
{
id: 'item1',
href: 'https://foo',
title: 'Group 1A Item 1',
path: ['group1', 'group1A', 'item1'],
},
@ -97,6 +100,7 @@ describe('<Navigation />', () => {
{
id: 'item1',
title: 'Group 1A_1 Item 1',
href: 'https://foo',
path: ['group1', 'group1A', 'group1A_1', 'item1'],
},
],
@ -132,8 +136,8 @@ describe('<Navigation />', () => {
<Navigation.Group id="root">
<Navigation.Group id="group1">
{/* Title from deeplink */}
<Navigation.Item id="item1" link="item1" />
<Navigation.Item id="item2" link="item1" title="Overwrite deeplink title" />
<Navigation.Item<any> id="item1" link="item1" />
<Navigation.Item<any> id="item2" link="item1" title="Overwrite deeplink title" />
<Navigation.Item id="item3" title="Title in props" />
<Navigation.Item id="item4">Title in children</Navigation.Item>
</Navigation.Group>
@ -225,9 +229,9 @@ describe('<Navigation />', () => {
<Navigation.Group id="root">
<Navigation.Group id="group1">
{/* Title from deeplink */}
<Navigation.Item id="item1" link="item1" />
<Navigation.Item<any> id="item1" link="item1" />
{/* Should not appear */}
<Navigation.Item id="unknownLink" link="unknown" title="Should NOT be there" />
<Navigation.Item<any> id="unknownLink" link="unknown" title="Should NOT be there" />
</Navigation.Group>
</Navigation.Group>
</Navigation>
@ -275,6 +279,83 @@ describe('<Navigation />', () => {
});
});
test('should not render the group if it does not have children AND no href or deeplink', async () => {
const navLinks$: Observable<ChromeNavLink[]> = of([
{
id: 'item1',
title: 'Title from deeplink',
baseUrl: '',
url: '',
href: '',
},
]);
const onProjectNavigationChange = jest.fn();
const { queryByTestId } = render(
<NavigationProvider
{...services}
navLinks$={navLinks$}
onProjectNavigationChange={onProjectNavigationChange}
>
<Navigation homeRef="https://elastic.co">
<Navigation.Group id="root">
<Navigation.Group id="group1">
<Navigation.Item<any> id="item1" link="notRegistered" />
</Navigation.Group>
<Navigation.Group id="group2">
<Navigation.Item<any> id="item1" link="item1" />
</Navigation.Group>
</Navigation.Group>
</Navigation>
</NavigationProvider>
);
expect(await queryByTestId('nav-group-root.group1')).toBeNull();
expect(await queryByTestId('nav-item-root.group2.item1')).toBeVisible();
expect(onProjectNavigationChange).toHaveBeenCalled();
const lastCall =
onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1];
const [navTree] = lastCall;
expect(navTree).toEqual({
homeRef: 'https://elastic.co',
navigationTree: [
{
id: 'root',
path: ['root'],
title: '',
children: [
{
id: 'group1',
path: ['root', 'group1'],
title: '',
},
{
id: 'group2',
path: ['root', 'group2'],
title: '',
children: [
{
id: 'item1',
path: ['root', 'group2', 'item1'],
title: 'Title from deeplink',
deepLink: {
id: 'item1',
title: 'Title from deeplink',
baseUrl: '',
url: '',
href: '',
},
},
],
},
],
},
],
});
});
test('should render custom react element', async () => {
const navLinks$: Observable<ChromeNavLink[]> = of([
{
@ -297,10 +378,10 @@ describe('<Navigation />', () => {
<Navigation homeRef="https://elastic.co">
<Navigation.Group id="root">
<Navigation.Group id="group1">
<Navigation.Item link="item1">
<Navigation.Item<any> link="item1">
<div data-test-subj="my-custom-element">Custom element</div>
</Navigation.Item>
<Navigation.Item id="item2" title="Children prop">
<Navigation.Item id="item2" title="Children prop" href="http://foo">
{(navNode) => <div data-test-subj="my-other-custom-element">{navNode.title}</div>}
</Navigation.Item>
</Navigation.Group>
@ -348,6 +429,7 @@ describe('<Navigation />', () => {
},
{
id: 'item2',
href: 'http://foo',
path: ['root', 'group1', 'item2'],
title: 'Children prop',
renderItem: expect.any(Function),
@ -445,5 +527,71 @@ describe('<Navigation />', () => {
'RecentThis is an exampleAnother example'
);
});
test('should allow href for absolute links', async () => {
const onProjectNavigationChange = jest.fn();
render(
<NavigationProvider {...services} onProjectNavigationChange={onProjectNavigationChange}>
<Navigation homeRef="https://elastic.co">
<Navigation.Group id="group1">
<Navigation.Item id="item1" title="Item 1" href="https://example.com" />
</Navigation.Group>
</Navigation>
</NavigationProvider>
);
expect(onProjectNavigationChange).toHaveBeenCalled();
const lastCall =
onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1];
const [navTreeGenerated] = lastCall;
expect(navTreeGenerated).toEqual({
homeRef: 'https://elastic.co',
navigationTree: [
{
id: 'group1',
path: ['group1'],
title: '',
children: [
{
id: 'item1',
title: 'Item 1',
href: 'https://example.com',
path: ['group1', 'item1'],
},
],
},
],
});
});
test('should throw if href is not an absolute links', async () => {
// We'll mock the console.error to avoid dumping the (expected) error in the console
// source: https://github.com/jestjs/jest/pull/5267#issuecomment-356605468
jest.spyOn(console, 'error');
// @ts-expect-error we're mocking the console so "mockImplementation" exists
// eslint-disable-next-line no-console
console.error.mockImplementation(() => {});
const onProjectNavigationChange = jest.fn();
const expectToThrow = () => {
render(
<NavigationProvider {...services} onProjectNavigationChange={onProjectNavigationChange}>
<Navigation homeRef="https://elastic.co">
<Navigation.Group id="group1">
<Navigation.Item id="item1" title="Item 1" href="../dashboards" />
</Navigation.Group>
</Navigation>
</NavigationProvider>
);
};
expect(expectToThrow).toThrowError('href must be an absolute URL. Node id [item1].');
// @ts-expect-error we're mocking the console so "mockImplementation" exists
// eslint-disable-next-line no-console
console.error.mockRestore();
});
});
});

View file

@ -6,33 +6,40 @@
* Side Public License, v 1.
*/
import React, { FC, useCallback } from 'react';
import React, { useCallback } from 'react';
import type { AppDeepLinkId, NodeDefinition } from '@kbn/core-chrome-browser';
import { getPresets } from '../nav_tree_presets';
import { Navigation } from './navigation';
import type { NavigationGroupPreset, NodeDefinition } from '../types';
import type { NavigationGroupPreset } from '../types';
const navTreePresets = getPresets('all');
export interface Props {
export interface Props<
LinkId extends AppDeepLinkId = AppDeepLinkId,
Id extends string = string,
ChildrenId extends string = Id
> {
preset?: NavigationGroupPreset;
nodeDefinition?: NodeDefinition;
nodeDefinition?: NodeDefinition<LinkId, Id, ChildrenId>;
defaultIsCollapsed?: boolean;
}
export const NavigationBucket: FC<Props> = ({
nodeDefinition: _nodeDefinition,
defaultIsCollapsed,
preset,
}) => {
const nodeDefinition = preset ? navTreePresets[preset] : _nodeDefinition;
export function NavigationBucket<
LinkId extends AppDeepLinkId = AppDeepLinkId,
Id extends string = string,
ChildrenId extends string = Id
>({ nodeDefinition: _nodeDefinition, defaultIsCollapsed, preset }: Props<LinkId, Id, ChildrenId>) {
const nodeDefinition = preset
? (navTreePresets[preset] as NodeDefinition<LinkId, Id, ChildrenId>)
: _nodeDefinition;
if (!nodeDefinition) {
throw new Error('Either preset or nodeDefinition must be defined');
}
const renderItems = useCallback(
(items: NodeDefinition[], isRoot = false) => {
(items: Array<NodeDefinition<LinkId, Id, ChildrenId>>, isRoot = false) => {
return items.map((item) => {
const id = item.id ?? item.link;
@ -64,4 +71,4 @@ export const NavigationBucket: FC<Props> = ({
);
return <>{renderItems([nodeDefinition], true)}</>;
};
}

View file

@ -8,11 +8,12 @@
import React, { createContext, useCallback, useMemo, useContext } from 'react';
import type { AppDeepLinkId } from '@kbn/core-chrome-browser';
import { useInitNavNode } from '../hooks';
import type { NodeProps, RegisterFunction } from '../types';
import { NavigationSectionUI } from './navigation_section_ui';
import { useNavigation } from './navigation';
import { NavigationBucket, Props as NavigationBucketProps } from './navigation_bucket';
import { NavigationBucket, type Props as NavigationBucketProps } from './navigation_bucket';
interface Context {
register: RegisterFunction;
@ -30,12 +31,20 @@ export function useNavigationGroup<T extends boolean = true>(
return context as T extends true ? Context : Context | undefined;
}
export interface Props extends NodeProps {
export interface Props<
LinkId extends AppDeepLinkId = AppDeepLinkId,
Id extends string = string,
ChildrenId extends string = Id
> extends NodeProps<LinkId, Id, ChildrenId> {
unstyled?: boolean;
defaultIsCollapsed?: boolean;
}
function NavigationGroupInternalComp(props: Props) {
function NavigationGroupInternalComp<
LinkId extends AppDeepLinkId = AppDeepLinkId,
Id extends string = string,
ChildrenId extends string = Id
>(props: Props<LinkId, Id, ChildrenId>) {
const navigationContext = useNavigation();
const { children, defaultIsCollapsed, ...node } = props;
const { navNode, registerChildNode, path, childrenNodes } = useInitNavNode(node);
@ -90,10 +99,19 @@ function NavigationGroupInternalComp(props: Props) {
);
}
function NavigationGroupComp(props: Props & NavigationBucketProps) {
function NavigationGroupComp<
LinkId extends AppDeepLinkId = AppDeepLinkId,
Id extends string = string,
ChildrenId extends string = Id
>(props: Props<LinkId, Id, ChildrenId> & NavigationBucketProps) {
if (props.preset) {
const { id, title, link, icon, children, ...rest } = props;
return <NavigationBucket {...rest} />;
return (
<NavigationBucket
preset={props.preset}
nodeDefinition={props.nodeDefinition}
defaultIsCollapsed={props.defaultIsCollapsed}
/>
);
}
const { preset, nodeDefinition, ...rest } = props;

View file

@ -8,11 +8,16 @@
import React, { Fragment, ReactElement, ReactNode, useEffect } from 'react';
import type { AppDeepLinkId } from '@kbn/core-chrome-browser';
import type { ChromeProjectNavigationNodeEnhanced, NodeProps } from '../types';
import { useInitNavNode } from '../hooks';
import { useNavigation } from './navigation';
export interface Props extends NodeProps {
export interface Props<
LinkId extends AppDeepLinkId = AppDeepLinkId,
Id extends string = string,
ChildrenId extends string = Id
> extends NodeProps<LinkId, Id, ChildrenId> {
element?: string;
unstyled?: boolean;
}
@ -21,7 +26,11 @@ function isReactElement(element: ReactNode): element is ReactElement {
return React.isValidElement(element);
}
function NavigationItemComp(props: Props) {
function NavigationItemComp<
LinkId extends AppDeepLinkId = AppDeepLinkId,
Id extends string = string,
ChildrenId extends string = Id
>(props: Props<LinkId, Id, ChildrenId>) {
const navigationContext = useNavigation();
const navNodeRef = React.useRef<ChromeProjectNavigationNodeEnhanced | null>(null);
@ -57,4 +66,4 @@ function NavigationItemComp(props: Props) {
return <Element>{navNode.title}</Element>;
}
export const NavigationItem = React.memo(NavigationItemComp);
export const NavigationItem = React.memo(NavigationItemComp) as typeof NavigationItemComp;

View file

@ -10,6 +10,7 @@ import React, { FC } from 'react';
import {
EuiCollapsibleNavGroup,
EuiIcon,
EuiLink,
EuiSideNav,
EuiSideNavItemType,
EuiText,
@ -18,13 +19,32 @@ import type { BasePathService, NavigateToUrlFn } from '../../../types/internal';
import { navigationStyles as styles } from '../../styles';
import { useNavigation as useServices } from '../../services';
import { ChromeProjectNavigationNodeEnhanced } from '../types';
import { isAbsoluteLink } from '../../utils';
type RenderItem = EuiSideNavItemType<unknown>['renderItem'];
const navigationNodeToEuiItem = (
item: ChromeProjectNavigationNodeEnhanced,
{ navigateToUrl, basePath }: { navigateToUrl: NavigateToUrlFn; basePath: BasePathService }
): EuiSideNavItemType<unknown> => {
const href = item.deepLink?.href ?? item.href;
const href = item.deepLink?.url ?? item.href;
const id = item.path ? item.path.join('.') : item.id;
const isExternal = Boolean(href) && isAbsoluteLink(href!);
const dataTestSubj = `nav-item-${id}`;
const getRenderItem = (): RenderItem | undefined => {
if (!isExternal || item.renderItem) {
return item.renderItem;
}
return () => (
<div className="euiSideNavItemButton" data-test-subj={dataTestSubj}>
<EuiLink href={href} external>
{item.title}
</EuiLink>
</div>
);
};
return {
id,
@ -33,15 +53,15 @@ const navigationNodeToEuiItem = (
href !== undefined
? (event: React.MouseEvent) => {
event.preventDefault();
navigateToUrl(basePath.prepend(href!));
navigateToUrl(href);
}
: undefined,
href,
renderItem: item.renderItem,
renderItem: getRenderItem(),
items: item.children?.map((_item) =>
navigationNodeToEuiItem(_item, { navigateToUrl, basePath })
),
['data-test-subj']: `nav-item-${id}`,
['data-test-subj']: dataTestSubj,
...(item.icon && {
icon: <EuiIcon type={item.icon} size="s" />,
}),
@ -62,6 +82,35 @@ export const NavigationSectionUI: FC<Props> = ({
const { id, title, icon } = navNode;
const { navigateToUrl, basePath } = useServices();
// If the item has no link and no cildren, we don't want to render it
const itemHasLinkOrChildren = (item: ChromeProjectNavigationNodeEnhanced) => {
const hasLink = Boolean(item.deepLink) || Boolean(item.href);
if (hasLink) {
return true;
}
const hasChildren = Boolean(item.children?.length);
if (hasChildren) {
return item.children!.some(itemHasLinkOrChildren);
}
return false;
};
const filteredItems = items.filter(itemHasLinkOrChildren).map((item) => {
if (item.children) {
return {
...item,
children: item.children.filter(itemHasLinkOrChildren),
};
}
return item;
});
const groupHasLink = Boolean(navNode.deepLink) || Boolean(navNode.href);
if (!groupHasLink && !filteredItems.some(itemHasLinkOrChildren)) {
return null;
}
return (
<EuiCollapsibleNavGroup
id={id}
@ -73,7 +122,9 @@ export const NavigationSectionUI: FC<Props> = ({
>
<EuiText color="default">
<EuiSideNav
items={items?.map((item) => navigationNodeToEuiItem(item, { navigateToUrl, basePath }))}
items={filteredItems.map((item) =>
navigationNodeToEuiItem(item, { navigateToUrl, basePath })
)}
css={styles.euiSideNavItems}
/>
</EuiText>

Some files were not shown because too many files have changed in this diff Show more