diff --git a/x-pack/plugins/security_solution/common/ecs/process/index.ts b/x-pack/plugins/security_solution/common/ecs/process/index.ts index 2a58c6d5b47d0..02122c776e95d 100644 --- a/x-pack/plugins/security_solution/common/ecs/process/index.ts +++ b/x-pack/plugins/security_solution/common/ecs/process/index.ts @@ -11,6 +11,9 @@ export interface ProcessEcs { Ext?: Ext; command_line?: string[]; entity_id?: string[]; + entry_leader?: ProcessSessionData; + session_leader?: ProcessSessionData; + group_leader?: ProcessSessionData; exit_code?: number[]; hash?: ProcessHashData; parent?: ProcessParentData; @@ -25,6 +28,12 @@ export interface ProcessEcs { working_directory?: string[]; } +export interface ProcessSessionData { + entity_id?: string[]; + pid?: string[]; + name?: string[]; +} + export interface ProcessHashData { md5?: string[]; sha1?: string[]; diff --git a/x-pack/plugins/security_solution/common/types/timeline/index.ts b/x-pack/plugins/security_solution/common/types/timeline/index.ts index 93dd6f9efb671..d2e9c2a6715fe 100644 --- a/x-pack/plugins/security_solution/common/types/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/types/timeline/index.ts @@ -482,6 +482,7 @@ export enum TimelineTabs { notes = 'notes', pinned = 'pinned', eql = 'eql', + session = 'session', } /** diff --git a/x-pack/plugins/security_solution/kibana.json b/x-pack/plugins/security_solution/kibana.json index bd18b5d4acc31..cf13586a1a03f 100644 --- a/x-pack/plugins/security_solution/kibana.json +++ b/x-pack/plugins/security_solution/kibana.json @@ -22,6 +22,7 @@ "licensing", "maps", "ruleRegistry", + "sessionView", "taskManager", "timelines", "triggersActionsUi", diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx index 0dd137a2321c6..7e71174c85a14 100644 --- a/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx +++ b/x-pack/plugins/security_solution/public/common/components/alerts_viewer/alerts_table.tsx @@ -80,7 +80,7 @@ const AlertsTableComponent: React.FC = ({ const dispatch = useDispatch(); const alertsFilter = useMemo(() => [...defaultAlertsFilters, ...pageFilters], [pageFilters]); const { filterManager } = useKibana().services.data.query; - const ACTION_BUTTON_COUNT = 4; + const ACTION_BUTTON_COUNT = 5; const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled'); diff --git a/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx b/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx index cfd6546470d4a..30b7bd4f53e60 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_tab/events_query_tab_body.tsx @@ -84,7 +84,7 @@ const EventsQueryTabBodyComponent: React.FC = }) => { const dispatch = useDispatch(); const { globalFullScreen } = useGlobalFullScreen(); - const ACTION_BUTTON_COUNT = 4; + const ACTION_BUTTON_COUNT = 5; const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled'); useEffect(() => { diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx index bd3321511156d..9da27bc470a94 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.test.tsx @@ -9,9 +9,8 @@ import React from 'react'; import useResizeObserver from 'use-resize-observer/polyfilled'; import '../../mock/match_media'; -import { waitFor } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { TestProviders } from '../../mock'; -import { useMountAppended } from '../../utils/use_mount_appended'; import { mockEventViewerResponse } from './mock'; import { StatefulEventsViewer } from '.'; @@ -61,37 +60,27 @@ const testProps = { start: from, }; describe('StatefulEventsViewer', () => { - const mount = useMountAppended(); - (useTimelineEvents as jest.Mock).mockReturnValue([false, mockEventViewerResponse]); test('it renders the events viewer', async () => { - const wrapper = mount( + const wrapper = render( ); - await waitFor(() => { - wrapper.update(); - - expect(wrapper.text()).toMatchInlineSnapshot(`"hello grid"`); - }); + expect(wrapper.getByText('hello grid')).toBeTruthy(); }); // InspectButtonContainer controls displaying InspectButton components test('it renders InspectButtonContainer', async () => { - const wrapper = mount( + const wrapper = render( ); - await waitFor(() => { - wrapper.update(); - - expect(wrapper.find(`InspectButtonContainer`).exists()).toBe(true); - }); + expect(wrapper.getByTestId(`hoverVisibilityContainer`)).toBeTruthy(); }); test('it closes field editor when unmounted', async () => { @@ -101,14 +90,14 @@ describe('StatefulEventsViewer', () => { return {}; }); - const wrapper = mount( + const { unmount } = render( ); expect(mockCloseEditor).not.toHaveBeenCalled(); - wrapper.unmount(); + unmount(); expect(mockCloseEditor).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index 0053ed13923d4..d46ab4b62be68 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -24,7 +24,6 @@ import { SourcererScopeName } from '../../store/sourcerer/model'; import { useSourcererDataView } from '../../containers/sourcerer'; import type { EntityType } from '../../../../../timelines/common'; import { TGridCellAction } from '../../../../../timelines/common/types'; -import { DetailsPanel } from '../../../timelines/components/side_panel'; import { CellValueElementProps } from '../../../timelines/components/timeline/cell_rendering'; import { FIELDS_WITHOUT_CELL_ACTIONS } from '../../lib/cell_actions/constants'; import { useGetUserCasesPermissions, useKibana } from '../../lib/kibana'; @@ -33,6 +32,7 @@ import { useFieldBrowserOptions, FieldEditorActions, } from '../../../timelines/components/fields_browser'; +import { useSessionView } from '../../../timelines/components/timeline/session_tab_content/use_session_view'; const EMPTY_CONTROL_COLUMNS: ControlColumnProps[] = []; @@ -105,6 +105,7 @@ const StatefulEventsViewerComponent: React.FC = ({ itemsPerPage, itemsPerPageOptions, kqlMode, + sessionViewId, showCheckboxes, sort, } = defaultModel, @@ -155,11 +156,19 @@ const StatefulEventsViewerComponent: React.FC = ({ const globalFilters = useMemo(() => [...filters, ...(pageFilters ?? [])], [filters, pageFilters]); const trailingControlColumns: ControlColumnProps[] = EMPTY_CONTROL_COLUMNS; - const graphOverlay = useMemo( - () => - graphEventId != null && graphEventId.length > 0 ? : null, - [graphEventId, id] - ); + + const { DetailsPanel, SessionView, Navigation } = useSessionView({ + entityType, + timelineId: id, + }); + + const graphOverlay = useMemo(() => { + const shouldShowOverlay = + (graphEventId != null && graphEventId.length > 0) || sessionViewId !== null; + return shouldShowOverlay ? ( + + ) : null; + }, [graphEventId, id, sessionViewId, SessionView, Navigation]); const setQuery = useCallback( (inspect, loading, refetch) => { dispatch(inputsActions.setQuery({ id, inputId: 'global', inspect, loading, refetch })); @@ -239,14 +248,7 @@ const StatefulEventsViewerComponent: React.FC = ({ })} - + {DetailsPanel} ); diff --git a/x-pack/plugins/security_solution/public/common/mock/global_state.ts b/x-pack/plugins/security_solution/public/common/mock/global_state.ts index bd90892a43fc6..31948a13db0f1 100644 --- a/x-pack/plugins/security_solution/public/common/mock/global_state.ts +++ b/x-pack/plugins/security_solution/public/common/mock/global_state.ts @@ -321,6 +321,7 @@ export const mockGlobalState: State = { end: '2020-07-08T08:20:18.966Z', }, selectedEventIds: {}, + sessionViewId: null, show: false, showCheckboxes: false, pinnedEventIds: {}, diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts index 2de29a8c3acf8..4bbdca8564a8e 100644 --- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts +++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts @@ -2011,6 +2011,7 @@ export const mockTimelineModel: TimelineModel = { savedObjectId: 'ef579e40-jibber-jabber', selectAll: false, selectedEventIds: {}, + sessionViewId: null, show: false, showCheckboxes: false, sort: [ @@ -2132,6 +2133,7 @@ export const defaultTimelineProps: CreateTimelineProps = { savedObjectId: null, selectAll: false, selectedEventIds: {}, + sessionViewId: null, show: false, showCheckboxes: false, sort: [{ columnId: '@timestamp', columnType: 'number', sortDirection: Direction.desc }], diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index b1226e5b59190..63f71dc0d3723 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -228,6 +228,7 @@ describe('alert actions', () => { savedObjectId: null, selectAll: false, selectedEventIds: {}, + sessionViewId: null, show: true, showCheckboxes: false, sort: [ diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx index dc8c5bf4de65e..7dc3561628193 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/default_config.tsx @@ -176,4 +176,5 @@ export const requiredFieldsForActions = [ 'file.hash.sha256', 'host.os.family', 'event.code', + 'process.entry_leader.entity_id', ]; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index c82c0c11237ee..b4f81e3e5f0e4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -104,7 +104,7 @@ export const AlertsTableComponent: React.FC = ({ const kibana = useKibana(); const [, dispatchToaster] = useStateToaster(); const { addWarning } = useAppToasts(); - const ACTION_BUTTON_COUNT = 4; + const ACTION_BUTTON_COUNT = 5; const getGlobalQuery = useCallback( (customFilters: Filter[]) => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.test.tsx index e8d144f07827f..90a5798108d88 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.test.tsx @@ -5,10 +5,10 @@ * 2.0. */ -import { waitFor } from '@testing-library/react'; -import { mount } from 'enzyme'; +import { render } from '@testing-library/react'; import React from 'react'; +import '@testing-library/jest-dom'; import { useGlobalFullScreen, useTimelineFullScreen, @@ -37,6 +37,24 @@ jest.mock('../../../resolver/view/use_state_syncing_actions'); const useStateSyncingActionsMock = useStateSyncingActions as jest.Mock; jest.mock('../../../resolver/view/use_sync_selected_node'); +jest.mock('../../../common/lib/kibana', () => { + const original = jest.requireActual('../../../common/lib/kibana'); + return { + ...original, + useKibana: () => ({ + services: { + sessionView: { + getSessionView: () =>
, + }, + data: { + search: { + search: jest.fn(), + }, + }, + }, + }), + }; +}); describe('GraphOverlay', () => { const { storage } = createSecuritySolutionStorageMock(); @@ -54,20 +72,18 @@ describe('GraphOverlay', () => { }); describe('when used in an events viewer (i.e. in the Detections view, or the Host > Events view)', () => { - test('it has 100% width when NOT in full screen mode', async () => { - const wrapper = mount( + test('it has 100% width when NOT in full screen mode', () => { + const wrapper = render( - + } Navigation={
} /> ); - await waitFor(() => { - const overlayContainer = wrapper.find('[data-test-subj="overlayContainer"]').first(); - expect(overlayContainer).toHaveStyleRule('width', '100%'); - }); + const overlayContainer = wrapper.getByTestId('overlayContainer'); + expect(overlayContainer).toHaveStyleRule('width', '100%'); }); - test('it has a fixed position when in full screen mode', async () => { + test('it has a fixed position when in full screen mode', () => { (useGlobalFullScreen as jest.Mock).mockReturnValue({ globalFullScreen: true, setGlobalFullScreen: jest.fn(), @@ -77,20 +93,18 @@ describe('GraphOverlay', () => { setTimelineFullScreen: jest.fn(), }); - const wrapper = mount( + const wrapper = render( - + } Navigation={
} /> ); - await waitFor(() => { - const overlayContainer = wrapper.find('[data-test-subj="overlayContainer"]').first(); - expect(overlayContainer).toHaveStyleRule('position', 'fixed'); - }); + const overlayContainer = wrapper.getByTestId('overlayContainer'); + expect(overlayContainer).toHaveStyleRule('position', 'fixed'); }); test('it gets index pattern from default data view', () => { - mount( + render( { storage )} > - + } Navigation={
} /> ); @@ -123,20 +137,18 @@ describe('GraphOverlay', () => { describe('when used in the active timeline', () => { const timelineId = TimelineId.active; - test('it has 100% width when NOT in full screen mode', async () => { - const wrapper = mount( + test('it has 100% width when NOT in full screen mode', () => { + const wrapper = render( - + } Navigation={
} /> ); - await waitFor(() => { - const overlayContainer = wrapper.find('[data-test-subj="overlayContainer"]').first(); - expect(overlayContainer).toHaveStyleRule('width', '100%'); - }); + const overlayContainer = wrapper.getByTestId('overlayContainer'); + expect(overlayContainer).toHaveStyleRule('width', '100%'); }); - test('it has 100% width when the active timeline is in full screen mode', async () => { + test('it has 100% width when the active timeline is in full screen mode', () => { (useGlobalFullScreen as jest.Mock).mockReturnValue({ globalFullScreen: false, setGlobalFullScreen: jest.fn(), @@ -146,20 +158,18 @@ describe('GraphOverlay', () => { setTimelineFullScreen: jest.fn(), }); - const wrapper = mount( + const wrapper = render( - + } Navigation={
} /> ); - await waitFor(() => { - const overlayContainer = wrapper.find('[data-test-subj="overlayContainer"]').first(); - expect(overlayContainer).toHaveStyleRule('width', '100%'); - }); + const overlayContainer = wrapper.getByTestId('overlayContainer'); + expect(overlayContainer).toHaveStyleRule('width', '100%'); }); test('it gets index pattern from Timeline data view', () => { - mount( + render( { storage )} > - + } Navigation={
} /> ); expect(useStateSyncingActionsMock.mock.calls[0][0].indices).toEqual(mockIndexNames); }); + + test('it renders session view controls', () => { + (useGlobalFullScreen as jest.Mock).mockReturnValue({ + globalFullScreen: false, + setGlobalFullScreen: jest.fn(), + }); + (useTimelineFullScreen as jest.Mock).mockReturnValue({ + timelineFullScreen: true, + setTimelineFullScreen: jest.fn(), + }); + + const wrapper = render( + + } + Navigation={
{'Close Session'}
} + /> +
+ ); + + expect(wrapper.getByText('Close Session')).toBeTruthy(); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx index 64475147edc9d..694003311e6c8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx @@ -5,25 +5,10 @@ * 2.0. */ -import { - EuiButtonEmpty, - EuiButtonIcon, - EuiFlexGroup, - EuiFlexItem, - EuiHorizontalRule, - EuiToolTip, - EuiLoadingSpinner, -} from '@elastic/eui'; -import React, { useCallback, useMemo, useEffect } from 'react'; +import React, { useMemo, useEffect } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiLoadingSpinner } from '@elastic/eui'; import { useDispatch } from 'react-redux'; import styled from 'styled-components'; - -import { FULL_SCREEN } from '../timeline/body/column_headers/translations'; -import { EXIT_FULL_SCREEN } from '../../../common/components/exit_full_screen/translations'; -import { - FULL_SCREEN_TOGGLED_CLASS_NAME, - SCROLLING_DISABLED_CLASS_NAME, -} from '../../../../common/constants'; import { useGlobalFullScreen, useTimelineFullScreen, @@ -33,7 +18,6 @@ import { TimelineId } from '../../../../common/types/timeline'; import { timelineSelectors } from '../../store/timeline'; import { timelineDefaults } from '../../store/timeline/defaults'; import { isFullScreen } from '../timeline/body/column_headers'; -import { updateTimelineGraphEventId } from '../../../timelines/store/timeline/actions'; import { inputsActions } from '../../../common/store/actions'; import { Resolver } from '../../../resolver/view'; import { @@ -41,7 +25,6 @@ import { startSelector, endSelector, } from '../../../common/components/super_date_picker/selectors'; -import * as i18n from './translations'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; import { sourcererSelectors } from '../../../common/store'; @@ -66,71 +49,35 @@ const StyledResolver = styled(Resolver)` height: 100%; `; -const FullScreenButtonIcon = styled(EuiButtonIcon)` - margin: 4px 0 4px 0; +const ScrollableFlexItem = styled(EuiFlexItem)` + ${({ theme }) => `margin: 0 ${theme.eui.euiSizeM};`} + overflow: hidden; + width: 100%; `; -interface OwnProps { - timelineId: TimelineId; -} - -interface NavigationProps { - fullScreen: boolean; - globalFullScreen: boolean; - onCloseOverlay: () => void; +interface GraphOverlayProps { timelineId: TimelineId; - timelineFullScreen: boolean; - toggleFullScreen: () => void; + SessionView: JSX.Element | null; + Navigation: JSX.Element | null; } -const NavigationComponent: React.FC = ({ - fullScreen, - globalFullScreen, - onCloseOverlay, +const GraphOverlayComponent: React.FC = ({ timelineId, - timelineFullScreen, - toggleFullScreen, -}) => ( - - - - {i18n.CLOSE_ANALYZER} - - - {timelineId !== TimelineId.active && ( - - - - - - )} - -); - -NavigationComponent.displayName = 'NavigationComponent'; - -const Navigation = React.memo(NavigationComponent); - -const GraphOverlayComponent: React.FC = ({ timelineId }) => { + SessionView, + Navigation, +}) => { const dispatch = useDispatch(); - const { globalFullScreen, setGlobalFullScreen } = useGlobalFullScreen(); - const { timelineFullScreen, setTimelineFullScreen } = useTimelineFullScreen(); + const { globalFullScreen } = useGlobalFullScreen(); + const { timelineFullScreen } = useTimelineFullScreen(); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const graphEventId = useDeepEqualSelector( (state) => (getTimeline(state, timelineId) ?? timelineDefaults).graphEventId ); + const sessionViewId = useDeepEqualSelector( + (state) => (getTimeline(state, timelineId) ?? timelineDefaults).sessionViewId + ); + const getStartSelector = useMemo(() => startSelector(), []); const getEndSelector = useMemo(() => endSelector(), []); const getIsLoadingSelector = useMemo(() => isLoadingSelector(), []); @@ -163,24 +110,6 @@ const GraphOverlayComponent: React.FC = ({ timelineId }) => { ); const isInTimeline = timelineId === TimelineId.active; - const onCloseOverlay = useCallback(() => { - const isDataGridFullScreen = document.querySelector('.euiDataGrid--fullScreen') !== null; - // Since EUI changes these values directly as a side effect, need to add them back on close. - if (isDataGridFullScreen) { - if (timelineId === TimelineId.active) { - document.body.classList.add('euiDataGrid__restrictBody'); - } else { - document.body.classList.add(SCROLLING_DISABLED_CLASS_NAME, 'euiDataGrid__restrictBody'); - } - } else { - if (timelineId === TimelineId.active) { - setTimelineFullScreen(false); - } else { - setGlobalFullScreen(false); - } - } - dispatch(updateTimelineGraphEventId({ id: timelineId, graphEventId: '' })); - }, [dispatch, timelineId, setTimelineFullScreen, setGlobalFullScreen]); useEffect(() => { return () => { @@ -192,20 +121,6 @@ const GraphOverlayComponent: React.FC = ({ timelineId }) => { }; }, [dispatch, timelineId]); - const toggleFullScreen = useCallback(() => { - if (timelineId === TimelineId.active) { - setTimelineFullScreen(!timelineFullScreen); - } else { - setGlobalFullScreen(!globalFullScreen); - } - }, [ - timelineId, - setTimelineFullScreen, - timelineFullScreen, - setGlobalFullScreen, - globalFullScreen, - ]); - const getDefaultDataViewSelector = useMemo( () => sourcererSelectors.defaultDataViewSelector(), [] @@ -219,21 +134,32 @@ const GraphOverlayComponent: React.FC = ({ timelineId }) => { [defaultDataView.patternList, isInTimeline, timelinePatterns] ); - if (fullScreen && !isInTimeline) { + if (!isInTimeline && sessionViewId !== null) { + if (fullScreen) { + return ( + + + {Navigation} + {SessionView} + + + ); + } else { + return ( + + + {Navigation} + {SessionView} + + + ); + } + } else if (fullScreen && !isInTimeline) { return ( - - - + {Navigation} {graphEventId !== undefined ? ( @@ -256,16 +182,7 @@ const GraphOverlayComponent: React.FC = ({ timelineId }) => { - - - + {Navigation} {graphEventId !== undefined ? ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap index 01089552be251..6ea24e5ca57f6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap @@ -380,6 +380,313 @@ Array [ runtimeMappings={Object {}} tabType="query" timelineId="test" + > + + +
+ + + +
+ +
+ +
+ + + +
+ + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+ + + +
+ +
+ +
+ +
+ +
+ + + + + +
+ , + .c0 { + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; + margin-top: 8px; +} + +.c1 .euiFlyoutBody__overflow { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + overflow: hidden; +} + +.c1 .euiFlyoutBody__overflow .euiFlyoutBody__overflowContent { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + overflow: hidden; + padding: 0 16px 16px; +} + +
+
- , - .c0 { - -webkit-flex: 0 1 auto; - -ms-flex: 0 1 auto; - flex: 0 1 auto; - margin-top: 8px; -} - -.c1 .euiFlyoutBody__overflow { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex: 1; - -ms-flex: 1; - flex: 1; - overflow: hidden; -} - -.c1 .euiFlyoutBody__overflow .euiFlyoutBody__overflowContent { - -webkit-flex: 1; - -ms-flex: 1; - flex: 1; - overflow: hidden; - padding: 0 16px 16px; -} - -
-