mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Security Solution][Network details] add ability to show full network flyout from preview (#211065)
## Summary This PR is a follow up of [this PR](https://github.com/elastic/kibana/pull/187870) that replaced the old network/ip flyout with a new panel for the expandable flyout. Since then we improved the UI or preview and added the ability to jump to a full flyout from its preview. This PR fixes the issues where users could not navigate to the full details network/ip flyout from a preview. This functionality already exists for the alert, event, host and user flyouts. The PR adds a new footer to the network/ip flyout - only shown in preview mode - that allows users to navigate to the full detail network/ip flyout. | Old behavior | New behavior | | ------------- | ------------- | |  |  | The user has the ability to navigate to the full detail flyout: https://github.com/user-attachments/assets/4c809e5d-0b59-4498-9966-0133d139233b ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
1aac3188a1
commit
96b4f8442e
5 changed files with 189 additions and 0 deletions
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { PreviewPanelFooter } from './footer';
|
||||
import { PREVIEW_FOOTER_LINK_TEST_ID, PREVIEW_FOOTER_TEST_ID } from './test_ids';
|
||||
import { NetworkPanelKey } from '.';
|
||||
import { FlowTargetSourceDest } from '../../../common/search_strategy';
|
||||
import { mockFlyoutApi } from '../document_details/shared/mocks/mock_flyout_context';
|
||||
|
||||
jest.mock('@kbn/expandable-flyout');
|
||||
|
||||
const ip = 'ip';
|
||||
const flowTarget = FlowTargetSourceDest.destination;
|
||||
const scopeId = 'scopeId';
|
||||
|
||||
describe('<PreviewPanelFooter />', () => {
|
||||
beforeEach(() => {
|
||||
jest.mocked(useExpandableFlyoutApi).mockReturnValue(mockFlyoutApi);
|
||||
});
|
||||
|
||||
it('should open network details flyout when clicked', () => {
|
||||
const { getByTestId } = render(
|
||||
<PreviewPanelFooter ip={ip} flowTarget={flowTarget} scopeId={scopeId} />
|
||||
);
|
||||
|
||||
expect(getByTestId(PREVIEW_FOOTER_TEST_ID)).toBeInTheDocument();
|
||||
|
||||
getByTestId(PREVIEW_FOOTER_LINK_TEST_ID).click();
|
||||
expect(mockFlyoutApi.openFlyout).toHaveBeenCalledWith({
|
||||
right: {
|
||||
id: NetworkPanelKey,
|
||||
params: {
|
||||
ip,
|
||||
flowTarget,
|
||||
scopeId,
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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 { FC } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiFlyoutFooter, EuiLink, EuiPanel } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { PREVIEW_FOOTER_LINK_TEST_ID, PREVIEW_FOOTER_TEST_ID } from './test_ids';
|
||||
import type { FlowTargetSourceDest } from '../../../common/search_strategy';
|
||||
import { NetworkPanelKey } from '.';
|
||||
|
||||
export interface PreviewPanelFooterProps {
|
||||
/**
|
||||
* IP value
|
||||
*/
|
||||
ip: string;
|
||||
/**
|
||||
* Destination or source information
|
||||
*/
|
||||
flowTarget: FlowTargetSourceDest;
|
||||
/**
|
||||
* Scope ID
|
||||
*/
|
||||
scopeId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Footer at the bottom of preview panel with a link to open network details flyout
|
||||
*/
|
||||
export const PreviewPanelFooter: FC<PreviewPanelFooterProps> = ({ ip, flowTarget, scopeId }) => {
|
||||
const { openFlyout } = useExpandableFlyoutApi();
|
||||
|
||||
const openNetworkFlyout = useCallback(() => {
|
||||
openFlyout({
|
||||
right: {
|
||||
id: NetworkPanelKey,
|
||||
params: {
|
||||
ip,
|
||||
flowTarget,
|
||||
scopeId,
|
||||
},
|
||||
},
|
||||
});
|
||||
}, [openFlyout, flowTarget, ip, scopeId]);
|
||||
|
||||
const fullDetailsLink = useMemo(
|
||||
() => (
|
||||
<EuiLink
|
||||
onClick={openNetworkFlyout}
|
||||
target="_blank"
|
||||
data-test-subj={PREVIEW_FOOTER_LINK_TEST_ID}
|
||||
>
|
||||
<>
|
||||
{i18n.translate('xpack.securitySolution.flyout.network.preview.openFlyoutLabel', {
|
||||
defaultMessage: 'Show full network details',
|
||||
})}
|
||||
</>
|
||||
</EuiLink>
|
||||
),
|
||||
[openNetworkFlyout]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiFlyoutFooter data-test-subj={PREVIEW_FOOTER_TEST_ID}>
|
||||
<EuiPanel color="transparent">
|
||||
<EuiFlexGroup justifyContent="center">
|
||||
<EuiFlexItem grow={false}>{fullDetailsLink}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
</EuiFlyoutFooter>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import {
|
||||
useExpandableFlyoutApi,
|
||||
useExpandableFlyoutHistory,
|
||||
useExpandableFlyoutState,
|
||||
} from '@kbn/expandable-flyout';
|
||||
import { PREVIEW_FOOTER_TEST_ID } from './test_ids';
|
||||
import { NetworkPanel } from '.';
|
||||
import { FlowTargetSourceDest } from '../../../common/search_strategy';
|
||||
import { mockFlyoutApi } from '../document_details/shared/mocks/mock_flyout_context';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
|
||||
jest.mock('@kbn/expandable-flyout');
|
||||
jest.mock('../../common/hooks/use_experimental_features');
|
||||
|
||||
const ip = 'ip';
|
||||
const flowTarget = FlowTargetSourceDest.destination;
|
||||
const scopeId = 'scopeId';
|
||||
|
||||
describe('<NetworkPanel />', () => {
|
||||
beforeEach(() => {
|
||||
jest.mocked(useExpandableFlyoutApi).mockReturnValue(mockFlyoutApi);
|
||||
jest.mocked(useExpandableFlyoutHistory).mockReturnValue([]);
|
||||
(useExpandableFlyoutState as jest.Mock).mockReturnValue({});
|
||||
});
|
||||
|
||||
it('should not show footer if non-preview mode', () => {
|
||||
const { queryByTestId } = render(
|
||||
<TestProviders>
|
||||
<NetworkPanel ip={ip} flowTarget={flowTarget} scopeId={scopeId} isPreviewMode={false} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(queryByTestId(PREVIEW_FOOTER_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show footer if preview mode', () => {
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<NetworkPanel ip={ip} flowTarget={flowTarget} scopeId={scopeId} isPreviewMode={true} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(getByTestId(PREVIEW_FOOTER_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -10,6 +10,7 @@ import React, { memo } from 'react';
|
|||
import type { FlyoutPanelProps } from '@kbn/expandable-flyout';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { TableId } from '@kbn/securitysolution-data-table';
|
||||
import { PreviewPanelFooter } from './footer';
|
||||
import type { FlowTargetSourceDest } from '../../../common/search_strategy';
|
||||
import { PanelHeader } from './header';
|
||||
import { PanelContent } from './content';
|
||||
|
@ -64,6 +65,7 @@ export const NetworkPanel: FC<NetworkPanelProps> = memo(
|
|||
/>
|
||||
<PanelHeader ip={ip} flowTarget={flowTarget} />
|
||||
<PanelContent ip={ip} flowTarget={flowTarget} />
|
||||
{isPreviewMode && <PreviewPanelFooter ip={ip} flowTarget={flowTarget} scopeId={scopeId} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 { PREFIX } from '../shared/test_ids';
|
||||
|
||||
export const PREVIEW_FOOTER_TEST_ID = `${PREFIX}NetworkPreviewFooter` as const;
|
||||
export const PREVIEW_FOOTER_LINK_TEST_ID = `${PREVIEW_FOOTER_TEST_ID}Link` as const;
|
Loading…
Add table
Add a link
Reference in a new issue