Fixes problem with one chart plotted for multiple y axis when migrating from an old SO (#112972) (#114098)
* Fixes problem with one chart plotted for multiple y axis when migrationg from an old SO * Add unit tests * Address PR comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
This commit is contained in:
parent
2284304ecd
commit
5f03c68a64
|
@ -6,7 +6,7 @@
|
||||||
* Side Public License, v 1.
|
* Side Public License, v 1.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
import React, { useState, useEffect, useCallback } from 'react';
|
||||||
import { cloneDeep, get } from 'lodash';
|
import { cloneDeep, get } from 'lodash';
|
||||||
|
|
||||||
import { EuiSpacer } from '@elastic/eui';
|
import { EuiSpacer } from '@elastic/eui';
|
||||||
|
@ -19,7 +19,6 @@ import { SeriesPanel } from './series_panel';
|
||||||
import { CategoryAxisPanel } from './category_axis_panel';
|
import { CategoryAxisPanel } from './category_axis_panel';
|
||||||
import { ValueAxesPanel } from './value_axes_panel';
|
import { ValueAxesPanel } from './value_axes_panel';
|
||||||
import {
|
import {
|
||||||
makeSerie,
|
|
||||||
isAxisHorizontal,
|
isAxisHorizontal,
|
||||||
countNextAxisNumber,
|
countNextAxisNumber,
|
||||||
getUpdatedAxisName,
|
getUpdatedAxisName,
|
||||||
|
@ -27,6 +26,7 @@ import {
|
||||||
mapPosition,
|
mapPosition,
|
||||||
mapPositionOpposingOpposite,
|
mapPositionOpposingOpposite,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
import { getSeriesParams } from '../../../../utils/get_series_params';
|
||||||
|
|
||||||
export type SetParamByIndex = <P extends keyof ValueAxis, O extends keyof SeriesParam>(
|
export type SetParamByIndex = <P extends keyof ValueAxis, O extends keyof SeriesParam>(
|
||||||
axesName: 'valueAxes' | 'seriesParams',
|
axesName: 'valueAxes' | 'seriesParams',
|
||||||
|
@ -273,40 +273,19 @@ function MetricsAxisOptions(props: ValidationVisOptionsProps<VisParams>) {
|
||||||
);
|
);
|
||||||
|
|
||||||
const schemaName = vis.type.schemas.metrics[0].name;
|
const schemaName = vis.type.schemas.metrics[0].name;
|
||||||
const metrics = useMemo(() => {
|
|
||||||
return aggs.bySchemaName(schemaName);
|
|
||||||
}, [schemaName, aggs]);
|
|
||||||
|
|
||||||
const firstValueAxesId = stateParams.valueAxes[0].id;
|
const firstValueAxesId = stateParams.valueAxes[0].id;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updatedSeries = metrics.map((agg) => {
|
const updatedSeries = getSeriesParams(
|
||||||
const params = stateParams.seriesParams.find((param) => param.data.id === agg.id);
|
aggs,
|
||||||
const label = agg.makeLabel();
|
stateParams.seriesParams,
|
||||||
|
schemaName,
|
||||||
|
firstValueAxesId
|
||||||
|
);
|
||||||
|
|
||||||
// update labels for existing params or create new one
|
if (updatedSeries) setValue('seriesParams', updatedSeries);
|
||||||
if (params) {
|
|
||||||
return {
|
|
||||||
...params,
|
|
||||||
data: {
|
|
||||||
...params.data,
|
|
||||||
label,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const series = makeSerie(
|
|
||||||
agg.id,
|
|
||||||
label,
|
|
||||||
firstValueAxesId,
|
|
||||||
stateParams.seriesParams[stateParams.seriesParams.length - 1]
|
|
||||||
);
|
|
||||||
return series;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setValue('seriesParams', updatedSeries);
|
|
||||||
updateAxisTitle(updatedSeries);
|
updateAxisTitle(updatedSeries);
|
||||||
}, [metrics, firstValueAxesId, setValue, stateParams.seriesParams, updateAxisTitle]);
|
}, [firstValueAxesId, setValue, stateParams.seriesParams, updateAxisTitle, aggs, schemaName]);
|
||||||
|
|
||||||
return isTabSelected ? (
|
return isTabSelected ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -10,30 +10,7 @@ import { upperFirst } from 'lodash';
|
||||||
|
|
||||||
import { Position } from '@elastic/charts';
|
import { Position } from '@elastic/charts';
|
||||||
|
|
||||||
import { VisParams, ValueAxis, SeriesParam, ChartMode, InterpolationMode } from '../../../../types';
|
import { VisParams, ValueAxis } from '../../../../types';
|
||||||
import { ChartType } from '../../../../../common';
|
|
||||||
|
|
||||||
export const makeSerie = (
|
|
||||||
id: string,
|
|
||||||
label: string,
|
|
||||||
defaultValueAxis: ValueAxis['id'],
|
|
||||||
lastSerie?: SeriesParam
|
|
||||||
): SeriesParam => {
|
|
||||||
const data = { id, label };
|
|
||||||
const defaultSerie = {
|
|
||||||
show: true,
|
|
||||||
mode: ChartMode.Normal,
|
|
||||||
type: ChartType.Line,
|
|
||||||
drawLinesBetweenPoints: true,
|
|
||||||
showCircles: true,
|
|
||||||
circlesRadius: 3,
|
|
||||||
interpolate: InterpolationMode.Linear,
|
|
||||||
lineWidth: 2,
|
|
||||||
valueAxis: defaultValueAxis,
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
return lastSerie ? { ...lastSerie, data } : defaultSerie;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isAxisHorizontal = (position: Position) =>
|
export const isAxisHorizontal = (position: Position) =>
|
||||||
[Position.Top, Position.Bottom].includes(position as any);
|
[Position.Top, Position.Bottom].includes(position as any);
|
||||||
|
|
|
@ -1780,6 +1780,28 @@ export const sampleAreaVis = {
|
||||||
},
|
},
|
||||||
aggs: {
|
aggs: {
|
||||||
typesRegistry: {},
|
typesRegistry: {},
|
||||||
|
bySchemaName: () => [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
enabled: true,
|
||||||
|
type: 'sum',
|
||||||
|
params: {
|
||||||
|
field: 'total_quantity',
|
||||||
|
},
|
||||||
|
schema: 'metric',
|
||||||
|
makeLabel: () => 'Total quantity',
|
||||||
|
toSerializedFieldFormat: () => ({
|
||||||
|
id: 'number',
|
||||||
|
params: {
|
||||||
|
parsedUrl: {
|
||||||
|
origin: 'http://localhost:5801',
|
||||||
|
pathname: '/app/visualize',
|
||||||
|
basePath: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
getResponseAggs: () => [
|
getResponseAggs: () => [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
|
|
|
@ -33,6 +33,7 @@ import { visName, VisTypeXyExpressionFunctionDefinition } from './expression_fun
|
||||||
import { XyVisType } from '../common';
|
import { XyVisType } from '../common';
|
||||||
import { getEsaggsFn } from './to_ast_esaggs';
|
import { getEsaggsFn } from './to_ast_esaggs';
|
||||||
import { TimeRangeBounds } from '../../../data/common';
|
import { TimeRangeBounds } from '../../../data/common';
|
||||||
|
import { getSeriesParams } from './utils/get_series_params';
|
||||||
|
|
||||||
const prepareLabel = (data: Labels) => {
|
const prepareLabel = (data: Labels) => {
|
||||||
const label = buildExpressionFunction('label', {
|
const label = buildExpressionFunction('label', {
|
||||||
|
@ -145,6 +146,17 @@ export const toExpressionAst: VisToExpressionAst<VisParams> = async (vis, params
|
||||||
|
|
||||||
const responseAggs = vis.data.aggs?.getResponseAggs().filter(({ enabled }) => enabled) ?? [];
|
const responseAggs = vis.data.aggs?.getResponseAggs().filter(({ enabled }) => enabled) ?? [];
|
||||||
|
|
||||||
|
const schemaName = vis.type.schemas?.metrics[0].name;
|
||||||
|
const firstValueAxesId = vis.params.valueAxes[0].id;
|
||||||
|
const updatedSeries = getSeriesParams(
|
||||||
|
vis.data.aggs,
|
||||||
|
vis.params.seriesParams,
|
||||||
|
schemaName,
|
||||||
|
firstValueAxesId
|
||||||
|
);
|
||||||
|
|
||||||
|
const finalSeriesParams = updatedSeries ?? vis.params.seriesParams;
|
||||||
|
|
||||||
if (dimensions.x) {
|
if (dimensions.x) {
|
||||||
const xAgg = responseAggs[dimensions.x.accessor] as any;
|
const xAgg = responseAggs[dimensions.x.accessor] as any;
|
||||||
if (xAgg.type.name === BUCKET_TYPES.DATE_HISTOGRAM) {
|
if (xAgg.type.name === BUCKET_TYPES.DATE_HISTOGRAM) {
|
||||||
|
@ -202,7 +214,7 @@ export const toExpressionAst: VisToExpressionAst<VisParams> = async (vis, params
|
||||||
orderBucketsBySum: vis.params.orderBucketsBySum,
|
orderBucketsBySum: vis.params.orderBucketsBySum,
|
||||||
categoryAxes: vis.params.categoryAxes.map(prepareCategoryAxis),
|
categoryAxes: vis.params.categoryAxes.map(prepareCategoryAxis),
|
||||||
valueAxes: vis.params.valueAxes.map(prepareValueAxis),
|
valueAxes: vis.params.valueAxes.map(prepareValueAxis),
|
||||||
seriesParams: vis.params.seriesParams.map(prepareSeriesParam),
|
seriesParams: finalSeriesParams.map(prepareSeriesParam),
|
||||||
labels: prepareLabel(vis.params.labels),
|
labels: prepareLabel(vis.params.labels),
|
||||||
thresholdLine: prepareThresholdLine(vis.params.thresholdLine),
|
thresholdLine: prepareThresholdLine(vis.params.thresholdLine),
|
||||||
gridCategoryLines: vis.params.grid.categoryLines,
|
gridCategoryLines: vis.params.grid.categoryLines,
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
import type { AggConfigs } from '../../../../data/public';
|
||||||
|
import type { SeriesParam } from '../types';
|
||||||
|
import { getSeriesParams } from './get_series_params';
|
||||||
|
import { sampleAreaVis } from '../sample_vis.test.mocks';
|
||||||
|
|
||||||
|
describe('getSeriesParams', () => {
|
||||||
|
it('returns correct params', () => {
|
||||||
|
const seriesParams = getSeriesParams(
|
||||||
|
sampleAreaVis.data.aggs as unknown as AggConfigs,
|
||||||
|
sampleAreaVis.params.seriesParams as unknown as SeriesParam[],
|
||||||
|
'metric',
|
||||||
|
'ValueAxis-1'
|
||||||
|
);
|
||||||
|
expect(seriesParams).toStrictEqual([
|
||||||
|
{
|
||||||
|
circlesRadius: 5,
|
||||||
|
data: {
|
||||||
|
id: '1',
|
||||||
|
label: 'Total quantity',
|
||||||
|
},
|
||||||
|
drawLinesBetweenPoints: true,
|
||||||
|
interpolate: 'linear',
|
||||||
|
mode: 'stacked',
|
||||||
|
show: 'true',
|
||||||
|
showCircles: true,
|
||||||
|
type: 'area',
|
||||||
|
valueAxis: 'ValueAxis-1',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns default params if no params provided', () => {
|
||||||
|
const seriesParams = getSeriesParams(
|
||||||
|
sampleAreaVis.data.aggs as unknown as AggConfigs,
|
||||||
|
[],
|
||||||
|
'metric',
|
||||||
|
'ValueAxis-1'
|
||||||
|
);
|
||||||
|
expect(seriesParams).toStrictEqual([
|
||||||
|
{
|
||||||
|
circlesRadius: 3,
|
||||||
|
data: {
|
||||||
|
id: '1',
|
||||||
|
label: 'Total quantity',
|
||||||
|
},
|
||||||
|
drawLinesBetweenPoints: true,
|
||||||
|
interpolate: 'linear',
|
||||||
|
lineWidth: 2,
|
||||||
|
mode: 'normal',
|
||||||
|
show: true,
|
||||||
|
showCircles: true,
|
||||||
|
type: 'line',
|
||||||
|
valueAxis: 'ValueAxis-1',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
63
src/plugins/vis_types/xy/public/utils/get_series_params.ts
Normal file
63
src/plugins/vis_types/xy/public/utils/get_series_params.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License
|
||||||
|
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||||
|
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||||
|
* Side Public License, v 1.
|
||||||
|
*/
|
||||||
|
import { ValueAxis, SeriesParam, ChartMode, InterpolationMode } from '../types';
|
||||||
|
import { ChartType } from '../../common';
|
||||||
|
import type { AggConfigs } from '../../../../data/public';
|
||||||
|
|
||||||
|
const makeSerie = (
|
||||||
|
id: string,
|
||||||
|
label: string,
|
||||||
|
defaultValueAxis: ValueAxis['id'],
|
||||||
|
lastSerie?: SeriesParam
|
||||||
|
): SeriesParam => {
|
||||||
|
const data = { id, label };
|
||||||
|
const defaultSerie = {
|
||||||
|
show: true,
|
||||||
|
mode: ChartMode.Normal,
|
||||||
|
type: ChartType.Line,
|
||||||
|
drawLinesBetweenPoints: true,
|
||||||
|
showCircles: true,
|
||||||
|
circlesRadius: 3,
|
||||||
|
interpolate: InterpolationMode.Linear,
|
||||||
|
lineWidth: 2,
|
||||||
|
valueAxis: defaultValueAxis,
|
||||||
|
};
|
||||||
|
return { ...defaultSerie, ...lastSerie, data };
|
||||||
|
};
|
||||||
|
export const getSeriesParams = (
|
||||||
|
aggs: AggConfigs | undefined,
|
||||||
|
seriesParams: SeriesParam[],
|
||||||
|
schemaName: string,
|
||||||
|
firstValueAxesId: string
|
||||||
|
) => {
|
||||||
|
const metrics = aggs?.bySchemaName(schemaName);
|
||||||
|
|
||||||
|
return metrics?.map((agg) => {
|
||||||
|
const params = seriesParams.find((param) => param.data.id === agg.id);
|
||||||
|
const label = agg.makeLabel();
|
||||||
|
|
||||||
|
// update labels for existing params or create new one
|
||||||
|
if (params) {
|
||||||
|
return {
|
||||||
|
...params,
|
||||||
|
data: {
|
||||||
|
...params.data,
|
||||||
|
label,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const series = makeSerie(
|
||||||
|
agg.id,
|
||||||
|
label,
|
||||||
|
firstValueAxesId,
|
||||||
|
seriesParams[seriesParams.length - 1]
|
||||||
|
);
|
||||||
|
return series;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -78,7 +78,7 @@ export const areaVisTypeDefinition = {
|
||||||
truncate: 100,
|
truncate: 100,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: defaultCountLabel,
|
text: '',
|
||||||
},
|
},
|
||||||
style: {},
|
style: {},
|
||||||
},
|
},
|
||||||
|
|
|
@ -80,7 +80,7 @@ export const histogramVisTypeDefinition = {
|
||||||
truncate: 100,
|
truncate: 100,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: defaultCountLabel,
|
text: '',
|
||||||
},
|
},
|
||||||
style: {},
|
style: {},
|
||||||
},
|
},
|
||||||
|
|
|
@ -81,7 +81,7 @@ export const horizontalBarVisTypeDefinition = {
|
||||||
truncate: 100,
|
truncate: 100,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: defaultCountLabel,
|
text: '',
|
||||||
},
|
},
|
||||||
style: {},
|
style: {},
|
||||||
},
|
},
|
||||||
|
|
|
@ -78,7 +78,7 @@ export const lineVisTypeDefinition = {
|
||||||
truncate: 100,
|
truncate: 100,
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
text: defaultCountLabel,
|
text: '',
|
||||||
},
|
},
|
||||||
style: {},
|
style: {},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue