Skip to content

Commit bcd34d0

Browse files
Create a new menu for observability links (elastic#54847)
* Create a new menu for observability links. Use it on inentory page. * Change the order of props for clarity * Fix default message * Composition over configuration * Show ids and ips. PR feedback. * Don't wrap subtitle. Use fields in inventory model for name * Tooltip was becoming hacky. Keep it simple and wrap the id. * Create observability plugin. Add action menu to it. * Fix path * Satisfy linter and fix test * Please the linter * Update translastions * Update test for disabled links * Update more tests Co-authored-by: Elastic Machine <[email protected]>
1 parent 2f2cd9a commit bcd34d0

File tree

17 files changed

+243
-64
lines changed

17 files changed

+243
-64
lines changed

x-pack/legacy/plugins/infra/common/inventory_models/aws_ec2/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export const awsEC2: InventoryModel = {
1313
displayName: i18n.translate('xpack.infra.inventoryModels.awsEC2.displayName', {
1414
defaultMessage: 'EC2 Instances',
1515
}),
16+
singularDisplayName: i18n.translate('xpack.infra.inventoryModels.awsEC2.singularDisplayName', {
17+
defaultMessage: 'EC2 Instance',
18+
}),
1619
requiredModule: 'aws',
1720
crosslinkSupport: {
1821
details: true,

x-pack/legacy/plugins/infra/common/inventory_models/aws_rds/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export const awsRDS: InventoryModel = {
1313
displayName: i18n.translate('xpack.infra.inventoryModels.awsRDS.displayName', {
1414
defaultMessage: 'RDS Databases',
1515
}),
16+
singularDisplayName: i18n.translate('xpack.infra.inventoryModels.awsRDS.singularDisplayName', {
17+
defaultMessage: 'RDS Database',
18+
}),
1619
requiredModule: 'aws',
1720
crosslinkSupport: {
1821
details: true,

x-pack/legacy/plugins/infra/common/inventory_models/aws_s3/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export const awsS3: InventoryModel = {
1313
displayName: i18n.translate('xpack.infra.inventoryModels.awsS3.displayName', {
1414
defaultMessage: 'S3 Buckets',
1515
}),
16+
singularDisplayName: i18n.translate('xpack.infra.inventoryModels.awsS3.singularDisplayName', {
17+
defaultMessage: 'S3 Bucket',
18+
}),
1619
requiredModule: 'aws',
1720
crosslinkSupport: {
1821
details: true,

x-pack/legacy/plugins/infra/common/inventory_models/aws_sqs/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export const awsSQS: InventoryModel = {
1313
displayName: i18n.translate('xpack.infra.inventoryModels.awsSQS.displayName', {
1414
defaultMessage: 'SQS Queues',
1515
}),
16+
singularDisplayName: i18n.translate('xpack.infra.inventoryModels.awsSQS.singularDisplayName', {
17+
defaultMessage: 'SQS Queue',
18+
}),
1619
requiredModule: 'aws',
1720
crosslinkSupport: {
1821
details: true,

x-pack/legacy/plugins/infra/common/inventory_models/container/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export const container: InventoryModel = {
1313
displayName: i18n.translate('xpack.infra.inventoryModel.container.displayName', {
1414
defaultMessage: 'Docker Containers',
1515
}),
16+
singularDisplayName: i18n.translate('xpack.infra.inventoryModel.container.singularDisplayName', {
17+
defaultMessage: 'Docker Container',
18+
}),
1619
requiredModule: 'docker',
1720
crosslinkSupport: {
1821
details: true,

x-pack/legacy/plugins/infra/common/inventory_models/host/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ export const host: InventoryModel = {
1717
displayName: i18n.translate('xpack.infra.inventoryModel.host.displayName', {
1818
defaultMessage: 'Hosts',
1919
}),
20+
singularDisplayName: i18n.translate('xpack.infra.inventoryModels.host.singularDisplayName', {
21+
defaultMessage: 'Host',
22+
}),
2023
requiredModule: 'system',
2124
crosslinkSupport: {
2225
details: true,

x-pack/legacy/plugins/infra/common/inventory_models/pod/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export const pod: InventoryModel = {
1414
displayName: i18n.translate('xpack.infra.inventoryModel.pod.displayName', {
1515
defaultMessage: 'Kubernetes Pods',
1616
}),
17+
singularDisplayName: i18n.translate('xpack.infra.inventoryModels.pod.singularDisplayName', {
18+
defaultMessage: 'Kubernetes Pod',
19+
}),
1720
requiredModule: 'kubernetes',
1821
crosslinkSupport: {
1922
details: true,

x-pack/legacy/plugins/infra/common/inventory_models/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ export interface InventoryMetrics {
320320
export interface InventoryModel {
321321
id: string;
322322
displayName: string;
323+
singularDisplayName: string;
323324
requiredModule: string;
324325
fields: {
325326
id: string;

x-pack/legacy/plugins/infra/public/components/waffle/node_context_menu.tsx

Lines changed: 107 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,26 @@
44
* you may not use this file except in compliance with the Elastic License.
55
*/
66

7-
import {
8-
EuiContextMenu,
9-
EuiContextMenuPanelDescriptor,
10-
EuiPopover,
11-
EuiPopoverProps,
12-
} from '@elastic/eui';
7+
import { EuiPopoverProps, EuiCode } from '@elastic/eui';
138
import { i18n } from '@kbn/i18n';
9+
import { FormattedMessage } from '@kbn/i18n/react';
1410

15-
import React from 'react';
11+
import React, { useMemo } from 'react';
1612
import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../lib/lib';
1713
import { getNodeDetailUrl, getNodeLogsUrl } from '../../pages/link_to';
1814
import { createUptimeLink } from './lib/create_uptime_link';
19-
import { findInventoryModel } from '../../../common/inventory_models';
15+
import { findInventoryModel, findInventoryFields } from '../../../common/inventory_models';
2016
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
2117
import { InventoryItemType } from '../../../common/inventory_models/types';
18+
import {
19+
Section,
20+
SectionLinkProps,
21+
ActionMenu,
22+
SectionTitle,
23+
SectionSubtitle,
24+
SectionLinks,
25+
SectionLink,
26+
} from '../../../../../../plugins/observability/public';
2227

2328
interface Props {
2429
options: InfraWaffleMapOptions;
@@ -43,83 +48,139 @@ export const NodeContextMenu = ({
4348
}: Props) => {
4449
const uiCapabilities = useKibana().services.application?.capabilities;
4550
const inventoryModel = findInventoryModel(nodeType);
51+
const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;
4652
// Due to the changing nature of the fields between APM and this UI,
4753
// We need to have some exceptions until 7.0 & ECS is finalized. Reference
4854
// #26620 for the details for these fields.
4955
// TODO: This is tech debt, remove it after 7.0 & ECS migration.
5056
const apmField = nodeType === 'host' ? 'host.hostname' : inventoryModel.fields.id;
5157

52-
const nodeLogsMenuItem = {
53-
name: i18n.translate('xpack.infra.nodeContextMenu.viewLogsName', {
54-
defaultMessage: 'View logs',
58+
const showDetail = inventoryModel.crosslinkSupport.details;
59+
const showLogsLink =
60+
inventoryModel.crosslinkSupport.logs && node.id && uiCapabilities?.logs?.show;
61+
const showAPMTraceLink =
62+
inventoryModel.crosslinkSupport.apm && uiCapabilities?.apm && uiCapabilities?.apm.show;
63+
const showUptimeLink =
64+
inventoryModel.crosslinkSupport.uptime && (['pod', 'container'].includes(nodeType) || node.ip);
65+
66+
const inventoryId = useMemo(() => {
67+
if (nodeType === 'host') {
68+
if (node.ip) {
69+
return { label: <EuiCode>host.ip</EuiCode>, value: node.ip };
70+
}
71+
} else {
72+
if (options.fields) {
73+
const { id } = findInventoryFields(nodeType, options.fields);
74+
return {
75+
label: <EuiCode>{id}</EuiCode>,
76+
value: node.id,
77+
};
78+
}
79+
}
80+
return { label: '', value: '' };
81+
}, [nodeType, node.ip, node.id, options.fields]);
82+
83+
const nodeLogsMenuItem: SectionLinkProps = {
84+
label: i18n.translate('xpack.infra.nodeContextMenu.viewLogsName', {
85+
defaultMessage: '{inventoryName} logs',
86+
values: { inventoryName: inventoryModel.singularDisplayName },
5587
}),
5688
href: getNodeLogsUrl({
5789
nodeType,
5890
nodeId: node.id,
5991
time: currentTime,
6092
}),
6193
'data-test-subj': 'viewLogsContextMenuItem',
94+
isDisabled: !showLogsLink,
6295
};
6396

64-
const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000;
65-
const nodeDetailMenuItem = {
66-
name: i18n.translate('xpack.infra.nodeContextMenu.viewMetricsName', {
67-
defaultMessage: 'View metrics',
97+
const nodeDetailMenuItem: SectionLinkProps = {
98+
label: i18n.translate('xpack.infra.nodeContextMenu.viewMetricsName', {
99+
defaultMessage: '{inventoryName} metrics',
100+
values: { inventoryName: inventoryModel.singularDisplayName },
68101
}),
69102
href: getNodeDetailUrl({
70103
nodeType,
71104
nodeId: node.id,
72105
from: nodeDetailFrom,
73106
to: currentTime,
74107
}),
108+
isDisabled: !showDetail,
75109
};
76110

77-
const apmTracesMenuItem = {
78-
name: i18n.translate('xpack.infra.nodeContextMenu.viewAPMTraces', {
79-
defaultMessage: 'View APM traces',
111+
const apmTracesMenuItem: SectionLinkProps = {
112+
label: i18n.translate('xpack.infra.nodeContextMenu.viewAPMTraces', {
113+
defaultMessage: '{inventoryName} APM traces',
114+
values: { inventoryName: inventoryModel.singularDisplayName },
80115
}),
81116
href: `../app/apm#/traces?_g=()&kuery=${apmField}:"${node.id}"`,
82117
'data-test-subj': 'viewApmTracesContextMenuItem',
118+
isDisabled: !showAPMTraceLink,
83119
};
84120

85-
const uptimeMenuItem = {
86-
name: i18n.translate('xpack.infra.nodeContextMenu.viewUptimeLink', {
87-
defaultMessage: 'View in Uptime',
121+
const uptimeMenuItem: SectionLinkProps = {
122+
label: i18n.translate('xpack.infra.nodeContextMenu.viewUptimeLink', {
123+
defaultMessage: '{inventoryName} in Uptime',
124+
values: { inventoryName: inventoryModel.singularDisplayName },
88125
}),
89126
href: createUptimeLink(options, nodeType, node),
127+
isDisabled: !showUptimeLink,
90128
};
91129

92-
const showDetail = inventoryModel.crosslinkSupport.details;
93-
const showLogsLink =
94-
inventoryModel.crosslinkSupport.logs && node.id && uiCapabilities?.logs?.show;
95-
const showAPMTraceLink =
96-
inventoryModel.crosslinkSupport.apm && uiCapabilities?.apm && uiCapabilities?.apm.show;
97-
const showUptimeLink =
98-
inventoryModel.crosslinkSupport.uptime && (['pod', 'container'].includes(nodeType) || node.ip);
99-
100-
const items = [
101-
...(showLogsLink ? [nodeLogsMenuItem] : []),
102-
...(showDetail ? [nodeDetailMenuItem] : []),
103-
...(showAPMTraceLink ? [apmTracesMenuItem] : []),
104-
...(showUptimeLink ? [uptimeMenuItem] : []),
105-
];
106-
const panels: EuiContextMenuPanelDescriptor[] = [{ id: 0, title: '', items }];
107-
108-
// If there is nothing to show then we need to return the child as is
109-
if (items.length === 0) {
110-
return <>{children}</>;
111-
}
112-
113130
return (
114-
<EuiPopover
131+
<ActionMenu
115132
closePopover={closePopover}
116133
id={`${node.pathId}-popover`}
117134
isOpen={isPopoverOpen}
118135
button={children}
119-
panelPaddingSize="none"
120136
anchorPosition={popoverPosition}
121137
>
122-
<EuiContextMenu initialPanelId={0} panels={panels} data-test-subj="nodeContextMenu" />
123-
</EuiPopover>
138+
<div style={{ maxWidth: 300 }} data-test-subj="nodeContextMenu">
139+
<Section>
140+
<SectionTitle>
141+
<FormattedMessage
142+
id="xpack.infra.nodeContextMenu.title"
143+
defaultMessage="{inventoryName} details"
144+
values={{ inventoryName: inventoryModel.singularDisplayName }}
145+
/>
146+
</SectionTitle>
147+
{inventoryId.label && (
148+
<SectionSubtitle>
149+
<div style={{ wordBreak: 'break-all' }}>
150+
<FormattedMessage
151+
id="xpack.infra.nodeContextMenu.description"
152+
defaultMessage="View details for {label} {value}"
153+
values={{ label: inventoryId.label, value: inventoryId.value }}
154+
/>
155+
</div>
156+
</SectionSubtitle>
157+
)}
158+
<SectionLinks>
159+
<SectionLink
160+
data-test-subj="viewLogsContextMenuItem"
161+
label={nodeLogsMenuItem.label}
162+
href={nodeLogsMenuItem.href}
163+
isDisabled={nodeLogsMenuItem.isDisabled}
164+
/>
165+
<SectionLink
166+
label={nodeDetailMenuItem.label}
167+
href={nodeDetailMenuItem.href}
168+
isDisabled={nodeDetailMenuItem.isDisabled}
169+
/>
170+
<SectionLink
171+
label={apmTracesMenuItem.label}
172+
href={apmTracesMenuItem.href}
173+
data-test-subj="viewApmTracesContextMenuItem"
174+
isDisabled={apmTracesMenuItem.isDisabled}
175+
/>
176+
<SectionLink
177+
label={uptimeMenuItem.label}
178+
href={uptimeMenuItem.href}
179+
isDisabled={uptimeMenuItem.isDisabled}
180+
/>
181+
</SectionLinks>
182+
</Section>
183+
</div>
184+
</ActionMenu>
124185
);
125186
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"id": "observability",
3+
"version": "8.0.0",
4+
"kibanaVersion": "kibana",
5+
"ui": true
6+
}

0 commit comments

Comments
 (0)