From ff80d9062632c8843d4d21e28b1445e7b6e1f340 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Wed, 7 Oct 2020 10:36:56 +0200 Subject: [PATCH] [Lens] Fix for Percentage and Metric suggestions/visualizations on 0 or empty vlaues (#79309) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../metric_visualization/expression.test.tsx | 84 +++++++++++++++++++ .../metric_visualization/expression.tsx | 30 ++++--- .../xy_visualization/expression.test.tsx | 54 ++++++++++++ .../public/xy_visualization/expression.tsx | 9 +- 4 files changed, 165 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx index 77a8ce64b21a..7e80fcc06dff 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx @@ -179,5 +179,89 @@ describe('metric_expression', () => { `); }); + + test('it renders an EmptyPlaceholder when no tables is passed as data', () => { + const { data, noAttributesArgs } = sampleArgs(); + + expect( + shallow( + x as IFieldFormat} + /> + ) + ).toMatchInlineSnapshot(` + + `); + }); + + test('it renders an EmptyPlaceholder when null value is passed as data', () => { + const { data, noAttributesArgs } = sampleArgs(); + + data.tables.l1.rows[0].a = null; + + expect( + shallow( + x as IFieldFormat} + /> + ) + ).toMatchInlineSnapshot(` + + `); + }); + + test('it renders 0 value', () => { + const { data, noAttributesArgs } = sampleArgs(); + + data.tables.l1.rows[0].a = 0; + + expect( + shallow( + x as IFieldFormat} + /> + ) + ).toMatchInlineSnapshot(` + + +
+ 0 +
+
+ My fanci metric chart +
+
+
+ `); + }); }); }); diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.tsx index 29c9cc3e454c..58814f62da60 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.tsx @@ -17,6 +17,8 @@ import { MetricConfig } from './types'; import { FormatFactory, LensMultiTable } from '../types'; import { AutoScale } from './auto_scale'; import { VisualizationContainer } from '../visualization_container'; +import { EmptyPlaceholder } from '../shared_components'; +import { LensIconChartMetric } from '../assets/chart_metric'; export interface MetricChartProps { data: LensMultiTable; @@ -107,7 +109,6 @@ export function MetricChart({ formatFactory, }: MetricChartProps & { formatFactory: FormatFactory }) { const { metricTitle, title, description, accessor, mode } = args; - let value = '-'; const firstTable = Object.values(data.tables)[0]; if (!accessor) { return ( @@ -119,17 +120,26 @@ export function MetricChart({ ); } - if (firstTable) { - const column = firstTable.columns[0]; - const row = firstTable.rows[0]; - if (row[accessor]) { - value = - column && column.formatHint - ? formatFactory(column.formatHint).convert(row[accessor]) - : Number(Number(row[accessor]).toFixed(3)).toString(); - } + if (!firstTable) { + return ; } + const column = firstTable.columns[0]; + const row = firstTable.rows[0]; + + // NOTE: Cardinality and Sum never receives "null" as value, but always 0, even for empty dataset. + // Mind falsy values here as 0! + const shouldShowResults = row[accessor] != null; + + if (!shouldShowResults) { + return ; + } + + const value = + column && column.formatHint + ? formatFactory(column.formatHint).convert(row[accessor]) + : Number(Number(row[accessor]).toFixed(3)).toString(); + return ( { expect(component.find(Settings).prop('rotation')).toEqual(90); }); + test('it renders regular bar empty placeholder for no results', () => { + const { data, args } = sampleArgs(); + + // send empty data to the chart + data.tables.first.rows = []; + + const component = shallow( + + ); + + expect(component.find(BarSeries)).toHaveLength(0); + expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); + }); + test('onBrushEnd returns correct context data for date histogram data', () => { const { args } = sampleArgs(); @@ -957,6 +981,36 @@ describe('xy_expression', () => { expect(component.find(Settings).prop('rotation')).toEqual(90); }); + test('it renders stacked bar empty placeholder for no results', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + + expect(component.find(BarSeries)).toHaveLength(0); + expect(component.find(EmptyPlaceholder).prop('icon')).toBeDefined(); + }); + test('it passes time zone to the series', () => { const { data, args } = sampleArgs(); const component = shallow( diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index a034b8a6e5fb..dad1d31ced71 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -248,12 +248,17 @@ export function XYChart({ const chartTheme = chartsThemeService.useChartsTheme(); const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); - const filteredLayers = layers.filter(({ layerId, xAccessor, accessors }) => { + const filteredLayers = layers.filter(({ layerId, xAccessor, accessors, splitAccessor }) => { return !( !accessors.length || !data.tables[layerId] || data.tables[layerId].rows.length === 0 || - (xAccessor && data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) + (xAccessor && + data.tables[layerId].rows.every((row) => typeof row[xAccessor] === 'undefined')) || + // stacked percentage bars have no xAccessors but splitAccessor with undefined values in them when empty + (!xAccessor && + splitAccessor && + data.tables[layerId].rows.every((row) => typeof row[splitAccessor] === 'undefined')) ); });