Skip to content

Commit 901b9eb

Browse files
[Security Solution] Exp flyout expandable widget rendering (#160625)
## Summary This tiny PR ensures that widget contents are not rendered until expanded (in the expandable flyout). This will prevent unnecessary requests being sent when we open the flyout. Co-authored-by: Kibana Machine <[email protected]>
1 parent bc4ffb6 commit 901b9eb

File tree

4 files changed

+67
-13
lines changed

4 files changed

+67
-13
lines changed

packages/kbn-expandable-flyout/src/index.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ export interface ExpandableFlyoutProps extends EuiFlyoutProps {
2727
handleOnFlyoutClosed?: () => void;
2828
}
2929

30+
const flyoutStyles = css`
31+
overflow-y: scroll;
32+
`;
33+
34+
const flyoutInnerStyles = { height: '100%' };
35+
3036
/**
3137
* Expandable flyout UI React component.
3238
* Displays 3 sections (right, left, preview) depending on the panels in the context.
@@ -65,9 +71,10 @@ export const ExpandableFlyout: React.FC<ExpandableFlyoutProps> = ({
6571
[mostRecentPreview, registeredPanels]
6672
);
6773

68-
// do not add the flyout to the dom if there aren't any panels to display
69-
if (!left && !right && !preview.length) {
70-
return <></>;
74+
const hideFlyout = !left && !right && !preview.length;
75+
76+
if (hideFlyout) {
77+
return null;
7178
}
7279

7380
const flyoutWidth: string = leftSection && rightSection ? 'l' : 's';
@@ -77,9 +84,7 @@ export const ExpandableFlyout: React.FC<ExpandableFlyoutProps> = ({
7784

7885
return (
7986
<EuiFlyout
80-
css={css`
81-
overflow-y: scroll;
82-
`}
87+
css={flyoutStyles}
8388
{...flyoutProps}
8489
size={flyoutWidth}
8590
ownFocus={false}
@@ -89,7 +94,7 @@ export const ExpandableFlyout: React.FC<ExpandableFlyoutProps> = ({
8994
direction={leftSection ? 'row' : 'column'}
9095
wrap={false}
9196
gutterSize="none"
92-
style={{ height: '100%' }}
97+
style={flyoutInnerStyles}
9398
>
9499
{leftSection && left ? (
95100
<LeftSection

x-pack/plugins/security_solution/cypress/e2e/investigations/alerts/expandable_flyout/alert_details_left_panel_correlations_tab.cy.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,14 @@ describe(
4646
waitForAlertsToPopulate();
4747
expandFirstAlertExpandableFlyout();
4848
expandDocumentDetailsExpandableFlyoutLeftSection();
49+
createNewCaseFromExpandableFlyout();
4950
openInsightsTab();
5051
openCorrelationsTab();
5152
});
5253

5354
it('should render correlations details correctly', () => {
5455
cy.log('link the alert to a new case');
5556

56-
createNewCaseFromExpandableFlyout();
57-
5857
cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB).scrollIntoView();
5958

6059
cy.log('should render the Insights header');

x-pack/plugins/security_solution/public/flyout/right/components/expandable_section.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
*/
77

88
import { EuiAccordion, EuiFlexGroup, EuiSpacer, EuiTitle, useGeneratedHtmlId } from '@elastic/eui';
9-
import type { VFC } from 'react';
10-
import React from 'react';
9+
import React, { type VFC } from 'react';
10+
import { useAccordionState } from '../hooks/use_accordion_state';
1111

1212
export const HEADER_TEST_ID = 'Header';
1313
export const CONTENT_TEST_ID = 'Content';
@@ -46,6 +46,8 @@ export const ExpandableSection: VFC<DescriptionSectionProps> = ({
4646
}) => {
4747
const accordionId = useGeneratedHtmlId({ prefix: 'accordion' });
4848

49+
const { renderContent, toggle, state } = useAccordionState(expanded);
50+
4951
const headerDataTestSub = dataTestSub + HEADER_TEST_ID;
5052
const contentDataTestSub = dataTestSub + CONTENT_TEST_ID;
5153

@@ -56,10 +58,10 @@ export const ExpandableSection: VFC<DescriptionSectionProps> = ({
5658
);
5759

5860
return (
59-
<EuiAccordion id={accordionId} buttonContent={header} initialIsOpen={expanded}>
61+
<EuiAccordion forceState={state} onToggle={toggle} id={accordionId} buttonContent={header}>
6062
<EuiSpacer size="m" />
6163
<EuiFlexGroup gutterSize="none" direction="column" data-test-subj={contentDataTestSub}>
62-
{children}
64+
{renderContent && children}
6365
</EuiFlexGroup>
6466
</EuiAccordion>
6567
);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { useReducer } from 'react';
9+
10+
const CLOSED = 'closed' as const;
11+
const OPEN = 'open' as const;
12+
13+
type ToggleReducerState = typeof CLOSED | typeof OPEN;
14+
const toggleReducer = (state: ToggleReducerState) => {
15+
return state === CLOSED ? OPEN : CLOSED;
16+
};
17+
18+
export interface UseAccordionStateValue {
19+
/**
20+
* Should children be rendered in the dom
21+
*/
22+
renderContent: boolean;
23+
/**
24+
* Use this to control the accordion visual state
25+
*/
26+
state: typeof CLOSED | typeof OPEN;
27+
28+
/**
29+
* Handler function for cycling between the states
30+
*/
31+
toggle: VoidFunction;
32+
}
33+
34+
/**
35+
* Tiny hook for controlled useAccordionState
36+
* @param expandedInitially - is accordion expanded on first render
37+
*/
38+
export const useAccordionState = (expandedInitially: boolean): UseAccordionStateValue => {
39+
const initialState = expandedInitially ? OPEN : CLOSED;
40+
const [state, toggle] = useReducer(toggleReducer, initialState);
41+
const renderContent = state === OPEN;
42+
43+
return {
44+
renderContent,
45+
state,
46+
toggle,
47+
};
48+
};

0 commit comments

Comments
 (0)