mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Nav unified show timeline (#131811)
* Update useShowTimeline to work with new grouped navigation * Fix bundle size * Fix broken unit tests * Please code review * Fix create rules deepLink * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
e4a365a298
commit
fa7df7983c
20 changed files with 195 additions and 86 deletions
|
@ -108,6 +108,7 @@ export enum SecurityPageName {
|
|||
overview = 'overview',
|
||||
policies = 'policy',
|
||||
rules = 'rules',
|
||||
rulesCreate = 'rules-create',
|
||||
timelines = 'timelines',
|
||||
timelinesTemplates = 'timelines-templates',
|
||||
trustedApps = 'trusted_apps',
|
||||
|
@ -119,7 +120,7 @@ export enum SecurityPageName {
|
|||
sessions = 'sessions',
|
||||
usersEvents = 'users-events',
|
||||
usersExternalAlerts = 'users-external_alerts',
|
||||
threatHuntingLanding = 'threat-hunting',
|
||||
threatHuntingLanding = 'threat_hunting',
|
||||
dashboardsLanding = 'dashboards',
|
||||
}
|
||||
|
||||
|
@ -134,6 +135,7 @@ export const DETECTION_RESPONSE_PATH = '/detection_response' as const;
|
|||
export const DETECTIONS_PATH = '/detections' as const;
|
||||
export const ALERTS_PATH = '/alerts' as const;
|
||||
export const RULES_PATH = '/rules' as const;
|
||||
export const RULES_CREATE_PATH = `${RULES_PATH}/create` as const;
|
||||
export const EXCEPTIONS_PATH = '/exceptions' as const;
|
||||
export const HOSTS_PATH = '/hosts' as const;
|
||||
export const USERS_PATH = '/users' as const;
|
||||
|
|
|
@ -35,6 +35,7 @@ import {
|
|||
GETTING_STARTED,
|
||||
THREAT_HUNTING,
|
||||
DASHBOARDS,
|
||||
CREATE_NEW_RULE,
|
||||
} from '../translations';
|
||||
import {
|
||||
OVERVIEW_PATH,
|
||||
|
@ -59,6 +60,7 @@ import {
|
|||
THREAT_HUNTING_PATH,
|
||||
DASHBOARDS_PATH,
|
||||
MANAGE_PATH,
|
||||
RULES_CREATE_PATH,
|
||||
} from '../../../common/constants';
|
||||
import { ExperimentalFeatures } from '../../../common/experimental_features';
|
||||
|
||||
|
@ -183,6 +185,15 @@ export const securitySolutionsDeepLinks: SecuritySolutionDeepLink[] = [
|
|||
}),
|
||||
],
|
||||
searchable: true,
|
||||
deepLinks: [
|
||||
{
|
||||
id: SecurityPageName.rulesCreate,
|
||||
title: CREATE_NEW_RULE,
|
||||
path: RULES_CREATE_PATH,
|
||||
navLinkStatus: AppNavLinkStatus.hidden,
|
||||
searchable: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.exceptions,
|
||||
|
|
|
@ -110,6 +110,10 @@ export const BLOCKLIST = i18n.translate('xpack.securitySolution.navigation.block
|
|||
defaultMessage: 'Blocklist',
|
||||
});
|
||||
|
||||
export const CREATE_NEW_RULE = i18n.translate('xpack.securitySolution.navigation.newRuleTitle', {
|
||||
defaultMessage: 'Create new rule',
|
||||
});
|
||||
|
||||
export const GO_TO_DOCUMENTATION = i18n.translate(
|
||||
'xpack.securitySolution.goToDocumentationButton',
|
||||
{
|
||||
|
|
|
@ -21,9 +21,11 @@ export const getCasesLinkItems = (): LinkItem => {
|
|||
[SecurityPageName.caseConfigure]: {
|
||||
features: [FEATURE.casesCrud],
|
||||
licenseType: 'gold',
|
||||
hideTimeline: true,
|
||||
},
|
||||
[SecurityPageName.caseCreate]: {
|
||||
features: [FEATURE.casesCrud],
|
||||
hideTimeline: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -11,8 +11,6 @@ export const getDetectionEngineUrl = (search?: string) => `${appendSearch(search
|
|||
|
||||
export const getRulesUrl = (search?: string) => `${appendSearch(search)}`;
|
||||
|
||||
export const getCreateRuleUrl = (search?: string) => `/create${appendSearch(search)}`;
|
||||
|
||||
export const getRuleDetailsUrl = (detailName: string, search?: string) =>
|
||||
`/id/${detailName}${appendSearch(search)}`;
|
||||
|
||||
|
|
|
@ -83,7 +83,9 @@ const isAdminRoutes = (spyState: RouteSpyState): spyState is AdministrationRoute
|
|||
spyState != null && spyState.pageName === SecurityPageName.administration;
|
||||
|
||||
const isRulesRoutes = (spyState: RouteSpyState): spyState is AdministrationRouteSpyState =>
|
||||
spyState != null && spyState.pageName === SecurityPageName.rules;
|
||||
spyState != null &&
|
||||
(spyState.pageName === SecurityPageName.rules ||
|
||||
spyState.pageName === SecurityPageName.rulesCreate);
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
export const getBreadcrumbsForRoute = (
|
||||
|
|
|
@ -30,6 +30,7 @@ import { SourcererScopeName, SourcererUrlState } from '../../store/sourcerer/mod
|
|||
export const isDetectionsPages = (pageName: string) =>
|
||||
pageName === SecurityPageName.alerts ||
|
||||
pageName === SecurityPageName.rules ||
|
||||
pageName === SecurityPageName.rulesCreate ||
|
||||
pageName === SecurityPageName.exceptions;
|
||||
|
||||
export const decodeRisonUrlState = <T>(value: string | undefined): T | null => {
|
||||
|
@ -103,7 +104,7 @@ export const getUrlType = (pageName: string): UrlStateType => {
|
|||
return 'network';
|
||||
} else if (pageName === SecurityPageName.alerts) {
|
||||
return 'alerts';
|
||||
} else if (pageName === SecurityPageName.rules) {
|
||||
} else if (pageName === SecurityPageName.rules || pageName === SecurityPageName.rulesCreate) {
|
||||
return 'rules';
|
||||
} else if (pageName === SecurityPageName.exceptions) {
|
||||
return 'exceptions';
|
||||
|
|
|
@ -33,6 +33,8 @@ export const appLinks: Readonly<LinkItem[]> = Object.freeze([
|
|||
}),
|
||||
],
|
||||
links: [hostsLinks, networkLinks, usersLinks],
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
},
|
||||
timelinesLinks,
|
||||
getCasesLinkItems(),
|
||||
|
|
|
@ -98,9 +98,11 @@ const threatHuntingLinkInfo = {
|
|||
features: ['siem.show'],
|
||||
globalNavEnabled: false,
|
||||
globalSearchKeywords: ['Threat hunting'],
|
||||
id: 'threat-hunting',
|
||||
id: 'threat_hunting',
|
||||
path: '/threat_hunting',
|
||||
title: 'Threat Hunting',
|
||||
hideTimeline: true,
|
||||
skipUrlState: true,
|
||||
};
|
||||
|
||||
const hostsLinkInfo = {
|
||||
|
|
|
@ -155,7 +155,6 @@ const getNormalizedLinks = (
|
|||
* Normalized indexed version of the global `links` array, referencing the parent by id, instead of having nested links children
|
||||
*/
|
||||
const normalizedLinks: Readonly<NormalizedLinks> = Object.freeze(getNormalizedLinks(appLinks));
|
||||
|
||||
/**
|
||||
* Returns the `NormalizedLink` from a link id parameter.
|
||||
* The object reference is frozen to make sure it is not mutated by the caller.
|
||||
|
@ -193,3 +192,7 @@ export const getAncestorLinksInfo = (id: SecurityPageName): LinkInfo[] => {
|
|||
export const needsUrlState = (id: SecurityPageName): boolean => {
|
||||
return !getNormalizedLink(id).skipUrlState;
|
||||
};
|
||||
|
||||
export const getLinksWithHiddenTimeline = (): LinkInfo[] => {
|
||||
return Object.values(normalizedLinks).filter((link) => link.hideTimeline);
|
||||
};
|
||||
|
|
|
@ -58,6 +58,7 @@ export interface LinkItem {
|
|||
links?: LinkItem[];
|
||||
path: string;
|
||||
skipUrlState?: boolean; // defaults to false
|
||||
hideTimeline?: boolean; // defaults to false
|
||||
title: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,40 +17,96 @@ jest.mock('react-router-dom', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const mockedUseIsGroupedNavigationEnabled = jest.fn();
|
||||
|
||||
jest.mock('../../components/navigation/helpers', () => ({
|
||||
useIsGroupedNavigationEnabled: () => mockedUseIsGroupedNavigationEnabled(),
|
||||
}));
|
||||
|
||||
describe('use show timeline', () => {
|
||||
it('shows timeline for routes on default', async () => {
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([true]);
|
||||
describe('useIsGroupedNavigationEnabled false', () => {
|
||||
beforeAll(() => {
|
||||
mockedUseIsGroupedNavigationEnabled.mockReturnValue(false);
|
||||
});
|
||||
|
||||
it('shows timeline for routes on default', async () => {
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([true]);
|
||||
});
|
||||
});
|
||||
|
||||
it('hides timeline for blacklist routes', async () => {
|
||||
mockUseLocation.mockReturnValueOnce({ pathname: '/rules/create' });
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([false]);
|
||||
});
|
||||
});
|
||||
it('shows timeline for partial blacklist routes', async () => {
|
||||
mockUseLocation.mockReturnValueOnce({ pathname: '/rules' });
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([true]);
|
||||
});
|
||||
});
|
||||
it('hides timeline for sub blacklist routes', async () => {
|
||||
mockUseLocation.mockReturnValueOnce({ pathname: '/administration/policy' });
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([false]);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('hides timeline for blacklist routes', async () => {
|
||||
mockUseLocation.mockReturnValueOnce({ pathname: '/rules/create' });
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([false]);
|
||||
|
||||
describe('useIsGroupedNavigationEnabled true', () => {
|
||||
beforeAll(() => {
|
||||
mockedUseIsGroupedNavigationEnabled.mockReturnValue(true);
|
||||
});
|
||||
});
|
||||
it('shows timeline for partial blacklist routes', async () => {
|
||||
mockUseLocation.mockReturnValueOnce({ pathname: '/rules' });
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([true]);
|
||||
|
||||
it('shows timeline for routes on default', async () => {
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([true]);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('hides timeline for sub blacklist routes', async () => {
|
||||
mockUseLocation.mockReturnValueOnce({ pathname: '/administration/policy' });
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([false]);
|
||||
|
||||
it('hides timeline for blacklist routes', async () => {
|
||||
mockUseLocation.mockReturnValueOnce({ pathname: '/rules/create' });
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([false]);
|
||||
});
|
||||
});
|
||||
it('shows timeline for partial blacklist routes', async () => {
|
||||
mockUseLocation.mockReturnValueOnce({ pathname: '/rules' });
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([true]);
|
||||
});
|
||||
});
|
||||
it('hides timeline for sub blacklist routes', async () => {
|
||||
mockUseLocation.mockReturnValueOnce({ pathname: '/administration/policy' });
|
||||
await act(async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useShowTimeline());
|
||||
await waitForNextUpdate();
|
||||
const showTimeline = result.current;
|
||||
expect(showTimeline).toEqual([false]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,10 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { matchPath, useLocation } from 'react-router-dom';
|
||||
|
||||
const HIDDEN_TIMELINE_ROUTES: readonly string[] = [
|
||||
import { getLinksWithHiddenTimeline } from '../../links';
|
||||
import { useIsGroupedNavigationEnabled } from '../../components/navigation/helpers';
|
||||
|
||||
const DEPRECATED_HIDDEN_TIMELINE_ROUTES: readonly string[] = [
|
||||
`/cases/configure`,
|
||||
'/administration',
|
||||
'/rules/create',
|
||||
|
@ -18,17 +21,27 @@ const HIDDEN_TIMELINE_ROUTES: readonly string[] = [
|
|||
'/manage',
|
||||
];
|
||||
|
||||
const isHiddenTimelinePath = (currentPath: string): boolean => {
|
||||
return !!HIDDEN_TIMELINE_ROUTES.find((route) => matchPath(currentPath, route));
|
||||
const isTimelineHidden = (currentPath: string, isGroupedNavigationEnabled: boolean): boolean => {
|
||||
const groupLinksWithHiddenTimelinePaths = getLinksWithHiddenTimeline().map((l) => l.path);
|
||||
|
||||
const hiddenTimelineRoutes = isGroupedNavigationEnabled
|
||||
? groupLinksWithHiddenTimelinePaths
|
||||
: DEPRECATED_HIDDEN_TIMELINE_ROUTES;
|
||||
|
||||
return !!hiddenTimelineRoutes.find((route) => matchPath(currentPath, route));
|
||||
};
|
||||
|
||||
export const useShowTimeline = () => {
|
||||
const isGroupedNavigationEnabled = useIsGroupedNavigationEnabled();
|
||||
const { pathname } = useLocation();
|
||||
const [showTimeline, setShowTimeline] = useState(!isHiddenTimelinePath(pathname));
|
||||
|
||||
const [showTimeline, setShowTimeline] = useState(
|
||||
!isTimelineHidden(pathname, isGroupedNavigationEnabled)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setShowTimeline(!isHiddenTimelinePath(pathname));
|
||||
}, [pathname]);
|
||||
setShowTimeline(!isTimelineHidden(pathname, isGroupedNavigationEnabled));
|
||||
}, [pathname, isGroupedNavigationEnabled]);
|
||||
|
||||
return [showTimeline];
|
||||
};
|
||||
|
|
|
@ -161,9 +161,9 @@ describe('LoadPrebuiltRulesAndTemplatesButton', () => {
|
|||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="load-prebuilt-rules"] button').props().disabled
|
||||
).toEqual(true);
|
||||
expect(wrapper.find('button[data-test-subj="load-prebuilt-rules"]').props().disabled).toEqual(
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,14 +9,11 @@ import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
|||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { getCreateRuleUrl } from '../../../../common/components/link_to/redirect_to_detection_engine';
|
||||
import * as i18n from './translations';
|
||||
import { LinkButton } from '../../../../common/components/links';
|
||||
import { SecuritySolutionLinkButton } from '../../../../common/components/links';
|
||||
import { SecurityPageName } from '../../../../app/types';
|
||||
import { useFormatUrl } from '../../../../common/components/link_to';
|
||||
import { usePrePackagedRules } from '../../../containers/detection_engine/rules';
|
||||
import { useUserData } from '../../user_info';
|
||||
import { useNavigateTo } from '../../../../common/lib/kibana/hooks';
|
||||
|
||||
const EmptyPrompt = styled(EuiEmptyPrompt)`
|
||||
align-self: center; /* Corrects horizontal centering in IE11 */
|
||||
|
@ -38,16 +35,6 @@ const PrePackagedRulesPromptComponent: React.FC<PrePackagedRulesPromptProps> = (
|
|||
const handlePreBuiltCreation = useCallback(() => {
|
||||
createPrePackagedRules();
|
||||
}, [createPrePackagedRules]);
|
||||
const { formatUrl } = useFormatUrl(SecurityPageName.rules);
|
||||
const { navigateTo } = useNavigateTo();
|
||||
|
||||
const goToCreateRule = useCallback(
|
||||
(ev) => {
|
||||
ev.preventDefault();
|
||||
navigateTo({ deepLinkId: SecurityPageName.rules, path: getCreateRuleUrl() });
|
||||
},
|
||||
[navigateTo]
|
||||
);
|
||||
|
||||
const [{ isSignalIndexExists, isAuthenticated, hasEncryptionKey, canUserCRUD, hasIndexWrite }] =
|
||||
useUserData();
|
||||
|
@ -80,14 +67,13 @@ const PrePackagedRulesPromptComponent: React.FC<PrePackagedRulesPromptProps> = (
|
|||
<EuiFlexGroup justifyContent="center">
|
||||
<EuiFlexItem grow={false}>{loadPrebuiltRulesAndTemplatesButton}</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LinkButton
|
||||
<SecuritySolutionLinkButton
|
||||
isDisabled={!userHasPermissions}
|
||||
onClick={goToCreateRule}
|
||||
href={formatUrl(getCreateRuleUrl())}
|
||||
iconType="plusInCircle"
|
||||
deepLinkId={SecurityPageName.rulesCreate}
|
||||
>
|
||||
{i18n.CREATE_RULE_ACTION}
|
||||
</LinkButton>
|
||||
</SecuritySolutionLinkButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
}
|
||||
|
|
|
@ -439,7 +439,7 @@ const CreateRulePageComponent: React.FC = () => {
|
|||
</EuiFlexGroup>
|
||||
</SecuritySolutionPageWrapper>
|
||||
|
||||
<SpyRoute pageName={SecurityPageName.rules} />
|
||||
<SpyRoute pageName={SecurityPageName.rulesCreate} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -33,7 +33,25 @@ jest.mock('../../../containers/detection_engine/rules/use_find_rules_query');
|
|||
jest.mock('../../../../common/components/link_to');
|
||||
jest.mock('../../../components/user_info');
|
||||
|
||||
jest.mock('../../../../common/lib/kibana');
|
||||
jest.mock('../../../../common/lib/kibana', () => {
|
||||
const actual = jest.requireActual('../../../../common/lib/kibana');
|
||||
return {
|
||||
...actual,
|
||||
|
||||
useKibana: () => ({
|
||||
services: {
|
||||
...actual.useKibana().services,
|
||||
application: {
|
||||
navigateToApp: jest.fn(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
useNavigation: () => ({
|
||||
navigateTo: jest.fn(),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../../common/components/toasters', () => {
|
||||
const actual = jest.requireActual('../../../../common/components/toasters');
|
||||
return {
|
||||
|
|
|
@ -10,10 +10,7 @@ import React, { useCallback, useMemo } from 'react';
|
|||
|
||||
import { usePrePackagedRules, importRules } from '../../../containers/detection_engine/rules';
|
||||
import { useListsConfig } from '../../../containers/detection_engine/lists/use_lists_config';
|
||||
import {
|
||||
getDetectionEngineUrl,
|
||||
getCreateRuleUrl,
|
||||
} from '../../../../common/components/link_to/redirect_to_detection_engine';
|
||||
import { getDetectionEngineUrl } from '../../../../common/components/link_to/redirect_to_detection_engine';
|
||||
import { SecuritySolutionPageWrapper } from '../../../../common/components/page_wrapper';
|
||||
import { SpyRoute } from '../../../../common/utils/route/spy_routes';
|
||||
|
||||
|
@ -30,8 +27,7 @@ import {
|
|||
} from './helpers';
|
||||
import * as i18n from './translations';
|
||||
import { SecurityPageName } from '../../../../app/types';
|
||||
import { LinkButton } from '../../../../common/components/links';
|
||||
import { useFormatUrl } from '../../../../common/components/link_to';
|
||||
import { SecuritySolutionLinkButton } from '../../../../common/components/links';
|
||||
import { NeedAdminForUpdateRulesCallOut } from '../../../components/callouts/need_admin_for_update_callout';
|
||||
import { MlJobCompatibilityCallout } from '../../../components/callouts/ml_job_compatibility_callout';
|
||||
import { MissingPrivilegesCallOut } from '../../../components/callouts/missing_privileges_callout';
|
||||
|
@ -96,7 +92,6 @@ const RulesPageComponent: React.FC = () => {
|
|||
timelinesNotInstalled,
|
||||
timelinesNotUpdated
|
||||
);
|
||||
const { formatUrl } = useFormatUrl(SecurityPageName.rules);
|
||||
|
||||
const handleCreatePrePackagedRules = useCallback(async () => {
|
||||
if (createPrePackagedRules != null) {
|
||||
|
@ -113,14 +108,6 @@ const RulesPageComponent: React.FC = () => {
|
|||
}
|
||||
}, [refetchPrePackagedRulesStatus]);
|
||||
|
||||
const goToNewRule = useCallback(
|
||||
(ev) => {
|
||||
ev.preventDefault();
|
||||
navigateToApp(APP_UI_ID, { deepLinkId: SecurityPageName.rules, path: getCreateRuleUrl() });
|
||||
},
|
||||
[navigateToApp]
|
||||
);
|
||||
|
||||
const loadPrebuiltRulesAndTemplatesButton = useMemo(
|
||||
() =>
|
||||
getLoadPrebuiltRulesAndTemplatesButton({
|
||||
|
@ -212,16 +199,15 @@ const RulesPageComponent: React.FC = () => {
|
|||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<LinkButton
|
||||
<SecuritySolutionLinkButton
|
||||
data-test-subj="create-new-rule"
|
||||
fill
|
||||
onClick={goToNewRule}
|
||||
href={formatUrl(getCreateRuleUrl())}
|
||||
iconType="plusInCircle"
|
||||
isDisabled={!userHasPermissions(canUserCRUD) || loading}
|
||||
deepLinkId={SecurityPageName.rulesCreate}
|
||||
>
|
||||
{i18n.ADD_NEW_RULE}
|
||||
</LinkButton>
|
||||
</SecuritySolutionLinkButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</HeaderPage>
|
||||
|
|
|
@ -12,14 +12,16 @@ import {
|
|||
EVENT_FILTERS_PATH,
|
||||
EXCEPTIONS_PATH,
|
||||
HOST_ISOLATION_EXCEPTIONS_PATH,
|
||||
MANAGEMENT_PATH,
|
||||
MANAGE_PATH,
|
||||
POLICIES_PATH,
|
||||
RULES_CREATE_PATH,
|
||||
RULES_PATH,
|
||||
SecurityPageName,
|
||||
TRUSTED_APPS_PATH,
|
||||
} from '../../common/constants';
|
||||
import {
|
||||
BLOCKLIST,
|
||||
CREATE_NEW_RULE,
|
||||
ENDPOINTS,
|
||||
EVENT_FILTERS,
|
||||
EXCEPTIONS,
|
||||
|
@ -44,8 +46,9 @@ import { IconTrustedApplications } from './icons/trusted_applications';
|
|||
export const links: LinkItem = {
|
||||
id: SecurityPageName.administration,
|
||||
title: MANAGE,
|
||||
path: MANAGEMENT_PATH,
|
||||
path: MANAGE_PATH,
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
globalNavEnabled: false,
|
||||
features: [FEATURE.general],
|
||||
globalSearchKeywords: [
|
||||
|
@ -71,6 +74,16 @@ export const links: LinkItem = {
|
|||
}),
|
||||
],
|
||||
globalSearchEnabled: true,
|
||||
links: [
|
||||
{
|
||||
id: SecurityPageName.rulesCreate,
|
||||
title: CREATE_NEW_RULE,
|
||||
path: RULES_CREATE_PATH,
|
||||
globalNavEnabled: false,
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.exceptions,
|
||||
|
@ -99,6 +112,7 @@ export const links: LinkItem = {
|
|||
globalNavOrder: 9006,
|
||||
path: ENDPOINTS_PATH,
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.policies,
|
||||
|
@ -110,6 +124,7 @@ export const links: LinkItem = {
|
|||
landingIcon: IconEndpointPolicies,
|
||||
path: POLICIES_PATH,
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
experimentalKey: 'policyListEnabled',
|
||||
},
|
||||
{
|
||||
|
@ -125,6 +140,7 @@ export const links: LinkItem = {
|
|||
landingIcon: IconTrustedApplications,
|
||||
path: TRUSTED_APPS_PATH,
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.eventFilters,
|
||||
|
@ -135,6 +151,7 @@ export const links: LinkItem = {
|
|||
landingIcon: IconEventFilters,
|
||||
path: EVENT_FILTERS_PATH,
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.hostIsolationExceptions,
|
||||
|
@ -145,6 +162,7 @@ export const links: LinkItem = {
|
|||
landingIcon: IconHostIsolation,
|
||||
path: HOST_ISOLATION_EXCEPTIONS_PATH,
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
},
|
||||
{
|
||||
id: SecurityPageName.blocklist,
|
||||
|
@ -155,6 +173,7 @@ export const links: LinkItem = {
|
|||
landingIcon: IconBlocklist,
|
||||
path: BLOCKLIST_PATH,
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -48,6 +48,7 @@ export const gettingStartedLinks: LinkItem = {
|
|||
}),
|
||||
],
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
};
|
||||
|
||||
export const detectionResponseLinks: LinkItem = {
|
||||
|
@ -81,4 +82,6 @@ export const dashboardsLandingLinks: LinkItem = {
|
|||
}),
|
||||
],
|
||||
links: [overviewLinks, detectionResponseLinks],
|
||||
skipUrlState: true,
|
||||
hideTimeline: true,
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue