[Security Solution] Fix app layout (#76668)
This commit is contained in:
parent
341c1ace0d
commit
012fa42ee1
13 changed files with 134 additions and 135 deletions
|
@ -474,6 +474,10 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
|
|||
return parseInt(scrollSize, 10);
|
||||
}
|
||||
|
||||
public async scrollTop() {
|
||||
await driver.executeScript('document.documentElement.scrollTop = 0');
|
||||
}
|
||||
|
||||
// return promise with REAL scroll position
|
||||
public async setScrollTop(scrollSize: number | string) {
|
||||
await driver.executeScript('document.body.scrollTop = ' + scrollSize);
|
||||
|
|
|
@ -34,6 +34,7 @@ export const DEFAULT_INTERVAL_TYPE = 'manual';
|
|||
export const DEFAULT_INTERVAL_VALUE = 300000; // ms
|
||||
export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges';
|
||||
export const SCROLLING_DISABLED_CLASS_NAME = 'scrolling-disabled';
|
||||
export const GLOBAL_HEADER_HEIGHT = 98; // px
|
||||
export const FILTERS_GLOBAL_HEIGHT = 109; // px
|
||||
export const FULL_SCREEN_TOGGLED_CLASS_NAME = 'fullScreenToggled';
|
||||
export const NO_ALERT_INDEX = 'no-alert-index-049FC71A-4C2C-446F-9901-37XMC5024C51';
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
FIELDS_BROWSER_SELECTED_CATEGORY_TITLE,
|
||||
} from '../screens/fields_browser';
|
||||
import {
|
||||
EVENTS_PAGE,
|
||||
HEADER_SUBTITLE,
|
||||
HOST_GEO_CITY_NAME_HEADER,
|
||||
HOST_GEO_COUNTRY_NAME_HEADER,
|
||||
|
@ -173,7 +172,7 @@ describe.skip('Events Viewer', () => {
|
|||
const expectedOrderAfterDragAndDrop =
|
||||
'message@timestamphost.nameevent.moduleevent.datasetevent.actionuser.namesource.ipdestination.ip';
|
||||
|
||||
cy.get(EVENTS_PAGE).scrollTo('bottom');
|
||||
cy.scrollTo('bottom');
|
||||
cy.get(HEADERS_GROUP).invoke('text').should('equal', originalColumnOrder);
|
||||
dragAndDropColumn({ column: 0, newPosition: 1 });
|
||||
cy.get(HEADERS_GROUP).invoke('text').should('equal', expectedOrderAfterDragAndDrop);
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
export const CLOSE_MODAL = '[data-test-subj="modal-inspect-close"]';
|
||||
|
||||
export const EVENTS_PAGE = '[data-test-subj="pageContainer"]';
|
||||
|
||||
export const EVENTS_VIEWER_FIELDS_BUTTON =
|
||||
'[data-test-subj="events-viewer-panel"] [data-test-subj="show-field-browser"]';
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import styled from 'styled-components';
|
|||
import { TimelineId } from '../../../common/types/timeline';
|
||||
import { DragDropContextWrapper } from '../../common/components/drag_and_drop/drag_drop_context_wrapper';
|
||||
import { Flyout } from '../../timelines/components/flyout';
|
||||
import { SecuritySolutionAppWrapper } from '../../common/components/page';
|
||||
import { HeaderGlobal } from '../../common/components/header_global';
|
||||
import { HelpMenu } from '../../common/components/help_menu';
|
||||
import { AutoSaveWarningMsg } from '../../timelines/components/timeline/auto_save_warning';
|
||||
|
@ -20,18 +21,17 @@ import { useInitSourcerer, useSourcererScope } from '../../common/containers/sou
|
|||
import { useKibana } from '../../common/lib/kibana';
|
||||
import { DETECTIONS_SUB_PLUGIN_ID } from '../../../common/constants';
|
||||
import { SourcererScopeName } from '../../common/store/sourcerer/model';
|
||||
import { useThrottledResizeObserver } from '../../common/components/utils';
|
||||
|
||||
const SecuritySolutionAppWrapper = styled.div`
|
||||
const Main = styled.main.attrs<{ paddingTop: number }>(({ paddingTop }) => ({
|
||||
style: {
|
||||
paddingTop: `${paddingTop}px`,
|
||||
},
|
||||
}))<{ paddingTop: number }>`
|
||||
overflow: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
SecuritySolutionAppWrapper.displayName = 'SecuritySolutionAppWrapper';
|
||||
|
||||
const Main = styled.main`
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
flex: 1 1 auto;
|
||||
`;
|
||||
|
||||
Main.displayName = 'Main';
|
||||
|
@ -45,7 +45,7 @@ interface HomePageProps {
|
|||
const HomePageComponent: React.FC<HomePageProps> = ({ children }) => {
|
||||
const { application } = useKibana().services;
|
||||
const subPluginId = useRef<string>('');
|
||||
|
||||
const { ref, height = 0 } = useThrottledResizeObserver(300);
|
||||
application.currentAppId$.subscribe((appId) => {
|
||||
subPluginId.current = appId ?? '';
|
||||
});
|
||||
|
@ -61,9 +61,9 @@ const HomePageComponent: React.FC<HomePageProps> = ({ children }) => {
|
|||
|
||||
return (
|
||||
<SecuritySolutionAppWrapper>
|
||||
<HeaderGlobal />
|
||||
<HeaderGlobal ref={ref} />
|
||||
|
||||
<Main data-test-subj="pageContainer">
|
||||
<Main paddingTop={height} data-test-subj="pageContainer">
|
||||
<DragDropContextWrapper browserFields={browserFields}>
|
||||
<UseUrlState indexPattern={indexPattern} navTabs={navTabs} />
|
||||
{indicesExist && showTimeline && (
|
||||
|
|
|
@ -402,7 +402,7 @@ export const CaseView = React.memo(({ caseId, userCanCrud }: Props) => {
|
|||
}
|
||||
if (isLoading) {
|
||||
return (
|
||||
<MyEuiFlexGroup justifyContent="center" alignItems="center">
|
||||
<MyEuiFlexGroup gutterSize="none" justifyContent="center" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner data-test-subj="case-view-loading" size="xl" />
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -10,8 +10,7 @@ import { gutterTimeline } from '../../../common/lib/helpers';
|
|||
export const WhitePageWrapper = styled.div`
|
||||
background-color: ${({ theme }) => theme.eui.euiColorEmptyShade};
|
||||
border-top: ${({ theme }) => theme.eui.euiBorderThin};
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
flex: 1 1 auto;
|
||||
`;
|
||||
|
||||
export const SectionWrapper = styled.div`
|
||||
|
|
|
@ -29,6 +29,7 @@ const DEFAULT_EVENTS_VIEWER_HEIGHT = 652;
|
|||
|
||||
const FullScreenContainer = styled.div<{ $isFullScreen: boolean }>`
|
||||
height: ${({ $isFullScreen }) => ($isFullScreen ? '100%' : `${DEFAULT_EVENTS_VIEWER_HEIGHT}px`)};
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
`;
|
||||
|
|
|
@ -6,11 +6,16 @@
|
|||
|
||||
import { EuiButton, EuiWindowEvent } from '@elastic/eui';
|
||||
import React, { useCallback } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { useFullScreen } from '../../../common/containers/use_full_screen';
|
||||
|
||||
import * as i18n from './translations';
|
||||
|
||||
const StyledEuiButton = styled(EuiButton)`
|
||||
margin: ${({ theme }) => theme.eui.paddingSizes.s};
|
||||
`;
|
||||
|
||||
export const ExitFullScreen: React.FC = () => {
|
||||
const { globalFullScreen, setGlobalFullScreen } = useFullScreen();
|
||||
|
||||
|
@ -36,14 +41,14 @@ export const ExitFullScreen: React.FC = () => {
|
|||
return (
|
||||
<>
|
||||
<EuiWindowEvent event="keydown" handler={onKeyDown} />
|
||||
<EuiButton
|
||||
<StyledEuiButton
|
||||
data-test-subj="exit-full-screen"
|
||||
iconType="fullScreen"
|
||||
isDisabled={!globalFullScreen}
|
||||
onClick={exitFullScreen}
|
||||
>
|
||||
{i18n.EXIT_FULL_SCREEN}
|
||||
</EuiButton>
|
||||
</StyledEuiButton>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
|
||||
import { pickBy } from 'lodash/fp';
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { forwardRef, useCallback } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { OutPortal } from 'react-reverse-portal';
|
||||
|
||||
|
@ -24,30 +24,37 @@ import { APP_ID, ADD_DATA_PATH, APP_DETECTIONS_PATH } from '../../../../common/c
|
|||
import { useGlobalHeaderPortal } from '../../hooks/use_global_header_portal';
|
||||
import { LinkAnchor } from '../links';
|
||||
|
||||
const Wrapper = styled.header<{ $globalFullScreen: boolean }>`
|
||||
${({ $globalFullScreen, theme }) => `
|
||||
const Wrapper = styled.header`
|
||||
${({ theme }) => `
|
||||
background: ${theme.eui.euiColorEmptyShade};
|
||||
border-bottom: ${theme.eui.euiBorderThin};
|
||||
padding-top: ${$globalFullScreen ? theme.eui.paddingSizes.s : theme.eui.paddingSizes.m};
|
||||
width: 100%;
|
||||
z-index: ${theme.eui.euiZNavigation};
|
||||
position: fixed;
|
||||
`}
|
||||
`;
|
||||
Wrapper.displayName = 'Wrapper';
|
||||
|
||||
const WrapperContent = styled.div<{ $globalFullScreen: boolean }>`
|
||||
display: ${({ $globalFullScreen }) => ($globalFullScreen ? 'none' : 'block')};
|
||||
padding-top: ${({ $globalFullScreen, theme }) =>
|
||||
$globalFullScreen ? theme.eui.paddingSizes.s : theme.eui.paddingSizes.m};
|
||||
`;
|
||||
|
||||
WrapperContent.displayName = 'WrapperContent';
|
||||
|
||||
const FlexItem = styled(EuiFlexItem)`
|
||||
min-width: 0;
|
||||
`;
|
||||
FlexItem.displayName = 'FlexItem';
|
||||
|
||||
const FlexGroup = styled(EuiFlexGroup)<{ $globalFullScreen: boolean; $hasSibling: boolean }>`
|
||||
${({ $globalFullScreen, $hasSibling, theme }) => `
|
||||
const FlexGroup = styled(EuiFlexGroup)<{ $hasSibling: boolean }>`
|
||||
${({ $hasSibling, theme }) => `
|
||||
border-bottom: ${theme.eui.euiBorderThin};
|
||||
margin-bottom: 1px;
|
||||
padding-bottom: 4px;
|
||||
padding-left: ${theme.eui.paddingSizes.l};
|
||||
padding-right: ${gutterTimeline};
|
||||
${$globalFullScreen ? 'display: none;' : ''}
|
||||
${$hasSibling ? `border-bottom: ${theme.eui.euiBorderThin};` : 'border-bottom-width: 0px;'}
|
||||
`}
|
||||
`;
|
||||
|
@ -56,77 +63,74 @@ FlexGroup.displayName = 'FlexGroup';
|
|||
interface HeaderGlobalProps {
|
||||
hideDetectionEngine?: boolean;
|
||||
}
|
||||
export const HeaderGlobal = React.memo<HeaderGlobalProps>(({ hideDetectionEngine = false }) => {
|
||||
const { globalHeaderPortalNode } = useGlobalHeaderPortal();
|
||||
const { globalFullScreen } = useFullScreen();
|
||||
const search = useGetUrlSearch(navTabs.overview);
|
||||
const { application, http } = useKibana().services;
|
||||
const { navigateToApp } = application;
|
||||
const basePath = http.basePath.get();
|
||||
const goToOverview = useCallback(
|
||||
(ev) => {
|
||||
ev.preventDefault();
|
||||
navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { path: search });
|
||||
},
|
||||
[navigateToApp, search]
|
||||
);
|
||||
|
||||
return (
|
||||
<Wrapper className="siemHeaderGlobal" $globalFullScreen={globalFullScreen}>
|
||||
<FlexGroup
|
||||
alignItems="center"
|
||||
$globalFullScreen={globalFullScreen}
|
||||
$hasSibling={globalHeaderPortalNode.hasChildNodes()}
|
||||
justifyContent="spaceBetween"
|
||||
wrap
|
||||
>
|
||||
<>
|
||||
<FlexItem>
|
||||
<EuiFlexGroup alignItems="center" responsive={false}>
|
||||
<FlexItem grow={false}>
|
||||
<LinkAnchor onClick={goToOverview} href={getAppOverviewUrl(search)}>
|
||||
<EuiIcon aria-label={i18n.SECURITY_SOLUTION} type="logoSecurity" size="l" />
|
||||
</LinkAnchor>
|
||||
</FlexItem>
|
||||
|
||||
<FlexItem component="nav">
|
||||
<SiemNavigation
|
||||
display="condensed"
|
||||
navTabs={
|
||||
hideDetectionEngine
|
||||
? pickBy((_, key) => key !== SecurityPageName.detections, navTabs)
|
||||
: navTabs
|
||||
}
|
||||
/>
|
||||
</FlexItem>
|
||||
</EuiFlexGroup>
|
||||
</FlexItem>
|
||||
|
||||
<FlexItem grow={false}>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap>
|
||||
{window.location.pathname.includes(APP_DETECTIONS_PATH) && (
|
||||
export const HeaderGlobal = React.memo(
|
||||
forwardRef<HTMLDivElement, HeaderGlobalProps>(({ hideDetectionEngine = false }, ref) => {
|
||||
const { globalHeaderPortalNode } = useGlobalHeaderPortal();
|
||||
const { globalFullScreen } = useFullScreen();
|
||||
const search = useGetUrlSearch(navTabs.overview);
|
||||
const { application, http } = useKibana().services;
|
||||
const { navigateToApp } = application;
|
||||
const basePath = http.basePath.get();
|
||||
const goToOverview = useCallback(
|
||||
(ev) => {
|
||||
ev.preventDefault();
|
||||
navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { path: search });
|
||||
},
|
||||
[navigateToApp, search]
|
||||
);
|
||||
return (
|
||||
<Wrapper ref={ref} className="siemHeaderGlobal">
|
||||
<WrapperContent $globalFullScreen={globalFullScreen}>
|
||||
<FlexGroup
|
||||
alignItems="center"
|
||||
$hasSibling={globalHeaderPortalNode.hasChildNodes()}
|
||||
justifyContent="spaceBetween"
|
||||
wrap
|
||||
>
|
||||
<FlexItem>
|
||||
<EuiFlexGroup alignItems="center" responsive={false}>
|
||||
<FlexItem grow={false}>
|
||||
<MlPopover />
|
||||
<LinkAnchor onClick={goToOverview} href={getAppOverviewUrl(search)}>
|
||||
<EuiIcon aria-label={i18n.SECURITY_SOLUTION} type="logoSecurity" size="l" />
|
||||
</LinkAnchor>
|
||||
</FlexItem>
|
||||
)}
|
||||
|
||||
<FlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="add-data"
|
||||
href={`${basePath}${ADD_DATA_PATH}`}
|
||||
iconType="plusInCircle"
|
||||
>
|
||||
{i18n.BUTTON_ADD_DATA}
|
||||
</EuiButtonEmpty>
|
||||
</FlexItem>
|
||||
</EuiFlexGroup>
|
||||
</FlexItem>
|
||||
</>
|
||||
</FlexGroup>
|
||||
<div>
|
||||
<OutPortal node={globalHeaderPortalNode} />
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
});
|
||||
<FlexItem component="nav">
|
||||
<SiemNavigation
|
||||
display="condensed"
|
||||
navTabs={
|
||||
hideDetectionEngine
|
||||
? pickBy((_, key) => key !== SecurityPageName.detections, navTabs)
|
||||
: navTabs
|
||||
}
|
||||
/>
|
||||
</FlexItem>
|
||||
</EuiFlexGroup>
|
||||
</FlexItem>
|
||||
<FlexItem grow={false}>
|
||||
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap>
|
||||
{window.location.pathname.includes(APP_DETECTIONS_PATH) && (
|
||||
<FlexItem grow={false}>
|
||||
<MlPopover />
|
||||
</FlexItem>
|
||||
)}
|
||||
|
||||
<FlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="add-data"
|
||||
href={`${basePath}${ADD_DATA_PATH}`}
|
||||
iconType="plusInCircle"
|
||||
>
|
||||
{i18n.BUTTON_ADD_DATA}
|
||||
</EuiButtonEmpty>
|
||||
</FlexItem>
|
||||
</EuiFlexGroup>
|
||||
</FlexItem>
|
||||
</FlexGroup>
|
||||
<OutPortal node={globalHeaderPortalNode} />
|
||||
</WrapperContent>
|
||||
</Wrapper>
|
||||
);
|
||||
})
|
||||
);
|
||||
HeaderGlobal.displayName = 'HeaderGlobal';
|
||||
|
|
|
@ -8,27 +8,36 @@ import { EuiBadge, EuiDescriptionList, EuiFlexGroup, EuiIcon, EuiPage } from '@e
|
|||
import styled, { createGlobalStyle } from 'styled-components';
|
||||
|
||||
import {
|
||||
GLOBAL_HEADER_HEIGHT,
|
||||
FULL_SCREEN_TOGGLED_CLASS_NAME,
|
||||
SCROLLING_DISABLED_CLASS_NAME,
|
||||
} from '../../../../common/constants';
|
||||
|
||||
export const SecuritySolutionAppWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
`;
|
||||
SecuritySolutionAppWrapper.displayName = 'SecuritySolutionAppWrapper';
|
||||
|
||||
/*
|
||||
SIDE EFFECT: the following `createGlobalStyle` overrides default styling in angular code that was not theme-friendly
|
||||
and `EuiPopover`, `EuiToolTip` global styles
|
||||
*/
|
||||
export const AppGlobalStyle = createGlobalStyle<{ theme: { eui: { euiColorPrimary: string } } }>`
|
||||
/* dirty hack to fix draggables with tooltip on FF */
|
||||
body#siem-app {
|
||||
position: static;
|
||||
}
|
||||
/* end of dirty hack to fix draggables with tooltip on FF */
|
||||
|
||||
div.app-wrapper {
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
div.application {
|
||||
background-color: rgba(0,0,0,0);
|
||||
|
||||
// Security App wrapper
|
||||
> div {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.euiPopover__panel.euiPopover__panel-isOpen {
|
||||
|
@ -67,37 +76,8 @@ export const AppGlobalStyle = createGlobalStyle<{ theme: { eui: { euiColorPrimar
|
|||
${({ theme }) => `background-color: ${theme.eui.euiColorPrimary} !important`};
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#kibana-body {
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
|
||||
> .content {
|
||||
height: 100%;
|
||||
|
||||
> .app-wrapper {
|
||||
height: 100%;
|
||||
|
||||
> .app-wrapper-panel {
|
||||
height: 100%;
|
||||
|
||||
> .application {
|
||||
height: 100%;
|
||||
|
||||
> div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.${SCROLLING_DISABLED_CLASS_NAME} #kibana-body {
|
||||
overflow-y: hidden;
|
||||
.${SCROLLING_DISABLED_CLASS_NAME} ${SecuritySolutionAppWrapper} {
|
||||
max-height: calc(100vh - ${GLOBAL_HEADER_HEIGHT}px);
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@ const Wrapper = styled.div`
|
|||
|
||||
&.siemWrapperPage--fullHeight {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&.siemWrapperPage--withTimeline {
|
||||
|
@ -36,6 +39,9 @@ const Wrapper = styled.div`
|
|||
|
||||
&.siemWrapperPage--noPadding {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { FtrProviderContext } from '../ftr_provider_context';
|
|||
export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const pageObjects = getPageObjects(['common', 'header']);
|
||||
const testSubjects = getService('testSubjects');
|
||||
const browser = getService('browser');
|
||||
|
||||
return {
|
||||
/**
|
||||
|
@ -88,6 +89,7 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr
|
|||
*/
|
||||
async confirmAndSave() {
|
||||
await this.ensureIsOnDetailsPage();
|
||||
await browser.scrollTop();
|
||||
await (await this.findSaveButton()).click();
|
||||
await testSubjects.existOrFail('policyDetailsConfirmModal');
|
||||
await pageObjects.common.clickConfirmOnModal();
|
||||
|
|
Loading…
Reference in a new issue