[Security Solution][Flyout] Split read and write access to the flyout (#175061)

## Summary

As part of the optimization effort within the flyout, this PR splits
reads and writes in the public API.

This brings in:
- the ability to avoid re-renders in components that only need to call
api methods of the flyout
- visibility - it is now immediately clear whether or not given consumer
mutates the flyout state or just reads it

**See the changes to the README for the idea**
This commit is contained in:
Luke G 2024-01-21 20:36:50 +01:00 committed by GitHub
parent df6f4fa01e
commit 345e359b90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 128 additions and 98 deletions

View file

@ -27,7 +27,7 @@ The expandable-flyout is making some strict UI design decisions:
The ExpandableFlyout [React component](https://github.com/elastic/kibana/tree/main/packages/kbn-expandable-flyout/src/index.tsx) renders the UI, leveraging an [EuiFlyout](https://eui.elastic.co/#/layout/flyout).
The ExpandableFlyout [React context](https://github.com/elastic/kibana/blob/main/packages/kbn-expandable-flyout/src/context.tsx) manages the internal state of the expandable flyout, and exposes the following api:
The ExpandableFlyout [hooks](https://github.com/elastic/kibana/blob/main/packages/kbn-expandable-flyout/src/context.tsx) expose the state and the following api:
- **openFlyout**: open the flyout with a set of panels
- **openRightPanel**: open a right panel
- **openLeftPanel**: open a left panel
@ -38,7 +38,9 @@ The ExpandableFlyout [React context](https://github.com/elastic/kibana/blob/main
- **previousPreviewPanel**: navigate to the previous preview panel
- **closeFlyout**: close the flyout
To retrieve the flyout's layout (left, right and preview panels), you can use the **panels** from the same [React context](https://github.com/elastic/kibana/blob/main/packages/kbn-expandable-flyout/src/context.tsx).
To retrieve the flyout's layout (left, right and preview panels), you can use the **useExpandableFlyoutState** from the same [React context](https://github.com/elastic/kibana/blob/main/packages/kbn-expandable-flyout/src/context.tsx).
To control (or mutate) flyout's layout, you can use the **useExpandableFlyoutApi** from the same [React context](https://github.com/elastic/kibana/blob/main/packages/kbn-expandable-flyout/src/context.tsx).
## Usage

View file

@ -8,7 +8,13 @@
export { ExpandableFlyout } from './src';
export { useExpandableFlyoutContext, type ExpandableFlyoutContext } from './src/context';
export {
type ExpandableFlyoutContext,
useExpandableFlyoutState,
useExpandableFlyoutApi,
} from './src/context';
export { type State as ExpandableFlyoutState } from './src/state';
export { ExpandableFlyoutProvider } from './src/provider';

View file

@ -24,7 +24,7 @@ import {
PREVIEW_SECTION_HEADER_TEST_ID,
PREVIEW_SECTION_TEST_ID,
} from './test_ids';
import { useExpandableFlyoutContext } from '../..';
import { useExpandableFlyoutApi } from '../..';
import { BACK_BUTTON, CLOSE_BUTTON } from './translations';
export interface PreviewBanner {
@ -89,7 +89,7 @@ export const PreviewSection: React.FC<PreviewSectionProps> = ({
banner,
}: PreviewSectionProps) => {
const { euiTheme } = useEuiTheme();
const { closePreviewPanel, previousPreviewPanel } = useExpandableFlyoutContext();
const { closePreviewPanel, previousPreviewPanel } = useExpandableFlyoutApi();
const left = leftPosition + 4;

View file

@ -20,7 +20,8 @@ export const ExpandableFlyoutContext = createContext<ExpandableFlyoutContextValu
);
/**
* Retrieve context's properties
* Retrieve Flyout's api and state
* @deprecated
*/
export const useExpandableFlyoutContext = (): ExpandableFlyoutApi => {
const contextValue = useContext(ExpandableFlyoutContext);
@ -36,3 +37,21 @@ export const useExpandableFlyoutContext = (): ExpandableFlyoutApi => {
return contextValue === 'memory' ? memoryState : urlState;
};
/**
* This hook allows you to interact with the flyout, open panels and previews etc.
*/
export const useExpandableFlyoutApi = () => {
const { panels, ...api } = useExpandableFlyoutContext();
return api;
};
/**
* This hook allows you to access the flyout state, read open panels and previews.
*/
export const useExpandableFlyoutState = () => {
const expandableFlyoutApiAndState = useExpandableFlyoutContext();
return expandableFlyoutApiAndState.panels;
};

View file

@ -10,7 +10,7 @@ import { useDispatch } from 'react-redux';
import type { CaseViewRefreshPropInterface } from '@kbn/cases-plugin/common';
import { CaseMetricsFeature } from '@kbn/cases-plugin/common';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { DocumentDetailsRightPanelKey } from '../../flyout/document_details/right';
import { useTourContext } from '../../common/components/guided_onboarding_tour';
import {
@ -61,7 +61,7 @@ const CaseContainerComponent: React.FC = () => {
const { formatUrl: detectionsFormatUrl, search: detectionsUrlSearch } = useFormatUrl(
SecurityPageName.rules
);
const { openFlyout } = useExpandableFlyoutContext();
const { openFlyout } = useExpandableFlyoutApi();
const [isSecurityFlyoutEnabled] = useUiSetting$<boolean>(ENABLE_EXPANDABLE_FLYOUT_SETTING);
const getDetectionsRuleDetailsHref = useCallback(

View file

@ -8,7 +8,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 { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { dataTableActions, TableId } from '@kbn/securitysolution-data-table';
import { useUiSetting$ } from '@kbn/kibana-react-plugin/public';
import { timelineActions } from '../../../../timelines/store';
@ -70,7 +70,7 @@ const RowActionComponent = ({
}: Props) => {
const { data: timelineNonEcsData, ecs: ecsData, _id: eventId, _index: indexName } = data ?? {};
const { openFlyout } = useExpandableFlyoutContext();
const { openFlyout } = useExpandableFlyoutApi();
const dispatch = useDispatch();
const [isSecurityFlyoutEnabled] = useUiSetting$<boolean>(ENABLE_EXPANDABLE_FLYOUT_SETTING);

View file

@ -7,7 +7,7 @@
import type { FC } from 'react';
import React, { useCallback } from 'react';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { DocumentDetailsRightPanelKey } from '../right';
import { useBasicDataFromDetailsData } from '../../../timelines/components/side_panel/event_details/helpers';
import { EndpointIsolateSuccess } from '../../../common/components/endpoint/host_isolation';
@ -20,7 +20,7 @@ import { FlyoutBody } from '../../shared/components/flyout_body';
* Document details expandable flyout section content for the isolate host component, displaying the form or the success banner
*/
export const PanelContent: FC = () => {
const { openRightPanel } = useExpandableFlyoutContext();
const { openRightPanel } = useExpandableFlyoutApi();
const { dataFormattedForFieldBrowser, eventId, scopeId, indexName, isolateAction } =
useIsolateHostPanelContext();

View file

@ -8,7 +8,7 @@
import type { FC } from 'react';
import React, { memo, useMemo } from 'react';
import type { FlyoutPanelProps, PanelPath } from '@kbn/expandable-flyout';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { PanelHeader } from './header';
import { PanelContent } from './content';
import type { LeftPanelTabsType } from './tabs';
@ -33,7 +33,7 @@ export interface LeftPanelProps extends FlyoutPanelProps {
}
export const LeftPanel: FC<Partial<LeftPanelProps>> = memo(({ path }) => {
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const { eventId, indexName, scopeId } = useLeftPanelContext();
const selectedTabId = useMemo(() => {

View file

@ -11,7 +11,7 @@ import { EuiButtonGroup, EuiSpacer } from '@elastic/eui';
import type { EuiButtonGroupOptionProps } from '@elastic/eui/src/components/button/button_group/button_group';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi, useExpandableFlyoutState } from '@kbn/expandable-flyout';
import {
INSIGHTS_TAB_BUTTON_GROUP_TEST_ID,
INSIGHTS_TAB_ENTITIES_BUTTON_TEST_ID,
@ -77,7 +77,8 @@ const insightsButtons: EuiButtonGroupOptionProps[] = [
*/
export const InsightsTab: React.FC = memo(() => {
const { eventId, indexName, scopeId } = useLeftPanelContext();
const { openLeftPanel, panels } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const panels = useExpandableFlyoutState();
const activeInsightsId = panels.left?.path?.subTab ?? ENTITIES_TAB_ID;
const onChangeCompressed = useCallback(

View file

@ -9,7 +9,7 @@ import type { FC } from 'react';
import React, { memo, useState, useCallback, useEffect } from 'react';
import { EuiButtonGroup, EuiSpacer } from '@elastic/eui';
import type { EuiButtonGroupOptionProps } from '@elastic/eui/src/components/button/button_group/button_group';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi, useExpandableFlyoutState } from '@kbn/expandable-flyout';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { useLeftPanelContext } from '../context';
@ -52,7 +52,8 @@ const visualizeButtons: EuiButtonGroupOptionProps[] = [
*/
export const VisualizeTab: FC = memo(() => {
const { eventId, indexName, scopeId } = useLeftPanelContext();
const { panels, openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const panels = useExpandableFlyoutState();
const [activeVisualizationId, setActiveVisualizationId] = useState(
panels.left?.path?.subTab ?? SESSION_VIEW_ID
);

View file

@ -8,7 +8,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import type { ExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { RightPanelContext } from '../context';
import { TestProviders } from '../../../../common/mock';
import { CorrelationsOverview } from './correlations_overview';
@ -89,13 +89,13 @@ const flyoutContextValue = {
} as unknown as ExpandableFlyoutApi;
jest.mock('@kbn/expandable-flyout', () => ({
useExpandableFlyoutContext: jest.fn(),
useExpandableFlyoutApi: jest.fn(),
ExpandableFlyoutProvider: ({ children }: React.PropsWithChildren<{}>) => <>{children}</>,
}));
describe('<CorrelationsOverview />', () => {
beforeAll(() => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue(flyoutContextValue);
jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue);
});
it('should render wrapper component', () => {

View file

@ -7,7 +7,7 @@
import React, { useCallback } from 'react';
import { EuiFlexGroup } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
import { useShowRelatedAlertsBySession } from '../../shared/hooks/use_show_related_alerts_by_session';
@ -39,7 +39,7 @@ export const CorrelationsOverview: React.FC = () => {
getFieldsData,
scopeId,
} = useRightPanelContext();
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const goToCorrelationsTab = useCallback(() => {
openLeftPanel({

View file

@ -19,10 +19,10 @@ import { mockGetFieldsData } from '../../shared/mocks/mock_get_fields_data';
import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common';
import { DocumentDetailsPreviewPanelKey } from '../../preview';
import { i18n } from '@kbn/i18n';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import type { ExpandableFlyoutApi } from '@kbn/expandable-flyout';
jest.mock('@kbn/expandable-flyout', () => ({ useExpandableFlyoutContext: jest.fn() }));
jest.mock('@kbn/expandable-flyout', () => ({ useExpandableFlyoutApi: jest.fn() }));
const ruleUuid = {
category: 'kibana',
@ -74,7 +74,7 @@ const NO_DATA_MESSAGE = "There's no description for this rule.";
describe('<Description />', () => {
beforeAll(() => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue(flyoutContextValue);
jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue);
});
it('should render the component', () => {

View file

@ -10,7 +10,7 @@ import type { FC } from 'react';
import React, { useMemo, useCallback } from 'react';
import { isEmpty } from 'lodash';
import { css } from '@emotion/react';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { useRightPanelContext } from '../context';
@ -36,7 +36,7 @@ export const Description: FC = () => {
const { isAlert, ruleDescription, ruleName, ruleId } = useBasicDataFromDetailsData(
dataFormattedForFieldBrowser
);
const { openPreviewPanel } = useExpandableFlyoutContext();
const { openPreviewPanel } = useExpandableFlyoutApi();
const openRulePreview = useCallback(() => {
const PreviewPanelRulePreview: PreviewPanelProps['path'] = { tab: RulePreviewPanel };
openPreviewPanel({

View file

@ -7,7 +7,7 @@
import React, { useCallback } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { INSIGHTS_ENTITIES_TEST_ID } from './test_ids';
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
@ -23,7 +23,7 @@ import { ENTITIES_TAB_ID } from '../../left/components/entities_details';
*/
export const EntitiesOverview: React.FC = () => {
const { eventId, getFieldsData, indexName, scopeId } = useRightPanelContext();
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const hostName = getField(getFieldsData('host.name'));
const userName = getField(getFieldsData('user.name'));

View file

@ -19,13 +19,13 @@ import { TestProviders } from '../../../../common/mock';
import { ENTITIES_TAB_ID } from '../../left/components/entities_details';
import { useGetEndpointDetails } from '../../../../management/hooks';
import { useSentinelOneAgentData } from '../../../../detections/components/host_isolation/use_sentinelone_host_isolation';
import { useExpandableFlyoutContext, type ExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi, type ExpandableFlyoutApi } from '@kbn/expandable-flyout';
jest.mock('../../../../management/hooks');
jest.mock('../../../../detections/components/host_isolation/use_sentinelone_host_isolation');
jest.mock('@kbn/expandable-flyout', () => ({
useExpandableFlyoutContext: jest.fn(),
useExpandableFlyoutApi: jest.fn(),
ExpandableFlyoutProvider: ({ children }: React.PropsWithChildren<{}>) => <>{children}</>,
}));
@ -48,7 +48,7 @@ const renderHighlightedFieldsCell = (values: string[], field: string) =>
describe('<HighlightedFieldsCell />', () => {
beforeAll(() => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue(flyoutContextValue);
jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue);
});
it('should render a basic cell', () => {

View file

@ -8,7 +8,7 @@
import type { VFC } from 'react';
import React, { useCallback } from 'react';
import { EuiFlexItem, EuiLink } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { SentinelOneAgentStatus } from '../../../../detections/components/host_isolation/sentinel_one_agent_status';
import { SENTINEL_ONE_AGENT_ID_FIELD } from '../../../../common/utils/sentinelone_alert_check';
import { EndpointAgentStatusById } from '../../../../common/components/endpoint/endpoint_agent_status';
@ -40,7 +40,7 @@ interface LinkFieldCellProps {
*/
const LinkFieldCell: VFC<LinkFieldCellProps> = ({ value }) => {
const { scopeId, eventId, indexName } = useRightPanelContext();
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const goToInsightsEntities = useCallback(() => {
openLeftPanel({

View file

@ -20,7 +20,7 @@ import {
import { RightPanelContext } from '../context';
import { mockContextValue } from '../mocks/mock_context';
import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_formatted_for_field_browser';
import { useExpandableFlyoutContext, type ExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi, type ExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { LeftPanelInsightsTab, DocumentDetailsLeftPanelKey } from '../../left';
import { ENTITIES_TAB_ID } from '../../left/components/entities_details';
import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
@ -41,7 +41,7 @@ const panelContextValue = {
};
jest.mock('@kbn/expandable-flyout', () => ({
useExpandableFlyoutContext: jest.fn(),
useExpandableFlyoutApi: jest.fn(),
ExpandableFlyoutProvider: ({ children }: React.PropsWithChildren<{}>) => <>{children}</>,
}));
@ -83,7 +83,7 @@ const renderHostEntityContent = () =>
describe('<HostEntityContent />', () => {
beforeAll(() => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue(flyoutContextValue);
jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue);
});
describe('license is valid', () => {

View file

@ -18,7 +18,7 @@ import {
import { css } from '@emotion/css';
import { getOr } from 'lodash/fp';
import { i18n } from '@kbn/i18n';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
import { useRightPanelContext } from '../context';
import type { DescriptionList } from '../../../../../common/utility_types';
@ -67,7 +67,7 @@ export interface HostEntityOverviewProps {
*/
export const HostEntityOverview: React.FC<HostEntityOverviewProps> = ({ hostName }) => {
const { eventId, indexName, scopeId } = useRightPanelContext();
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const goToEntitiesTab = useCallback(() => {
openLeftPanel({
id: DocumentDetailsLeftPanelKey,

View file

@ -18,12 +18,12 @@ import {
import { mockContextValue } from '../mocks/mock_context';
import { mockFlyoutContextValue } from '../../shared/mocks/mock_flyout_context';
import type { ExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useInvestigationGuide } from '../../shared/hooks/use_investigation_guide';
import { LeftPanelInvestigationTab, DocumentDetailsLeftPanelKey } from '../../left';
jest.mock('../../shared/hooks/use_investigation_guide');
jest.mock('@kbn/expandable-flyout', () => ({ useExpandableFlyoutContext: jest.fn() }));
jest.mock('@kbn/expandable-flyout', () => ({ useExpandableFlyoutApi: jest.fn() }));
const NO_DATA_MESSAGE = 'Investigation guideTheres no investigation guide for this rule.';
const PREVIEW_MESSAGE = 'Investigation guide is not available in alert preview.';
@ -39,7 +39,7 @@ const renderInvestigationGuide = () =>
describe('<InvestigationGuide />', () => {
beforeAll(() => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue({
jest.mocked(useExpandableFlyoutApi).mockReturnValue({
openLeftPanel: mockFlyoutContextValue.openLeftPanel,
} as unknown as ExpandableFlyoutApi);
});

View file

@ -6,7 +6,7 @@
*/
import React, { useCallback } from 'react';
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSkeletonText } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { useInvestigationGuide } from '../../shared/hooks/use_investigation_guide';
@ -23,7 +23,7 @@ import {
* or a no-data message if investigation guide hasn't been set up on the rule
*/
export const InvestigationGuide: React.FC = () => {
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const { eventId, indexName, scopeId, dataFormattedForFieldBrowser, isPreview } =
useRightPanelContext();

View file

@ -22,7 +22,7 @@ import {
} from '../../../shared/components/test_ids';
import { usePrevalence } from '../../shared/hooks/use_prevalence';
import { mockContextValue } from '../mocks/mock_context';
import { type ExpandableFlyoutApi, useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { type ExpandableFlyoutApi, useExpandableFlyoutApi } from '@kbn/expandable-flyout';
jest.mock('../../shared/hooks/use_prevalence');
@ -38,7 +38,7 @@ const flyoutContextValue = {
} as unknown as ExpandableFlyoutApi;
jest.mock('@kbn/expandable-flyout', () => ({
useExpandableFlyoutContext: jest.fn(),
useExpandableFlyoutApi: jest.fn(),
ExpandableFlyoutProvider: ({ children }: React.PropsWithChildren<{}>) => <>{children}</>,
}));
@ -53,7 +53,7 @@ const renderPrevalenceOverview = (contextValue: RightPanelContext = mockContextV
describe('<PrevalenceOverview />', () => {
beforeAll(() => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue(flyoutContextValue);
jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue);
});
it('should render wrapper component', () => {

View file

@ -8,7 +8,7 @@
import type { FC } from 'react';
import React, { useCallback, useMemo } from 'react';
import { EuiFlexGroup } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
import { usePrevalence } from '../../shared/hooks/use_prevalence';
@ -29,7 +29,7 @@ const DEFAULT_TO = 'now';
export const PrevalenceOverview: FC = () => {
const { eventId, indexName, dataFormattedForFieldBrowser, scopeId, investigationFields } =
useRightPanelContext();
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const goPrevalenceTab = useCallback(() => {
openLeftPanel({

View file

@ -16,14 +16,14 @@ import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_f
import { DocumentDetailsPreviewPanelKey } from '../../preview';
import { i18n } from '@kbn/i18n';
import { type ExpandableFlyoutApi, useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { type ExpandableFlyoutApi, useExpandableFlyoutApi } from '@kbn/expandable-flyout';
const flyoutContextValue = {
openPreviewPanel: jest.fn(),
} as unknown as ExpandableFlyoutApi;
jest.mock('@kbn/expandable-flyout', () => ({
useExpandableFlyoutContext: jest.fn(),
useExpandableFlyoutApi: jest.fn(),
ExpandableFlyoutProvider: ({ children }: React.PropsWithChildren<{}>) => <>{children}</>,
}));
@ -48,7 +48,7 @@ const NO_DATA_MESSAGE = "There's no source event information for this alert.";
describe('<Reason />', () => {
beforeAll(() => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue(flyoutContextValue);
jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue);
});
it('should render the component for alert', () => {

View file

@ -8,7 +8,7 @@
import type { FC } from 'react';
import React, { useCallback, useMemo } from 'react';
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { ALERT_REASON } from '@kbn/rule-data-utils';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
@ -31,7 +31,7 @@ export const Reason: FC = () => {
const { isAlert } = useBasicDataFromDetailsData(dataFormattedForFieldBrowser);
const alertReason = getField(getFieldsData(ALERT_REASON));
const { openPreviewPanel } = useExpandableFlyoutContext();
const { openPreviewPanel } = useExpandableFlyoutApi();
const openRulePreview = useCallback(() => {
openPreviewPanel({
id: DocumentDetailsPreviewPanelKey,

View file

@ -6,7 +6,7 @@
*/
import React, { useCallback } from 'react';
import { EuiButton } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { useRightPanelContext } from '../context';
import { DocumentDetailsLeftPanelKey, LeftPanelResponseTab } from '../../left';
@ -16,7 +16,7 @@ import { RESPONSE_BUTTON_TEST_ID } from './test_ids';
* Response button that opens Response section in the left panel
*/
export const ResponseButton: React.FC = () => {
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const { eventId, indexName, scopeId } = useRightPanelContext();
const goToResponseTab = useCallback(() => {

View file

@ -8,7 +8,7 @@
import type { FC } from 'react';
import React, { useMemo } from 'react';
import { find } from 'lodash/fp';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { CellActionsMode } from '@kbn/cell-actions';
import { getSourcererScopeId } from '../../../../helpers';
import { SecurityCellActions } from '../../../../common/components/cell_actions';
@ -33,7 +33,7 @@ function hasData(fieldInfo?: EnrichedFieldInfo): fieldInfo is EnrichedFieldInfoW
* Document details status displayed in flyout right section header
*/
export const DocumentStatus: FC = () => {
const { closeFlyout } = useExpandableFlyoutContext();
const { closeFlyout } = useExpandableFlyoutApi();
const { eventId, browserFields, dataFormattedForFieldBrowser, scopeId } = useRightPanelContext();
const statusData = useMemo(() => {

View file

@ -7,7 +7,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import { useExpandableFlyoutContext, type ExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi, type ExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { RightPanelContext } from '../context';
import { TestProviders } from '../../../../common/mock';
import { ThreatIntelligenceOverview } from './threat_intelligence_overview';
@ -48,7 +48,7 @@ const panelContextValue = {
} as unknown as RightPanelContext;
jest.mock('@kbn/expandable-flyout', () => ({
useExpandableFlyoutContext: jest.fn(),
useExpandableFlyoutApi: jest.fn(),
ExpandableFlyoutProvider: ({ children }: React.PropsWithChildren<{}>) => <>{children}</>,
}));
@ -66,7 +66,7 @@ const flyoutContextValue = {
describe('<ThreatIntelligenceOverview />', () => {
beforeAll(() => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue(flyoutContextValue);
jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue);
});
it('should render wrapper component', () => {

View file

@ -8,7 +8,7 @@
import type { FC } from 'react';
import React, { useCallback } from 'react';
import { EuiFlexGroup } from '@elastic/eui';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { ExpandablePanel } from '../../../shared/components/expandable_panel';
import { useFetchThreatIntelligence } from '../hooks/use_fetch_threat_intelligence';
@ -25,7 +25,7 @@ import { THREAT_INTELLIGENCE_TAB_ID } from '../../left/components/threat_intelli
*/
export const ThreatIntelligenceOverview: FC = () => {
const { eventId, indexName, scopeId, dataFormattedForFieldBrowser } = useRightPanelContext();
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const goToThreatIntelligenceTab = useCallback(() => {
openLeftPanel({

View file

@ -23,7 +23,7 @@ import { RightPanelContext } from '../context';
import { LeftPanelInsightsTab, DocumentDetailsLeftPanelKey } from '../../left';
import { ENTITIES_TAB_ID } from '../../left/components/entities_details';
import { useRiskScore } from '../../../../entity_analytics/api/hooks/use_risk_score';
import { type ExpandableFlyoutApi, useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { type ExpandableFlyoutApi, useExpandableFlyoutApi } from '@kbn/expandable-flyout';
const userName = 'user';
const domain = 'n54bg2lfc7';
@ -41,7 +41,7 @@ const panelContextValue = {
};
jest.mock('@kbn/expandable-flyout', () => ({
useExpandableFlyoutContext: jest.fn(),
useExpandableFlyoutApi: jest.fn(),
ExpandableFlyoutProvider: ({ children }: React.PropsWithChildren<{}>) => <>{children}</>,
}));
@ -83,7 +83,7 @@ const renderUserEntityOverview = () =>
describe('<UserEntityOverview />', () => {
beforeAll(() => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue(flyoutContextValue);
jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue);
});
describe('license is valid', () => {

View file

@ -18,7 +18,7 @@ import {
import { css } from '@emotion/css';
import { getOr } from 'lodash/fp';
import { i18n } from '@kbn/i18n';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { LeftPanelInsightsTab, DocumentDetailsLeftPanelKey } from '../../left';
import { ENTITIES_TAB_ID } from '../../left/components/entities_details';
import { useRightPanelContext } from '../context';
@ -68,7 +68,7 @@ export interface UserEntityOverviewProps {
*/
export const UserEntityOverview: React.FC<UserEntityOverviewProps> = ({ userName }) => {
const { eventId, indexName, scopeId } = useRightPanelContext();
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const goToEntitiesTab = useCallback(() => {
openLeftPanel({
id: DocumentDetailsLeftPanelKey,

View file

@ -7,7 +7,7 @@
import type { FC } from 'react';
import React, { useCallback } from 'react';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import styled from 'styled-components';
import { euiThemeVars } from '@kbn/ui-theme';
import { FlyoutFooter } from '../../../timelines/components/side_panel/event_details/flyout';
@ -31,7 +31,7 @@ interface PanelFooterProps {
*
*/
export const PanelFooter: FC<PanelFooterProps> = ({ isPreview }) => {
const { closeFlyout, openRightPanel } = useExpandableFlyoutContext();
const { closeFlyout, openRightPanel } = useExpandableFlyoutApi();
const {
eventId,
indexName,

View file

@ -8,7 +8,7 @@
import type { FC } from 'react';
import React, { memo, useMemo, useEffect } from 'react';
import type { FlyoutPanelProps, PanelPath } from '@kbn/expandable-flyout';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { EventKind } from '../shared/constants/event_kinds';
import { getField } from '../shared/utils';
import { useRightPanelContext } from './context';
@ -36,7 +36,7 @@ export interface RightPanelProps extends FlyoutPanelProps {
* Panel to be displayed in the document details expandable flyout right section
*/
export const RightPanel: FC<Partial<RightPanelProps>> = memo(({ path }) => {
const { openRightPanel, closeFlyout } = useExpandableFlyoutContext();
const { openRightPanel, closeFlyout } = useExpandableFlyoutApi();
const { eventId, getFieldsData, indexName, scopeId, isPreview } = useRightPanelContext();
// for 8.10, we only render the flyout in its expandable mode if the document viewed is of type signal

View file

@ -7,7 +7,7 @@
import type { FC } from 'react';
import React, { memo, useCallback } from 'react';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { HeaderActions } from './components/header_actions';
import { FlyoutNavigation } from '../../shared/components/flyout_navigation';
import { DocumentDetailsLeftPanelKey } from '../left';
@ -21,7 +21,7 @@ interface PanelNavigationProps {
}
export const PanelNavigation: FC<PanelNavigationProps> = memo(({ flyoutIsExpandable }) => {
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const { eventId, indexName, scopeId } = useRightPanelContext();
const expandDetails = useCallback(() => {

View file

@ -7,7 +7,7 @@
import React, { useCallback, useMemo } from 'react';
import type { FlyoutPanelProps } from '@kbn/expandable-flyout';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useKibana } from '../../../common/lib/kibana/kibana_react';
import { hostToCriteria } from '../../../common/components/ml/criteria/host_to_criteria';
@ -50,7 +50,7 @@ const FIRST_RECORD_PAGINATION = {
export const HostPanel = ({ contextID, scopeId, hostName, isDraggable }: HostPanelProps) => {
const { telemetry } = useKibana().services;
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const { to, from, isInitializing, setQuery, deleteQuery } = useGlobalTime();
const hostNameFilterQuery = useMemo(
() => (hostName ? buildHostNamesFilter([hostName]) : undefined),

View file

@ -7,7 +7,7 @@
import React, { useMemo } from 'react';
import type { FlyoutPanelProps, PanelPath } from '@kbn/expandable-flyout';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useManagedUser } from '../../../timelines/components/side_panel/new_user_detail/hooks/use_managed_user';
import { useTabs } from './tabs';
import { FlyoutLoading } from '../../shared/components/flyout_loading';
@ -59,7 +59,7 @@ const useSelectedTab = (
tabs: LeftPanelTabsType,
path: PanelPath | undefined
) => {
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const selectedTabId = useMemo(() => {
const defaultTab = tabs[0].id;

View file

@ -7,7 +7,7 @@
import React, { useCallback, useMemo } from 'react';
import type { FlyoutPanelProps } from '@kbn/expandable-flyout';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useKibana } from '../../../common/lib/kibana/kibana_react';
import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score';
import { ManagedUserDatasetKey } from '../../../../common/search_strategy/security_solution/users/managed_details';
@ -79,7 +79,7 @@ export const UserPanel = ({ contextID, scopeId, userName, isDraggable }: UserPan
setQuery,
});
const { openLeftPanel } = useExpandableFlyoutContext();
const { openLeftPanel } = useExpandableFlyoutApi();
const openPanelTab = useCallback(
(tab?: EntityDetailsLeftPanelTab) => {
telemetry.reportRiskInputsExpandedFlyoutOpened({

View file

@ -15,10 +15,12 @@ import {
EXPAND_DETAILS_BUTTON_TEST_ID,
HEADER_ACTIONS_TEST_ID,
} from './test_ids';
import type { ExpandableFlyoutState } from '@kbn/expandable-flyout';
import {
useExpandableFlyoutContext,
useExpandableFlyoutApi,
type ExpandableFlyoutApi,
ExpandableFlyoutProvider,
useExpandableFlyoutState,
} from '@kbn/expandable-flyout';
const expandDetails = jest.fn();
@ -32,24 +34,23 @@ const ExpandableFlyoutTestProviders: FC<PropsWithChildren<{}>> = ({ children })
};
jest.mock('@kbn/expandable-flyout', () => ({
useExpandableFlyoutContext: jest.fn(),
useExpandableFlyoutApi: jest.fn(),
useExpandableFlyoutState: jest.fn(),
ExpandableFlyoutProvider: ({ children }: React.PropsWithChildren<{}>) => <>{children}</>,
}));
const flyoutContextValue = {
panels: {},
closeLeftPanel: jest.fn(),
} as unknown as ExpandableFlyoutApi;
describe('<FlyoutNavigation />', () => {
beforeEach(() => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue(flyoutContextValue);
jest.mocked(useExpandableFlyoutApi).mockReturnValue(flyoutContextValue);
jest.mocked(useExpandableFlyoutState).mockReturnValue({} as unknown as ExpandableFlyoutState);
});
describe('when flyout is expandable', () => {
it('should render expand button', () => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue(flyoutContextValue);
const { getByTestId, queryByTestId } = render(
<ExpandableFlyoutTestProviders>
<FlyoutNavigation flyoutIsExpandable={true} expandDetails={expandDetails} />
@ -64,10 +65,9 @@ describe('<FlyoutNavigation />', () => {
});
it('should render collapse button', () => {
jest.mocked(useExpandableFlyoutContext).mockReturnValue({
...flyoutContextValue,
panels: { left: {} },
} as unknown as ExpandableFlyoutApi);
jest
.mocked(useExpandableFlyoutState)
.mockReturnValue({ left: {} } as unknown as ExpandableFlyoutState);
const { getByTestId, queryByTestId } = render(
<ExpandableFlyoutTestProviders>

View file

@ -15,7 +15,7 @@ import {
EuiButtonEmpty,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { useExpandableFlyoutApi, useExpandableFlyoutState } from '@kbn/expandable-flyout';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import {
@ -46,7 +46,8 @@ export interface FlyoutNavigationProps {
export const FlyoutNavigation: FC<FlyoutNavigationProps> = memo(
({ flyoutIsExpandable = false, expandDetails, actions }) => {
const { euiTheme } = useEuiTheme();
const { closeLeftPanel, panels } = useExpandableFlyoutContext();
const { closeLeftPanel } = useExpandableFlyoutApi();
const panels = useExpandableFlyoutState();
const isExpanded: boolean = !!panels.left;
const collapseDetails = useCallback(() => closeLeftPanel(), [closeLeftPanel]);

View file

@ -29,7 +29,7 @@ jest.mock('@kbn/expandable-flyout/src/context', () => {
return {
...original,
useExpandableFlyoutContext: () => ({
useExpandableFlyoutApi: () => ({
openRightPanel: mockOpenRightPanel,
}),
};

View file

@ -9,8 +9,8 @@ import React, { useCallback, useContext, useMemo } from 'react';
import type { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui';
import { useDispatch } from 'react-redux';
import { isString } from 'lodash/fp';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { TableId } from '@kbn/securitysolution-data-table';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import { HostPanelKey } from '../../../../../flyout/entity_details/host_right';
import type { ExpandedDetailType } from '../../../../../../common/types';
@ -51,7 +51,7 @@ const HostNameComponent: React.FC<Props> = ({
value,
}) => {
const isNewHostDetailsFlyoutEnabled = useIsExperimentalFeatureEnabled('newHostDetailsFlyout');
const { openRightPanel } = useExpandableFlyoutContext();
const { openRightPanel } = useExpandableFlyoutApi();
const dispatch = useDispatch();
const eventContext = useContext(StatefulEventContext);

View file

@ -9,8 +9,8 @@ import React, { useCallback, useContext, useMemo } from 'react';
import type { EuiButtonEmpty, EuiButtonIcon } from '@elastic/eui';
import { useDispatch } from 'react-redux';
import { isString } from 'lodash/fp';
import { useExpandableFlyoutContext } from '@kbn/expandable-flyout';
import { TableId } from '@kbn/securitysolution-data-table';
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
import { UserPanelKey } from '../../../../../flyout/entity_details/user_right';
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import { StatefulEventContext } from '../../../../../common/components/events_viewer/stateful_event_context';
@ -55,7 +55,7 @@ const UserNameComponent: React.FC<Props> = ({
const isNewUserDetailsFlyoutEnable = useIsExperimentalFeatureEnabled('newUserDetailsFlyout');
const userName = `${value}`;
const isInTimelineContext = userName && eventContext?.timelineID;
const { openRightPanel } = useExpandableFlyoutContext();
const { openRightPanel } = useExpandableFlyoutApi();
const openUserDetailsSidePanel = useCallback(
(e) => {