mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[RAC] T-Grid is moving to a new home (#100265)
* wip * First pass at standalone and embedded redux stores and usage * wip * First pass at standalone and embedded redux stores and usage * wip * clean up * wip * refact(NA): remove extra pkg_npm target and add specific target folders on @kbn/i18n * cleanup * - fixes type errors in tests * WIP remove use_manage_timeline * wip add query + selector * finishing integrating timeline manage context from redux * integrating t-grid in security solution * fix RowRender type * WIP begin to move components from package to plugin * integration of t-grid inside of security solution * wip to make redux work * little trick to make it render * - fixes a few type errors * better integration betwen tgrid and security solutions * bringing back tsconfig on timeline * wip integration t-grid in observability * fix types * fix type in security solutions * add type to import + trie dto get the bundle size as small as possible * fix type in integration test * fix type in integration test * - fix tests * clean up to use technical fields * - fixes unit tests * - mocks the `useDateFormat` function of the `useKibana` service to fix unit tests * fix t-grid settings vs create timeline + fix inspect button * fix last suites test * Update unit tests, snapshots and lint * Fix bad merge * fix plugin export * Fix some failing tests * fix unit tets in timelines plugins * fix latest test * fix i18n * free obs from t-grid * Fix timeline functional plugin types * fix store provider * Update failing defaultHeader test * Fix i18n usage in security solution * Fix remaining i18n errors in timelines plugin * Dedupe common shared types * move drag and drop utils in package to avoid duplication * More shared type cleanup * add feature flag * review I * fix merge with master * fix i18n translation * More type deduping * Use @kbn/common-utils, fix remaining types * fix types * fix tests * missing type * fix cypress tests Co-authored-by: Kevin Qualters <kevin.qualters@elastic.co> Co-authored-by: Tiago Costa <tiagoffcc@hotmail.com> Co-authored-by: Andrew Goldstein <andrew.goldstein@elastic.co>
This commit is contained in:
parent
369127e8c2
commit
4fa3dc46cb
528 changed files with 60237 additions and 3993 deletions
12
.eslintrc.js
12
.eslintrc.js
|
@ -893,6 +893,8 @@ module.exports = {
|
|||
files: [
|
||||
'x-pack/plugins/security_solution/public/**/*.{js,mjs,ts,tsx}',
|
||||
'x-pack/plugins/security_solution/common/**/*.{js,mjs,ts,tsx}',
|
||||
'x-pack/plugins/timelines/public/**/*.{js,mjs,ts,tsx}',
|
||||
'x-pack/plugins/timelines/common/**/*.{js,mjs,ts,tsx}',
|
||||
],
|
||||
rules: {
|
||||
'import/no-nodejs-modules': 'error',
|
||||
|
@ -907,7 +909,10 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
// typescript only for front and back end
|
||||
files: ['x-pack/plugins/security_solution/**/*.{ts,tsx}'],
|
||||
files: [
|
||||
'x-pack/plugins/security_solution/**/*.{ts,tsx}',
|
||||
'x-pack/plugins/timelines/**/*.{ts,tsx}',
|
||||
],
|
||||
rules: {
|
||||
'@typescript-eslint/no-this-alias': 'error',
|
||||
'@typescript-eslint/no-explicit-any': 'error',
|
||||
|
@ -917,7 +922,10 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
// typescript and javascript for front and back end
|
||||
files: ['x-pack/plugins/security_solution/**/*.{js,mjs,ts,tsx}'],
|
||||
files: [
|
||||
'x-pack/plugins/security_solution/**/*.{js,mjs,ts,tsx}',
|
||||
'x-pack/plugins/timelines/**/*.{js,mjs,ts,tsx}',
|
||||
],
|
||||
plugins: ['eslint-plugin-node', 'react'],
|
||||
env: {
|
||||
jest: true,
|
||||
|
|
|
@ -149,6 +149,7 @@
|
|||
"@kbn/securitysolution-list-api": "link:bazel-bin/packages/kbn-securitysolution-list-api",
|
||||
"@kbn/securitysolution-list-hooks": "link:bazel-bin/packages/kbn-securitysolution-list-hooks",
|
||||
"@kbn/securitysolution-list-utils": "link:bazel-bin/packages/kbn-securitysolution-list-utils",
|
||||
"@kbn/securitysolution-t-grid": "link:bazel-bin/packages/kbn-securitysolution-t-grid",
|
||||
"@kbn/securitysolution-utils": "link:bazel-bin/packages/kbn-securitysolution-utils",
|
||||
"@kbn/server-http-tools": "link:bazel-bin/packages/kbn-server-http-tools",
|
||||
"@kbn/server-route-repository": "link:bazel-bin/packages/kbn-server-route-repository",
|
||||
|
@ -217,6 +218,8 @@
|
|||
"cytoscape-dagre": "^2.2.2",
|
||||
"d3": "3.5.17",
|
||||
"d3-array": "1.2.4",
|
||||
"d3-cloud": "1.2.5",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-scale": "1.0.7",
|
||||
"d3-shape": "^1.1.0",
|
||||
"d3-time": "^1.1.0",
|
||||
|
@ -511,6 +514,7 @@
|
|||
"@types/cytoscape": "^3.14.0",
|
||||
"@types/d3": "^3.5.43",
|
||||
"@types/d3-array": "^1.2.7",
|
||||
"@types/d3-interpolate": "^2.0.0",
|
||||
"@types/d3-scale": "^2.1.1",
|
||||
"@types/d3-shape": "^1.3.1",
|
||||
"@types/d3-time": "^1.0.10",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
filegroup(
|
||||
name = "build",
|
||||
srcs = [
|
||||
"//packages/elastic-datemath:build",
|
||||
"//packages/elastic-datemath:build",
|
||||
"//packages/elastic-eslint-config-kibana:build",
|
||||
"//packages/elastic-safer-lodash-set:build",
|
||||
"//packages/kbn-ace:build",
|
||||
|
@ -41,6 +41,7 @@ filegroup(
|
|||
"//packages/kbn-securitysolution-list-utils:build",
|
||||
"//packages/kbn-securitysolution-utils:build",
|
||||
"//packages/kbn-securitysolution-es-utils:build",
|
||||
"//packages/kbn-securitysolution-t-grid:build",
|
||||
"//packages/kbn-securitysolution-hook-utils:build",
|
||||
"//packages/kbn-server-http-tools:build",
|
||||
"//packages/kbn-server-route-repository:build",
|
||||
|
|
|
@ -67,7 +67,7 @@ pageLoadAssetSize:
|
|||
searchprofiler: 67080
|
||||
security: 95864
|
||||
securityOss: 30806
|
||||
securitySolution: 76000
|
||||
securitySolution: 217673
|
||||
share: 99061
|
||||
snapshotRestore: 79032
|
||||
spaces: 57868
|
||||
|
@ -107,7 +107,7 @@ pageLoadAssetSize:
|
|||
dataVisualizer: 27530
|
||||
banners: 17946
|
||||
mapsEms: 26072
|
||||
timelines: 28613
|
||||
timelines: 230410
|
||||
screenshotMode: 17856
|
||||
visTypePie: 35583
|
||||
cases: 144442
|
||||
|
|
125
packages/kbn-securitysolution-t-grid/BUILD.bazel
Normal file
125
packages/kbn-securitysolution-t-grid/BUILD.bazel
Normal file
|
@ -0,0 +1,125 @@
|
|||
load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm")
|
||||
|
||||
PKG_BASE_NAME = "kbn-securitysolution-t-grid"
|
||||
|
||||
PKG_REQUIRE_NAME = "@kbn/securitysolution-t-grid"
|
||||
|
||||
SOURCE_FILES = glob(
|
||||
[
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
],
|
||||
exclude = [
|
||||
"**/*.test.*",
|
||||
"**/*.mock.*",
|
||||
],
|
||||
)
|
||||
|
||||
SRCS = SOURCE_FILES
|
||||
|
||||
filegroup(
|
||||
name = "srcs",
|
||||
srcs = SRCS,
|
||||
)
|
||||
|
||||
NPM_MODULE_EXTRA_FILES = [
|
||||
"react/package.json",
|
||||
"package.json",
|
||||
"README.md",
|
||||
]
|
||||
|
||||
SRC_DEPS = [
|
||||
"//packages/kbn-babel-preset",
|
||||
"//packages/kbn-dev-utils",
|
||||
"//packages/kbn-i18n",
|
||||
"@npm//@babel/core",
|
||||
"@npm//babel-loader",
|
||||
"@npm//enzyme",
|
||||
"@npm//jest",
|
||||
"@npm//lodash",
|
||||
"@npm//react",
|
||||
"@npm//react-beautiful-dnd",
|
||||
"@npm//tslib",
|
||||
]
|
||||
|
||||
TYPES_DEPS = [
|
||||
"@npm//typescript",
|
||||
"@npm//@types/enzyme",
|
||||
"@npm//@types/jest",
|
||||
"@npm//@types/lodash",
|
||||
"@npm//@types/node",
|
||||
"@npm//@types/react",
|
||||
"@npm//@types/react-beautiful-dnd",
|
||||
]
|
||||
|
||||
DEPS = SRC_DEPS + TYPES_DEPS
|
||||
|
||||
ts_config(
|
||||
name = "tsconfig",
|
||||
src = "tsconfig.json",
|
||||
deps = [
|
||||
"//:tsconfig.base.json",
|
||||
],
|
||||
)
|
||||
|
||||
ts_config(
|
||||
name = "tsconfig_browser",
|
||||
src = "tsconfig.browser.json",
|
||||
deps = [
|
||||
"//:tsconfig.base.json",
|
||||
"//:tsconfig.browser.json",
|
||||
],
|
||||
)
|
||||
|
||||
ts_project(
|
||||
name = "tsc",
|
||||
args = ["--pretty"],
|
||||
srcs = SRCS,
|
||||
deps = DEPS,
|
||||
declaration = True,
|
||||
declaration_dir = "target_types",
|
||||
declaration_map = True,
|
||||
incremental = True,
|
||||
out_dir = "target_node",
|
||||
root_dir = "src",
|
||||
source_map = True,
|
||||
tsconfig = ":tsconfig",
|
||||
)
|
||||
|
||||
ts_project(
|
||||
name = "tsc_browser",
|
||||
args = ['--pretty'],
|
||||
srcs = SRCS,
|
||||
deps = DEPS,
|
||||
allow_js = True,
|
||||
declaration = False,
|
||||
incremental = True,
|
||||
out_dir = "target_web",
|
||||
source_map = True,
|
||||
root_dir = "src",
|
||||
tsconfig = ":tsconfig_browser",
|
||||
)
|
||||
|
||||
js_library(
|
||||
name = PKG_BASE_NAME,
|
||||
package_name = PKG_REQUIRE_NAME,
|
||||
srcs = NPM_MODULE_EXTRA_FILES,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":tsc", ":tsc_browser"] + DEPS,
|
||||
)
|
||||
|
||||
pkg_npm(
|
||||
name = "npm_module",
|
||||
deps = [
|
||||
":%s" % PKG_BASE_NAME,
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "build",
|
||||
srcs = [
|
||||
":npm_module",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
3
packages/kbn-securitysolution-t-grid/README.md
Normal file
3
packages/kbn-securitysolution-t-grid/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# kbn-securitysolution-t-grid
|
||||
|
||||
We do not want to create circular dependencies between security_solution and timelines plugins. Therefore , we will use this packages to share components between these two plugins.
|
19
packages/kbn-securitysolution-t-grid/babel.config.js
Normal file
19
packages/kbn-securitysolution-t-grid/babel.config.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
env: {
|
||||
web: {
|
||||
presets: ['@kbn/babel-preset/webpack_preset'],
|
||||
},
|
||||
node: {
|
||||
presets: ['@kbn/babel-preset/node_preset'],
|
||||
},
|
||||
},
|
||||
ignore: ['**/*.test.ts', '**/*.test.tsx'],
|
||||
};
|
13
packages/kbn-securitysolution-t-grid/jest.config.js
Normal file
13
packages/kbn-securitysolution-t-grid/jest.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../..',
|
||||
roots: ['<rootDir>/packages/kbn-securitysolution-t-grid'],
|
||||
};
|
10
packages/kbn-securitysolution-t-grid/package.json
Normal file
10
packages/kbn-securitysolution-t-grid/package.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "@kbn/securitysolution-t-grid",
|
||||
"version": "1.0.0",
|
||||
"description": "security solution t-grid packages will allow sharing components between timelines and security_solution plugin until we transfer all functionality to timelines plugin",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0",
|
||||
"browser": "./target_web/browser.js",
|
||||
"main": "./target_node/index.js",
|
||||
"types": "./target_types/index.d.ts",
|
||||
"private": true
|
||||
}
|
5
packages/kbn-securitysolution-t-grid/react/package.json
Normal file
5
packages/kbn-securitysolution-t-grid/react/package.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"browser": "../target_web/react",
|
||||
"main": "../target_node/react",
|
||||
"types": "../target_types/react/index.d.ts"
|
||||
}
|
26
packages/kbn-securitysolution-t-grid/src/constants/index.ts
Normal file
26
packages/kbn-securitysolution-t-grid/src/constants/index.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const HIGHLIGHTED_DROP_TARGET_CLASS_NAME = 'highlighted-drop-target';
|
||||
export const EMPTY_PROVIDERS_GROUP_CLASS_NAME = 'empty-providers-group';
|
||||
|
||||
/** The draggable will move this many pixels via the keyboard when the arrow key is pressed */
|
||||
export const KEYBOARD_DRAG_OFFSET = 20;
|
||||
|
||||
export const DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME = 'draggable-keyboard-wrapper';
|
||||
|
||||
export const ROW_RENDERER_CLASS_NAME = 'row-renderer';
|
||||
|
||||
export const NOTES_CONTAINER_CLASS_NAME = 'notes-container';
|
||||
|
||||
export const NOTE_CONTENT_CLASS_NAME = 'note-content';
|
||||
|
||||
/** This class is added to the document body while dragging */
|
||||
export const IS_DRAGGING_CLASS_NAME = 'is-dragging';
|
||||
|
||||
export const HOVER_ACTIONS_ALWAYS_SHOW_CLASS_NAME = 'hover-actions-always-show';
|
11
packages/kbn-securitysolution-t-grid/src/index.ts
Normal file
11
packages/kbn-securitysolution-t-grid/src/index.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export * from './constants';
|
||||
export * from './utils';
|
||||
export * from './mock';
|
9
packages/kbn-securitysolution-t-grid/src/mock/index.ts
Normal file
9
packages/kbn-securitysolution-t-grid/src/mock/index.ts
Normal file
|
@ -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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export * from './mock_event_details';
|
|
@ -1,8 +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.
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const eventHit = {
|
42
packages/kbn-securitysolution-t-grid/src/utils/api/index.ts
Normal file
42
packages/kbn-securitysolution-t-grid/src/utils/api/index.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { has } from 'lodash/fp';
|
||||
|
||||
export interface AppError extends Error {
|
||||
body: {
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface KibanaError extends AppError {
|
||||
body: {
|
||||
message: string;
|
||||
statusCode: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SecurityAppError extends AppError {
|
||||
body: {
|
||||
message: string;
|
||||
status_code: number;
|
||||
};
|
||||
}
|
||||
|
||||
export const isKibanaError = (error: unknown): error is KibanaError =>
|
||||
has('message', error) && has('body.message', error) && has('body.statusCode', error);
|
||||
|
||||
export const isSecurityAppError = (error: unknown): error is SecurityAppError =>
|
||||
has('message', error) && has('body.message', error) && has('body.status_code', error);
|
||||
|
||||
export const isAppError = (error: unknown): error is AppError =>
|
||||
isKibanaError(error) || isSecurityAppError(error);
|
||||
|
||||
export const isNotFoundError = (error: unknown) =>
|
||||
(isKibanaError(error) && error.body.statusCode === 404) ||
|
||||
(isSecurityAppError(error) && error.body.status_code === 404);
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { DropResult } from 'react-beautiful-dnd';
|
||||
|
||||
export const draggableIdPrefix = 'draggableId';
|
||||
|
||||
export const droppableIdPrefix = 'droppableId';
|
||||
|
||||
export const draggableContentPrefix = `${draggableIdPrefix}.content.`;
|
||||
|
||||
export const draggableTimelineProvidersPrefix = `${draggableIdPrefix}.timelineProviders.`;
|
||||
|
||||
export const draggableFieldPrefix = `${draggableIdPrefix}.field.`;
|
||||
|
||||
export const droppableContentPrefix = `${droppableIdPrefix}.content.`;
|
||||
|
||||
export const droppableFieldPrefix = `${droppableIdPrefix}.field.`;
|
||||
|
||||
export const droppableTimelineProvidersPrefix = `${droppableIdPrefix}.timelineProviders.`;
|
||||
|
||||
export const droppableTimelineColumnsPrefix = `${droppableIdPrefix}.timelineColumns.`;
|
||||
|
||||
export const droppableTimelineFlyoutBottomBarPrefix = `${droppableIdPrefix}.flyoutButton.`;
|
||||
|
||||
export const getDraggableId = (dataProviderId: string): string =>
|
||||
`${draggableContentPrefix}${dataProviderId}`;
|
||||
|
||||
export const getDraggableFieldId = ({
|
||||
contextId,
|
||||
fieldId,
|
||||
}: {
|
||||
contextId: string;
|
||||
fieldId: string;
|
||||
}): string => `${draggableFieldPrefix}${escapeContextId(contextId)}.${escapeFieldId(fieldId)}`;
|
||||
|
||||
export const getTimelineProviderDroppableId = ({
|
||||
groupIndex,
|
||||
timelineId,
|
||||
}: {
|
||||
groupIndex: number;
|
||||
timelineId: string;
|
||||
}): string => `${droppableTimelineProvidersPrefix}${timelineId}.group.${groupIndex}`;
|
||||
|
||||
export const getTimelineProviderDraggableId = ({
|
||||
dataProviderId,
|
||||
groupIndex,
|
||||
timelineId,
|
||||
}: {
|
||||
dataProviderId: string;
|
||||
groupIndex: number;
|
||||
timelineId: string;
|
||||
}): string =>
|
||||
`${draggableTimelineProvidersPrefix}${timelineId}.group.${groupIndex}.${dataProviderId}`;
|
||||
|
||||
export const getDroppableId = (visualizationPlaceholderId: string): string =>
|
||||
`${droppableContentPrefix}${visualizationPlaceholderId}`;
|
||||
|
||||
export const sourceIsContent = (result: DropResult): boolean =>
|
||||
result.source.droppableId.startsWith(droppableContentPrefix);
|
||||
|
||||
export const sourceAndDestinationAreSameTimelineProviders = (result: DropResult): boolean => {
|
||||
const regex = /^droppableId\.timelineProviders\.(\S+)\./;
|
||||
const sourceMatches = result.source.droppableId.match(regex) || [];
|
||||
const destinationMatches =
|
||||
(result.destination && result.destination.droppableId.match(regex)) || [];
|
||||
|
||||
return (
|
||||
sourceMatches.length >= 2 &&
|
||||
destinationMatches.length >= 2 &&
|
||||
sourceMatches[1] === destinationMatches[1]
|
||||
);
|
||||
};
|
||||
|
||||
export const draggableIsContent = (result: DropResult | { draggableId: string }): boolean =>
|
||||
result.draggableId.startsWith(draggableContentPrefix);
|
||||
|
||||
export const draggableIsField = (result: DropResult | { draggableId: string }): boolean =>
|
||||
result.draggableId.startsWith(draggableFieldPrefix);
|
||||
|
||||
export const reasonIsDrop = (result: DropResult): boolean => result.reason === 'DROP';
|
||||
|
||||
export const destinationIsTimelineProviders = (result: DropResult): boolean =>
|
||||
result.destination != null &&
|
||||
result.destination.droppableId.startsWith(droppableTimelineProvidersPrefix);
|
||||
|
||||
export const destinationIsTimelineColumns = (result: DropResult): boolean =>
|
||||
result.destination != null &&
|
||||
result.destination.droppableId.startsWith(droppableTimelineColumnsPrefix);
|
||||
|
||||
export const destinationIsTimelineButton = (result: DropResult): boolean =>
|
||||
result.destination != null &&
|
||||
result.destination.droppableId.startsWith(droppableTimelineFlyoutBottomBarPrefix);
|
||||
|
||||
export const getProviderIdFromDraggable = (result: DropResult): string =>
|
||||
result.draggableId.substring(result.draggableId.lastIndexOf('.') + 1);
|
||||
|
||||
export const getFieldIdFromDraggable = (result: DropResult): string =>
|
||||
unEscapeFieldId(result.draggableId.substring(result.draggableId.lastIndexOf('.') + 1));
|
||||
|
||||
export const escapeDataProviderId = (path: string) => path.replace(/\./g, '_');
|
||||
|
||||
export const escapeContextId = (path: string) => path.replace(/\./g, '_');
|
||||
|
||||
export const escapeFieldId = (path: string) => path.replace(/\./g, '!!!DOT!!!');
|
||||
|
||||
export const unEscapeFieldId = (path: string) => path.replace(/!!!DOT!!!/g, '.');
|
||||
|
||||
export const providerWasDroppedOnTimeline = (result: DropResult): boolean =>
|
||||
reasonIsDrop(result) &&
|
||||
draggableIsContent(result) &&
|
||||
sourceIsContent(result) &&
|
||||
destinationIsTimelineProviders(result);
|
||||
|
||||
export const userIsReArrangingProviders = (result: DropResult): boolean =>
|
||||
reasonIsDrop(result) && sourceAndDestinationAreSameTimelineProviders(result);
|
||||
|
||||
export const fieldWasDroppedOnTimelineColumns = (result: DropResult): boolean =>
|
||||
reasonIsDrop(result) && draggableIsField(result) && destinationIsTimelineColumns(result);
|
||||
|
||||
/**
|
||||
* Prevents fields from being dragged or dropped to any area other than column
|
||||
* header drop zone in the timeline
|
||||
*/
|
||||
export const DRAG_TYPE_FIELD = 'drag-type-field';
|
||||
|
||||
/** This class is added to the document body while timeline field dragging */
|
||||
export const IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME = 'is-timeline-field-dragging';
|
10
packages/kbn-securitysolution-t-grid/src/utils/index.ts
Normal file
10
packages/kbn-securitysolution-t-grid/src/utils/index.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export * from './api';
|
||||
export * from './drag_and_drop';
|
23
packages/kbn-securitysolution-t-grid/tsconfig.browser.json
Normal file
23
packages/kbn-securitysolution-t-grid/tsconfig.browser.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"extends": "../../tsconfig.browser.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"incremental": true,
|
||||
"outDir": "./target_web",
|
||||
"declaration": false,
|
||||
"isolatedModules": true,
|
||||
"sourceMap": true,
|
||||
"sourceRoot": "../../../../../packages/kbn-securitysolution-t-grid/src",
|
||||
"types": [
|
||||
"jest",
|
||||
"node"
|
||||
],
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"**/__fixtures__/**/*"
|
||||
]
|
||||
}
|
19
packages/kbn-securitysolution-t-grid/tsconfig.json
Normal file
19
packages/kbn-securitysolution-t-grid/tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"incremental": true,
|
||||
"outDir": "target",
|
||||
"rootDir": "src",
|
||||
"sourceMap": true,
|
||||
"sourceRoot": "../../../../packages/kbn-securitysolution-t-grid/src",
|
||||
"types": [
|
||||
"jest",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
|
@ -94,7 +94,7 @@ module.exports = {
|
|||
transformIgnorePatterns: [
|
||||
// ignore all node_modules except monaco-editor and react-monaco-editor which requires babel transforms to handle dynamic import()
|
||||
// since ESM modules are not natively supported in Jest yet (https://github.com/facebook/jest/issues/4842)
|
||||
'[/\\\\]node_modules(?)[/\\\\].+\\.js$',
|
||||
'[/\\\\]node_modules(?)[/\\\\].+\\.js$',
|
||||
'packages/kbn-pm/dist/index.js',
|
||||
],
|
||||
|
||||
|
|
|
@ -70,7 +70,6 @@
|
|||
{ "path": "./src/plugins/visualize/tsconfig.json" },
|
||||
{ "path": "./src/plugins/index_pattern_management/tsconfig.json" },
|
||||
{ "path": "./src/plugins/index_pattern_field_editor/tsconfig.json" },
|
||||
|
||||
{ "path": "./x-pack/plugins/actions/tsconfig.json" },
|
||||
{ "path": "./x-pack/plugins/alerting/tsconfig.json" },
|
||||
{ "path": "./x-pack/plugins/apm/tsconfig.json" },
|
||||
|
|
|
@ -105,6 +105,7 @@
|
|||
{ "path": "./x-pack/plugins/stack_alerts/tsconfig.json" },
|
||||
{ "path": "./x-pack/plugins/task_manager/tsconfig.json" },
|
||||
{ "path": "./x-pack/plugins/telemetry_collection_xpack/tsconfig.json" },
|
||||
{ "path": "./x-pack/plugins/timelines/tsconfig.json" },
|
||||
{ "path": "./x-pack/plugins/transform/tsconfig.json" },
|
||||
{ "path": "./x-pack/plugins/translations/tsconfig.json" },
|
||||
{ "path": "./x-pack/plugins/triggers_actions_ui/tsconfig.json" },
|
||||
|
|
|
@ -15,6 +15,7 @@ const allowedExperimentalValues = Object.freeze({
|
|||
trustedAppsByPolicyEnabled: false,
|
||||
metricsEntitiesEnabled: false,
|
||||
ruleRegistryEnabled: false,
|
||||
tGridEnabled: false,
|
||||
});
|
||||
|
||||
type ExperimentalConfigKeys = Array<keyof ExperimentalFeatures>;
|
||||
|
|
|
@ -4,3 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './types';
|
||||
export * from './search_strategy';
|
||||
export * from './utility_types';
|
||||
|
|
|
@ -4,52 +4,27 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
import { IEsSearchResponse } from '../../../../../../src/plugins/data/common';
|
||||
export type {
|
||||
Inspect,
|
||||
SortField,
|
||||
TimerangeInput,
|
||||
PaginationInputPaginated,
|
||||
DocValueFields,
|
||||
CursorType,
|
||||
TotalValue,
|
||||
} from '../../../../timelines/common';
|
||||
export { Direction } from '../../../../timelines/common';
|
||||
|
||||
export type Maybe<T> = T | null;
|
||||
|
||||
export type SearchHit = IEsSearchResponse<object>['rawResponse']['hits']['hits'][0];
|
||||
|
||||
export interface TotalValue {
|
||||
value: number;
|
||||
relation: string;
|
||||
}
|
||||
|
||||
export interface Inspect {
|
||||
dsl: string[];
|
||||
}
|
||||
|
||||
export interface PageInfoPaginated {
|
||||
activePage: number;
|
||||
fakeTotalCount: number;
|
||||
showMorePagesIndicator: boolean;
|
||||
}
|
||||
|
||||
export interface CursorType {
|
||||
value?: Maybe<string>;
|
||||
tiebreaker?: Maybe<string>;
|
||||
}
|
||||
|
||||
export enum Direction {
|
||||
asc = 'asc',
|
||||
desc = 'desc',
|
||||
}
|
||||
|
||||
export interface SortField<Field = string> {
|
||||
field: Field;
|
||||
direction: Direction;
|
||||
}
|
||||
|
||||
export interface TimerangeInput {
|
||||
/** The interval string to use for last bucket. The format is '{value}{unit}'. For example '5m' would return the metrics for the last 5 minutes of the timespan. */
|
||||
interval: string;
|
||||
/** The end of the timerange */
|
||||
to: string;
|
||||
/** The beginning of the timerange */
|
||||
from: string;
|
||||
}
|
||||
|
||||
export interface PaginationInput {
|
||||
/** The limit parameter allows you to configure the maximum amount of items to be returned */
|
||||
limit: number;
|
||||
|
@ -59,19 +34,6 @@ export interface PaginationInput {
|
|||
tiebreaker?: Maybe<string>;
|
||||
}
|
||||
|
||||
export interface PaginationInputPaginated {
|
||||
/** The activePage parameter defines the page of results you want to fetch */
|
||||
activePage: number;
|
||||
/** The cursorStart parameter defines the start of the results to be displayed */
|
||||
cursorStart: number;
|
||||
/** The fakePossibleCount parameter determines the total count in order to show 5 additional pages */
|
||||
fakePossibleCount: number;
|
||||
/** The querySize parameter is the number of items to be returned */
|
||||
querySize: number;
|
||||
}
|
||||
|
||||
export type DocValueFields = estypes.SearchDocValueField;
|
||||
|
||||
export interface Explanation {
|
||||
value: number;
|
||||
description: string;
|
||||
|
@ -111,13 +73,3 @@ export interface GenericBuckets {
|
|||
}
|
||||
|
||||
export type StringOrNumber = string | number;
|
||||
|
||||
export interface TimerangeFilter {
|
||||
range: {
|
||||
[timestamp: string]: {
|
||||
gte: string;
|
||||
lte: string;
|
||||
format: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
export * from './common';
|
||||
export * from './security_solution';
|
||||
export * from './timeline';
|
||||
export * from './index_fields';
|
||||
|
|
|
@ -5,37 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/common';
|
||||
import { Ecs } from '../../../../ecs';
|
||||
import { CursorType, Inspect, Maybe, PaginationInputPaginated } from '../../../common';
|
||||
import { TimelineRequestOptionsPaginated } from '../..';
|
||||
|
||||
export interface TimelineEdges {
|
||||
node: TimelineItem;
|
||||
cursor: CursorType;
|
||||
}
|
||||
|
||||
export interface TimelineItem {
|
||||
_id: string;
|
||||
_index?: Maybe<string>;
|
||||
data: TimelineNonEcsData[];
|
||||
ecs: Ecs;
|
||||
}
|
||||
|
||||
export interface TimelineNonEcsData {
|
||||
field: string;
|
||||
value?: Maybe<string[]>;
|
||||
}
|
||||
|
||||
export interface TimelineEventsAllStrategyResponse extends IEsSearchResponse {
|
||||
edges: TimelineEdges[];
|
||||
totalCount: number;
|
||||
pageInfo: Pick<PaginationInputPaginated, 'activePage' | 'querySize'>;
|
||||
inspect?: Maybe<Inspect>;
|
||||
}
|
||||
|
||||
export interface TimelineEventsAllRequestOptions extends TimelineRequestOptionsPaginated {
|
||||
fields: string[] | Array<{ field: string; include_unmapped: boolean }>;
|
||||
fieldRequested: string[];
|
||||
language: 'eql' | 'kuery' | 'lucene';
|
||||
}
|
||||
export type {
|
||||
TimelineEdges,
|
||||
TimelineItem,
|
||||
TimelineNonEcsData,
|
||||
TimelineEventsAllStrategyResponse,
|
||||
TimelineEventsAllRequestOptions,
|
||||
} from '../../../../../../timelines/common';
|
||||
|
|
|
@ -5,22 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Ecs } from '../../../../ecs';
|
||||
import { CursorType, Maybe } from '../../../common';
|
||||
|
||||
export interface TimelineEdges {
|
||||
node: TimelineItem;
|
||||
cursor: CursorType;
|
||||
}
|
||||
|
||||
export interface TimelineItem {
|
||||
_id: string;
|
||||
_index?: Maybe<string>;
|
||||
data: TimelineNonEcsData[];
|
||||
ecs: Ecs;
|
||||
}
|
||||
|
||||
export interface TimelineNonEcsData {
|
||||
field: string;
|
||||
value?: Maybe<string[] | string>;
|
||||
}
|
||||
export type {
|
||||
TimelineEdges,
|
||||
TimelineItem,
|
||||
TimelineNonEcsData,
|
||||
} from '../../../../../../timelines/common';
|
||||
|
|
|
@ -5,27 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/common';
|
||||
import { Inspect, Maybe } from '../../../common';
|
||||
import { TimelineRequestOptionsPaginated } from '../..';
|
||||
|
||||
export interface TimelineEventsDetailsItem {
|
||||
ariaRowindex?: Maybe<number>;
|
||||
category?: string;
|
||||
field: string;
|
||||
values?: Maybe<string[]>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
originalValue?: Maybe<any>;
|
||||
isObjectArray: boolean;
|
||||
}
|
||||
|
||||
export interface TimelineEventsDetailsStrategyResponse extends IEsSearchResponse {
|
||||
data?: Maybe<TimelineEventsDetailsItem[]>;
|
||||
inspect?: Maybe<Inspect>;
|
||||
}
|
||||
|
||||
export interface TimelineEventsDetailsRequestOptions
|
||||
extends Partial<TimelineRequestOptionsPaginated> {
|
||||
indexName: string;
|
||||
eventId: string;
|
||||
}
|
||||
export type {
|
||||
TimelineEventsDetailsItem,
|
||||
TimelineEventsDetailsStrategyResponse,
|
||||
TimelineEventsDetailsRequestOptions,
|
||||
} from '../../../../../../timelines/common';
|
||||
|
|
|
@ -5,43 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiComboBoxOptionOption } from '@elastic/eui';
|
||||
import {
|
||||
EqlSearchStrategyRequest,
|
||||
EqlSearchStrategyResponse,
|
||||
} from '../../../../../../../../src/plugins/data/common';
|
||||
import { Inspect, Maybe, PaginationInputPaginated } from '../../..';
|
||||
import { TimelineEdges, TimelineEventsAllRequestOptions } from '../..';
|
||||
import { EqlSearchResponse } from '../../../../detection_engine/types';
|
||||
|
||||
export interface TimelineEqlRequestOptions
|
||||
extends EqlSearchStrategyRequest,
|
||||
Omit<TimelineEventsAllRequestOptions, 'params'> {
|
||||
eventCategoryField?: string;
|
||||
tiebreakerField?: string;
|
||||
timestampField?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export interface TimelineEqlResponse extends EqlSearchStrategyResponse<EqlSearchResponse<unknown>> {
|
||||
edges: TimelineEdges[];
|
||||
totalCount: number;
|
||||
pageInfo: Pick<PaginationInputPaginated, 'activePage' | 'querySize'>;
|
||||
inspect: Maybe<Inspect>;
|
||||
}
|
||||
|
||||
export interface EqlOptionsData {
|
||||
keywordFields: EuiComboBoxOptionOption[];
|
||||
dateFields: EuiComboBoxOptionOption[];
|
||||
nonDateFields: EuiComboBoxOptionOption[];
|
||||
}
|
||||
|
||||
export interface EqlOptionsSelected {
|
||||
eventCategoryField?: string;
|
||||
tiebreakerField?: string;
|
||||
timestampField?: string;
|
||||
query?: string;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
export type FieldsEqlOptions = keyof EqlOptionsSelected;
|
||||
export type {
|
||||
TimelineEqlRequestOptions,
|
||||
TimelineEqlResponse,
|
||||
EqlOptionsData,
|
||||
EqlOptionsSelected,
|
||||
FieldsEqlOptions,
|
||||
} from '../../../../../../timelines/common';
|
||||
|
|
|
@ -5,38 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/common';
|
||||
import { Inspect, Maybe } from '../../../common';
|
||||
import { TimelineRequestBasicOptions } from '../..';
|
||||
export { LastEventIndexKey } from '../../../../../../timelines/common';
|
||||
|
||||
export enum LastEventIndexKey {
|
||||
hostDetails = 'hostDetails',
|
||||
hosts = 'hosts',
|
||||
ipDetails = 'ipDetails',
|
||||
network = 'network',
|
||||
}
|
||||
|
||||
export interface LastTimeDetails {
|
||||
hostName?: Maybe<string>;
|
||||
ip?: Maybe<string>;
|
||||
}
|
||||
|
||||
export interface TimelineEventsLastEventTimeStrategyResponse extends IEsSearchResponse {
|
||||
lastSeen: Maybe<string>;
|
||||
inspect?: Maybe<Inspect>;
|
||||
}
|
||||
|
||||
export interface TimelineKpiStrategyResponse extends IEsSearchResponse {
|
||||
destinationIpCount: number;
|
||||
inspect?: Maybe<Inspect>;
|
||||
hostCount: number;
|
||||
processCount: number;
|
||||
sourceIpCount: number;
|
||||
userCount: number;
|
||||
}
|
||||
|
||||
export interface TimelineEventsLastEventTimeRequestOptions
|
||||
extends Omit<TimelineRequestBasicOptions, 'filterQuery' | 'timerange'> {
|
||||
indexKey: LastEventIndexKey;
|
||||
details: LastTimeDetails;
|
||||
}
|
||||
export type {
|
||||
LastTimeDetails,
|
||||
TimelineEventsLastEventTimeStrategyResponse,
|
||||
TimelineKpiStrategyResponse,
|
||||
TimelineEventsLastEventTimeRequestOptions,
|
||||
} from '../../../../../../timelines/common';
|
||||
|
|
|
@ -24,7 +24,12 @@ import {
|
|||
SortField,
|
||||
Maybe,
|
||||
} from '../common';
|
||||
import { DataProviderType, TimelineType, TimelineStatus } from '../../types/timeline';
|
||||
import {
|
||||
DataProviderType,
|
||||
TimelineType,
|
||||
TimelineStatus,
|
||||
RowRendererId,
|
||||
} from '../../types/timeline';
|
||||
|
||||
export * from './events';
|
||||
|
||||
|
@ -165,25 +170,6 @@ export interface SortTimelineInput {
|
|||
sortDirection?: Maybe<string>;
|
||||
}
|
||||
|
||||
export enum RowRendererId {
|
||||
alerts = 'alerts',
|
||||
auditd = 'auditd',
|
||||
auditd_file = 'auditd_file',
|
||||
library = 'library',
|
||||
netflow = 'netflow',
|
||||
plain = 'plain',
|
||||
registry = 'registry',
|
||||
suricata = 'suricata',
|
||||
system = 'system',
|
||||
system_dns = 'system_dns',
|
||||
system_endgame_process = 'system_endgame_process',
|
||||
system_file = 'system_file',
|
||||
system_fim = 'system_fim',
|
||||
system_security_event = 'system_security_event',
|
||||
system_socket = 'system_socket',
|
||||
zeek = 'zeek',
|
||||
}
|
||||
|
||||
export interface TimelineInput {
|
||||
columns?: Maybe<ColumnHeaderInput[]>;
|
||||
dataProviders?: Maybe<DataProviderInput[]>;
|
||||
|
|
8
x-pack/plugins/security_solution/common/types/index.ts
Normal file
8
x-pack/plugins/security_solution/common/types/index.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './timeline';
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
export type {
|
||||
ActionProps,
|
||||
HeaderActionProps,
|
||||
GenericActionRowCellRenderProps,
|
||||
HeaderCellRender,
|
||||
RowCellRender,
|
||||
ControlColumnProps,
|
||||
} from '../../../../../timelines/common';
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type { CellValueElementProps } from '../../../../../timelines/common';
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type {
|
||||
ColumnHeaderType,
|
||||
ColumnId,
|
||||
ColumnHeaderOptions,
|
||||
ColumnRenderer,
|
||||
} from '../../../../../timelines/common';
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
export { IS_OPERATOR, EXISTS_OPERATOR } from '../../../../../timelines/common';
|
||||
|
||||
export type {
|
||||
QueryOperator,
|
||||
DataProviderType,
|
||||
QueryMatch,
|
||||
DataProvider,
|
||||
DataProvidersAnd,
|
||||
} from '../../../../../timelines/common';
|
|
@ -23,6 +23,13 @@ import { FlowTarget } from '../../search_strategy/security_solution/network';
|
|||
import { errorSchema } from '../../detection_engine/schemas/response/error_schema';
|
||||
import { Direction, Maybe } from '../../search_strategy';
|
||||
|
||||
export * from './actions';
|
||||
export * from './cells';
|
||||
export * from './columns';
|
||||
export * from './data_provider';
|
||||
export * from './rows';
|
||||
export * from './store';
|
||||
|
||||
/*
|
||||
* ColumnHeader Types
|
||||
*/
|
||||
|
@ -492,6 +499,11 @@ export type TimelineExpandedDetail = {
|
|||
[tab in TimelineTabs]?: TimelineExpandedDetailType;
|
||||
};
|
||||
|
||||
export type ToggleDetailPanel = TimelineExpandedDetailType & {
|
||||
tabType?: TimelineTabs;
|
||||
timelineId: string;
|
||||
};
|
||||
|
||||
export const pageInfoTimeline = runtimeTypes.type({
|
||||
pageIndex: runtimeTypes.number,
|
||||
pageSize: runtimeTypes.number,
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
export type { RowRenderer } from '../../../../../timelines/common';
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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 {
|
||||
ColumnHeaderOptions,
|
||||
ColumnId,
|
||||
RowRendererId,
|
||||
TimelineExpandedDetail,
|
||||
TimelineTypeLiteral,
|
||||
} from '.';
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { Filter } from '../../../../../../src/plugins/data/public';
|
||||
|
||||
import { Direction } from '../../search_strategy';
|
||||
import { DataProvider } from './data_provider';
|
||||
|
||||
export type KueryFilterQueryKind = 'kuery' | 'lucene' | 'eql';
|
||||
|
||||
export interface KueryFilterQuery {
|
||||
kind: KueryFilterQueryKind;
|
||||
expression: string;
|
||||
}
|
||||
|
||||
export interface SerializedFilterQuery {
|
||||
kuery: KueryFilterQuery | null;
|
||||
serializedQuery: string;
|
||||
}
|
||||
|
||||
export type SortDirection = 'none' | 'asc' | 'desc' | Direction;
|
||||
export interface SortColumnTimeline {
|
||||
columnId: string;
|
||||
columnType: string;
|
||||
sortDirection: SortDirection;
|
||||
}
|
||||
|
||||
export interface TimelinePersistInput {
|
||||
id: string;
|
||||
dataProviders?: DataProvider[];
|
||||
dateRange?: {
|
||||
start: string;
|
||||
end: string;
|
||||
};
|
||||
excludedRowRendererIds?: RowRendererId[];
|
||||
expandedDetail?: TimelineExpandedDetail;
|
||||
filters?: Filter[];
|
||||
columns: ColumnHeaderOptions[];
|
||||
itemsPerPage?: number;
|
||||
indexNames: string[];
|
||||
kqlQuery?: {
|
||||
filterQuery: SerializedFilterQuery | null;
|
||||
};
|
||||
show?: boolean;
|
||||
sort?: SortColumnTimeline[];
|
||||
showCheckboxes?: boolean;
|
||||
timelineType?: TimelineTypeLiteral;
|
||||
templateTimelineId?: string | null;
|
||||
templateTimelineVersion?: number | null;
|
||||
}
|
||||
|
||||
/** Invoked when a column is sorted */
|
||||
export type OnColumnSorted = (sorted: { columnId: ColumnId; sortDirection: SortDirection }) => void;
|
||||
|
||||
export type OnColumnsSorted = (
|
||||
sorted: Array<{ columnId: ColumnId; sortDirection: SortDirection }>
|
||||
) => void;
|
||||
|
||||
export type OnColumnRemoved = (columnId: ColumnId) => void;
|
||||
|
||||
export type OnColumnResized = ({ columnId, delta }: { columnId: ColumnId; delta: number }) => void;
|
||||
|
||||
/** Invoked when a user clicks to load more item */
|
||||
export type OnChangePage = (nextPage: number) => void;
|
||||
|
||||
/** Invoked when a user checks/un-checks a row */
|
||||
export type OnRowSelected = ({
|
||||
eventIds,
|
||||
isSelected,
|
||||
}: {
|
||||
eventIds: string[];
|
||||
isSelected: boolean;
|
||||
}) => void;
|
||||
|
||||
/** Invoked when a user checks/un-checks the select all checkbox */
|
||||
export type OnSelectAll = ({ isSelected }: { isSelected: boolean }) => void;
|
||||
|
||||
/** Invoked when columns are updated */
|
||||
export type OnUpdateColumns = (columns: ColumnHeaderOptions[]) => void;
|
||||
|
||||
/** Invoked when a user pins an event */
|
||||
export type OnPinEvent = (eventId: string) => void;
|
||||
|
||||
/** Invoked when a user unpins an event */
|
||||
export type OnUnPinEvent = (eventId: string) => void;
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { EventHit, EventSource } from '../search_strategy';
|
||||
import { getDataFromFieldsHits, getDataFromSourceHits, getDataSafety } from './field_formatters';
|
||||
import { eventDetailsFormattedFields, eventHit } from './mock_event_details';
|
||||
import { eventDetailsFormattedFields, eventHit } from '@kbn/securitysolution-t-grid';
|
||||
|
||||
describe('Events Details Helpers', () => {
|
||||
const fields: EventHit['fields'] = eventHit.fields;
|
||||
|
|
|
@ -45,7 +45,7 @@ describe('Overview Page', () => {
|
|||
|
||||
describe('with no data', () => {
|
||||
it('Splash screen should be here', () => {
|
||||
cy.stubSearchStrategyApi(emptyInstance, undefined, 'securitySolutionIndexFields');
|
||||
cy.stubSearchStrategyApi(emptyInstance, undefined, 'indexFields');
|
||||
loginAndWaitForPage(OVERVIEW_URL);
|
||||
cy.get(OVERVIEW_EMPTY_PAGE).should('be.visible');
|
||||
});
|
||||
|
|
|
@ -35,7 +35,7 @@ Cypress.Commands.add(
|
|||
'stubSearchStrategyApi',
|
||||
function (stubObject, factoryQueryType, searchStrategyName = 'securitySolutionSearchStrategy') {
|
||||
cy.intercept('POST', '/internal/bsearch', (req) => {
|
||||
if (searchStrategyName === 'securitySolutionIndexFields') {
|
||||
if (searchStrategyName === 'indexFields') {
|
||||
req.reply(stubObject.rawResponse);
|
||||
} else if (factoryQueryType === 'overviewHost') {
|
||||
req.reply(stubObject.overviewHost);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"inspector",
|
||||
"licensing",
|
||||
"maps",
|
||||
"timelines",
|
||||
"triggersActionsUi",
|
||||
"uiActions"
|
||||
],
|
||||
|
|
|
@ -21,7 +21,6 @@ import { GlobalToaster, ManageGlobalToaster } from '../common/components/toaster
|
|||
import { KibanaContextProvider, useKibana, useUiSetting$ } from '../common/lib/kibana';
|
||||
import { State } from '../common/store';
|
||||
|
||||
import { ManageGlobalTimeline } from '../timelines/components/manage_timeline';
|
||||
import { StartServices } from '../types';
|
||||
import { PageRouter } from './routes';
|
||||
import { EuiThemeProvider } from '../../../../../src/plugins/kibana_react/common';
|
||||
|
@ -42,23 +41,21 @@ const StartAppComponent: FC<StartAppComponent> = ({ children, history, onAppLeav
|
|||
<EuiErrorBoundary>
|
||||
<i18n.Context>
|
||||
<ManageGlobalToaster>
|
||||
<ManageGlobalTimeline>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<EuiThemeProvider darkMode={darkMode}>
|
||||
<MlCapabilitiesProvider>
|
||||
<UserPrivilegesProvider>
|
||||
<ManageUserInfo>
|
||||
<PageRouter history={history} onAppLeave={onAppLeave}>
|
||||
{children}
|
||||
</PageRouter>
|
||||
</ManageUserInfo>
|
||||
</UserPrivilegesProvider>
|
||||
</MlCapabilitiesProvider>
|
||||
</EuiThemeProvider>
|
||||
<ErrorToastDispatcher />
|
||||
<GlobalToaster />
|
||||
</ReduxStoreProvider>
|
||||
</ManageGlobalTimeline>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<EuiThemeProvider darkMode={darkMode}>
|
||||
<MlCapabilitiesProvider>
|
||||
<UserPrivilegesProvider>
|
||||
<ManageUserInfo>
|
||||
<PageRouter history={history} onAppLeave={onAppLeave}>
|
||||
{children}
|
||||
</PageRouter>
|
||||
</ManageUserInfo>
|
||||
</UserPrivilegesProvider>
|
||||
</MlCapabilitiesProvider>
|
||||
</EuiThemeProvider>
|
||||
<ErrorToastDispatcher />
|
||||
<GlobalToaster />
|
||||
</ReduxStoreProvider>
|
||||
</ManageGlobalToaster>
|
||||
</i18n.Context>
|
||||
</EuiErrorBoundary>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './tooltip_with_keyboard_shortcut';
|
|
@ -10,7 +10,7 @@ import React from 'react';
|
|||
|
||||
import * as i18n from './translations';
|
||||
|
||||
interface Props {
|
||||
export interface TooltipWithKeyboardShortcutProps {
|
||||
additionalScreenReaderOnlyContext?: string;
|
||||
content: React.ReactNode;
|
||||
shortcut: string;
|
||||
|
@ -22,7 +22,7 @@ const TooltipWithKeyboardShortcutComponent = ({
|
|||
content,
|
||||
shortcut,
|
||||
showShortcut,
|
||||
}: Props) => (
|
||||
}: TooltipWithKeyboardShortcutProps) => (
|
||||
<>
|
||||
<div data-test-subj="content">{content}</div>
|
||||
{additionalScreenReaderOnlyContext !== '' && (
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
*/
|
||||
|
||||
import React, { useEffect, useMemo } from 'react';
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { timelineActions } from '../../../timelines/store/timeline';
|
||||
import { Filter } from '../../../../../../../src/plugins/data/public';
|
||||
import { TimelineIdLiteral } from '../../../../common/types/timeline';
|
||||
import { StatefulEventsViewer } from '../events_viewer';
|
||||
import { alertsDefaultModel } from './default_headers';
|
||||
import { useManageTimeline } from '../../../timelines/components/manage_timeline';
|
||||
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
|
||||
import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell_rendering/default_cell_renderer';
|
||||
import * as i18n from './translations';
|
||||
|
@ -70,22 +70,24 @@ const AlertsTableComponent: React.FC<Props> = ({
|
|||
startDate,
|
||||
pageFilters = [],
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const alertsFilter = useMemo(() => [...defaultAlertsFilters, ...pageFilters], [pageFilters]);
|
||||
const { filterManager } = useKibana().services.data.query;
|
||||
const { initializeTimeline } = useManageTimeline();
|
||||
|
||||
useEffect(() => {
|
||||
initializeTimeline({
|
||||
id: timelineId,
|
||||
documentType: i18n.ALERTS_DOCUMENT_TYPE,
|
||||
filterManager,
|
||||
defaultModel: alertsDefaultModel,
|
||||
footerText: i18n.TOTAL_COUNT_OF_ALERTS,
|
||||
title: i18n.ALERTS_TABLE_TITLE,
|
||||
unit: i18n.UNIT,
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
dispatch(
|
||||
timelineActions.initializeTGridSettings({
|
||||
id: timelineId,
|
||||
documentType: i18n.ALERTS_DOCUMENT_TYPE,
|
||||
filterManager,
|
||||
defaultColumns: alertsDefaultModel.columns,
|
||||
excludedRowRendererIds: alertsDefaultModel.excludedRowRendererIds,
|
||||
footerText: i18n.TOTAL_COUNT_OF_ALERTS,
|
||||
title: i18n.ALERTS_TABLE_TITLE,
|
||||
// TODO: avoid passing this through the store
|
||||
})
|
||||
);
|
||||
}, [dispatch, filterManager, timelineId]);
|
||||
|
||||
return (
|
||||
<StatefulEventsViewer
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { RowRendererId } from '../../../../common/types/timeline';
|
||||
import { ColumnHeaderOptions, RowRendererId } from '../../../../common/types/timeline';
|
||||
import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
import {
|
||||
DEFAULT_COLUMN_MIN_WIDTH,
|
||||
DEFAULT_DATE_COLUMN_MIN_WIDTH,
|
||||
} from '../../../timelines/components/timeline/body/constants';
|
||||
import { ColumnHeaderOptions, SubsetTimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
|
||||
export const alertsHeaders: ColumnHeaderOptions[] = [
|
||||
|
|
|
@ -15,6 +15,8 @@ import { TestProviders } from '../../mock';
|
|||
import { MIN_LEGEND_HEIGHT, DraggableLegend } from './draggable_legend';
|
||||
import { LegendItem } from './draggable_legend_item';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
jest.mock('@elastic/eui', () => {
|
||||
const original = jest.requireActual('@elastic/eui');
|
||||
return {
|
||||
|
|
|
@ -14,6 +14,8 @@ import { TestProviders } from '../../mock';
|
|||
|
||||
import { DraggableLegendItem, LegendItem } from './draggable_legend_item';
|
||||
|
||||
jest.mock('../../../common/lib/kibana');
|
||||
|
||||
jest.mock('@elastic/eui', () => {
|
||||
const original = jest.requireActual('@elastic/eui');
|
||||
return {
|
||||
|
|
|
@ -13,6 +13,8 @@ import { TestProviders } from '../../mock';
|
|||
|
||||
import { DragDropContextWrapper } from './drag_drop_context_wrapper';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
describe('DragDropContextWrapper', () => {
|
||||
describe('rendering', () => {
|
||||
test('it renders against the snapshot', () => {
|
||||
|
|
|
@ -11,6 +11,7 @@ import { DropResult, DragDropContext } from 'react-beautiful-dnd';
|
|||
import { useDispatch } from 'react-redux';
|
||||
import { Dispatch } from 'redux';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid';
|
||||
|
||||
import { BeforeCapture } from './drag_drop_context';
|
||||
import { BrowserFields } from '../../containers/source';
|
||||
|
@ -23,22 +24,24 @@ import {
|
|||
ADDED_TO_TIMELINE_MESSAGE,
|
||||
ADDED_TO_TIMELINE_TEMPLATE_MESSAGE,
|
||||
} from '../../hooks/translations';
|
||||
import { useAddToTimelineSensor } from '../../hooks/use_add_to_timeline';
|
||||
import { displaySuccessToast, useStateToaster } from '../toasters';
|
||||
import { TimelineId, TimelineType } from '../../../../common/types/timeline';
|
||||
import {
|
||||
addFieldToTimelineColumns,
|
||||
addProviderToTimeline,
|
||||
fieldWasDroppedOnTimelineColumns,
|
||||
getTimelineIdFromColumnDroppableId,
|
||||
IS_DRAGGING_CLASS_NAME,
|
||||
IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME,
|
||||
providerWasDroppedOnTimeline,
|
||||
draggableIsField,
|
||||
userIsReArrangingProviders,
|
||||
} from './helpers';
|
||||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
import {
|
||||
addFieldToTimelineColumns,
|
||||
getTimelineIdFromColumnDroppableId,
|
||||
} from '../../../../../timelines/public';
|
||||
import { alertsHeaders } from '../alerts_viewer/default_headers';
|
||||
|
||||
// @ts-expect-error
|
||||
window['__react-beautiful-dnd-disable-dev-warnings'] = true;
|
||||
|
@ -85,6 +88,7 @@ const onDragEndHandler = ({
|
|||
} else if (fieldWasDroppedOnTimelineColumns(result)) {
|
||||
addFieldToTimelineColumns({
|
||||
browserFields,
|
||||
defaultsHeader: alertsHeaders,
|
||||
dispatch,
|
||||
result,
|
||||
timelineId: getTimelineIdFromColumnDroppableId(result.destination?.droppableId ?? ''),
|
||||
|
@ -92,8 +96,6 @@ const onDragEndHandler = ({
|
|||
}
|
||||
};
|
||||
|
||||
const sensors = [useAddToTimelineSensor];
|
||||
|
||||
/**
|
||||
* DragDropContextWrapperComponent handles all drag end events
|
||||
*/
|
||||
|
@ -101,7 +103,8 @@ export const DragDropContextWrapperComponent: React.FC<Props> = ({ browserFields
|
|||
const dispatch = useDispatch();
|
||||
const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []);
|
||||
const getDataProviders = useMemo(() => dragAndDropSelectors.getDataProvidersSelector(), []);
|
||||
|
||||
const { timelines } = useKibana().services;
|
||||
const sensors = [timelines.getUseAddToTimelineSensor()];
|
||||
const {
|
||||
dataProviders: activeTimelineDataProviders,
|
||||
timelineType,
|
||||
|
|
|
@ -17,6 +17,8 @@ import { DragDropContextWrapper } from './drag_drop_context_wrapper';
|
|||
import { ConditionalPortal, DraggableWrapper, getStyle } from './draggable_wrapper';
|
||||
import { useMountAppended } from '../../utils/use_mount_appended';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
jest.mock('@elastic/eui', () => {
|
||||
const original = jest.requireActual('@elastic/eui');
|
||||
return {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { EuiScreenReaderOnly } from '@elastic/eui';
|
||||
import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME } from '@kbn/securitysolution-t-grid';
|
||||
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
||||
import {
|
||||
Draggable,
|
||||
|
@ -24,12 +25,12 @@ import { ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID } from '../../../timelines/com
|
|||
|
||||
import { TruncatableText } from '../truncatable_text';
|
||||
import { WithHoverActions } from '../with_hover_actions';
|
||||
import { useDraggableKeyboardWrapper } from './draggable_keyboard_wrapper_hook';
|
||||
import { DraggableWrapperHoverContent, useGetTimelineId } from './draggable_wrapper_hover_content';
|
||||
import { DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME, getDraggableId, getDroppableId } from './helpers';
|
||||
import { getDraggableId, getDroppableId } from './helpers';
|
||||
import { ProviderContainer } from './provider_container';
|
||||
|
||||
import * as i18n from './translations';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
|
||||
// As right now, we do not know what we want there, we will keep it as a placeholder
|
||||
export const DragEffects = styled.div``;
|
||||
|
@ -142,6 +143,7 @@ const DraggableWrapperComponent: React.FC<Props> = ({
|
|||
const isDisabled = dataProvider.id.includes(`-${ROW_RENDERER_BROWSER_EXAMPLE_TIMELINE_ID}-`);
|
||||
const [hoverActionsOwnFocus, setHoverActionsOwnFocus] = useState<boolean>(false);
|
||||
const dispatch = useDispatch();
|
||||
const { timelines } = useKibana().services;
|
||||
|
||||
const handleClosePopOverTrigger = useCallback(() => {
|
||||
setClosePopOverTrigger((prevClosePopOverTrigger) => !prevClosePopOverTrigger);
|
||||
|
@ -297,7 +299,7 @@ const DraggableWrapperComponent: React.FC<Props> = ({
|
|||
setHoverActionsOwnFocus(true);
|
||||
}, []);
|
||||
|
||||
const { onBlur, onKeyDown } = useDraggableKeyboardWrapper({
|
||||
const { onBlur, onKeyDown } = timelines.getUseDraggableKeyboardWrapper()({
|
||||
closePopover: handleClosePopOverTrigger,
|
||||
draggableId: getDraggableId(dataProvider.id),
|
||||
fieldName: dataProvider.queryMatch.field,
|
||||
|
|
|
@ -17,14 +17,10 @@ import { TestProviders } from '../../mock';
|
|||
import { FilterManager } from '../../../../../../../src/plugins/data/public';
|
||||
import { useSourcererScope } from '../../containers/sourcerer';
|
||||
import { DraggableWrapperHoverContent } from './draggable_wrapper_hover_content';
|
||||
import {
|
||||
ManageGlobalTimeline,
|
||||
getTimelineDefaults,
|
||||
} from '../../../timelines/components/manage_timeline';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { useDeepEqualSelector } from '../../../common/hooks/use_selector';
|
||||
|
||||
jest.mock('../link_to');
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
jest.mock('../../containers/sourcerer', () => {
|
||||
const original = jest.requireActual('../../containers/sourcerer');
|
||||
|
@ -42,29 +38,18 @@ jest.mock('uuid', () => {
|
|||
};
|
||||
});
|
||||
const mockStartDragToTimeline = jest.fn();
|
||||
jest.mock('../../hooks/use_add_to_timeline', () => {
|
||||
const original = jest.requireActual('../../hooks/use_add_to_timeline');
|
||||
jest.mock('../../../../../timelines/public/hooks/use_add_to_timeline', () => {
|
||||
const original = jest.requireActual('../../../../../timelines/public/hooks/use_add_to_timeline');
|
||||
return {
|
||||
...original,
|
||||
useAddToTimeline: () => ({ startDragToTimeline: mockStartDragToTimeline }),
|
||||
};
|
||||
});
|
||||
const mockAddFilters = jest.fn();
|
||||
const mockGetTimelineFilterManager = jest.fn().mockReturnValue({
|
||||
addFilters: mockAddFilters,
|
||||
});
|
||||
jest.mock('../../../timelines/components/manage_timeline', () => {
|
||||
const original = jest.requireActual('../../../timelines/components/manage_timeline');
|
||||
|
||||
return {
|
||||
...original,
|
||||
useManageTimeline: () => ({
|
||||
getManageTimelineById: jest.fn().mockReturnValue({ indexToAdd: [] }),
|
||||
getTimelineFilterManager: mockGetTimelineFilterManager,
|
||||
isManagedTimeline: jest.fn().mockReturnValue(false),
|
||||
}),
|
||||
};
|
||||
});
|
||||
jest.mock('../../../common/hooks/use_selector', () => ({
|
||||
useShallowEqualSelector: jest.fn(),
|
||||
useDeepEqualSelector: jest.fn(),
|
||||
}));
|
||||
|
||||
const mockUiSettingsForFilterManager = coreMock.createStart().uiSettings;
|
||||
const timelineId = TimelineId.active;
|
||||
|
@ -85,6 +70,9 @@ const defaultProps = {
|
|||
describe('DraggableWrapperHoverContent', () => {
|
||||
beforeAll(() => {
|
||||
mockStartDragToTimeline.mockReset();
|
||||
(useDeepEqualSelector as jest.Mock).mockReturnValue({
|
||||
filterManager: { addFilters: mockAddFilters },
|
||||
});
|
||||
(useSourcererScope as jest.Mock).mockReturnValue({
|
||||
browserFields: mockBrowserFields,
|
||||
selectedPatterns: [],
|
||||
|
@ -144,15 +132,10 @@ describe('DraggableWrapperHoverContent', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
onFilterAdded = jest.fn();
|
||||
const manageTimelineForTesting = {
|
||||
[timelineId]: getTimelineDefaults(timelineId),
|
||||
};
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}>
|
||||
<DraggableWrapperHoverContent {...{ ...defaultProps, onFilterAdded }} />
|
||||
</ManageGlobalTimeline>
|
||||
<DraggableWrapperHoverContent {...{ ...defaultProps, onFilterAdded }} />
|
||||
</TestProviders>
|
||||
);
|
||||
});
|
||||
|
@ -237,18 +220,9 @@ describe('DraggableWrapperHoverContent', () => {
|
|||
filterManager.addFilters = jest.fn();
|
||||
onFilterAdded = jest.fn();
|
||||
|
||||
const manageTimelineForTesting = {
|
||||
[timelineId]: {
|
||||
...getTimelineDefaults(timelineId),
|
||||
filterManager,
|
||||
},
|
||||
};
|
||||
|
||||
wrapper = mount(
|
||||
<TestProviders>
|
||||
<ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}>
|
||||
<DraggableWrapperHoverContent {...{ ...defaultProps, onFilterAdded, value: '' }} />
|
||||
</ManageGlobalTimeline>
|
||||
<DraggableWrapperHoverContent {...{ ...defaultProps, onFilterAdded, value: '' }} />
|
||||
</TestProviders>
|
||||
);
|
||||
});
|
||||
|
@ -586,39 +560,4 @@ describe('DraggableWrapperHoverContent', () => {
|
|||
expect(wrapper.find(`[data-test-subj="copy-to-clipboard"]`).first().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Filter Manager', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
test('filter manager, not active timeline', () => {
|
||||
mount(
|
||||
<TestProviders>
|
||||
<DraggableWrapperHoverContent {...{ ...defaultProps, timelineId: TimelineId.test }} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(mockGetTimelineFilterManager).not.toBeCalled();
|
||||
});
|
||||
test('filter manager, active timeline', () => {
|
||||
mount(
|
||||
<TestProviders>
|
||||
<DraggableWrapperHoverContent {...defaultProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(mockGetTimelineFilterManager).toBeCalled();
|
||||
});
|
||||
test('filter manager, active timeline in draggableId', () => {
|
||||
mount(
|
||||
<TestProviders>
|
||||
<DraggableWrapperHoverContent
|
||||
{...{ ...defaultProps, draggableId: `blahblah-${TimelineId.active}-lala` }}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(mockGetTimelineFilterManager).toBeCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,14 +12,12 @@ import {
|
|||
EuiScreenReaderOnly,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import React, { useCallback, useEffect, useRef, useMemo, useState } from 'react';
|
||||
import { DraggableId } from 'react-beautiful-dnd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { stopPropagationAndPreventDefault } from '../accessibility/helpers';
|
||||
import { TooltipWithKeyboardShortcut } from '../accessibility/tooltip_with_keyboard_shortcut';
|
||||
import { getAllFieldsByName } from '../../containers/source';
|
||||
import { useAddToTimeline } from '../../hooks/use_add_to_timeline';
|
||||
import { COPY_TO_CLIPBOARD_BUTTON_CLASS_NAME } from '../../lib/clipboard/clipboard';
|
||||
import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
|
@ -28,11 +26,14 @@ import { StatefulTopN } from '../top_n';
|
|||
|
||||
import { allowTopN } from './helpers';
|
||||
import * as i18n from './translations';
|
||||
import { useManageTimeline } from '../../../timelines/components/manage_timeline';
|
||||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from '../../../timelines/components/timeline/styles';
|
||||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
import { useSourcererScope } from '../../containers/sourcerer';
|
||||
import { timelineSelectors } from '../../../timelines/store/timeline';
|
||||
import { stopPropagationAndPreventDefault } from '../../../../../timelines/public';
|
||||
import { TooltipWithKeyboardShortcut } from '../accessibility';
|
||||
|
||||
export const AdditionalContent = styled.div`
|
||||
padding: 2px;
|
||||
|
@ -102,21 +103,25 @@ const DraggableWrapperHoverContentComponent: React.FC<Props> = ({
|
|||
toggleTopN,
|
||||
value,
|
||||
}) => {
|
||||
const { startDragToTimeline } = useAddToTimeline({ draggableId, fieldName: field });
|
||||
const kibana = useKibana();
|
||||
const { timelines } = kibana.services;
|
||||
const { startDragToTimeline } = timelines.getUseAddToTimeline()({
|
||||
draggableId,
|
||||
fieldName: field,
|
||||
});
|
||||
const filterManagerBackup = useMemo(() => kibana.services.data.query.filterManager, [
|
||||
kibana.services.data.query.filterManager,
|
||||
]);
|
||||
const { getTimelineFilterManager } = useManageTimeline();
|
||||
const getManageTimeline = useMemo(() => timelineSelectors.getManageTimelineById(), []);
|
||||
const { filterManager: activeFilterMananager } = useDeepEqualSelector((state) =>
|
||||
getManageTimeline(state, timelineId ?? '')
|
||||
);
|
||||
const defaultFocusedButtonRef = useRef<HTMLButtonElement | null>(null);
|
||||
const panelRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const filterManager = useMemo(
|
||||
() =>
|
||||
timelineId === TimelineId.active
|
||||
? getTimelineFilterManager(TimelineId.active)
|
||||
: filterManagerBackup,
|
||||
[timelineId, getTimelineFilterManager, filterManagerBackup]
|
||||
() => (timelineId === TimelineId.active ? activeFilterMananager : filterManagerBackup),
|
||||
[timelineId, activeFilterMananager, filterManagerBackup]
|
||||
);
|
||||
|
||||
// Regarding data from useManageTimeline:
|
||||
|
|
|
@ -15,6 +15,8 @@ import { DragDropContextWrapper } from './drag_drop_context_wrapper';
|
|||
import { DroppableWrapper } from './droppable_wrapper';
|
||||
import { useMountAppended } from '../../utils/use_mount_appended';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
describe('DroppableWrapper', () => {
|
||||
const mount = useMountAppended();
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { omit } from 'lodash/fp';
|
||||
import { DropResult } from 'react-beautiful-dnd';
|
||||
import { getTimelineIdFromColumnDroppableId } from '../../../../../timelines/public';
|
||||
|
||||
import { IdToDataProvider } from '../../store/drag_and_drop/model';
|
||||
|
||||
|
@ -33,7 +34,6 @@ import {
|
|||
getDroppableId,
|
||||
getFieldIdFromDraggable,
|
||||
getProviderIdFromDraggable,
|
||||
getTimelineIdFromColumnDroppableId,
|
||||
getTimelineProviderDraggableId,
|
||||
getTimelineProviderDroppableId,
|
||||
providerWasDroppedOnTimeline,
|
||||
|
|
|
@ -4,138 +4,53 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isString } from 'lodash/fp';
|
||||
import { DropResult, FluidDragActions, Position } from 'react-beautiful-dnd';
|
||||
import { DropResult } from 'react-beautiful-dnd';
|
||||
import { Dispatch } from 'redux';
|
||||
import { ActionCreator } from 'typescript-fsa';
|
||||
import { getProviderIdFromDraggable } from '@kbn/securitysolution-t-grid';
|
||||
|
||||
import { stopPropagationAndPreventDefault } from '../accessibility/helpers';
|
||||
import { alertsHeaders } from '../alerts_viewer/default_headers';
|
||||
import { BrowserField, BrowserFields, getAllFieldsByName } from '../../containers/source';
|
||||
import { BrowserField } from '../../containers/source';
|
||||
import { dragAndDropActions } from '../../store/actions';
|
||||
import { IdToDataProvider } from '../../store/drag_and_drop/model';
|
||||
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
|
||||
import { timelineActions } from '../../../timelines/store/timeline';
|
||||
import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants';
|
||||
import { addContentToTimeline } from '../../../timelines/components/timeline/data_providers/helpers';
|
||||
import { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
|
||||
export const draggableIdPrefix = 'draggableId';
|
||||
|
||||
export const droppableIdPrefix = 'droppableId';
|
||||
|
||||
export const draggableContentPrefix = `${draggableIdPrefix}.content.`;
|
||||
|
||||
export const draggableTimelineProvidersPrefix = `${draggableIdPrefix}.timelineProviders.`;
|
||||
|
||||
export const draggableFieldPrefix = `${draggableIdPrefix}.field.`;
|
||||
|
||||
export const droppableContentPrefix = `${droppableIdPrefix}.content.`;
|
||||
|
||||
export const droppableFieldPrefix = `${droppableIdPrefix}.field.`;
|
||||
|
||||
export const droppableTimelineProvidersPrefix = `${droppableIdPrefix}.timelineProviders.`;
|
||||
|
||||
export const droppableTimelineColumnsPrefix = `${droppableIdPrefix}.timelineColumns.`;
|
||||
|
||||
export const droppableTimelineFlyoutBottomBarPrefix = `${droppableIdPrefix}.flyoutButton.`;
|
||||
|
||||
export const getDraggableId = (dataProviderId: string): string =>
|
||||
`${draggableContentPrefix}${dataProviderId}`;
|
||||
|
||||
export const getDraggableFieldId = ({
|
||||
contextId,
|
||||
fieldId,
|
||||
}: {
|
||||
contextId: string;
|
||||
fieldId: string;
|
||||
}): string => `${draggableFieldPrefix}${escapeContextId(contextId)}.${escapeFieldId(fieldId)}`;
|
||||
|
||||
export const getTimelineProviderDroppableId = ({
|
||||
groupIndex,
|
||||
timelineId,
|
||||
}: {
|
||||
groupIndex: number;
|
||||
timelineId: string;
|
||||
}): string => `${droppableTimelineProvidersPrefix}${timelineId}.group.${groupIndex}`;
|
||||
|
||||
export const getTimelineProviderDraggableId = ({
|
||||
dataProviderId,
|
||||
groupIndex,
|
||||
timelineId,
|
||||
}: {
|
||||
dataProviderId: string;
|
||||
groupIndex: number;
|
||||
timelineId: string;
|
||||
}): string =>
|
||||
`${draggableTimelineProvidersPrefix}${timelineId}.group.${groupIndex}.${dataProviderId}`;
|
||||
|
||||
export const getDroppableId = (visualizationPlaceholderId: string): string =>
|
||||
`${droppableContentPrefix}${visualizationPlaceholderId}`;
|
||||
|
||||
export const sourceIsContent = (result: DropResult): boolean =>
|
||||
result.source.droppableId.startsWith(droppableContentPrefix);
|
||||
|
||||
export const sourceAndDestinationAreSameTimelineProviders = (result: DropResult): boolean => {
|
||||
const regex = /^droppableId\.timelineProviders\.(\S+)\./;
|
||||
const sourceMatches = result.source.droppableId.match(regex) ?? [];
|
||||
const destinationMatches = result.destination?.droppableId.match(regex) ?? [];
|
||||
|
||||
return (
|
||||
sourceMatches.length >= 2 &&
|
||||
destinationMatches.length >= 2 &&
|
||||
sourceMatches[1] === destinationMatches[1]
|
||||
);
|
||||
};
|
||||
|
||||
export const draggableIsContent = (result: DropResult | { draggableId: string }): boolean =>
|
||||
result.draggableId.startsWith(draggableContentPrefix);
|
||||
|
||||
export const draggableIsField = (result: DropResult | { draggableId: string }): boolean =>
|
||||
result.draggableId.startsWith(draggableFieldPrefix);
|
||||
|
||||
export const reasonIsDrop = (result: DropResult): boolean => result.reason === 'DROP';
|
||||
|
||||
export const destinationIsTimelineProviders = (result: DropResult): boolean =>
|
||||
result.destination != null &&
|
||||
result.destination.droppableId.startsWith(droppableTimelineProvidersPrefix);
|
||||
|
||||
export const destinationIsTimelineColumns = (result: DropResult): boolean =>
|
||||
result.destination != null &&
|
||||
result.destination.droppableId.startsWith(droppableTimelineColumnsPrefix);
|
||||
|
||||
export const destinationIsTimelineButton = (result: DropResult): boolean =>
|
||||
result.destination != null &&
|
||||
result.destination.droppableId.startsWith(droppableTimelineFlyoutBottomBarPrefix);
|
||||
|
||||
export const getProviderIdFromDraggable = (result: DropResult): string =>
|
||||
result.draggableId.substring(result.draggableId.lastIndexOf('.') + 1);
|
||||
|
||||
export const getFieldIdFromDraggable = (result: DropResult): string =>
|
||||
unEscapeFieldId(result.draggableId.substring(result.draggableId.lastIndexOf('.') + 1));
|
||||
|
||||
export const escapeDataProviderId = (path: string) => path.replace(/\./g, '_');
|
||||
|
||||
export const escapeContextId = (path: string) => path.replace(/\./g, '_');
|
||||
|
||||
export const escapeFieldId = (path: string) => path.replace(/\./g, '!!!DOT!!!');
|
||||
|
||||
export const unEscapeFieldId = (path: string) => path.replace(/!!!DOT!!!/g, '.');
|
||||
|
||||
export const providerWasDroppedOnTimeline = (result: DropResult): boolean =>
|
||||
reasonIsDrop(result) &&
|
||||
draggableIsContent(result) &&
|
||||
sourceIsContent(result) &&
|
||||
destinationIsTimelineProviders(result);
|
||||
|
||||
export const userIsReArrangingProviders = (result: DropResult): boolean =>
|
||||
reasonIsDrop(result) && sourceAndDestinationAreSameTimelineProviders(result);
|
||||
|
||||
export const fieldWasDroppedOnTimelineColumns = (result: DropResult): boolean =>
|
||||
reasonIsDrop(result) && draggableIsField(result) && destinationIsTimelineColumns(result);
|
||||
|
||||
export {
|
||||
draggableIdPrefix,
|
||||
droppableIdPrefix,
|
||||
draggableContentPrefix,
|
||||
draggableTimelineProvidersPrefix,
|
||||
draggableFieldPrefix,
|
||||
draggableIsField,
|
||||
droppableContentPrefix,
|
||||
droppableFieldPrefix,
|
||||
droppableTimelineProvidersPrefix,
|
||||
droppableTimelineColumnsPrefix,
|
||||
droppableTimelineFlyoutBottomBarPrefix,
|
||||
getDraggableId,
|
||||
getDraggableFieldId,
|
||||
getTimelineProviderDroppableId,
|
||||
getTimelineProviderDraggableId,
|
||||
getDroppableId,
|
||||
sourceIsContent,
|
||||
sourceAndDestinationAreSameTimelineProviders,
|
||||
draggableIsContent,
|
||||
reasonIsDrop,
|
||||
destinationIsTimelineProviders,
|
||||
destinationIsTimelineColumns,
|
||||
destinationIsTimelineButton,
|
||||
getProviderIdFromDraggable,
|
||||
getFieldIdFromDraggable,
|
||||
escapeDataProviderId,
|
||||
escapeContextId,
|
||||
escapeFieldId,
|
||||
unEscapeFieldId,
|
||||
providerWasDroppedOnTimeline,
|
||||
userIsReArrangingProviders,
|
||||
fieldWasDroppedOnTimelineColumns,
|
||||
DRAG_TYPE_FIELD,
|
||||
IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME,
|
||||
} from '@kbn/securitysolution-t-grid';
|
||||
interface AddProviderToTimelineParams {
|
||||
activeTimelineDataProviders: DataProvider[];
|
||||
dataProviders: IdToDataProvider;
|
||||
|
@ -148,18 +63,6 @@ interface AddProviderToTimelineParams {
|
|||
timelineId: string;
|
||||
}
|
||||
|
||||
interface AddFieldToTimelineColumnsParams {
|
||||
upsertColumn?: ActionCreator<{
|
||||
column: ColumnHeaderOptions;
|
||||
id: string;
|
||||
index: number;
|
||||
}>;
|
||||
browserFields: BrowserFields;
|
||||
dispatch: Dispatch;
|
||||
result: DropResult;
|
||||
timelineId: string;
|
||||
}
|
||||
|
||||
export const addProviderToTimeline = ({
|
||||
activeTimelineDataProviders,
|
||||
dataProviders,
|
||||
|
@ -186,73 +89,6 @@ export const addProviderToTimeline = ({
|
|||
}
|
||||
};
|
||||
|
||||
const linkFields: Record<string, string> = {
|
||||
'signal.rule.name': 'signal.rule.id',
|
||||
'event.module': 'rule.reference',
|
||||
};
|
||||
|
||||
export const addFieldToTimelineColumns = ({
|
||||
upsertColumn = timelineActions.upsertColumn,
|
||||
browserFields,
|
||||
dispatch,
|
||||
result,
|
||||
timelineId,
|
||||
}: AddFieldToTimelineColumnsParams): void => {
|
||||
const fieldId = getFieldIdFromDraggable(result);
|
||||
const allColumns = getAllFieldsByName(browserFields);
|
||||
const column = allColumns[fieldId];
|
||||
const initColumnHeader =
|
||||
timelineId === TimelineId.detectionsPage || timelineId === TimelineId.detectionsRulesDetailsPage
|
||||
? alertsHeaders.find((c) => c.id === fieldId) ?? {}
|
||||
: {};
|
||||
|
||||
if (column != null) {
|
||||
dispatch(
|
||||
upsertColumn({
|
||||
column: {
|
||||
category: column.category,
|
||||
columnHeaderType: 'not-filtered',
|
||||
description: isString(column.description) ? column.description : undefined,
|
||||
example: isString(column.example) ? column.example : undefined,
|
||||
id: fieldId,
|
||||
linkField: linkFields[fieldId] ?? undefined,
|
||||
type: column.type,
|
||||
aggregatable: column.aggregatable,
|
||||
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
|
||||
...initColumnHeader,
|
||||
},
|
||||
id: timelineId,
|
||||
index: result.destination != null ? result.destination.index : 0,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
// create a column definition, because it doesn't exist in the browserFields:
|
||||
dispatch(
|
||||
upsertColumn({
|
||||
column: {
|
||||
columnHeaderType: 'not-filtered',
|
||||
id: fieldId,
|
||||
initialWidth: DEFAULT_COLUMN_MIN_WIDTH,
|
||||
},
|
||||
id: timelineId,
|
||||
index: result.destination != null ? result.destination.index : 0,
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Prevents fields from being dragged or dropped to any area other than column
|
||||
* header drop zone in the timeline
|
||||
*/
|
||||
export const DRAG_TYPE_FIELD = 'drag-type-field';
|
||||
|
||||
/** This class is added to the document body while dragging */
|
||||
export const IS_DRAGGING_CLASS_NAME = 'is-dragging';
|
||||
|
||||
/** This class is added to the document body while timeline field dragging */
|
||||
export const IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME = 'is-timeline-field-dragging';
|
||||
|
||||
export const allowTopN = ({
|
||||
browserField,
|
||||
fieldName,
|
||||
|
@ -347,125 +183,3 @@ export const allowTopN = ({
|
|||
|
||||
return isAllowlistedNonBrowserField || (isAggregatable && isAllowedType);
|
||||
};
|
||||
|
||||
export const getTimelineIdFromColumnDroppableId = (droppableId: string) =>
|
||||
droppableId.slice(droppableId.lastIndexOf('.') + 1);
|
||||
|
||||
/** The draggable will move this many pixes via the keyboard when the arrow key is pressed */
|
||||
export const KEYBOARD_DRAG_OFFSET = 20;
|
||||
|
||||
export const DRAGGABLE_KEYBOARD_WRAPPER_CLASS_NAME = 'draggable-keyboard-wrapper';
|
||||
|
||||
/**
|
||||
* Temporarily disables tab focus on child links of the draggable to work
|
||||
* around an issue where tab focus becomes stuck on the interactive children
|
||||
*
|
||||
* NOTE: This function is (intentionally) only effective when used in a key
|
||||
* event handler, because it automatically restores focus capabilities on
|
||||
* the next tick.
|
||||
*/
|
||||
export const temporarilyDisableInteractiveChildTabIndexes = (draggableElement: HTMLDivElement) => {
|
||||
const interactiveChildren = draggableElement.querySelectorAll('a, button');
|
||||
interactiveChildren.forEach((interactiveChild) => {
|
||||
interactiveChild.setAttribute('tabindex', '-1'); // DOM mutation
|
||||
});
|
||||
|
||||
// restore the default tabindexs on the next tick:
|
||||
setTimeout(() => {
|
||||
interactiveChildren.forEach((interactiveChild) => {
|
||||
interactiveChild.setAttribute('tabindex', '0'); // DOM mutation
|
||||
});
|
||||
}, 0);
|
||||
};
|
||||
|
||||
export const draggableKeyDownHandler = ({
|
||||
beginDrag,
|
||||
cancelDragActions,
|
||||
closePopover,
|
||||
draggableElement,
|
||||
dragActions,
|
||||
dragToLocation,
|
||||
endDrag,
|
||||
keyboardEvent,
|
||||
openPopover,
|
||||
setDragActions,
|
||||
}: {
|
||||
beginDrag: () => FluidDragActions | null;
|
||||
cancelDragActions: () => void;
|
||||
closePopover?: () => void;
|
||||
draggableElement: HTMLDivElement;
|
||||
dragActions: FluidDragActions | null;
|
||||
dragToLocation: ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
dragActions,
|
||||
position,
|
||||
}: {
|
||||
dragActions: FluidDragActions | null;
|
||||
position: Position;
|
||||
}) => void;
|
||||
keyboardEvent: React.KeyboardEvent;
|
||||
endDrag: (dragActions: FluidDragActions | null) => void;
|
||||
openPopover?: () => void;
|
||||
setDragActions: (value: React.SetStateAction<FluidDragActions | null>) => void;
|
||||
}) => {
|
||||
let currentPosition: DOMRect | null = null;
|
||||
|
||||
switch (keyboardEvent.key) {
|
||||
case ' ':
|
||||
if (!dragActions) {
|
||||
// start dragging, because space was pressed
|
||||
if (closePopover != null) {
|
||||
closePopover();
|
||||
}
|
||||
setDragActions(beginDrag());
|
||||
} else {
|
||||
// end dragging, because space was pressed
|
||||
endDrag(dragActions);
|
||||
setDragActions(null);
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
cancelDragActions();
|
||||
break;
|
||||
case 'Tab':
|
||||
// IMPORTANT: we do NOT want to stop propagation and prevent default when Tab is pressed
|
||||
temporarilyDisableInteractiveChildTabIndexes(draggableElement);
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
currentPosition = draggableElement.getBoundingClientRect();
|
||||
dragToLocation({
|
||||
dragActions,
|
||||
position: { x: currentPosition.x, y: currentPosition.y - KEYBOARD_DRAG_OFFSET },
|
||||
});
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
currentPosition = draggableElement.getBoundingClientRect();
|
||||
dragToLocation({
|
||||
dragActions,
|
||||
position: { x: currentPosition.x, y: currentPosition.y + KEYBOARD_DRAG_OFFSET },
|
||||
});
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
currentPosition = draggableElement.getBoundingClientRect();
|
||||
dragToLocation({
|
||||
dragActions,
|
||||
position: { x: currentPosition.x - KEYBOARD_DRAG_OFFSET, y: currentPosition.y },
|
||||
});
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
currentPosition = draggableElement.getBoundingClientRect();
|
||||
dragToLocation({
|
||||
dragActions,
|
||||
position: { x: currentPosition.x + KEYBOARD_DRAG_OFFSET, y: currentPosition.y },
|
||||
});
|
||||
break;
|
||||
case 'Enter':
|
||||
stopPropagationAndPreventDefault(keyboardEvent); // prevents the first item in the popover from getting an errant ENTER
|
||||
if (!dragActions && openPopover != null) {
|
||||
openPopover();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -21,6 +21,8 @@ import {
|
|||
tooltipContentIsExplicitlyNull,
|
||||
} from '.';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
describe('draggables', () => {
|
||||
const mount = useMountAppended();
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ import { TestProviders } from '../../mock';
|
|||
import { mockBrowserFields } from '../../containers/source/mock';
|
||||
import { useMountAppended } from '../../utils/use_mount_appended';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
jest.mock('../../../detections/containers/detection_engine/rules/use_rule_async', () => {
|
||||
return {
|
||||
useRuleAsync: jest.fn(),
|
||||
|
|
|
@ -21,9 +21,8 @@ import { get, isEmpty } from 'lodash';
|
|||
import memoizeOne from 'memoize-one';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { onFocusReFocusDraggable } from '../accessibility/helpers';
|
||||
import { onFocusReFocusDraggable } from '../../../../../timelines/public';
|
||||
import { BrowserFields } from '../../containers/source';
|
||||
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
|
||||
import { DragEffects } from '../drag_and_drop/draggable_wrapper';
|
||||
import { DroppableWrapper } from '../drag_and_drop/droppable_wrapper';
|
||||
import { DRAG_TYPE_FIELD, getDroppableId } from '../drag_and_drop/helpers';
|
||||
|
@ -38,6 +37,7 @@ import { OnUpdateColumns } from '../../../timelines/components/timeline/events';
|
|||
import { getIconFromType, getExampleText } from './helpers';
|
||||
import * as i18n from './translations';
|
||||
import { EventFieldsData } from './types';
|
||||
import { ColumnHeaderOptions } from '../../../../common';
|
||||
|
||||
const HoverActionsContainer = styled(EuiPanel)`
|
||||
align-items: center;
|
||||
|
|
|
@ -20,6 +20,8 @@ import { mockAlertDetailsData } from './__mocks__';
|
|||
import { TimelineEventsDetailsItem } from '../../../../common/search_strategy';
|
||||
import { TimelineTabs } from '../../../../common/types/timeline';
|
||||
|
||||
jest.mock('../../../common/lib/kibana');
|
||||
|
||||
jest.mock('../link_to');
|
||||
describe('EventDetails', () => {
|
||||
const mount = useMountAppended();
|
||||
|
|
|
@ -16,6 +16,8 @@ import { mockBrowserFields } from '../../containers/source/mock';
|
|||
import { useMountAppended } from '../../utils/use_mount_appended';
|
||||
import { TimelineTabs } from '../../../../common/types/timeline';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
jest.mock('@elastic/eui', () => {
|
||||
const original = jest.requireActual('@elastic/eui');
|
||||
return {
|
||||
|
|
|
@ -11,26 +11,24 @@ import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
|||
import { useDispatch } from 'react-redux';
|
||||
import { rgba } from 'polished';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
arrayIndexToAriaIndex,
|
||||
DATA_COLINDEX_ATTRIBUTE,
|
||||
DATA_ROWINDEX_ATTRIBUTE,
|
||||
isTab,
|
||||
onKeyDownFocusHandler,
|
||||
} from '../accessibility/helpers';
|
||||
} from '../../../../../timelines/public';
|
||||
|
||||
import { ADD_TIMELINE_BUTTON_CLASS_NAME } from '../../../timelines/components/flyout/add_timeline_button';
|
||||
import { timelineActions, timelineSelectors } from '../../../timelines/store/timeline';
|
||||
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
|
||||
import { BrowserFields, getAllFieldsByName } from '../../containers/source';
|
||||
import { TimelineEventsDetailsItem } from '../../../../common/search_strategy/timeline';
|
||||
import { getColumnHeaders } from '../../../timelines/components/timeline/body/column_headers/helpers';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
|
||||
import { getColumns } from './columns';
|
||||
import { EVENT_FIELDS_TABLE_CLASS_NAME, onEventDetailsTabKeyPressed, search } from './helpers';
|
||||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
import { TimelineTabs } from '../../../../common/types/timeline';
|
||||
import { ColumnHeaderOptions, TimelineTabs } from '../../../../common/types/timeline';
|
||||
|
||||
interface Props {
|
||||
browserFields: BrowserFields;
|
||||
|
|
|
@ -15,15 +15,15 @@ import {
|
|||
getTableSkipFocus,
|
||||
handleSkipFocus,
|
||||
stopPropagationAndPreventDefault,
|
||||
} from '../accessibility/helpers';
|
||||
} from '../../../../../timelines/public';
|
||||
import { BrowserField, BrowserFields } from '../../containers/source';
|
||||
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
|
||||
import {
|
||||
DEFAULT_DATE_COLUMN_MIN_WIDTH,
|
||||
DEFAULT_COLUMN_MIN_WIDTH,
|
||||
} from '../../../timelines/components/timeline/body/constants';
|
||||
|
||||
import * as i18n from './translations';
|
||||
import { ColumnHeaderOptions } from '../../../../common';
|
||||
|
||||
/**
|
||||
* Defines the behavior of the search input that appears above the table of data
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
|
||||
import { ColumnHeaderOptions } from '../../../../common';
|
||||
import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
import {
|
||||
DEFAULT_COLUMN_MIN_WIDTH,
|
||||
|
|
|
@ -21,9 +21,8 @@ import { mockBrowserFields, mockDocValueFields } from '../../containers/source/m
|
|||
import { eventsDefaultModel } from './default_model';
|
||||
import { useMountAppended } from '../../utils/use_mount_appended';
|
||||
import { inputsModel } from '../../store/inputs';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { TimelineId, SortDirection } from '../../../../common/types/timeline';
|
||||
import { KqlMode } from '../../../timelines/store/timeline/model';
|
||||
import { SortDirection } from '../../../timelines/components/timeline/body/sort';
|
||||
import { AlertsTableFilterGroup } from '../../../detections/components/alerts_table/alerts_filter_group';
|
||||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
|
||||
|
@ -31,6 +30,8 @@ import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell
|
|||
import { useTimelineEvents } from '../../../timelines/containers';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
jest.mock('../../hooks/use_experimental_features');
|
||||
const useIsExperimentalFeatureEnabledMock = useIsExperimentalFeatureEnabled as jest.Mock;
|
||||
|
||||
|
@ -144,18 +145,18 @@ describe('EventsViewer', () => {
|
|||
mockUseTimelineEvents.mockReturnValue([false, mockEventViewerResponseWithEvents]);
|
||||
});
|
||||
|
||||
test('call the right reduce action to show event details', async () => {
|
||||
test('call the right reduce action to show event details', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<StatefulEventsViewer {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
act(() => {
|
||||
wrapper.find(`[data-test-subj="expand-event"]`).first().simulate('click');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
waitFor(() => {
|
||||
expect(mockDispatch).toBeCalledTimes(2);
|
||||
expect(mockDispatch.mock.calls[1][0]).toEqual({
|
||||
payload: {
|
||||
|
@ -197,7 +198,7 @@ describe('EventsViewer', () => {
|
|||
);
|
||||
expect(wrapper.find(`[data-test-subj="show-field-browser"]`).first().exists()).toBe(true);
|
||||
});
|
||||
// TO DO sourcerer @X
|
||||
|
||||
test('it renders the footer containing the pagination', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
|
|
|
@ -10,11 +10,12 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|||
import styled from 'styled-components';
|
||||
import deepEqual from 'fast-deep-equal';
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { Direction } from '../../../../common/search_strategy';
|
||||
import { BrowserFields, DocValueFields } from '../../containers/source';
|
||||
import { useTimelineEvents } from '../../../timelines/containers';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { ColumnHeaderOptions, KqlMode } from '../../../timelines/store/timeline/model';
|
||||
import { KqlMode } from '../../../timelines/store/timeline/model';
|
||||
import { HeaderSection } from '../header_section';
|
||||
import { defaultHeaders } from '../../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
import { Sort } from '../../../timelines/components/timeline/body/sort';
|
||||
|
@ -36,18 +37,21 @@ import {
|
|||
Query,
|
||||
} from '../../../../../../../src/plugins/data/public';
|
||||
import { inputsModel } from '../../store';
|
||||
import { useManageTimeline } from '../../../timelines/components/manage_timeline';
|
||||
import { ExitFullScreen } from '../exit_full_screen';
|
||||
import { useGlobalFullScreen } from '../../containers/use_full_screen';
|
||||
import { TimelineId, TimelineTabs } from '../../../../common/types/timeline';
|
||||
import { RowRenderer } from '../../../timelines/components/timeline/body/renderers/row_renderer';
|
||||
import {
|
||||
ColumnHeaderOptions,
|
||||
ControlColumnProps,
|
||||
RowRenderer,
|
||||
TimelineId,
|
||||
TimelineTabs,
|
||||
} from '../../../../common/types/timeline';
|
||||
import { GraphOverlay } from '../../../timelines/components/graph_overlay';
|
||||
import { CellValueElementProps } from '../../../timelines/components/timeline/cell_rendering';
|
||||
import { SELECTOR_TIMELINE_GLOBAL_CONTAINER } from '../../../timelines/components/timeline/styles';
|
||||
import {
|
||||
defaultControlColumn,
|
||||
ControlColumnProps,
|
||||
} from '../../../timelines/components/timeline/body/control_columns';
|
||||
import { defaultControlColumn } from '../../../timelines/components/timeline/body/control_columns';
|
||||
import { timelineSelectors, timelineActions } from '../../../timelines/store/timeline';
|
||||
import { useDeepEqualSelector } from '../../hooks/use_selector';
|
||||
|
||||
export const EVENTS_VIEWER_HEADER_HEIGHT = 90; // px
|
||||
const UTILITY_BAR_HEIGHT = 19; // px
|
||||
|
@ -162,21 +166,19 @@ const EventsViewerComponent: React.FC<Props> = ({
|
|||
utilityBar,
|
||||
graphEventId,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { globalFullScreen, setGlobalFullScreen } = useGlobalFullScreen();
|
||||
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
|
||||
const kibana = useKibana();
|
||||
const [isQueryLoading, setIsQueryLoading] = useState(false);
|
||||
|
||||
const { getManageTimelineById, setIsTimelineLoading } = useManageTimeline();
|
||||
|
||||
useEffect(() => {
|
||||
setIsTimelineLoading({ id, isLoading: isQueryLoading });
|
||||
}, [id, isQueryLoading, setIsTimelineLoading]);
|
||||
dispatch(timelineActions.updateIsLoading({ id, isLoading: isQueryLoading }));
|
||||
}, [dispatch, id, isQueryLoading]);
|
||||
|
||||
const { queryFields, title, unit } = useMemo(() => getManageTimelineById(id), [
|
||||
getManageTimelineById,
|
||||
id,
|
||||
]);
|
||||
const getManageTimeline = useMemo(() => timelineSelectors.getManageTimelineById(), []);
|
||||
const unit = useMemo(() => (n: number) => i18n.UNIT(n), []);
|
||||
const { queryFields, title } = useDeepEqualSelector((state) => getManageTimeline(state, id));
|
||||
|
||||
const justTitle = useMemo(() => <TitleText data-test-subj="title">{title}</TitleText>, [title]);
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ import { DefaultCellRenderer } from '../../../timelines/components/timeline/cell
|
|||
import { useTimelineEvents } from '../../../timelines/containers';
|
||||
import { defaultRowRenderers } from '../../../timelines/components/timeline/body/renderers';
|
||||
|
||||
jest.mock('../../../common/lib/kibana');
|
||||
|
||||
jest.mock('../../../timelines/containers', () => ({
|
||||
useTimelineEvents: jest.fn(),
|
||||
}));
|
||||
|
@ -60,7 +62,9 @@ describe('StatefulEventsViewer', () => {
|
|||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('[data-test-subj="events-viewer-panel"]').first().exists()).toBe(true);
|
||||
expect(wrapper.text()).toMatchInlineSnapshot(
|
||||
`"Showing: 12 events1 fields sorted@timestamp1event.severityevent.categoryevent.actionhost.namesource.ipdestination.ipdestination.bytesuser.name_idmessage0 of 12 events123"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -12,18 +12,20 @@ import styled from 'styled-components';
|
|||
|
||||
import { inputsModel, inputsSelectors, State } from '../../store';
|
||||
import { inputsActions } from '../../store/actions';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { ControlColumnProps, RowRenderer, TimelineId } from '../../../../common/types/timeline';
|
||||
import { timelineSelectors, timelineActions } from '../../../timelines/store/timeline';
|
||||
import { SubsetTimelineModel, TimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { Filter } from '../../../../../../../src/plugins/data/public';
|
||||
import { EventsViewer } from './events_viewer';
|
||||
import { InspectButtonContainer } from '../inspect';
|
||||
import { useGlobalFullScreen } from '../../containers/use_full_screen';
|
||||
import { useIsExperimentalFeatureEnabled } from '../../hooks/use_experimental_features';
|
||||
import { SourcererScopeName } from '../../store/sourcerer/model';
|
||||
import { useSourcererScope } from '../../containers/sourcerer';
|
||||
import { DetailsPanel } from '../../../timelines/components/side_panel';
|
||||
import { RowRenderer } from '../../../timelines/components/timeline/body/renderers/row_renderer';
|
||||
import { CellValueElementProps } from '../../../timelines/components/timeline/cell_rendering';
|
||||
import { useKibana } from '../../lib/kibana';
|
||||
import { defaultControlColumn } from '../../../timelines/components/timeline/body/control_columns';
|
||||
import { EventsViewer } from './events_viewer';
|
||||
|
||||
const DEFAULT_EVENTS_VIEWER_HEIGHT = 652;
|
||||
|
||||
|
@ -83,6 +85,7 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
// If truthy, the graph viewer (Resolver) is showing
|
||||
graphEventId,
|
||||
}) => {
|
||||
const { timelines: timelinesUi } = useKibana().services;
|
||||
const {
|
||||
browserFields,
|
||||
docValueFields,
|
||||
|
@ -90,8 +93,9 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
selectedPatterns,
|
||||
loading: isLoadingIndexPattern,
|
||||
} = useSourcererScope(scopeId);
|
||||
const { globalFullScreen } = useGlobalFullScreen();
|
||||
|
||||
const { globalFullScreen, setGlobalFullScreen } = useGlobalFullScreen();
|
||||
// TODO: Once we are past experimental phase this code should be removed
|
||||
const tGridEnabled = useIsExperimentalFeatureEnabled('tGridEnabled');
|
||||
useEffect(() => {
|
||||
if (createTimeline != null) {
|
||||
createTimeline({
|
||||
|
@ -111,37 +115,73 @@ const StatefulEventsViewerComponent: React.FC<Props> = ({
|
|||
}, []);
|
||||
|
||||
const globalFilters = useMemo(() => [...filters, ...(pageFilters ?? [])], [filters, pageFilters]);
|
||||
const leadingControlColumns: ControlColumnProps[] = [defaultControlColumn];
|
||||
const trailingControlColumns: ControlColumnProps[] = [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<FullScreenContainer $isFullScreen={globalFullScreen}>
|
||||
<InspectButtonContainer>
|
||||
<EventsViewer
|
||||
browserFields={browserFields}
|
||||
columns={columns}
|
||||
docValueFields={docValueFields}
|
||||
id={id}
|
||||
dataProviders={dataProviders!}
|
||||
deletedEventIds={deletedEventIds}
|
||||
end={end}
|
||||
isLoadingIndexPattern={isLoadingIndexPattern}
|
||||
filters={globalFilters}
|
||||
headerFilterGroup={headerFilterGroup}
|
||||
indexNames={selectedPatterns}
|
||||
indexPattern={indexPattern}
|
||||
isLive={isLive}
|
||||
itemsPerPage={itemsPerPage!}
|
||||
itemsPerPageOptions={itemsPerPageOptions!}
|
||||
kqlMode={kqlMode}
|
||||
query={query}
|
||||
onRuleChange={onRuleChange}
|
||||
renderCellValue={renderCellValue}
|
||||
rowRenderers={rowRenderers}
|
||||
start={start}
|
||||
sort={sort}
|
||||
utilityBar={utilityBar}
|
||||
graphEventId={graphEventId}
|
||||
/>
|
||||
{tGridEnabled ? (
|
||||
timelinesUi.getTGrid<'embedded'>({
|
||||
type: 'embedded',
|
||||
browserFields,
|
||||
columns,
|
||||
dataProviders: dataProviders!,
|
||||
deletedEventIds,
|
||||
docValueFields,
|
||||
end,
|
||||
filters: globalFilters,
|
||||
globalFullScreen,
|
||||
headerFilterGroup,
|
||||
id,
|
||||
indexNames: selectedPatterns,
|
||||
indexPattern,
|
||||
isLive,
|
||||
isLoadingIndexPattern,
|
||||
itemsPerPage,
|
||||
itemsPerPageOptions: itemsPerPageOptions!,
|
||||
kqlMode,
|
||||
query,
|
||||
onRuleChange,
|
||||
renderCellValue,
|
||||
rowRenderers,
|
||||
setGlobalFullScreen,
|
||||
start,
|
||||
sort,
|
||||
utilityBar,
|
||||
graphEventId,
|
||||
leadingControlColumns,
|
||||
trailingControlColumns,
|
||||
})
|
||||
) : (
|
||||
<EventsViewer
|
||||
browserFields={browserFields}
|
||||
columns={columns}
|
||||
docValueFields={docValueFields}
|
||||
id={id}
|
||||
dataProviders={dataProviders!}
|
||||
deletedEventIds={deletedEventIds}
|
||||
end={end}
|
||||
isLoadingIndexPattern={isLoadingIndexPattern}
|
||||
filters={globalFilters}
|
||||
headerFilterGroup={headerFilterGroup}
|
||||
indexNames={selectedPatterns}
|
||||
indexPattern={indexPattern}
|
||||
isLive={isLive}
|
||||
itemsPerPage={itemsPerPage!}
|
||||
itemsPerPageOptions={itemsPerPageOptions!}
|
||||
kqlMode={kqlMode}
|
||||
query={query}
|
||||
onRuleChange={onRuleChange}
|
||||
renderCellValue={renderCellValue}
|
||||
rowRenderers={rowRenderers}
|
||||
start={start}
|
||||
sort={sort}
|
||||
utilityBar={utilityBar}
|
||||
graphEventId={graphEventId}
|
||||
/>
|
||||
)}
|
||||
</InspectButtonContainer>
|
||||
</FullScreenContainer>
|
||||
<DetailsPanel
|
||||
|
|
|
@ -22,13 +22,6 @@ export const EVENTS = i18n.translate('xpack.securitySolution.eventsViewer.events
|
|||
defaultMessage: 'Events',
|
||||
});
|
||||
|
||||
export const LOADING_EVENTS = i18n.translate(
|
||||
'xpack.securitySolution.eventsViewer.footer.loadingEventsDataLabel',
|
||||
{
|
||||
defaultMessage: 'Loading Events',
|
||||
}
|
||||
);
|
||||
|
||||
export const UNIT = (totalCount: number) =>
|
||||
i18n.translate('xpack.securitySolution.eventsViewer.unit', {
|
||||
values: { totalCount },
|
||||
|
|
|
@ -13,6 +13,8 @@ import { TestProviders } from '../../mock';
|
|||
import { Title } from './title';
|
||||
import { useMountAppended } from '../../utils/use_mount_appended';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
describe('Title', () => {
|
||||
const mount = useMountAppended();
|
||||
|
||||
|
|
|
@ -131,7 +131,7 @@ const InspectButtonComponent: React.FC<InspectButtonProps> = ({
|
|||
color="text"
|
||||
iconSide="left"
|
||||
iconType="inspect"
|
||||
isDisabled={loading || isDisabled}
|
||||
isDisabled={loading || isDisabled || false}
|
||||
isLoading={loading}
|
||||
onClick={handleClick}
|
||||
>
|
||||
|
@ -145,7 +145,7 @@ const InspectButtonComponent: React.FC<InspectButtonProps> = ({
|
|||
data-test-subj="inspect-icon-button"
|
||||
iconSize="m"
|
||||
iconType="inspect"
|
||||
isDisabled={loading || isDisabled}
|
||||
isDisabled={loading || isDisabled || false}
|
||||
title={i18n.INSPECT}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
|
|
|
@ -13,6 +13,8 @@ import { EntityDraggableComponent } from './entity_draggable';
|
|||
import { TestProviders } from '../../mock/test_providers';
|
||||
import { useMountAppended } from '../../utils/use_mount_appended';
|
||||
|
||||
jest.mock('../../../common/lib/kibana');
|
||||
|
||||
jest.mock('@elastic/eui', () => {
|
||||
const original = jest.requireActual('@elastic/eui');
|
||||
return {
|
||||
|
|
|
@ -17,6 +17,8 @@ import { useMountAppended } from '../../../utils/use_mount_appended';
|
|||
import { Anomalies } from '../types';
|
||||
import { waitFor } from '@testing-library/dom';
|
||||
|
||||
jest.mock('../../../lib/kibana');
|
||||
|
||||
const startDate: string = '2020-07-07T08:20:18.966Z';
|
||||
const endDate: string = '3000-01-01T00:00:00.000Z';
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@ import { Anomalies } from '../types';
|
|||
import { useMountAppended } from '../../../utils/use_mount_appended';
|
||||
import { waitFor } from '@testing-library/dom';
|
||||
|
||||
jest.mock('../../../lib/kibana');
|
||||
|
||||
const startDate: string = '2020-07-07T08:20:18.966Z';
|
||||
const endDate: string = '3000-01-01T00:00:00.000Z';
|
||||
const narrowDateRange = jest.fn();
|
||||
|
|
|
@ -16,6 +16,8 @@ import { Columns } from '../../paginated_table';
|
|||
import { TestProviders } from '../../../mock';
|
||||
import { useMountAppended } from '../../../utils/use_mount_appended';
|
||||
|
||||
jest.mock('../../../lib/kibana');
|
||||
|
||||
const startDate = new Date(2001).toISOString();
|
||||
const endDate = new Date(3000).toISOString();
|
||||
const interval = 'days';
|
||||
|
|
|
@ -15,6 +15,8 @@ import React from 'react';
|
|||
import { TestProviders } from '../../../mock';
|
||||
import { useMountAppended } from '../../../utils/use_mount_appended';
|
||||
|
||||
jest.mock('../../../../common/lib/kibana');
|
||||
|
||||
const startDate = new Date(2001).toISOString();
|
||||
const endDate = new Date(3000).toISOString();
|
||||
describe('get_anomalies_network_table_columns', () => {
|
||||
|
|
|
@ -18,6 +18,9 @@ import {
|
|||
import { TestProviders } from '../../mock';
|
||||
import { getEmptyValue } from '../empty_value';
|
||||
import { useMountAppended } from '../../utils/use_mount_appended';
|
||||
|
||||
jest.mock('../../lib/kibana');
|
||||
|
||||
describe('Table Helpers', () => {
|
||||
const items = ['item1', 'item2', 'item3'];
|
||||
const mount = useMountAppended();
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
import type React from 'react';
|
||||
import uuid from 'uuid';
|
||||
import { isError } from 'lodash/fp';
|
||||
import { isAppError } from '@kbn/securitysolution-t-grid';
|
||||
|
||||
import { AppToast, ActionToaster } from './';
|
||||
import { isToasterError } from './errors';
|
||||
import { isAppError } from '../../utils/api';
|
||||
|
||||
/**
|
||||
* Displays an error toast for the provided title and message
|
||||
|
|
|
@ -18,17 +18,11 @@ import {
|
|||
createSecuritySolutionStorageMock,
|
||||
mockIndexPattern,
|
||||
} from '../../mock';
|
||||
import { FilterManager } from '../../../../../../../src/plugins/data/public';
|
||||
import { createStore, State } from '../../store';
|
||||
|
||||
import { Props } from './top_n';
|
||||
import { StatefulTopN } from '.';
|
||||
import {
|
||||
ManageGlobalTimeline,
|
||||
getTimelineDefaults,
|
||||
} from '../../../timelines/components/manage_timeline';
|
||||
import { TimelineId } from '../../../../common/types/timeline';
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
|
||||
jest.mock('react-router-dom', () => {
|
||||
const original = jest.requireActual('react-router-dom');
|
||||
|
@ -45,8 +39,6 @@ jest.mock('../link_to');
|
|||
jest.mock('../../lib/kibana');
|
||||
jest.mock('../../../timelines/store/timeline/actions');
|
||||
|
||||
const mockUiSettingsForFilterManager = coreMock.createStart().uiSettings;
|
||||
|
||||
const field = 'process.name';
|
||||
const value = 'nice';
|
||||
|
||||
|
@ -175,9 +167,7 @@ describe('StatefulTopN', () => {
|
|||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ManageGlobalTimeline>
|
||||
<StatefulTopN {...testProps} />
|
||||
</ManageGlobalTimeline>
|
||||
<StatefulTopN {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
});
|
||||
|
@ -244,26 +234,16 @@ describe('StatefulTopN', () => {
|
|||
});
|
||||
|
||||
describe('rendering in a timeline context', () => {
|
||||
let filterManager: FilterManager;
|
||||
let wrapper: ReactWrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
filterManager = new FilterManager(mockUiSettingsForFilterManager);
|
||||
const manageTimelineForTesting = {
|
||||
[TimelineId.active]: {
|
||||
...getTimelineDefaults(TimelineId.active),
|
||||
filterManager,
|
||||
},
|
||||
};
|
||||
testProps = {
|
||||
...testProps,
|
||||
timelineId: TimelineId.active,
|
||||
};
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}>
|
||||
<StatefulTopN {...testProps} />
|
||||
</ManageGlobalTimeline>
|
||||
<StatefulTopN {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
});
|
||||
|
@ -320,25 +300,13 @@ describe('StatefulTopN', () => {
|
|||
});
|
||||
describe('rendering in a NON-active timeline context', () => {
|
||||
test(`defaults to the 'Alert events' option when rendering in a NON-active timeline context (e.g. the Alerts table on the Detections page) when 'documentType' from 'useTimelineTypeContext()' is 'alerts'`, async () => {
|
||||
const filterManager = new FilterManager(mockUiSettingsForFilterManager);
|
||||
|
||||
const manageTimelineForTesting = {
|
||||
[TimelineId.active]: {
|
||||
...getTimelineDefaults(TimelineId.active),
|
||||
filterManager,
|
||||
documentType: 'alerts',
|
||||
},
|
||||
};
|
||||
|
||||
testProps = {
|
||||
...testProps,
|
||||
timelineId: TimelineId.detectionsPage,
|
||||
};
|
||||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}>
|
||||
<StatefulTopN {...testProps} />
|
||||
</ManageGlobalTimeline>
|
||||
<StatefulTopN {...testProps} />
|
||||
</TestProviders>
|
||||
);
|
||||
await waitFor(() => {
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
*/
|
||||
|
||||
import { EuiPopover } from '@elastic/eui';
|
||||
import {
|
||||
HOVER_ACTIONS_ALWAYS_SHOW_CLASS_NAME,
|
||||
IS_DRAGGING_CLASS_NAME,
|
||||
} from '@kbn/securitysolution-t-grid';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { IS_DRAGGING_CLASS_NAME } from '../drag_and_drop/helpers';
|
||||
|
||||
export const HOVER_ACTIONS_ALWAYS_SHOW_CLASS_NAME = 'hover-actions-always-show';
|
||||
|
||||
/**
|
||||
* To avoid expensive changes to the DOM, delay showing the popover menu
|
||||
*/
|
||||
|
|
|
@ -83,7 +83,7 @@ export const useTimelineLastEventTime = ({
|
|||
TimelineEventsLastEventTimeRequestOptions,
|
||||
TimelineEventsLastEventTimeStrategyResponse
|
||||
>(request, {
|
||||
strategy: 'securitySolutionTimelineSearchStrategy',
|
||||
strategy: 'timelineSearchStrategy',
|
||||
abortSignal: abortCtrl.current.signal,
|
||||
})
|
||||
.subscribe({
|
||||
|
|
|
@ -151,7 +151,7 @@ export const useFetchIndex = (
|
|||
{ indices: iNames, onlyCheckIfIndicesExist },
|
||||
{
|
||||
abortSignal: abortCtrl.current.signal,
|
||||
strategy: 'securitySolutionIndexFields',
|
||||
strategy: 'indexFields',
|
||||
}
|
||||
)
|
||||
.subscribe({
|
||||
|
@ -235,7 +235,7 @@ export const useIndexFields = (sourcererScopeName: SourcererScopeName) => {
|
|||
{ indices: indicesName, onlyCheckIfIndicesExist: false },
|
||||
{
|
||||
abortSignal: abortCtrl.current.signal,
|
||||
strategy: 'securitySolutionIndexFields',
|
||||
strategy: 'indexFields',
|
||||
}
|
||||
)
|
||||
.subscribe({
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { IEsError } from 'src/plugins/data/public';
|
||||
import { KibanaError, SecurityAppError } from '@kbn/securitysolution-t-grid';
|
||||
|
||||
import { useToasts } from '../lib/kibana';
|
||||
import { KibanaError, SecurityAppError } from '../utils/api';
|
||||
|
||||
import {
|
||||
appErrorToErrorStack,
|
||||
convertErrorToEnumerable,
|
||||
|
|
|
@ -7,11 +7,17 @@
|
|||
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { isString } from 'lodash/fp';
|
||||
import {
|
||||
AppError,
|
||||
isAppError,
|
||||
isKibanaError,
|
||||
isSecurityAppError,
|
||||
} from '@kbn/securitysolution-t-grid';
|
||||
|
||||
import { IEsError, isEsError } from '../../../../../../src/plugins/data/public';
|
||||
|
||||
import { ErrorToastOptions, ToastsStart, Toast } from '../../../../../../src/core/public';
|
||||
import { useToasts } from '../lib/kibana';
|
||||
import { AppError, isAppError, isKibanaError, isSecurityAppError } from '../utils/api';
|
||||
|
||||
export type UseAppToasts = Pick<ToastsStart, 'addSuccess' | 'addWarning'> & {
|
||||
api: ToastsStart;
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
*/
|
||||
|
||||
import { EuiToolTip } from '@elastic/eui';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { TooltipWithKeyboardShortcut } from '../../components/accessibility/tooltip_with_keyboard_shortcut';
|
||||
import { TooltipWithKeyboardShortcut } from '../../components/accessibility';
|
||||
import * as i18n from '../../components/drag_and_drop/translations';
|
||||
|
||||
import { Clipboard } from './clipboard';
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
*/
|
||||
|
||||
import { notificationServiceMock } from '../../../../../../../../src/core/public/mocks';
|
||||
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { createTGridMocks } from '../../../../../../timelines/public/mock';
|
||||
|
||||
import {
|
||||
createKibanaContextProviderMock,
|
||||
createUseUiSettingMock,
|
||||
|
@ -30,14 +34,24 @@ export const useKibana = jest.fn().mockReturnValue({
|
|||
})),
|
||||
})),
|
||||
},
|
||||
query: {
|
||||
...mockStartServicesMock.data.query,
|
||||
filterManager: {
|
||||
addFilters: jest.fn(),
|
||||
getFilters: jest.fn(),
|
||||
getUpdates$: jest.fn().mockReturnValue({ subscribe: jest.fn() }),
|
||||
setAppFilters: jest.fn(),
|
||||
},
|
||||
},
|
||||
},
|
||||
timelines: createTGridMocks(),
|
||||
},
|
||||
});
|
||||
export const useUiSetting = jest.fn(createUseUiSettingMock());
|
||||
export const useUiSetting$ = jest.fn(createUseUiSetting$Mock());
|
||||
export const useHttp = jest.fn().mockReturnValue(createStartServicesMock().http);
|
||||
export const useTimeZone = jest.fn();
|
||||
export const useDateFormat = jest.fn();
|
||||
export const useDateFormat = jest.fn().mockReturnValue('MMM D, YYYY @ HH:mm:ss.SSS');
|
||||
export const useBasePath = jest.fn(() => '/test/base/path');
|
||||
export const useToasts = jest
|
||||
.fn()
|
||||
|
|
|
@ -43,6 +43,7 @@ export const mockGlobalState: State = {
|
|||
trustedAppsByPolicyEnabled: false,
|
||||
metricsEntitiesEnabled: false,
|
||||
ruleRegistryEnabled: false,
|
||||
tGridEnabled: false,
|
||||
},
|
||||
},
|
||||
hosts: {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { ColumnHeaderOptions } from '../../timelines/store/timeline/model';
|
||||
import { ColumnHeaderOptions } from '../../../common';
|
||||
import { defaultColumnHeaderType } from '../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
import {
|
||||
DEFAULT_COLUMN_MIN_WIDTH,
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
EuiPopoverTitle,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { ControlColumnProps } from '../../timelines/components/timeline/body/control_columns';
|
||||
import { ControlColumnProps } from '../../../common/types/timeline';
|
||||
|
||||
const SelectionHeaderCell = () => {
|
||||
return (
|
||||
|
|
|
@ -5,12 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AnyAction, Reducer } from 'redux';
|
||||
import reduceReducers from 'reduce-reducers';
|
||||
|
||||
import { tGridReducer } from '../../../../timelines/public';
|
||||
|
||||
import { hostsReducer } from '../../hosts/store';
|
||||
import { networkReducer } from '../../network/store';
|
||||
import { timelineReducer } from '../../timelines/store/timeline/reducer';
|
||||
import { managementReducer } from '../../management/store/reducer';
|
||||
import { ManagementPluginReducer } from '../../management';
|
||||
import { SubPluginsInitReducer } from '../store';
|
||||
import { mockGlobalState } from './global_state';
|
||||
import { TimelineState } from '../../timelines/store/timeline/types';
|
||||
import { defaultHeaders } from '../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
|
||||
interface Global extends NodeJS.Global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
@ -19,10 +27,32 @@ interface Global extends NodeJS.Global {
|
|||
|
||||
export const globalNode: Global = global;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const combineTimelineReducer = reduceReducers<any>(
|
||||
{
|
||||
...mockGlobalState.timeline,
|
||||
timelineById: {
|
||||
...mockGlobalState.timeline.timelineById,
|
||||
test: {
|
||||
...mockGlobalState.timeline.timelineById.test,
|
||||
defaultColumns: defaultHeaders,
|
||||
loadingText: 'events',
|
||||
footerText: 'events',
|
||||
documentType: '',
|
||||
selectAll: false,
|
||||
queryFields: [],
|
||||
unit: (n: number) => n,
|
||||
},
|
||||
},
|
||||
},
|
||||
tGridReducer,
|
||||
timelineReducer
|
||||
) as Reducer<TimelineState, AnyAction>;
|
||||
|
||||
export const SUB_PLUGINS_REDUCER: SubPluginsInitReducer = {
|
||||
hosts: hostsReducer,
|
||||
network: networkReducer,
|
||||
timeline: timelineReducer,
|
||||
timeline: combineTimelineReducer,
|
||||
/**
|
||||
* These state's are wrapped in `Immutable`, but for compatibility with the overall app architecture,
|
||||
* they are cast to mutable versions here.
|
||||
|
|
|
@ -60,6 +60,7 @@ export interface GlobalGenericQuery {
|
|||
isInspected: boolean;
|
||||
loading: boolean;
|
||||
selectedInspectIndex: number;
|
||||
invalidKqlQuery?: Error;
|
||||
}
|
||||
|
||||
export interface GlobalGraphqlQuery extends GlobalGenericQuery {
|
||||
|
|
|
@ -37,18 +37,6 @@ export type StoreState = HostsPluginState &
|
|||
*/
|
||||
export type State = CombinedState<StoreState>;
|
||||
|
||||
export type KueryFilterQueryKind = 'kuery' | 'lucene' | 'eql';
|
||||
|
||||
export interface KueryFilterQuery {
|
||||
kind: KueryFilterQueryKind;
|
||||
expression: string;
|
||||
}
|
||||
|
||||
export interface SerializedFilterQuery {
|
||||
kuery: KueryFilterQuery | null;
|
||||
serializedQuery: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* like redux's `MiddlewareAPI` but `getState` returns an `Immutable` version of
|
||||
* state and `dispatch` accepts `Immutable` versions of actions.
|
||||
|
|
|
@ -14,6 +14,7 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import type { Filter } from '../../../../../../../src/plugins/data/common/es_query/filters';
|
||||
import {
|
||||
KueryFilterQueryKind,
|
||||
TimelineId,
|
||||
TimelineResult,
|
||||
TimelineStatus,
|
||||
|
@ -44,7 +45,6 @@ import {
|
|||
replaceTemplateFieldFromMatchFilters,
|
||||
replaceTemplateFieldFromDataProviders,
|
||||
} from './helpers';
|
||||
import { KueryFilterQueryKind } from '../../../common/store';
|
||||
import {
|
||||
DataProvider,
|
||||
QueryOperator,
|
||||
|
@ -399,7 +399,7 @@ export const sendAlertToTimelineAction = async ({
|
|||
factoryQueryType: TimelineEventsQueries.details,
|
||||
},
|
||||
{
|
||||
strategy: 'securitySolutionTimelineSearchStrategy',
|
||||
strategy: 'timelineSearchStrategy',
|
||||
}
|
||||
)
|
||||
.toPromise(),
|
||||
|
|
|
@ -11,6 +11,7 @@ import { shallow, mount } from 'enzyme';
|
|||
import { AlertsUtilityBar, AlertsUtilityBarProps } from './index';
|
||||
import { TestProviders } from '../../../../common/mock/test_providers';
|
||||
|
||||
jest.useFakeTimers();
|
||||
jest.mock('../../../../common/lib/kibana');
|
||||
|
||||
describe('AlertsUtilityBar', () => {
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
*/
|
||||
|
||||
import { defaultColumnHeaderType } from '../../../timelines/components/timeline/body/column_headers/default_headers';
|
||||
import { RowRendererId } from '../../../../common/types/timeline';
|
||||
import { ColumnHeaderOptions, RowRendererId } from '../../../../common/types/timeline';
|
||||
import { Status } from '../../../../common/detection_engine/schemas/common/schemas';
|
||||
import { Filter } from '../../../../../../../src/plugins/data/common/es_query';
|
||||
|
||||
import { ColumnHeaderOptions, SubsetTimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { SubsetTimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
import { columns } from '../../configurations/security_solution_detections/columns';
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
import { EuiPanel, EuiLoadingContent } from '@elastic/eui';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import { connect, ConnectedProps, useDispatch } from 'react-redux';
|
||||
import { Dispatch } from 'redux';
|
||||
import { Status } from '../../../../common/detection_engine/schemas/common/schemas';
|
||||
import { Filter, esQuery } from '../../../../../../../src/plugins/data/public';
|
||||
import { TimelineIdLiteral } from '../../../../common/types/timeline';
|
||||
import { RowRendererId, TimelineIdLiteral } from '../../../../common/types/timeline';
|
||||
import { useAppToasts } from '../../../common/hooks/use_app_toasts';
|
||||
import { StatefulEventsViewer } from '../../../common/components/events_viewer';
|
||||
import { HeaderSection } from '../../../common/components/header_section';
|
||||
|
@ -23,8 +23,6 @@ import { inputsSelectors, State, inputsModel } from '../../../common/store';
|
|||
import { timelineActions, timelineSelectors } from '../../../timelines/store/timeline';
|
||||
import { TimelineModel } from '../../../timelines/store/timeline/model';
|
||||
import { timelineDefaults } from '../../../timelines/store/timeline/defaults';
|
||||
import { useManageTimeline } from '../../../timelines/components/manage_timeline';
|
||||
|
||||
import { updateAlertStatusAction } from './actions';
|
||||
import {
|
||||
requiredFieldsForActions,
|
||||
|
@ -95,6 +93,7 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
|
|||
timelineId,
|
||||
to,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const [showClearSelectionAction, setShowClearSelectionAction] = useState(false);
|
||||
const [filterGroup, setFilterGroup] = useState<Status>(FILTER_OPEN);
|
||||
const {
|
||||
|
@ -106,7 +105,6 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
|
|||
const kibana = useKibana();
|
||||
const [, dispatchToaster] = useStateToaster();
|
||||
const { addWarning } = useAppToasts();
|
||||
const { initializeTimeline, setSelectAll } = useManageTimeline();
|
||||
// TODO: Once we are past experimental phase this code should be removed
|
||||
const ruleRegistryEnabled = useIsExperimentalFeatureEnabled('ruleRegistryEnabled');
|
||||
|
||||
|
@ -195,14 +193,16 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
|
|||
// Catches state change isSelectAllChecked->false upon user selection change to reset utility bar
|
||||
useEffect(() => {
|
||||
if (isSelectAllChecked) {
|
||||
setSelectAll({
|
||||
id: timelineId,
|
||||
selectAll: false,
|
||||
});
|
||||
dispatch(
|
||||
timelineActions.setTGridSelectAll({
|
||||
id: timelineId,
|
||||
selectAll: false,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
setShowClearSelectionAction(false);
|
||||
}
|
||||
}, [isSelectAllChecked, setSelectAll, timelineId]);
|
||||
}, [dispatch, isSelectAllChecked, timelineId]);
|
||||
|
||||
// Callback for when open/closed filter changes
|
||||
const onFilterGroupChangedCallback = useCallback(
|
||||
|
@ -218,23 +218,27 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
|
|||
// Callback for clearing entire selection from utility bar
|
||||
const clearSelectionCallback = useCallback(() => {
|
||||
clearSelected!({ id: timelineId });
|
||||
setSelectAll({
|
||||
id: timelineId,
|
||||
selectAll: false,
|
||||
});
|
||||
dispatch(
|
||||
timelineActions.setTGridSelectAll({
|
||||
id: timelineId,
|
||||
selectAll: false,
|
||||
})
|
||||
);
|
||||
setShowClearSelectionAction(false);
|
||||
}, [clearSelected, setSelectAll, setShowClearSelectionAction, timelineId]);
|
||||
}, [clearSelected, dispatch, timelineId]);
|
||||
|
||||
// Callback for selecting all events on all pages from utility bar
|
||||
// Dispatches to stateful_body's selectAll via TimelineTypeContext props
|
||||
// as scope of response data required to actually set selectedEvents
|
||||
const selectAllOnAllPagesCallback = useCallback(() => {
|
||||
setSelectAll({
|
||||
id: timelineId,
|
||||
selectAll: true,
|
||||
});
|
||||
dispatch(
|
||||
timelineActions.setTGridSelectAll({
|
||||
id: timelineId,
|
||||
selectAll: true,
|
||||
})
|
||||
);
|
||||
setShowClearSelectionAction(true);
|
||||
}, [setSelectAll, setShowClearSelectionAction, timelineId]);
|
||||
}, [dispatch, timelineId]);
|
||||
|
||||
const updateAlertsStatusCallback: UpdateAlertsStatusCallback = useCallback(
|
||||
async (
|
||||
|
@ -330,22 +334,22 @@ export const AlertsTableComponent: React.FC<AlertsTableComponentProps> = ({
|
|||
: alertsDefaultModel;
|
||||
|
||||
useEffect(() => {
|
||||
initializeTimeline({
|
||||
defaultModel: {
|
||||
...defaultTimelineModel,
|
||||
columns,
|
||||
},
|
||||
documentType: i18n.ALERTS_DOCUMENT_TYPE,
|
||||
filterManager,
|
||||
footerText: i18n.TOTAL_COUNT_OF_ALERTS,
|
||||
id: timelineId,
|
||||
loadingText: i18n.LOADING_ALERTS,
|
||||
selectAll: false,
|
||||
queryFields: requiredFieldsForActions,
|
||||
title: '',
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
dispatch(
|
||||
timelineActions.initializeTGridSettings({
|
||||
defaultColumns: columns,
|
||||
documentType: i18n.ALERTS_DOCUMENT_TYPE,
|
||||
excludedRowRendererIds: defaultTimelineModel.excludedRowRendererIds as RowRendererId[],
|
||||
filterManager,
|
||||
footerText: i18n.TOTAL_COUNT_OF_ALERTS,
|
||||
id: timelineId,
|
||||
loadingText: i18n.LOADING_ALERTS,
|
||||
selectAll: false,
|
||||
queryFields: requiredFieldsForActions,
|
||||
title: '',
|
||||
showCheckboxes: true,
|
||||
})
|
||||
);
|
||||
}, [dispatch, defaultTimelineModel, filterManager, timelineId]);
|
||||
|
||||
const headerFilterGroup = useMemo(
|
||||
() => <AlertsTableFilterGroup onFilterGroupChanged={onFilterGroupChangedCallback} />,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue