diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/ServiceListMetric.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/ServiceListMetric.tsx index af2cde7f861cc..7a4721407e69b 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/ServiceListMetric.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/ServiceListMetric.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import React from 'react'; import { Coordinate } from '../../../../../typings/timeseries'; import { SparkPlot } from '../../../shared/charts/spark_plot'; @@ -14,18 +15,32 @@ export function ServiceListMetric({ series, valueLabel, comparisonSeries, + hideSeries = false, }: { color: 'euiColorVis1' | 'euiColorVis0' | 'euiColorVis7'; series?: Coordinate[]; comparisonSeries?: Coordinate[]; valueLabel: React.ReactNode; + hideSeries?: boolean; }) { + if (!hideSeries) { + return ( + + ); + } + return ( - + + + + {valueLabel} + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx index 39e65f7eb15d1..f50816809df07 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/index.tsx @@ -17,7 +17,10 @@ import { TypeOf } from '@kbn/typed-react-router-config'; import { orderBy } from 'lodash'; import React, { useMemo } from 'react'; import { ValuesType } from 'utility-types'; -import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; +import { + BreakPoints, + useBreakPoints, +} from '../../../../hooks/use_break_points'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { ServiceHealthStatus } from '../../../../../common/service_health_status'; import { @@ -38,6 +41,7 @@ import { ITableColumn, ManagedTable } from '../../../shared/managed_table'; import { ServiceLink } from '../../../shared/service_link'; import { HealthBadge } from './HealthBadge'; import { ServiceListMetric } from './ServiceListMetric'; +import { TruncateWithTooltip } from '../../../shared/truncate_with_tooltip'; type ServiceListAPIResponse = APIReturnType<'GET /api/apm/services'>; type Items = ServiceListAPIResponse['items']; @@ -49,13 +53,6 @@ function formatString(value?: string | null) { return value || NOT_AVAILABLE_LABEL; } -const ToolTipWrapper = euiStyled.span` - width: 100%; - .apmServiceList__serviceNameTooltip { - width: 100%; - } -`; - const SERVICE_HEALTH_STATUS_ORDER = [ ServiceHealthStatus.unknown, ServiceHealthStatus.healthy, @@ -67,11 +64,16 @@ export function getServiceColumns({ query, showTransactionTypeColumn, comparisonData, + breakPoints, }: { query: TypeOf['query']; showTransactionTypeColumn: boolean; + breakPoints: BreakPoints; comparisonData?: ServicesDetailedStatisticsAPIResponse; }): Array> { + const { isSmall, isLarge, isXl } = breakPoints; + const showWhenSmallOrGreaterThanLarge = isSmall || !isLarge; + const showWhenSmallOrGreaterThanXL = isSmall || !isXl; return [ { field: 'healthStatus', @@ -96,34 +98,38 @@ export function getServiceColumns({ width: '40%', sortable: true, render: (_, { serviceName, agentName }) => ( - - + - - - ), - }, - { - field: 'environments', - name: i18n.translate('xpack.apm.servicesTable.environmentColumnLabel', { - defaultMessage: 'Environment', - }), - width: `${unit * 10}px`, - sortable: true, - render: (_, { environments }) => ( - + } + /> ), }, - ...(showTransactionTypeColumn + ...(showWhenSmallOrGreaterThanLarge + ? [ + { + field: 'environments', + name: i18n.translate( + 'xpack.apm.servicesTable.environmentColumnLabel', + { + defaultMessage: 'Environment', + } + ), + width: `${unit * 10}px`, + sortable: true, + render: (_, { environments }) => ( + + ), + } as ITableColumn, + ] + : []), + ...(showTransactionTypeColumn && showWhenSmallOrGreaterThanXL ? [ { field: 'transactionType', @@ -149,12 +155,13 @@ export function getServiceColumns({ comparisonSeries={ comparisonData?.previousPeriod[serviceName]?.latency } + hideSeries={!showWhenSmallOrGreaterThanLarge} color="euiColorVis1" valueLabel={asMillisecondDuration(latency || 0)} /> ), align: 'left', - width: `${unit * 10}px`, + width: showWhenSmallOrGreaterThanLarge ? `${unit * 10}px` : 'auto', }, { field: 'throughput', @@ -169,12 +176,13 @@ export function getServiceColumns({ comparisonSeries={ comparisonData?.previousPeriod[serviceName]?.throughput } + hideSeries={!showWhenSmallOrGreaterThanLarge} color="euiColorVis0" valueLabel={asTransactionRate(throughput)} /> ), align: 'left', - width: `${unit * 10}px`, + width: showWhenSmallOrGreaterThanLarge ? `${unit * 10}px` : 'auto', }, { field: 'transactionErrorRate', @@ -193,13 +201,14 @@ export function getServiceColumns({ comparisonSeries={ comparisonData?.previousPeriod[serviceName]?.transactionErrorRate } + hideSeries={!showWhenSmallOrGreaterThanLarge} color="euiColorVis7" valueLabel={valueLabel} /> ); }, align: 'left', - width: `${unit * 10}px`, + width: showWhenSmallOrGreaterThanLarge ? `${unit * 10}px` : 'auto', }, ]; } @@ -217,6 +226,7 @@ export function ServiceList({ comparisonData, isLoading, }: Props) { + const breakPoints = useBreakPoints(); const displayHealthStatus = items.some((item) => 'healthStatus' in item); const showTransactionTypeColumn = items.some( @@ -229,8 +239,13 @@ export function ServiceList({ const serviceColumns = useMemo( () => - getServiceColumns({ query, showTransactionTypeColumn, comparisonData }), - [query, showTransactionTypeColumn, comparisonData] + getServiceColumns({ + query, + showTransactionTypeColumn, + comparisonData, + breakPoints, + }), + [query, showTransactionTypeColumn, comparisonData, breakPoints] ); const columns = displayHealthStatus diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/service_list.test.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/service_list.test.tsx index d6cf7bf018d0f..51636e938f8cb 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/service_list/service_list.test.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/service_list/service_list.test.tsx @@ -7,6 +7,7 @@ import React, { ReactNode } from 'react'; import { MemoryRouter } from 'react-router-dom'; +import { BreakPoints } from '../../../../hooks/use_break_points'; import { ServiceHealthStatus } from '../../../../../common/service_health_status'; import { MockApmPluginContextWrapper } from '../../../../context/apm_plugin/mock_apm_plugin_context'; import { mockMoment, renderWithTheme } from '../../../../utils/testHelpers'; @@ -42,7 +43,12 @@ describe('ServiceList', () => { ).not.toThrowError(); }); - it('renders columns correctly', () => { + describe('responsive columns', () => { + const query = { + rangeFrom: 'now-15m', + rangeTo: 'now', + }; + const service: any = { serviceName: 'opbeans-python', agentName: 'python', @@ -59,20 +65,132 @@ describe('ServiceList', () => { timeseries: [], }, environments: ['test'], + transactionType: 'request', }; - const renderedColumns = getServiceColumns({ - query: { - rangeFrom: 'now-15m', - rangeTo: 'now', - }, - showTransactionTypeColumn: false, - }).map((c) => c.render!(service[c.field!], service)); - - expect(renderedColumns[0]).toMatchInlineSnapshot(` - - `); + describe('when small', () => { + it('shows environment, transaction type and sparklines', () => { + const renderedColumns = getServiceColumns({ + query, + showTransactionTypeColumn: true, + breakPoints: { + isSmall: true, + isLarge: true, + isXl: true, + } as BreakPoints, + }).map((c) => + c.render ? c.render!(service[c.field!], service) : service[c.field!] + ); + expect(renderedColumns.length).toEqual(7); + expect(renderedColumns[2]).toMatchInlineSnapshot(` + + `); + expect(renderedColumns[3]).toMatchInlineSnapshot(`"request"`); + expect(renderedColumns[4]).toMatchInlineSnapshot(` + + `); + }); + }); + + describe('when Large', () => { + it('hides environment, transaction type and sparklines', () => { + const renderedColumns = getServiceColumns({ + query, + showTransactionTypeColumn: true, + breakPoints: { + isSmall: false, + isLarge: true, + isXl: true, + } as BreakPoints, + }).map((c) => + c.render ? c.render!(service[c.field!], service) : service[c.field!] + ); + expect(renderedColumns.length).toEqual(5); + expect(renderedColumns[2]).toMatchInlineSnapshot(` + + `); + }); + + describe('when XL', () => { + it('hides transaction type', () => { + const renderedColumns = getServiceColumns({ + query, + showTransactionTypeColumn: true, + breakPoints: { + isSmall: false, + isLarge: false, + isXl: true, + } as BreakPoints, + }).map((c) => + c.render ? c.render!(service[c.field!], service) : service[c.field!] + ); + expect(renderedColumns.length).toEqual(6); + expect(renderedColumns[2]).toMatchInlineSnapshot(` + + `); + expect(renderedColumns[3]).toMatchInlineSnapshot(` + + `); + }); + }); + + describe('when XXL', () => { + it('hides transaction type', () => { + const renderedColumns = getServiceColumns({ + query, + showTransactionTypeColumn: true, + breakPoints: { + isSmall: false, + isLarge: false, + isXl: false, + } as BreakPoints, + }).map((c) => + c.render ? c.render!(service[c.field!], service) : service[c.field!] + ); + expect(renderedColumns.length).toEqual(7); + expect(renderedColumns[2]).toMatchInlineSnapshot(` + + `); + expect(renderedColumns[3]).toMatchInlineSnapshot(`"request"`); + expect(renderedColumns[4]).toMatchInlineSnapshot(` + + `); + }); + }); + }); }); describe('without ML data', () => { diff --git a/x-pack/plugins/apm/public/components/shared/truncate_with_tooltip/index.tsx b/x-pack/plugins/apm/public/components/shared/truncate_with_tooltip/index.tsx index d9268c14aa2ea..3b649d77172ee 100644 --- a/x-pack/plugins/apm/public/components/shared/truncate_with_tooltip/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/truncate_with_tooltip/index.tsx @@ -27,13 +27,14 @@ const ContentWrapper = euiStyled.div` interface Props { text: string; content?: React.ReactNode; + 'data-test-subj'?: string; } export function TruncateWithTooltip(props: Props) { - const { text, content } = props; + const { text, content, ...rest } = props; return ( - + ; + export function getScreenSizes(windowWidth: number) { return { isXSmall: isWithinMaxBreakpoint(windowWidth, 'xs'),