[Security Solution] Fix app layout (#76668)

This commit is contained in:
Patryk Kopyciński 2020-09-25 14:15:41 +02:00 committed by GitHub
parent 341c1ace0d
commit 012fa42ee1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 134 additions and 135 deletions

View file

@ -474,6 +474,10 @@ export async function BrowserProvider({ getService }: FtrProviderContext) {
return parseInt(scrollSize, 10); return parseInt(scrollSize, 10);
} }
public async scrollTop() {
await driver.executeScript('document.documentElement.scrollTop = 0');
}
// return promise with REAL scroll position // return promise with REAL scroll position
public async setScrollTop(scrollSize: number | string) { public async setScrollTop(scrollSize: number | string) {
await driver.executeScript('document.body.scrollTop = ' + scrollSize); await driver.executeScript('document.body.scrollTop = ' + scrollSize);

View file

@ -34,6 +34,7 @@ export const DEFAULT_INTERVAL_TYPE = 'manual';
export const DEFAULT_INTERVAL_VALUE = 300000; // ms export const DEFAULT_INTERVAL_VALUE = 300000; // ms
export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges'; export const DEFAULT_TIMEPICKER_QUICK_RANGES = 'timepicker:quickRanges';
export const SCROLLING_DISABLED_CLASS_NAME = 'scrolling-disabled'; export const SCROLLING_DISABLED_CLASS_NAME = 'scrolling-disabled';
export const GLOBAL_HEADER_HEIGHT = 98; // px
export const FILTERS_GLOBAL_HEIGHT = 109; // px export const FILTERS_GLOBAL_HEIGHT = 109; // px
export const FULL_SCREEN_TOGGLED_CLASS_NAME = 'fullScreenToggled'; export const FULL_SCREEN_TOGGLED_CLASS_NAME = 'fullScreenToggled';
export const NO_ALERT_INDEX = 'no-alert-index-049FC71A-4C2C-446F-9901-37XMC5024C51'; export const NO_ALERT_INDEX = 'no-alert-index-049FC71A-4C2C-446F-9901-37XMC5024C51';

View file

@ -10,7 +10,6 @@ import {
FIELDS_BROWSER_SELECTED_CATEGORY_TITLE, FIELDS_BROWSER_SELECTED_CATEGORY_TITLE,
} from '../screens/fields_browser'; } from '../screens/fields_browser';
import { import {
EVENTS_PAGE,
HEADER_SUBTITLE, HEADER_SUBTITLE,
HOST_GEO_CITY_NAME_HEADER, HOST_GEO_CITY_NAME_HEADER,
HOST_GEO_COUNTRY_NAME_HEADER, HOST_GEO_COUNTRY_NAME_HEADER,
@ -173,7 +172,7 @@ describe.skip('Events Viewer', () => {
const expectedOrderAfterDragAndDrop = const expectedOrderAfterDragAndDrop =
'message@timestamphost.nameevent.moduleevent.datasetevent.actionuser.namesource.ipdestination.ip'; '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); cy.get(HEADERS_GROUP).invoke('text').should('equal', originalColumnOrder);
dragAndDropColumn({ column: 0, newPosition: 1 }); dragAndDropColumn({ column: 0, newPosition: 1 });
cy.get(HEADERS_GROUP).invoke('text').should('equal', expectedOrderAfterDragAndDrop); cy.get(HEADERS_GROUP).invoke('text').should('equal', expectedOrderAfterDragAndDrop);

View file

@ -6,8 +6,6 @@
export const CLOSE_MODAL = '[data-test-subj="modal-inspect-close"]'; export const CLOSE_MODAL = '[data-test-subj="modal-inspect-close"]';
export const EVENTS_PAGE = '[data-test-subj="pageContainer"]';
export const EVENTS_VIEWER_FIELDS_BUTTON = export const EVENTS_VIEWER_FIELDS_BUTTON =
'[data-test-subj="events-viewer-panel"] [data-test-subj="show-field-browser"]'; '[data-test-subj="events-viewer-panel"] [data-test-subj="show-field-browser"]';

View file

@ -10,6 +10,7 @@ import styled from 'styled-components';
import { TimelineId } from '../../../common/types/timeline'; import { TimelineId } from '../../../common/types/timeline';
import { DragDropContextWrapper } from '../../common/components/drag_and_drop/drag_drop_context_wrapper'; import { DragDropContextWrapper } from '../../common/components/drag_and_drop/drag_drop_context_wrapper';
import { Flyout } from '../../timelines/components/flyout'; import { Flyout } from '../../timelines/components/flyout';
import { SecuritySolutionAppWrapper } from '../../common/components/page';
import { HeaderGlobal } from '../../common/components/header_global'; import { HeaderGlobal } from '../../common/components/header_global';
import { HelpMenu } from '../../common/components/help_menu'; import { HelpMenu } from '../../common/components/help_menu';
import { AutoSaveWarningMsg } from '../../timelines/components/timeline/auto_save_warning'; 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 { useKibana } from '../../common/lib/kibana';
import { DETECTIONS_SUB_PLUGIN_ID } from '../../../common/constants'; import { DETECTIONS_SUB_PLUGIN_ID } from '../../../common/constants';
import { SourcererScopeName } from '../../common/store/sourcerer/model'; 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; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; flex: 1 1 auto;
width: 100%;
`;
SecuritySolutionAppWrapper.displayName = 'SecuritySolutionAppWrapper';
const Main = styled.main`
overflow: auto;
flex: 1;
`; `;
Main.displayName = 'Main'; Main.displayName = 'Main';
@ -45,7 +45,7 @@ interface HomePageProps {
const HomePageComponent: React.FC<HomePageProps> = ({ children }) => { const HomePageComponent: React.FC<HomePageProps> = ({ children }) => {
const { application } = useKibana().services; const { application } = useKibana().services;
const subPluginId = useRef<string>(''); const subPluginId = useRef<string>('');
const { ref, height = 0 } = useThrottledResizeObserver(300);
application.currentAppId$.subscribe((appId) => { application.currentAppId$.subscribe((appId) => {
subPluginId.current = appId ?? ''; subPluginId.current = appId ?? '';
}); });
@ -61,9 +61,9 @@ const HomePageComponent: React.FC<HomePageProps> = ({ children }) => {
return ( return (
<SecuritySolutionAppWrapper> <SecuritySolutionAppWrapper>
<HeaderGlobal /> <HeaderGlobal ref={ref} />
<Main data-test-subj="pageContainer"> <Main paddingTop={height} data-test-subj="pageContainer">
<DragDropContextWrapper browserFields={browserFields}> <DragDropContextWrapper browserFields={browserFields}>
<UseUrlState indexPattern={indexPattern} navTabs={navTabs} /> <UseUrlState indexPattern={indexPattern} navTabs={navTabs} />
{indicesExist && showTimeline && ( {indicesExist && showTimeline && (

View file

@ -402,7 +402,7 @@ export const CaseView = React.memo(({ caseId, userCanCrud }: Props) => {
} }
if (isLoading) { if (isLoading) {
return ( return (
<MyEuiFlexGroup justifyContent="center" alignItems="center"> <MyEuiFlexGroup gutterSize="none" justifyContent="center" alignItems="center">
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>
<EuiLoadingSpinner data-test-subj="case-view-loading" size="xl" /> <EuiLoadingSpinner data-test-subj="case-view-loading" size="xl" />
</EuiFlexItem> </EuiFlexItem>

View file

@ -10,8 +10,7 @@ import { gutterTimeline } from '../../../common/lib/helpers';
export const WhitePageWrapper = styled.div` export const WhitePageWrapper = styled.div`
background-color: ${({ theme }) => theme.eui.euiColorEmptyShade}; background-color: ${({ theme }) => theme.eui.euiColorEmptyShade};
border-top: ${({ theme }) => theme.eui.euiBorderThin}; border-top: ${({ theme }) => theme.eui.euiBorderThin};
height: 100%; flex: 1 1 auto;
min-height: 100vh;
`; `;
export const SectionWrapper = styled.div` export const SectionWrapper = styled.div`

View file

@ -29,6 +29,7 @@ const DEFAULT_EVENTS_VIEWER_HEIGHT = 652;
const FullScreenContainer = styled.div<{ $isFullScreen: boolean }>` const FullScreenContainer = styled.div<{ $isFullScreen: boolean }>`
height: ${({ $isFullScreen }) => ($isFullScreen ? '100%' : `${DEFAULT_EVENTS_VIEWER_HEIGHT}px`)}; height: ${({ $isFullScreen }) => ($isFullScreen ? '100%' : `${DEFAULT_EVENTS_VIEWER_HEIGHT}px`)};
flex: 1 1 auto;
display: flex; display: flex;
width: 100%; width: 100%;
`; `;

View file

@ -6,11 +6,16 @@
import { EuiButton, EuiWindowEvent } from '@elastic/eui'; import { EuiButton, EuiWindowEvent } from '@elastic/eui';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import styled from 'styled-components';
import { useFullScreen } from '../../../common/containers/use_full_screen'; import { useFullScreen } from '../../../common/containers/use_full_screen';
import * as i18n from './translations'; import * as i18n from './translations';
const StyledEuiButton = styled(EuiButton)`
margin: ${({ theme }) => theme.eui.paddingSizes.s};
`;
export const ExitFullScreen: React.FC = () => { export const ExitFullScreen: React.FC = () => {
const { globalFullScreen, setGlobalFullScreen } = useFullScreen(); const { globalFullScreen, setGlobalFullScreen } = useFullScreen();
@ -36,14 +41,14 @@ export const ExitFullScreen: React.FC = () => {
return ( return (
<> <>
<EuiWindowEvent event="keydown" handler={onKeyDown} /> <EuiWindowEvent event="keydown" handler={onKeyDown} />
<EuiButton <StyledEuiButton
data-test-subj="exit-full-screen" data-test-subj="exit-full-screen"
iconType="fullScreen" iconType="fullScreen"
isDisabled={!globalFullScreen} isDisabled={!globalFullScreen}
onClick={exitFullScreen} onClick={exitFullScreen}
> >
{i18n.EXIT_FULL_SCREEN} {i18n.EXIT_FULL_SCREEN}
</EuiButton> </StyledEuiButton>
</> </>
); );
}; };

View file

@ -6,7 +6,7 @@
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui'; import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
import { pickBy } from 'lodash/fp'; import { pickBy } from 'lodash/fp';
import React, { useCallback } from 'react'; import React, { forwardRef, useCallback } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { OutPortal } from 'react-reverse-portal'; 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 { useGlobalHeaderPortal } from '../../hooks/use_global_header_portal';
import { LinkAnchor } from '../links'; import { LinkAnchor } from '../links';
const Wrapper = styled.header<{ $globalFullScreen: boolean }>` const Wrapper = styled.header`
${({ $globalFullScreen, theme }) => ` ${({ theme }) => `
background: ${theme.eui.euiColorEmptyShade}; background: ${theme.eui.euiColorEmptyShade};
border-bottom: ${theme.eui.euiBorderThin}; border-bottom: ${theme.eui.euiBorderThin};
padding-top: ${$globalFullScreen ? theme.eui.paddingSizes.s : theme.eui.paddingSizes.m};
width: 100%; width: 100%;
z-index: ${theme.eui.euiZNavigation}; z-index: ${theme.eui.euiZNavigation};
position: fixed;
`} `}
`; `;
Wrapper.displayName = 'Wrapper'; 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)` const FlexItem = styled(EuiFlexItem)`
min-width: 0; min-width: 0;
`; `;
FlexItem.displayName = 'FlexItem'; FlexItem.displayName = 'FlexItem';
const FlexGroup = styled(EuiFlexGroup)<{ $globalFullScreen: boolean; $hasSibling: boolean }>` const FlexGroup = styled(EuiFlexGroup)<{ $hasSibling: boolean }>`
${({ $globalFullScreen, $hasSibling, theme }) => ` ${({ $hasSibling, theme }) => `
border-bottom: ${theme.eui.euiBorderThin}; border-bottom: ${theme.eui.euiBorderThin};
margin-bottom: 1px; margin-bottom: 1px;
padding-bottom: 4px; padding-bottom: 4px;
padding-left: ${theme.eui.paddingSizes.l}; padding-left: ${theme.eui.paddingSizes.l};
padding-right: ${gutterTimeline}; padding-right: ${gutterTimeline};
${$globalFullScreen ? 'display: none;' : ''}
${$hasSibling ? `border-bottom: ${theme.eui.euiBorderThin};` : 'border-bottom-width: 0px;'} ${$hasSibling ? `border-bottom: ${theme.eui.euiBorderThin};` : 'border-bottom-width: 0px;'}
`} `}
`; `;
@ -56,77 +63,74 @@ FlexGroup.displayName = 'FlexGroup';
interface HeaderGlobalProps { interface HeaderGlobalProps {
hideDetectionEngine?: boolean; hideDetectionEngine?: boolean;
} }
export const HeaderGlobal = React.memo<HeaderGlobalProps>(({ hideDetectionEngine = false }) => { export const HeaderGlobal = React.memo(
const { globalHeaderPortalNode } = useGlobalHeaderPortal(); forwardRef<HTMLDivElement, HeaderGlobalProps>(({ hideDetectionEngine = false }, ref) => {
const { globalFullScreen } = useFullScreen(); const { globalHeaderPortalNode } = useGlobalHeaderPortal();
const search = useGetUrlSearch(navTabs.overview); const { globalFullScreen } = useFullScreen();
const { application, http } = useKibana().services; const search = useGetUrlSearch(navTabs.overview);
const { navigateToApp } = application; const { application, http } = useKibana().services;
const basePath = http.basePath.get(); const { navigateToApp } = application;
const goToOverview = useCallback( const basePath = http.basePath.get();
(ev) => { const goToOverview = useCallback(
ev.preventDefault(); (ev) => {
navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { path: search }); ev.preventDefault();
}, navigateToApp(`${APP_ID}:${SecurityPageName.overview}`, { path: search });
[navigateToApp, search] },
); [navigateToApp, search]
);
return ( return (
<Wrapper className="siemHeaderGlobal" $globalFullScreen={globalFullScreen}> <Wrapper ref={ref} className="siemHeaderGlobal">
<FlexGroup <WrapperContent $globalFullScreen={globalFullScreen}>
alignItems="center" <FlexGroup
$globalFullScreen={globalFullScreen} alignItems="center"
$hasSibling={globalHeaderPortalNode.hasChildNodes()} $hasSibling={globalHeaderPortalNode.hasChildNodes()}
justifyContent="spaceBetween" justifyContent="spaceBetween"
wrap wrap
> >
<> <FlexItem>
<FlexItem> <EuiFlexGroup alignItems="center" responsive={false}>
<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) && (
<FlexItem grow={false}> <FlexItem grow={false}>
<MlPopover /> <LinkAnchor onClick={goToOverview} href={getAppOverviewUrl(search)}>
<EuiIcon aria-label={i18n.SECURITY_SOLUTION} type="logoSecurity" size="l" />
</LinkAnchor>
</FlexItem> </FlexItem>
)}
<FlexItem grow={false}> <FlexItem component="nav">
<EuiButtonEmpty <SiemNavigation
data-test-subj="add-data" display="condensed"
href={`${basePath}${ADD_DATA_PATH}`} navTabs={
iconType="plusInCircle" hideDetectionEngine
> ? pickBy((_, key) => key !== SecurityPageName.detections, navTabs)
{i18n.BUTTON_ADD_DATA} : navTabs
</EuiButtonEmpty> }
</FlexItem> />
</EuiFlexGroup> </FlexItem>
</FlexItem> </EuiFlexGroup>
</> </FlexItem>
</FlexGroup> <FlexItem grow={false}>
<div> <EuiFlexGroup alignItems="center" gutterSize="s" responsive={false} wrap>
<OutPortal node={globalHeaderPortalNode} /> {window.location.pathname.includes(APP_DETECTIONS_PATH) && (
</div> <FlexItem grow={false}>
</Wrapper> <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'; HeaderGlobal.displayName = 'HeaderGlobal';

View file

@ -8,27 +8,36 @@ import { EuiBadge, EuiDescriptionList, EuiFlexGroup, EuiIcon, EuiPage } from '@e
import styled, { createGlobalStyle } from 'styled-components'; import styled, { createGlobalStyle } from 'styled-components';
import { import {
GLOBAL_HEADER_HEIGHT,
FULL_SCREEN_TOGGLED_CLASS_NAME, FULL_SCREEN_TOGGLED_CLASS_NAME,
SCROLLING_DISABLED_CLASS_NAME, SCROLLING_DISABLED_CLASS_NAME,
} from '../../../../common/constants'; } 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 SIDE EFFECT: the following `createGlobalStyle` overrides default styling in angular code that was not theme-friendly
and `EuiPopover`, `EuiToolTip` global styles and `EuiPopover`, `EuiToolTip` global styles
*/ */
export const AppGlobalStyle = createGlobalStyle<{ theme: { eui: { euiColorPrimary: string } } }>` 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 { div.app-wrapper {
background-color: rgba(0,0,0,0); background-color: rgba(0,0,0,0);
} }
div.application { div.application {
background-color: rgba(0,0,0,0); background-color: rgba(0,0,0,0);
// Security App wrapper
> div {
display: flex;
flex: 1 1 auto;
}
} }
.euiPopover__panel.euiPopover__panel-isOpen { .euiPopover__panel.euiPopover__panel-isOpen {
@ -67,37 +76,8 @@ export const AppGlobalStyle = createGlobalStyle<{ theme: { eui: { euiColorPrimar
${({ theme }) => `background-color: ${theme.eui.euiColorPrimary} !important`}; ${({ theme }) => `background-color: ${theme.eui.euiColorPrimary} !important`};
} }
body { .${SCROLLING_DISABLED_CLASS_NAME} ${SecuritySolutionAppWrapper} {
overflow-y: hidden; max-height: calc(100vh - ${GLOBAL_HEADER_HEIGHT}px);
}
#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;
} }
`; `;

View file

@ -28,6 +28,9 @@ const Wrapper = styled.div`
&.siemWrapperPage--fullHeight { &.siemWrapperPage--fullHeight {
height: 100%; height: 100%;
display: flex;
flex-direction: column;
flex: 1 1 auto;
} }
&.siemWrapperPage--withTimeline { &.siemWrapperPage--withTimeline {
@ -36,6 +39,9 @@ const Wrapper = styled.div`
&.siemWrapperPage--noPadding { &.siemWrapperPage--noPadding {
padding: 0; padding: 0;
display: flex;
flex-direction: column;
flex: 1 1 auto;
} }
`; `;

View file

@ -9,6 +9,7 @@ import { FtrProviderContext } from '../ftr_provider_context';
export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrProviderContext) { export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'header']); const pageObjects = getPageObjects(['common', 'header']);
const testSubjects = getService('testSubjects'); const testSubjects = getService('testSubjects');
const browser = getService('browser');
return { return {
/** /**
@ -88,6 +89,7 @@ export function EndpointPolicyPageProvider({ getService, getPageObjects }: FtrPr
*/ */
async confirmAndSave() { async confirmAndSave() {
await this.ensureIsOnDetailsPage(); await this.ensureIsOnDetailsPage();
await browser.scrollTop();
await (await this.findSaveButton()).click(); await (await this.findSaveButton()).click();
await testSubjects.existOrFail('policyDetailsConfirmModal'); await testSubjects.existOrFail('policyDetailsConfirmModal');
await pageObjects.common.clickConfirmOnModal(); await pageObjects.common.clickConfirmOnModal();