mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution] expandable flyout - expandable mode only for signal documents (#163557)
This commit is contained in:
parent
d74f4d3e8f
commit
f38a37c0c1
6 changed files with 127 additions and 28 deletions
|
@ -39,7 +39,7 @@ const renderHeader = (contextValue: RightPanelContext) =>
|
|||
<TestProvidersComponent>
|
||||
<ExpandableFlyoutContext.Provider value={flyoutContextValue}>
|
||||
<RightPanelContext.Provider value={contextValue}>
|
||||
<HeaderTitle />
|
||||
<HeaderTitle flyoutIsExpandable={true} />
|
||||
</RightPanelContext.Provider>
|
||||
</ExpandableFlyoutContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
|
@ -52,7 +52,7 @@ describe('<HeaderTitle />', () => {
|
|||
jest.mocked(useAssistant).mockReturnValue({ showAssistant: true, promptContextId: '' });
|
||||
});
|
||||
|
||||
it('should render mitre attack information', () => {
|
||||
it('should render component', () => {
|
||||
const contextValue = {
|
||||
dataFormattedForFieldBrowser: mockDataFormattedForFieldBrowser,
|
||||
getFieldsData: jest.fn().mockImplementation(mockGetFieldsData),
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { FC } from 'react';
|
||||
import type { VFC } from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import { NewChatById } from '@kbn/elastic-assistant';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
|
@ -26,10 +26,17 @@ import { PreferenceFormattedDate } from '../../../common/components/formatted_da
|
|||
import { FLYOUT_HEADER_TITLE_TEST_ID } from './test_ids';
|
||||
import { ShareButton } from './share_button';
|
||||
|
||||
export interface HeaderTitleProps {
|
||||
/**
|
||||
* If false, update the margin-top to compensate the fact that the expand detail button is not displayed
|
||||
*/
|
||||
flyoutIsExpandable: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document details flyout right section header
|
||||
*/
|
||||
export const HeaderTitle: FC = memo(() => {
|
||||
export const HeaderTitle: VFC<HeaderTitleProps> = memo(({ flyoutIsExpandable }) => {
|
||||
const { dataFormattedForFieldBrowser } = useRightPanelContext();
|
||||
const { isAlert, ruleName, timestamp, alertUrl } = useBasicDataFromDetailsData(
|
||||
dataFormattedForFieldBrowser
|
||||
|
@ -48,7 +55,7 @@ export const HeaderTitle: FC = memo(() => {
|
|||
justifyContent="flexEnd"
|
||||
gutterSize="none"
|
||||
css={css`
|
||||
margin-top: -44px;
|
||||
margin-top: ${flyoutIsExpandable ? '-44px' : '-28px'};
|
||||
padding: 0 25px;
|
||||
`}
|
||||
>
|
||||
|
|
|
@ -10,23 +10,28 @@ import type { VFC } from 'react';
|
|||
import React, { useMemo } from 'react';
|
||||
import { FLYOUT_BODY_TEST_ID } from './test_ids';
|
||||
import type { RightPanelPaths } from '.';
|
||||
import { tabs } from './tabs';
|
||||
import type { RightPanelTabsType } from './tabs';
|
||||
import {} from './tabs';
|
||||
|
||||
export interface PanelContentProps {
|
||||
/**
|
||||
* Id of the tab selected in the parent component to display its content
|
||||
*/
|
||||
selectedTabId: RightPanelPaths;
|
||||
/**
|
||||
* Tabs display right below the flyout's header
|
||||
*/
|
||||
tabs: RightPanelTabsType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Document details expandable flyout right section, that will display the content
|
||||
* of the overview, table and json tabs.
|
||||
*/
|
||||
export const PanelContent: VFC<PanelContentProps> = ({ selectedTabId }) => {
|
||||
export const PanelContent: VFC<PanelContentProps> = ({ selectedTabId, tabs }) => {
|
||||
const selectedTabContent = useMemo(() => {
|
||||
return tabs.find((tab) => tab.id === selectedTabId)?.content;
|
||||
}, [selectedTabId]);
|
||||
}, [selectedTabId, tabs]);
|
||||
|
||||
return <EuiFlyoutBody data-test-subj={FLYOUT_BODY_TEST_ID}>{selectedTabContent}</EuiFlyoutBody>;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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 { ExpandableFlyoutContext } from '@kbn/expandable-flyout/src/context';
|
||||
import { TestProviders } from '../../common/mock';
|
||||
import { RightPanelContext } from './context';
|
||||
import { mockContextValue } from './mocks/mock_right_panel_context';
|
||||
import { PanelHeader } from './header';
|
||||
import {
|
||||
COLLAPSE_DETAILS_BUTTON_TEST_ID,
|
||||
EXPAND_DETAILS_BUTTON_TEST_ID,
|
||||
} from './components/test_ids';
|
||||
import { mockFlyoutContextValue } from '../shared/mocks/mock_flyout_context';
|
||||
|
||||
describe('<PanelHeader />', () => {
|
||||
it('should render expand details button if flyout is expandable', () => {
|
||||
const { getByTestId } = render(
|
||||
<TestProviders>
|
||||
<ExpandableFlyoutContext.Provider value={mockFlyoutContextValue}>
|
||||
<RightPanelContext.Provider value={mockContextValue}>
|
||||
<PanelHeader
|
||||
flyoutIsExpandable={true}
|
||||
selectedTabId={'overview'}
|
||||
setSelectedTabId={() => window.alert('test')}
|
||||
tabs={[]}
|
||||
/>
|
||||
</RightPanelContext.Provider>
|
||||
</ExpandableFlyoutContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(getByTestId(EXPAND_DETAILS_BUTTON_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render expand details button if flyout is not expandable', () => {
|
||||
const { queryByTestId } = render(
|
||||
<TestProviders>
|
||||
<ExpandableFlyoutContext.Provider value={mockFlyoutContextValue}>
|
||||
<RightPanelContext.Provider value={mockContextValue}>
|
||||
<PanelHeader
|
||||
flyoutIsExpandable={false}
|
||||
selectedTabId={'overview'}
|
||||
setSelectedTabId={() => window.alert('test')}
|
||||
tabs={[]}
|
||||
/>
|
||||
</RightPanelContext.Provider>
|
||||
</ExpandableFlyoutContext.Provider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(queryByTestId(EXPAND_DETAILS_BUTTON_TEST_ID)).not.toBeInTheDocument();
|
||||
expect(queryByTestId(COLLAPSE_DETAILS_BUTTON_TEST_ID)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
|
@ -10,18 +10,32 @@ import type { VFC } from 'react';
|
|||
import React, { memo } from 'react';
|
||||
import { css } from '@emotion/react';
|
||||
import type { RightPanelPaths } from '.';
|
||||
import { tabs } from './tabs';
|
||||
import type { RightPanelTabsType } from './tabs';
|
||||
import { HeaderTitle } from './components/header_title';
|
||||
import { ExpandDetailButton } from './components/expand_detail_button';
|
||||
|
||||
export interface PanelHeaderProps {
|
||||
/**
|
||||
* Id of the tab selected in the parent component to display its content
|
||||
*/
|
||||
selectedTabId: RightPanelPaths;
|
||||
/**
|
||||
* Callback to set the selected tab id in the parent component
|
||||
* @param selected
|
||||
*/
|
||||
setSelectedTabId: (selected: RightPanelPaths) => void;
|
||||
handleOnEventClosed?: () => void;
|
||||
/**
|
||||
* Tabs to display in the header
|
||||
*/
|
||||
tabs: RightPanelTabsType;
|
||||
/**
|
||||
* If true, the expand detail button will be displayed
|
||||
*/
|
||||
flyoutIsExpandable: boolean;
|
||||
}
|
||||
|
||||
export const PanelHeader: VFC<PanelHeaderProps> = memo(
|
||||
({ selectedTabId, setSelectedTabId, handleOnEventClosed }) => {
|
||||
({ flyoutIsExpandable, selectedTabId, setSelectedTabId, tabs }) => {
|
||||
const onSelectedTabChanged = (id: RightPanelPaths) => setSelectedTabId(id);
|
||||
const renderTabs = tabs.map((tab, index) => (
|
||||
<EuiTab
|
||||
|
@ -38,20 +52,22 @@ export const PanelHeader: VFC<PanelHeaderProps> = memo(
|
|||
<EuiFlyoutHeader
|
||||
hasBorder
|
||||
css={css`
|
||||
margin-bottom: -24px;
|
||||
margin-bottom: ${flyoutIsExpandable ? '-24px' : '0px'};
|
||||
`}
|
||||
>
|
||||
<div
|
||||
// moving the buttons up in the header
|
||||
css={css`
|
||||
margin-top: -24px;
|
||||
margin-left: -8px;
|
||||
`}
|
||||
>
|
||||
<ExpandDetailButton />
|
||||
</div>
|
||||
{flyoutIsExpandable && (
|
||||
<div
|
||||
// moving the buttons up in the header
|
||||
css={css`
|
||||
margin-top: -24px;
|
||||
margin-left: -8px;
|
||||
`}
|
||||
>
|
||||
<ExpandDetailButton />
|
||||
</div>
|
||||
)}
|
||||
<EuiSpacer size="xs" />
|
||||
<HeaderTitle />
|
||||
<HeaderTitle flyoutIsExpandable={flyoutIsExpandable} />
|
||||
<EuiSpacer size="m" />
|
||||
<EuiTabs
|
||||
size="l"
|
||||
|
|
|
@ -9,6 +9,8 @@ import type { FC } from 'react';
|
|||
import React, { memo, useMemo } from 'react';
|
||||
import type { FlyoutPanelProps, PanelPath } from '@kbn/expandable-flyout';
|
||||
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
|
||||
import { EventKind } from '../shared/hooks/use_fetch_field_value_pair_by_event_type';
|
||||
import { getField } from '../shared/utils';
|
||||
import { useRightPanelContext } from './context';
|
||||
import { PanelHeader } from './header';
|
||||
import { PanelContent } from './content';
|
||||
|
@ -34,13 +36,17 @@ export interface RightPanelProps extends FlyoutPanelProps {
|
|||
*/
|
||||
export const RightPanel: FC<Partial<RightPanelProps>> = memo(({ path }) => {
|
||||
const { openRightPanel } = useExpandableFlyoutContext();
|
||||
const { eventId, indexName, scopeId } = useRightPanelContext();
|
||||
const { eventId, getFieldsData, indexName, scopeId } = useRightPanelContext();
|
||||
|
||||
// for 8.10, we only render the flyout in its expandable mode if the document viewed is of type signal
|
||||
const documentIsSignal = getField(getFieldsData('event.kind')) === EventKind.signal;
|
||||
const tabsDisplayed = documentIsSignal ? tabs : tabs.filter((tab) => tab.id !== 'overview');
|
||||
|
||||
const selectedTabId = useMemo(() => {
|
||||
const defaultTab = tabs[0].id;
|
||||
const defaultTab = tabsDisplayed[0].id;
|
||||
if (!path) return defaultTab;
|
||||
return tabs.map((tab) => tab.id).find((tabId) => tabId === path.tab) ?? defaultTab;
|
||||
}, [path]);
|
||||
return tabsDisplayed.map((tab) => tab.id).find((tabId) => tabId === path.tab) ?? defaultTab;
|
||||
}, [path, tabsDisplayed]);
|
||||
|
||||
const setSelectedTabId = (tabId: RightPanelTabsType[number]['id']) => {
|
||||
openRightPanel({
|
||||
|
@ -58,8 +64,13 @@ export const RightPanel: FC<Partial<RightPanelProps>> = memo(({ path }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<PanelHeader selectedTabId={selectedTabId} setSelectedTabId={setSelectedTabId} />
|
||||
<PanelContent selectedTabId={selectedTabId} />
|
||||
<PanelHeader
|
||||
flyoutIsExpandable={documentIsSignal}
|
||||
tabs={tabsDisplayed}
|
||||
selectedTabId={selectedTabId}
|
||||
setSelectedTabId={setSelectedTabId}
|
||||
/>
|
||||
<PanelContent tabs={tabsDisplayed} selectedTabId={selectedTabId} />
|
||||
<PanelFooter />
|
||||
</>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue