Skip to content

Commit

Permalink
[Security Solution] expandable flyout - expandable mode only for sign…
Browse files Browse the repository at this point in the history
…al documents (elastic#163557)
  • Loading branch information
PhilippeOberti authored Aug 16, 2023
1 parent d74f4d3 commit f38a37c
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
Expand All @@ -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;
`}
>
Expand Down
11 changes: 8 additions & 3 deletions x-pack/plugins/security_solution/public/flyout/right/content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
});
});
44 changes: 30 additions & 14 deletions x-pack/plugins/security_solution/public/flyout/right/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"
Expand Down
23 changes: 17 additions & 6 deletions x-pack/plugins/security_solution/public/flyout/right/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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({
Expand All @@ -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 />
</>
);
Expand Down

0 comments on commit f38a37c

Please sign in to comment.