Skip to content

Commit

Permalink
[Security Solution] Exp flyout expandable widget rendering (elastic#1…
Browse files Browse the repository at this point in the history
…60625)

## 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]>
  • Loading branch information
lgestc and kibanamachine authored Jul 5, 2023
1 parent bc4ffb6 commit 901b9eb
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 13 deletions.
19 changes: 12 additions & 7 deletions packages/kbn-expandable-flyout/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export interface ExpandableFlyoutProps extends EuiFlyoutProps {
handleOnFlyoutClosed?: () => void;
}

const flyoutStyles = css`
overflow-y: scroll;
`;

const flyoutInnerStyles = { height: '100%' };

/**
* Expandable flyout UI React component.
* Displays 3 sections (right, left, preview) depending on the panels in the context.
Expand Down Expand Up @@ -65,9 +71,10 @@ export const ExpandableFlyout: React.FC<ExpandableFlyoutProps> = ({
[mostRecentPreview, registeredPanels]
);

// do not add the flyout to the dom if there aren't any panels to display
if (!left && !right && !preview.length) {
return <></>;
const hideFlyout = !left && !right && !preview.length;

if (hideFlyout) {
return null;
}

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

return (
<EuiFlyout
css={css`
overflow-y: scroll;
`}
css={flyoutStyles}
{...flyoutProps}
size={flyoutWidth}
ownFocus={false}
Expand All @@ -89,7 +94,7 @@ export const ExpandableFlyout: React.FC<ExpandableFlyoutProps> = ({
direction={leftSection ? 'row' : 'column'}
wrap={false}
gutterSize="none"
style={{ height: '100%' }}
style={flyoutInnerStyles}
>
{leftSection && left ? (
<LeftSection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,14 @@ describe(
waitForAlertsToPopulate();
expandFirstAlertExpandableFlyout();
expandDocumentDetailsExpandableFlyoutLeftSection();
createNewCaseFromExpandableFlyout();
openInsightsTab();
openCorrelationsTab();
});

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

createNewCaseFromExpandableFlyout();

cy.get(DOCUMENT_DETAILS_FLYOUT_INSIGHTS_TAB).scrollIntoView();

cy.log('should render the Insights header');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
*/

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

export const HEADER_TEST_ID = 'Header';
export const CONTENT_TEST_ID = 'Content';
Expand Down Expand Up @@ -46,6 +46,8 @@ export const ExpandableSection: VFC<DescriptionSectionProps> = ({
}) => {
const accordionId = useGeneratedHtmlId({ prefix: 'accordion' });

const { renderContent, toggle, state } = useAccordionState(expanded);

const headerDataTestSub = dataTestSub + HEADER_TEST_ID;
const contentDataTestSub = dataTestSub + CONTENT_TEST_ID;

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

return (
<EuiAccordion id={accordionId} buttonContent={header} initialIsOpen={expanded}>
<EuiAccordion forceState={state} onToggle={toggle} id={accordionId} buttonContent={header}>
<EuiSpacer size="m" />
<EuiFlexGroup gutterSize="none" direction="column" data-test-subj={contentDataTestSub}>
{children}
{renderContent && children}
</EuiFlexGroup>
</EuiAccordion>
);
Expand Down
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 { useReducer } from 'react';

const CLOSED = 'closed' as const;
const OPEN = 'open' as const;

type ToggleReducerState = typeof CLOSED | typeof OPEN;
const toggleReducer = (state: ToggleReducerState) => {
return state === CLOSED ? OPEN : CLOSED;
};

export interface UseAccordionStateValue {
/**
* Should children be rendered in the dom
*/
renderContent: boolean;
/**
* Use this to control the accordion visual state
*/
state: typeof CLOSED | typeof OPEN;

/**
* Handler function for cycling between the states
*/
toggle: VoidFunction;
}

/**
* Tiny hook for controlled useAccordionState
* @param expandedInitially - is accordion expanded on first render
*/
export const useAccordionState = (expandedInitially: boolean): UseAccordionStateValue => {
const initialState = expandedInitially ? OPEN : CLOSED;
const [state, toggle] = useReducer(toggleReducer, initialState);
const renderContent = state === OPEN;

return {
renderContent,
state,
toggle,
};
};

0 comments on commit 901b9eb

Please sign in to comment.