From 26fe2fe04419122b7ee75a3efb22ab87a2295208 Mon Sep 17 00:00:00 2001 From: Ari Aviran Date: Wed, 5 Jan 2022 11:15:12 +0200 Subject: [PATCH] Add breadcrumbs to CSP UI (#84) --- .../public/application/app.tsx | 2 +- .../public/application/routes.tsx | 2 +- .../public/common/constants.ts | 4 -- .../public/common/navigation/constants.ts | 19 +++++++++ .../public/common/navigation/translations.ts | 20 ++++++++++ .../navigation/types.ts} | 10 ++--- .../common/navigation/use_csp_breadcrumbs.ts | 40 +++++++++++++++++++ .../use_navigate_to_csp_findings.ts | 3 +- .../{ => public}/common/translations.ts | 0 .../public/components/page_template.tsx | 26 ++++++------ .../cloud_posture_score_chart.tsx | 2 +- .../resources_at_risk_chart.tsx | 2 +- .../compliance_dashboard.tsx | 24 ++++++----- .../public/pages/findings/findings.tsx | 3 ++ .../cloud_security_posture/public/plugin.ts | 4 +- 15 files changed, 122 insertions(+), 39 deletions(-) create mode 100644 x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts create mode 100644 x-pack/plugins/cloud_security_posture/public/common/navigation/translations.ts rename x-pack/plugins/cloud_security_posture/public/{application/navigation.ts => common/navigation/types.ts} (53%) create mode 100644 x-pack/plugins/cloud_security_posture/public/common/navigation/use_csp_breadcrumbs.ts rename x-pack/plugins/cloud_security_posture/public/common/{hooks => navigation}/use_navigate_to_csp_findings.ts (86%) rename x-pack/plugins/cloud_security_posture/{ => public}/common/translations.ts (100%) diff --git a/x-pack/plugins/cloud_security_posture/public/application/app.tsx b/x-pack/plugins/cloud_security_posture/public/application/app.tsx index 9f0ae6a25f7c76..8e504c38d196ee 100755 --- a/x-pack/plugins/cloud_security_posture/public/application/app.tsx +++ b/x-pack/plugins/cloud_security_posture/public/application/app.tsx @@ -8,9 +8,9 @@ import React from 'react'; import { I18nProvider } from '@kbn/i18n-react'; import { Router, Redirect, Switch, Route } from 'react-router-dom'; import { QueryClient, QueryClientProvider } from 'react-query'; +import { CSP_DASHBOARD_PATH } from '../common/navigation/constants'; import { routes } from './routes'; import { UnknownRoute } from '../components/unknown_route'; -import { CSP_DASHBOARD_PATH } from '../common/constants'; import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; import type { AppMountParameters, CoreStart } from '../../../../../src/core/public'; import type { CspStart } from '../types'; diff --git a/x-pack/plugins/cloud_security_posture/public/application/routes.tsx b/x-pack/plugins/cloud_security_posture/public/application/routes.tsx index ba2ebe194a9bad..3264fb454c324b 100644 --- a/x-pack/plugins/cloud_security_posture/public/application/routes.tsx +++ b/x-pack/plugins/cloud_security_posture/public/application/routes.tsx @@ -5,8 +5,8 @@ * 2.0. */ import { RouteProps } from 'react-router-dom'; +import { CSP_DASHBOARD_PATH, CSP_FINDINGS_PATH } from '../common/navigation/constants'; import * as pages from '../pages'; -import { CSP_FINDINGS_PATH, CSP_DASHBOARD_PATH } from '../common/constants'; export const routes: readonly RouteProps[] = [ { path: CSP_FINDINGS_PATH, component: pages.Findings }, diff --git a/x-pack/plugins/cloud_security_posture/public/common/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/constants.ts index 5ff3331d3322c2..e7ad29937e49da 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/constants.ts @@ -10,7 +10,3 @@ import { euiPaletteForStatus } from '@elastic/eui'; const [success, warning, danger] = euiPaletteForStatus(3); export const statusColors = { success, warning, danger }; - -export const CSP_ROOT_PATH = '/csp'; -export const CSP_FINDINGS_PATH = '/findings'; -export const CSP_DASHBOARD_PATH = '/dashboard'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts new file mode 100644 index 00000000000000..446478b74a280f --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/common/navigation/constants.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as TEXT from './translations'; +import type { CspNavigationItem } from './types'; + +export const CSP_FINDINGS_PATH = '/findings'; +export const CSP_DASHBOARD_PATH = '/dashboard'; + +type NavigableScreens = 'dashboard' | 'findings'; + +export const allNavigationItems: Record = { + dashboard: { name: TEXT.DASHBOARD, path: CSP_DASHBOARD_PATH }, + findings: { name: TEXT.FINDINGS, path: CSP_FINDINGS_PATH }, +}; diff --git a/x-pack/plugins/cloud_security_posture/public/common/navigation/translations.ts b/x-pack/plugins/cloud_security_posture/public/common/navigation/translations.ts new file mode 100644 index 00000000000000..81aaafacca9f22 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/common/navigation/translations.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const CLOUD_POSTURE = i18n.translate('xpack.csp.navigation.cloudPosture', { + defaultMessage: 'Cloud Posture', +}); + +export const FINDINGS = i18n.translate('xpack.csp.navigation.findings', { + defaultMessage: 'Findings', +}); + +export const DASHBOARD = i18n.translate('xpack.csp.navigation.dashboard', { + defaultMessage: 'Dashboard', +}); diff --git a/x-pack/plugins/cloud_security_posture/public/application/navigation.ts b/x-pack/plugins/cloud_security_posture/public/common/navigation/types.ts similarity index 53% rename from x-pack/plugins/cloud_security_posture/public/application/navigation.ts rename to x-pack/plugins/cloud_security_posture/public/common/navigation/types.ts index 2e290da24d373b..ab80a53f764e69 100644 --- a/x-pack/plugins/cloud_security_posture/public/application/navigation.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/navigation/types.ts @@ -5,9 +5,7 @@ * 2.0. */ -import { CSP_FINDINGS_PATH, CSP_DASHBOARD_PATH } from '../common/constants'; - -export const navigationLinks = [ - { name: 'Dashboard', path: CSP_DASHBOARD_PATH }, - { name: 'Findings', path: CSP_FINDINGS_PATH }, -] as const; +export interface CspNavigationItem { + name: string; + path: string; +} diff --git a/x-pack/plugins/cloud_security_posture/public/common/navigation/use_csp_breadcrumbs.ts b/x-pack/plugins/cloud_security_posture/public/common/navigation/use_csp_breadcrumbs.ts new file mode 100644 index 00000000000000..f11b09bc559dce --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/common/navigation/use_csp_breadcrumbs.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ChromeBreadcrumb, CoreStart } from 'kibana/public'; +import { useEffect } from 'react'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { PLUGIN_ID } from '../../../common'; +import type { CspNavigationItem } from './types'; +import { CLOUD_POSTURE } from './translations'; + +export const useCspBreadcrumbs = (breadcrumbs: CspNavigationItem[]) => { + const { + services: { + chrome: { setBreadcrumbs }, + application: { getUrlForApp }, + }, + } = useKibana(); + + useEffect(() => { + const cspPath = getUrlForApp(PLUGIN_ID); + const additionalBreadCrumbs: ChromeBreadcrumb[] = breadcrumbs.map((breadcrumb) => ({ + text: breadcrumb.name, + path: breadcrumb.path.startsWith('/') + ? `${cspPath}${breadcrumb.path}` + : `${cspPath}/${breadcrumb.path}`, + })); + + setBreadcrumbs([ + { + text: CLOUD_POSTURE, + href: cspPath, + }, + ...additionalBreadCrumbs, + ]); + }, [getUrlForApp, setBreadcrumbs, breadcrumbs]); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_to_csp_findings.ts b/x-pack/plugins/cloud_security_posture/public/common/navigation/use_navigate_to_csp_findings.ts similarity index 86% rename from x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_to_csp_findings.ts rename to x-pack/plugins/cloud_security_posture/public/common/navigation/use_navigate_to_csp_findings.ts index 9b044110988017..30e7533391e358 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_navigate_to_csp_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/navigation/use_navigate_to_csp_findings.ts @@ -6,6 +6,7 @@ */ import { useHistory } from 'react-router-dom'; +import { CSP_FINDINGS_PATH } from './constants'; export const useNavigateToCSPFindings = () => { const history = useHistory(); @@ -13,7 +14,7 @@ export const useNavigateToCSPFindings = () => { return { navigate: (query: string) => history.push({ - pathname: '/findings', + pathname: CSP_FINDINGS_PATH, search: new URLSearchParams([['query', query]].filter((p) => !!p[1])).toString(), }), }; diff --git a/x-pack/plugins/cloud_security_posture/common/translations.ts b/x-pack/plugins/cloud_security_posture/public/common/translations.ts similarity index 100% rename from x-pack/plugins/cloud_security_posture/common/translations.ts rename to x-pack/plugins/cloud_security_posture/public/common/translations.ts diff --git a/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx b/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx index 99fa91c6df10fb..3ad44c0277fa1c 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/page_template.tsx @@ -11,22 +11,22 @@ import { KibanaPageTemplate, KibanaPageTemplateProps, } from '../../../../../src/plugins/kibana_react/public'; -import { navigationLinks } from '../application/navigation'; -import { CLOUD_SECURITY_POSTURE } from '../../common/translations'; +import { allNavigationItems } from '../common/navigation/constants'; +import { CLOUD_SECURITY_POSTURE } from '../common/translations'; const activeItemStyle = { fontWeight: 700 }; -const navItems: NonNullable['items'] = navigationLinks.map( - (route) => ({ - id: route.name, - ...route, - renderItem: () => ( - - {route.name} - - ), - }) -); +const navItems: NonNullable['items'] = Object.values( + allNavigationItems +).map((route) => ({ + id: route.path, + ...route, + renderItem: () => ( + + {route.name} + + ), +})); const defaultProps: KibanaPageTemplateProps = { solutionNav: { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx index 0b24e7ec2a647b..71d4285c1a2396 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/cloud_posture_score_chart.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { Chart, Datum, Partition, PartitionLayout, Settings } from '@elastic/charts'; import { EuiText } from '@elastic/eui'; -import { useNavigateToCSPFindings } from '../../../common/hooks/use_navigate_to_csp_findings'; +import { useNavigateToCSPFindings } from '../../../common/navigation/use_navigate_to_csp_findings'; import type { BenchmarkStats } from '../../../../common/types'; import { statusColors } from '../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/resources_at_risk_chart.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/resources_at_risk_chart.tsx index 1942aaaf5d86c8..b7f89bb58ccdb9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/resources_at_risk_chart.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/resources_at_risk_chart.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { Axis, BarSeries, Chart, Settings } from '@elastic/charts'; import { euiPaletteForStatus } from '@elastic/eui'; -import { useNavigateToCSPFindings } from '../../../common/hooks/use_navigate_to_csp_findings'; +import { useNavigateToCSPFindings } from '../../../common/navigation/use_navigate_to_csp_findings'; import { CloudPostureStats } from '../../../../common/types'; export function sortAscending(getter: (x: T) => number | string) { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx index 679a193b43fb84..fb84c203392759 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx @@ -7,6 +7,8 @@ import React from 'react'; import { EuiSpacer, EuiTitle } from '@elastic/eui'; +import { allNavigationItems } from '../../common/navigation/constants'; +import { useCspBreadcrumbs } from '../../common/navigation/use_csp_breadcrumbs'; import { SummarySection } from './dashboard_sections/summary_section'; import { BenchmarksSection } from './dashboard_sections/benchmarks_section'; import { useCloudPostureStatsApi } from '../../common/api'; @@ -34,12 +36,16 @@ const CompliancePage = () => { ); }; -export const ComplianceDashboard = () => ( - - - -); +export const ComplianceDashboard = () => { + useCspBreadcrumbs([allNavigationItems.dashboard]); + + return ( + + + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx index aa9c268252f02c..292a2b605a7fe6 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/findings.tsx @@ -7,6 +7,8 @@ import React from 'react'; import { EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui'; import type { EuiPageHeaderProps } from '@elastic/eui'; +import { allNavigationItems } from '../../common/navigation/constants'; +import { useCspBreadcrumbs } from '../../common/navigation/use_csp_breadcrumbs'; import { FindingsTableContainer } from './findings_container'; import { CspPageTemplate } from '../../components/page_template'; import { useKubebeatDataView } from './utils'; @@ -18,6 +20,7 @@ const pageHeader: EuiPageHeaderProps = { export const Findings = () => { const dataView = useKubebeatDataView(); + useCspBreadcrumbs([allNavigationItems.findings]); return ( diff --git a/x-pack/plugins/cloud_security_posture/public/plugin.ts b/x-pack/plugins/cloud_security_posture/public/plugin.ts index 347a982e63ef1f..022edc2a5c1b50 100755 --- a/x-pack/plugins/cloud_security_posture/public/plugin.ts +++ b/x-pack/plugins/cloud_security_posture/public/plugin.ts @@ -8,14 +8,14 @@ import type { AppMountParameters, CoreSetup, CoreStart, Plugin } from '../../../../src/core/public'; import type { CspSetup, CspStart, CspPluginSetup, CspPluginStart } from './types'; import { AppNavLinkStatus, AppStatus } from '../../../../src/core/public'; -import { PLUGIN_NAME } from '../common'; +import { PLUGIN_NAME, PLUGIN_ID } from '../common'; export class CspPlugin implements Plugin { public setup(core: CoreSetup, plugins: CspPluginSetup): CspSetup { // Register an application into the side navigation menu core.application.register({ - id: 'csp', + id: PLUGIN_ID, title: PLUGIN_NAME, status: AppStatus.accessible, navLinkStatus: AppNavLinkStatus.hidden,