Skip to content

Commit

Permalink
[Security Solution] expanded flyout - right section skeleton (elastic…
Browse files Browse the repository at this point in the history
…#152047)

- header with simple static title
- 3 empty tabs (overview, table and json)
  • Loading branch information
PhilippeOberti authored and bmorelli25 committed Mar 10, 2023
1 parent 2f1eb2c commit 0f1c4e0
Show file tree
Hide file tree
Showing 20 changed files with 603 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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 {
ALERT_DETAILS_FLYOUT_HEADER_TITLE,
ALERT_DETAILS_FLYOUT_JSON_TAB,
ALERT_DETAILS_FLYOUT_JSON_TAB_CONTENT,
ALERT_DETAILS_FLYOUT_OVERVIEW_TAB,
ALERT_DETAILS_FLYOUT_OVERVIEW_TAB_CONTENT,
ALERT_DETAILS_FLYOUT_TABLE_TAB,
ALERT_DETAILS_FLYOUT_TABLE_TAB_CONTENT,
} from '../../../screens/alert_details_expandable_flyout';
import {
expandFirstAlertExpandableFlyout,
openJsonTab,
openOverviewTab,
openTableTab,
} from '../../../tasks/alert_details_expandable_flyout';
import { cleanKibana } from '../../../tasks/common';
import { login, visit } from '../../../tasks/login';
import { createCustomRuleEnabled } from '../../../tasks/api_calls/rules';
import { getNewRule } from '../../../objects/rule';
import { ALERTS_URL } from '../../../urls/navigation';
import { waitForAlertsToPopulate } from '../../../tasks/create_new_rule';

// Skipping these for now as the feature is protected behind a feature flag set to false by default
// To run the tests locally, add 'securityFlyoutEnabled' in the Cypress config.ts here https://github.com/elastic/kibana/blob/main/x-pack/test/security_solution_cypress/config.ts#L50
describe.skip('Alert details expandable flyout right panel', { testIsolation: false }, () => {
before(() => {
cleanKibana();
login();
createCustomRuleEnabled(getNewRule());
visit(ALERTS_URL);
waitForAlertsToPopulate();
expandFirstAlertExpandableFlyout();
});

it('should display title in the header', () => {
cy.get(ALERT_DETAILS_FLYOUT_HEADER_TITLE)
.should('be.visible')
.and('have.text', 'Alert details');
});

it('should display 3 tabs in the right section', () => {
cy.get(ALERT_DETAILS_FLYOUT_OVERVIEW_TAB).should('be.visible').and('have.text', 'Overview');
cy.get(ALERT_DETAILS_FLYOUT_TABLE_TAB).should('be.visible').and('have.text', 'Table');
cy.get(ALERT_DETAILS_FLYOUT_JSON_TAB).should('be.visible').and('have.text', 'JSON');
});

it('should display tab content when switching tabs in the right section', () => {
openOverviewTab();
cy.get(ALERT_DETAILS_FLYOUT_OVERVIEW_TAB_CONTENT).should('be.visible');

openTableTab();
cy.get(ALERT_DETAILS_FLYOUT_TABLE_TAB_CONTENT).should('be.visible');

openJsonTab();
cy.get(ALERT_DETAILS_FLYOUT_JSON_TAB_CONTENT).should('be.visible');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 {
JSON_TAB_TEST_ID,
OVERVIEW_TAB_TEST_ID,
TABLE_TAB_TEST_ID,
} from '../../public/flyout/right/test_ids';
import {
JSON_TAB_CONTENT_TEST_ID,
OVERVIEW_TAB_CONTENT_TEST_ID,
TABLE_TAB_CONTENT_TEST_ID,
} from '../../public/flyout/right/tabs/test_ids';
import { FLYOUT_HEADER_TITLE } from '../../public/flyout/right/components/test_ids';

export const ALERT_DETAILS_FLYOUT_HEADER_TITLE = `[data-test-subj="${FLYOUT_HEADER_TITLE}"]`;
export const ALERT_DETAILS_FLYOUT_OVERVIEW_TAB = `[data-test-subj="${OVERVIEW_TAB_TEST_ID}"]`;
export const ALERT_DETAILS_FLYOUT_TABLE_TAB = `[data-test-subj="${TABLE_TAB_TEST_ID}"]`;
export const ALERT_DETAILS_FLYOUT_JSON_TAB = `[data-test-subj="${JSON_TAB_TEST_ID}"]`;
export const ALERT_DETAILS_FLYOUT_OVERVIEW_TAB_CONTENT = `[data-test-subj="${OVERVIEW_TAB_CONTENT_TEST_ID}"]`;
export const ALERT_DETAILS_FLYOUT_TABLE_TAB_CONTENT = `[data-test-subj="${TABLE_TAB_CONTENT_TEST_ID}"]`;
export const ALERT_DETAILS_FLYOUT_JSON_TAB_CONTENT = `[data-test-subj="${JSON_TAB_CONTENT_TEST_ID}"]`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* 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 {
ALERT_DETAILS_FLYOUT_JSON_TAB,
ALERT_DETAILS_FLYOUT_OVERVIEW_TAB,
ALERT_DETAILS_FLYOUT_TABLE_TAB,
} from '../screens/alert_details_expandable_flyout';
import { EXPAND_ALERT_BTN } from '../screens/alerts';

/**
* Find the first alert row in the alerts table then click on the expand icon button to open the flyout
*/
export const expandFirstAlertExpandableFlyout = () => {
cy.get(EXPAND_ALERT_BTN).first().click();
};

/**
* Open the Overview tab in the alert details expandable flyout right section
*/
export const openOverviewTab = () =>
cy.get(ALERT_DETAILS_FLYOUT_OVERVIEW_TAB).should('be.visible').click();

/**
* Open the Table tab in the alert details expandable flyout right section
*/
export const openTableTab = () =>
cy.get(ALERT_DETAILS_FLYOUT_TABLE_TAB).should('be.visible').click();

/**
* Open the Json tab in the alert details expandable flyout right section
*/
export const openJsonTab = () => cy.get(ALERT_DETAILS_FLYOUT_JSON_TAB).should('be.visible').click();
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid';
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template';
import { ExpandableFlyout, ExpandableFlyoutProvider } from '@kbn/expandable-flyout';
import { expandableFlyoutDocumentsPanels } from '../../../flyout';
import { useSecuritySolutionNavigation } from '../../../common/components/navigation/use_security_solution_navigation';
import { TimelineId } from '../../../../common/types/timeline';
import { getTimelineShowStatusByIdSelector } from '../../../timelines/components/flyout/selectors';
Expand Down Expand Up @@ -107,7 +108,7 @@ export const SecuritySolutionTemplateWrapper: React.FC<Omit<KibanaPageTemplatePr
</EuiThemeProvider>
</KibanaPageTemplate.BottomBar>
)}
<ExpandableFlyout registeredPanels={[]} onClose={() => {}} />
<ExpandableFlyout registeredPanels={expandableFlyoutDocumentsPanels} onClose={() => {}} />
</StyledKibanaPageTemplate>
</ExpandableFlyoutProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { EuiDataGridCellValueElementProps } from '@elastic/eui';
import React, { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { RightPanelKey } from '../../../../flyout/right';
import type {
SetEventsDeleted,
SetEventsLoading,
Expand Down Expand Up @@ -96,7 +97,15 @@ const RowActionComponent = ({
};

if (isSecurityFlyoutEnabled) {
openFlyout({});
openFlyout({
right: {
id: RightPanelKey,
params: {
id: eventId,
indexName,
},
},
});
} else {
dispatch(
dataTableActions.toggleDetailPanel({
Expand Down
44 changes: 44 additions & 0 deletions x-pack/plugins/security_solution/public/flyout/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# expandable flyout panels

## Description

This folder hosts the panels that are displayed in the expandable flyout (see `@kbn/expandable-flyout` package).

> Remember to add any new panels to the `index.tsx` at the root of the `flyout` folder. These are passed to the `@kbn/expandable-flyout` package as `registeredPanels`.
## Notes

At the moment, we only have a single expandable flyout for the Security Solution plugin. This flyout will be used for all documents (signals, events, indicators, assets and findings). We're using a set of generic right/left/preview panels, hence the following folder structure:

```
flyout
│ index.tsx
│ README.md
└───right
└───left
└───preview
```

If different right, left or preview panels are needed, we should refactor the folder structure as follows:

```
flyout
│ index.tsx
│ README.md
└───documents
│ └───right
│ └───left
│ └───preview
└───new_type
│ └───right
│ └───left
│ └───preview
└───other_new_type
└───right
└───left
└───preview
```
28 changes: 28 additions & 0 deletions x-pack/plugins/security_solution/public/flyout/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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 type { ExpandableFlyoutProps } from '@kbn/expandable-flyout';
import type { RightPanelProps } from './right';
import { RightPanel, RightPanelKey } from './right';
import { RightPanelProvider } from './right/context';

/**
* List of all panels that will be used within the document details expandable flyout.
* This needs to be passed to the expandable flyout registeredPanels property.
*/
export const expandableFlyoutDocumentsPanels: ExpandableFlyoutProps['registeredPanels'] = [
{
key: RightPanelKey,
width: 500,
component: (props) => (
<RightPanelProvider {...(props as RightPanelProps).params}>
<RightPanel path={props.path as RightPanelProps['path']} />
</RightPanelProvider>
),
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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, { memo } from 'react';
import { EuiSpacer, EuiTitle } from '@elastic/eui';
import { FLYOUT_HEADER_TITLE } from './test_ids';
import { HEADER_TITLE } from '../translations';

/**
* Document details flyout right section header
*/
export const HeaderTitle: FC = memo(() => {
return (
<>
<EuiTitle size="s" data-test-subj={FLYOUT_HEADER_TITLE}>
<h4>{HEADER_TITLE}</h4>
</EuiTitle>
<EuiSpacer size="m" />
</>
);
});

HeaderTitle.displayName = 'HeaderTitle';
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 const FLYOUT_HEADER_TITLE = 'securitySolutionDocumentDetailsFlyoutHeaderTitle';
33 changes: 33 additions & 0 deletions x-pack/plugins/security_solution/public/flyout/right/content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 { EuiFlyoutBody } from '@elastic/eui';
import type { VFC } from 'react';
import React, { useMemo } from 'react';
import type { RightPanelPaths } from '.';
import { tabs } from './tabs';

export interface PanelContentProps {
/**
* Id of the tab selected in the parent component to display its content
*/
selectedTabId: RightPanelPaths;
}

/**
* Document details expandable flyout right section, that will display the content
* of the overview, table and json tabs.
*/
export const PanelContent: VFC<PanelContentProps> = ({ selectedTabId }) => {
const selectedTabContent = useMemo(() => {
return tabs.find((tab) => tab.id === selectedTabId)?.content;
}, [selectedTabId]);

return <EuiFlyoutBody>{selectedTabContent}</EuiFlyoutBody>;
};

PanelContent.displayName = 'PanelContent';
48 changes: 48 additions & 0 deletions x-pack/plugins/security_solution/public/flyout/right/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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, { createContext, useContext } from 'react';
import type { RightPanelProps } from '.';

export interface RightPanelContext {
/**
* Id of the document
*/
eventId: string;
/**
* Name of the index used in the parent's page
*/
indexName: string;
}

export const RightPanelContext = createContext<RightPanelContext | undefined>(undefined);

export type RightPanelProviderProps = {
/**
* React components to render
*/
children: React.ReactNode;
} & Partial<RightPanelProps['params']>;

export const RightPanelProvider = ({ id, indexName, children }: RightPanelProviderProps) => {
const contextValue = {
eventId: id as string,
indexName: indexName as string,
};

return <RightPanelContext.Provider value={contextValue}>{children}</RightPanelContext.Provider>;
};

export const useRightPanelContext = (): RightPanelContext => {
const contextValue = useContext(RightPanelContext);

if (!contextValue) {
throw new Error('RightPanelContext can only be used within RightPanelContext provider');
}

return contextValue;
};
Loading

0 comments on commit 0f1c4e0

Please sign in to comment.