diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index e006944eb914f..1e0d798cf7f07 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -1114,3 +1114,15 @@ export interface GetExceptionSummaryResponse { macos: number; linux: number; } + +/** + * Supported React-Router state for the Generic List page + */ +export interface ListPageRouteState { + /** Where the user should be redirected to when the `Back` button is clicked */ + onBackButtonNavigateTo: Parameters; + /** The URL for the `Back` button */ + backButtonUrl?: string; + /** The label for the button */ + backButtonLabel?: string; +} diff --git a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts index 8d66370fea4d3..94a2e7f236beb 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts @@ -6,7 +6,6 @@ */ import { TypeOf } from '@kbn/config-schema'; -import { ApplicationStart } from 'kibana/public'; import { DeleteTrustedAppsRequestSchema, @@ -133,15 +132,3 @@ export type TrustedApp = NewTrustedApp & { updated_at: string; updated_by: string; }; - -/** - * Supported React-Router state for the Trusted Apps List page - */ -export interface TrustedAppsListPageRouteState { - /** Where the user should be redirected to when the `Back` button is clicked */ - onBackButtonNavigateTo: Parameters; - /** The URL for the `Back` button */ - backButtonUrl?: string; - /** The label for the button */ - backButtonLabel?: string; -} diff --git a/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx b/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx new file mode 100644 index 0000000000000..78c854d933584 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/back_to_external_app_button.tsx @@ -0,0 +1,52 @@ +/* + * 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, { memo } from 'react'; + +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiButtonEmpty } from '@elastic/eui'; +import styled from 'styled-components'; + +import { ListPageRouteState } from '../../../../common/endpoint/types'; + +import { useNavigateToAppEventHandler } from '../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; + +const EuiButtonEmptyStyled = styled(EuiButtonEmpty)` + margin-bottom: ${({ theme }) => theme.eui.euiSizeS}; + + .euiIcon { + width: ${({ theme }) => theme.eui.euiIconSizes.small}; + height: ${({ theme }) => theme.eui.euiIconSizes.small}; + } + + .text { + font-size: ${({ theme }) => theme.eui.euiFontSizeXS}; + } +`; + +export const BackToExternalAppButton = memo( + ({ backButtonLabel, backButtonUrl, onBackButtonNavigateTo }) => { + const handleBackOnClick = useNavigateToAppEventHandler(...onBackButtonNavigateTo!); + + return ( + + {backButtonLabel || ( + + )} + + ); + } +); + +BackToExternalAppButton.displayName = 'BackToExternalAppButton'; diff --git a/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/index.ts b/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/index.ts new file mode 100644 index 0000000000000..d4a2f8de13546 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/back_to_external_app_button/index.ts @@ -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 { BackToExternalAppButton } from './back_to_external_app_button'; diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx index 465f92dfda767..59d409874c561 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.test.tsx @@ -175,4 +175,30 @@ describe('When on the Event Filters List Page', () => { }); }); }); + + describe('and the back button is present', () => { + beforeEach(async () => { + renderResult = render(); + act(() => { + history.push('/event_filters', { + onBackButtonNavigateTo: [{ appId: 'appId' }], + backButtonLabel: 'back to fleet', + backButtonUrl: '/fleet', + }); + }); + }); + + it('back button is present', () => { + const button = renderResult.queryByTestId('backToOrigin'); + expect(button).not.toBeNull(); + expect(button).toHaveAttribute('href', '/fleet'); + }); + + it('back button is not present', () => { + act(() => { + history.push('/event_filters'); + }); + expect(renderResult.queryByTestId('backToOrigin')).toBeNull(); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx index 32fc018210418..00ee80c5d7022 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/event_filters_list_page.tsx @@ -5,10 +5,10 @@ * 2.0. */ -import React, { memo, useCallback, useEffect } from 'react'; +import React, { memo, useCallback, useMemo, useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { Dispatch } from 'redux'; -import { useHistory } from 'react-router-dom'; +import { useHistory, useLocation } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton, EuiSpacer, EuiHorizontalRule, EuiText } from '@elastic/eui'; @@ -34,7 +34,7 @@ import { showDeleteModal, } from '../store/selector'; import { PaginatedContent, PaginatedContentProps } from '../../../components/paginated_content'; -import { Immutable } from '../../../../../common/endpoint/types'; +import { Immutable, ListPageRouteState } from '../../../../../common/endpoint/types'; import { ExceptionItem, ExceptionItemProps, @@ -42,6 +42,7 @@ import { import { EventFilterDeleteModal } from './components/event_filter_delete_modal'; import { SearchBar } from '../../../components/search_bar'; +import { BackToExternalAppButton } from '../../../components/back_to_external_app_button'; type EventListPaginatedContent = PaginatedContentProps< Immutable, @@ -59,6 +60,7 @@ const AdministrationListPage = styled(_AdministrationListPage)` `; export const EventFiltersListPage = memo(() => { + const { state: routeState } = useLocation(); const history = useHistory(); const dispatch = useDispatch>(); const isActionError = useEventFiltersSelector(getActionError); @@ -103,6 +105,13 @@ export const EventFiltersListPage = memo(() => { } }, [dispatch, formEntry, history, isActionError, location, navigateCallback]); + const backButton = useMemo(() => { + if (routeState && routeState.onBackButtonNavigateTo) { + return ; + } + return null; + }, [routeState]); + const handleAddButtonClick = useCallback( () => navigateCallback({ @@ -173,6 +182,7 @@ export const EventFiltersListPage = memo(() => { return ( ( }; }, [eventFiltersApi, toasts]); - const eventFiltersRouteState = useMemo(() => { + const eventFiltersRouteState = useMemo(() => { const fleetPackageCustomUrlPath = `#${pagePathGetters.integration_details_custom({ pkgkey })}`; return { backButtonLabel: i18n.translate( diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx index b1464d23e00fb..ed3ba10c1e62b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/endpoint_package_custom_extension/components/fleet_trusted_apps_card.tsx @@ -17,7 +17,7 @@ import { import { useKibana } from '../../../../../../../../../../../src/plugins/kibana_react/public'; import { getTrustedAppsListPath } from '../../../../../../common/routing'; import { - TrustedAppsListPageRouteState, + ListPageRouteState, GetExceptionSummaryResponse, } from '../../../../../../../../common/endpoint/types'; import { PLUGIN_ID as FLEET_PLUGIN_ID } from '../../../../../../../../../fleet/common'; @@ -67,7 +67,7 @@ export const FleetTrustedAppsCard = memo(( }, [toasts, trustedAppsApi]); const trustedAppsListUrlPath = getTrustedAppsListPath(); - const trustedAppRouteState = useMemo(() => { + const trustedAppRouteState = useMemo(() => { const fleetPackageCustomUrlPath = `#${pagePathGetters.integration_details_custom({ pkgkey })}`; return { backButtonLabel: i18n.translate( diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx index fac9fb1e5bf6e..691601b69e3cd 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx @@ -900,4 +900,34 @@ describe('When on the Trusted Apps Page', () => { }); }); }); + + describe('and the back button is present', () => { + let renderResult: ReturnType; + beforeEach(async () => { + renderResult = render(); + await act(async () => { + await waitForAction('trustedAppsListResourceStateChanged'); + }); + reactTestingLibrary.act(() => { + history.push('/trusted_apps', { + onBackButtonNavigateTo: [{ appId: 'appId' }], + backButtonLabel: 'back to fleet', + backButtonUrl: '/fleet', + }); + }); + }); + + it('back button is present', () => { + const button = renderResult.queryByTestId('backToOrigin'); + expect(button).not.toBeNull(); + expect(button).toHaveAttribute('href', '/fleet'); + }); + + it('back button is not present', () => { + reactTestingLibrary.act(() => { + history.push('/trusted_apps'); + }); + expect(renderResult.queryByTestId('backToOrigin')).toBeNull(); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx index 5603b8e2d61c9..4cd6ad62f3a35 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx @@ -7,11 +7,9 @@ import React, { memo, useMemo } from 'react'; import { useLocation } from 'react-router-dom'; -import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiButton, - EuiButtonEmpty, EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, @@ -35,14 +33,14 @@ import { TrustedAppsGrid } from './components/trusted_apps_grid'; import { TrustedAppsList } from './components/trusted_apps_list'; import { TrustedAppDeletionDialog } from './trusted_app_deletion_dialog'; import { TrustedAppsNotifications } from './trusted_apps_notifications'; -import { TrustedAppsListPageRouteState } from '../../../../../common/endpoint/types'; -import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; import { ABOUT_TRUSTED_APPS, SEARCH_TRUSTED_APP_PLACEHOLDER } from './translations'; import { EmptyState } from './components/empty_state'; import { SearchBar } from '../../../components/search_bar'; +import { BackToExternalAppButton } from '../../../components/back_to_external_app_button'; +import { ListPageRouteState } from '../../../../../common/endpoint/types'; export const TrustedAppsPage = memo(() => { - const { state: routeState } = useLocation(); + const { state: routeState } = useLocation(); const location = useTrustedAppsSelector(getCurrentLocation); const totalItemsCount = useTrustedAppsSelector(getListTotalItemsCount); const isCheckingIfEntriesExists = useTrustedAppsSelector(checkingIfEntriesExist); @@ -161,43 +159,3 @@ export const TrustedAppsPage = memo(() => { }); TrustedAppsPage.displayName = 'TrustedAppsPage'; - -const EuiButtonEmptyStyled = styled(EuiButtonEmpty)` - margin-bottom: ${({ theme }) => theme.eui.euiSizeS}; - - .euiIcon { - width: ${({ theme }) => theme.eui.euiIconSizes.small}; - height: ${({ theme }) => theme.eui.euiIconSizes.small}; - } - - .text { - font-size: ${({ theme }) => theme.eui.euiFontSizeXS}; - } -`; - -const BackToExternalAppButton = memo( - ({ backButtonLabel, backButtonUrl, onBackButtonNavigateTo }) => { - const handleBackOnClick = useNavigateToAppEventHandler(...onBackButtonNavigateTo!); - - return ( - - {backButtonLabel || ( - - )} - - ); - } -); - -BackToExternalAppButton.displayName = 'BackToExternalAppButton'; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 879c666cff672..a909207d7848a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -21393,7 +21393,6 @@ "xpack.securitySolution.trustedapps.list.actions.delete": "削除", "xpack.securitySolution.trustedapps.list.actions.delete.description": "このエントリを削除", "xpack.securitySolution.trustedapps.list.addButton": "信頼できるアプリケーションを追加", - "xpack.securitySolution.trustedapps.list.backButton": "戻る", "xpack.securitySolution.trustedapps.list.columns.actions": "アクション", "xpack.securitySolution.trustedapps.list.pageTitle": "信頼できるアプリケーション", "xpack.securitySolution.trustedapps.listEmptyState.message": "現在、エンドポイントには信頼できるアプリケーションがありません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ac1a8d69796a2..b4f27d2677efa 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -21728,7 +21728,6 @@ "xpack.securitySolution.trustedapps.list.actions.delete": "移除", "xpack.securitySolution.trustedapps.list.actions.delete.description": "移除此条目", "xpack.securitySolution.trustedapps.list.addButton": "添加受信任的应用程序", - "xpack.securitySolution.trustedapps.list.backButton": "返回", "xpack.securitySolution.trustedapps.list.columns.actions": "操作", "xpack.securitySolution.trustedapps.list.pageTitle": "受信任的应用程序", "xpack.securitySolution.trustedapps.list.totalCount": "{totalItemCount, plural, other {# 个受信任的应用程序}}",