[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);
}
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);

View file

@ -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';

View file

@ -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);

View file

@ -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"]';

View file

@ -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 && (

View file

@ -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>

View file

@ -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`

View file

@ -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%;
`;

View file

@ -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>
</>
);
};

View file

@ -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';

View file

@ -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);
}
`;

View file

@ -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;
}
`;

View file

@ -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();