Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions x-pack/plugins/security_solution/common/endpoint/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ApplicationStart['navigateToApp']>;
/** The URL for the `Back` button */
backButtonUrl?: string;
/** The label for the button */
backButtonLabel?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import { TypeOf } from '@kbn/config-schema';
import { ApplicationStart } from 'kibana/public';

import {
DeleteTrustedAppsRequestSchema,
Expand Down Expand Up @@ -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<ApplicationStart['navigateToApp']>;
/** The URL for the `Back` button */
backButtonUrl?: string;
/** The label for the button */
backButtonLabel?: string;
}
Original file line number Diff line number Diff line change
@@ -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<ListPageRouteState>(
({ backButtonLabel, backButtonUrl, onBackButtonNavigateTo }) => {
const handleBackOnClick = useNavigateToAppEventHandler(...onBackButtonNavigateTo!);

return (
<EuiButtonEmptyStyled
flush="left"
size="xs"
iconType="arrowLeft"
href={backButtonUrl!}
onClick={handleBackOnClick}
textProps={{ className: 'text' }}
data-test-subj="backToOrigin"
>
{backButtonLabel || (
<FormattedMessage id="xpack.securitySolution.list.backButton" defaultMessage="Back" />
)}
</EuiButtonEmptyStyled>
);
}
);

BackToExternalAppButton.displayName = 'BackToExternalAppButton';
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 { BackToExternalAppButton } from './back_to_external_app_button';
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,30 @@ describe('When on the Event Filters List Page', () => {
});
});
});

describe('and the back button is present', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: Maybe just call this Back button since now it looks similar to the test description name below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to merge this before having more out of date check errors and will do this change in next pr :)

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();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -34,14 +34,15 @@ 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,
} from '../../../../common/components/exceptions/viewer/exception_item';
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<ExceptionListItemSchema>,
Expand All @@ -59,6 +60,7 @@ const AdministrationListPage = styled(_AdministrationListPage)`
`;

export const EventFiltersListPage = memo(() => {
const { state: routeState } = useLocation<ListPageRouteState | undefined>();
const history = useHistory();
const dispatch = useDispatch<Dispatch<AppAction>>();
const isActionError = useEventFiltersSelector(getActionError);
Expand Down Expand Up @@ -103,6 +105,13 @@ export const EventFiltersListPage = memo(() => {
}
}, [dispatch, formEntry, history, isActionError, location, navigateCallback]);

const backButton = useMemo(() => {
if (routeState && routeState.onBackButtonNavigateTo) {
return <BackToExternalAppButton {...routeState} />;
}
return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: does headerComponent expect a null instead of undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's an optional component, so null works in this case

}, [routeState]);

const handleAddButtonClick = useCallback(
() =>
navigateCallback({
Expand Down Expand Up @@ -173,6 +182,7 @@ export const EventFiltersListPage = memo(() => {
return (
<AdministrationListPage
beta={false}
headerBackComponent={backButton}
title={
<FormattedMessage
id="xpack.securitySolution.eventFilters.list.pageTitle"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import {
} from '../../../../../../../../../fleet/public';
import { useKibana } from '../../../../../../../../../../../src/plugins/kibana_react/public';
import { getEventFiltersListPath } from '../../../../../../common/routing';
import { GetExceptionSummaryResponse } from '../../../../../../../../common/endpoint/types';
import {
GetExceptionSummaryResponse,
ListPageRouteState,
} from '../../../../../../../../common/endpoint/types';
import { PLUGIN_ID as FLEET_PLUGIN_ID } from '../../../../../../../../../fleet/common';
import { MANAGEMENT_APP_ID } from '../../../../../../common/constants';
import { useToasts } from '../../../../../../../common/lib/kibana';
Expand Down Expand Up @@ -64,7 +67,7 @@ export const FleetEventFiltersCard = memo<PackageCustomExtensionComponentProps>(
};
}, [eventFiltersApi, toasts]);

const eventFiltersRouteState = useMemo(() => {
const eventFiltersRouteState = useMemo<ListPageRouteState>(() => {
const fleetPackageCustomUrlPath = `#${pagePathGetters.integration_details_custom({ pkgkey })}`;
return {
backButtonLabel: i18n.translate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -67,7 +67,7 @@ export const FleetTrustedAppsCard = memo<PackageCustomExtensionComponentProps>((
}, [toasts, trustedAppsApi]);
const trustedAppsListUrlPath = getTrustedAppsListPath();

const trustedAppRouteState = useMemo<TrustedAppsListPageRouteState>(() => {
const trustedAppRouteState = useMemo<ListPageRouteState>(() => {
const fleetPackageCustomUrlPath = `#${pagePathGetters.integration_details_custom({ pkgkey })}`;
return {
backButtonLabel: i18n.translate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -900,4 +900,34 @@ describe('When on the Trusted Apps Page', () => {
});
});
});

describe('and the back button is present', () => {
let renderResult: ReturnType<AppContextTestRender['render']>;
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();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<TrustedAppsListPageRouteState | undefined>();
const { state: routeState } = useLocation<ListPageRouteState | undefined>();
const location = useTrustedAppsSelector(getCurrentLocation);
const totalItemsCount = useTrustedAppsSelector(getListTotalItemsCount);
const isCheckingIfEntriesExists = useTrustedAppsSelector(checkingIfEntriesExist);
Expand Down Expand Up @@ -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<TrustedAppsListPageRouteState>(
({ backButtonLabel, backButtonUrl, onBackButtonNavigateTo }) => {
const handleBackOnClick = useNavigateToAppEventHandler(...onBackButtonNavigateTo!);

return (
<EuiButtonEmptyStyled
flush="left"
size="xs"
iconType="arrowLeft"
href={backButtonUrl!}
onClick={handleBackOnClick}
textProps={{ className: 'text' }}
data-test-subj="backToOrigin"
>
{backButtonLabel || (
<FormattedMessage
id="xpack.securitySolution.trustedapps.list.backButton"
defaultMessage="Back"
/>
)}
</EuiButtonEmptyStyled>
);
}
);

BackToExternalAppButton.displayName = 'BackToExternalAppButton';
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -21394,7 +21394,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": "現在、エンドポイントには信頼できるアプリケーションがありません。",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -21730,7 +21730,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 {# 个受信任的应用程序}}",
Expand Down