From 901b9eb127ff6604218f13fbe0422af68b2229e7 Mon Sep 17 00:00:00 2001 From: Luke <11671118+lgestc@users.noreply.github.com> Date: Wed, 5 Jul 2023 15:20:55 +0200 Subject: [PATCH] [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 <42973632+kibanamachine@users.noreply.github.com> --- packages/kbn-expandable-flyout/src/index.tsx | 19 +++++--- ..._details_left_panel_correlations_tab.cy.ts | 3 +- .../right/components/expandable_section.tsx | 10 ++-- .../flyout/right/hooks/use_accordion_state.ts | 48 +++++++++++++++++++ 4 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/flyout/right/hooks/use_accordion_state.ts diff --git a/packages/kbn-expandable-flyout/src/index.tsx b/packages/kbn-expandable-flyout/src/index.tsx index 8dc44a87dab45..8a41dcee48729 100644 --- a/packages/kbn-expandable-flyout/src/index.tsx +++ b/packages/kbn-expandable-flyout/src/index.tsx @@ -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. @@ -65,9 +71,10 @@ export const ExpandableFlyout: React.FC = ({ [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'; @@ -77,9 +84,7 @@ export const ExpandableFlyout: React.FC = ({ return ( = ({ direction={leftSection ? 'row' : 'column'} wrap={false} gutterSize="none" - style={{ height: '100%' }} + style={flyoutInnerStyles} > {leftSection && left ? ( { 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'); diff --git a/x-pack/plugins/security_solution/public/flyout/right/components/expandable_section.tsx b/x-pack/plugins/security_solution/public/flyout/right/components/expandable_section.tsx index 4cb7595798fef..66da1a50eb082 100644 --- a/x-pack/plugins/security_solution/public/flyout/right/components/expandable_section.tsx +++ b/x-pack/plugins/security_solution/public/flyout/right/components/expandable_section.tsx @@ -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'; @@ -46,6 +46,8 @@ export const ExpandableSection: VFC = ({ }) => { const accordionId = useGeneratedHtmlId({ prefix: 'accordion' }); + const { renderContent, toggle, state } = useAccordionState(expanded); + const headerDataTestSub = dataTestSub + HEADER_TEST_ID; const contentDataTestSub = dataTestSub + CONTENT_TEST_ID; @@ -56,10 +58,10 @@ export const ExpandableSection: VFC = ({ ); return ( - + - {children} + {renderContent && children} ); diff --git a/x-pack/plugins/security_solution/public/flyout/right/hooks/use_accordion_state.ts b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_accordion_state.ts new file mode 100644 index 0000000000000..b3d93c0ed056b --- /dev/null +++ b/x-pack/plugins/security_solution/public/flyout/right/hooks/use_accordion_state.ts @@ -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, + }; +};