From 3b22b1fc2aac0ac7062f76142e3b3e1323b445a4 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Thu, 12 Nov 2020 16:26:37 -0600 Subject: [PATCH] APM header changes (#82870) - Make APM and UX headers size medium instead of large - Remove margin around APM main container - Make APM tabs condensed - Switch environment filter and date picker positions - Move search bar (kuery + date picker) below the tabs - Wrap pages in `EuiPage` components - Set a minimum width on the enironment selector so it doesn't collapse when loading - Don't show search bar on service map Fixes #81954. --- .../plugins/apm/public/application/index.tsx | 2 - .../components/app/Correlations/index.tsx | 2 +- .../app/ErrorGroupDetails/index.tsx | 108 +++--- .../app/ErrorGroupOverview/index.tsx | 66 ++-- .../apm/public/components/app/Home/index.tsx | 101 +++--- .../components/app/RumDashboard/RumHome.tsx | 2 +- .../app/ServiceDetails/ServiceDetailTabs.tsx | 143 ++++---- .../components/app/ServiceDetails/index.tsx | 2 +- .../app/ServiceMap/cytoscape_options.ts | 1 - .../components/app/ServiceMap/index.tsx | 55 ++-- .../components/app/ServiceMetrics/index.tsx | 44 +-- .../app/ServiceNodeMetrics/index.tsx | 2 +- .../app/ServiceNodeOverview/index.tsx | 67 ++-- .../components/app/TraceOverview/index.tsx | 41 +-- .../app/TransactionDetails/index.tsx | 101 +++--- .../TransactionOverview.test.tsx | 8 +- .../app/TransactionOverview/index.tsx | 150 ++++----- .../app/service_inventory/index.tsx | 84 ++--- .../service_inventory.test.tsx | 31 +- .../components/app/service_overview/index.tsx | 307 +++++++++--------- .../service_overview.test.tsx | 50 ++- .../shared/ApmHeader/apm_header.stories.tsx | 2 +- .../components/shared/ApmHeader/index.tsx | 35 +- .../shared/EnvironmentFilter/index.tsx | 6 + .../public/components/shared/EuiTabLink.tsx | 49 --- .../components/shared/KueryBar/index.tsx | 10 +- .../components/shared/Links/apm/APMLink.tsx | 19 +- .../shared/Links/apm/ErrorOverviewLink.tsx | 29 +- .../shared/Links/apm/MetricOverviewLink.tsx | 30 +- .../shared/Links/apm/ServiceMapLink.tsx | 24 +- .../Links/apm/ServiceNodeOverviewLink.tsx | 28 +- .../shared/Links/apm/TraceOverviewLink.tsx | 34 +- .../Links/apm/TransactionOverviewLink.tsx | 30 +- .../Links/apm/service_inventory_link.tsx | 19 +- .../Links/apm/service_overview_link.tsx | 6 +- .../public/components/shared/main_tabs.tsx | 21 ++ .../public/components/shared/search_bar.tsx | 29 ++ .../public/components/app/header/index.tsx | 2 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 40 files changed, 927 insertions(+), 815 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/shared/EuiTabLink.tsx create mode 100644 x-pack/plugins/apm/public/components/shared/main_tabs.tsx create mode 100644 x-pack/plugins/apm/public/components/shared/search_bar.tsx diff --git a/x-pack/plugins/apm/public/application/index.tsx b/x-pack/plugins/apm/public/application/index.tsx index 2e1f259bd8c420..79c29867cb8e31 100644 --- a/x-pack/plugins/apm/public/application/index.tsx +++ b/x-pack/plugins/apm/public/application/index.tsx @@ -33,11 +33,9 @@ import { ApmPluginSetupDeps } from '../plugin'; import { createCallApmApi } from '../services/rest/createCallApmApi'; import { createStaticIndexPattern } from '../services/rest/index_pattern'; import { setHelpExtension } from '../setHelpExtension'; -import { px, units } from '../style/variables'; import { setReadonlyBadge } from '../updateBadge'; const MainContainer = styled.div` - padding: ${px(units.plus)}; height: 100%; `; diff --git a/x-pack/plugins/apm/public/components/app/Correlations/index.tsx b/x-pack/plugins/apm/public/components/app/Correlations/index.tsx index afee2b9f5e8814..e3dea70a232eb6 100644 --- a/x-pack/plugins/apm/public/components/app/Correlations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Correlations/index.tsx @@ -75,7 +75,7 @@ export function Correlations() { return ( <> - +

Correlations

diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx index e95d35142684d6..f47674ba5891f2 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx @@ -8,6 +8,8 @@ import { EuiBadge, EuiFlexGroup, EuiFlexItem, + EuiPage, + EuiPageBody, EuiPanel, EuiSpacer, EuiText, @@ -24,6 +26,7 @@ import { useUrlParams } from '../../../hooks/useUrlParams'; import { callApmApi } from '../../../services/rest/createCallApmApi'; import { fontFamilyCode, fontSizes, px, units } from '../../../style/variables'; import { ApmHeader } from '../../shared/ApmHeader'; +import { SearchBar } from '../../shared/search_bar'; import { DetailView } from './DetailView'; import { ErrorDistribution } from './Distribution'; @@ -120,7 +123,7 @@ export function ErrorGroupDetails({ location, match }: ErrorGroupDetailsProps) { errorGroupData.error?.error.exception?.[0].handled === false; return ( -
+ <> @@ -146,62 +149,67 @@ export function ErrorGroupDetails({ location, match }: ErrorGroupDetailsProps) { )} - - - - - {showDetails && ( - - - {logMessage && ( - + + + + + {showDetails && ( + + + {logMessage && ( + + + {logMessage} + + )} - {logMessage} - + {excMessage || NOT_AVAILABLE_LABEL} + + {culprit || NOT_AVAILABLE_LABEL} + + + )} + - {i18n.translate( - 'xpack.apm.errorGroupDetails.exceptionMessageLabel', - { - defaultMessage: 'Exception message', - } - )} - - {excMessage || NOT_AVAILABLE_LABEL} - - {culprit || NOT_AVAILABLE_LABEL} - - - )} - + + + {showDetails && ( + )} - /> - - - {showDetails && ( - - )} -
+ + + ); } diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx index 42b0016ca8cfef..52fb4b33cbc55c 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx @@ -7,6 +7,7 @@ import { EuiFlexGroup, EuiFlexItem, + EuiPage, EuiPanel, EuiSpacer, EuiTitle, @@ -19,6 +20,7 @@ import { useFetcher } from '../../../hooks/useFetcher'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { callApmApi } from '../../../services/rest/createCallApmApi'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; +import { SearchBar } from '../../shared/search_bar'; import { ErrorDistribution } from '../ErrorGroupDetails/Distribution'; import { ErrorGroupList } from './List'; @@ -95,39 +97,41 @@ function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) { return ( <> - - - - - - - - - - - - - - -

Errors

-
+ + + + + + + + + + + - -
-
-
+ + +

Errors

+
+ + + +
+ + + ); } diff --git a/x-pack/plugins/apm/public/components/app/Home/index.tsx b/x-pack/plugins/apm/public/components/app/Home/index.tsx index 717ce9b8ebd528..5b1ddaf1e49ef1 100644 --- a/x-pack/plugins/apm/public/components/app/Home/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Home/index.tsx @@ -4,79 +4,76 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiTabs, EuiTitle } from '@elastic/eui'; +import { EuiTab, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { ComponentType } from 'react'; import { $ElementType } from 'utility-types'; import { ApmHeader } from '../../shared/ApmHeader'; -import { EuiTabLink } from '../../shared/EuiTabLink'; -import { ServiceMapLink } from '../../shared/Links/apm/ServiceMapLink'; -import { ServiceInventoryLink } from '../../shared/Links/apm/service_inventory_link'; -import { TraceOverviewLink } from '../../shared/Links/apm/TraceOverviewLink'; +import { useServiceMapHref } from '../../shared/Links/apm/ServiceMapLink'; +import { useServiceInventoryHref } from '../../shared/Links/apm/service_inventory_link'; +import { useTraceOverviewHref } from '../../shared/Links/apm/TraceOverviewLink'; +import { MainTabs } from '../../shared/main_tabs'; import { ServiceMap } from '../ServiceMap'; import { ServiceInventory } from '../service_inventory'; import { TraceOverview } from '../TraceOverview'; -const homeTabs = [ - { - link: ( - - {i18n.translate('xpack.apm.home.servicesTabLabel', { - defaultMessage: 'Services', - })} - - ), - render: () => , - name: 'services', - }, - { - link: ( - - {i18n.translate('xpack.apm.home.tracesTabLabel', { - defaultMessage: 'Traces', - })} - - ), - render: () => , - name: 'traces', - }, - { - link: ( - - {i18n.translate('xpack.apm.home.serviceMapTabLabel', { - defaultMessage: 'Service Map', - })} - - ), - render: () => , - name: 'service-map', - }, -]; +interface Tab { + key: string; + href: string; + text: string; + Component: ComponentType; +} interface Props { tab: 'traces' | 'services' | 'service-map'; } export function Home({ tab }: Props) { + const homeTabs: Tab[] = [ + { + key: 'services', + href: useServiceInventoryHref(), + text: i18n.translate('xpack.apm.home.servicesTabLabel', { + defaultMessage: 'Services', + }), + Component: ServiceInventory, + }, + { + key: 'traces', + href: useTraceOverviewHref(), + text: i18n.translate('xpack.apm.home.tracesTabLabel', { + defaultMessage: 'Traces', + }), + Component: TraceOverview, + }, + { + key: 'service-map', + href: useServiceMapHref(), + text: i18n.translate('xpack.apm.home.serviceMapTabLabel', { + defaultMessage: 'Service Map', + }), + Component: ServiceMap, + }, + ]; const selectedTab = homeTabs.find( - (homeTab) => homeTab.name === tab + (homeTab) => homeTab.key === tab ) as $ElementType; return ( -
+ <> - +

APM

- - {homeTabs.map((homeTab) => ( - - {homeTab.link} - + + {homeTabs.map(({ href, key, text }) => ( + + {text} + ))} - - {selectedTab.render()} -
+ + + ); } diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx index f25761bd8abad8..da83d3425a99c3 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/RumHome.tsx @@ -21,7 +21,7 @@ export function RumHome() { - +

{UX_LABEL}

diff --git a/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx index 625a8e73debc94..f42b94b8afe335 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx @@ -4,20 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiTabs } from '@elastic/eui'; +import { EuiTab } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { ReactNode } from 'react'; import { isJavaAgentName, isRumAgentName } from '../../../../common/agent_name'; import { enableServiceOverview } from '../../../../common/ui_settings_keys'; import { useAgentName } from '../../../hooks/useAgentName'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; -import { EuiTabLink } from '../../shared/EuiTabLink'; -import { ErrorOverviewLink } from '../../shared/Links/apm/ErrorOverviewLink'; -import { MetricOverviewLink } from '../../shared/Links/apm/MetricOverviewLink'; -import { ServiceMapLink } from '../../shared/Links/apm/ServiceMapLink'; -import { ServiceNodeOverviewLink } from '../../shared/Links/apm/ServiceNodeOverviewLink'; -import { ServiceOverviewLink } from '../../shared/Links/apm/service_overview_link'; -import { TransactionOverviewLink } from '../../shared/Links/apm/TransactionOverviewLink'; +import { useErrorOverviewHref } from '../../shared/Links/apm/ErrorOverviewLink'; +import { useMetricOverviewHref } from '../../shared/Links/apm/MetricOverviewLink'; +import { useServiceMapHref } from '../../shared/Links/apm/ServiceMapLink'; +import { useServiceNodeOverviewHref } from '../../shared/Links/apm/ServiceNodeOverviewLink'; +import { useServiceOverviewHref } from '../../shared/Links/apm/service_overview_link'; +import { useTransactionOverviewHref } from '../../shared/Links/apm/TransactionOverviewLink'; +import { MainTabs } from '../../shared/main_tabs'; import { ErrorGroupOverview } from '../ErrorGroupOverview'; import { ServiceMap } from '../ServiceMap'; import { ServiceMetrics } from '../ServiceMetrics'; @@ -25,6 +25,13 @@ import { ServiceNodeOverview } from '../ServiceNodeOverview'; import { ServiceOverview } from '../service_overview'; import { TransactionOverview } from '../TransactionOverview'; +interface Tab { + key: string; + href: string; + text: string; + render: () => ReactNode; +} + interface Props { serviceName: string; tab: @@ -41,107 +48,91 @@ export function ServiceDetailTabs({ serviceName, tab }: Props) { const { uiSettings } = useApmPluginContext().core; const overviewTab = { - link: ( - - {i18n.translate('xpack.apm.serviceDetails.overviewTabLabel', { - defaultMessage: 'Overview', - })} - - ), + key: 'overview', + href: useServiceOverviewHref(serviceName), + text: i18n.translate('xpack.apm.serviceDetails.overviewTabLabel', { + defaultMessage: 'Overview', + }), render: () => ( ), - name: 'overview', }; const transactionsTab = { - link: ( - - {i18n.translate('xpack.apm.serviceDetails.transactionsTabLabel', { - defaultMessage: 'Transactions', - })} - - ), + key: 'transactions', + href: useTransactionOverviewHref(serviceName), + text: i18n.translate('xpack.apm.serviceDetails.transactionsTabLabel', { + defaultMessage: 'Transactions', + }), render: () => , - name: 'transactions', }; const errorsTab = { - link: ( - - {i18n.translate('xpack.apm.serviceDetails.errorsTabLabel', { - defaultMessage: 'Errors', - })} - - ), + key: 'errors', + href: useErrorOverviewHref(serviceName), + text: i18n.translate('xpack.apm.serviceDetails.errorsTabLabel', { + defaultMessage: 'Errors', + }), render: () => { return ; }, - name: 'errors', }; const serviceMapTab = { - link: ( - - {i18n.translate('xpack.apm.home.serviceMapTabLabel', { - defaultMessage: 'Service Map', - })} - - ), + key: 'service-map', + href: useServiceMapHref(serviceName), + text: i18n.translate('xpack.apm.home.serviceMapTabLabel', { + defaultMessage: 'Service Map', + }), render: () => , - name: 'service-map', }; - const tabs = [transactionsTab, errorsTab, serviceMapTab]; + const nodesListTab = { + key: 'nodes', + href: useServiceNodeOverviewHref(serviceName), + text: i18n.translate('xpack.apm.serviceDetails.nodesTabLabel', { + defaultMessage: 'JVMs', + }), + render: () => , + }; + + const metricsTab = { + key: 'metrics', + href: useMetricOverviewHref(serviceName), + text: i18n.translate('xpack.apm.serviceDetails.metricsTabLabel', { + defaultMessage: 'Metrics', + }), + render: () => + agentName ? ( + + ) : null, + }; + + const tabs: Tab[] = [transactionsTab, errorsTab]; if (uiSettings.get(enableServiceOverview)) { tabs.unshift(overviewTab); } if (isJavaAgentName(agentName)) { - const nodesListTab = { - link: ( - - {i18n.translate('xpack.apm.serviceDetails.nodesTabLabel', { - defaultMessage: 'JVMs', - })} - - ), - render: () => , - name: 'nodes', - }; tabs.push(nodesListTab); } else if (agentName && !isRumAgentName(agentName)) { - const metricsTab = { - link: ( - - {i18n.translate('xpack.apm.serviceDetails.metricsTabLabel', { - defaultMessage: 'Metrics', - })} - - ), - render: () => ( - - ), - name: 'metrics', - }; tabs.push(metricsTab); } - const selectedTab = tabs.find((serviceTab) => serviceTab.name === tab); + tabs.push(serviceMapTab); + + const selectedTab = tabs.find((serviceTab) => serviceTab.key === tab); return ( <> - - {tabs.map((serviceTab) => ( - - {serviceTab.link} - + + {tabs.map(({ href, key, text }) => ( + + {text} + ))} - + {selectedTab ? selectedTab.render() : null} ); diff --git a/x-pack/plugins/apm/public/components/app/ServiceDetails/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/index.tsx index aa5dcd5a5ea18b..8df2b0fda7a7e1 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceDetails/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/index.tsx @@ -20,7 +20,7 @@ export function ServiceDetails({ match, tab }: Props) { return (
- +

{serviceName}

diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts index e51f53567b5ffe..d8a8a3c8e9ab4d 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape_options.ts @@ -252,7 +252,6 @@ center, ${theme.eui.euiColorLightShade}`, backgroundSize: `${theme.eui.euiSizeL} ${theme.eui.euiSizeL}`, cursor: `${status === FETCH_STATUS.LOADING ? 'wait' : 'grab'}`, - margin: `-${theme.eui.gutterTypes.gutterLarge}`, marginTop: 0, }); diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx index 752f9b7fda243e..15adf8a70d3570 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -5,7 +5,8 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; -import React, { ReactNode } from 'react'; +import React, { PropsWithChildren, ReactNode } from 'react'; +import styled from 'styled-components'; import { useTrackPageview } from '../../../../../observability/public'; import { invalidLicenseMessage, @@ -17,20 +18,27 @@ import { useLicense } from '../../../hooks/useLicense'; import { useTheme } from '../../../hooks/useTheme'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { callApmApi } from '../../../services/rest/createCallApmApi'; +import { DatePicker } from '../../shared/DatePicker'; import { LicensePrompt } from '../../shared/LicensePrompt'; import { Controls } from './Controls'; import { Cytoscape } from './Cytoscape'; import { getCytoscapeDivStyle } from './cytoscape_options'; import { EmptyBanner } from './EmptyBanner'; import { EmptyPrompt } from './empty_prompt'; -import { TimeoutPrompt } from './timeout_prompt'; import { Popover } from './Popover'; +import { TimeoutPrompt } from './timeout_prompt'; import { useRefDimensions } from './useRefDimensions'; interface ServiceMapProps { serviceName?: string; } +const ServiceMapDatePickerFlexGroup = styled(EuiFlexGroup)` + padding: ${({ theme }) => theme.eui.euiSizeM}; + border-bottom: ${({ theme }) => theme.eui.euiBorderThin}; + margin: 0; +`; + function PromptContainer({ children }: { children: ReactNode }) { return ( ) { const theme = useTheme(); const license = useLicense(); const { urlParams } = useUrlParams(); @@ -126,24 +136,25 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { } return ( -
- - - {serviceName && } - {status === FETCH_STATUS.LOADING && } - - -
+ <> + + + + + +
+ + + {serviceName && } + {status === FETCH_STATUS.LOADING && } + + +
+ ); } diff --git a/x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx index 042752ef62f531..5808c54d578c6d 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx @@ -7,6 +7,7 @@ import { EuiFlexGrid, EuiFlexItem, + EuiPage, EuiPanel, EuiSpacer, EuiFlexGroup, @@ -18,6 +19,7 @@ import { useUrlParams } from '../../../hooks/useUrlParams'; import { LegacyChartsSyncContextProvider as ChartsSyncContextProvider } from '../../../context/charts_sync_context'; import { Projection } from '../../../../common/projections'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; +import { SearchBar } from '../../shared/search_bar'; interface ServiceMetricsProps { agentName: string; @@ -46,26 +48,28 @@ export function ServiceMetrics({ return ( <> - - - - - - - - - {data.charts.map((chart) => ( - - - - - - ))} - - - - - + + + + + + + + + + {data.charts.map((chart) => ( + + + + + + ))} + + + + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx index 566585c67e212e..7c6b63f75382cd 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx @@ -83,7 +83,7 @@ export function ServiceNodeMetrics({ match }: ServiceNodeMetricsProps) { - +

{serviceName}

diff --git a/x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx index 02dc3934dd01eb..b05785db146254 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx @@ -3,30 +3,31 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem, + EuiPage, EuiPanel, EuiToolTip, - EuiSpacer, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; +import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../common/i18n'; +import { Projection } from '../../../../common/projections'; +import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; import { asDynamicBytes, asInteger, asPercent, } from '../../../../common/utils/formatters'; -import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../common/i18n'; -import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; -import { Projection } from '../../../../common/projections'; -import { LocalUIFilters } from '../../shared/LocalUIFilters'; -import { useUrlParams } from '../../../hooks/useUrlParams'; -import { ManagedTable, ITableColumn } from '../../shared/ManagedTable'; import { useFetcher } from '../../../hooks/useFetcher'; +import { useUrlParams } from '../../../hooks/useUrlParams'; +import { px, truncate, unit } from '../../../style/variables'; import { ServiceNodeMetricOverviewLink } from '../../shared/Links/apm/ServiceNodeMetricOverviewLink'; -import { truncate, px, unit } from '../../../style/variables'; +import { LocalUIFilters } from '../../shared/LocalUIFilters'; +import { ITableColumn, ManagedTable } from '../../shared/ManagedTable'; +import { SearchBar } from '../../shared/search_bar'; const INITIAL_PAGE_SIZE = 25; const INITIAL_SORT_FIELD = 'cpu'; @@ -157,29 +158,31 @@ function ServiceNodeOverview({ serviceName }: ServiceNodeOverviewProps) { return ( <> - - - - - - - - - - - + + + + + + + + + + + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/TraceOverview/index.tsx b/x-pack/plugins/apm/public/components/app/TraceOverview/index.tsx index 06b4459fb56ebd..a87bbdb926a21e 100644 --- a/x-pack/plugins/apm/public/components/app/TraceOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceOverview/index.tsx @@ -4,15 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiPage, EuiPanel } from '@elastic/eui'; import React, { useMemo } from 'react'; -import { FETCH_STATUS, useFetcher } from '../../../hooks/useFetcher'; -import { TraceList } from './TraceList'; -import { useUrlParams } from '../../../hooks/useUrlParams'; import { useTrackPageview } from '../../../../../observability/public'; -import { LocalUIFilters } from '../../shared/LocalUIFilters'; import { Projection } from '../../../../common/projections'; +import { FETCH_STATUS, useFetcher } from '../../../hooks/useFetcher'; +import { useUrlParams } from '../../../hooks/useUrlParams'; import { APIReturnType } from '../../../services/rest/createCallApmApi'; +import { LocalUIFilters } from '../../shared/LocalUIFilters'; +import { SearchBar } from '../../shared/search_bar'; +import { TraceList } from './TraceList'; type TracesAPIResponse = APIReturnType<'/api/apm/traces'>; const DEFAULT_RESPONSE: TracesAPIResponse = { @@ -56,20 +57,22 @@ export function TraceOverview() { return ( <> - - - - - - - - - - - + + + + + + + + + + + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx index e4c36b028e55cc..9d9261fec6c1e8 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx @@ -8,6 +8,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, + EuiPage, EuiPanel, EuiSpacer, EuiTitle, @@ -32,6 +33,7 @@ import { useUrlParams } from '../../../hooks/useUrlParams'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; import { HeightRetainer } from '../../shared/HeightRetainer'; import { Correlations } from '../Correlations'; +import { SearchBar } from '../../shared/search_bar'; interface Sample { traceId: string; @@ -109,58 +111,59 @@ export function TransactionDetails({ }; return ( -
+ <> - +

{transactionName}

- - - - - - - - - - - - - - - - { - if (!isEmpty(bucket.samples)) { - selectSampleFromBucketClick(bucket.samples[0]); - } - }} - /> - - - - - - - - - -
+ + + + + + + + + + + + + + + + { + if (!isEmpty(bucket.samples)) { + selectSampleFromBucketClick(bucket.samples[0]); + } + }} + /> + + + + + + + + + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/TransactionOverview/TransactionOverview.test.tsx b/x-pack/plugins/apm/public/components/app/TransactionOverview/TransactionOverview.test.tsx index c530a7e1489adc..2d7992feb3760c 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionOverview/TransactionOverview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionOverview/TransactionOverview.test.tsx @@ -119,14 +119,18 @@ describe('TransactionOverview', () => { }, }); - expect(history.location.search).toEqual('?transactionType=secondType'); + expect(history.location.search).toEqual( + '?transactionType=secondType&rangeFrom=now-15m&rangeTo=now' + ); expect(getByText(container, 'firstType')).toBeInTheDocument(); expect(getByText(container, 'secondType')).toBeInTheDocument(); fireEvent.click(getByText(container, 'firstType')); expect(history.push).toHaveBeenCalled(); - expect(history.location.search).toEqual('?transactionType=firstType'); + expect(history.location.search).toEqual( + '?transactionType=firstType&rangeFrom=now-15m&rangeTo=now' + ); }); }); diff --git a/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx index df9e673ed4847e..a4f8d37867dd52 100644 --- a/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx @@ -10,6 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, + EuiPage, EuiPanel, EuiSpacer, EuiTitle, @@ -33,6 +34,7 @@ import { ElasticDocsLink } from '../../shared/Links/ElasticDocsLink'; import { fromQuery, toQuery } from '../../shared/Links/url_helpers'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; import { TransactionTypeFilter } from '../../shared/LocalUIFilters/TransactionTypeFilter'; +import { SearchBar } from '../../shared/search_bar'; import { Correlations } from '../Correlations'; import { TransactionList } from './TransactionList'; import { useRedirect } from './useRedirect'; @@ -120,82 +122,84 @@ export function TransactionOverview({ serviceName }: TransactionOverviewProps) { return ( <> + - - - - - - - - - - - {transactionType === TRANSACTION_PAGE_LOAD && ( - <> - - - - )} - - - - - - -

Transactions

-
- - {!transactionListData.isAggregationAccurate && ( - -

- - xpack.apm.ui.transactionGroupBucketSize - - ), - }} - /> - - - {i18n.translate( - 'xpack.apm.transactionCardinalityWarning.docsLink', - { defaultMessage: 'Learn more in the docs' } - )} - -

-
+ + + + + + + + + + + {transactionType === TRANSACTION_PAGE_LOAD && ( + <> + + + )} - - -
-
-
+ + + +

Transactions

+
+ + {!transactionListData.isAggregationAccurate && ( + +

+ + xpack.apm.ui.transactionGroupBucketSize + + ), + }} + /> + + + {i18n.translate( + 'xpack.apm.transactionCardinalityWarning.docsLink', + { defaultMessage: 'Learn more in the docs' } + )} + +

+
+ )} + + +
+ +
+ ); } diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx index 43af1a3e04dcd0..7a5893314ddf0c 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx @@ -4,24 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { EuiLink } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiLink, + EuiPage, + EuiPanel, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useMemo } from 'react'; import url from 'url'; import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public'; -import { useFetcher, FETCH_STATUS } from '../../../hooks/useFetcher'; -import { NoServicesMessage } from './no_services_message'; -import { ServiceList } from './ServiceList'; -import { useUrlParams } from '../../../hooks/useUrlParams'; import { useTrackPageview } from '../../../../../observability/public'; import { Projection } from '../../../../common/projections'; -import { LocalUIFilters } from '../../shared/LocalUIFilters'; +import { useAnomalyDetectionJobs } from '../../../hooks/useAnomalyDetectionJobs'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; -import { MLCallout } from './ServiceList/MLCallout'; +import { FETCH_STATUS, useFetcher } from '../../../hooks/useFetcher'; import { useLocalStorage } from '../../../hooks/useLocalStorage'; -import { useAnomalyDetectionJobs } from '../../../hooks/useAnomalyDetectionJobs'; +import { useUrlParams } from '../../../hooks/useUrlParams'; +import { LocalUIFilters } from '../../shared/LocalUIFilters'; +import { SearchBar } from '../../shared/search_bar'; import { Correlations } from '../Correlations'; +import { NoServicesMessage } from './no_services_message'; +import { ServiceList } from './ServiceList'; +import { MLCallout } from './ServiceList/MLCallout'; const initialData = { items: [], @@ -121,37 +127,39 @@ export function ServiceInventory() { return ( <> - - - - - - - - - - - {displayMlCallout ? ( + + + + + + + + + + {displayMlCallout ? ( + + setUserHasDismissedCallout(true)} + /> + + ) : null} - setUserHasDismissedCallout(true)} /> + + + } + /> + - ) : null} - - - - } - /> - - - - - + + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx index 247e91fb438ef5..de5e92664a769c 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_inventory.test.tsx @@ -21,8 +21,9 @@ import { import * as useAnomalyDetectionJobs from '../../../hooks/useAnomalyDetectionJobs'; import { FETCH_STATUS } from '../../../hooks/useFetcher'; import * as useLocalUIFilters from '../../../hooks/useLocalUIFilters'; -import * as urlParamsHooks from '../../../hooks/useUrlParams'; +import * as useDynamicIndexPatternHooks from '../../../hooks/useDynamicIndexPattern'; import { SessionStorageMock } from '../../../services/__test__/SessionStorageMock'; +import { MockUrlParamsContextProvider } from '../../../context/UrlParamsContext/MockUrlParamsContextProvider'; const KibanaReactContext = createKibanaReactContext({ usageCollection: { reportUiStats: () => {} }, @@ -50,7 +51,16 @@ function wrapper({ children }: { children?: ReactNode }) { - {children} + + {children} + @@ -63,16 +73,6 @@ describe('ServiceInventory', () => { // @ts-expect-error global.sessionStorage = new SessionStorageMock(); - // mock urlParams - jest.spyOn(urlParamsHooks, 'useUrlParams').mockReturnValue({ - urlParams: { - start: 'myStart', - end: 'myEnd', - }, - refreshTimeRange: jest.fn(), - uiFilters: {}, - }); - jest.spyOn(useLocalUIFilters, 'useLocalUIFilters').mockReturnValue({ filters: [], setFilterValue: () => null, @@ -90,6 +90,13 @@ describe('ServiceInventory', () => { }, refetch: () => undefined, }); + + jest + .spyOn(useDynamicIndexPatternHooks, 'useDynamicIndexPattern') + .mockReturnValue({ + indexPattern: undefined, + status: FETCH_STATUS.SUCCESS, + }); }); afterEach(() => { diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx index ee77157fe4eb36..44bd7d6c73d8ef 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx @@ -4,7 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiTitle } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiPage, + EuiPanel, + EuiTitle, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import styled from 'styled-components'; @@ -14,6 +20,7 @@ import { ChartsSyncContextProvider } from '../../../context/charts_sync_context' import { TransactionErrorRateChart } from '../../shared/charts/transaction_error_rate_chart'; import { ServiceMapLink } from '../../shared/Links/apm/ServiceMapLink'; import { TransactionOverviewLink } from '../../shared/Links/apm/TransactionOverviewLink'; +import { SearchBar } from '../../shared/search_bar'; import { ServiceOverviewErrorsTable } from './service_overview_errors_table'; import { TableLinkFlexItem } from './table_link_flex_item'; @@ -42,177 +49,167 @@ export function ServiceOverview({ return ( - - - - - Search bar - - - Comparison picker - - - Date picker - - - - - - -

- {i18n.translate('xpack.apm.serviceOverview.latencyChartTitle', { - defaultMessage: 'Latency', - })} -

-
-
-
- - - - - -

- {i18n.translate( - 'xpack.apm.serviceOverview.trafficChartTitle', - { - defaultMessage: 'Traffic', - } - )} -

-
-
-
- - - - - -

- {i18n.translate( - 'xpack.apm.serviceOverview.transactionsTableTitle', - { - defaultMessage: 'Transactions', - } - )} -

-
-
- - + + + + + + +

+ {i18n.translate( + 'xpack.apm.serviceOverview.latencyChartTitle', + { + defaultMessage: 'Latency', + } + )} +

+
+
+
+ + + + + +

{i18n.translate( - 'xpack.apm.serviceOverview.transactionsTableLinkText', + 'xpack.apm.serviceOverview.trafficChartTitle', { - defaultMessage: 'View transactions', + defaultMessage: 'Traffic', } )} - - - - - - - - - - {!isRumAgentName(agentName) && ( - - +

+
+
- )} - - - - - -
-
- - - - - - - -

+ + + + + +

+ {i18n.translate( + 'xpack.apm.serviceOverview.transactionsTableTitle', + { + defaultMessage: 'Transactions', + } + )} +

+
+
+ + {i18n.translate( - 'xpack.apm.serviceOverview.averageDurationBySpanTypeChartTitle', + 'xpack.apm.serviceOverview.transactionsTableLinkText', { - defaultMessage: 'Average duration by span type', + defaultMessage: 'View transactions', } )} -

-
-
-
-
-
- - - - - -

+ + + + + + + + + + {!isRumAgentName(agentName) && ( + + + + )} + + + + + + + + + + + + + + +

+ {i18n.translate( + 'xpack.apm.serviceOverview.averageDurationBySpanTypeChartTitle', + { + defaultMessage: 'Average duration by span type', + } + )} +

+
+
+
+
+
+ + + + + +

+ {i18n.translate( + 'xpack.apm.serviceOverview.dependenciesTableTitle', + { + defaultMessage: 'Dependencies', + } + )} +

+
+
+ + {i18n.translate( - 'xpack.apm.serviceOverview.dependenciesTableTitle', + 'xpack.apm.serviceOverview.dependenciesTableLinkText', { - defaultMessage: 'Dependencies', + defaultMessage: 'View service map', } )} -

-
-
- - + + +
+
+
+
+
+ + + + + +

{i18n.translate( - 'xpack.apm.serviceOverview.dependenciesTableLinkText', + 'xpack.apm.serviceOverview.instancesLatencyDistributionChartTitle', { - defaultMessage: 'View service map', + defaultMessage: 'Instances latency distribution', } )} - - - - - - - - - - - - -

- {i18n.translate( - 'xpack.apm.serviceOverview.instancesLatencyDistributionChartTitle', - { - defaultMessage: 'Instances latency distribution', - } - )} -

-
-
-
- - - -

- {i18n.translate( - 'xpack.apm.serviceOverview.instancesTableTitle', - { - defaultMessage: 'Instances', - } - )} -

-
-
-
-
-
- +

+
+
+
+ + + +

+ {i18n.translate( + 'xpack.apm.serviceOverview.instancesTableTitle', + { + defaultMessage: 'Instances', + } + )} +

+
+
+
+
+
+
+
); } diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx index 210d3533038549..8f9e76a5a79a65 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview.test.tsx @@ -8,7 +8,15 @@ import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; import { CoreStart } from 'src/core/public'; import { createKibanaReactContext } from '../../../../../../../src/plugins/kibana_react/public'; -import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext'; +import { ApmPluginContextValue } from '../../../context/ApmPluginContext'; +import { + mockApmPluginContextValue, + MockApmPluginContextWrapper, +} from '../../../context/ApmPluginContext/MockApmPluginContext'; +import { MockUrlParamsContextProvider } from '../../../context/UrlParamsContext/MockUrlParamsContextProvider'; +import * as useDynamicIndexPatternHooks from '../../../hooks/useDynamicIndexPattern'; +import * as useFetcherHooks from '../../../hooks/useFetcher'; +import { FETCH_STATUS } from '../../../hooks/useFetcher'; import { renderWithTheme } from '../../../utils/testHelpers'; import { ServiceOverview } from './'; @@ -17,10 +25,27 @@ const KibanaReactContext = createKibanaReactContext({ } as Partial); function Wrapper({ children }: { children?: ReactNode }) { + const value = ({ + ...mockApmPluginContextValue, + core: { + ...mockApmPluginContextValue.core, + http: { + basePath: { prepend: () => {} }, + get: () => {}, + }, + }, + } as unknown) as ApmPluginContextValue; + return ( - + - {children} + + + {children} + + ); @@ -28,6 +53,25 @@ function Wrapper({ children }: { children?: ReactNode }) { describe('ServiceOverview', () => { it('renders', () => { + jest + .spyOn(useDynamicIndexPatternHooks, 'useDynamicIndexPattern') + .mockReturnValue({ + indexPattern: undefined, + status: FETCH_STATUS.SUCCESS, + }); + jest.spyOn(useFetcherHooks, 'useFetcher').mockReturnValue({ + data: { + items: [], + tableOptions: { + pageIndex: 0, + sort: { direction: 'desc', field: 'test field' }, + }, + totalItemCount: 0, + }, + refetch: () => {}, + status: FETCH_STATUS.SUCCESS, + }); + expect(() => renderWithTheme(, { wrapper: Wrapper, diff --git a/x-pack/plugins/apm/public/components/shared/ApmHeader/apm_header.stories.tsx b/x-pack/plugins/apm/public/components/shared/ApmHeader/apm_header.stories.tsx index 4078998bc7e3e8..56501d8c916f47 100644 --- a/x-pack/plugins/apm/public/components/shared/ApmHeader/apm_header.stories.tsx +++ b/x-pack/plugins/apm/public/components/shared/ApmHeader/apm_header.stories.tsx @@ -38,7 +38,7 @@ export default { export function Example() { return ( - +

GET /api/v1/regions/azure-eastus2/clusters/elasticsearch/xc18de071deb4262be54baebf5f6a1ce/proxy/_snapshot/found-snapshots/_all diff --git a/x-pack/plugins/apm/public/components/shared/ApmHeader/index.tsx b/x-pack/plugins/apm/public/components/shared/ApmHeader/index.tsx index 96f170fa6a093c..a806a3ea601541 100644 --- a/x-pack/plugins/apm/public/components/shared/ApmHeader/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/ApmHeader/index.tsx @@ -4,40 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React, { ReactNode } from 'react'; +import styled from 'styled-components'; import { HeaderMenuPortal } from '../../../../../observability/public'; import { ActionMenu } from '../../../application/action_menu'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; -import { DatePicker } from '../DatePicker'; import { EnvironmentFilter } from '../EnvironmentFilter'; -import { KueryBar } from '../KueryBar'; + +const HeaderFlexGroup = styled(EuiFlexGroup)` + padding: ${({ theme }) => theme.eui.gutterTypes.gutterMedium}; + border-bottom: ${({ theme }) => theme.eui.euiBorderThin}; +`; export function ApmHeader({ children }: { children: ReactNode }) { const { setHeaderActionMenu } = useApmPluginContext().appMountParameters; return ( - <> + - - {children} - - - - - - - - - - - - - - - - + {children} + + + + ); } diff --git a/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx b/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx index e6e40e44bad380..cace4c2770f370 100644 --- a/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx @@ -73,6 +73,11 @@ export function EnvironmentFilter() { end, }); + // Set the min-width so we don't see as much collapsing of the select during + // the loading state. 200px is what is looks like if "production" is + // the contents. + const minWidth = 200; + return ( ); } diff --git a/x-pack/plugins/apm/public/components/shared/EuiTabLink.tsx b/x-pack/plugins/apm/public/components/shared/EuiTabLink.tsx deleted file mode 100644 index d29ccd8abcd420..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/EuiTabLink.tsx +++ /dev/null @@ -1,49 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import React from 'react'; -import cls from 'classnames'; -import styled from 'styled-components'; -import { px, unit } from '../../style/variables'; - -// TODO: replace this component with EUITab w/ a href prop -// as soon as EUI is upgraded to 13.8.1 -// see https://github.com/elastic/eui/pull/2275 - -interface Props { - isSelected: boolean; - children: React.ReactNode; -} - -// We need to remove padding and add it to the link, -// to prevent the user from clicking in the tab, but outside of the link -// We also need to override the color here to subdue the color of the link -// when not selected - -const Wrapper = styled.div<{ isSelected: boolean }>` - padding: 0; - a { - display: inline-block; - padding: ${px(unit * 0.75)} ${px(unit)}; - ${({ isSelected, theme }) => - !isSelected ? `color: ${theme.eui.euiTextColor} !important;` : ''} - } -`; - -function EuiTabLink(props: Props) { - const { isSelected, children } = props; - - const className = cls('euiTab', { - 'euiTab-isSelected': isSelected, - }); - - return ( - - {children} - - ); -} - -export { EuiTabLink }; diff --git a/x-pack/plugins/apm/public/components/shared/KueryBar/index.tsx b/x-pack/plugins/apm/public/components/shared/KueryBar/index.tsx index dce8e49deec414..2ef93fc32200e7 100644 --- a/x-pack/plugins/apm/public/components/shared/KueryBar/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/KueryBar/index.tsx @@ -80,13 +80,6 @@ export function KueryBar() { }, }); - // The bar should be disabled when viewing the service map - const disabled = /\/(service-map)$/.test(location.pathname); - const disabledPlaceholder = i18n.translate( - 'xpack.apm.kueryBar.disabledPlaceholder', - { defaultMessage: 'Search is not available here' } - ); - async function onChange(inputValue: string, selectionStart: number) { if (indexPattern == null) { return; @@ -153,13 +146,12 @@ export function KueryBar() { return ( ); diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.tsx index bd43f2455c545f..41c932bf9c9f55 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.tsx @@ -10,7 +10,9 @@ import { pick } from 'lodash'; import React from 'react'; import { useLocation } from 'react-router-dom'; import url from 'url'; +import { pickKeys } from '../../../../../common/utils/pick_keys'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; import { APMQueryParams, fromQuery, toQuery } from '../url_helpers'; interface Props extends EuiLinkAnchorProps { @@ -21,7 +23,7 @@ interface Props extends EuiLinkAnchorProps { export type APMLinkExtendProps = Omit; -export const PERSISTENT_APM_PARAMS = [ +export const PERSISTENT_APM_PARAMS: Array = [ 'kuery', 'rangeFrom', 'rangeTo', @@ -30,6 +32,21 @@ export const PERSISTENT_APM_PARAMS = [ 'environment', ]; +/** + * Hook to get a link for a path with persisted filters + */ +export function useAPMHref( + path: string, + persistentFilters: Array = PERSISTENT_APM_PARAMS +) { + const { urlParams } = useUrlParams(); + const { basePath } = useApmPluginContext().core.http; + const { search } = useLocation(); + const query = pickKeys(urlParams as APMQueryParams, ...persistentFilters); + + return getAPMHref({ basePath, path, query, search }); +} + /** * Get an APM link for a path. */ diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx index 862b1ac649648f..30b91fe2564f16 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx @@ -4,37 +4,38 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { APMLink, APMLinkExtendProps } from './APMLink'; -import { useUrlParams } from '../../../../hooks/useUrlParams'; import { pickKeys } from '../../../../../common/utils/pick_keys'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; import { APMQueryParams } from '../url_helpers'; +import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; + +const persistedFilters: Array = [ + 'host', + 'containerId', + 'podName', + 'serviceVersion', +]; + +export function useErrorOverviewHref(serviceName: string) { + return useAPMHref(`/services/${serviceName}/errors`, persistedFilters); +} interface Props extends APMLinkExtendProps { serviceName: string; query?: APMQueryParams; } -function ErrorOverviewLink({ serviceName, query, ...rest }: Props) { +export function ErrorOverviewLink({ serviceName, query, ...rest }: Props) { const { urlParams } = useUrlParams(); - const persistedFilters = pickKeys( - urlParams, - 'host', - 'containerId', - 'podName', - 'serviceVersion' - ); - return ( ); } - -export { ErrorOverviewLink }; diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx index 35ba5db68d507f..fbae80203f03bb 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx @@ -4,32 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { APMLink, APMLinkExtendProps } from './APMLink'; -import { useUrlParams } from '../../../../hooks/useUrlParams'; import { pickKeys } from '../../../../../common/utils/pick_keys'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; +import { APMQueryParams } from '../url_helpers'; +import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; + +const persistedFilters: Array = [ + 'host', + 'containerId', + 'podName', + 'serviceVersion', +]; + +export function useMetricOverviewHref(serviceName: string) { + return useAPMHref(`/services/${serviceName}/metrics`, persistedFilters); +} interface Props extends APMLinkExtendProps { serviceName: string; } -function MetricOverviewLink({ serviceName, ...rest }: Props) { +export function MetricOverviewLink({ serviceName, ...rest }: Props) { const { urlParams } = useUrlParams(); - const persistedFilters = pickKeys( - urlParams, - 'host', - 'containerId', - 'podName', - 'serviceVersion' - ); - return ( ); } - -export { MetricOverviewLink }; diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx index ff8b1354daeb5e..ae5dc86608a90e 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx @@ -1,26 +1,24 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { APMLink, APMLinkExtendProps } from './APMLink'; +import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; + +function pathFor(serviceName?: string) { + return serviceName ? `/services/${serviceName}/service-map` : '/service-map'; +} + +export function useServiceMapHref(serviceName?: string) { + return useAPMHref(pathFor(serviceName)); +} interface ServiceMapLinkProps extends APMLinkExtendProps { serviceName?: string; } -function ServiceMapLink({ serviceName, ...rest }: ServiceMapLinkProps) { - const path = serviceName - ? `/services/${serviceName}/service-map` - : '/service-map'; +export function ServiceMapLink({ serviceName, ...rest }: ServiceMapLinkProps) { + const path = pathFor(serviceName); return ; } - -export { ServiceMapLink }; diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx index 111c2391cd54fe..0a9553bcbfe6ca 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx @@ -4,32 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { APMLink, APMLinkExtendProps } from './APMLink'; +import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; import { useUrlParams } from '../../../../hooks/useUrlParams'; import { pickKeys } from '../../../../../common/utils/pick_keys'; +import { APMQueryParams } from '../url_helpers'; + +const persistedFilters: Array = [ + 'host', + 'containerId', + 'podName', + 'serviceVersion', +]; + +export function useServiceNodeOverviewHref(serviceName: string) { + return useAPMHref(`/services/${serviceName}/nodes`, persistedFilters); +} interface Props extends APMLinkExtendProps { serviceName: string; } -function ServiceNodeOverviewLink({ serviceName, ...rest }: Props) { +export function ServiceNodeOverviewLink({ serviceName, ...rest }: Props) { const { urlParams } = useUrlParams(); - const persistedFilters = pickKeys( - urlParams, - 'host', - 'containerId', - 'podName', - 'serviceVersion' - ); - return ( ); } - -export { ServiceNodeOverviewLink }; diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx index 8f3ea191fab1aa..6aa362707800fd 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx @@ -10,22 +10,30 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { APMLink, APMLinkExtendProps } from './APMLink'; -import { useUrlParams } from '../../../../hooks/useUrlParams'; import { pickKeys } from '../../../../../common/utils/pick_keys'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; +import { APMQueryParams } from '../url_helpers'; +import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; -function TraceOverviewLink(props: APMLinkExtendProps) { +const persistedFilters: Array = [ + 'transactionResult', + 'host', + 'containerId', + 'podName', +]; + +export function useTraceOverviewHref() { + return useAPMHref('/traces', persistedFilters); +} + +export function TraceOverviewLink(props: APMLinkExtendProps) { const { urlParams } = useUrlParams(); - const persistedFilters = pickKeys( - urlParams, - 'transactionResult', - 'host', - 'containerId', - 'podName' + return ( + ); - - return ; } - -export { TraceOverviewLink }; diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx index adc64f5a2d3dcd..23e795b026d0cf 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx @@ -4,33 +4,35 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { APMLink, APMLinkExtendProps } from './APMLink'; +import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; import { useUrlParams } from '../../../../hooks/useUrlParams'; import { pickKeys } from '../../../../../common/utils/pick_keys'; +import { APMQueryParams } from '../url_helpers'; + +const persistedFilters: Array = [ + 'transactionResult', + 'host', + 'containerId', + 'podName', + 'serviceVersion', +]; + +export function useTransactionOverviewHref(serviceName: string) { + return useAPMHref(`/services/${serviceName}/transactions`, persistedFilters); +} interface Props extends APMLinkExtendProps { serviceName: string; } -function TransactionOverviewLink({ serviceName, ...rest }: Props) { +export function TransactionOverviewLink({ serviceName, ...rest }: Props) { const { urlParams } = useUrlParams(); - const persistedFilters = pickKeys( - urlParams, - 'transactionResult', - 'host', - 'containerId', - 'podName', - 'serviceVersion' - ); - return ( ); } - -export { TransactionOverviewLink }; diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx index e3fa03a4d4f86a..039d9dcb1c0ed9 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_inventory_link.tsx @@ -10,16 +10,21 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { APMLink, APMLinkExtendProps } from './APMLink'; -import { useUrlParams } from '../../../../hooks/useUrlParams'; import { pickKeys } from '../../../../../common/utils/pick_keys'; +import { useUrlParams } from '../../../../hooks/useUrlParams'; +import { APMQueryParams } from '../url_helpers'; +import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; + +const persistedFilters: Array = ['host', 'agentName']; -function ServiceInventoryLink(props: APMLinkExtendProps) { +export function useServiceInventoryHref() { + return useAPMHref('/services', persistedFilters); +} + +export function ServiceInventoryLink(props: APMLinkExtendProps) { const { urlParams } = useUrlParams(); - const persistedFilters = pickKeys(urlParams, 'host', 'agentName'); + const query = pickKeys(urlParams as APMQueryParams, ...persistedFilters); - return ; + return ; } - -export { ServiceInventoryLink }; diff --git a/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx index 5d7859e7362c72..78e409bb4558ca 100644 --- a/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/service_overview_link.tsx @@ -9,12 +9,16 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { APMLink, APMLinkExtendProps } from './APMLink'; +import { APMLink, APMLinkExtendProps, useAPMHref } from './APMLink'; interface ServiceOverviewLinkProps extends APMLinkExtendProps { serviceName: string; } +export function useServiceOverviewHref(serviceName: string) { + return useAPMHref(`/services/${serviceName}/overview`); +} + export function ServiceOverviewLink({ serviceName, ...rest diff --git a/x-pack/plugins/apm/public/components/shared/main_tabs.tsx b/x-pack/plugins/apm/public/components/shared/main_tabs.tsx new file mode 100644 index 00000000000000..a165cabec857c0 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/main_tabs.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiTabs } from '@elastic/eui'; +import React, { ReactNode } from 'react'; +import styled from 'styled-components'; + +// Since our `EuiTab` components have `APMLink`s inside of them and not just +// `href`s, we need to override the color of the links inside or they will all +// be the primary color. +const StyledTabs = styled(EuiTabs)` + padding: ${({ theme }) => `${theme.eui.gutterTypes.gutterMedium}`}; + border-bottom: ${({ theme }) => theme.eui.euiBorderThin}; +`; + +export function MainTabs({ children }: { children: ReactNode }) { + return {children}; +} diff --git a/x-pack/plugins/apm/public/components/shared/search_bar.tsx b/x-pack/plugins/apm/public/components/shared/search_bar.tsx new file mode 100644 index 00000000000000..686df42cf3b8a7 --- /dev/null +++ b/x-pack/plugins/apm/public/components/shared/search_bar.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; +import { DatePicker } from './DatePicker'; +import { KueryBar } from './KueryBar'; + +const SearchBarFlexGroup = styled(EuiFlexGroup)` + margin: ${({ theme }) => + `${theme.eui.euiSizeM} ${theme.eui.euiSizeM} -${theme.eui.gutterTypes.gutterMedium} ${theme.eui.euiSizeM}`}; +`; + +export function SearchBar() { + return ( + + + + + + + + + ); +} diff --git a/x-pack/plugins/observability/public/components/app/header/index.tsx b/x-pack/plugins/observability/public/components/app/header/index.tsx index 5884fef36b46d7..b195bb52e7ed29 100644 --- a/x-pack/plugins/observability/public/components/app/header/index.tsx +++ b/x-pack/plugins/observability/public/components/app/header/index.tsx @@ -65,7 +65,7 @@ export function Header({ color, datePicker = null, restrictWidth }: Props) { - +

{i18n.translate('xpack.observability.home.title', { defaultMessage: 'Observability', diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index afe05ddc1edb94..678dfc361f8940 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4930,7 +4930,6 @@ "xpack.apm.jvmsTable.noJvmsLabel": "JVM が見つかりませんでした", "xpack.apm.jvmsTable.nonHeapMemoryColumnLabel": "非ヒープ領域の平均", "xpack.apm.jvmsTable.threadCountColumnLabel": "最大スレッド数", - "xpack.apm.kueryBar.disabledPlaceholder": "ここでは検索は利用できません", "xpack.apm.license.betaBadge": "ベータ", "xpack.apm.license.betaTooltipMessage": "現在、この機能はベータです。不具合を見つけた場合やご意見がある場合、サポートに問い合わせるか、またはディスカッションフォーラムにご報告ください。", "xpack.apm.license.button": "トライアルを開始", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 31fc796f06ccfc..4117e060dd9a61 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4932,7 +4932,6 @@ "xpack.apm.jvmsTable.noJvmsLabel": "未找到任何 JVM", "xpack.apm.jvmsTable.nonHeapMemoryColumnLabel": "非堆内存平均值", "xpack.apm.jvmsTable.threadCountColumnLabel": "线程计数最大值", - "xpack.apm.kueryBar.disabledPlaceholder": "搜索在此不可用", "xpack.apm.kueryBar.placeholder": "搜索{event, select,\n transaction {事务}\n metric {指标}\n error {错误}\n other {事务、错误和指标}\n }(例如 {queryExample})", "xpack.apm.license.betaBadge": "公测版", "xpack.apm.license.betaTooltipMessage": "此功能当前为公测版。如果遇到任何错误或有任何反馈,请报告问题或访问我们的论坛。",