From ae07660f3125958415801580a620b64be9398130 Mon Sep 17 00:00:00 2001 From: achyutjhunjhunwala Date: Mon, 6 Nov 2023 15:56:10 +0100 Subject: [PATCH 01/10] Add logic to display highlights in Flyout --- .../plugins/log_explorer/common/constants.ts | 13 ++ .../flyout_detail/flyout_detail.tsx | 50 ++-- .../flyout_detail/flyout_highlights.tsx | 217 ++++++++++++++++++ .../flyout_detail/fylout_header.tsx | 46 ++++ .../sub_components/highlight_container.tsx | 52 +++++ .../sub_components/highlight_field.tsx | 64 ++++++ .../sub_components/hover_action.tsx | 76 ++++++ .../components/flyout_detail/translations.ts | 127 ++++++++++ .../public/components/flyout_detail/types.ts | 30 +++ .../flyout_detail/use_doc_detail.ts | 59 ++++- .../discover_actions_context.tsx | 17 ++ .../discover_actions/use_discover_action.ts | 16 ++ 12 files changed, 722 insertions(+), 45 deletions(-) create mode 100644 x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx create mode 100644 x-pack/plugins/log_explorer/public/components/flyout_detail/fylout_header.tsx create mode 100644 x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx create mode 100644 x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx create mode 100644 x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx create mode 100644 x-pack/plugins/log_explorer/public/context/discover_actions/discover_actions_context.tsx create mode 100644 x-pack/plugins/log_explorer/public/context/discover_actions/use_discover_action.ts diff --git a/x-pack/plugins/log_explorer/common/constants.ts b/x-pack/plugins/log_explorer/common/constants.ts index 4a93192b13acb..6f4e85b901732 100644 --- a/x-pack/plugins/log_explorer/common/constants.ts +++ b/x-pack/plugins/log_explorer/common/constants.ts @@ -13,6 +13,19 @@ export const HOST_NAME_FIELD = 'host.name'; export const LOG_LEVEL_FIELD = 'log.level'; export const MESSAGE_FIELD = 'message'; export const SERVICE_NAME_FIELD = 'service.name'; +export const TRACE_ID = 'trace.id'; + +export const AGENT_NAME = 'agent.name'; +export const ORCHESTRATOR_CLUSTER_NAME = 'orchestrator.cluster.name'; +export const ORCHESTRATOR_RESOURCE_ID = 'orchestrator.resource.id'; +export const CLOUD_PROVIDER = 'cloud.provider'; +export const CLOUD_REGION = 'cloud.region'; +export const CLOUD_AVAILABILITY_ZONE = 'cloud.availability_zone'; +export const CLOUD_PROJECT_ID = 'cloud.project.id'; +export const CLOUD_INSTANCE_ID = 'cloud.instance.id'; +export const LOG_FILE_PATH = 'log.file.path'; +export const DATASTREAM_NAMESPACE = 'data_stream.namespace'; +export const DATASTREAM_DATASET = 'data_stream.dataset'; // Sizing export const DATA_GRID_COLUMN_WIDTH_SMALL = 240; diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx index 012e5c914ed61..5f58bef6b75e7 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx @@ -6,42 +6,24 @@ */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { LogLevel } from './sub_components/log_level'; -import { Timestamp } from './sub_components/timestamp'; +import { EuiHorizontalRule } from '@elastic/eui'; import { FlyoutProps, LogDocument } from './types'; -import { getDocDetailRenderFlags, useDocDetail } from './use_doc_detail'; -import { Message } from './sub_components/message'; +import { useDocDetail } from './use_doc_detail'; +import { FlyoutHeader } from './fylout_header'; +import { FlyoutHighlights } from './flyout_highlights'; -export function FlyoutDetail({ dataView, doc }: Pick) { +export function FlyoutDetail({ + dataView, + doc, + actions, +}: Pick) { const parsedDoc = useDocDetail(doc as LogDocument, { dataView }); - const { hasTimestamp, hasLogLevel, hasMessage, hasBadges, hasFlyoutHeader } = - getDocDetailRenderFlags(parsedDoc); - - return hasFlyoutHeader ? ( - - - {hasBadges && ( - - {hasLogLevel && ( - - - - )} - {hasTimestamp && ( - - - - )} - - )} - - {hasMessage && ( - - - - )} - - ) : null; + return ( + <> + + + + + ); } diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx new file mode 100644 index 0000000000000..96828a7657834 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx @@ -0,0 +1,217 @@ +/* + * 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 { EuiPanel, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import { FlyoutContentActions } from '@kbn/discover-plugin/public'; +import { DataTableRecord } from '@kbn/discover-utils/src/types'; +import { FlyoutDoc } from './types'; +import * as constants from '../../../common/constants'; +import { HighlightField } from './sub_components/highlight_field'; +import { + cloudAccordionTitle, + flyoutCloudAvailabilityZoneLabel, + flyoutCloudInstanceIdLabel, + flyoutCloudProjectIdLabel, + flyoutCloudProviderLabel, + flyoutCloudRegionLabel, + flyoutDatasetLabel, + flyoutHostNameLabel, + flyoutLogPathFileLabel, + flyoutNamespaceLabel, + flyoutOrchestratorClusterIdLabel, + flyoutOrchestratorResourceIdLabel, + flyoutServiceLabel, + flyoutShipperLabel, + flyoutTraceLabel, + infraAccordionTitle, + otherAccordionTitle, + serviceAccordionTitle, +} from './translations'; +import { HighlightSection } from './sub_components/highlight_container'; +import { DiscoverActionContext } from '../../context/discover_actions/discover_actions_context'; + +export function FlyoutHighlights({ + formattedDoc, + flattenedDoc, + actions, +}: { + formattedDoc: FlyoutDoc; + flattenedDoc: DataTableRecord['flattened']; + actions: FlyoutContentActions; +}) { + return ( + + + + {formattedDoc[constants.SERVICE_NAME_FIELD] && ( + + + + )} + {formattedDoc[constants.TRACE_ID] && ( + + + + )} + + + + {formattedDoc[constants.HOST_NAME_FIELD] && ( + + + + )} + {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME] && ( + + + + )} + {formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID] && ( + + + + )} + + + + {formattedDoc[constants.CLOUD_PROVIDER] && ( + + + + )} + {formattedDoc[constants.CLOUD_REGION] && ( + + + + )} + {formattedDoc[constants.CLOUD_AVAILABILITY_ZONE] && ( + + + + )} + {formattedDoc[constants.CLOUD_PROJECT_ID] && ( + + + + )} + {formattedDoc[constants.CLOUD_INSTANCE_ID] && ( + + + + )} + + + + {formattedDoc[constants.LOG_FILE_PATH] && ( + + + + )} + {formattedDoc[constants.DATASTREAM_NAMESPACE] && ( + + + + )} + {formattedDoc[constants.DATASTREAM_DATASET] && ( + + + + )} + {formattedDoc[constants.AGENT_NAME] && ( + + + + )} + + + + ); +} diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/fylout_header.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/fylout_header.tsx new file mode 100644 index 0000000000000..babca8f273860 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/fylout_header.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FlyoutDoc } from './types'; +import { getDocDetailHeaderRenderFlags } from './use_doc_detail'; +import { LogLevel } from './sub_components/log_level'; +import { Timestamp } from './sub_components/timestamp'; +import { Message } from './sub_components/message'; +import * as constants from '../../../common/constants'; + +export function FlyoutHeader({ doc }: { doc: FlyoutDoc }) { + const { hasTimestamp, hasLogLevel, hasMessage, hasBadges, hasFlyoutHeader } = + getDocDetailHeaderRenderFlags(doc); + + return hasFlyoutHeader ? ( + + + {hasBadges && ( + + {hasLogLevel && ( + + + + )} + {hasTimestamp && ( + + + + )} + + )} + + {hasMessage && ( + + + + )} + + ) : null; +} diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx new file mode 100644 index 0000000000000..e1f033134321c --- /dev/null +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { + EuiAccordion, + EuiFlexGrid, + EuiHorizontalRule, + EuiTitle, + useGeneratedHtmlId, +} from '@elastic/eui'; + +interface HighlightSectionProps { + title: string; + children: React.ReactNode; + showBottomRule?: boolean; +} + +export function HighlightSection({ + title, + children, + showBottomRule = true, +}: HighlightSectionProps) { + const shouldRenderSection = React.Children.toArray(children).filter(Boolean).length > 0; + const accordionTitle = ( + +

{title}

+
+ ); + + const accordionId = useGeneratedHtmlId({ + prefix: title, + }); + + return shouldRenderSection ? ( + <> + + {children} + + {showBottomRule && } + + ) : null; +} diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx new file mode 100644 index 0000000000000..964d42e342611 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx @@ -0,0 +1,64 @@ +/* + * 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 { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import React, { ReactNode, useMemo } from 'react'; +import { HoverAction, HoverActionType } from './hover_action'; +import { flyoutHoverActionFilterForText, flyoutHoverActionFilterOutText } from '../translations'; +import { useDiscoverAction } from '../../../context/discover_actions/use_discover_action'; + +interface HighlightFieldProps { + label: string | ReactNode; + field: string; + value: unknown; + formattedValue: string; + dataTestSubj: string; +} + +export function HighlightField({ + label, + field, + value, + formattedValue, + dataTestSubj, +}: HighlightFieldProps) { + const filterForText = flyoutHoverActionFilterForText(value); + const filterOutText = flyoutHoverActionFilterOutText(value); + const { actions } = useDiscoverAction(); + + const hoverActions: HoverActionType[] = useMemo( + () => [ + { + id: 'addToFilterAction', + tooltipContent: filterForText, + iconType: 'plusInCircle', + onClick: () => actions?.addFilter && actions.addFilter(field, value, '+'), + display: true, + }, + { + id: 'removeFromFilterAction', + tooltipContent: filterOutText, + iconType: 'minusInCircle', + onClick: () => actions?.addFilter && actions.addFilter(field, value, '-'), + display: true, + }, + ], + [actions, field, value, filterForText, filterOutText] + ); + return formattedValue ? ( + + + + {label} + + + + + + + ) : null; +} diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx new file mode 100644 index 0000000000000..fe20920f86976 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx @@ -0,0 +1,76 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiButtonIcon, useEuiTheme } from '@elastic/eui'; +import type { IconType } from '@elastic/eui'; + +export interface HoverActionType { + id: string; + tooltipContent: string; + iconType: IconType; + onClick: () => void; + display: boolean; +} + +interface HoverActionProps { + displayText: string; + actions: HoverActionType[]; +} + +export const HoverAction = ({ displayText, actions }: HoverActionProps) => { + const { euiTheme } = useEuiTheme(); + return ( + + + + {actions.map((action) => ( + + + action.onClick()} + /> + + + ))} + + + ); +}; diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts b/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts index fcb42cf79a5dd..8afe3bd10c72d 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts @@ -10,3 +10,130 @@ import { i18n } from '@kbn/i18n'; export const flyoutMessageLabel = i18n.translate('xpack.logExplorer.flyoutDetail.label.message', { defaultMessage: 'Message', }); + +export const flyoutServiceLabel = i18n.translate('xpack.logExplorer.flyoutDetail.label.service', { + defaultMessage: 'Service', +}); + +export const flyoutTraceLabel = i18n.translate('xpack.logExplorer.flyoutDetail.label.trace', { + defaultMessage: 'Trace', +}); + +export const flyoutHostNameLabel = i18n.translate('xpack.logExplorer.flyoutDetail.label.hostName', { + defaultMessage: 'Host name', +}); + +export const serviceAccordionTitle = i18n.translate( + 'xpack.logExplorer.flyoutDetail.accordion.title.service', + { + defaultMessage: 'Service', + } +); + +export const infraAccordionTitle = i18n.translate( + 'xpack.logExplorer.flyoutDetail.accordion.title.infrastructure', + { + defaultMessage: 'Infrastructure', + } +); + +export const cloudAccordionTitle = i18n.translate( + 'xpack.logExplorer.flyoutDetail.accordion.title.cloud', + { + defaultMessage: 'Cloud', + } +); + +export const otherAccordionTitle = i18n.translate( + 'xpack.logExplorer.flyoutDetail.accordion.title.other', + { + defaultMessage: 'Other', + } +); + +export const flyoutOrchestratorClusterIdLabel = i18n.translate( + 'xpack.logExplorer.flyoutDetail.label.orchestratorClusterId', + { + defaultMessage: 'Orchestrator cluster ID', + } +); + +export const flyoutOrchestratorResourceIdLabel = i18n.translate( + 'xpack.logExplorer.flyoutDetail.label.orchestratorResourceId', + { + defaultMessage: 'Orchestrator resource ID', + } +); + +export const flyoutCloudProviderLabel = i18n.translate( + 'xpack.logExplorer.flyoutDetail.label.cloudProvider', + { + defaultMessage: 'Cloud provider', + } +); + +export const flyoutCloudRegionLabel = i18n.translate( + 'xpack.logExplorer.flyoutDetail.label.cloudRegion', + { + defaultMessage: 'Cloud region', + } +); + +export const flyoutCloudAvailabilityZoneLabel = i18n.translate( + 'xpack.logExplorer.flyoutDetail.label.cloudAvailabilityZone', + { + defaultMessage: 'Cloud availability zone', + } +); + +export const flyoutCloudProjectIdLabel = i18n.translate( + 'xpack.logExplorer.flyoutDetail.label.cloudProjectId', + { + defaultMessage: 'Cloud project ID', + } +); + +export const flyoutCloudInstanceIdLabel = i18n.translate( + 'xpack.logExplorer.flyoutDetail.label.cloudInstanceId', + { + defaultMessage: 'Cloud Instance ID', + } +); + +export const flyoutLogPathFileLabel = i18n.translate( + 'xpack.logExplorer.flyoutDetail.label.logPathFile', + { + defaultMessage: 'Log path file', + } +); + +export const flyoutNamespaceLabel = i18n.translate( + 'xpack.logExplorer.flyoutDetail.label.namespace', + { + defaultMessage: 'Namespace', + } +); + +export const flyoutDatasetLabel = i18n.translate('xpack.logExplorer.flyoutDetail.label.dataset', { + defaultMessage: 'Dataset', +}); + +export const flyoutShipperLabel = i18n.translate('xpack.logExplorer.flyoutDetail.label.shipper', { + defaultMessage: 'Shipper', +}); + +export const flyoutHoverActionFilterForText = (text: unknown) => + i18n.translate('xpack.logExplorer.flyoutDetail.value.hover.filterFor', { + defaultMessage: 'Filter for this {value}', + values: { + value: text as string, + }, + }); + +export const flyoutHoverActionFilterOutText = (text: unknown) => + i18n.translate('xpack.logExplorer.flyoutDetail.value.hover.filterOut', { + defaultMessage: 'Filter out this {value}', + values: { + value: text as string, + }, + }); diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts b/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts index cf8cfb8170e21..fae871bbcfccf 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts @@ -19,6 +19,21 @@ export interface LogDocument extends DataTableRecord { '@timestamp': string; 'log.level'?: string; message?: string; + + 'host.name'?: string; + 'service.name'?: string; + 'trace.id'?: string; + 'agent.name'?: string; + 'orchestrator.cluster.name'?: string; + 'orchestrator.resource.id'?: string; + 'cloud.provider'?: string; + 'cloud.region'?: string; + 'cloud.availability_zone'?: string; + 'cloud.project.id'?: string; + 'cloud.instance.id'?: string; + 'log.file.path'?: string; + 'data_stream.namespace': string; + 'data_stream.dataset': string; }; } @@ -26,6 +41,21 @@ export interface FlyoutDoc { '@timestamp': string; 'log.level'?: string; message?: string; + + 'host.name'?: string; + 'service.name'?: string; + 'trace.id'?: string; + 'agent.name'?: string; + 'orchestrator.cluster.name'?: string; + 'orchestrator.resource.id'?: string; + 'cloud.provider'?: string; + 'cloud.region'?: string; + 'cloud.availability_zone'?: string; + 'cloud.project.id'?: string; + 'cloud.instance.id'?: string; + 'log.file.path'?: string; + 'data_stream.namespace': string; + 'data_stream.dataset': string; } export interface FlyoutHighlightField { diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/use_doc_detail.ts b/x-pack/plugins/log_explorer/public/components/flyout_detail/use_doc_detail.ts index 32e4bcd966745..166a491e630ce 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/use_doc_detail.ts +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/use_doc_detail.ts @@ -5,7 +5,7 @@ * 2.0. */ import { formatFieldValue } from '@kbn/discover-utils'; -import { LOG_LEVEL_FIELD, MESSAGE_FIELD, TIMESTAMP_FIELD } from '../../../common/constants'; +import * as constants from '../../../common/constants'; import { useKibanaContextForPlugin } from '../../utils/use_kibana'; import { FlyoutDoc, FlyoutProps, LogDocument } from './types'; @@ -30,21 +30,58 @@ export function useDocDetail( ); }; - const level = formatField(LOG_LEVEL_FIELD)?.toLowerCase(); - const timestamp = formatField(TIMESTAMP_FIELD); - const message = formatField(MESSAGE_FIELD); + // Flyout Headers + const level = formatField(constants.LOG_LEVEL_FIELD)?.toLowerCase(); + const timestamp = formatField(constants.TIMESTAMP_FIELD); + const message = formatField(constants.MESSAGE_FIELD); + + // Service Highlights + const serviceName = formatField(constants.SERVICE_NAME_FIELD); + const traceId = formatField(constants.TRACE_ID); + + // Infrastructure Highlights + const hostname = formatField(constants.HOST_NAME_FIELD); + const orchestratorClusterName = formatField(constants.ORCHESTRATOR_CLUSTER_NAME); + const orchestratorResourceId = formatField(constants.ORCHESTRATOR_RESOURCE_ID); + + // Cloud Highlights + const cloudProvider = formatField(constants.CLOUD_PROVIDER); + const cloudRegion = formatField(constants.CLOUD_REGION); + const cloudAz = formatField(constants.CLOUD_AVAILABILITY_ZONE); + const cloudProjectId = formatField(constants.CLOUD_PROJECT_ID); + const cloudInstanceId = formatField(constants.CLOUD_INSTANCE_ID); + + // Other Highlights + const logFilePath = formatField(constants.LOG_FILE_PATH); + const namespace = formatField(constants.DATASTREAM_NAMESPACE); + const dataset = formatField(constants.DATASTREAM_DATASET); + const agentName = formatField(constants.AGENT_NAME); return { - [LOG_LEVEL_FIELD]: level, - [TIMESTAMP_FIELD]: timestamp, - [MESSAGE_FIELD]: message, + [constants.LOG_LEVEL_FIELD]: level, + [constants.TIMESTAMP_FIELD]: timestamp, + [constants.MESSAGE_FIELD]: message, + [constants.SERVICE_NAME_FIELD]: serviceName, + [constants.TRACE_ID]: traceId, + [constants.HOST_NAME_FIELD]: hostname, + [constants.ORCHESTRATOR_CLUSTER_NAME]: orchestratorClusterName, + [constants.ORCHESTRATOR_RESOURCE_ID]: orchestratorResourceId, + [constants.CLOUD_PROVIDER]: cloudProvider, + [constants.CLOUD_REGION]: cloudRegion, + [constants.CLOUD_AVAILABILITY_ZONE]: cloudAz, + [constants.CLOUD_PROJECT_ID]: cloudProjectId, + [constants.CLOUD_INSTANCE_ID]: cloudInstanceId, + [constants.LOG_FILE_PATH]: logFilePath, + [constants.DATASTREAM_NAMESPACE]: namespace, + [constants.DATASTREAM_DATASET]: dataset, + [constants.AGENT_NAME]: agentName, }; } -export const getDocDetailRenderFlags = (doc: FlyoutDoc) => { - const hasTimestamp = Boolean(doc['@timestamp']); - const hasLogLevel = Boolean(doc['log.level']); - const hasMessage = Boolean(doc.message); +export const getDocDetailHeaderRenderFlags = (doc: FlyoutDoc) => { + const hasTimestamp = Boolean(doc[constants.TIMESTAMP_FIELD]); + const hasLogLevel = Boolean(doc[constants.LOG_LEVEL_FIELD]); + const hasMessage = Boolean(doc[constants.MESSAGE_FIELD]); const hasBadges = hasTimestamp || hasLogLevel; diff --git a/x-pack/plugins/log_explorer/public/context/discover_actions/discover_actions_context.tsx b/x-pack/plugins/log_explorer/public/context/discover_actions/discover_actions_context.tsx new file mode 100644 index 0000000000000..3fb999374f9e8 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/context/discover_actions/discover_actions_context.tsx @@ -0,0 +1,17 @@ +/* + * 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 { createContext } from 'react'; +import { FlyoutContentActions } from '@kbn/discover-plugin/public'; + +export const DiscoverActionContext = createContext<{ actions: FlyoutContentActions }>({ + actions: { + addFilter: () => {}, + addColumn: () => {}, + removeColumn: () => {}, + }, +}); diff --git a/x-pack/plugins/log_explorer/public/context/discover_actions/use_discover_action.ts b/x-pack/plugins/log_explorer/public/context/discover_actions/use_discover_action.ts new file mode 100644 index 0000000000000..4e2d18d7f2a82 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/context/discover_actions/use_discover_action.ts @@ -0,0 +1,16 @@ +/* + * 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 { useContext } from 'react'; +import { DiscoverActionContext } from './discover_actions_context'; + +export function useDiscoverAction() { + const ctx = useContext(DiscoverActionContext); + if (!ctx) { + throw new Error('DiscoverActionContext can only be used inside of FlyoutHighlights!'); + } + return ctx; +} From 41b5e36736d229caef4cdc7536ef3b2c68ac49fa Mon Sep 17 00:00:00 2001 From: achyutjhunjhunwala Date: Tue, 7 Nov 2023 14:53:50 +0100 Subject: [PATCH 02/10] Add Stateful tests --- .../flyout_detail/flyout_highlights.tsx | 6 +- .../sub_components/highlight_container.tsx | 1 + .../components/flyout_detail/translations.ts | 6 +- .../apps/observability_log_explorer/flyout.ts | 2 +- .../flyout_highlights.ts | 281 ++++++++++++++++++ .../apps/observability_log_explorer/index.ts | 1 + .../observability_log_explorer.ts | 36 ++- 7 files changed, 325 insertions(+), 8 deletions(-) create mode 100644 x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx index 96828a7657834..237f831a9e27e 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx @@ -22,7 +22,7 @@ import { flyoutHostNameLabel, flyoutLogPathFileLabel, flyoutNamespaceLabel, - flyoutOrchestratorClusterIdLabel, + flyoutOrchestratorClusterNameLabel, flyoutOrchestratorResourceIdLabel, flyoutServiceLabel, flyoutShipperLabel, @@ -86,11 +86,11 @@ export function FlyoutHighlights({ {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME] && ( )} diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx index e1f033134321c..0a7e718963558 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx @@ -43,6 +43,7 @@ export function HighlightSection({ buttonContent={accordionTitle} paddingSize="s" initialIsOpen={true} + data-test-subj={title} > {children} diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts b/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts index 8afe3bd10c72d..9479f030b1807 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts @@ -51,10 +51,10 @@ export const otherAccordionTitle = i18n.translate( } ); -export const flyoutOrchestratorClusterIdLabel = i18n.translate( - 'xpack.logExplorer.flyoutDetail.label.orchestratorClusterId', +export const flyoutOrchestratorClusterNameLabel = i18n.translate( + 'xpack.logExplorer.flyoutDetail.label.orchestratorClusterName', { - defaultMessage: 'Orchestrator cluster ID', + defaultMessage: 'Orchestrator cluster Name', } ); diff --git a/x-pack/test/functional/apps/observability_log_explorer/flyout.ts b/x-pack/test/functional/apps/observability_log_explorer/flyout.ts index f8277db16e8db..c394f0ff0f34d 100644 --- a/x-pack/test/functional/apps/observability_log_explorer/flyout.ts +++ b/x-pack/test/functional/apps/observability_log_explorer/flyout.ts @@ -54,7 +54,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - after('clean up archives', async () => { + after('clean up DataStream', async () => { if (cleanupDataStreamSetup) { cleanupDataStreamSetup(); } diff --git a/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts b/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts new file mode 100644 index 0000000000000..3cf4f62989387 --- /dev/null +++ b/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts @@ -0,0 +1,281 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +const DATASET_NAME = 'flyout'; +const NAMESPACE = 'default'; +const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`; +const NOW = Date.now(); + +const sharedDoc = { + time: NOW + 1000, + logFilepath: '/flyout.log', + serviceName: 'frontend-node', + datasetName: DATASET_NAME, + namespace: NAMESPACE, + message: 'full document', + logLevel: 'info', + traceId: 'abcdef', + hostName: 'gke-edge-oblt-pool', + orchestratorClusterId: 'my-cluster-id', + orchestratorClusterName: 'my-cluster-id', + orchestratorResourceId: 'orchestratorResourceId', + cloudProvider: 'gcp', + cloudRegion: 'us-central-1', + cloudAz: 'us-central-1a', + cloudProjectId: 'elastic-project', + cloudInstanceId: 'BgfderflkjTheUiGuy', + agentName: 'node', +}; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const dataGrid = getService('dataGrid'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['observabilityLogExplorer']); + + describe('Flyout highlight customization', () => { + let cleanupDataStreamSetup: () => Promise; + + xdescribe('Service container', () => { + const { serviceName, traceId, ...rest } = sharedDoc; + const docWithoutServiceName = { ...rest, traceId, time: NOW - 1000 }; + const docWithoutTraceId = { ...rest, serviceName, time: NOW - 2000 }; + const docWithoutServiceContainer = { ...rest, time: NOW - 4000 }; + + const docs = [ + sharedDoc, + docWithoutServiceName, + docWithoutTraceId, + docWithoutServiceContainer, + ]; + before('setup DataStream', async () => { + cleanupDataStreamSetup = await PageObjects.observabilityLogExplorer.setupDataStream( + DATASET_NAME, + NAMESPACE + ); + await PageObjects.observabilityLogExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); + }); + + after('clean up DataStream', async () => { + if (cleanupDataStreamSetup) { + await cleanupDataStreamSetup(); + } + }); + + beforeEach(async () => { + await PageObjects.observabilityLogExplorer.navigateTo({ + from: new Date(NOW - 60_000).toISOString(), + to: new Date(NOW + 60_000).toISOString(), + }); + }); + + it('should load the service container with all fields', async () => { + await dataGrid.clickRowToggle(); + await testSubjects.existOrFail('Service'); + await testSubjects.existOrFail('logExplorerFlyoutService'); + await testSubjects.existOrFail('logExplorerFlyoutTrace'); + await dataGrid.closeFlyout(); + }); + + it('should load the service container even when 1 field is missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 1 }); + await testSubjects.existOrFail('Service'); + await testSubjects.missingOrFail('logExplorerFlyoutService'); + await testSubjects.existOrFail('logExplorerFlyoutTrace'); + await dataGrid.closeFlyout(); + }); + + it('should not load the service container if all fields are missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 3 }); + await testSubjects.missingOrFail('Service'); + await testSubjects.missingOrFail('logExplorerFlyoutService'); + await testSubjects.missingOrFail('logExplorerFlyoutTrace'); + await dataGrid.closeFlyout(); + }); + }); + + xdescribe('Infrastructure container', () => { + const { hostName, orchestratorClusterName, orchestratorResourceId, ...rest } = sharedDoc; + const docWithoutHostName = { + ...rest, + orchestratorClusterName, + orchestratorResourceId, + time: NOW - 1000, + }; + const docWithoutInfrastructureContainer = { ...rest, time: NOW - 2000 }; + + const docs = [sharedDoc, docWithoutHostName, docWithoutInfrastructureContainer]; + before('setup DataStream', async () => { + cleanupDataStreamSetup = await PageObjects.observabilityLogExplorer.setupDataStream( + DATASET_NAME, + NAMESPACE + ); + await PageObjects.observabilityLogExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); + }); + + after('clean up DataStream', async () => { + if (cleanupDataStreamSetup) { + await cleanupDataStreamSetup(); + } + }); + + beforeEach(async () => { + await PageObjects.observabilityLogExplorer.navigateTo({ + from: new Date(NOW - 60_000).toISOString(), + to: new Date(NOW + 60_000).toISOString(), + }); + }); + + it('should load the infrastructure container with all fields', async () => { + await dataGrid.clickRowToggle(); + await testSubjects.existOrFail('Infrastructure'); + await testSubjects.existOrFail('logExplorerFlyoutHostName'); + await testSubjects.existOrFail('logExplorerFlyoutClusterName'); + await testSubjects.existOrFail('logExplorerFlyoutResourceId'); + await dataGrid.closeFlyout(); + }); + + it('should load the infrastructure container even when 1 field is missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 1 }); + await testSubjects.existOrFail('Infrastructure'); + await testSubjects.missingOrFail('logExplorerFlyoutHostName'); + await testSubjects.existOrFail('logExplorerFlyoutClusterName'); + await testSubjects.existOrFail('logExplorerFlyoutResourceId'); + await dataGrid.closeFlyout(); + }); + + it('should not load the infrastructure container if all fields are missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 2 }); + await testSubjects.missingOrFail('Infrastructure'); + await testSubjects.missingOrFail('logExplorerFlyoutHostName'); + await testSubjects.missingOrFail('logExplorerFlyoutClusterName'); + await testSubjects.missingOrFail('logExplorerFlyoutResourceId'); + await dataGrid.closeFlyout(); + }); + }); + + xdescribe('Cloud container', () => { + const { cloudProvider, cloudInstanceId, cloudProjectId, cloudRegion, cloudAz, ...rest } = + sharedDoc; + const docWithoutCloudProviderAndInstanceId = { + ...rest, + cloudProjectId, + cloudRegion, + cloudAz, + time: NOW - 1000, + }; + const docWithoutCloudContainer = { ...rest, time: NOW - 2000 }; + + const docs = [sharedDoc, docWithoutCloudProviderAndInstanceId, docWithoutCloudContainer]; + before('setup DataStream', async () => { + cleanupDataStreamSetup = await PageObjects.observabilityLogExplorer.setupDataStream( + DATASET_NAME, + NAMESPACE + ); + await PageObjects.observabilityLogExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); + }); + + after('clean up DataStream', async () => { + if (cleanupDataStreamSetup) { + await cleanupDataStreamSetup(); + } + }); + + beforeEach(async () => { + await PageObjects.observabilityLogExplorer.navigateTo({ + from: new Date(NOW - 60_000).toISOString(), + to: new Date(NOW + 60_000).toISOString(), + }); + }); + + it('should load the cloud container with all fields', async () => { + await dataGrid.clickRowToggle(); + await testSubjects.existOrFail('Cloud'); + await testSubjects.existOrFail('logExplorerFlyoutCloudProvider'); + await testSubjects.existOrFail('logExplorerFlyoutCloudRegion'); + await testSubjects.existOrFail('logExplorerFlyoutCloudAz'); + await testSubjects.existOrFail('logExplorerFlyoutCloudProjectId'); + await testSubjects.existOrFail('logExplorerFlyoutCloudInstanceId'); + await dataGrid.closeFlyout(); + }); + + it('should load the cloud container even when some fields are missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 1 }); + await testSubjects.existOrFail('Cloud'); + + await testSubjects.missingOrFail('logExplorerFlyoutCloudProvider'); + await testSubjects.missingOrFail('logExplorerFlyoutCloudInstanceId'); + + await testSubjects.existOrFail('logExplorerFlyoutCloudRegion'); + await testSubjects.existOrFail('logExplorerFlyoutCloudAz'); + await dataGrid.closeFlyout(); + }); + + it('should not load the cloud container if all fields are missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 2 }); + await testSubjects.missingOrFail('logExplorerFlyoutCloudProvider'); + await testSubjects.missingOrFail('logExplorerFlyoutCloudRegion'); + await testSubjects.missingOrFail('logExplorerFlyoutCloudAz'); + await testSubjects.missingOrFail('logExplorerFlyoutCloudProjectId'); + await testSubjects.missingOrFail('logExplorerFlyoutCloudInstanceId'); + await dataGrid.closeFlyout(); + }); + }); + + describe('Other container', () => { + const { logFilepath, agentName, ...rest } = sharedDoc; + const docWithoutLogPathAndAgentName = { + ...rest, + time: NOW - 1000, + }; + + const docs = [sharedDoc, docWithoutLogPathAndAgentName]; + before('setup DataStream', async () => { + cleanupDataStreamSetup = await PageObjects.observabilityLogExplorer.setupDataStream( + DATASET_NAME, + NAMESPACE + ); + await PageObjects.observabilityLogExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); + }); + + after('clean up DataStream', async () => { + if (cleanupDataStreamSetup) { + await cleanupDataStreamSetup(); + } + }); + + beforeEach(async () => { + await PageObjects.observabilityLogExplorer.navigateTo({ + from: new Date(NOW - 60_000).toISOString(), + to: new Date(NOW + 60_000).toISOString(), + }); + }); + + it('should load the other container with all fields', async () => { + await dataGrid.clickRowToggle(); + await testSubjects.existOrFail('Other'); + await testSubjects.existOrFail('logExplorerFlyoutLogPathFile'); + await testSubjects.existOrFail('logExplorerFlyoutNamespace'); + await testSubjects.existOrFail('logExplorerFlyoutDataset'); + await testSubjects.existOrFail('logExplorerFlyoutLogShipper'); + await dataGrid.closeFlyout(); + }); + + it('should load the other container even when some fields are missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 1 }); + await testSubjects.existOrFail('Other'); + + await testSubjects.missingOrFail('logExplorerFlyoutLogPathFile'); + await testSubjects.missingOrFail('logExplorerFlyoutLogShipper'); + + await testSubjects.existOrFail('logExplorerFlyoutNamespace'); + await testSubjects.existOrFail('logExplorerFlyoutDataset'); + await dataGrid.closeFlyout(); + }); + }); + }); +} diff --git a/x-pack/test/functional/apps/observability_log_explorer/index.ts b/x-pack/test/functional/apps/observability_log_explorer/index.ts index 948910dab6ab4..7a767baa887de 100644 --- a/x-pack/test/functional/apps/observability_log_explorer/index.ts +++ b/x-pack/test/functional/apps/observability_log_explorer/index.ts @@ -16,5 +16,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./filter_controls')); loadTestFile(require.resolve('./flyout')); loadTestFile(require.resolve('./header_menu')); + loadTestFile(require.resolve('./flyout_highlights.ts')); }); } diff --git a/x-pack/test/functional/page_objects/observability_log_explorer.ts b/x-pack/test/functional/page_objects/observability_log_explorer.ts index 2978e3205f464..0b3266465c32e 100644 --- a/x-pack/test/functional/page_objects/observability_log_explorer.ts +++ b/x-pack/test/functional/page_objects/observability_log_explorer.ts @@ -406,12 +406,24 @@ export function ObservabilityLogExplorerPageObject({ interface MockLogDoc { time: number; - logFilepath: string; + logFilepath?: string; serviceName?: string; namespace: string; datasetName: string; message?: string; logLevel?: string; + traceId?: string; + hostName?: string; + orchestratorClusterId?: string; + orchestratorClusterName?: string; + orchestratorResourceId?: string; + cloudProvider?: string; + cloudRegion?: string; + cloudAz?: string; + cloudProjectId?: string; + cloudInstanceId?: string; + agentName?: string; + [key: string]: unknown; } @@ -423,6 +435,17 @@ export function createLogDoc({ datasetName, message, logLevel, + traceId, + hostName, + orchestratorClusterId, + orchestratorClusterName, + orchestratorResourceId, + cloudProvider, + cloudRegion, + cloudAz, + cloudProjectId, + cloudInstanceId, + agentName, ...extraFields }: MockLogDoc) { return { @@ -452,6 +475,17 @@ export function createLogDoc({ dataset: datasetName, }, ...(logLevel && { 'log.level': logLevel }), + ...(traceId && { 'trace.id': traceId }), + ...(hostName && { 'host.name': hostName }), + ...(orchestratorClusterId && { 'orchestrator.cluster.id': orchestratorClusterId }), + ...(orchestratorClusterName && { 'orchestrator.cluster.name': orchestratorClusterName }), + ...(orchestratorResourceId && { 'orchestrator.resource.id': orchestratorResourceId }), + ...(cloudProvider && { 'cloud.provider': cloudProvider }), + ...(cloudRegion && { 'cloud.region': cloudRegion }), + ...(cloudAz && { 'cloud.availability_zone': cloudAz }), + ...(cloudProjectId && { 'cloud.project.id': cloudProjectId }), + ...(cloudInstanceId && { 'cloud.instance.id': cloudInstanceId }), + ...(agentName && { 'agent.name': agentName }), ...extraFields, }; } From f8008d24c5bff1f570fa7dc0248510994e97c1e8 Mon Sep 17 00:00:00 2001 From: achyutjhunjhunwala Date: Tue, 7 Nov 2023 15:16:03 +0100 Subject: [PATCH 03/10] Add Serverless tests --- .../flyout_highlights.ts | 6 +- .../flyout_highlights.ts | 289 ++++++++++++++++++ .../observability_log_explorer/index.ts | 2 + 3 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/flyout_highlights.ts diff --git a/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts b/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts index 3cf4f62989387..0830495aeab0b 100644 --- a/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts +++ b/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts @@ -40,7 +40,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { describe('Flyout highlight customization', () => { let cleanupDataStreamSetup: () => Promise; - xdescribe('Service container', () => { + describe('Service container', () => { const { serviceName, traceId, ...rest } = sharedDoc; const docWithoutServiceName = { ...rest, traceId, time: NOW - 1000 }; const docWithoutTraceId = { ...rest, serviceName, time: NOW - 2000 }; @@ -98,7 +98,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - xdescribe('Infrastructure container', () => { + describe('Infrastructure container', () => { const { hostName, orchestratorClusterName, orchestratorResourceId, ...rest } = sharedDoc; const docWithoutHostName = { ...rest, @@ -158,7 +158,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - xdescribe('Cloud container', () => { + describe('Cloud container', () => { const { cloudProvider, cloudInstanceId, cloudProjectId, cloudRegion, cloudAz, ...rest } = sharedDoc; const docWithoutCloudProviderAndInstanceId = { diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/flyout_highlights.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/flyout_highlights.ts new file mode 100644 index 0000000000000..995fb9cb7cdee --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/flyout_highlights.ts @@ -0,0 +1,289 @@ +/* + * 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 { FtrProviderContext } from '../../../ftr_provider_context'; + +const DATASET_NAME = 'flyout'; +const NAMESPACE = 'default'; +const DATA_STREAM_NAME = `logs-${DATASET_NAME}-${NAMESPACE}`; +const NOW = Date.now(); + +const sharedDoc = { + time: NOW + 1000, + logFilepath: '/flyout.log', + serviceName: 'frontend-node', + datasetName: DATASET_NAME, + namespace: NAMESPACE, + message: 'full document', + logLevel: 'info', + traceId: 'abcdef', + hostName: 'gke-edge-oblt-pool', + orchestratorClusterId: 'my-cluster-id', + orchestratorClusterName: 'my-cluster-id', + orchestratorResourceId: 'orchestratorResourceId', + cloudProvider: 'gcp', + cloudRegion: 'us-central-1', + cloudAz: 'us-central-1a', + cloudProjectId: 'elastic-project', + cloudInstanceId: 'BgfderflkjTheUiGuy', + agentName: 'node', +}; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const dataGrid = getService('dataGrid'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['observabilityLogExplorer', 'svlCommonPage']); + + describe('Flyout highlight customization', () => { + let cleanupDataStreamSetup: () => Promise; + + describe('Service container', () => { + const { serviceName, traceId, ...rest } = sharedDoc; + const docWithoutServiceName = { ...rest, traceId, time: NOW - 1000 }; + const docWithoutTraceId = { ...rest, serviceName, time: NOW - 2000 }; + const docWithoutServiceContainer = { ...rest, time: NOW - 4000 }; + + const docs = [ + sharedDoc, + docWithoutServiceName, + docWithoutTraceId, + docWithoutServiceContainer, + ]; + before('setup DataStream', async () => { + cleanupDataStreamSetup = await PageObjects.observabilityLogExplorer.setupDataStream( + DATASET_NAME, + NAMESPACE + ); + await PageObjects.observabilityLogExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); + await PageObjects.svlCommonPage.login(); + }); + + after('clean up DataStream', async () => { + await PageObjects.svlCommonPage.forceLogout(); + if (cleanupDataStreamSetup) { + await cleanupDataStreamSetup(); + } + }); + + beforeEach(async () => { + await PageObjects.observabilityLogExplorer.navigateTo({ + from: new Date(NOW - 60_000).toISOString(), + to: new Date(NOW + 60_000).toISOString(), + }); + }); + + it('should load the service container with all fields', async () => { + await dataGrid.clickRowToggle(); + await testSubjects.existOrFail('Service'); + await testSubjects.existOrFail('logExplorerFlyoutService'); + await testSubjects.existOrFail('logExplorerFlyoutTrace'); + await dataGrid.closeFlyout(); + }); + + it('should load the service container even when 1 field is missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 1 }); + await testSubjects.existOrFail('Service'); + await testSubjects.missingOrFail('logExplorerFlyoutService'); + await testSubjects.existOrFail('logExplorerFlyoutTrace'); + await dataGrid.closeFlyout(); + }); + + it('should not load the service container if all fields are missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 3 }); + await testSubjects.missingOrFail('Service'); + await testSubjects.missingOrFail('logExplorerFlyoutService'); + await testSubjects.missingOrFail('logExplorerFlyoutTrace'); + await dataGrid.closeFlyout(); + }); + }); + + describe('Infrastructure container', () => { + const { hostName, orchestratorClusterName, orchestratorResourceId, ...rest } = sharedDoc; + const docWithoutHostName = { + ...rest, + orchestratorClusterName, + orchestratorResourceId, + time: NOW - 1000, + }; + const docWithoutInfrastructureContainer = { ...rest, time: NOW - 2000 }; + + const docs = [sharedDoc, docWithoutHostName, docWithoutInfrastructureContainer]; + before('setup DataStream', async () => { + cleanupDataStreamSetup = await PageObjects.observabilityLogExplorer.setupDataStream( + DATASET_NAME, + NAMESPACE + ); + await PageObjects.observabilityLogExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); + await PageObjects.svlCommonPage.login(); + }); + + after('clean up DataStream', async () => { + await PageObjects.svlCommonPage.forceLogout(); + if (cleanupDataStreamSetup) { + await cleanupDataStreamSetup(); + } + }); + + beforeEach(async () => { + await PageObjects.observabilityLogExplorer.navigateTo({ + from: new Date(NOW - 60_000).toISOString(), + to: new Date(NOW + 60_000).toISOString(), + }); + }); + + it('should load the infrastructure container with all fields', async () => { + await dataGrid.clickRowToggle(); + await testSubjects.existOrFail('Infrastructure'); + await testSubjects.existOrFail('logExplorerFlyoutHostName'); + await testSubjects.existOrFail('logExplorerFlyoutClusterName'); + await testSubjects.existOrFail('logExplorerFlyoutResourceId'); + await dataGrid.closeFlyout(); + }); + + it('should load the infrastructure container even when 1 field is missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 1 }); + await testSubjects.existOrFail('Infrastructure'); + await testSubjects.missingOrFail('logExplorerFlyoutHostName'); + await testSubjects.existOrFail('logExplorerFlyoutClusterName'); + await testSubjects.existOrFail('logExplorerFlyoutResourceId'); + await dataGrid.closeFlyout(); + }); + + it('should not load the infrastructure container if all fields are missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 2 }); + await testSubjects.missingOrFail('Infrastructure'); + await testSubjects.missingOrFail('logExplorerFlyoutHostName'); + await testSubjects.missingOrFail('logExplorerFlyoutClusterName'); + await testSubjects.missingOrFail('logExplorerFlyoutResourceId'); + await dataGrid.closeFlyout(); + }); + }); + + describe('Cloud container', () => { + const { cloudProvider, cloudInstanceId, cloudProjectId, cloudRegion, cloudAz, ...rest } = + sharedDoc; + const docWithoutCloudProviderAndInstanceId = { + ...rest, + cloudProjectId, + cloudRegion, + cloudAz, + time: NOW - 1000, + }; + const docWithoutCloudContainer = { ...rest, time: NOW - 2000 }; + + const docs = [sharedDoc, docWithoutCloudProviderAndInstanceId, docWithoutCloudContainer]; + before('setup DataStream', async () => { + cleanupDataStreamSetup = await PageObjects.observabilityLogExplorer.setupDataStream( + DATASET_NAME, + NAMESPACE + ); + await PageObjects.observabilityLogExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); + await PageObjects.svlCommonPage.login(); + }); + + after('clean up DataStream', async () => { + await PageObjects.svlCommonPage.forceLogout(); + if (cleanupDataStreamSetup) { + await cleanupDataStreamSetup(); + } + }); + + beforeEach(async () => { + await PageObjects.observabilityLogExplorer.navigateTo({ + from: new Date(NOW - 60_000).toISOString(), + to: new Date(NOW + 60_000).toISOString(), + }); + }); + + it('should load the cloud container with all fields', async () => { + await dataGrid.clickRowToggle(); + await testSubjects.existOrFail('Cloud'); + await testSubjects.existOrFail('logExplorerFlyoutCloudProvider'); + await testSubjects.existOrFail('logExplorerFlyoutCloudRegion'); + await testSubjects.existOrFail('logExplorerFlyoutCloudAz'); + await testSubjects.existOrFail('logExplorerFlyoutCloudProjectId'); + await testSubjects.existOrFail('logExplorerFlyoutCloudInstanceId'); + await dataGrid.closeFlyout(); + }); + + it('should load the cloud container even when some fields are missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 1 }); + await testSubjects.existOrFail('Cloud'); + + await testSubjects.missingOrFail('logExplorerFlyoutCloudProvider'); + await testSubjects.missingOrFail('logExplorerFlyoutCloudInstanceId'); + + await testSubjects.existOrFail('logExplorerFlyoutCloudRegion'); + await testSubjects.existOrFail('logExplorerFlyoutCloudAz'); + await dataGrid.closeFlyout(); + }); + + it('should not load the cloud container if all fields are missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 2 }); + await testSubjects.missingOrFail('logExplorerFlyoutCloudProvider'); + await testSubjects.missingOrFail('logExplorerFlyoutCloudRegion'); + await testSubjects.missingOrFail('logExplorerFlyoutCloudAz'); + await testSubjects.missingOrFail('logExplorerFlyoutCloudProjectId'); + await testSubjects.missingOrFail('logExplorerFlyoutCloudInstanceId'); + await dataGrid.closeFlyout(); + }); + }); + + describe('Other container', () => { + const { logFilepath, agentName, ...rest } = sharedDoc; + const docWithoutLogPathAndAgentName = { + ...rest, + time: NOW - 1000, + }; + + const docs = [sharedDoc, docWithoutLogPathAndAgentName]; + before('setup DataStream', async () => { + cleanupDataStreamSetup = await PageObjects.observabilityLogExplorer.setupDataStream( + DATASET_NAME, + NAMESPACE + ); + await PageObjects.observabilityLogExplorer.ingestLogEntries(DATA_STREAM_NAME, docs); + await PageObjects.svlCommonPage.login(); + }); + + after('clean up DataStream', async () => { + await PageObjects.svlCommonPage.forceLogout(); + if (cleanupDataStreamSetup) { + await cleanupDataStreamSetup(); + } + }); + + beforeEach(async () => { + await PageObjects.observabilityLogExplorer.navigateTo({ + from: new Date(NOW - 60_000).toISOString(), + to: new Date(NOW + 60_000).toISOString(), + }); + }); + + it('should load the other container with all fields', async () => { + await dataGrid.clickRowToggle(); + await testSubjects.existOrFail('Other'); + await testSubjects.existOrFail('logExplorerFlyoutLogPathFile'); + await testSubjects.existOrFail('logExplorerFlyoutNamespace'); + await testSubjects.existOrFail('logExplorerFlyoutDataset'); + await testSubjects.existOrFail('logExplorerFlyoutLogShipper'); + await dataGrid.closeFlyout(); + }); + + it('should load the other container even when some fields are missing', async () => { + await dataGrid.clickRowToggle({ rowIndex: 1 }); + await testSubjects.existOrFail('Other'); + + await testSubjects.missingOrFail('logExplorerFlyoutLogPathFile'); + await testSubjects.missingOrFail('logExplorerFlyoutLogShipper'); + + await testSubjects.existOrFail('logExplorerFlyoutNamespace'); + await testSubjects.existOrFail('logExplorerFlyoutDataset'); + await dataGrid.closeFlyout(); + }); + }); + }); +} diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/index.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/index.ts index 6e0b650869680..9f3d9b7e39c86 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/index.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/index.ts @@ -16,5 +16,7 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./filter_controls')); loadTestFile(require.resolve('./flyout')); loadTestFile(require.resolve('./header_menu')); + loadTestFile(require.resolve('./header_menu')); + loadTestFile(require.resolve('./flyout_highlights.ts')); }); } From c5b6201c616ae08f8270c39b08723550f67715d5 Mon Sep 17 00:00:00 2001 From: achyutjhunjhunwala Date: Wed, 8 Nov 2023 15:46:43 +0100 Subject: [PATCH 04/10] Fix few review commits, few more to come. --- .../plugins/log_explorer/common/constants.ts | 24 +- .../flyout_detail/flyout_detail.tsx | 2 +- .../{fylout_header.tsx => flyout_header.tsx} | 0 .../flyout_detail/flyout_highlights.tsx | 258 ++++++++---------- .../sub_components/highlight_field.tsx | 10 +- ...ht_container.tsx => highlight_section.tsx} | 12 +- .../sub_components/hover_action.tsx | 36 +-- .../components/flyout_detail/translations.ts | 2 +- .../public/components/flyout_detail/types.ts | 6 - .../flyout_detail/use_doc_detail.ts | 52 ++-- .../discover_actions/use_discover_action.ts | 16 -- .../use_discover_action.ts} | 18 +- .../apps/observability_log_explorer/flyout.ts | 2 +- .../flyout_highlights.ts | 22 +- .../flyout_highlights.ts | 22 +- 15 files changed, 222 insertions(+), 260 deletions(-) rename x-pack/plugins/log_explorer/public/components/flyout_detail/{fylout_header.tsx => flyout_header.tsx} (100%) rename x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/{highlight_container.tsx => highlight_section.tsx} (73%) delete mode 100644 x-pack/plugins/log_explorer/public/context/discover_actions/use_discover_action.ts rename x-pack/plugins/log_explorer/public/{context/discover_actions/discover_actions_context.tsx => hooks/use_discover_action.ts} (53%) diff --git a/x-pack/plugins/log_explorer/common/constants.ts b/x-pack/plugins/log_explorer/common/constants.ts index 6f4e85b901732..5aeb491b5a9d2 100644 --- a/x-pack/plugins/log_explorer/common/constants.ts +++ b/x-pack/plugins/log_explorer/common/constants.ts @@ -13,19 +13,19 @@ export const HOST_NAME_FIELD = 'host.name'; export const LOG_LEVEL_FIELD = 'log.level'; export const MESSAGE_FIELD = 'message'; export const SERVICE_NAME_FIELD = 'service.name'; -export const TRACE_ID = 'trace.id'; +export const TRACE_ID_FIELD = 'trace.id'; -export const AGENT_NAME = 'agent.name'; -export const ORCHESTRATOR_CLUSTER_NAME = 'orchestrator.cluster.name'; -export const ORCHESTRATOR_RESOURCE_ID = 'orchestrator.resource.id'; -export const CLOUD_PROVIDER = 'cloud.provider'; -export const CLOUD_REGION = 'cloud.region'; -export const CLOUD_AVAILABILITY_ZONE = 'cloud.availability_zone'; -export const CLOUD_PROJECT_ID = 'cloud.project.id'; -export const CLOUD_INSTANCE_ID = 'cloud.instance.id'; -export const LOG_FILE_PATH = 'log.file.path'; -export const DATASTREAM_NAMESPACE = 'data_stream.namespace'; -export const DATASTREAM_DATASET = 'data_stream.dataset'; +export const AGENT_NAME_FIELD = 'agent.name'; +export const ORCHESTRATOR_CLUSTER_NAME_FIELD = 'orchestrator.cluster.name'; +export const ORCHESTRATOR_RESOURCE_ID_FIELD = 'orchestrator.resource.id'; +export const CLOUD_PROVIDER_FIELD = 'cloud.provider'; +export const CLOUD_REGION_FIELD = 'cloud.region'; +export const CLOUD_AVAILABILITY_ZONE_FIELD = 'cloud.availability_zone'; +export const CLOUD_PROJECT_ID_FIELD = 'cloud.project.id'; +export const CLOUD_INSTANCE_ID_FIELD = 'cloud.instance.id'; +export const LOG_FILE_PATH_FIELD = 'log.file.path'; +export const DATASTREAM_NAMESPACE_FIELD = 'data_stream.namespace'; +export const DATASTREAM_DATASET_FIELD = 'data_stream.dataset'; // Sizing export const DATA_GRID_COLUMN_WIDTH_SMALL = 240; diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx index 5f58bef6b75e7..b4db0deb31180 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { EuiHorizontalRule } from '@elastic/eui'; import { FlyoutProps, LogDocument } from './types'; import { useDocDetail } from './use_doc_detail'; -import { FlyoutHeader } from './fylout_header'; +import { FlyoutHeader } from './flyout_header'; import { FlyoutHighlights } from './flyout_highlights'; export function FlyoutDetail({ diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/fylout_header.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_header.tsx similarity index 100% rename from x-pack/plugins/log_explorer/public/components/flyout_detail/fylout_header.tsx rename to x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_header.tsx diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx index 237f831a9e27e..433d6b524572e 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiPanel, EuiFlexItem } from '@elastic/eui'; +import { EuiPanel } from '@elastic/eui'; import React from 'react'; import { FlyoutContentActions } from '@kbn/discover-plugin/public'; import { DataTableRecord } from '@kbn/discover-utils/src/types'; @@ -31,8 +31,8 @@ import { otherAccordionTitle, serviceAccordionTitle, } from './translations'; -import { HighlightSection } from './sub_components/highlight_container'; -import { DiscoverActionContext } from '../../context/discover_actions/discover_actions_context'; +import { HighlightSection } from './sub_components/highlight_section'; +import { DiscoverActionsProvider } from '../../hooks/use_discover_action'; export function FlyoutHighlights({ formattedDoc, @@ -44,174 +44,146 @@ export function FlyoutHighlights({ actions: FlyoutContentActions; }) { return ( - + {formattedDoc[constants.SERVICE_NAME_FIELD] && ( - - - + )} - {formattedDoc[constants.TRACE_ID] && ( - - - + {formattedDoc[constants.TRACE_ID_FIELD] && ( + )} {formattedDoc[constants.HOST_NAME_FIELD] && ( - - - + )} - {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME] && ( - - - + {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( + )} - {formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID] && ( - - - + {formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( + )} - {formattedDoc[constants.CLOUD_PROVIDER] && ( - - - + {formattedDoc[constants.CLOUD_PROVIDER_FIELD] && ( + )} - {formattedDoc[constants.CLOUD_REGION] && ( - - - + {formattedDoc[constants.CLOUD_REGION_FIELD] && ( + )} - {formattedDoc[constants.CLOUD_AVAILABILITY_ZONE] && ( - - - + {formattedDoc[constants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( + )} - {formattedDoc[constants.CLOUD_PROJECT_ID] && ( - - - + {formattedDoc[constants.CLOUD_PROJECT_ID_FIELD] && ( + )} - {formattedDoc[constants.CLOUD_INSTANCE_ID] && ( - - - + {formattedDoc[constants.CLOUD_INSTANCE_ID_FIELD] && ( + )} - {formattedDoc[constants.LOG_FILE_PATH] && ( - - - + {formattedDoc[constants.LOG_FILE_PATH_FIELD] && ( + )} - {formattedDoc[constants.DATASTREAM_NAMESPACE] && ( - - - + {formattedDoc[constants.DATASTREAM_NAMESPACE_FIELD] && ( + )} - {formattedDoc[constants.DATASTREAM_DATASET] && ( - - - + {formattedDoc[constants.DATASTREAM_DATASET_FIELD] && ( + )} - {formattedDoc[constants.AGENT_NAME] && ( - - - + {formattedDoc[constants.AGENT_NAME_FIELD] && ( + )} - + ); } diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx index 964d42e342611..7215f77ad87c9 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx @@ -9,7 +9,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import React, { ReactNode, useMemo } from 'react'; import { HoverAction, HoverActionType } from './hover_action'; import { flyoutHoverActionFilterForText, flyoutHoverActionFilterOutText } from '../translations'; -import { useDiscoverAction } from '../../../context/discover_actions/use_discover_action'; +import { useDiscoverActionsContext } from '../../../hooks/use_discover_action'; interface HighlightFieldProps { label: string | ReactNode; @@ -28,7 +28,7 @@ export function HighlightField({ }: HighlightFieldProps) { const filterForText = flyoutHoverActionFilterForText(value); const filterOutText = flyoutHoverActionFilterOutText(value); - const { actions } = useDiscoverAction(); + const actions = useDiscoverActionsContext(); const hoverActions: HoverActionType[] = useMemo( () => [ @@ -50,13 +50,13 @@ export function HighlightField({ [actions, field, value, filterForText, filterOutText] ); return formattedValue ? ( - - + + {label} - + diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx similarity index 73% rename from x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx rename to x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx index 0a7e718963558..f4d9a65797e29 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx @@ -11,6 +11,7 @@ import { EuiFlexGrid, EuiHorizontalRule, EuiTitle, + EuiFlexItem, useGeneratedHtmlId, } from '@elastic/eui'; @@ -25,13 +26,18 @@ export function HighlightSection({ children, showBottomRule = true, }: HighlightSectionProps) { - const shouldRenderSection = React.Children.toArray(children).filter(Boolean).length > 0; + const validChildren = React.Children.toArray(children).filter(Boolean); + const shouldRenderSection = validChildren.length > 0; const accordionTitle = (

{title}

); + const flexChildren = validChildren.map((child, idx) => ( + {child} + )); + const accordionId = useGeneratedHtmlId({ prefix: title, }); @@ -43,9 +49,9 @@ export function HighlightSection({ buttonContent={accordionTitle} paddingSize="s" initialIsOpen={true} - data-test-subj={title} + data-test-subj={`logExplorerFlyoutHighlightSection${title}`} > - {children} + {flexChildren} {showBottomRule && } diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx index fe20920f86976..fedc0d0deada1 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx @@ -23,6 +23,15 @@ interface HoverActionProps { actions: HoverActionType[]; } +const hoverActionDisplayCss = { + ':hover, :focus-within': { + '.visibleOnHoverFocus': { + opacity: 1, + visibility: 'visible', + }, + }, +}; + export const HoverAction = ({ displayText, actions }: HoverActionProps) => { const { euiTheme } = useEuiTheme(); return ( @@ -31,14 +40,7 @@ export const HoverAction = ({ displayText, actions }: HoverActionProps) => { alignItems="center" justifyContent="flexStart" gutterSize="s" - css={{ - ':hover, :focus-within': { - '.visibleOnHoverFocus': { - opacity: 1, - visibility: 'visible', - }, - }, - }} + css={hoverActionDisplayCss} > { gutterSize="none" > {actions.map((action) => ( - - - action.onClick()} - /> - - + + action.onClick()} + /> + ))}
diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts b/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts index 9479f030b1807..68b97b3555c84 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts @@ -96,7 +96,7 @@ export const flyoutCloudProjectIdLabel = i18n.translate( export const flyoutCloudInstanceIdLabel = i18n.translate( 'xpack.logExplorer.flyoutDetail.label.cloudInstanceId', { - defaultMessage: 'Cloud Instance ID', + defaultMessage: 'Cloud instance ID', } ); diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts b/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts index fae871bbcfccf..122d0219c0289 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts @@ -57,9 +57,3 @@ export interface FlyoutDoc { 'data_stream.namespace': string; 'data_stream.dataset': string; } - -export interface FlyoutHighlightField { - label: string; - value: string; - iconType?: EuiIconType; -} diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/use_doc_detail.ts b/x-pack/plugins/log_explorer/public/components/flyout_detail/use_doc_detail.ts index 166a491e630ce..938855f00f5a3 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/use_doc_detail.ts +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/use_doc_detail.ts @@ -5,6 +5,7 @@ * 2.0. */ import { formatFieldValue } from '@kbn/discover-utils'; +import he from 'he'; import * as constants from '../../../common/constants'; import { useKibanaContextForPlugin } from '../../utils/use_kibana'; import { FlyoutDoc, FlyoutProps, LogDocument } from './types'; @@ -33,48 +34,49 @@ export function useDocDetail( // Flyout Headers const level = formatField(constants.LOG_LEVEL_FIELD)?.toLowerCase(); const timestamp = formatField(constants.TIMESTAMP_FIELD); - const message = formatField(constants.MESSAGE_FIELD); + const formattedMessage = formatField(constants.MESSAGE_FIELD); + const message = formattedMessage ? he.decode(formattedMessage) : undefined; // Service Highlights const serviceName = formatField(constants.SERVICE_NAME_FIELD); - const traceId = formatField(constants.TRACE_ID); + const traceId = formatField(constants.TRACE_ID_FIELD); // Infrastructure Highlights const hostname = formatField(constants.HOST_NAME_FIELD); - const orchestratorClusterName = formatField(constants.ORCHESTRATOR_CLUSTER_NAME); - const orchestratorResourceId = formatField(constants.ORCHESTRATOR_RESOURCE_ID); + const orchestratorClusterName = formatField(constants.ORCHESTRATOR_CLUSTER_NAME_FIELD); + const orchestratorResourceId = formatField(constants.ORCHESTRATOR_RESOURCE_ID_FIELD); // Cloud Highlights - const cloudProvider = formatField(constants.CLOUD_PROVIDER); - const cloudRegion = formatField(constants.CLOUD_REGION); - const cloudAz = formatField(constants.CLOUD_AVAILABILITY_ZONE); - const cloudProjectId = formatField(constants.CLOUD_PROJECT_ID); - const cloudInstanceId = formatField(constants.CLOUD_INSTANCE_ID); + const cloudProvider = formatField(constants.CLOUD_PROVIDER_FIELD); + const cloudRegion = formatField(constants.CLOUD_REGION_FIELD); + const cloudAz = formatField(constants.CLOUD_AVAILABILITY_ZONE_FIELD); + const cloudProjectId = formatField(constants.CLOUD_PROJECT_ID_FIELD); + const cloudInstanceId = formatField(constants.CLOUD_INSTANCE_ID_FIELD); // Other Highlights - const logFilePath = formatField(constants.LOG_FILE_PATH); - const namespace = formatField(constants.DATASTREAM_NAMESPACE); - const dataset = formatField(constants.DATASTREAM_DATASET); - const agentName = formatField(constants.AGENT_NAME); + const logFilePath = formatField(constants.LOG_FILE_PATH_FIELD); + const namespace = formatField(constants.DATASTREAM_NAMESPACE_FIELD); + const dataset = formatField(constants.DATASTREAM_DATASET_FIELD); + const agentName = formatField(constants.AGENT_NAME_FIELD); return { [constants.LOG_LEVEL_FIELD]: level, [constants.TIMESTAMP_FIELD]: timestamp, [constants.MESSAGE_FIELD]: message, [constants.SERVICE_NAME_FIELD]: serviceName, - [constants.TRACE_ID]: traceId, + [constants.TRACE_ID_FIELD]: traceId, [constants.HOST_NAME_FIELD]: hostname, - [constants.ORCHESTRATOR_CLUSTER_NAME]: orchestratorClusterName, - [constants.ORCHESTRATOR_RESOURCE_ID]: orchestratorResourceId, - [constants.CLOUD_PROVIDER]: cloudProvider, - [constants.CLOUD_REGION]: cloudRegion, - [constants.CLOUD_AVAILABILITY_ZONE]: cloudAz, - [constants.CLOUD_PROJECT_ID]: cloudProjectId, - [constants.CLOUD_INSTANCE_ID]: cloudInstanceId, - [constants.LOG_FILE_PATH]: logFilePath, - [constants.DATASTREAM_NAMESPACE]: namespace, - [constants.DATASTREAM_DATASET]: dataset, - [constants.AGENT_NAME]: agentName, + [constants.ORCHESTRATOR_CLUSTER_NAME_FIELD]: orchestratorClusterName, + [constants.ORCHESTRATOR_RESOURCE_ID_FIELD]: orchestratorResourceId, + [constants.CLOUD_PROVIDER_FIELD]: cloudProvider, + [constants.CLOUD_REGION_FIELD]: cloudRegion, + [constants.CLOUD_AVAILABILITY_ZONE_FIELD]: cloudAz, + [constants.CLOUD_PROJECT_ID_FIELD]: cloudProjectId, + [constants.CLOUD_INSTANCE_ID_FIELD]: cloudInstanceId, + [constants.LOG_FILE_PATH_FIELD]: logFilePath, + [constants.DATASTREAM_NAMESPACE_FIELD]: namespace, + [constants.DATASTREAM_DATASET_FIELD]: dataset, + [constants.AGENT_NAME_FIELD]: agentName, }; } diff --git a/x-pack/plugins/log_explorer/public/context/discover_actions/use_discover_action.ts b/x-pack/plugins/log_explorer/public/context/discover_actions/use_discover_action.ts deleted file mode 100644 index 4e2d18d7f2a82..0000000000000 --- a/x-pack/plugins/log_explorer/public/context/discover_actions/use_discover_action.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * 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 { useContext } from 'react'; -import { DiscoverActionContext } from './discover_actions_context'; - -export function useDiscoverAction() { - const ctx = useContext(DiscoverActionContext); - if (!ctx) { - throw new Error('DiscoverActionContext can only be used inside of FlyoutHighlights!'); - } - return ctx; -} diff --git a/x-pack/plugins/log_explorer/public/context/discover_actions/discover_actions_context.tsx b/x-pack/plugins/log_explorer/public/hooks/use_discover_action.ts similarity index 53% rename from x-pack/plugins/log_explorer/public/context/discover_actions/discover_actions_context.tsx rename to x-pack/plugins/log_explorer/public/hooks/use_discover_action.ts index 3fb999374f9e8..ef1a1ae715ca6 100644 --- a/x-pack/plugins/log_explorer/public/context/discover_actions/discover_actions_context.tsx +++ b/x-pack/plugins/log_explorer/public/hooks/use_discover_action.ts @@ -4,14 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -import { createContext } from 'react'; +import createContainer from 'constate'; import { FlyoutContentActions } from '@kbn/discover-plugin/public'; -export const DiscoverActionContext = createContext<{ actions: FlyoutContentActions }>({ - actions: { - addFilter: () => {}, - addColumn: () => {}, - removeColumn: () => {}, - }, -}); +interface UseFlyoutActionsDeps { + value: FlyoutContentActions; +} + +const useDiscoverActions = ({ value }: UseFlyoutActionsDeps) => value; + +export const [DiscoverActionsProvider, useDiscoverActionsContext] = + createContainer(useDiscoverActions); diff --git a/x-pack/test/functional/apps/observability_log_explorer/flyout.ts b/x-pack/test/functional/apps/observability_log_explorer/flyout.ts index c394f0ff0f34d..b2b71c817255a 100644 --- a/x-pack/test/functional/apps/observability_log_explorer/flyout.ts +++ b/x-pack/test/functional/apps/observability_log_explorer/flyout.ts @@ -56,7 +56,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { after('clean up DataStream', async () => { if (cleanupDataStreamSetup) { - cleanupDataStreamSetup(); + await cleanupDataStreamSetup(); } }); diff --git a/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts b/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts index 0830495aeab0b..3b71935cd6fbc 100644 --- a/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts +++ b/x-pack/test/functional/apps/observability_log_explorer/flyout_highlights.ts @@ -75,7 +75,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the service container with all fields', async () => { await dataGrid.clickRowToggle(); - await testSubjects.existOrFail('Service'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionService'); await testSubjects.existOrFail('logExplorerFlyoutService'); await testSubjects.existOrFail('logExplorerFlyoutTrace'); await dataGrid.closeFlyout(); @@ -83,7 +83,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the service container even when 1 field is missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 1 }); - await testSubjects.existOrFail('Service'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionService'); await testSubjects.missingOrFail('logExplorerFlyoutService'); await testSubjects.existOrFail('logExplorerFlyoutTrace'); await dataGrid.closeFlyout(); @@ -91,7 +91,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not load the service container if all fields are missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 3 }); - await testSubjects.missingOrFail('Service'); + await testSubjects.missingOrFail('logExplorerFlyoutHighlightSectionService'); await testSubjects.missingOrFail('logExplorerFlyoutService'); await testSubjects.missingOrFail('logExplorerFlyoutTrace'); await dataGrid.closeFlyout(); @@ -132,7 +132,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the infrastructure container with all fields', async () => { await dataGrid.clickRowToggle(); - await testSubjects.existOrFail('Infrastructure'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionInfrastructure'); await testSubjects.existOrFail('logExplorerFlyoutHostName'); await testSubjects.existOrFail('logExplorerFlyoutClusterName'); await testSubjects.existOrFail('logExplorerFlyoutResourceId'); @@ -141,7 +141,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the infrastructure container even when 1 field is missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 1 }); - await testSubjects.existOrFail('Infrastructure'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionInfrastructure'); await testSubjects.missingOrFail('logExplorerFlyoutHostName'); await testSubjects.existOrFail('logExplorerFlyoutClusterName'); await testSubjects.existOrFail('logExplorerFlyoutResourceId'); @@ -150,7 +150,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not load the infrastructure container if all fields are missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 2 }); - await testSubjects.missingOrFail('Infrastructure'); + await testSubjects.missingOrFail('logExplorerFlyoutHighlightSectionInfrastructure'); await testSubjects.missingOrFail('logExplorerFlyoutHostName'); await testSubjects.missingOrFail('logExplorerFlyoutClusterName'); await testSubjects.missingOrFail('logExplorerFlyoutResourceId'); @@ -194,7 +194,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the cloud container with all fields', async () => { await dataGrid.clickRowToggle(); - await testSubjects.existOrFail('Cloud'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionCloud'); await testSubjects.existOrFail('logExplorerFlyoutCloudProvider'); await testSubjects.existOrFail('logExplorerFlyoutCloudRegion'); await testSubjects.existOrFail('logExplorerFlyoutCloudAz'); @@ -205,18 +205,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the cloud container even when some fields are missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 1 }); - await testSubjects.existOrFail('Cloud'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionCloud'); await testSubjects.missingOrFail('logExplorerFlyoutCloudProvider'); await testSubjects.missingOrFail('logExplorerFlyoutCloudInstanceId'); await testSubjects.existOrFail('logExplorerFlyoutCloudRegion'); await testSubjects.existOrFail('logExplorerFlyoutCloudAz'); + await testSubjects.existOrFail('logExplorerFlyoutCloudProjectId'); await dataGrid.closeFlyout(); }); it('should not load the cloud container if all fields are missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 2 }); + await testSubjects.missingOrFail('logExplorerFlyoutHighlightSectionCloud'); await testSubjects.missingOrFail('logExplorerFlyoutCloudProvider'); await testSubjects.missingOrFail('logExplorerFlyoutCloudRegion'); await testSubjects.missingOrFail('logExplorerFlyoutCloudAz'); @@ -257,7 +259,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the other container with all fields', async () => { await dataGrid.clickRowToggle(); - await testSubjects.existOrFail('Other'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionOther'); await testSubjects.existOrFail('logExplorerFlyoutLogPathFile'); await testSubjects.existOrFail('logExplorerFlyoutNamespace'); await testSubjects.existOrFail('logExplorerFlyoutDataset'); @@ -267,7 +269,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the other container even when some fields are missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 1 }); - await testSubjects.existOrFail('Other'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionOther'); await testSubjects.missingOrFail('logExplorerFlyoutLogPathFile'); await testSubjects.missingOrFail('logExplorerFlyoutLogShipper'); diff --git a/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/flyout_highlights.ts b/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/flyout_highlights.ts index 995fb9cb7cdee..98a3914fed4e8 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/flyout_highlights.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/observability_log_explorer/flyout_highlights.ts @@ -77,7 +77,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the service container with all fields', async () => { await dataGrid.clickRowToggle(); - await testSubjects.existOrFail('Service'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionService'); await testSubjects.existOrFail('logExplorerFlyoutService'); await testSubjects.existOrFail('logExplorerFlyoutTrace'); await dataGrid.closeFlyout(); @@ -85,7 +85,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the service container even when 1 field is missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 1 }); - await testSubjects.existOrFail('Service'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionService'); await testSubjects.missingOrFail('logExplorerFlyoutService'); await testSubjects.existOrFail('logExplorerFlyoutTrace'); await dataGrid.closeFlyout(); @@ -93,7 +93,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not load the service container if all fields are missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 3 }); - await testSubjects.missingOrFail('Service'); + await testSubjects.missingOrFail('logExplorerFlyoutHighlightSectionService'); await testSubjects.missingOrFail('logExplorerFlyoutService'); await testSubjects.missingOrFail('logExplorerFlyoutTrace'); await dataGrid.closeFlyout(); @@ -136,7 +136,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the infrastructure container with all fields', async () => { await dataGrid.clickRowToggle(); - await testSubjects.existOrFail('Infrastructure'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionInfrastructure'); await testSubjects.existOrFail('logExplorerFlyoutHostName'); await testSubjects.existOrFail('logExplorerFlyoutClusterName'); await testSubjects.existOrFail('logExplorerFlyoutResourceId'); @@ -145,7 +145,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the infrastructure container even when 1 field is missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 1 }); - await testSubjects.existOrFail('Infrastructure'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionInfrastructure'); await testSubjects.missingOrFail('logExplorerFlyoutHostName'); await testSubjects.existOrFail('logExplorerFlyoutClusterName'); await testSubjects.existOrFail('logExplorerFlyoutResourceId'); @@ -154,7 +154,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should not load the infrastructure container if all fields are missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 2 }); - await testSubjects.missingOrFail('Infrastructure'); + await testSubjects.missingOrFail('logExplorerFlyoutHighlightSectionInfrastructure'); await testSubjects.missingOrFail('logExplorerFlyoutHostName'); await testSubjects.missingOrFail('logExplorerFlyoutClusterName'); await testSubjects.missingOrFail('logExplorerFlyoutResourceId'); @@ -200,7 +200,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the cloud container with all fields', async () => { await dataGrid.clickRowToggle(); - await testSubjects.existOrFail('Cloud'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionCloud'); await testSubjects.existOrFail('logExplorerFlyoutCloudProvider'); await testSubjects.existOrFail('logExplorerFlyoutCloudRegion'); await testSubjects.existOrFail('logExplorerFlyoutCloudAz'); @@ -211,18 +211,20 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the cloud container even when some fields are missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 1 }); - await testSubjects.existOrFail('Cloud'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionCloud'); await testSubjects.missingOrFail('logExplorerFlyoutCloudProvider'); await testSubjects.missingOrFail('logExplorerFlyoutCloudInstanceId'); await testSubjects.existOrFail('logExplorerFlyoutCloudRegion'); await testSubjects.existOrFail('logExplorerFlyoutCloudAz'); + await testSubjects.existOrFail('logExplorerFlyoutCloudProjectId'); await dataGrid.closeFlyout(); }); it('should not load the cloud container if all fields are missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 2 }); + await testSubjects.missingOrFail('logExplorerFlyoutHighlightSectionCloud'); await testSubjects.missingOrFail('logExplorerFlyoutCloudProvider'); await testSubjects.missingOrFail('logExplorerFlyoutCloudRegion'); await testSubjects.missingOrFail('logExplorerFlyoutCloudAz'); @@ -265,7 +267,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the other container with all fields', async () => { await dataGrid.clickRowToggle(); - await testSubjects.existOrFail('Other'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionOther'); await testSubjects.existOrFail('logExplorerFlyoutLogPathFile'); await testSubjects.existOrFail('logExplorerFlyoutNamespace'); await testSubjects.existOrFail('logExplorerFlyoutDataset'); @@ -275,7 +277,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('should load the other container even when some fields are missing', async () => { await dataGrid.clickRowToggle({ rowIndex: 1 }); - await testSubjects.existOrFail('Other'); + await testSubjects.existOrFail('logExplorerFlyoutHighlightSectionOther'); await testSubjects.missingOrFail('logExplorerFlyoutLogPathFile'); await testSubjects.missingOrFail('logExplorerFlyoutLogShipper'); From 5d4d31ba9e3065689a75609038195ac5941f6dc3 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 8 Nov 2023 15:29:23 +0000 Subject: [PATCH 05/10] [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' --- .../log_explorer/public/components/flyout_detail/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts b/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts index 122d0219c0289..28110024108da 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/types.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import type { DataView } from '@kbn/data-views-plugin/common'; import type { FlyoutContentProps } from '@kbn/discover-plugin/public'; import type { DataTableRecord } from '@kbn/discover-utils/types'; From 892892d6f123303e8a467c229c7f8de0cf80bc2c Mon Sep 17 00:00:00 2001 From: achyutjhunjhunwala Date: Thu, 9 Nov 2023 11:26:53 +0100 Subject: [PATCH 06/10] Fix width issue and empty component render issue --- .../flyout_detail/flyout_detail.tsx | 2 - .../flyout_detail/flyout_highlights.tsx | 27 ++++++++++-- .../sub_components/highlight_container.tsx | 42 ++++++++++++++++++ .../sub_components/highlight_field.tsx | 6 ++- .../sub_components/hover_action.tsx | 35 ++++++++------- .../public/hooks/use_dimensions.tsx | 44 +++++++++++++++++++ 6 files changed, 131 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx create mode 100644 x-pack/plugins/log_explorer/public/hooks/use_dimensions.tsx diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx index b4db0deb31180..66c4788a32ae2 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx @@ -6,7 +6,6 @@ */ import React from 'react'; -import { EuiHorizontalRule } from '@elastic/eui'; import { FlyoutProps, LogDocument } from './types'; import { useDocDetail } from './use_doc_detail'; import { FlyoutHeader } from './flyout_header'; @@ -22,7 +21,6 @@ export function FlyoutDetail({ return ( <> - ); diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx index 433d6b524572e..6910f40347f99 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx @@ -4,8 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiPanel } from '@elastic/eui'; -import React from 'react'; +import React, { useRef } from 'react'; import { FlyoutContentActions } from '@kbn/discover-plugin/public'; import { DataTableRecord } from '@kbn/discover-utils/src/types'; import { FlyoutDoc } from './types'; @@ -33,6 +32,8 @@ import { } from './translations'; import { HighlightSection } from './sub_components/highlight_section'; import { DiscoverActionsProvider } from '../../hooks/use_discover_action'; +import { HighlightContainer } from './sub_components/highlight_container'; +import { useDimension } from '../../hooks/use_dimensions'; export function FlyoutHighlights({ formattedDoc, @@ -43,9 +44,13 @@ export function FlyoutHighlights({ flattenedDoc: DataTableRecord['flattened']; actions: FlyoutContentActions; }) { + const elementRef = useRef(null); + const [ref, dimensions] = useDimension(elementRef); + const flyoutWidth = dimensions.width ?? 600; + const fieldWidth = flyoutWidth / 6; return ( - + {formattedDoc[constants.SERVICE_NAME_FIELD] && ( )} {formattedDoc[constants.TRACE_ID_FIELD] && ( @@ -63,6 +69,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.TRACE_ID_FIELD]} formattedValue={formattedDoc[constants.TRACE_ID_FIELD]} dataTestSubj="logExplorerFlyoutTrace" + width={fieldWidth} /> )} @@ -75,6 +82,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.HOST_NAME_FIELD]} formattedValue={formattedDoc[constants.HOST_NAME_FIELD]} dataTestSubj="logExplorerFlyoutHostName" + width={fieldWidth} /> )} {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( @@ -84,6 +92,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.ORCHESTRATOR_CLUSTER_NAME_FIELD]} formattedValue={formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME_FIELD]} dataTestSubj="logExplorerFlyoutClusterName" + width={fieldWidth} /> )} {formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( @@ -93,6 +102,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.ORCHESTRATOR_RESOURCE_ID_FIELD]} formattedValue={formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID_FIELD]} dataTestSubj="logExplorerFlyoutResourceId" + width={fieldWidth} /> )} @@ -105,6 +115,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.CLOUD_PROVIDER_FIELD]} formattedValue={formattedDoc[constants.CLOUD_PROVIDER_FIELD]} dataTestSubj="logExplorerFlyoutCloudProvider" + width={fieldWidth} /> )} {formattedDoc[constants.CLOUD_REGION_FIELD] && ( @@ -114,6 +125,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.CLOUD_REGION_FIELD]} formattedValue={formattedDoc[constants.CLOUD_REGION_FIELD]} dataTestSubj="logExplorerFlyoutCloudRegion" + width={fieldWidth} /> )} {formattedDoc[constants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( @@ -123,6 +135,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.CLOUD_AVAILABILITY_ZONE_FIELD]} formattedValue={formattedDoc[constants.CLOUD_AVAILABILITY_ZONE_FIELD]} dataTestSubj="logExplorerFlyoutCloudAz" + width={fieldWidth} /> )} {formattedDoc[constants.CLOUD_PROJECT_ID_FIELD] && ( @@ -132,6 +145,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.CLOUD_PROJECT_ID_FIELD]} formattedValue={formattedDoc[constants.CLOUD_PROJECT_ID_FIELD]} dataTestSubj="logExplorerFlyoutCloudProjectId" + width={fieldWidth} /> )} {formattedDoc[constants.CLOUD_INSTANCE_ID_FIELD] && ( @@ -141,6 +155,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.CLOUD_INSTANCE_ID_FIELD]} formattedValue={formattedDoc[constants.CLOUD_INSTANCE_ID_FIELD]} dataTestSubj="logExplorerFlyoutCloudInstanceId" + width={fieldWidth} /> )} @@ -153,6 +168,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.LOG_FILE_PATH_FIELD]} formattedValue={formattedDoc[constants.LOG_FILE_PATH_FIELD]} dataTestSubj="logExplorerFlyoutLogPathFile" + width={fieldWidth} /> )} {formattedDoc[constants.DATASTREAM_NAMESPACE_FIELD] && ( @@ -162,6 +178,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.DATASTREAM_NAMESPACE_FIELD]} formattedValue={formattedDoc[constants.DATASTREAM_NAMESPACE_FIELD]} dataTestSubj="logExplorerFlyoutNamespace" + width={fieldWidth} /> )} {formattedDoc[constants.DATASTREAM_DATASET_FIELD] && ( @@ -171,6 +188,7 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.DATASTREAM_DATASET_FIELD]} formattedValue={formattedDoc[constants.DATASTREAM_DATASET_FIELD]} dataTestSubj="logExplorerFlyoutDataset" + width={fieldWidth} /> )} {formattedDoc[constants.AGENT_NAME_FIELD] && ( @@ -180,10 +198,11 @@ export function FlyoutHighlights({ value={flattenedDoc[constants.AGENT_NAME_FIELD]} formattedValue={formattedDoc[constants.AGENT_NAME_FIELD]} dataTestSubj="logExplorerFlyoutLogShipper" + width={fieldWidth} /> )} - + ); } diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx new file mode 100644 index 0000000000000..9e00948699404 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_container.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiHorizontalRule, EuiPanel } from '@elastic/eui'; + +interface HighlightContainerProps { + children: React.ReactNode; +} + +const hasNonUndefinedSubChild = (children: React.ReactNode[]): boolean => { + return children.some((child) => { + if (React.isValidElement(child)) { + const subChildren = React.Children.toArray(child.props.children); + return subChildren.some((subChild) => subChild !== undefined && subChild !== null); + } + return false; + }); +}; + +export const HighlightContainer = React.forwardRef( + ({ children }, ref) => { + const validChildren = React.Children.toArray(children).filter(Boolean); + const hasChildren = validChildren.length > 0; + const shouldRender = hasChildren && hasNonUndefinedSubChild(validChildren); + + const flexChildren = validChildren.map((child, idx) =>
{child}
); + + return shouldRender ? ( +
+ + + {flexChildren} + +
+ ) : null; + } +); diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx index 7215f77ad87c9..ae6b6102367e0 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx @@ -17,6 +17,7 @@ interface HighlightFieldProps { value: unknown; formattedValue: string; dataTestSubj: string; + width: number; } export function HighlightField({ @@ -25,6 +26,7 @@ export function HighlightField({ value, formattedValue, dataTestSubj, + width, }: HighlightFieldProps) { const filterForText = flyoutHoverActionFilterForText(value); const filterOutText = flyoutHoverActionFilterOutText(value); @@ -52,12 +54,12 @@ export function HighlightField({ return formattedValue ? ( - + {label} - + ) : null; diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx index fedc0d0deada1..852649f16a6a9 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx @@ -7,7 +7,13 @@ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiToolTip, EuiButtonIcon, useEuiTheme } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiToolTip, + EuiButtonIcon, + useEuiTheme, + EuiTextTruncate, +} from '@elastic/eui'; import type { IconType } from '@elastic/eui'; export interface HoverActionType { @@ -21,18 +27,10 @@ export interface HoverActionType { interface HoverActionProps { displayText: string; actions: HoverActionType[]; + width: number; } -const hoverActionDisplayCss = { - ':hover, :focus-within': { - '.visibleOnHoverFocus': { - opacity: 1, - visibility: 'visible', - }, - }, -}; - -export const HoverAction = ({ displayText, actions }: HoverActionProps) => { +export const HoverAction = ({ displayText, actions, width }: HoverActionProps) => { const { euiTheme } = useEuiTheme(); return ( { alignItems="center" justifyContent="flexStart" gutterSize="s" - css={hoverActionDisplayCss} + css={{ + ':hover, :focus-within': { + '.visibleOnHoverFocus': { + opacity: 1, + visibility: 'visible', + }, + }, + }} > - + ( + ref: React.RefObject +): [React.RefObject, Dimension] => { + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); + const resizeObserverRef = useRef(null); + + useEffect(() => { + const currentElement = ref.current; + if (!currentElement) return; + + const resizeObserver = new ResizeObserver((entries) => { + const [entry] = entries; + const { width, height } = entry.contentRect; + setDimensions({ width, height }); + }); + + resizeObserver.observe(currentElement); + + resizeObserverRef.current = resizeObserver; + + return () => { + if (resizeObserverRef.current) { + resizeObserverRef.current.unobserve(currentElement); + resizeObserverRef.current = null; + } + }; + }, [ref]); + + return [ref, dimensions]; +}; From 7ac3862b1a246ee8924cf6a59301728edd2a776c Mon Sep 17 00:00:00 2001 From: achyutjhunjhunwala Date: Thu, 9 Nov 2023 12:24:45 +0100 Subject: [PATCH 07/10] Add more actions --- .../flyout_detail/flyout_highlights.tsx | 3 +- .../sub_components/highlight_field.tsx | 41 ++++++++++++++++--- .../components/flyout_detail/translations.ts | 14 +++++++ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx index 6910f40347f99..3e1256ab9a329 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx @@ -46,8 +46,7 @@ export function FlyoutHighlights({ }) { const elementRef = useRef(null); const [ref, dimensions] = useDimension(elementRef); - const flyoutWidth = dimensions.width ?? 600; - const fieldWidth = flyoutWidth / 6; + const fieldWidth = (dimensions.width - 20) / 7; return ( diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx index ae6b6102367e0..48479fc13b1e2 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx @@ -6,9 +6,14 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import React, { ReactNode, useMemo } from 'react'; +import React, { ReactNode, useMemo, useState } from 'react'; import { HoverAction, HoverActionType } from './hover_action'; -import { flyoutHoverActionFilterForText, flyoutHoverActionFilterOutText } from '../translations'; +import { + flyoutHoverActionFilterForText, + flyoutHoverActionFilterOutText, + flyoutHoverActionFilterForFieldPresentText, + flyoutHoverActionToggleColumnText, +} from '../translations'; import { useDiscoverActionsContext } from '../../../hooks/use_discover_action'; interface HighlightFieldProps { @@ -31,6 +36,7 @@ export function HighlightField({ const filterForText = flyoutHoverActionFilterForText(value); const filterOutText = flyoutHoverActionFilterOutText(value); const actions = useDiscoverActionsContext(); + const [columnAdded, setColumnAdded] = useState(false); const hoverActions: HoverActionType[] = useMemo( () => [ @@ -48,17 +54,40 @@ export function HighlightField({ onClick: () => actions?.addFilter && actions.addFilter(field, value, '-'), display: true, }, + { + id: 'filterForFieldPresentAction', + tooltipContent: flyoutHoverActionFilterForFieldPresentText, + iconType: 'filter', + onClick: () => actions?.addFilter && actions.addFilter('_exists_', field, '+'), + display: true, + }, + { + id: 'toggleColumnAction', + tooltipContent: flyoutHoverActionToggleColumnText, + iconType: 'listAdd', + onClick: () => { + if (actions) { + if (columnAdded) { + actions?.removeColumn?.(field); + } else { + actions?.addColumn?.(field); + } + setColumnAdded(!columnAdded); + } + }, + display: true, + }, ], - [actions, field, value, filterForText, filterOutText] + [filterForText, filterOutText, actions, field, value, columnAdded] ); return formattedValue ? ( - - + + {label} - + diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts b/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts index 68b97b3555c84..ef755c97f1f53 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts @@ -137,3 +137,17 @@ export const flyoutHoverActionFilterOutText = (text: unknown) => value: text as string, }, }); + +export const flyoutHoverActionFilterForFieldPresentText = i18n.translate( + 'xpack.logExplorer.flyoutDetail.value.hover.filterForFieldPresent', + { + defaultMessage: 'Filter for field present', + } +); + +export const flyoutHoverActionToggleColumnText = i18n.translate( + 'xpack.logExplorer.flyoutDetail.value.hover.toggleColumn', + { + defaultMessage: 'Toggle column in table', + } +); From 632c24c66fa510a2e35113e2dcb85df949a42092 Mon Sep 17 00:00:00 2001 From: achyutjhunjhunwala Date: Thu, 9 Nov 2023 14:24:39 +0100 Subject: [PATCH 08/10] Add logic to handle breakpoint based column display --- .../flyout_detail/flyout_highlights.tsx | 11 ++++---- .../sub_components/highlight_field.tsx | 10 ++++++- .../sub_components/highlight_section.tsx | 4 ++- .../components/flyout_detail/translations.ts | 7 +++++ .../public/hooks/use_flyouot_column_width.tsx | 26 +++++++++++++++++++ 5 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 x-pack/plugins/log_explorer/public/hooks/use_flyouot_column_width.tsx diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx index 3e1256ab9a329..d549f6a40a373 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx @@ -34,6 +34,7 @@ import { HighlightSection } from './sub_components/highlight_section'; import { DiscoverActionsProvider } from '../../hooks/use_discover_action'; import { HighlightContainer } from './sub_components/highlight_container'; import { useDimension } from '../../hooks/use_dimensions'; +import { useFlyoutColumnWidth } from '../../hooks/use_flyouot_column_width'; export function FlyoutHighlights({ formattedDoc, @@ -46,11 +47,11 @@ export function FlyoutHighlights({ }) { const elementRef = useRef(null); const [ref, dimensions] = useDimension(elementRef); - const fieldWidth = (dimensions.width - 20) / 7; + const { columns, fieldWidth } = useFlyoutColumnWidth(dimensions.width); return ( - + {formattedDoc[constants.SERVICE_NAME_FIELD] && ( - + {formattedDoc[constants.HOST_NAME_FIELD] && ( - + {formattedDoc[constants.CLOUD_PROVIDER_FIELD] && ( - + {formattedDoc[constants.LOG_FILE_PATH_FIELD] && ( copyToClipboard(value as string), + display: true, + }, ], [filterForText, filterOutText, actions, field, value, columnAdded] ); diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx index f4d9a65797e29..0b598339ed29b 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_section.tsx @@ -19,12 +19,14 @@ interface HighlightSectionProps { title: string; children: React.ReactNode; showBottomRule?: boolean; + columns: 1 | 2 | 3; } export function HighlightSection({ title, children, showBottomRule = true, + columns, }: HighlightSectionProps) { const validChildren = React.Children.toArray(children).filter(Boolean); const shouldRenderSection = validChildren.length > 0; @@ -51,7 +53,7 @@ export function HighlightSection({ initialIsOpen={true} data-test-subj={`logExplorerFlyoutHighlightSection${title}`} > - {flexChildren} + {flexChildren} {showBottomRule && } diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts b/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts index ef755c97f1f53..9c27e333a4bdc 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/translations.ts @@ -151,3 +151,10 @@ export const flyoutHoverActionToggleColumnText = i18n.translate( defaultMessage: 'Toggle column in table', } ); + +export const flyoutHoverActionCopyToClipboardText = i18n.translate( + 'xpack.logExplorer.flyoutDetail.value.hover.copyToClipboard', + { + defaultMessage: 'Copy to clipboard', + } +); diff --git a/x-pack/plugins/log_explorer/public/hooks/use_flyouot_column_width.tsx b/x-pack/plugins/log_explorer/public/hooks/use_flyouot_column_width.tsx new file mode 100644 index 0000000000000..53e626223b910 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/hooks/use_flyouot_column_width.tsx @@ -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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEuiTheme } from '@elastic/eui'; + +interface FlyoutColumnWidth { + columns: 1 | 2 | 3; + fieldWidth: number; +} + +export const useFlyoutColumnWidth = (width: number): FlyoutColumnWidth => { + const { euiTheme } = useEuiTheme(); + + const numberOfColumns = width > euiTheme.breakpoint.m ? 3 : width > euiTheme.breakpoint.s ? 2 : 1; + const widthFactor = numberOfColumns === 3 ? 2.5 : 2.2; + const fieldWidth = width / (numberOfColumns * widthFactor); + + return { + columns: numberOfColumns, + fieldWidth, + }; +}; From eb85ed237c8b332654231d066b728d7ea46185de Mon Sep 17 00:00:00 2001 From: achyutjhunjhunwala Date: Fri, 10 Nov 2023 16:05:51 +0100 Subject: [PATCH 09/10] Fix review comments around hooks and highlights --- .../flyout_detail/flyout_highlights.tsx | 7 ++- .../sub_components/hover_action.tsx | 11 ++++- .../public/hooks/use_dimensions.tsx | 44 ------------------- 3 files changed, 13 insertions(+), 49 deletions(-) delete mode 100644 x-pack/plugins/log_explorer/public/hooks/use_dimensions.tsx diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx index d549f6a40a373..74d65d73f23b8 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx @@ -4,9 +4,10 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useRef } from 'react'; +import React from 'react'; import { FlyoutContentActions } from '@kbn/discover-plugin/public'; import { DataTableRecord } from '@kbn/discover-utils/src/types'; +import { useMeasure } from 'react-use/lib'; import { FlyoutDoc } from './types'; import * as constants from '../../../common/constants'; import { HighlightField } from './sub_components/highlight_field'; @@ -33,7 +34,6 @@ import { import { HighlightSection } from './sub_components/highlight_section'; import { DiscoverActionsProvider } from '../../hooks/use_discover_action'; import { HighlightContainer } from './sub_components/highlight_container'; -import { useDimension } from '../../hooks/use_dimensions'; import { useFlyoutColumnWidth } from '../../hooks/use_flyouot_column_width'; export function FlyoutHighlights({ @@ -45,8 +45,7 @@ export function FlyoutHighlights({ flattenedDoc: DataTableRecord['flattened']; actions: FlyoutContentActions; }) { - const elementRef = useRef(null); - const [ref, dimensions] = useDimension(elementRef); + const [ref, dimensions] = useMeasure(); const { columns, fieldWidth } = useFlyoutColumnWidth(dimensions.width); return ( diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx index 852649f16a6a9..5ed25be2b36d9 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_action.tsx @@ -13,6 +13,7 @@ import { EuiButtonIcon, useEuiTheme, EuiTextTruncate, + EuiText, } from '@elastic/eui'; import type { IconType } from '@elastic/eui'; @@ -32,6 +33,7 @@ interface HoverActionProps { export const HoverAction = ({ displayText, actions, width }: HoverActionProps) => { const { euiTheme } = useEuiTheme(); + return ( - + + {(truncatedText: string) => ( + + )} + ( - ref: React.RefObject -): [React.RefObject, Dimension] => { - const [dimensions, setDimensions] = useState({ width: 0, height: 0 }); - const resizeObserverRef = useRef(null); - - useEffect(() => { - const currentElement = ref.current; - if (!currentElement) return; - - const resizeObserver = new ResizeObserver((entries) => { - const [entry] = entries; - const { width, height } = entry.contentRect; - setDimensions({ width, height }); - }); - - resizeObserver.observe(currentElement); - - resizeObserverRef.current = resizeObserver; - - return () => { - if (resizeObserverRef.current) { - resizeObserverRef.current.unobserve(currentElement); - resizeObserverRef.current = null; - } - }; - }, [ref]); - - return [ref, dimensions]; -}; From 5546d5d9eb475c63d40ed79ea1b27bd6e7c9e97c Mon Sep 17 00:00:00 2001 From: achyutjhunjhunwala Date: Fri, 10 Nov 2023 17:01:40 +0100 Subject: [PATCH 10/10] Fix limits --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index de07bccf8bbcb..3762d976169fd 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -88,7 +88,7 @@ pageLoadAssetSize: licensing: 29004 links: 44490 lists: 22900 - logExplorer: 39045 + logExplorer: 54342 logsShared: 281060 logstash: 53548 management: 46112