Skip to content

Commit

Permalink
[RAC][Security Solution] Make analyzer work with EuiDataGrid full scr…
Browse files Browse the repository at this point in the history
…een (elastic#110913)

* Make analyzer work with EuiDataGrid full screen

* Don't ever restrict the width, remove console.log

* Remove isEventViewer prop no longer used

* Make global filters appear below data grid
  • Loading branch information
kqualters-elastic authored and kibanamachine committed Sep 2, 2021
1 parent a7efb20 commit 1fc43b1
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { useGlobalHeaderPortal } from '../../../../common/hooks/use_global_heade

const StyledStickyWrapper = styled.div`
position: sticky;
z-index: ${(props) => props.theme.eui.euiZLevel2};
z-index: ${(props) => props.theme.eui.euiZHeaderBelowDataGrid};
// TOP location is declared in src/public/rendering/_base.scss to keep in line with Kibana Chrome
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ const EventsViewerComponent: React.FC<Props> = ({
refetch={refetch}
/>

{graphEventId && <GraphOverlay isEventViewer={true} timelineId={id} />}
{graphEventId && <GraphOverlay timelineId={id} />}
<FullWidthFlexGroup $visible={!graphEventId} gutterSize="none">
<ScrollableFlexItem grow={1}>
<StatefulBody
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,7 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
const trailingControlColumns: ControlColumnProps[] = EMPTY_CONTROL_COLUMNS;
const graphOverlay = useMemo(
() =>
graphEventId != null && graphEventId.length > 0 ? (
<GraphOverlay isEventViewer={true} timelineId={id} />
) : null,
graphEventId != null && graphEventId.length > 0 ? <GraphOverlay timelineId={id} /> : null,
[graphEventId, id]
);
const setQuery = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export const resetScroll = () => {
}
}, 0);
};

interface GlobalFullScreen {
globalFullScreen: boolean;
setGlobalFullScreen: (fullScreen: boolean) => void;
Expand All @@ -46,10 +45,10 @@ export const useGlobalFullScreen = (): GlobalFullScreen => {
const setGlobalFullScreen = useCallback(
(fullScreen: boolean) => {
if (fullScreen) {
document.body.classList.add(SCROLLING_DISABLED_CLASS_NAME);
document.body.classList.add(SCROLLING_DISABLED_CLASS_NAME, 'euiDataGrid__restrictBody');
resetScroll();
} else {
document.body.classList.remove(SCROLLING_DISABLED_CLASS_NAME);
document.body.classList.remove(SCROLLING_DISABLED_CLASS_NAME, 'euiDataGrid__restrictBody');
resetScroll();
}

Expand All @@ -71,9 +70,15 @@ export const useTimelineFullScreen = (): TimelineFullScreen => {
const dispatch = useDispatch();
const timelineFullScreen =
useShallowEqualSelector(inputsSelectors.timelineFullScreenSelector) ?? false;

const setTimelineFullScreen = useCallback(
(fullScreen: boolean) => dispatch(inputsActions.setFullScreen({ id: 'timeline', fullScreen })),
(fullScreen: boolean) => {
if (fullScreen) {
document.body.classList.add('euiDataGrid__restrictBody');
} else {
document.body.classList.remove('euiDataGrid__restrictBody');
}
dispatch(inputsActions.setFullScreen({ id: 'timeline', fullScreen }));
},
[dispatch]
);
const memoizedReturn = useMemo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ import {
setActiveTabTimeline,
updateTimelineGraphEventId,
} from '../../../../timelines/store/timeline/actions';
import {
useGlobalFullScreen,
useTimelineFullScreen,
} from '../../../../common/containers/use_full_screen';
import { TimelineId, TimelineTabs } from '../../../../../common';
import { ACTION_INVESTIGATE_IN_RESOLVER } from '../../../../timelines/components/timeline/body/translations';
import { Ecs } from '../../../../../common/ecs';
Expand All @@ -35,13 +39,23 @@ export const useInvestigateInResolverContextItem = ({
}: InvestigateInResolverProps) => {
const dispatch = useDispatch();
const isDisabled = useMemo(() => !isInvestigateInResolverActionEnabled(ecsData), [ecsData]);
const { setGlobalFullScreen } = useGlobalFullScreen();
const { setTimelineFullScreen } = useTimelineFullScreen();
const handleClick = useCallback(() => {
const dataGridIsFullScreen = document.querySelector('.euiDataGrid--fullScreen');
dispatch(updateTimelineGraphEventId({ id: timelineId, graphEventId: ecsData._id }));
if (timelineId === TimelineId.active) {
if (dataGridIsFullScreen) {
setTimelineFullScreen(true);
}
dispatch(setActiveTabTimeline({ id: timelineId, activeTab: TimelineTabs.graph }));
} else {
if (dataGridIsFullScreen) {
setGlobalFullScreen(true);
}
}
onClose();
}, [dispatch, ecsData._id, onClose, timelineId]);
}, [dispatch, ecsData._id, onClose, timelineId, setGlobalFullScreen, setTimelineFullScreen]);
return isDisabled
? []
: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,10 @@ describe('GraphOverlay', () => {
});

describe('when used in an events viewer (i.e. in the Detections view, or the Host > Events view)', () => {
const isEventViewer = true;

test('it has 100% width when isEventViewer is true and NOT in full screen mode', async () => {
test('it has 100% width when NOT in full screen mode', async () => {
const wrapper = mount(
<TestProviders>
<GraphOverlay timelineId={TimelineId.test} isEventViewer={isEventViewer} />
<GraphOverlay timelineId={TimelineId.test} />
</TestProviders>
);

Expand All @@ -59,9 +57,9 @@ describe('GraphOverlay', () => {
});
});

test('it has a calculated width that makes room for the Timeline flyout button when isEventViewer is true in full screen mode', async () => {
test('it has a fixed position when in full screen mode', async () => {
(useGlobalFullScreen as jest.Mock).mockReturnValue({
globalFullScreen: true, // <-- true when an events viewer is in full screen mode
globalFullScreen: true,
setGlobalFullScreen: jest.fn(),
});
(useTimelineFullScreen as jest.Mock).mockReturnValue({
Expand All @@ -71,25 +69,24 @@ describe('GraphOverlay', () => {

const wrapper = mount(
<TestProviders>
<GraphOverlay timelineId={TimelineId.test} isEventViewer={isEventViewer} />
<GraphOverlay timelineId={TimelineId.test} />
</TestProviders>
);

await waitFor(() => {
const overlayContainer = wrapper.find('[data-test-subj="overlayContainer"]').first();
expect(overlayContainer).toHaveStyleRule('width', 'calc(100% - 36px)');
expect(overlayContainer).toHaveStyleRule('position', 'fixed');
});
});
});

describe('when used in the active timeline', () => {
const isEventViewer = false;
const timelineId = TimelineId.active;

test('it has 100% width when isEventViewer is false and NOT in full screen mode', async () => {
test('it has 100% width when NOT in full screen mode', async () => {
const wrapper = mount(
<TestProviders>
<GraphOverlay timelineId={timelineId} isEventViewer={isEventViewer} />
<GraphOverlay timelineId={timelineId} />
</TestProviders>
);

Expand All @@ -99,7 +96,7 @@ describe('GraphOverlay', () => {
});
});

test('it has 100% width when isEventViewer is false and the active timeline is in full screen mode', async () => {
test('it has 100% width when the active timeline is in full screen mode', async () => {
(useGlobalFullScreen as jest.Mock).mockReturnValue({
globalFullScreen: false,
setGlobalFullScreen: jest.fn(),
Expand All @@ -111,7 +108,7 @@ describe('GraphOverlay', () => {

const wrapper = mount(
<TestProviders>
<GraphOverlay timelineId={timelineId} isEventViewer={isEventViewer} />
<GraphOverlay timelineId={timelineId} />
</TestProviders>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,19 @@ import {
import * as i18n from './translations';

const OverlayContainer = styled.div`
${({ $restrictWidth }: { $restrictWidth: boolean }) =>
`
display: flex;
flex-direction: column;
flex: 1;
width: ${$restrictWidth ? 'calc(100% - 36px)' : '100%'};
`}
display: flex;
flex-direction: column;
flex: 1;
width: 100%;
`;

const FullScreenOverlayContainer = styled.div`
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: ${(props) => props.theme.eui.euiZLevel3};
`;

const StyledResolver = styled(Resolver)`
Expand All @@ -59,7 +65,6 @@ const FullScreenButtonIcon = styled(EuiButtonIcon)`
`;

interface OwnProps {
isEventViewer: boolean;
timelineId: TimelineId;
}

Expand Down Expand Up @@ -111,18 +116,15 @@ NavigationComponent.displayName = 'NavigationComponent';

const Navigation = React.memo(NavigationComponent);

const GraphOverlayComponent: React.FC<OwnProps> = ({ isEventViewer, timelineId }) => {
const GraphOverlayComponent: React.FC<OwnProps> = ({ timelineId }) => {
const dispatch = useDispatch();
const onCloseOverlay = useCallback(() => {
dispatch(updateTimelineGraphEventId({ id: timelineId, graphEventId: '' }));
}, [dispatch, timelineId]);
const { globalFullScreen, setGlobalFullScreen } = useGlobalFullScreen();
const { timelineFullScreen, setTimelineFullScreen } = useTimelineFullScreen();

const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
const graphEventId = useDeepEqualSelector(
(state) => (getTimeline(state, timelineId) ?? timelineDefaults).graphEventId
);

const { globalFullScreen, setGlobalFullScreen } = useGlobalFullScreen();
const { timelineFullScreen, setTimelineFullScreen } = useTimelineFullScreen();
const getStartSelector = useMemo(() => startSelector(), []);
const getEndSelector = useMemo(() => endSelector(), []);
const getIsLoadingSelector = useMemo(() => isLoadingSelector(), []);
Expand Down Expand Up @@ -154,6 +156,16 @@ const GraphOverlayComponent: React.FC<OwnProps> = ({ isEventViewer, timelineId }
[globalFullScreen, timelineId, timelineFullScreen]
);

const isInTimeline = timelineId === TimelineId.active;
const onCloseOverlay = useCallback(() => {
if (timelineId === TimelineId.active) {
setTimelineFullScreen(false);
} else {
setGlobalFullScreen(false);
}
dispatch(updateTimelineGraphEventId({ id: timelineId, graphEventId: '' }));
}, [dispatch, timelineId, setTimelineFullScreen, setGlobalFullScreen]);

const toggleFullScreen = useCallback(() => {
if (timelineId === TimelineId.active) {
setTimelineFullScreen(!timelineFullScreen);
Expand All @@ -173,41 +185,71 @@ const GraphOverlayComponent: React.FC<OwnProps> = ({ isEventViewer, timelineId }
[]
);
const existingIndexNames = useDeepEqualSelector<string[]>(existingIndexNamesSelector);

return (
<OverlayContainer
data-test-subj="overlayContainer"
$restrictWidth={isEventViewer && fullScreen}
>
<EuiHorizontalRule margin="none" />
<EuiFlexGroup gutterSize="none" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<Navigation
fullScreen={fullScreen}
globalFullScreen={globalFullScreen}
onCloseOverlay={onCloseOverlay}
timelineId={timelineId}
timelineFullScreen={timelineFullScreen}
toggleFullScreen={toggleFullScreen}
if (fullScreen && !isInTimeline) {
return (
<FullScreenOverlayContainer data-test-subj="overlayContainer">
<EuiHorizontalRule margin="none" />
<EuiFlexGroup gutterSize="none" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<Navigation
fullScreen={fullScreen}
globalFullScreen={globalFullScreen}
onCloseOverlay={onCloseOverlay}
timelineId={timelineId}
timelineFullScreen={timelineFullScreen}
toggleFullScreen={toggleFullScreen}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="none" />
{graphEventId !== undefined ? (
<StyledResolver
databaseDocumentID={graphEventId}
resolverComponentInstanceID={timelineId}
indices={existingIndexNames}
shouldUpdate={shouldUpdate}
filters={{ from, to }}
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiHorizontalRule margin="none" />
{graphEventId !== undefined ? (
<StyledResolver
databaseDocumentID={graphEventId}
resolverComponentInstanceID={timelineId}
indices={existingIndexNames}
shouldUpdate={shouldUpdate}
filters={{ from, to }}
/>
) : (
<EuiFlexGroup alignItems="center" justifyContent="center" style={{ height: '100%' }}>
<EuiLoadingSpinner size="xl" />
) : (
<EuiFlexGroup alignItems="center" justifyContent="center" style={{ height: '100%' }}>
<EuiLoadingSpinner size="xl" />
</EuiFlexGroup>
)}
</FullScreenOverlayContainer>
);
} else {
return (
<OverlayContainer data-test-subj="overlayContainer">
<EuiHorizontalRule margin="none" />
<EuiFlexGroup gutterSize="none" justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<Navigation
fullScreen={fullScreen}
globalFullScreen={globalFullScreen}
onCloseOverlay={onCloseOverlay}
timelineId={timelineId}
timelineFullScreen={timelineFullScreen}
toggleFullScreen={toggleFullScreen}
/>
</EuiFlexItem>
</EuiFlexGroup>
)}
</OverlayContainer>
);
<EuiHorizontalRule margin="none" />
{graphEventId !== undefined ? (
<StyledResolver
databaseDocumentID={graphEventId}
resolverComponentInstanceID={timelineId}
indices={existingIndexNames}
shouldUpdate={shouldUpdate}
filters={{ from, to }}
/>
) : (
<EuiFlexGroup alignItems="center" justifyContent="center" style={{ height: '100%' }}>
<EuiLoadingSpinner size="xl" />
</EuiFlexGroup>
)}
</OverlayContainer>
);
}
};

export const GraphOverlay = React.memo(GraphOverlayComponent);
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const GraphTabContentComponent: React.FC<GraphTabContentProps> = ({ timelineId }
return null;
}

return <GraphOverlay isEventViewer={false} timelineId={timelineId} />;
return <GraphOverlay timelineId={timelineId} />;
};

GraphTabContentComponent.displayName = 'GraphTabContentComponent';
Expand Down
Loading

0 comments on commit 1fc43b1

Please sign in to comment.