mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Security Solution][Flyout] Use kbn url storage provider, housekeeping (#175882)
## Summary **This does not change any of the api's defined previously.** Changes: - replace custom hook for url serialization with off the shelf solution from public plugins - cleanup the codebase futher - remove internal context based hook ### Checklist Delete any items that are not applicable to this PR. - [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 <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
585630d060
commit
1fe7833a23
29 changed files with 265 additions and 418 deletions
|
@ -27,7 +27,11 @@ 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 [hooks](https://github.com/elastic/kibana/blob/main/packages/kbn-expandable-flyout/src/context.tsx) expose the state and the following api:
|
||||
To retrieve the flyout's layout (left, right and preview panels), you can utilize [useExpandableFlyoutState](https://github.com/elastic/kibana/blob/main/packages/kbn-expandable-flyout/src/hooks/use_expandable_flyout_state.ts).
|
||||
|
||||
To control (or mutate) flyout's layout, you can utilize [useExpandableFlyoutApi](https://github.com/elastic/kibana/blob/main/packages/kbn-expandable-flyout/src/hooks/use_expandable_flyout_api.ts).
|
||||
|
||||
**Expandable Flyout API** exposes the following methods:
|
||||
- **openFlyout**: open the flyout with a set of panels
|
||||
- **openRightPanel**: open a right panel
|
||||
- **openLeftPanel**: open a left panel
|
||||
|
@ -38,10 +42,6 @@ The ExpandableFlyout [hooks](https://github.com/elastic/kibana/blob/main/package
|
|||
- **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 **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
|
||||
|
||||
To use the expandable flyout in your plugin, first you need wrap your code with the [context provider](https://github.com/elastic/kibana/blob/main/packages/kbn-expandable-flyout/src/context.tsx) at a high enough level as follows:
|
||||
|
|
|
@ -8,11 +8,8 @@
|
|||
|
||||
export { ExpandableFlyout } from './src';
|
||||
|
||||
export {
|
||||
type ExpandableFlyoutContext,
|
||||
useExpandableFlyoutState,
|
||||
useExpandableFlyoutApi,
|
||||
} from './src/context';
|
||||
export { useExpandableFlyoutApi } from './src/hooks/use_expandable_flyout_api';
|
||||
export { useExpandableFlyoutState } from './src/hooks/use_expandable_flyout_state';
|
||||
|
||||
export { type State as ExpandableFlyoutState } from './src/state';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"type": "shared-browser",
|
||||
"id": "@kbn/expandable-flyout",
|
||||
"owner": "@elastic/security-threat-hunting-investigations"
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ export enum ActionType {
|
|||
closePreviewPanel = 'close_preview_panel',
|
||||
previousPreviewPanel = 'previous_preview_panel',
|
||||
closeFlyout = 'close_flyout',
|
||||
urlChanged = 'urlChanged',
|
||||
}
|
||||
|
||||
export const openPanelsAction = createAction<{
|
||||
|
@ -37,3 +38,9 @@ export const closeLeftPanelAction = createAction(ActionType.closeLeftPanel);
|
|||
export const closePreviewPanelAction = createAction(ActionType.closePreviewPanel);
|
||||
|
||||
export const previousPreviewPanelAction = createAction(ActionType.previousPreviewPanel);
|
||||
|
||||
export const urlChangedAction = createAction<{
|
||||
right?: FlyoutPanelProps;
|
||||
left?: FlyoutPanelProps;
|
||||
preview?: FlyoutPanelProps;
|
||||
}>(ActionType.urlChanged);
|
||||
|
|
|
@ -14,21 +14,19 @@ import {
|
|||
PREVIEW_SECTION_CLOSE_BUTTON_TEST_ID,
|
||||
PREVIEW_SECTION_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { ExpandableFlyoutContextValue } from '../context';
|
||||
import { TestProvider } from '../test/provider';
|
||||
import { State } from '../state';
|
||||
|
||||
describe('PreviewSection', () => {
|
||||
const context = {
|
||||
panels: {
|
||||
right: {},
|
||||
left: {},
|
||||
preview: [
|
||||
{
|
||||
id: 'key',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as unknown as ExpandableFlyoutContextValue;
|
||||
right: {},
|
||||
left: {},
|
||||
preview: [
|
||||
{
|
||||
id: 'key',
|
||||
},
|
||||
],
|
||||
} as unknown as State;
|
||||
|
||||
const component = <div>{'component'}</div>;
|
||||
const left = 500;
|
||||
|
@ -37,7 +35,7 @@ describe('PreviewSection', () => {
|
|||
const showBackButton = false;
|
||||
|
||||
const { getByTestId } = render(
|
||||
<TestProvider state={context.panels}>
|
||||
<TestProvider state={context}>
|
||||
<PreviewSection component={component} leftPosition={left} showBackButton={showBackButton} />
|
||||
</TestProvider>
|
||||
);
|
||||
|
@ -49,7 +47,7 @@ describe('PreviewSection', () => {
|
|||
const showBackButton = true;
|
||||
|
||||
const { getByTestId } = render(
|
||||
<TestProvider state={context.panels}>
|
||||
<TestProvider state={context}>
|
||||
<PreviewSection component={component} leftPosition={left} showBackButton={showBackButton} />
|
||||
</TestProvider>
|
||||
);
|
||||
|
@ -67,7 +65,7 @@ describe('PreviewSection', () => {
|
|||
};
|
||||
|
||||
const { getByTestId, getByText } = render(
|
||||
<TestProvider state={context.panels}>
|
||||
<TestProvider state={context}>
|
||||
<PreviewSection
|
||||
component={component}
|
||||
leftPosition={left}
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createContext, useContext } from 'react';
|
||||
import { useFlyoutMemoryState } from './context/memory_state_provider';
|
||||
import { useFlyoutUrlState } from './context/url_state_provider';
|
||||
import { type ExpandableFlyoutApi } from './types';
|
||||
|
||||
export type { ExpandableFlyoutApi as ExpandableFlyoutContextValue };
|
||||
|
||||
type ExpandableFlyoutContextValue = 'memory' | 'url';
|
||||
|
||||
export const ExpandableFlyoutContext = createContext<ExpandableFlyoutContextValue | undefined>(
|
||||
undefined
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieve Flyout's api and state
|
||||
* @deprecated
|
||||
*/
|
||||
export const useExpandableFlyoutContext = (): ExpandableFlyoutApi => {
|
||||
const contextValue = useContext(ExpandableFlyoutContext);
|
||||
|
||||
if (!contextValue) {
|
||||
throw new Error(
|
||||
'ExpandableFlyoutContext can only be used within ExpandableFlyoutContext provider'
|
||||
);
|
||||
}
|
||||
|
||||
const memoryState = useFlyoutMemoryState();
|
||||
const urlState = useFlyoutUrlState();
|
||||
|
||||
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;
|
||||
};
|
|
@ -1,109 +0,0 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { FlyoutPanelProps, ExpandableFlyoutApi } from '../types';
|
||||
import { useRightPanel } from '../hooks/use_right_panel';
|
||||
import { useLeftPanel } from '../hooks/use_left_panel';
|
||||
import { usePreviewPanel } from '../hooks/use_preview_panel';
|
||||
import { State } from '../state';
|
||||
|
||||
export const useFlyoutUrlState = (): ExpandableFlyoutApi => {
|
||||
const { setRightPanelState, rightPanelState } = useRightPanel();
|
||||
const { setLeftPanelState, leftPanelState } = useLeftPanel();
|
||||
const { previewState, setPreviewState } = usePreviewPanel();
|
||||
|
||||
const panels: State = useMemo(
|
||||
() => ({
|
||||
left: leftPanelState,
|
||||
right: rightPanelState,
|
||||
preview: previewState || [],
|
||||
}),
|
||||
[leftPanelState, previewState, rightPanelState]
|
||||
);
|
||||
|
||||
const openPanels = useCallback(
|
||||
({
|
||||
right,
|
||||
left,
|
||||
preview,
|
||||
}: {
|
||||
right?: FlyoutPanelProps;
|
||||
left?: FlyoutPanelProps;
|
||||
preview?: FlyoutPanelProps;
|
||||
}) => {
|
||||
setRightPanelState(right);
|
||||
setLeftPanelState(left);
|
||||
setPreviewState(preview ? [preview] : []);
|
||||
},
|
||||
[setRightPanelState, setLeftPanelState, setPreviewState]
|
||||
);
|
||||
|
||||
const openRightPanel = useCallback(
|
||||
(panel: FlyoutPanelProps) => {
|
||||
setRightPanelState(panel);
|
||||
},
|
||||
[setRightPanelState]
|
||||
);
|
||||
|
||||
const openLeftPanel = useCallback(
|
||||
(panel: FlyoutPanelProps) => setLeftPanelState(panel),
|
||||
[setLeftPanelState]
|
||||
);
|
||||
|
||||
const openPreviewPanel = useCallback(
|
||||
(panel: FlyoutPanelProps) => setPreviewState([...(previewState ?? []), panel]),
|
||||
[previewState, setPreviewState]
|
||||
);
|
||||
|
||||
const closeRightPanel = useCallback(() => setRightPanelState(undefined), [setRightPanelState]);
|
||||
|
||||
const closeLeftPanel = useCallback(() => setLeftPanelState(undefined), [setLeftPanelState]);
|
||||
|
||||
const closePreviewPanel = useCallback(() => setPreviewState([]), [setPreviewState]);
|
||||
|
||||
const previousPreviewPanel = useCallback(
|
||||
() => setPreviewState(previewState?.slice(0, previewState.length - 1)),
|
||||
[previewState, setPreviewState]
|
||||
);
|
||||
|
||||
const closePanels = useCallback(() => {
|
||||
setRightPanelState(undefined);
|
||||
setLeftPanelState(undefined);
|
||||
setPreviewState([]);
|
||||
}, [setRightPanelState, setLeftPanelState, setPreviewState]);
|
||||
|
||||
const contextValue: ExpandableFlyoutApi = useMemo(
|
||||
() => ({
|
||||
panels,
|
||||
openFlyout: openPanels,
|
||||
openRightPanel,
|
||||
openLeftPanel,
|
||||
openPreviewPanel,
|
||||
closeRightPanel,
|
||||
closeLeftPanel,
|
||||
closePreviewPanel,
|
||||
closeFlyout: closePanels,
|
||||
previousPreviewPanel,
|
||||
}),
|
||||
[
|
||||
panels,
|
||||
openPanels,
|
||||
openRightPanel,
|
||||
openLeftPanel,
|
||||
openPreviewPanel,
|
||||
closeRightPanel,
|
||||
closeLeftPanel,
|
||||
closePreviewPanel,
|
||||
closePanels,
|
||||
previousPreviewPanel,
|
||||
]
|
||||
);
|
||||
|
||||
return contextValue;
|
||||
};
|
|
@ -6,18 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { createContext, FC, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
createDispatchHook,
|
||||
createSelectorHook,
|
||||
Provider as ReduxProvider,
|
||||
ReactReduxContextValue,
|
||||
} from 'react-redux';
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
|
||||
import { reducer } from '../reducer';
|
||||
import { initialState, State } from '../state';
|
||||
import type { ExpandableFlyoutApi, FlyoutPanelProps } from '../types';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
closeLeftPanelAction,
|
||||
closePanelsAction,
|
||||
|
@ -29,24 +18,15 @@ import {
|
|||
openRightPanelAction,
|
||||
previousPreviewPanelAction,
|
||||
} from '../actions';
|
||||
import { useDispatch } from '../redux';
|
||||
import { FlyoutPanelProps, type ExpandableFlyoutApi } from '../types';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer,
|
||||
devTools: process.env.NODE_ENV !== 'production',
|
||||
preloadedState: {},
|
||||
enhancers: [],
|
||||
});
|
||||
export type { ExpandableFlyoutApi };
|
||||
|
||||
export const Context = createContext<ReactReduxContextValue<State>>({
|
||||
store,
|
||||
storeState: initialState,
|
||||
});
|
||||
|
||||
const useDispatch = createDispatchHook(Context);
|
||||
const useSelector = createSelectorHook(Context);
|
||||
|
||||
export const useFlyoutMemoryState = (): ExpandableFlyoutApi => {
|
||||
const state = useSelector((s) => s);
|
||||
/**
|
||||
* This hook allows you to interact with the flyout, open panels and previews etc.
|
||||
*/
|
||||
export const useExpandableFlyoutApi = () => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const openPanels = useCallback(
|
||||
|
@ -94,7 +74,6 @@ export const useFlyoutMemoryState = (): ExpandableFlyoutApi => {
|
|||
|
||||
const api: ExpandableFlyoutApi = useMemo(
|
||||
() => ({
|
||||
panels: state,
|
||||
openFlyout: openPanels,
|
||||
openRightPanel,
|
||||
openLeftPanel,
|
||||
|
@ -106,7 +85,6 @@ export const useFlyoutMemoryState = (): ExpandableFlyoutApi => {
|
|||
previousPreviewPanel,
|
||||
}),
|
||||
[
|
||||
state,
|
||||
openPanels,
|
||||
openRightPanel,
|
||||
openLeftPanel,
|
||||
|
@ -121,15 +99,3 @@ export const useFlyoutMemoryState = (): ExpandableFlyoutApi => {
|
|||
|
||||
return api;
|
||||
};
|
||||
|
||||
/**
|
||||
* In-memory state provider for the expandable flyout, for cases when we don't want changes to be persisted
|
||||
* in the url.
|
||||
*/
|
||||
export const MemoryStateProvider: FC = ({ children }) => {
|
||||
return (
|
||||
<ReduxProvider context={Context} store={store}>
|
||||
{children}
|
||||
</ReduxProvider>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { stateSelector, useSelector } from '../redux';
|
||||
|
||||
/**
|
||||
* This hook allows you to access the flyout state, read open panels and previews.
|
||||
*/
|
||||
export const useExpandableFlyoutState = () => {
|
||||
return useSelector(stateSelector);
|
||||
};
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { useUrlState } from '@kbn/url-state';
|
||||
import { EXPANDABLE_FLYOUT_URL_KEY } from '../constants';
|
||||
import { FlyoutPanelProps } from '../types';
|
||||
|
||||
/**
|
||||
* This hook stores state in the URL
|
||||
*/
|
||||
export const useLeftPanel = () => {
|
||||
const [leftPanelState, setLeftPanelState] = useUrlState<FlyoutPanelProps>(
|
||||
EXPANDABLE_FLYOUT_URL_KEY,
|
||||
'leftPanel'
|
||||
);
|
||||
|
||||
return { leftPanelState, setLeftPanelState } as const;
|
||||
};
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { useUrlState } from '@kbn/url-state';
|
||||
import { EXPANDABLE_FLYOUT_URL_KEY } from '../constants';
|
||||
import { FlyoutPanelProps } from '../types';
|
||||
|
||||
/**
|
||||
* This hook stores state in the URL
|
||||
*/
|
||||
export const usePreviewPanel = () => {
|
||||
const [previewState, setPreviewState] = useUrlState<FlyoutPanelProps[]>(
|
||||
EXPANDABLE_FLYOUT_URL_KEY,
|
||||
'preview'
|
||||
);
|
||||
|
||||
return { previewState, setPreviewState } as const;
|
||||
};
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { useUrlState } from '@kbn/url-state';
|
||||
import { EXPANDABLE_FLYOUT_URL_KEY } from '../constants';
|
||||
import { FlyoutPanelProps } from '../types';
|
||||
|
||||
/**
|
||||
* This hook stores state in the URL
|
||||
*/
|
||||
export const useRightPanel = () => {
|
||||
const [rightPanelState, setRightPanelState] = useUrlState<FlyoutPanelProps>(
|
||||
EXPANDABLE_FLYOUT_URL_KEY,
|
||||
'rightPanel'
|
||||
);
|
||||
|
||||
return { rightPanelState, setRightPanelState } as const;
|
||||
};
|
|
@ -19,8 +19,8 @@ import {
|
|||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { ExpandableFlyout } from '.';
|
||||
import { ExpandableFlyoutContextValue } from './context';
|
||||
import { TestProvider } from './test/provider';
|
||||
import { State } from './state';
|
||||
|
||||
export default {
|
||||
component: ExpandableFlyout,
|
||||
|
@ -101,89 +101,81 @@ const registeredPanels = [
|
|||
];
|
||||
|
||||
export const Right: Story<void> = () => {
|
||||
const context = {
|
||||
panels: {
|
||||
right: {
|
||||
id: 'right',
|
||||
},
|
||||
left: {},
|
||||
preview: [],
|
||||
const state = {
|
||||
right: {
|
||||
id: 'right',
|
||||
},
|
||||
} as unknown as ExpandableFlyoutContextValue;
|
||||
left: {},
|
||||
preview: [],
|
||||
} as unknown as State;
|
||||
|
||||
return (
|
||||
<TestProvider state={context.panels}>
|
||||
<TestProvider state={state}>
|
||||
<ExpandableFlyout registeredPanels={registeredPanels} />
|
||||
</TestProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const Left: Story<void> = () => {
|
||||
const context = {
|
||||
panels: {
|
||||
right: {
|
||||
id: 'right',
|
||||
},
|
||||
left: {
|
||||
id: 'left',
|
||||
},
|
||||
preview: [],
|
||||
const state = {
|
||||
right: {
|
||||
id: 'right',
|
||||
},
|
||||
} as unknown as ExpandableFlyoutContextValue;
|
||||
left: {
|
||||
id: 'left',
|
||||
},
|
||||
preview: [],
|
||||
} as unknown as State;
|
||||
|
||||
return (
|
||||
<TestProvider state={context.panels}>
|
||||
<TestProvider state={state}>
|
||||
<ExpandableFlyout registeredPanels={registeredPanels} />
|
||||
</TestProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const Preview: Story<void> = () => {
|
||||
const context = {
|
||||
panels: {
|
||||
right: {
|
||||
id: 'right',
|
||||
},
|
||||
left: {
|
||||
id: 'left',
|
||||
},
|
||||
preview: [
|
||||
{
|
||||
id: 'preview1',
|
||||
},
|
||||
],
|
||||
const state = {
|
||||
right: {
|
||||
id: 'right',
|
||||
},
|
||||
} as unknown as ExpandableFlyoutContextValue;
|
||||
left: {
|
||||
id: 'left',
|
||||
},
|
||||
preview: [
|
||||
{
|
||||
id: 'preview1',
|
||||
},
|
||||
],
|
||||
} as unknown as State;
|
||||
|
||||
return (
|
||||
<TestProvider state={context.panels}>
|
||||
<TestProvider state={state}>
|
||||
<ExpandableFlyout registeredPanels={registeredPanels} />
|
||||
</TestProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const MultiplePreviews: Story<void> = () => {
|
||||
const context = {
|
||||
panels: {
|
||||
right: {
|
||||
id: 'right',
|
||||
},
|
||||
left: {
|
||||
id: 'left',
|
||||
},
|
||||
preview: [
|
||||
{
|
||||
id: 'preview1',
|
||||
},
|
||||
{
|
||||
id: 'preview2',
|
||||
},
|
||||
],
|
||||
const state = {
|
||||
right: {
|
||||
id: 'right',
|
||||
},
|
||||
} as unknown as ExpandableFlyoutContextValue;
|
||||
left: {
|
||||
id: 'left',
|
||||
},
|
||||
preview: [
|
||||
{
|
||||
id: 'preview1',
|
||||
},
|
||||
{
|
||||
id: 'preview2',
|
||||
},
|
||||
],
|
||||
} as unknown as State;
|
||||
|
||||
return (
|
||||
<TestProvider state={context.panels}>
|
||||
<TestProvider state={state}>
|
||||
<ExpandableFlyout registeredPanels={registeredPanels} />
|
||||
</TestProvider>
|
||||
);
|
||||
|
|
|
@ -18,7 +18,6 @@ import {
|
|||
} from './components/test_ids';
|
||||
import { type State } from './state';
|
||||
import { TestProvider } from './test/provider';
|
||||
jest.mock('./context/url_state_provider');
|
||||
|
||||
const registeredPanels: Panel[] = [
|
||||
{
|
||||
|
|
|
@ -11,7 +11,8 @@ import { EuiFlyoutProps } from '@elastic/eui';
|
|||
import { EuiFlexGroup, EuiFlyout } from '@elastic/eui';
|
||||
import { useSectionSizes } from './hooks/use_sections_sizes';
|
||||
import { useWindowSize } from './hooks/use_window_size';
|
||||
import { useExpandableFlyoutContext } from './context';
|
||||
import { useExpandableFlyoutState } from './hooks/use_expandable_flyout_state';
|
||||
import { useExpandableFlyoutApi } from './hooks/use_expandable_flyout_api';
|
||||
import { PreviewSection } from './components/preview_section';
|
||||
import { RightSection } from './components/right_section';
|
||||
import type { FlyoutPanelProps, Panel } from './types';
|
||||
|
@ -40,9 +41,8 @@ export const ExpandableFlyout: React.FC<ExpandableFlyoutProps> = ({
|
|||
}) => {
|
||||
const windowWidth = useWindowSize();
|
||||
|
||||
const { closeFlyout, panels } = useExpandableFlyoutContext();
|
||||
|
||||
const { left, right, preview } = panels;
|
||||
const { left, right, preview } = useExpandableFlyoutState();
|
||||
const { closeFlyout } = useExpandableFlyoutApi();
|
||||
|
||||
const leftSection = useMemo(
|
||||
() => registeredPanels.find((panel) => panel.key === left?.id),
|
||||
|
|
|
@ -6,16 +6,70 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { FC, PropsWithChildren } from 'react';
|
||||
import { ExpandableFlyoutContext } from './context';
|
||||
import { MemoryStateProvider } from './context/memory_state_provider';
|
||||
import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public';
|
||||
import React, { FC, PropsWithChildren, useEffect, useMemo } from 'react';
|
||||
import { Provider as ReduxProvider } from 'react-redux';
|
||||
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { State } from './state';
|
||||
import { useExpandableFlyoutState } from './hooks/use_expandable_flyout_state';
|
||||
import { EXPANDABLE_FLYOUT_URL_KEY } from './constants';
|
||||
import { Context, store, useDispatch } from './redux';
|
||||
import { urlChangedAction } from './actions';
|
||||
|
||||
export type ExpandableFlyoutStorageMode = 'memory' | 'url';
|
||||
|
||||
/**
|
||||
* Dispatches actions when url state changes and initializes the state when the app is loaded with flyout url parameters
|
||||
*/
|
||||
const UrlSynchronizer = () => {
|
||||
const state = useExpandableFlyoutState();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
const urlStorage = useMemo(
|
||||
() =>
|
||||
createKbnUrlStateStorage({
|
||||
history,
|
||||
useHash: false,
|
||||
useHashQuery: false,
|
||||
}),
|
||||
[history]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const currentValue = urlStorage.get<State>(EXPANDABLE_FLYOUT_URL_KEY);
|
||||
|
||||
// Dispatch current value to redux store as it does not happen automatically
|
||||
if (currentValue) {
|
||||
dispatch(urlChangedAction({ ...currentValue, preview: currentValue?.preview[0] }));
|
||||
}
|
||||
|
||||
const subscription = urlStorage.change$<State>(EXPANDABLE_FLYOUT_URL_KEY).subscribe((value) => {
|
||||
dispatch(urlChangedAction({ ...value, preview: value?.preview?.[0] }));
|
||||
});
|
||||
|
||||
return () => subscription.unsubscribe();
|
||||
}, [dispatch, urlStorage]);
|
||||
|
||||
useEffect(() => {
|
||||
const { needsSync, ...stateToSync } = state;
|
||||
|
||||
if (needsSync) {
|
||||
urlStorage.set(EXPANDABLE_FLYOUT_URL_KEY, stateToSync);
|
||||
}
|
||||
}, [urlStorage, state]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
interface ExpandableFlyoutProviderProps {
|
||||
/**
|
||||
* This allows the user to choose how the flyout storage is handled.
|
||||
* Url storage syncs current values straight to the browser query string.
|
||||
*/
|
||||
storage?: 'url' | 'memory';
|
||||
storage?: ExpandableFlyoutStorageMode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,8 +84,11 @@ export const ExpandableFlyoutProvider: FC<PropsWithChildren<ExpandableFlyoutProv
|
|||
storage = 'url',
|
||||
}) => {
|
||||
return (
|
||||
<ExpandableFlyoutContext.Provider value={storage}>
|
||||
<MemoryStateProvider>{children}</MemoryStateProvider>
|
||||
</ExpandableFlyoutContext.Provider>
|
||||
<ReduxProvider context={Context} store={store}>
|
||||
<>
|
||||
{storage === 'url' ? <UrlSynchronizer /> : null}
|
||||
{children}
|
||||
</>
|
||||
</ReduxProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -61,6 +61,7 @@ describe('reducer', () => {
|
|||
left: leftPanel1,
|
||||
right: rightPanel1,
|
||||
preview: [previewPanel1],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -81,6 +82,7 @@ describe('reducer', () => {
|
|||
left: leftPanel2,
|
||||
right: rightPanel2,
|
||||
preview: [previewPanel2],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -99,6 +101,7 @@ describe('reducer', () => {
|
|||
left: undefined,
|
||||
right: rightPanel2,
|
||||
preview: [],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -113,6 +116,7 @@ describe('reducer', () => {
|
|||
left: undefined,
|
||||
right: rightPanel1,
|
||||
preview: [],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -129,6 +133,7 @@ describe('reducer', () => {
|
|||
left: leftPanel1,
|
||||
right: rightPanel2,
|
||||
preview: [previewPanel1],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -143,6 +148,7 @@ describe('reducer', () => {
|
|||
left: leftPanel1,
|
||||
right: undefined,
|
||||
preview: [],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -159,6 +165,7 @@ describe('reducer', () => {
|
|||
left: leftPanel2,
|
||||
right: rightPanel1,
|
||||
preview: [previewPanel1],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -173,6 +180,7 @@ describe('reducer', () => {
|
|||
left: undefined,
|
||||
right: undefined,
|
||||
preview: [previewPanel1],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -189,6 +197,7 @@ describe('reducer', () => {
|
|||
left: leftPanel1,
|
||||
right: rightPanel1,
|
||||
preview: [previewPanel1, previewPanel2],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -199,7 +208,7 @@ describe('reducer', () => {
|
|||
const action = closeRightPanelAction();
|
||||
const newState: State = reducer(state, action);
|
||||
|
||||
expect(newState).toEqual(state);
|
||||
expect(newState).toEqual({ ...state, needsSync: true });
|
||||
});
|
||||
|
||||
it(`should return unmodified state when removing right panel when no right panel exist`, () => {
|
||||
|
@ -207,6 +216,7 @@ describe('reducer', () => {
|
|||
left: leftPanel1,
|
||||
right: undefined,
|
||||
preview: [previewPanel1],
|
||||
needsSync: true,
|
||||
};
|
||||
const action = closeRightPanelAction();
|
||||
const newState: State = reducer(state, action);
|
||||
|
@ -228,6 +238,7 @@ describe('reducer', () => {
|
|||
left: leftPanel1,
|
||||
right: undefined,
|
||||
preview: [previewPanel1],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -238,7 +249,7 @@ describe('reducer', () => {
|
|||
const action = closeLeftPanelAction();
|
||||
const newState: State = reducer(state, action);
|
||||
|
||||
expect(newState).toEqual(state);
|
||||
expect(newState).toEqual({ ...state, needsSync: true });
|
||||
});
|
||||
|
||||
it(`should return unmodified state when removing left panel when no left panel exist`, () => {
|
||||
|
@ -246,6 +257,7 @@ describe('reducer', () => {
|
|||
left: undefined,
|
||||
right: rightPanel1,
|
||||
preview: [],
|
||||
needsSync: true,
|
||||
};
|
||||
const action = closeLeftPanelAction();
|
||||
const newState: State = reducer(state, action);
|
||||
|
@ -266,13 +278,14 @@ describe('reducer', () => {
|
|||
left: undefined,
|
||||
right: rightPanel1,
|
||||
preview: [previewPanel1],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('should handle closePreviewPanel action', () => {
|
||||
it('should return empty state when removing preview panel on empty state', () => {
|
||||
const state: State = initialState;
|
||||
const state: State = { ...initialState, needsSync: true };
|
||||
const action = closePreviewPanelAction();
|
||||
const newState: State = reducer(state, action);
|
||||
|
||||
|
@ -288,7 +301,7 @@ describe('reducer', () => {
|
|||
const action = closePreviewPanelAction();
|
||||
const newState: State = reducer(state, action);
|
||||
|
||||
expect(newState).toEqual(state);
|
||||
expect(newState).toEqual({ ...state, needsSync: true });
|
||||
});
|
||||
|
||||
it('should remove all preview panels', () => {
|
||||
|
@ -304,6 +317,7 @@ describe('reducer', () => {
|
|||
left: rightPanel1,
|
||||
right: leftPanel1,
|
||||
preview: [],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -314,7 +328,7 @@ describe('reducer', () => {
|
|||
const action = previousPreviewPanelAction();
|
||||
const newState: State = reducer(state, action);
|
||||
|
||||
expect(newState).toEqual(state);
|
||||
expect(newState).toEqual({ ...initialState, needsSync: true });
|
||||
});
|
||||
|
||||
it(`should return unmodified state when previous preview panel when no preview panel exist`, () => {
|
||||
|
@ -322,6 +336,7 @@ describe('reducer', () => {
|
|||
left: leftPanel1,
|
||||
right: rightPanel1,
|
||||
preview: [],
|
||||
needsSync: true,
|
||||
};
|
||||
const action = previousPreviewPanelAction();
|
||||
const newState: State = reducer(state, action);
|
||||
|
@ -342,6 +357,7 @@ describe('reducer', () => {
|
|||
left: leftPanel1,
|
||||
right: rightPanel1,
|
||||
preview: [previewPanel1],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -352,7 +368,7 @@ describe('reducer', () => {
|
|||
const action = closePanelsAction();
|
||||
const newState: State = reducer(state, action);
|
||||
|
||||
expect(newState).toEqual(initialState);
|
||||
expect(newState).toEqual({ ...initialState, needsSync: true });
|
||||
});
|
||||
|
||||
it('should remove all panels', () => {
|
||||
|
@ -368,6 +384,7 @@ describe('reducer', () => {
|
|||
left: undefined,
|
||||
right: undefined,
|
||||
preview: [],
|
||||
needsSync: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
closeRightPanelAction,
|
||||
previousPreviewPanelAction,
|
||||
openPreviewPanelAction,
|
||||
urlChangedAction,
|
||||
} from './actions';
|
||||
import { initialState } from './state';
|
||||
|
||||
|
@ -25,39 +26,57 @@ export const reducer = createReducer(initialState, (builder) => {
|
|||
state.preview = preview ? [preview] : [];
|
||||
state.right = right;
|
||||
state.left = left;
|
||||
state.needsSync = true;
|
||||
});
|
||||
|
||||
builder.addCase(openLeftPanelAction, (state, { payload }) => {
|
||||
state.left = payload;
|
||||
state.needsSync = true;
|
||||
});
|
||||
|
||||
builder.addCase(openRightPanelAction, (state, { payload }) => {
|
||||
state.right = payload;
|
||||
state.needsSync = true;
|
||||
});
|
||||
|
||||
builder.addCase(openPreviewPanelAction, (state, { payload }) => {
|
||||
state.preview.push(payload);
|
||||
state.needsSync = true;
|
||||
});
|
||||
|
||||
builder.addCase(previousPreviewPanelAction, (state) => {
|
||||
state.preview.pop();
|
||||
state.needsSync = true;
|
||||
});
|
||||
|
||||
builder.addCase(closePanelsAction, (state) => {
|
||||
state.preview = [];
|
||||
state.right = undefined;
|
||||
state.left = undefined;
|
||||
state.needsSync = true;
|
||||
});
|
||||
|
||||
builder.addCase(closeLeftPanelAction, (state) => {
|
||||
state.left = undefined;
|
||||
state.needsSync = true;
|
||||
});
|
||||
|
||||
builder.addCase(closeRightPanelAction, (state) => {
|
||||
state.right = undefined;
|
||||
state.needsSync = true;
|
||||
});
|
||||
|
||||
builder.addCase(closePreviewPanelAction, (state) => {
|
||||
state.preview = [];
|
||||
state.needsSync = true;
|
||||
});
|
||||
|
||||
builder.addCase(urlChangedAction, (state, { payload: { preview, left, right } }) => {
|
||||
state.needsSync = false;
|
||||
state.preview = preview ? [preview] : [];
|
||||
state.left = left;
|
||||
state.right = right;
|
||||
|
||||
return state;
|
||||
});
|
||||
});
|
||||
|
|
29
packages/kbn-expandable-flyout/src/redux.ts
Normal file
29
packages/kbn-expandable-flyout/src/redux.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createContext } from 'react';
|
||||
import { createDispatchHook, createSelectorHook, ReactReduxContextValue } from 'react-redux';
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import { reducer } from './reducer';
|
||||
import { initialState, State } from './state';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer,
|
||||
devTools: process.env.NODE_ENV !== 'production',
|
||||
enhancers: [],
|
||||
});
|
||||
|
||||
export const Context = createContext<ReactReduxContextValue<State>>({
|
||||
store,
|
||||
storeState: initialState,
|
||||
});
|
||||
|
||||
export const useDispatch = createDispatchHook(Context);
|
||||
export const useSelector = createSelectorHook(Context);
|
||||
|
||||
export const stateSelector = (state: State) => state;
|
|
@ -21,10 +21,18 @@ export interface State {
|
|||
* Panels to render in the preview section
|
||||
*/
|
||||
preview: FlyoutPanelProps[];
|
||||
|
||||
/**
|
||||
* Is the flyout in sync with external storage (eg. url)?
|
||||
* This value can be used in useEffect for example, to control whether we should
|
||||
* call an external state sync method.
|
||||
*/
|
||||
needsSync?: boolean;
|
||||
}
|
||||
|
||||
export const initialState: State = {
|
||||
left: undefined,
|
||||
right: undefined,
|
||||
preview: [],
|
||||
needsSync: false,
|
||||
};
|
||||
|
|
|
@ -10,8 +10,7 @@ import { Provider as ReduxProvider } from 'react-redux';
|
|||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import React, { FC, PropsWithChildren } from 'react';
|
||||
import { reducer } from '../reducer';
|
||||
import { Context } from '../context/memory_state_provider';
|
||||
import { ExpandableFlyoutContext } from '../context';
|
||||
import { Context } from '../redux';
|
||||
import { initialState, State } from '../state';
|
||||
|
||||
interface TestProviderProps {
|
||||
|
@ -30,10 +29,8 @@ export const TestProvider: FC<PropsWithChildren<TestProviderProps>> = ({
|
|||
});
|
||||
|
||||
return (
|
||||
<ExpandableFlyoutContext.Provider value="memory">
|
||||
<ReduxProvider store={store} context={Context}>
|
||||
{children}
|
||||
</ReduxProvider>
|
||||
</ExpandableFlyoutContext.Provider>
|
||||
<ReduxProvider store={store} context={Context}>
|
||||
{children}
|
||||
</ReduxProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -7,14 +7,8 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { State } from './state';
|
||||
|
||||
export interface ExpandableFlyoutApi {
|
||||
/**
|
||||
* Right, left and preview panels
|
||||
*/
|
||||
panels: State;
|
||||
|
||||
/**
|
||||
* Open the flyout with left, right and/or preview panels
|
||||
*/
|
||||
|
|
|
@ -20,6 +20,6 @@
|
|||
],
|
||||
"kbn_references": [
|
||||
"@kbn/i18n",
|
||||
"@kbn/url-state"
|
||||
"@kbn/kibana-utils-plugin"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import { mockDataFormattedForFieldBrowser } from '../../shared/mocks/mock_data_f
|
|||
import { TestProvidersComponent } from '../../../../common/mock';
|
||||
import { useGetAlertDetailsFlyoutLink } from '../../../../timelines/components/side_panel/event_details/use_get_alert_details_flyout_link';
|
||||
import { URL_PARAM_KEY } from '../../../../common/hooks/use_url_state';
|
||||
import { ExpandableFlyoutProvider } from '@kbn/expandable-flyout';
|
||||
|
||||
jest.mock('../../../../common/lib/kibana');
|
||||
jest.mock('../hooks/use_assistant');
|
||||
|
@ -40,11 +39,9 @@ const mockContextValue = {
|
|||
const renderHeaderActions = (contextValue: RightPanelContext) =>
|
||||
render(
|
||||
<TestProvidersComponent>
|
||||
<ExpandableFlyoutProvider>
|
||||
<RightPanelContext.Provider value={contextValue}>
|
||||
<HeaderActions />
|
||||
</RightPanelContext.Provider>
|
||||
</ExpandableFlyoutProvider>
|
||||
<RightPanelContext.Provider value={contextValue}>
|
||||
<HeaderActions />
|
||||
</RightPanelContext.Provider>
|
||||
</TestProvidersComponent>
|
||||
);
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
INVESTIGATION_GUIDE_TEST_ID,
|
||||
} from './test_ids';
|
||||
import { mockContextValue } from '../mocks/mock_context';
|
||||
import { mockFlyoutContextValue } from '../../shared/mocks/mock_flyout_context';
|
||||
import type { ExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { useExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
import { useInvestigationGuide } from '../../shared/hooks/use_investigation_guide';
|
||||
|
@ -25,6 +24,8 @@ import { LeftPanelInvestigationTab, DocumentDetailsLeftPanelKey } from '../../le
|
|||
jest.mock('../../shared/hooks/use_investigation_guide');
|
||||
jest.mock('@kbn/expandable-flyout', () => ({ useExpandableFlyoutApi: jest.fn() }));
|
||||
|
||||
const mockFlyoutContextValue = { openLeftPanel: jest.fn() };
|
||||
|
||||
const NO_DATA_MESSAGE = 'Investigation guideThere’s no investigation guide for this rule.';
|
||||
const PREVIEW_MESSAGE = 'Investigation guide is not available in alert preview.';
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ExpandableFlyoutContextValue } from '@kbn/expandable-flyout/src/context';
|
||||
import type { ExpandableFlyoutApi } from '@kbn/expandable-flyout';
|
||||
|
||||
/**
|
||||
* Mock flyout context
|
||||
* Mock flyout api
|
||||
*/
|
||||
export const mockFlyoutContextValue: ExpandableFlyoutContextValue = {
|
||||
export const mockFlyoutApi: ExpandableFlyoutApi = {
|
||||
openFlyout: jest.fn(),
|
||||
openRightPanel: jest.fn(),
|
||||
openLeftPanel: jest.fn(),
|
||||
|
@ -20,9 +20,4 @@ export const mockFlyoutContextValue: ExpandableFlyoutContextValue = {
|
|||
closePreviewPanel: jest.fn(),
|
||||
previousPreviewPanel: jest.fn(),
|
||||
closeFlyout: jest.fn(),
|
||||
panels: {
|
||||
left: undefined,
|
||||
right: undefined,
|
||||
preview: [],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -19,18 +19,13 @@ import type { ExpandableFlyoutState } from '@kbn/expandable-flyout';
|
|||
import {
|
||||
useExpandableFlyoutApi,
|
||||
type ExpandableFlyoutApi,
|
||||
ExpandableFlyoutProvider,
|
||||
useExpandableFlyoutState,
|
||||
} from '@kbn/expandable-flyout';
|
||||
|
||||
const expandDetails = jest.fn();
|
||||
|
||||
const ExpandableFlyoutTestProviders: FC<PropsWithChildren<{}>> = ({ children }) => {
|
||||
return (
|
||||
<TestProviders>
|
||||
<ExpandableFlyoutProvider>{children}</ExpandableFlyoutProvider>
|
||||
</TestProviders>
|
||||
);
|
||||
return <TestProviders>{children}</TestProviders>;
|
||||
};
|
||||
|
||||
jest.mock('@kbn/expandable-flyout', () => ({
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { type PropsWithChildren } from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
|
@ -23,14 +23,12 @@ jest.mock('../../../../../common/hooks/use_experimental_features', () => ({
|
|||
useIsExperimentalFeatureEnabled: () => mockUseIsExperimentalFeatureEnabled,
|
||||
}));
|
||||
|
||||
jest.mock('@kbn/expandable-flyout/src/context', () => {
|
||||
const original = jest.requireActual('@kbn/expandable-flyout/src/context');
|
||||
|
||||
jest.mock('@kbn/expandable-flyout', () => {
|
||||
return {
|
||||
...original,
|
||||
useExpandableFlyoutApi: () => ({
|
||||
openRightPanel: mockOpenRightPanel,
|
||||
}),
|
||||
TestProvider: ({ children }: PropsWithChildren<{}>) => <>{children}</>,
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -26,13 +26,13 @@ describe('Expandable flyout state sync', { tags: ['@ess', '@serverless'] }, () =
|
|||
});
|
||||
|
||||
it('should test flyout url sync', () => {
|
||||
cy.url().should('not.include', 'rightPanel');
|
||||
cy.url().should('not.include', 'right');
|
||||
|
||||
expandFirstAlertExpandableFlyout();
|
||||
|
||||
cy.log('should serialize its state to url');
|
||||
|
||||
cy.url().should('include', 'rightPanel');
|
||||
cy.url().should('include', 'right');
|
||||
cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('have.text', rule.name);
|
||||
|
||||
cy.log('should reopen the flyout after browser refresh');
|
||||
|
@ -40,13 +40,13 @@ describe('Expandable flyout state sync', { tags: ['@ess', '@serverless'] }, () =
|
|||
cy.reload();
|
||||
waitForAlertsToPopulate();
|
||||
|
||||
cy.url().should('include', 'rightPanel');
|
||||
cy.url().should('include', 'right');
|
||||
cy.get(DOCUMENT_DETAILS_FLYOUT_HEADER_TITLE).should('have.text', rule.name);
|
||||
|
||||
cy.log('should clear the url state when flyout is closed');
|
||||
|
||||
closeFlyout();
|
||||
|
||||
cy.url().should('not.include', 'rightPanel');
|
||||
cy.url().should('not.include', 'right');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue