Skip to content

Commit

Permalink
pr feedback: dedupe code, disable sort buttons, fix flyout
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelolo24 committed Apr 18, 2024
1 parent cf61ecb commit e37faca
Show file tree
Hide file tree
Showing 8 changed files with 252 additions and 227 deletions.
Expand Up @@ -22,6 +22,7 @@ export interface UnifiedTimelineBodyProps extends ComponentProps<typeof UnifiedT
export const UnifiedTimelineBody = (props: UnifiedTimelineBodyProps) => {
const {
header,
isSortEnabled,
pageInfo,
columns,
rowRenderers,
Expand Down Expand Up @@ -68,6 +69,7 @@ export const UnifiedTimelineBody = (props: UnifiedTimelineBodyProps) => {
<UnifiedTimeline
columns={columnsHeader}
rowRenderers={rowRenderers}
isSortEnabled={isSortEnabled}
timelineId={timelineId}
itemsPerPage={itemsPerPage}
itemsPerPageOptions={itemsPerPageOptions}
Expand Down
Expand Up @@ -15,22 +15,17 @@ import deepEqual from 'fast-deep-equal';
import { InPortal } from 'react-reverse-portal';

import { DataLoadingState } from '@kbn/unified-data-table';
import type { BrowserFields, ColumnHeaderOptions } from '@kbn/timelines-plugin/common';
import memoizeOne from 'memoize-one';
import type { ControlColumnProps } from '../../../../../../common/types';
import { InputsModelId } from '../../../../../common/store/inputs/constants';
import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector';
import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features';
import { timelineActions, timelineSelectors } from '../../../../store';
import { useTimelineEvents } from '../../../../containers';
import { defaultHeaders } from '../../body/column_headers/default_headers';
import { StatefulBody } from '../../body';
import { Footer, footerHeight } from '../../footer';
import { calculateTotalPages } from '../../helpers';
import { TimelineRefetch } from '../../refetch_timeline';
import type { ToggleDetailPanel } from '../../../../../../common/types/timeline';
import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline';
import { requiredFieldsForActions } from '../../../../../detections/components/alerts_table/default_config';
import { EventDetailsWidthProvider } from '../../../../../common/components/events_viewer/event_details_width_context';
import type { inputsModel, State } from '../../../../../common/store';
import { inputsSelectors } from '../../../../../common/store';
Expand All @@ -41,10 +36,6 @@ import { useEqlEventsCountPortal } from '../../../../../common/hooks/use_timelin
import type { TimelineModel } from '../../../../store/model';
import { useTimelineFullScreen } from '../../../../../common/containers/use_full_screen';
import { DetailsPanel } from '../../../side_panel';
import { getDefaultControlColumn } from '../../body/control_columns';
import type { Sort } from '../../body/sort';
import { useLicense } from '../../../../../common/hooks/use_license';
import { HeaderActions } from '../../../../../common/components/header_actions/header_actions';
import {
EventsCountBadge,
FullWidthFlexGroup,
Expand All @@ -53,25 +44,19 @@ import {
StyledEuiFlyoutFooter,
VerticalRule,
} from '../shared/layout';
import { EMPTY_EVENTS, isTimerangeSame } from '../shared/utils';
import {
TIMELINE_EMPTY_EVENTS,
isTimerangeSame,
timelineEmptyTrailingControlColumns,
TIMELINE_NO_SORTING,
} from '../shared/utils';
import type { TimelineTabCommonProps } from '../shared/types';
import { defaultUdtHeaders } from '../../unified_components/default_headers';
import { UnifiedTimelineBody } from '../../body/unified_timeline_body';
import { getColumnHeaders } from '../../body/column_headers/helpers';
import { EqlTabHeader } from './header';

const memoizedGetColumnHeaders: (
headers: ColumnHeaderOptions[],
browserFields: BrowserFields,
isEventRenderedView: boolean
) => ColumnHeaderOptions[] = memoizeOne(getColumnHeaders);
import { useTimelineColumns } from '../shared/use-timeline-columns';

export type Props = TimelineTabCommonProps & PropsFromRedux;

const NO_SORTING: Sort[] = [];

const trailingControlColumns: ControlColumnProps[] = []; // stable reference

export const EqlTabContentComponent: React.FC<Props> = ({
activeTab,
columns,
Expand Down Expand Up @@ -100,9 +85,8 @@ export const EqlTabContentComponent: React.FC<Props> = ({
runtimeMappings,
selectedPatterns,
} = useSourcererDataView(SourcererScopeName.timeline);

const isEnterprisePlus = useLicense().isEnterprise();
const ACTION_BUTTON_COUNT = isEnterprisePlus ? 6 : 5;
const { augmentedColumnHeaders, getTimelineQueryFieldsFromColumns, leadingControlColumns } =
useTimelineColumns(columns);

const unifiedComponentsInTimelineEnabled = useIsExperimentalFeatureEnabled(
'unifiedComponentsInTimelineEnabled'
Expand All @@ -118,39 +102,24 @@ export const EqlTabContentComponent: React.FC<Props> = ({

const isBlankTimeline: boolean = isEmpty(eqlQuery);

const canQueryTimeline = () =>
loadingSourcerer != null &&
!loadingSourcerer &&
!isEmpty(start) &&
!isEmpty(end) &&
!isBlankTimeline;

const defaultColumns = useMemo(
() => (unifiedComponentsInTimelineEnabled ? defaultUdtHeaders : defaultHeaders),
[unifiedComponentsInTimelineEnabled]
);

const localColumns = useMemo(
() => (isEmpty(columns) ? defaultColumns : columns),
[columns, defaultColumns]
const canQueryTimeline = useCallback(
() =>
loadingSourcerer != null &&
!loadingSourcerer &&
!isEmpty(start) &&
!isEmpty(end) &&
!isBlankTimeline,
[end, isBlankTimeline, loadingSourcerer, start]
);

const augumentedColumnHeaders = memoizedGetColumnHeaders(localColumns, browserFields, false);

const getTimelineQueryFields = () => {
const columnFields = augumentedColumnHeaders.map((c) => c.id);

return [...columnFields, ...requiredFieldsForActions];
};

const [
dataLoadingState,
{ events, inspect, totalCount, pageInfo, loadPage, refreshedAt, refetch },
] = useTimelineEvents({
dataViewId,
endDate: end,
eqlOptions: restEqlOption,
fields: getTimelineQueryFields(),
fields: getTimelineQueryFieldsFromColumns(),
filterQuery: eqlQuery ?? '',
id: timelineId,
indexNames: selectedPatterns,
Expand Down Expand Up @@ -182,15 +151,6 @@ export const EqlTabContentComponent: React.FC<Props> = ({
);
}, [loadingSourcerer, timelineId, isQueryLoading, dispatch]);

const leadingControlColumns = useMemo(
() =>
getDefaultControlColumn(ACTION_BUTTON_COUNT).map((x) => ({
...x,
headerCellRender: HeaderActions,
})),
[ACTION_BUTTON_COUNT]
);

const unifiedHeader = useMemo(
() => (
<EuiFlexGroup gutterSize="s" direction="column">
Expand All @@ -216,12 +176,13 @@ export const EqlTabContentComponent: React.FC<Props> = ({
<ScrollableFlexItem grow={2}>
<UnifiedTimelineBody
header={unifiedHeader}
columns={augumentedColumnHeaders}
columns={augmentedColumnHeaders}
isSortEnabled={false}
rowRenderers={rowRenderers}
timelineId={timelineId}
itemsPerPage={itemsPerPage}
itemsPerPageOptions={itemsPerPageOptions}
sort={NO_SORTING}
sort={TIMELINE_NO_SORTING}
events={events}
refetch={refetch}
dataLoadingState={dataLoadingState}
Expand Down Expand Up @@ -268,19 +229,19 @@ export const EqlTabContentComponent: React.FC<Props> = ({
<StatefulBody
activePage={pageInfo.activePage}
browserFields={browserFields}
data={isBlankTimeline ? EMPTY_EVENTS : events}
data={isBlankTimeline ? TIMELINE_EMPTY_EVENTS : events}
id={timelineId}
refetch={refetch}
renderCellValue={renderCellValue}
rowRenderers={rowRenderers}
sort={NO_SORTING}
sort={TIMELINE_NO_SORTING}
tabType={TimelineTabs.eql}
totalPages={calculateTotalPages({
itemsCount: totalCount,
itemsPerPage,
})}
leadingControlColumns={leadingControlColumns}
trailingControlColumns={trailingControlColumns}
trailingControlColumns={timelineEmptyTrailingControlColumns}
/>
</StyledEuiFlyoutBody>

Expand Down
Expand Up @@ -8,26 +8,34 @@
import { EuiCallOut, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React, { useMemo } from 'react';
import type { FilterManager } from '@kbn/data-plugin/public';

import { InPortal } from 'react-reverse-portal';
import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid';
import styled from '@emotion/styled';
import { euiThemeVars } from '@kbn/ui-theme';

import { useIsExperimentalFeatureEnabled } from '../../../../../../common/hooks/use_experimental_features';
import { useTimelineEventsCountPortal } from '../../../../../../common/hooks/use_timeline_events_count';
import { useTimelineFullScreen } from '../../../../../../common/containers/use_full_screen';
import { ExitFullScreen } from '../../../../../../common/components/exit_full_screen';
import type { TimelineStatusLiteralWithNull } from '../../../../../../../common/api/timeline';
import { TimelineStatus, TimelineType } from '../../../../../../../common/api/timeline';
import type { TimelineTabs } from '../../../../../../../common/types/timeline';
import { timelineSelectors } from '../../../../../store';
import { useDeepEqualSelector } from '../../../../../../common/hooks/use_selector';
import { timelineDefaults } from '../../../../../store/defaults';
import * as i18n from './translations';
import { StatefulSearchOrFilter } from '../../../search_or_filter';
import { DataProviders } from '../../../data_providers';
import { StyledEuiFlyoutHeader, EventsCountBadge, TabHeaderContainer } from '../../shared/layout';

interface Props {
activeTab: TimelineTabs;
filterManager: FilterManager;
show: boolean;
showCallOutUnauthorizedMsg: boolean;
showEventsCountBadge: boolean;
status: TimelineStatusLiteralWithNull;
timelineId: string;
totalCount: number;
}

const DataProvidersContainer = styled.div<{ $shouldShowQueryBuilder: boolean }>`
Expand All @@ -50,12 +58,20 @@ const DataProvidersContainer = styled.div<{ $shouldShowQueryBuilder: boolean }>`
`;

const QueryTabHeaderComponent: React.FC<Props> = ({
activeTab,
filterManager,
show,
showCallOutUnauthorizedMsg,
status,
timelineId,
showEventsCountBadge,
totalCount,
}) => {
const unifiedComponentsInTimelineEnabled = useIsExperimentalFeatureEnabled(
'unifiedComponentsInTimelineEnabled'
);
const { portalNode: timelineEventsCountPortalNode } = useTimelineEventsCountPortal();
const { setTimelineFullScreen, timelineFullScreen } = useTimelineFullScreen();
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);

const getIsDataProviderVisible = useMemo(
Expand All @@ -74,41 +90,69 @@ const QueryTabHeaderComponent: React.FC<Props> = ({
const shouldShowQueryBuilder = isDataProviderVisible || timelineType === TimelineType.template;

return (
<EuiFlexGroup gutterSize="s" direction="column">
<EuiFlexItem>
<StatefulSearchOrFilter filterManager={filterManager} timelineId={timelineId} />
</EuiFlexItem>
{showCallOutUnauthorizedMsg && (
<EuiFlexItem>
<EuiCallOut
data-test-subj="timelineCallOutUnauthorized"
title={i18n.CALL_OUT_UNAUTHORIZED_MSG}
color="warning"
iconType="warning"
size="s"
/>
</EuiFlexItem>
)}
{status === TimelineStatus.immutable && (
<EuiFlexItem>
<EuiCallOut
data-test-subj="timelineImmutableCallOut"
title={i18n.CALL_OUT_IMMUTABLE}
color="primary"
iconType="warning"
size="s"
/>
<StyledEuiFlyoutHeader data-test-subj={`${activeTab}-tab-flyout-header`} hasBorder={false}>
<InPortal node={timelineEventsCountPortalNode}>
{showEventsCountBadge ? <EventsCountBadge>{totalCount}</EventsCountBadge> : null}
</InPortal>
<EuiFlexGroup gutterSize="s" direction="column">
{!unifiedComponentsInTimelineEnabled &&
timelineFullScreen &&
setTimelineFullScreen != null && (
<EuiFlexItem>
<EuiFlexGroup alignItems="center" gutterSize="s">
<ExitFullScreen
fullScreen={timelineFullScreen}
setFullScreen={setTimelineFullScreen}
/>
</EuiFlexGroup>
</EuiFlexItem>
)}
<EuiFlexItem data-test-subj="timeline-date-picker-container">
<TabHeaderContainer data-test-subj="timelineHeader">
<EuiFlexGroup gutterSize="s" direction="column">
<EuiFlexItem>
<StatefulSearchOrFilter filterManager={filterManager} timelineId={timelineId} />
</EuiFlexItem>
{showCallOutUnauthorizedMsg && (
<EuiFlexItem>
<EuiCallOut
data-test-subj="timelineCallOutUnauthorized"
title={i18n.CALL_OUT_UNAUTHORIZED_MSG}
color="warning"
iconType="warning"
size="s"
/>
</EuiFlexItem>
)}
{status === TimelineStatus.immutable && (
<EuiFlexItem>
<EuiCallOut
data-test-subj="timelineImmutableCallOut"
title={i18n.CALL_OUT_IMMUTABLE}
color="primary"
iconType="warning"
size="s"
/>
</EuiFlexItem>
)}
{show ? (
<DataProvidersContainer
className="data-providers-container"
$shouldShowQueryBuilder={shouldShowQueryBuilder}
>
<DataProviders timelineId={timelineId} />
</DataProvidersContainer>
) : null}
</EuiFlexGroup>
</TabHeaderContainer>
</EuiFlexItem>
)}
{show ? (
<DataProvidersContainer
className="data-providers-container"
$shouldShowQueryBuilder={shouldShowQueryBuilder}
>
<DataProviders timelineId={timelineId} />
</DataProvidersContainer>
) : null}
</EuiFlexGroup>
{/* TODO: This is a temporary solution to hide the KPIs until lens components play nicely with timelines */}
{/* https://github.com/elastic/kibana/issues/17156 */}
{/* <EuiFlexItem grow={false}> */}
{/* <TimelineKpi timelineId={timelineId} /> */}
{/* </EuiFlexItem> */}
</EuiFlexGroup>
</StyledEuiFlyoutHeader>
);
};

Expand Down

0 comments on commit e37faca

Please sign in to comment.