From c7285e1b50262d8e12e08cb91b64bbf824914412 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Fri, 16 Oct 2020 12:57:32 -0400 Subject: [PATCH 1/5] Add empty state for user experience metrics. --- .../RumDashboard/UXMetrics/KeyUXMetrics.tsx | 20 ++++++++++++++----- .../RumDashboard/UXMetrics/translations.ts | 7 +++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx index f30a3ea5fb2dd..793c9619edb3d 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/KeyUXMetrics.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiFlexItem, EuiStat, EuiFlexGroup } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { + DATA_UNDEFINED_LABEL, FCP_LABEL, LONGEST_LONG_TASK, NO_OF_LONG_TASK, @@ -36,6 +37,11 @@ interface Props { loading: boolean; } +function formatTitle(unit: string, value?: number) { + if (typeof value === 'undefined') return DATA_UNDEFINED_LABEL; + return formatToSec(value, unit); +} + export function KeyUXMetrics({ data, loading }: Props) { const uxQuery = useUxQuery(); @@ -62,7 +68,7 @@ export function KeyUXMetrics({ data, loading }: Props) { @@ -70,7 +76,7 @@ export function KeyUXMetrics({ data, loading }: Props) { @@ -78,7 +84,11 @@ export function KeyUXMetrics({ data, loading }: Props) { @@ -86,7 +96,7 @@ export function KeyUXMetrics({ data, loading }: Props) { @@ -94,7 +104,7 @@ export function KeyUXMetrics({ data, loading }: Props) { diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/translations.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/translations.ts index e6d8f881bee57..8f3a71f669ecf 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/translations.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/UXMetrics/translations.ts @@ -6,6 +6,13 @@ import { i18n } from '@kbn/i18n'; +export const DATA_UNDEFINED_LABEL = i18n.translate( + 'xpack.apm.rum.coreVitals.dataUndefined', + { + defaultMessage: 'N/A', + } +); + export const FCP_LABEL = i18n.translate('xpack.apm.rum.coreVitals.fcp', { defaultMessage: 'First contentful paint', }); From 59c61fac7a47df4ad94049b1e6c077abdbd1559f Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Fri, 16 Oct 2020 14:08:56 -0400 Subject: [PATCH 2/5] Add empty state for page load duration metrics. --- .../app/RumDashboard/ClientMetrics/index.tsx | 34 ++++++++++++------- .../app/RumDashboard/translations.ts | 3 ++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx index 310c01291aea4..0b4dcea5d12e0 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/ClientMetrics/index.tsx @@ -22,6 +22,24 @@ const ClFlexGroup = styled(EuiFlexGroup)` } `; +function formatTitle(unit: string, value?: number) { + if (typeof value === 'undefined') return I18LABELS.dataMissing; + return formatToSec(value, unit); +} + +function PageViewsTotalTitle({ pageViews }: { pageViews?: number }) { + if (typeof pageViews === 'undefined') { + return <>{I18LABELS.dataMissing}; + } + return pageViews < 10000 ? ( + <>{numeral(pageViews).format('0,0')} + ) : ( + + <>{numeral(pageViews).format('0 a')} + + ); +} + export function ClientMetrics() { const uxQuery = useUxQuery(); @@ -50,14 +68,12 @@ export function ClientMetrics() { const STAT_STYLE = { width: '240px' }; - const pageViewsTotal = data?.pageViews?.value ?? 0; - return ( @@ -65,7 +81,7 @@ export function ClientMetrics() { @@ -73,15 +89,7 @@ export function ClientMetrics() { - <>{numeral(pageViewsTotal).format('0 a')} - - ) - } + title={} description={I18LABELS.pageViews} isLoading={status !== 'success'} /> diff --git a/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts b/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts index c8db011874a89..a8c4d67305c98 100644 --- a/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts +++ b/x-pack/plugins/apm/public/components/app/RumDashboard/translations.ts @@ -7,6 +7,9 @@ import { i18n } from '@kbn/i18n'; export const I18LABELS = { + dataMissing: i18n.translate('xpack.apm.rum.dashboard.dataMissing', { + defaultMessage: 'N/A', + }), backEnd: i18n.translate('xpack.apm.rum.dashboard.backend', { defaultMessage: 'Backend', }), From 8578a8e3f896d2da8b883cb1550333ded2bd22fa Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Fri, 16 Oct 2020 14:57:15 -0400 Subject: [PATCH 3/5] Add empty state for core web vitals. --- .../shared/core_web_vitals/core_vital_item.tsx | 15 +++++++++++++-- .../components/shared/core_web_vitals/index.tsx | 13 ++++++++++--- .../shared/core_web_vitals/translations.ts | 4 ++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx index e5b8d2c243617..9fbb2c0fba672 100644 --- a/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx +++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx @@ -4,7 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiIconTip, euiPaletteForStatus, EuiSpacer, EuiStat } from '@elastic/eui'; +import { + EuiCard, + EuiFlexGroup, + EuiIconTip, + euiPaletteForStatus, + EuiSpacer, + EuiStat, +} from '@elastic/eui'; import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; import { PaletteLegends } from './palette_legends'; @@ -14,6 +21,7 @@ import { CV_GOOD_LABEL, LESS_LABEL, MORE_LABEL, + NO_DATA, CV_POOR_LABEL, IS_LABEL, TAKES_LABEL, @@ -26,7 +34,7 @@ export interface Thresholds { interface Props { title: string; - value: string; + value?: string; ranks?: number[]; loading: boolean; thresholds: Thresholds; @@ -80,6 +88,9 @@ export function CoreVitalItem({ const biggestValIndex = ranks.indexOf(Math.max(...ranks)); + if (value === undefined && ranks[0] === 100) { + return ; + } return ( <> Date: Mon, 19 Oct 2020 18:26:15 -0400 Subject: [PATCH 4/5] Fix bug injected by these changes. --- .../components/shared/core_web_vitals/core_vital_item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx index 9fbb2c0fba672..0d0a388855ff2 100644 --- a/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx +++ b/x-pack/plugins/observability/public/components/shared/core_web_vitals/core_vital_item.tsx @@ -88,7 +88,7 @@ export function CoreVitalItem({ const biggestValIndex = ranks.indexOf(Math.max(...ranks)); - if (value === undefined && ranks[0] === 100) { + if (value === undefined && ranks[0] === 100 && !loading) { return ; } return ( From cc03dc9ab0095e19427b7761b1fd2ff3e1bf5848 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Mon, 19 Oct 2020 18:32:47 -0400 Subject: [PATCH 5/5] Add a test. --- .../components/app/section/ux/index.test.tsx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx b/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx index cb23e4782a2d5..ef1820eaaeb3e 100644 --- a/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx +++ b/x-pack/plugins/observability/public/components/app/section/ux/index.test.tsx @@ -76,4 +76,27 @@ describe('UXSection', () => { expect(queryAllByText('View in app')).toEqual([]); expect(getByText('elastic-co-frontend')).toBeInTheDocument(); }); + it('shows empty state', () => { + jest.spyOn(fetcherHook, 'useFetcher').mockReturnValue({ + data: undefined, + status: fetcherHook.FETCH_STATUS.SUCCESS, + refetch: jest.fn(), + }); + const { getByText, queryAllByText, getAllByText } = render( + + ); + + expect(getByText('User Experience')).toBeInTheDocument(); + expect(getAllByText('No data is available.')).toHaveLength(3); + expect(queryAllByText('View in app')).toEqual([]); + expect(getByText('elastic-co-frontend')).toBeInTheDocument(); + }); });