Skip to content

Commit

Permalink
[Security Solution] New Side nav integrating links config (#132210)
Browse files Browse the repository at this point in the history
* Update navigation landing pages to use appLinks config

* align app links changes

* link configs refactor to use updater$

* navigation panel categories

* test and type fixes

* [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix'

* types changes

* shared style change moved to a separate PR

* use old deep links

* minor changes after ux meeting

* add links filtering

* remove duplicated categories

* temporary increase of plugin size limit

* swap management links order

* improve performance closing nav panel

* test updated

* host isolation page filterd and some improvements

* remove async from plugin start

* move links register from start to mount

* restore size limits

* Fix use_show_timeline unit tests

Co-authored-by: Pablo Neves Machado <[email protected]>
Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
3 people authored May 20, 2022
1 parent 92ac7f9 commit 1c2eb9f
Show file tree
Hide file tree
Showing 40 changed files with 1,710 additions and 1,121 deletions.
39 changes: 38 additions & 1 deletion x-pack/plugins/security_solution/public/app/deep_links/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import { i18n } from '@kbn/i18n';
import { get } from 'lodash';
import { LicenseType } from '@kbn/licensing-plugin/common/types';
import { getCasesDeepLinks } from '@kbn/cases-plugin/public';
import { AppDeepLink, AppNavLinkStatus, Capabilities } from '@kbn/core/public';
import { AppDeepLink, AppNavLinkStatus, AppUpdater, Capabilities } from '@kbn/core/public';
import { Subject } from 'rxjs';
import { SecurityPageName } from '../types';
import {
OVERVIEW,
Expand Down Expand Up @@ -63,6 +64,8 @@ import {
RULES_CREATE_PATH,
} from '../../../common/constants';
import { ExperimentalFeatures } from '../../../common/experimental_features';
import { subscribeAppLinks } from '../../common/links';
import { AppLinkItems } from '../../common/links/types';

const FEATURE = {
general: `${SERVER_APP_ID}.show`,
Expand Down Expand Up @@ -553,3 +556,37 @@ export function isPremiumLicense(licenseType?: LicenseType): boolean {
licenseType === 'trial'
);
}

/**
* New deep links code starts here.
* All the code above will be removed once the appLinks migration is over.
* The code below manages the new implementation using the unified appLinks.
*/

const formatDeepLinks = (appLinks: AppLinkItems): AppDeepLink[] =>
appLinks.map((appLink) => ({
id: appLink.id,
path: appLink.path,
title: appLink.title,
navLinkStatus: appLink.globalNavEnabled ? AppNavLinkStatus.visible : AppNavLinkStatus.hidden,
searchable: !appLink.globalSearchDisabled,
...(appLink.globalSearchKeywords != null ? { keywords: appLink.globalSearchKeywords } : {}),
...(appLink.globalNavOrder != null ? { order: appLink.globalNavOrder } : {}),
...(appLink.links && appLink.links?.length
? {
deepLinks: formatDeepLinks(appLink.links),
}
: {}),
}));

/**
* Registers any change in appLinks to be updated in app deepLinks
*/
export const registerDeepLinksUpdater = (appUpdater$: Subject<AppUpdater>) => {
subscribeAppLinks((appLinks) => {
appUpdater$.next(() => ({
navLinkStatus: AppNavLinkStatus.hidden, // needed to prevent main security link to switch to visible after update
deepLinks: formatDeepLinks(appLinks),
}));
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { gutterTimeline } from '../../../common/lib/helpers';
import { useKibana } from '../../../common/lib/kibana';
import { useShowPagesWithEmptyView } from '../../../common/utils/empty_view/use_show_pages_with_empty_view';
import { useIsPolicySettingsBarVisible } from '../../../management/pages/policy/view/policy_hooks';
import { useIsGroupedNavigationEnabled } from '../../../common/components/navigation/helpers';

const NO_DATA_PAGE_MAX_WIDTH = 950;

Expand All @@ -44,8 +45,7 @@ const NO_DATA_PAGE_TEMPLATE_PROPS = {
*/
const StyledKibanaPageTemplate = styled(KibanaPageTemplate)<{
$isShowingTimelineOverlay?: boolean;
$isTimelineBottomBarVisible?: boolean;
$isPolicySettingsVisible?: boolean;
$addBottomPadding?: boolean;
}>`
.${BOTTOM_BAR_CLASSNAME} {
animation: 'none !important'; // disable the default bottom bar slide animation
Expand All @@ -63,19 +63,8 @@ const StyledKibanaPageTemplate = styled(KibanaPageTemplate)<{
}
// If the bottom bar is visible add padding to the navigation
${({ $isTimelineBottomBarVisible }) =>
$isTimelineBottomBarVisible &&
`
@media (min-width: 768px) {
.kbnPageTemplateSolutionNav {
padding-bottom: ${gutterTimeline};
}
}
`}
// If the policy settings bottom bar is visible add padding to the navigation
${({ $isPolicySettingsVisible }) =>
$isPolicySettingsVisible &&
${({ $addBottomPadding }) =>
$addBottomPadding &&
`
@media (min-width: 768px) {
.kbnPageTemplateSolutionNav {
Expand All @@ -98,6 +87,9 @@ export const SecuritySolutionTemplateWrapper: React.FC<SecuritySolutionPageWrapp
const { show: isShowingTimelineOverlay } = useDeepEqualSelector((state) =>
getTimelineShowStatus(state, TimelineId.active)
);
const isGroupedNavEnabled = useIsGroupedNavigationEnabled();
const addBottomPadding =
isTimelineBottomBarVisible || isPolicySettingsVisible || isGroupedNavEnabled;

const userHasSecuritySolutionVisible = useKibana().services.application.capabilities.siem.show;
const showEmptyState = useShowPagesWithEmptyView();
Expand All @@ -117,9 +109,8 @@ export const SecuritySolutionTemplateWrapper: React.FC<SecuritySolutionPageWrapp
*/
return (
<StyledKibanaPageTemplate
$isTimelineBottomBarVisible={isTimelineBottomBarVisible}
$addBottomPadding={addBottomPadding}
$isShowingTimelineOverlay={isShowingTimelineOverlay}
$isPolicySettingsVisible={isPolicySettingsVisible}
bottomBarProps={SecuritySolutionBottomBarProps}
bottomBar={
userHasSecuritySolutionVisible && <SecuritySolutionBottomBar onAppLeave={onAppLeave} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const HOSTS = i18n.translate('xpack.securitySolution.navigation.hosts', {
});

export const GETTING_STARTED = i18n.translate('xpack.securitySolution.navigation.gettingStarted', {
defaultMessage: 'Getting started',
defaultMessage: 'Get started',
});

export const THREAT_HUNTING = i18n.translate('xpack.securitySolution.navigation.threatHunting', {
Expand Down
12 changes: 7 additions & 5 deletions x-pack/plugins/security_solution/public/cases/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
*/

import { getCasesDeepLinks } from '@kbn/cases-plugin/public';
import { CASES_PATH, SecurityPageName } from '../../common/constants';
import { FEATURE, LinkItem } from '../common/links/types';
import { CASES_FEATURE_ID, CASES_PATH, SecurityPageName } from '../../common/constants';
import { LinkItem } from '../common/links/types';

export const getCasesLinkItems = (): LinkItem => {
const casesLinks = getCasesDeepLinks<LinkItem>({
Expand All @@ -16,15 +16,17 @@ export const getCasesLinkItems = (): LinkItem => {
[SecurityPageName.case]: {
globalNavEnabled: true,
globalNavOrder: 9006,
features: [FEATURE.casesRead],
capabilities: [`${CASES_FEATURE_ID}.read_cases`],
},
[SecurityPageName.caseConfigure]: {
features: [FEATURE.casesCrud],
capabilities: [`${CASES_FEATURE_ID}.crud_cases`],
licenseType: 'gold',
sideNavDisabled: true,
hideTimeline: true,
},
[SecurityPageName.caseCreate]: {
features: [FEATURE.casesCrud],
capabilities: [`${CASES_FEATURE_ID}.crud_cases`],
sideNavDisabled: true,
hideTimeline: true,
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@

import { renderHook } from '@testing-library/react-hooks';
import { SecurityPageName } from '../../../app/types';
import { NavLinkItem } from '../../links/types';
import { AppLinkItems } from '../../links';
import { TestProviders } from '../../mock';
import { useAppNavLinks, useAppRootNavLink } from './nav_links';
import { NavLinkItem } from './types';

const mockNavLinks = [
const mockNavLinks: AppLinkItems = [
{
description: 'description',
id: SecurityPageName.administration,
Expand All @@ -22,6 +23,10 @@ const mockNavLinks = [
links: [],
path: '/path_2',
title: 'title 2',
sideNavDisabled: true,
landingIcon: 'someicon',
landingImage: 'someimage',
skipUrlState: true,
},
],
path: '/path',
Expand All @@ -30,7 +35,7 @@ const mockNavLinks = [
];

jest.mock('../../links', () => ({
getNavLinkItems: () => mockNavLinks,
useAppLinks: () => mockNavLinks,
}));

const renderUseAppNavLinks = () =>
Expand All @@ -44,11 +49,47 @@ const renderUseAppRootNavLink = (id: SecurityPageName) =>
describe('useAppNavLinks', () => {
it('should return all nav links', () => {
const { result } = renderUseAppNavLinks();
expect(result.current).toEqual(mockNavLinks);
expect(result.current).toMatchInlineSnapshot(`
Array [
Object {
"description": "description",
"id": "administration",
"links": Array [
Object {
"description": "description 2",
"disabled": true,
"icon": "someicon",
"id": "endpoints",
"image": "someimage",
"skipUrlState": true,
"title": "title 2",
},
],
"title": "title",
},
]
`);
});

it('should return a root nav links', () => {
const { result } = renderUseAppRootNavLink(SecurityPageName.administration);
expect(result.current).toEqual(mockNavLinks[0]);
expect(result.current).toMatchInlineSnapshot(`
Object {
"description": "description",
"id": "administration",
"links": Array [
Object {
"description": "description 2",
"disabled": true,
"icon": "someicon",
"id": "endpoints",
"image": "someimage",
"skipUrlState": true,
"title": "title 2",
},
],
"title": "title",
}
`);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,35 @@
* 2.0.
*/

import { useKibana } from '../../lib/kibana';
import { useEnableExperimental } from '../../hooks/use_experimental_features';
import { useLicense } from '../../hooks/use_license';
import { getNavLinkItems } from '../../links';
import { useMemo } from 'react';
import { useAppLinks } from '../../links';
import type { SecurityPageName } from '../../../app/types';
import type { NavLinkItem } from '../../links/types';
import { NavLinkItem } from './types';
import { AppLinkItems } from '../../links/types';

export const useAppNavLinks = (): NavLinkItem[] => {
const license = useLicense();
const enableExperimental = useEnableExperimental();
const capabilities = useKibana().services.application.capabilities;

return getNavLinkItems({ enableExperimental, license, capabilities });
const appLinks = useAppLinks();
const navLinks = useMemo(() => formatNavLinkItems(appLinks), [appLinks]);
return navLinks;
};

export const useAppRootNavLink = (linkId: SecurityPageName): NavLinkItem | undefined => {
return useAppNavLinks().find(({ id }) => id === linkId);
};

const formatNavLinkItems = (appLinks: AppLinkItems): NavLinkItem[] =>
appLinks.map((link) => ({
id: link.id,
title: link.title,
...(link.categories != null ? { categories: link.categories } : {}),
...(link.description != null ? { description: link.description } : {}),
...(link.sideNavDisabled === true ? { disabled: true } : {}),
...(link.landingIcon != null ? { icon: link.landingIcon } : {}),
...(link.landingImage != null ? { image: link.landingImage } : {}),
...(link.skipUrlState != null ? { skipUrlState: link.skipUrlState } : {}),
...(link.links && link.links.length
? {
links: formatNavLinkItems(link.links),
}
: {}),
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* 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 React, { SVGProps } from 'react';

export const EuiIconLaunch: React.FC<SVGProps<SVGSVGElement>> = ({ ...props }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={16}
height={16}
fill="none"
viewBox="0 0 16 16"
{...props}
>
<path d="M14.5 1.5L15 1.5L15 0.999999L14.5 0.999999L14.5 1.5ZM2.50018 7L2.15128 6.64186C1.98519 6.80366 1.95214 7.05842 2.07144 7.25725L2.50018 7ZM8.99958 13.5L8.74229 13.9287C8.93901 14.0468 9.19083 14.0158 9.35308 13.8536L8.99958 13.5ZM4.00018 9.5L3.57144 9.75725C3.59247 9.7923 3.61772 9.82465 3.64663 9.85355L4.00018 9.5ZM6.50018 12L6.14663 12.3536C6.17552 12.3824 6.20785 12.4077 6.24289 12.4287L6.50018 12ZM10.7526 8.71583L11.1263 9.04802L10.7526 8.71583ZM10.5 9.3802L10 9.3802L10.5 9.3802ZM7.28418 5.24741L7.61636 5.62111L7.28418 5.24741ZM6.61982 5.5L6.61982 6L6.61982 5.5ZM10.5 11.5857L11 11.5857L10.5 11.5857ZM10.207 12.2929L10.5605 12.6465L10.207 12.2929ZM14.5 2.599L15 2.599L14.5 2.599ZM13.401 1.5L13.401 0.999999L13.401 1.5ZM13.401 2L14.5 2L14.5 0.999999L13.401 0.999999L13.401 2ZM14 1.5L14 2.599L15 2.599L15 1.5L14 1.5ZM7.61636 5.62111L10.4114 3.13666L9.74701 2.38925L6.952 4.8737L7.61636 5.62111ZM4.44648 6L6.61982 6L6.61981 5L4.44648 5L4.44648 6ZM2.84909 7.35814L4.09758 6.14186L3.39977 5.42557L2.15128 6.64186L2.84909 7.35814ZM12.8634 5.58864L10.3789 8.38365L11.1263 9.04802L13.6108 6.253L12.8634 5.58864ZM10 9.3802L10 11.5857L11 11.5857L11 9.3802L10 9.3802ZM9.85352 11.9393L8.64608 13.1464L9.35308 13.8536L10.5605 12.6465L9.85352 11.9393ZM6.85374 11.6464L4.35374 9.14645L3.64663 9.85355L6.14663 12.3536L6.85374 11.6464ZM4.42893 9.24275L2.92893 6.74275L2.07144 7.25725L3.57144 9.75725L4.42893 9.24275ZM9.25687 13.0713L6.75748 11.5713L6.24289 12.4287L8.74229 13.9287L9.25687 13.0713ZM10.3789 8.38365C10.1348 8.65823 10 9.01283 10 9.3802L11 9.3802C11 9.25774 11.045 9.13954 11.1263 9.04802L10.3789 8.38365ZM4.44648 5C4.05544 5 3.67986 5.1527 3.39977 5.42557L4.09758 6.14186C4.19094 6.0509 4.31614 6 4.44648 6L4.44648 5ZM6.952 4.8737C6.86047 4.95506 6.74227 5 6.61981 5L6.61982 6C6.98719 6 7.34178 5.86518 7.61636 5.62111L6.952 4.8737ZM10 11.5857C10 11.7183 9.94731 11.8455 9.85352 11.9393L10.5605 12.6465C10.8419 12.3652 11 11.9836 11 11.5857L10 11.5857ZM14 2.599C14 3.70112 13.5956 4.76491 12.8634 5.58864L13.6108 6.253C14.5057 5.24622 15 3.94603 15 2.599L14 2.599ZM13.401 0.999999C12.054 0.999999 10.7538 1.49433 9.74701 2.38925L10.4114 3.13666C11.2351 2.40445 12.2989 2 13.401 2L13.401 0.999999Z" />
<path d="M2.5 10.5L1.5 12.5V14.5H3.5L5.5 13.5" strokeLinecap="round" strokeLinejoin="round" />
<path d="M1 14.1667L2.5 10.5L5.5 13.5L1.83333 15H1V14.1667Z" />
<circle cx="10" cy="6" r="1" />
<path d="M5.5 10.5L8 8" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export { SecuritySideNav } from './security_side_nav';
Loading

0 comments on commit 1c2eb9f

Please sign in to comment.