Skip to content

Commit

Permalink
[Security Solution] Expandable flyout - add rule preview skeleton (el…
Browse files Browse the repository at this point in the history
…astic#161999)

## Summary

This PR adds a rule preview panel to the expandable flyout:

- Preview panel skeleton is added, now we can open a preview on top of
right section of flyout
- Go to rule details button is replaced by a button that will open a
rule preview panel
- The rule preview contains placeholder sections (About, Definition,
Schedule) and footer with a link to rule details page


![image](https://github.com/elastic/kibana/assets/18648970/5510982a-91e5-4747-ae23-a8b5b87e0041)

**How to test**

- add `xpack.securitySolution.enableExperimental:
['securityFlyoutEnabled']` to the `kibana.dev.json` file
- go to the Alerts page, and click on the expand detail button on any
row of the table
- click on `Overview`, `About`, then `Rule summary`

### 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/packages/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

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
2 people authored and Devon Thomson committed Aug 1, 2023
1 parent 9131e78 commit 70eb71e
Show file tree
Hide file tree
Showing 31 changed files with 886 additions and 87 deletions.
70 changes: 58 additions & 12 deletions packages/kbn-expandable-flyout/src/components/preview_section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,55 @@ import {
EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
EuiPanel,
EuiText,
useEuiTheme,
EuiSplitPanel,
} from '@elastic/eui';
import React from 'react';
import { css } from '@emotion/react';

import { has } from 'lodash';
import {
PREVIEW_SECTION,
PREVIEW_SECTION_BACK_BUTTON,
PREVIEW_SECTION_CLOSE_BUTTON,
PREVIEW_SECTION_HEADER,
} from './test_ids';
import { useExpandableFlyoutContext } from '../..';
import { BACK_BUTTON, CLOSE_BUTTON } from './translations';

export interface PreviewBanner {
/**
* Optional title to be shown
*/
title?: string;
/**
* Optional string for background color
*/
backgroundColor?:
| 'primary'
| 'plain'
| 'warning'
| 'accent'
| 'success'
| 'danger'
| 'transparent'
| 'subdued';
/**
* Optional string for text color
*/
textColor?: string;
}

/**
* Type guard to check the passed object is of preview banner type
* @param banner passed from panel params
* @returns a boolean to indicate whether the banner passed is a preview banner
*/
export const isPreviewBanner = (banner: unknown): banner is PreviewBanner => {
return has(banner, 'title') || has(banner, 'backgroundColor') || has(banner, 'textColor');
};

interface PreviewSectionProps {
/**
* Component to be rendered
Expand All @@ -37,6 +73,10 @@ interface PreviewSectionProps {
* Display the back button in the header
*/
showBackButton: boolean;
/**
* Preview banner shown at the top of preview panel
*/
banner?: PreviewBanner;
}

/**
Expand All @@ -47,6 +87,7 @@ export const PreviewSection: React.FC<PreviewSectionProps> = ({
component,
showBackButton,
width,
banner,
}: PreviewSectionProps) => {
const { euiTheme } = useEuiTheme();
const { closePreviewPanel, previousPreviewPanel } = useExpandableFlyoutContext();
Expand Down Expand Up @@ -95,27 +136,32 @@ export const PreviewSection: React.FC<PreviewSectionProps> = ({
opacity: 0.5;
`}
/>
<div
<EuiSplitPanel.Outer
css={css`
margin: ${euiTheme.size.xs};
height: 99%;
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: ${left};
z-index: 1000;
`}
className="eui-yScroll"
data-test-subj={PREVIEW_SECTION}
>
<EuiPanel
css={css`
margin: ${euiTheme.size.xs};
height: 100%;
`}
data-test-subj={PREVIEW_SECTION}
>
{isPreviewBanner(banner) && (
<EuiSplitPanel.Inner grow={false} color={banner.backgroundColor} paddingSize="none">
<EuiText textAlign="center" color={banner.textColor} size="s">
{banner.title}
</EuiText>
</EuiSplitPanel.Inner>
)}
<EuiSplitPanel.Inner grow={false} paddingSize="s" data-test-subj={PREVIEW_SECTION_HEADER}>
{header}
{component}
</EuiPanel>
</div>
</EuiSplitPanel.Inner>
<EuiSplitPanel.Inner paddingSize="none">{component}</EuiSplitPanel.Inner>
</EuiSplitPanel.Outer>
</>
);
};
Expand Down
2 changes: 2 additions & 0 deletions packages/kbn-expandable-flyout/src/components/test_ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ export const PREVIEW_SECTION = 'previewSection';
export const PREVIEW_SECTION_CLOSE_BUTTON = 'previewSectionCloseButton';

export const PREVIEW_SECTION_BACK_BUTTON = 'previewSectionBackButton';

export const PREVIEW_SECTION_HEADER = 'previewSectionHeader';
6 changes: 6 additions & 0 deletions packages/kbn-expandable-flyout/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { PreviewSection } from './components/preview_section';
import { RightSection } from './components/right_section';
import type { FlyoutPanelProps, Panel } from './types';
import { LeftSection } from './components/left_section';
import { isPreviewBanner } from './components/preview_section';

export interface ExpandableFlyoutProps extends Omit<EuiFlyoutProps, 'onClose'> {
/**
Expand Down Expand Up @@ -65,6 +66,10 @@ export const ExpandableFlyout: React.FC<ExpandableFlyoutProps> = ({

// retrieve the last preview panel (most recent)
const mostRecentPreview = preview ? preview[preview.length - 1] : undefined;
const previewBanner = isPreviewBanner(mostRecentPreview?.params?.banner)
? mostRecentPreview?.params?.banner
: undefined;

const showBackButton = preview && preview.length > 1;
const previewSection = useMemo(
() => registeredPanels.find((panel) => panel.key === mostRecentPreview?.id),
Expand Down Expand Up @@ -115,6 +120,7 @@ export const ExpandableFlyout: React.FC<ExpandableFlyoutProps> = ({
component={previewSection.component({ ...(mostRecentPreview as FlyoutPanelProps) })}
showBackButton={showBackButton}
width={previewSectionWidth}
banner={previewBanner}
/>
) : null}
</EuiFlyout>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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 { expandFirstAlertExpandableFlyout } from '../../../../tasks/expandable_flyout/common';
import {
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SECTION,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_HEADER,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_BODY,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_ABOUT_SECTION_HEADER,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_ABOUT_SECTION_CONTENT,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_DEFINITION_SECTION_HEADER,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_DEFINITION_SECTION_CONTENT,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SCHEDULE_SECTION_HEADER,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SCHEDULE_SECTION_CONTENT,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_FOOTER,
} from '../../../../screens/expandable_flyout/alert_details_preview_panel_rule_preview';
import {
toggleRulePreviewAboutSection,
toggleRulePreviewDefinitionSection,
toggleRulePreviewScheduleSection,
} from '../../../../tasks/expandable_flyout/alert_details_preview_panel_rule_preview';
import { clickRuleSummaryButton } from '../../../../tasks/expandable_flyout/alert_details_right_panel_overview_tab';
import { cleanKibana } from '../../../../tasks/common';
import { login, visit } from '../../../../tasks/login';
import { createRule } from '../../../../tasks/api_calls/rules';
import { getNewRule } from '../../../../objects/rule';
import { ALERTS_URL } from '../../../../urls/navigation';
import { waitForAlertsToPopulate } from '../../../../tasks/create_new_rule';

describe(
'Alert details expandable flyout rule preview panel',
{ env: { ftrConfig: { enableExperimental: ['securityFlyoutEnabled'] } } },
() => {
const rule = getNewRule();

beforeEach(() => {
cleanKibana();
login();
createRule(rule);
visit(ALERTS_URL);
waitForAlertsToPopulate();
expandFirstAlertExpandableFlyout();
clickRuleSummaryButton();
});

describe('rule preview', () => {
it('should display rule preview and its sub sections', () => {
cy.log('rule preview panel');

cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SECTION).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SECTION).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_HEADER).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_BODY).should('be.visible');
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_FOOTER).should('be.visible');

cy.log('about');

cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_ABOUT_SECTION_HEADER)
.should('be.visible')
.and('contain.text', 'About');
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_ABOUT_SECTION_CONTENT).should('be.visible');
toggleRulePreviewAboutSection();

cy.log('definition');

toggleRulePreviewDefinitionSection();
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_DEFINITION_SECTION_HEADER)
.should('be.visible')
.and('contain.text', 'Definition');
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_DEFINITION_SECTION_CONTENT).should(
'be.visible'
);
toggleRulePreviewDefinitionSection();

cy.log('schedule');

toggleRulePreviewScheduleSection();
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SCHEDULE_SECTION_HEADER)
.should('be.visible')
.and('contain.text', 'Schedule');
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SCHEDULE_SECTION_CONTENT).should('be.visible');
toggleRulePreviewScheduleSection();
});
});
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_DETAILS,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_EXPAND_BUTTON,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_TITLE,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_NAVIGATE_TO_RULE_DETAILS_BUTTON,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_OPEN_RULE_PREVIEW_BUTTON,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_DETAILS,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_GO_TO_TABLE_LINK,
DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_HIGHLIGHTED_FIELDS_HEADER_TITLE,
Expand Down Expand Up @@ -98,9 +98,9 @@ describe(
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_TITLE)
.should('be.visible')
.within(() => {
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_NAVIGATE_TO_RULE_DETAILS_BUTTON)
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_OPEN_RULE_PREVIEW_BUTTON)
.should('be.visible')
.and('contain.text', 'View rule');
.and('have.text', 'Rule summary');
});
cy.get(DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_DETAILS)
.should('be.visible')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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 {
RULE_PREVIEW_BODY_TEST_ID,
RULE_PREVIEW_ABOUT_HEADER_TEST_ID,
RULE_PREVIEW_ABOUT_CONTENT_TEST_ID,
RULE_PREVIEW_DEFINITION_HEADER_TEST_ID,
RULE_PREVIEW_DEFINITION_CONTENT_TEST_ID,
RULE_PREVIEW_SCHEDULE_HEADER_TEST_ID,
RULE_PREVIEW_SCHEDULE_CONTENT_TEST_ID,
RULE_PREVIEW_FOOTER_TEST_ID,
} from '../../../public/flyout/preview/components/test_ids';
import { getDataTestSubjectSelector } from '../../helpers/common';

export const DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SECTION =
getDataTestSubjectSelector('previewSection');

export const DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_HEADER =
getDataTestSubjectSelector('previewSectionHeader');

export const DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_BODY =
getDataTestSubjectSelector(RULE_PREVIEW_BODY_TEST_ID);

export const DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_ABOUT_SECTION_HEADER = getDataTestSubjectSelector(
RULE_PREVIEW_ABOUT_HEADER_TEST_ID
);
export const DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_ABOUT_SECTION_CONTENT =
getDataTestSubjectSelector(RULE_PREVIEW_ABOUT_CONTENT_TEST_ID);

export const DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_DEFINITION_SECTION_HEADER =
getDataTestSubjectSelector(RULE_PREVIEW_DEFINITION_HEADER_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_DEFINITION_SECTION_CONTENT =
getDataTestSubjectSelector(RULE_PREVIEW_DEFINITION_CONTENT_TEST_ID);

export const DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SCHEDULE_SECTION_HEADER =
getDataTestSubjectSelector(RULE_PREVIEW_SCHEDULE_HEADER_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SCHEDULE_SECTION_CONTENT =
getDataTestSubjectSelector(RULE_PREVIEW_SCHEDULE_CONTENT_TEST_ID);

export const DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_FOOTER = getDataTestSubjectSelector(
RULE_PREVIEW_FOOTER_TEST_ID
);
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
DESCRIPTION_DETAILS_TEST_ID,
DESCRIPTION_EXPAND_BUTTON_TEST_ID,
DESCRIPTION_TITLE_TEST_ID,
DESCRIPTION_NAVIGATE_TO_RULE_TEST_ID,
RULE_SUMMARY_BUTTON_TEST_ID,
ENTITIES_CONTENT_TEST_ID,
ENTITIES_HEADER_TEST_ID,
ENTITIES_VIEW_ALL_BUTTON_TEST_ID,
Expand Down Expand Up @@ -58,8 +58,8 @@ export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_TITLE =
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_DETAILS = getDataTestSubjectSelector(
DESCRIPTION_DETAILS_TEST_ID
);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_NAVIGATE_TO_RULE_DETAILS_BUTTON =
getDataTestSubjectSelector(DESCRIPTION_NAVIGATE_TO_RULE_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_OPEN_RULE_PREVIEW_BUTTON =
getDataTestSubjectSelector(RULE_SUMMARY_BUTTON_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_DESCRIPTION_EXPAND_BUTTON =
getDataTestSubjectSelector(DESCRIPTION_EXPAND_BUTTON_TEST_ID);
export const DOCUMENT_DETAILS_FLYOUT_OVERVIEW_TAB_REASON_TITLE =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 {
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_ABOUT_SECTION_HEADER,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_DEFINITION_SECTION_HEADER,
DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SCHEDULE_SECTION_HEADER,
} from '../../screens/expandable_flyout/alert_details_preview_panel_rule_preview';

/* About section */

/**
* Toggle the About Section in Rule Preview panel
*/
export const toggleRulePreviewAboutSection = () => {
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_ABOUT_SECTION_HEADER).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_ABOUT_SECTION_HEADER).should('be.visible').click();
};

/* Definition section */

/**
* Toggle the Definition Section in Rule Preview panel
*/
export const toggleRulePreviewDefinitionSection = () => {
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_DEFINITION_SECTION_HEADER).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_DEFINITION_SECTION_HEADER)
.should('be.visible')
.click();
};

/* Schedule section */

/**
* Toggle the Schedule Section in Rule Preview panel
*/
export const toggleRulePreviewScheduleSection = () => {
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SCHEDULE_SECTION_HEADER).scrollIntoView();
cy.get(DOCUMENT_DETAILS_FLYOUT_RULE_PREVIEW_SCHEDULE_SECTION_HEADER).should('be.visible').click();
};
Loading

0 comments on commit 70eb71e

Please sign in to comment.