Skip to content

Commit

Permalink
[RAC] [TGrid] Field browser implemented in EuiDataGrid toolbar (#105207
Browse files Browse the repository at this point in the history
…) (#106278)

* tGid header using EuiDataGrid

* useFetchIndex migrated and column_headers refactor

* removed useless mock

* add badges translations

* i18n translations keys fixed

* code format

* filter default columns not present in field browser

* reset button to initial columns

* cleaning

* dependencies moved

* fix functional test with missing data service

* remove unused code (unrelated)

* fieldBrowser integration with security solutions timeline

* lint and translations cleaned

* timeline toolbar removed for merge & some test fixes

* type fix

* type fixes

* timeline static default colums

* limit size temporary increase

* limit size temporary increase

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Sergi Massaneda <sergi.massaneda@elastic.co>
  • Loading branch information
kibanamachine and semd authored Jul 20, 2021
1 parent 433e493 commit a74a3a2
Show file tree
Hide file tree
Showing 58 changed files with 1,195 additions and 869 deletions.
2 changes: 1 addition & 1 deletion packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ pageLoadAssetSize:
dataVisualizer: 27530
banners: 17946
mapsEms: 26072
timelines: 251886
timelines: 330000
screenshotMode: 17856
visTypePie: 35583
expressionRevealImage: 25675
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
FIELDS_BROWSER_FIELDS_COUNT,
FIELDS_BROWSER_HOST_CATEGORIES_COUNT,
FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER,
FIELDS_BROWSER_HOST_GEO_COUNTRY_NAME_HEADER,
FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER,
FIELDS_BROWSER_MESSAGE_HEADER,
FIELDS_BROWSER_SELECTED_CATEGORY_TITLE,
Expand All @@ -24,7 +23,6 @@ import { cleanKibana } from '../../tasks/common';
import {
addsHostGeoCityNameToTimeline,
addsHostGeoContinentNameToTimeline,
addsHostGeoCountryNameToTimelineDraggingIt,
clearFieldsBrowser,
closeFieldsBrowser,
filterFieldsBrowser,
Expand Down Expand Up @@ -156,18 +154,6 @@ describe('Fields Browser', () => {
cy.get(FIELDS_BROWSER_HOST_GEO_CITY_NAME_HEADER).should('exist');
});

it('adds a field to the timeline when the user drags and drops a field', () => {
const filterInput = 'host.geo.c';

filterFieldsBrowser(filterInput);

cy.get(FIELDS_BROWSER_HOST_GEO_COUNTRY_NAME_HEADER).should('not.exist');

addsHostGeoCountryNameToTimelineDraggingIt();

cy.get(FIELDS_BROWSER_HOST_GEO_COUNTRY_NAME_HEADER).should('exist');
});

it('resets all fields in the timeline when `Reset Fields` is clicked', () => {
const filterInput = 'host.geo.c';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ describe('EventsViewer', () => {
<StatefulEventsViewer {...testProps} />
</TestProviders>
);
expect(wrapper.find(`[data-test-subj="show-field-browser"]`).first().exists()).toBe(true);
expect(wrapper.find(`[data-test-subj="field-browser"]`).first().exists()).toBe(true);
});

test('it renders the footer containing the pagination', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,6 @@ export const getBreadcrumbsForRoute = (
}

if (isAdminRoutes(spyState) && object.navTabs) {
const tempNav: SearchNavTab = { urlKey: 'administration', isDetailPage: false };
let urlStateKeys = [getOr(tempNav, spyState.pageName, object.navTabs)];
if (spyState.tabName != null) {
urlStateKeys = [...urlStateKeys, getOr(tempNav, spyState.tabName, object.navTabs)];
}
return [siemRootBreadcrumb, ...getAdminBreadcrumbs(spyState)];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import { ISearchStart } from '../../../../../../../src/plugins/data/public';
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
import { getTimelineTemplate } from '../../../timelines/containers/api';
import { defaultHeaders } from '../../../timelines/components/timeline/body/column_headers/default_headers';

jest.mock('../../../timelines/containers/api', () => ({
getTimelineTemplate: jest.fn(),
Expand Down Expand Up @@ -139,6 +140,7 @@ describe('alert actions', () => {
initialWidth: 180,
},
],
defaultColumns: defaultHeaders,
dataProviders: [],
dateRange: {
end: '2018-11-05T19:03:25.937Z',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,71 +5,21 @@
* 2.0.
*/

import {
EuiCheckbox,
EuiIcon,
EuiToolTip,
EuiFlexGroup,
EuiFlexItem,
EuiScreenReaderOnly,
} from '@elastic/eui';
import { isEmpty, uniqBy } from 'lodash/fp';
import { isEmpty } from 'lodash/fp';
import React, { useCallback, useRef, useState } from 'react';
import { Draggable } from 'react-beautiful-dnd';
import styled from 'styled-components';

import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME } from '@kbn/securitysolution-t-grid';
import { BrowserField, BrowserFields } from '../../../common/containers/source';
import { DragEffects } from '../../../common/components/drag_and_drop/draggable_wrapper';
import { DroppableWrapper } from '../../../common/components/drag_and_drop/droppable_wrapper';
import {
DRAG_TYPE_FIELD,
DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME,
getDraggableFieldId,
getDroppableId,
} from '../../../common/components/drag_and_drop/helpers';
import { DraggableFieldBadge } from '../../../common/components/draggables/field_badge';
import { getEmptyValue } from '../../../common/components/empty_value';
import {
getColumnsWithTimestamp,
getExampleText,
getIconFromType,
} from '../../../common/components/event_details/helpers';
import { defaultColumnHeaderType } from '../timeline/body/column_headers/default_headers';
import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants';
import { OnUpdateColumns } from '../timeline/events';
import { TruncatableText } from '../../../common/components/truncatable_text';
} from '@kbn/securitysolution-t-grid';
import type { BrowserFields } from '../../../common/containers/source';
import { getColumnsWithTimestamp } from '../../../common/components/event_details/helpers';
import type { OnUpdateColumns } from '../timeline/events';
import { FieldName } from './field_name';
import * as i18n from './translations';
import { getAlertColumnHeader } from './helpers';
import { ColumnHeaderOptions } from '../../../../common';
import type { ColumnHeaderOptions } from '../../../../common';
import { useKibana } from '../../../common/lib/kibana';

const TypeIcon = styled(EuiIcon)`
margin: 0 4px;
position: relative;
top: -1px;
`;

TypeIcon.displayName = 'TypeIcon';

export const Description = styled.span`
user-select: text;
width: 400px;
`;

Description.displayName = 'Description';

/**
* An item rendered in the table
*/
export interface FieldItem {
ariaRowindex?: number;
checkbox: React.ReactNode;
description: React.ReactNode;
field: React.ReactNode;
fieldId: string;
}

const DraggableFieldsBrowserFieldComponent = ({
browserFields,
categoryId,
Expand Down Expand Up @@ -191,142 +141,3 @@ const DraggableFieldsBrowserFieldComponent = ({

export const DraggableFieldsBrowserField = React.memo(DraggableFieldsBrowserFieldComponent);
DraggableFieldsBrowserField.displayName = 'DraggableFieldsBrowserFieldComponent';

/**
* Returns the draggable fields, values, and descriptions shown when a user expands an event
*/
export const getFieldItems = ({
browserFields,
category,
categoryId,
columnHeaders,
highlight = '',
onUpdateColumns,
timelineId,
toggleColumn,
}: {
browserFields: BrowserFields;
category: Partial<BrowserField>;
categoryId: string;
columnHeaders: ColumnHeaderOptions[];
highlight?: string;
timelineId: string;
toggleColumn: (column: ColumnHeaderOptions) => void;
onUpdateColumns: OnUpdateColumns;
}): FieldItem[] =>
uniqBy('name', [
...Object.values(category != null && category.fields != null ? category.fields : {}),
]).map((field) => ({
checkbox: (
<EuiToolTip content={i18n.VIEW_COLUMN(field.name ?? '')}>
<EuiCheckbox
aria-label={i18n.VIEW_COLUMN(field.name ?? '')}
checked={columnHeaders.findIndex((c) => c.id === field.name) !== -1}
data-test-subj={`field-${field.name}-checkbox`}
data-colindex={1}
id={field.name ?? ''}
onChange={() =>
toggleColumn({
columnHeaderType: defaultColumnHeaderType,
id: field.name ?? '',
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
...getAlertColumnHeader(timelineId, field.name ?? ''),
})
}
/>
</EuiToolTip>
),
field: (
<EuiFlexGroup alignItems="center" gutterSize="none">
<EuiFlexItem grow={false}>
<EuiToolTip content={field.type}>
<TypeIcon
data-test-subj={`field-${field.name}-icon`}
type={getIconFromType(field.type ?? null)}
/>
</EuiToolTip>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<DroppableWrapper
droppableId={getDroppableId(
`field-browser-field-items-field-droppable-wrapper-${timelineId}-${categoryId}-${field.name}`
)}
key={`field-browser-field-items-field-droppable-wrapper-${timelineId}-${categoryId}-${field.name}`}
isDropDisabled={true}
type={DRAG_TYPE_FIELD}
renderClone={(provided) => (
<div
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
style={{ ...provided.draggableProps.style, zIndex: 9999 }}
tabIndex={-1}
>
<DragEffects>
<DraggableFieldBadge fieldId={field.name ?? ''} />
</DragEffects>
</div>
)}
>
<DraggableFieldsBrowserField
browserFields={browserFields}
categoryId={categoryId}
fieldName={field.name ?? ''}
fieldCategory={field.category ?? ''}
highlight={highlight}
onUpdateColumns={onUpdateColumns}
timelineId={timelineId}
toggleColumn={toggleColumn}
/>
</DroppableWrapper>
</EuiFlexItem>
</EuiFlexGroup>
),
description: (
<div data-colindex={3} tabIndex={0}>
<EuiToolTip content={field.description}>
<>
<EuiScreenReaderOnly data-test-subj="descriptionForScreenReaderOnly">
<p>{i18n.DESCRIPTION_FOR_FIELD(field.name ?? '')}</p>
</EuiScreenReaderOnly>
<TruncatableText>
<Description data-test-subj={`field-${field.name}-description`}>
{`${field.description ?? getEmptyValue()} ${getExampleText(field.example)}`}
</Description>
</TruncatableText>
</>
</EuiToolTip>
</div>
),
fieldId: field.name ?? '',
}));

/**
* Returns a table column template provided to the `EuiInMemoryTable`'s
* `columns` prop
*/
export const getFieldColumns = () => [
{
field: 'checkbox',
name: '',
render: (checkbox: React.ReactNode, _: FieldItem) => checkbox,
sortable: false,
width: '25px',
},
{
field: 'field',
name: i18n.FIELD,
render: (field: React.ReactNode, _: FieldItem) => field,
sortable: false,
width: '225px',
},
{
field: 'description',
name: i18n.DESCRIPTION,
render: (description: React.ReactNode, _: FieldItem) => description,
sortable: false,
truncateText: true,
width: '400px',
},
];
Loading

0 comments on commit a74a3a2

Please sign in to comment.