Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SecuritySolution] Replace donuts with Lens #148519

Merged
merged 9 commits into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ import {
Wrapper,
ChartWrapper,
} from './common';
import { VisualizationActions, HISTOGRAM_ACTIONS_BUTTON_CLASS } from '../visualization_actions';
import { VisualizationActions } from '../visualization_actions';
import type { VisualizationActionsProps } from '../visualization_actions/types';

import { HoverVisibilityContainer } from '../hover_visibility_container';
import { VISUALIZATION_ACTIONS_BUTTON_CLASS } from '../visualization_actions/utils';

// custom series styles: https://ela.st/areachart-styling
const getSeriesLineStyle = (): RecursivePartial<AreaSeriesStyle> => {
Expand Down Expand Up @@ -155,7 +156,7 @@ export const AreaChartComponent: React.FC<AreaChartComponentProps> = ({

return (
<Wrapper>
<HoverVisibilityContainer targetClassNames={[HISTOGRAM_ACTIONS_BUTTON_CLASS]}>
<HoverVisibilityContainer targetClassNames={[VISUALIZATION_ACTIONS_BUTTON_CLASS]}>
{isValidSeriesExist && areaChart && (
<ChartWrapper gutterSize="none">
<EuiFlexItem grow={true}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ import {
import { DraggableLegend } from './draggable_legend';
import type { LegendItem } from './draggable_legend_item';
import type { ChartData, ChartSeriesConfigs, ChartSeriesData } from './common';
import { VisualizationActions, HISTOGRAM_ACTIONS_BUTTON_CLASS } from '../visualization_actions';
import { VisualizationActions } from '../visualization_actions';
import type { VisualizationActionsProps } from '../visualization_actions/types';
import { HoverVisibilityContainer } from '../hover_visibility_container';
import { VISUALIZATION_ACTIONS_BUTTON_CLASS } from '../visualization_actions/utils';

const LegendFlexItem = styled(EuiFlexItem)`
overview: hidden;
Expand Down Expand Up @@ -207,7 +208,7 @@ export const BarChartComponent: React.FC<BarChartComponentProps> = ({

return (
<Wrapper>
<HoverVisibilityContainer targetClassNames={[HISTOGRAM_ACTIONS_BUTTON_CLASS]}>
<HoverVisibilityContainer targetClassNames={[VISUALIZATION_ACTIONS_BUTTON_CLASS]}>
{isValidSeriesExist && barChart && (
<BarChartWrapper gutterSize="none">
<EuiFlexItem grow={true}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import type { EuiFlexGroupProps } from '@elastic/eui';
import { EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip, useEuiTheme } from '@elastic/eui';
import React, { useMemo } from 'react';

Expand Down Expand Up @@ -44,46 +45,54 @@ export interface DonutChartProps {
data: DonutChartData[] | null | undefined;
fillColor: FillColor;
height?: number;
isChartEmbeddablesEnabled?: boolean;
label: React.ReactElement | string;
legendItems?: LegendItem[] | null | undefined;
onElementClick?: ElementClickListener;
title: React.ReactElement | string | number | null;
totalCount: number | null | undefined;
onElementClick?: ElementClickListener;
}

export interface DonutChartWrapperProps {
children?: React.ReactElement;
dataExists: boolean;
label: React.ReactElement | string;
title: React.ReactElement | string | number | null;
isChartEmbeddablesEnabled?: boolean;
}

/* Make this position absolute in order to overlap the text onto the donut */
const DonutTextWrapper = styled(EuiFlexGroup)`
top: 34%;
export const DonutTextWrapper = styled(EuiFlexGroup)<
EuiFlexGroupProps & { $isChartEmbeddablesEnabled?: boolean; $dataExists?: boolean }
>`
top: ${({ $isChartEmbeddablesEnabled, $dataExists }) =>
$isChartEmbeddablesEnabled && !$dataExists ? `66%` : `34%;`};
width: 100%;
max-width: 77px;
position: absolute;
z-index: 1;
`;

const StyledEuiFlexItem = styled(EuiFlexItem)`
export const StyledEuiFlexItem = styled(EuiFlexItem)`
position: relative;
align-items: center;
`;

export const DonutChart = ({
data,
fillColor,
height = 90,
const DonutChartWrapperComponent: React.FC<DonutChartWrapperProps> = ({
children,
dataExists,
isChartEmbeddablesEnabled,
label,
legendItems,
title,
totalCount,
onElementClick,
}: DonutChartProps) => {
const theme = useTheme();
}) => {
const { euiTheme } = useEuiTheme();
const emptyLabelStyle = useMemo(
() => ({
color: euiTheme.colors.disabled,
}),
[euiTheme.colors.disabled]
);

const className = isChartEmbeddablesEnabled ? undefined : 'eui-textTruncate';
return (
<EuiFlexGroup
alignItems="center"
Expand All @@ -92,26 +101,56 @@ export const DonutChart = ({
gutterSize="l"
data-test-subj="donut-chart"
>
<StyledEuiFlexItem grow={false}>
<StyledEuiFlexItem grow={isChartEmbeddablesEnabled}>
<DonutTextWrapper
$dataExists={dataExists}
$isChartEmbeddablesEnabled={isChartEmbeddablesEnabled}
alignItems="center"
direction="column"
gutterSize="none"
alignItems="center"
justifyContent="center"
>
<EuiFlexItem>{title}</EuiFlexItem>
<EuiFlexItem className="eui-textTruncate">
<EuiFlexItem className={className}>
<EuiToolTip content={label}>
<EuiText
className="eui-textTruncate"
className={className}
size="s"
style={data ? undefined : emptyLabelStyle}
style={dataExists ? undefined : emptyLabelStyle}
>
{label}
</EuiText>
</EuiToolTip>
</EuiFlexItem>
</DonutTextWrapper>
{children}
</StyledEuiFlexItem>
</EuiFlexGroup>
);
};
export const DonutChartWrapper = React.memo(DonutChartWrapperComponent);

export const DonutChart = ({
data,
fillColor,
height = 90,
isChartEmbeddablesEnabled,
label,
legendItems,
onElementClick,
title,
totalCount,
}: DonutChartProps) => {
const theme = useTheme();

return (
<DonutChartWrapper
dataExists={data != null && data.length > 0}
label={label}
title={title}
isChartEmbeddablesEnabled={isChartEmbeddablesEnabled}
>
<>
{data == null || totalCount == null || totalCount === 0 ? (
<DonutChartEmpty size={height} />
) : (
Expand All @@ -135,12 +174,13 @@ export const DonutChart = ({
/>
</Chart>
)}
</StyledEuiFlexItem>
{legendItems && legendItems?.length > 0 && (
<EuiFlexItem>
<DraggableLegend legendItems={legendItems} height={height} />
</EuiFlexItem>
)}
</EuiFlexGroup>

{legendItems && legendItems?.length > 0 && (
<EuiFlexItem>
<DraggableLegend legendItems={legendItems} height={height} />
</EuiFlexItem>
)}
</>
</DonutChartWrapper>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import numeral from '@elastic/numeral';

import { MatrixHistogramType } from '../../../../common/search_strategy/security_solution';
import { getExternalAlertLensAttributes } from '../visualization_actions/lens_attributes/common/external_alert';
import { getEventsHistogramLensAttributes } from '../visualization_actions/lens_attributes/hosts/events';
import { getEventsHistogramLensAttributes } from '../visualization_actions/lens_attributes/common/events';
import type { MatrixHistogramConfigs, MatrixHistogramOption } from '../matrix_histogram/types';
import * as i18n from './translations';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const DescriptionListStyled = styled(EuiDescriptionList)`
DescriptionListStyled.displayName = 'DescriptionListStyled';

export interface ModalInspectProps {
adHocDataViews?: string[] | null;
additionalRequests?: string[] | null;
additionalResponses?: string[] | null;
closeModal: () => void;
Expand Down Expand Up @@ -108,6 +109,7 @@ export const formatIndexPatternRequested = (indices: string[] = []) => {
};

export const ModalInspectQuery = ({
adHocDataViews,
additionalRequests,
additionalResponses,
closeModal,
Expand All @@ -120,6 +122,7 @@ export const ModalInspectQuery = ({
const { selectedPatterns } = useSourcererDataView(
inputId === 'timeline' ? SourcererScopeName.timeline : getScopeFromPath(pathname)
);

const requests: string[] = [request, ...(additionalRequests != null ? additionalRequests : [])];
const responses: string[] = [
response,
Expand Down Expand Up @@ -150,7 +153,13 @@ export const ModalInspectQuery = ({
),
description: (
<span data-test-subj="index-pattern-description">
<p>{formatIndexPatternRequested(inspectRequests[0]?.index ?? [])}</p>
<p>
{formatIndexPatternRequested(
adHocDataViews != null && adHocDataViews.length > 0
? adHocDataViews
: inspectRequests[0]?.index ?? []
)}
</p>

{!isSourcererPattern && (
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@ import type { GlobalTimeArgs } from '../../containers/use_global_time';
import { setAbsoluteRangeDatePicker } from '../../store/inputs/actions';
import { InputsModelId } from '../../store/inputs/constants';
import { HoverVisibilityContainer } from '../hover_visibility_container';
import { HISTOGRAM_ACTIONS_BUTTON_CLASS, VisualizationActions } from '../visualization_actions';
import { VisualizationActions } from '../visualization_actions';
import type { GetLensAttributes, LensAttributes } from '../visualization_actions/types';
import { SecurityPageName } from '../../../../common/constants';
import { useRouteSpy } from '../../utils/route/use_route_spy';
import { useQueryToggle } from '../../containers/query_toggle';
import { VISUALIZATION_ACTIONS_BUTTON_CLASS } from '../visualization_actions/utils';

export type MatrixHistogramComponentProps = MatrixHistogramProps &
Omit<MatrixHistogramQueryProps, 'stackByField'> & {
Expand Down Expand Up @@ -236,7 +237,7 @@ export const MatrixHistogramComponent: React.FC<MatrixHistogramComponentProps> =
<>
<HoverVisibilityContainer
show={!isInitialLoading}
targetClassNames={[HISTOGRAM_ACTIONS_BUTTON_CLASS]}
targetClassNames={[VISUALIZATION_ACTIONS_BUTTON_CLASS]}
>
<HistogramPanel
data-test-subj={`${id}Panel`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe(`useRefetchByRestartingSession`, () => {
children: React.ReactNode;
},
{
searchSessionId: string;
searchSessionId: string | undefined;
refetchByRestartingSession: Refetch;
}
>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { useCallback, useRef, useEffect, useMemo } from 'react';
import { useCallback, useRef, useMemo } from 'react';
import { useDispatch } from 'react-redux';
import { useDeepEqualSelector } from '../../hooks/use_selector';
import { useKibana } from '../../lib/kibana';
Expand All @@ -23,21 +23,28 @@ interface UseRefetchByRestartingSessionProps {
export const useRefetchByRestartingSession = ({
inputId,
queryId,
skip,
}: UseRefetchByRestartingSessionProps): {
searchSessionId: string;
searchSessionId: string | undefined;
refetchByRestartingSession: Refetch;
} => {
const dispatch = useDispatch();
const { data } = useKibana().services;

const session = useRef(data.search.session);
const searchSessionId = useMemo(() => session.current.start(), [session]);

const getGlobalQuery = inputsSelectors.globalQueryByIdSelector();
const getTimelineQuery = inputsSelectors.timelineQueryByIdSelector();
const { selectedInspectIndex } = useDeepEqualSelector((state) =>
inputId === InputsModelId.global
? getGlobalQuery(state, queryId)
: getTimelineQuery(state, queryId)
const { selectedInspectIndex, searchSessionId: existingSearchSessionId } = useDeepEqualSelector(
(state) =>
inputId === InputsModelId.global
? getGlobalQuery(state, queryId)
: getTimelineQuery(state, queryId)
);

const searchSessionId = useMemo(
() => (skip ? undefined : existingSearchSessionId ?? session.current.start()),
[existingSearchSessionId, skip]
);

const refetchByRestartingSession = useCallback(() => {
Expand All @@ -51,16 +58,10 @@ export const useRefetchByRestartingSession = ({
* like most of our components, it refetches when receiving a new search
* session ID.
**/
searchSessionId: session.current.start(),
searchSessionId: skip ? undefined : session.current.start(),
})
);
}, [dispatch, queryId, selectedInspectIndex]);

useEffect(() => {
return () => {
data.search.session.clear();
};
});
}, [dispatch, queryId, selectedInspectIndex, skip]);

return { searchSessionId, refetchByRestartingSession };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';

export const VisualizationActions = () => <div data-test-subj="visualizationActions" />;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';

export const LensEmbeddable = () => <div data-test-subj="lens-embeddable" />;
Loading