[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>
This commit is contained in:
parent
ae84bb2033
commit
ff80d90626
|
@ -179,5 +179,89 @@ describe('metric_expression', () => {
|
|||
</VisualizationContainer>
|
||||
`);
|
||||
});
|
||||
|
||||
test('it renders an EmptyPlaceholder when no tables is passed as data', () => {
|
||||
const { data, noAttributesArgs } = sampleArgs();
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
<MetricChart
|
||||
data={{ ...data, tables: {} }}
|
||||
args={noAttributesArgs}
|
||||
formatFactory={(x) => x as IFieldFormat}
|
||||
/>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<EmptyPlaceholder
|
||||
icon={[Function]}
|
||||
/>
|
||||
`);
|
||||
});
|
||||
|
||||
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(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={noAttributesArgs}
|
||||
formatFactory={(x) => x as IFieldFormat}
|
||||
/>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<EmptyPlaceholder
|
||||
icon={[Function]}
|
||||
/>
|
||||
`);
|
||||
});
|
||||
|
||||
test('it renders 0 value', () => {
|
||||
const { data, noAttributesArgs } = sampleArgs();
|
||||
|
||||
data.tables.l1.rows[0].a = 0;
|
||||
|
||||
expect(
|
||||
shallow(
|
||||
<MetricChart
|
||||
data={data}
|
||||
args={noAttributesArgs}
|
||||
formatFactory={(x) => x as IFieldFormat}
|
||||
/>
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
<VisualizationContainer
|
||||
className="lnsMetricExpression__container"
|
||||
reportDescription=""
|
||||
reportTitle=""
|
||||
>
|
||||
<AutoScale>
|
||||
<div
|
||||
data-test-subj="lns_metric_value"
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "60pt",
|
||||
"fontWeight": 600,
|
||||
}
|
||||
}
|
||||
>
|
||||
0
|
||||
</div>
|
||||
<div
|
||||
data-test-subj="lns_metric_title"
|
||||
style={
|
||||
Object {
|
||||
"fontSize": "24pt",
|
||||
}
|
||||
}
|
||||
>
|
||||
My fanci metric chart
|
||||
</div>
|
||||
</AutoScale>
|
||||
</VisualizationContainer>
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,16 +120,25 @@ export function MetricChart({
|
|||
);
|
||||
}
|
||||
|
||||
if (firstTable) {
|
||||
if (!firstTable) {
|
||||
return <EmptyPlaceholder icon={LensIconChartMetric} />;
|
||||
}
|
||||
|
||||
const column = firstTable.columns[0];
|
||||
const row = firstTable.rows[0];
|
||||
if (row[accessor]) {
|
||||
value =
|
||||
|
||||
// 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 <EmptyPlaceholder icon={LensIconChartMetric} />;
|
||||
}
|
||||
|
||||
const value =
|
||||
column && column.formatHint
|
||||
? formatFactory(column.formatHint).convert(row[accessor])
|
||||
: Number(Number(row[accessor]).toFixed(3)).toString();
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<VisualizationContainer
|
||||
|
|
|
@ -35,6 +35,7 @@ import {
|
|||
import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { chartPluginMock } from '../../../../../src/plugins/charts/public/mocks';
|
||||
import { EmptyPlaceholder } from '../shared_components/empty_placeholder';
|
||||
|
||||
const onClickValue = jest.fn();
|
||||
const onSelectRange = jest.fn();
|
||||
|
@ -721,6 +722,29 @@ describe('xy_expression', () => {
|
|||
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(
|
||||
<XYChart
|
||||
data={data}
|
||||
args={args}
|
||||
formatFactory={getFormatSpy}
|
||||
timeZone="UTC"
|
||||
chartsThemeService={chartsThemeService}
|
||||
histogramBarTarget={50}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
<XYChart
|
||||
data={data}
|
||||
args={{
|
||||
...args,
|
||||
layers: [
|
||||
{
|
||||
...args.layers[0],
|
||||
xAccessor: undefined,
|
||||
splitAccessor: 'e',
|
||||
seriesType: 'bar_stacked',
|
||||
},
|
||||
],
|
||||
}}
|
||||
formatFactory={getFormatSpy}
|
||||
timeZone="UTC"
|
||||
chartsThemeService={chartsThemeService}
|
||||
histogramBarTarget={50}
|
||||
onClickValue={onClickValue}
|
||||
onSelectRange={onSelectRange}
|
||||
/>
|
||||
);
|
||||
|
||||
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(
|
||||
|
|
|
@ -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'))
|
||||
);
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue