From a35b18af53cd7e1d43781bcceaad5952683e8dbd Mon Sep 17 00:00:00 2001 From: Yaroslav Kuznietsov Date: Wed, 22 Sep 2021 09:26:33 +0300 Subject: [PATCH] [Canvas] XY. Step 1. Remove all specific logic, related to aggType. (#111876) * Removed logic, related to BUCKET_TYPES. * removed aggType connected code. * Added `id` to `xy_dimension` to avoid direct comparison with aggId * Removed all checks of seriesParams at chart. * removed aggId and aggType from chart * removed all aggId/aggTypes from tests/mocks. * moved `get_agg_id.ts` util to the vislib * clarified the code, related to isSimpleField logic. * added a comment. * Fixed error at percentile agg. * Fixed render_all_series failure of tests. * Added tests for new behavior. * changed the way of handling `enableHistogramMode`. * updated snapshots. * Fixed shard_delay test. * removed all todos from xy. * removed latest todos. * Changed the type of tickFormatter. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/__snapshots__/to_ast.test.ts.snap | 2 +- .../__snapshots__/to_ast_pie.test.ts.snap | 2 +- .../helpers/point_series/_add_to_siri.ts | 4 +- .../helpers/point_series}/get_agg_id.ts | 0 src/plugins/vis_types/xy/kibana.json | 2 +- .../public/__snapshots__/to_ast.test.ts.snap | 3 + .../components/detailed_tooltip.mock.ts | 24 ++--- .../xy/public/components/detailed_tooltip.tsx | 14 +-- .../vis_types/xy/public/config/get_aspects.ts | 8 +- .../vis_types/xy/public/config/get_axis.ts | 22 ++--- .../vis_types/xy/public/config/get_config.ts | 39 ++++---- .../vis_types/xy/public/config/index.ts | 1 - .../public/expression_functions/xy_vis_fn.ts | 22 ++++- src/plugins/vis_types/xy/public/index.ts | 1 - src/plugins/vis_types/xy/public/plugin.ts | 6 +- .../xy/public/sample_vis.test.mocks.ts | 3 - src/plugins/vis_types/xy/public/services.ts | 3 +- src/plugins/vis_types/xy/public/to_ast.ts | 23 ++++- .../vis_types/xy/public/types/config.ts | 3 +- .../vis_types/xy/public/types/param.ts | 4 +- .../xy/public/utils/accessors.test.ts | 90 ++++++++++--------- .../vis_types/xy/public/utils/accessors.tsx | 63 +++++++++---- .../xy/public/utils/get_all_series.test.ts | 9 +- .../public/utils/get_series_name_fn.test.ts | 15 ++-- .../utils/render_all_series.test.mocks.ts | 39 +++----- .../xy/public/utils/render_all_series.tsx | 32 +++---- .../vis_types/xy/public/vis_component.tsx | 18 ++-- .../expression_functions/xy_dimension.ts | 11 ++- .../visualizations/public/vis_schemas.ts | 5 +- 29 files changed, 255 insertions(+), 213 deletions(-) rename src/plugins/vis_types/{xy/public/config => vislib/public/vislib/helpers/point_series}/get_agg_id.ts (100%) diff --git a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap index 233940d97d38a..a4778929ce26b 100644 --- a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast.test.ts.snap @@ -8,7 +8,7 @@ Object { "area", ], "visConfig": Array [ - "{\\"type\\":\\"area\\",\\"grid\\":{\\"categoryLines\\":false,\\"style\\":{\\"color\\":\\"#eee\\"}},\\"categoryAxes\\":[{\\"id\\":\\"CategoryAxis-1\\",\\"type\\":\\"category\\",\\"position\\":\\"bottom\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\"},\\"labels\\":{\\"show\\":true,\\"truncate\\":100},\\"title\\":{}}],\\"valueAxes\\":[{\\"id\\":\\"ValueAxis-1\\",\\"name\\":\\"LeftAxis-1\\",\\"type\\":\\"value\\",\\"position\\":\\"left\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\",\\"mode\\":\\"normal\\"},\\"labels\\":{\\"show\\":true,\\"rotate\\":0,\\"filter\\":false,\\"truncate\\":100},\\"title\\":{\\"text\\":\\"Sum of total_quantity\\"}}],\\"seriesParams\\":[{\\"show\\":\\"true\\",\\"type\\":\\"area\\",\\"mode\\":\\"stacked\\",\\"data\\":{\\"label\\":\\"Sum of total_quantity\\",\\"id\\":\\"1\\"},\\"drawLinesBetweenPoints\\":true,\\"showCircles\\":true,\\"circlesRadius\\":5,\\"interpolate\\":\\"linear\\",\\"valueAxis\\":\\"ValueAxis-1\\"}],\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"top\\",\\"times\\":[],\\"addTimeMarker\\":false,\\"truncateLegend\\":true,\\"maxLegendLines\\":1,\\"thresholdLine\\":{\\"show\\":false,\\"value\\":10,\\"width\\":1,\\"style\\":\\"full\\",\\"color\\":\\"#E7664C\\"},\\"palette\\":{\\"name\\":\\"default\\"},\\"labels\\":{},\\"dimensions\\":{\\"x\\":{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"HH:mm:ss.SSS\\"}},\\"params\\":{}},\\"y\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\",\\"params\\":{\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}],\\"series\\":[{\\"accessor\\":2,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}", + "{\\"type\\":\\"area\\",\\"grid\\":{\\"categoryLines\\":false,\\"style\\":{\\"color\\":\\"#eee\\"}},\\"categoryAxes\\":[{\\"id\\":\\"CategoryAxis-1\\",\\"type\\":\\"category\\",\\"position\\":\\"bottom\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\"},\\"labels\\":{\\"show\\":true,\\"truncate\\":100},\\"title\\":{}}],\\"valueAxes\\":[{\\"id\\":\\"ValueAxis-1\\",\\"name\\":\\"LeftAxis-1\\",\\"type\\":\\"value\\",\\"position\\":\\"left\\",\\"show\\":true,\\"style\\":{},\\"scale\\":{\\"type\\":\\"linear\\",\\"mode\\":\\"normal\\"},\\"labels\\":{\\"show\\":true,\\"rotate\\":0,\\"filter\\":false,\\"truncate\\":100},\\"title\\":{\\"text\\":\\"Sum of total_quantity\\"}}],\\"seriesParams\\":[{\\"show\\":\\"true\\",\\"type\\":\\"area\\",\\"mode\\":\\"stacked\\",\\"data\\":{\\"label\\":\\"Sum of total_quantity\\",\\"id\\":\\"1\\"},\\"drawLinesBetweenPoints\\":true,\\"showCircles\\":true,\\"circlesRadius\\":5,\\"interpolate\\":\\"linear\\",\\"valueAxis\\":\\"ValueAxis-1\\"}],\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"top\\",\\"times\\":[],\\"addTimeMarker\\":false,\\"truncateLegend\\":true,\\"maxLegendLines\\":1,\\"thresholdLine\\":{\\"show\\":false,\\"value\\":10,\\"width\\":1,\\"style\\":\\"full\\",\\"color\\":\\"#E7664C\\"},\\"palette\\":{\\"name\\":\\"default\\"},\\"labels\\":{},\\"dimensions\\":{\\"x\\":{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"date\\",\\"params\\":{\\"pattern\\":\\"HH:mm:ss.SSS\\"}},\\"params\\":{},\\"id\\":\\"2\\"},\\"y\\":[{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\",\\"params\\":{\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{},\\"id\\":\\"1\\"}],\\"series\\":[{\\"accessor\\":2,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{},\\"id\\":\\"3\\"}]}}", ], }, "getArgument": [Function], diff --git a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap index b8dc4b31747c4..72f74418ff6fb 100644 --- a/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap +++ b/src/plugins/vis_types/vislib/public/__snapshots__/to_ast_pie.test.ts.snap @@ -5,7 +5,7 @@ Object { "addArgument": [Function], "arguments": Object { "visConfig": Array [ - "{\\"type\\":\\"pie\\",\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"right\\",\\"isDonut\\":true,\\"labels\\":{\\"show\\":true,\\"values\\":true,\\"last_level\\":true,\\"truncate\\":100},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\"},\\"params\\":{}},\\"buckets\\":[{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{}}]}}", + "{\\"type\\":\\"pie\\",\\"addTooltip\\":true,\\"addLegend\\":true,\\"legendPosition\\":\\"right\\",\\"isDonut\\":true,\\"labels\\":{\\"show\\":true,\\"values\\":true,\\"last_level\\":true,\\"truncate\\":100},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"format\\":{\\"id\\":\\"number\\"},\\"params\\":{},\\"id\\":\\"1\\"},\\"buckets\\":[{\\"accessor\\":1,\\"format\\":{\\"id\\":\\"terms\\",\\"params\\":{\\"id\\":\\"string\\",\\"otherBucketLabel\\":\\"Other\\",\\"missingBucketLabel\\":\\"Missing\\",\\"parsedUrl\\":{\\"origin\\":\\"http://localhost:5801\\",\\"pathname\\":\\"/app/visualize\\",\\"basePath\\":\\"\\"}}},\\"params\\":{},\\"id\\":\\"2\\"}]}}", ], }, "getArgument": [Function], diff --git a/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_add_to_siri.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_add_to_siri.ts index c334a83f3dd6a..1e74ef34705b0 100644 --- a/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_add_to_siri.ts +++ b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/_add_to_siri.ts @@ -5,10 +5,8 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - -import { getAggId } from '../../../../../xy/public'; import type { Dimension } from '../../../../../xy/public'; - +import { getAggId } from './get_agg_id'; import { Point } from './_get_point'; export interface Serie { diff --git a/src/plugins/vis_types/xy/public/config/get_agg_id.ts b/src/plugins/vis_types/vislib/public/vislib/helpers/point_series/get_agg_id.ts similarity index 100% rename from src/plugins/vis_types/xy/public/config/get_agg_id.ts rename to src/plugins/vis_types/vislib/public/vislib/helpers/point_series/get_agg_id.ts diff --git a/src/plugins/vis_types/xy/kibana.json b/src/plugins/vis_types/xy/kibana.json index 1666a346e3482..75109630d9f2c 100644 --- a/src/plugins/vis_types/xy/kibana.json +++ b/src/plugins/vis_types/xy/kibana.json @@ -3,7 +3,7 @@ "version": "kibana", "ui": true, "server": false, - "requiredPlugins": ["charts", "data", "expressions", "visualizations", "usageCollection"], + "requiredPlugins": ["charts", "data", "expressions", "visualizations", "usageCollection", "fieldFormats"], "requiredBundles": ["kibanaUtils", "visDefaultEditor"], "extraPublicDirs": ["common/index"], "owner": { diff --git a/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap index 7ee1b0d2b2053..0282c6204e41d 100644 --- a/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap +++ b/src/plugins/vis_types/xy/public/__snapshots__/to_ast.test.ts.snap @@ -21,6 +21,9 @@ Object { "chartType": Array [ "area", ], + "enableHistogramMode": Array [ + false, + ], "gridCategoryLines": Array [ false, ], diff --git a/src/plugins/vis_types/xy/public/components/detailed_tooltip.mock.ts b/src/plugins/vis_types/xy/public/components/detailed_tooltip.mock.ts index bc6a1f292dad3..574d2c8f27ee1 100644 --- a/src/plugins/vis_types/xy/public/components/detailed_tooltip.mock.ts +++ b/src/plugins/vis_types/xy/public/components/detailed_tooltip.mock.ts @@ -17,8 +17,7 @@ export const aspects = { pattern: 'YYYY-MM-DD HH:mm', }, }, - aggType: 'date_histogram', - aggId: '3', + id: '3', params: { date: true, intervalESUnit: 'h', @@ -35,8 +34,7 @@ export const aspects = { format: { id: 'number', }, - aggType: 'count', - aggId: '1', + id: '1', params: {}, }, ], @@ -53,8 +51,7 @@ export const aspectsWithSplitColumn = { pattern: 'YYYY-MM-DD HH:mm', }, }, - aggType: 'date_histogram', - aggId: '3', + id: '3', params: { date: true, intervalESUnit: 'h', @@ -71,8 +68,7 @@ export const aspectsWithSplitColumn = { format: { id: 'number', }, - aggType: 'count', - aggId: '1', + id: '1', params: {}, }, ], @@ -88,8 +84,7 @@ export const aspectsWithSplitColumn = { missingBucketLabel: 'Missing', }, }, - aggType: 'terms', - aggId: '4', + id: '4', params: {}, }, }; @@ -105,8 +100,7 @@ export const aspectsWithSplitRow = { pattern: 'YYYY-MM-DD HH:mm', }, }, - aggType: 'date_histogram', - aggId: '3', + id: '3', params: { date: true, intervalESUnit: 'h', @@ -123,8 +117,7 @@ export const aspectsWithSplitRow = { format: { id: 'number', }, - aggType: 'count', - aggId: '1', + id: '1', params: {}, }, ], @@ -140,8 +133,7 @@ export const aspectsWithSplitRow = { missingBucketLabel: 'Missing', }, }, - aggType: 'terms', - aggId: '4', + id: '4', params: {}, }, }; diff --git a/src/plugins/vis_types/xy/public/components/detailed_tooltip.tsx b/src/plugins/vis_types/xy/public/components/detailed_tooltip.tsx index a7eb7a909615b..c143f8fe42198 100644 --- a/src/plugins/vis_types/xy/public/components/detailed_tooltip.tsx +++ b/src/plugins/vis_types/xy/public/components/detailed_tooltip.tsx @@ -17,9 +17,13 @@ import { } from '@elastic/charts'; import { Aspects } from '../types'; +import { + applyFormatterIfSimpleField, + applyFormatter, + COMPLEX_SPLIT_ACCESSOR, +} from '../utils/accessors'; import './_detailed_tooltip.scss'; -import { COMPLEX_SPLIT_ACCESSOR, isRangeAggType } from '../utils/accessors'; interface TooltipData { label: string; @@ -34,10 +38,10 @@ export const getTooltipData = ( const data: TooltipData[] = []; if (header) { - const xFormatter = isRangeAggType(aspects.x.aggType) ? null : aspects.x.formatter; data.push({ label: aspects.x.title, - value: xFormatter ? xFormatter(header.value) : `${header.value}`, + // already formatted while executing accessor on such a complex field type as `*_range` + value: `${applyFormatterIfSimpleField(aspects.x, header.value)}`, }); } @@ -47,14 +51,14 @@ export const getTooltipData = ( if (yAccessor) { data.push({ label: yAccessor.title, - value: yAccessor.formatter ? yAccessor.formatter(value.value) : `${value.value}`, + value: `${applyFormatter(yAccessor, value.value)}`, }); } if (aspects.z && !isNil(value.markValue)) { data.push({ label: aspects.z.title, - value: aspects.z.formatter ? aspects.z.formatter(value.markValue) : `${value.markValue}`, + value: `${applyFormatterIfSimpleField(aspects.z, value.markValue)}`, }); } diff --git a/src/plugins/vis_types/xy/public/config/get_aspects.ts b/src/plugins/vis_types/xy/public/config/get_aspects.ts index 666a913e48402..e5ad75d5bcccf 100644 --- a/src/plugins/vis_types/xy/public/config/get_aspects.ts +++ b/src/plugins/vis_types/xy/public/config/get_aspects.ts @@ -14,13 +14,10 @@ import { DatatableColumn } from '../../../../expressions/public'; import { Aspect, Dimension, Aspects, Dimensions } from '../types'; import { getFormatService } from '../services'; -import { getAggId } from './get_agg_id'; export function getEmptyAspect(): Aspect { return { accessor: null, - aggId: null, - aggType: null, title: i18n.translate('visTypeXy.aggResponse.allDocsTitle', { defaultMessage: 'All docs', }), @@ -76,14 +73,13 @@ function getAspectsFromDimension( const getAspect = ( { id: accessor, name: title }: DatatableColumn, - { accessor: column, format, params, aggType }: Dimension + { accessor: column, format, params, id }: Dimension ): Aspect => ({ accessor, column, title, format, - aggType, - aggId: getAggId(accessor), formatter: (value: any) => getFormatService().deserialize(format).convert(value), params, + id, }); diff --git a/src/plugins/vis_types/xy/public/config/get_axis.ts b/src/plugins/vis_types/xy/public/config/get_axis.ts index b5cc96830e46a..ac266402c4416 100644 --- a/src/plugins/vis_types/xy/public/config/get_axis.ts +++ b/src/plugins/vis_types/xy/public/config/get_axis.ts @@ -6,13 +6,9 @@ * Side Public License, v 1. */ -import { identity, isNil } from 'lodash'; - +import { isNil } from 'lodash'; import { AxisSpec, TickFormatter, YDomainRange, ScaleType as ECScaleType } from '@elastic/charts'; - import { LabelRotation } from '../../../../charts/public'; -import { BUCKET_TYPES } from '../../../../data/public'; - import { Aspect, CategoryAxis, @@ -27,13 +23,15 @@ import { YScaleType, SeriesParam, } from '../types'; +import { isSimpleField } from '../utils/accessors'; export function getAxis( { type, title: axisTitle, labels, scale: axisScale, ...axis }: CategoryAxis, { categoryLines, valueAxis }: Grid, - { params, format, formatter, title: fallbackTitle = '', aggType }: Aspect, + { params, format, formatter, title: fallbackTitle = '', accessor }: Aspect, seriesParams: SeriesParam[], - isDateHistogram = false + isDateHistogram = false, + shouldApplyFormatter = false ): AxisConfig { const isCategoryAxis = type === AxisType.Category; // Hide unassigned axis, not supported in elastic charts @@ -50,9 +48,10 @@ export function getAxis( : { show: valueAxis === axis.id, }; - // Date range formatter applied on xAccessor - const tickFormatter = - aggType === BUCKET_TYPES.DATE_RANGE || aggType === BUCKET_TYPES.RANGE ? identity : formatter; + + const tickFormatter: TickFormatter = (v) => + isSimpleField(format) || shouldApplyFormatter ? formatter?.(v) ?? v : v; + const ticks: TickOptions = { formatter: tickFormatter, labelFormatter: getLabelFormatter(labels.truncate, tickFormatter), @@ -61,6 +60,7 @@ export function getAxis( showOverlappingLabels: !labels.filter, showDuplicates: !labels.filter, }; + const scale = getScale(axisScale, params, format, isCategoryAxis); const title = axisTitle.text || fallbackTitle; const fallbackRotation = @@ -76,7 +76,7 @@ export function getAxis( scale, style: getAxisStyle(ticks, title, fallbackRotation), domain: getAxisDomain(scale, isCategoryAxis), - integersOnly: aggType === 'count', + integersOnly: params?.integersOnly ?? false, }; } diff --git a/src/plugins/vis_types/xy/public/config/get_config.ts b/src/plugins/vis_types/xy/public/config/get_config.ts index 13c9a6c275f8e..16f267d500a30 100644 --- a/src/plugins/vis_types/xy/public/config/get_config.ts +++ b/src/plugins/vis_types/xy/public/config/get_config.ts @@ -9,12 +9,12 @@ import { ScaleContinuousType } from '@elastic/charts'; import { Datatable } from '../../../../expressions/public'; -import { BUCKET_TYPES } from '../../../../data/public'; -import { DateHistogramParams } from '../../../../visualizations/public'; - +import { KBN_FIELD_TYPES } from '../../../../data/public'; import { Aspect, AxisConfig, + AxisMode, + ChartMode, SeriesParam, VisConfig, VisParams, @@ -42,23 +42,28 @@ export function getConfig(table: Datatable, params: VisParams): VisConfig { fillOpacity, } = params; const aspects = getAspects(table.columns, params.dimensions); + const yAxes = params.valueAxes.map((a) => + // shouldApplyFormatter = true, because no formatter was applied to this axis values before + // and will be not applied in the future + getAxis(a, params.grid, aspects.y[0], params.seriesParams, false, true) + ); + + const enableHistogramMode = + (params.enableHistogramMode ?? false) && + shouldEnableHistogramMode(params.seriesParams, aspects.y, yAxes); + + const timeChartFieldTypes: string[] = [KBN_FIELD_TYPES.DATE, KBN_FIELD_TYPES.DATE_RANGE]; + const isTimeChart = timeChartFieldTypes.includes(aspects.x.format?.id ?? ''); + const xAxis = getAxis( params.categoryAxes[0], params.grid, aspects.x, params.seriesParams, - params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM + aspects.x.format?.id === KBN_FIELD_TYPES.DATE ); + const tooltip = getTooltip(aspects, params); - const yAxes = params.valueAxes.map((a) => - // uses first y aspect in array for formatting axis - getAxis(a, params.grid, aspects.y[0], params.seriesParams) - ); - const enableHistogramMode = - (params.dimensions.x?.aggType === BUCKET_TYPES.DATE_HISTOGRAM || - params.dimensions.x?.aggType === BUCKET_TYPES.HISTOGRAM) && - shouldEnableHistogramMode(params.seriesParams, aspects.y, yAxes); - const isTimeChart = (aspects.x.params as DateHistogramParams).date ?? false; return { // NOTE: downscale ratio to match current vislib implementation @@ -94,11 +99,7 @@ const shouldEnableHistogramMode = ( yAspects: Aspect[], yAxes: Array> ): boolean => { - const bars = seriesParams.filter(({ type, data: { id: paramId } }) => { - return ( - type === ChartType.Histogram && yAspects.find(({ aggId }) => aggId === paramId) !== undefined - ); - }); + const bars = seriesParams.filter(({ type }) => type === ChartType.Histogram); const groupIds = [ ...bars.reduce>((acc, { valueAxis: groupId, mode }) => { @@ -114,6 +115,6 @@ const shouldEnableHistogramMode = ( return bars.every(({ valueAxis: groupId, mode }) => { const yAxisScale = yAxes.find(({ groupId: axisGroupId }) => axisGroupId === groupId)?.scale; - return mode === 'stacked' || yAxisScale?.mode === 'percentage'; + return mode === ChartMode.Stacked || yAxisScale?.mode === AxisMode.Percentage; }); }; diff --git a/src/plugins/vis_types/xy/public/config/index.ts b/src/plugins/vis_types/xy/public/config/index.ts index b00d6aea3d356..348be38924df5 100644 --- a/src/plugins/vis_types/xy/public/config/index.ts +++ b/src/plugins/vis_types/xy/public/config/index.ts @@ -7,4 +7,3 @@ */ export { getConfig } from './get_config'; -export { getAggId } from './get_agg_id'; diff --git a/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts b/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts index ccad0c520f8ea..608d0d6e42f17 100644 --- a/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts +++ b/src/plugins/vis_types/xy/public/expression_functions/xy_vis_fn.ts @@ -16,6 +16,7 @@ import type { import { prepareLogTable, Dimension } from '../../../../visualizations/public'; import type { ChartType } from '../../common'; import type { VisParams, XYVisConfig } from '../types'; +import { isValidSeriesForDimension } from '../utils/accessors'; export const visName = 'xy_vis'; export interface RenderValue { @@ -153,6 +154,12 @@ export const visTypeXyVisFn = (): VisTypeXyExpressionFunctionDefinition => ({ 'Flag to indicate old vislib visualizations. Used for backwards compatibility including colors', }), }, + enableHistogramMode: { + types: ['boolean'], + help: i18n.translate('visTypeXy.function.args.enableHistogramMode.help', { + defaultMessage: 'Flag to enable histogram mode', + }), + }, detailedTooltip: { types: ['boolean'], help: i18n.translate('visTypeXy.function.args.detailedTooltip.help', { @@ -255,10 +262,16 @@ export const visTypeXyVisFn = (): VisTypeXyExpressionFunctionDefinition => ({ categoryLines: args.gridCategoryLines, valueAxis: args.gridValueAxis, }, - seriesParams: args.seriesParams.map((seriesParam) => ({ - ...seriesParam, - type: seriesParam.seriesParamType, - })), + seriesParams: args.seriesParams.map((seriesParam) => { + const matchedSeries = args.yDimension.filter(({ id, accessor }) => + isValidSeriesForDimension(seriesParam.data.id)({ id, accessor }) + ); + return { + ...seriesParam, + show: matchedSeries.length > 0, + type: seriesParam.seriesParamType, + }; + }), radiusRatio: args.radiusRatio, times: args.times, isVislibVis: args.isVislibVis, @@ -269,6 +282,7 @@ export const visTypeXyVisFn = (): VisTypeXyExpressionFunctionDefinition => ({ }, fillOpacity: args.fillOpacity, fittingFunction: args.fittingFunction, + enableHistogramMode: args.enableHistogramMode, dimensions: { x: args.xDimension, y: args.yDimension, diff --git a/src/plugins/vis_types/xy/public/index.ts b/src/plugins/vis_types/xy/public/index.ts index 1ee96fab35253..8d83a54cf2761 100644 --- a/src/plugins/vis_types/xy/public/index.ts +++ b/src/plugins/vis_types/xy/public/index.ts @@ -30,7 +30,6 @@ export type { ValidationVisOptionsProps } from './editor/components/common/valid export { TruncateLabelsOption } from './editor/components/common/truncate_labels'; export { getPositions } from './editor/positions'; export { getScaleTypes } from './editor/scale_types'; -export { getAggId } from './config/get_agg_id'; // Export common types export * from '../common'; diff --git a/src/plugins/vis_types/xy/public/plugin.ts b/src/plugins/vis_types/xy/public/plugin.ts index c79ead242e35b..86b3f92e15155 100644 --- a/src/plugins/vis_types/xy/public/plugin.ts +++ b/src/plugins/vis_types/xy/public/plugin.ts @@ -7,6 +7,7 @@ */ import { CoreSetup, CoreStart, Plugin } from '../../../../core/public'; +import { FieldFormatsStart } from '../../../field_formats/public'; import { Plugin as ExpressionsPublicPlugin } from '../../../expressions/public'; import { VisualizationsSetup, VisualizationsStart } from '../../../visualizations/public'; import { ChartsPluginSetup, ChartsPluginStart } from '../../../charts/public'; @@ -47,6 +48,7 @@ export interface VisTypeXyPluginStartDependencies { visualizations: VisualizationsStart; data: DataPublicPluginStart; charts: ChartsPluginStart; + fieldFormats: FieldFormatsStart; } type VisTypeXyCoreSetup = CoreSetup; @@ -86,8 +88,8 @@ export class VisTypeXyPlugin return {}; } - public start(core: CoreStart, { data, charts }: VisTypeXyPluginStartDependencies) { - setFormatService(data.fieldFormats); + public start(core: CoreStart, { data, charts, fieldFormats }: VisTypeXyPluginStartDependencies) { + setFormatService(fieldFormats); setDataActions(data.actions); setDocLinks(core.docLinks); setActiveCursor(charts.activeCursor); diff --git a/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts b/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts index 7fff29edfab51..307da89b0a7e6 100644 --- a/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts +++ b/src/plugins/vis_types/xy/public/sample_vis.test.mocks.ts @@ -289,7 +289,6 @@ export const sampleAreaVis = { }, }, label: 'order_date per 12 hours', - aggType: 'date_histogram', }, y: [ { @@ -306,7 +305,6 @@ export const sampleAreaVis = { }, params: {}, label: 'Sum of total_quantity', - aggType: 'sum', }, ], series: [ @@ -322,7 +320,6 @@ export const sampleAreaVis = { }, params: {}, label: 'category.keyword: Descending', - aggType: 'terms', }, ], }, diff --git a/src/plugins/vis_types/xy/public/services.ts b/src/plugins/vis_types/xy/public/services.ts index d8c8689eee298..636f20d94b02f 100644 --- a/src/plugins/vis_types/xy/public/services.ts +++ b/src/plugins/vis_types/xy/public/services.ts @@ -11,6 +11,7 @@ import { CoreSetup, DocLinksStart } from '../../../../core/public'; import { createGetterSetter } from '../../../kibana_utils/public'; import { DataPublicPluginStart } from '../../../data/public'; import { ChartsPluginSetup, ChartsPluginStart } from '../../../charts/public'; +import { FieldFormatsStart } from '../../../field_formats/public'; export const [getUISettings, setUISettings] = createGetterSetter('xy core.uiSettings'); @@ -19,7 +20,7 @@ export const [getDataActions, setDataActions] = createGetterSetter('xy data.actions'); export const [getFormatService, setFormatService] = - createGetterSetter('xy data.fieldFormats'); + createGetterSetter('xy data.fieldFormats'); export const [getThemeService, setThemeService] = createGetterSetter('xy charts.theme'); diff --git a/src/plugins/vis_types/xy/public/to_ast.ts b/src/plugins/vis_types/xy/public/to_ast.ts index 5fc130a08ed27..16d9cd18b1738 100644 --- a/src/plugins/vis_types/xy/public/to_ast.ts +++ b/src/plugins/vis_types/xy/public/to_ast.ts @@ -28,6 +28,7 @@ import { ValueAxis, Scale, TimeMarker, + AxisMode, } from './types'; import { visName, VisTypeXyExpressionFunctionDefinition } from './expression_functions/xy_vis_fn'; import { XyVisType } from '../common'; @@ -123,9 +124,9 @@ const prepareVisDimension = (data: Dimension) => { const prepareXYDimension = (data: Dimension) => { const xyDimension = buildExpressionFunction('xydimension', { params: JSON.stringify(data.params), - aggType: data.aggType, label: data.label, visDimension: prepareVisDimension(data), + id: data.id, }); return buildExpression([xyDimension]); @@ -145,8 +146,12 @@ export const toExpressionAst: VisToExpressionAst = async (vis, params const responseAggs = vis.data.aggs?.getResponseAggs().filter(({ enabled }) => enabled) ?? []; - if (dimensions.x) { - const xAgg = responseAggs[dimensions.x.accessor] as any; + const xAgg = dimensions.x ? (responseAggs[dimensions.x?.accessor] as any) : null; + const enableHistogramMode = [BUCKET_TYPES.HISTOGRAM, BUCKET_TYPES.DATE_HISTOGRAM].includes( + xAgg?.type?.name + ); + + if (dimensions.x && xAgg) { if (xAgg.type.name === BUCKET_TYPES.DATE_HISTOGRAM) { (dimensions.x.params as DateHistogramParams).date = true; const { esUnit, esValue } = xAgg.buckets.getInterval(); @@ -184,10 +189,19 @@ export const toExpressionAst: VisToExpressionAst = async (vis, params const usedValueAxis = (vis.params.valueAxes || []).find( (valueAxis: any) => valueAxis.id === seriesParam.valueAxis ); - if (usedValueAxis?.scale.mode === 'percentage') { + if (usedValueAxis?.scale.mode === AxisMode.Percentage) { yDimension.format = { id: 'percent' }; } } + // if aggType is 'Count', need to display only integers at y-axis + // prevent from displaying floats on small charts with small step + if (yDimension.aggType === 'count') { + if (!yDimension.params) { + yDimension.params = {}; + } + + yDimension.params.integersOnly = true; + } }); const visTypeXy = buildExpressionFunction(visName, { @@ -221,6 +235,7 @@ export const toExpressionAst: VisToExpressionAst = async (vis, params seriesDimension: dimensions.series?.map(prepareXYDimension), splitRowDimension: dimensions.splitRow?.map(prepareXYDimension), splitColumnDimension: dimensions.splitColumn?.map(prepareXYDimension), + enableHistogramMode, }); const ast = buildExpression([getEsaggsFn(vis), visTypeXy]); diff --git a/src/plugins/vis_types/xy/public/types/config.ts b/src/plugins/vis_types/xy/public/types/config.ts index e52b47366bc85..6120ef08dfbc4 100644 --- a/src/plugins/vis_types/xy/public/types/config.ts +++ b/src/plugins/vis_types/xy/public/types/config.ts @@ -29,13 +29,12 @@ export interface Column { export interface Aspect { accessor: Column['id']; - aggType: string | null; - aggId: string | null; column?: Dimension['accessor']; title: Column['name']; format?: Dimension['format']; formatter?: TickFormatter; params: Dimension['params']; + id?: Dimension['id']; } export interface Aspects { diff --git a/src/plugins/vis_types/xy/public/types/param.ts b/src/plugins/vis_types/xy/public/types/param.ts index 81eeca55108ca..06464afc2aec9 100644 --- a/src/plugins/vis_types/xy/public/types/param.ts +++ b/src/plugins/vis_types/xy/public/types/param.ts @@ -103,7 +103,7 @@ export interface TimeMarker { export type Dimension = Omit & { params: DateHistogramParams | HistogramParams | FakeParams | {}; -}; +} & { params: { integersOnly?: boolean } }; export interface Dimensions { x: Dimension | null; @@ -145,6 +145,7 @@ export interface VisParams { palette: PaletteOutput; fillOpacity?: number; fittingFunction?: Exclude; + enableHistogramMode?: boolean; } export interface XYVisConfig { @@ -167,6 +168,7 @@ export interface XYVisConfig { thresholdLine: ExpressionValueThresholdLine; radiusRatio: number; times: ExpressionValueTimeMarker[]; // For compatibility with vislib + enableHistogramMode?: boolean; /** * flag to indicate old vislib visualizations * used for backwards compatibility including colors diff --git a/src/plugins/vis_types/xy/public/utils/accessors.test.ts b/src/plugins/vis_types/xy/public/utils/accessors.test.ts index 61d175fa8ff7d..309dd8508f174 100644 --- a/src/plugins/vis_types/xy/public/utils/accessors.test.ts +++ b/src/plugins/vis_types/xy/public/utils/accessors.test.ts @@ -7,95 +7,101 @@ */ import { COMPLEX_SPLIT_ACCESSOR, getComplexAccessor } from './accessors'; -import { BUCKET_TYPES } from '../../../../data/common'; import { AccessorFn, Datum } from '@elastic/charts'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; describe('XY chart datum accessors', () => { + const formatter = (val: Datum) => JSON.stringify(val); const aspectBase = { accessor: 'col-0-2', - formatter: (value: Datum) => value, - aggId: '', + formatter, + id: '', title: '', params: {}, }; - it('should return complex accessor for IP range aggregation', () => { + const shouldNotApplyFormatterForNotComplexField = (type: string) => { const aspect = { - aggType: BUCKET_TYPES.IP_RANGE, ...aspectBase, + format: { id: type }, + }; + const accessor = getComplexAccessor(COMPLEX_SPLIT_ACCESSOR)(aspect); + const val = 'data'; + const datum = { 'col-0-2': val }; + expect(accessor?.(datum)).toBe(val); + }; + + it('should format IP range aggregation', () => { + const aspect = { + ...aspectBase, + format: { id: 'range' }, }; const accessor = getComplexAccessor(COMPLEX_SPLIT_ACCESSOR)(aspect); const datum = { 'col-0-2': { type: 'range', from: '0.0.0.0', to: '127.255.255.255' }, }; - expect(typeof accessor).toBe('function'); - expect((accessor as AccessorFn)(datum)).toStrictEqual({ - type: 'range', - from: '0.0.0.0', - to: '127.255.255.255', - }); + expect((accessor as AccessorFn)(datum)).toStrictEqual( + formatter({ + type: 'range', + from: '0.0.0.0', + to: '127.255.255.255', + }) + ); }); - it('should return complex accessor for date range aggregation', () => { + it('should format date range aggregation', () => { const aspect = { - aggType: BUCKET_TYPES.DATE_RANGE, ...aspectBase, + format: { id: 'date_range' }, }; const accessor = getComplexAccessor(COMPLEX_SPLIT_ACCESSOR)(aspect); const datum = { 'col-0-2': { from: '1613941200000', to: '1614685113537' }, }; - expect(typeof accessor).toBe('function'); - expect((accessor as AccessorFn)(datum)).toStrictEqual({ - from: '1613941200000', - to: '1614685113537', - }); + expect((accessor as AccessorFn)(datum)).toStrictEqual( + formatter({ + from: '1613941200000', + to: '1614685113537', + }) + ); }); - it('should return complex accessor when isComplex option set to true', () => { - const aspect = { - aggType: BUCKET_TYPES.TERMS, - ...aspectBase, - }; - const accessor = getComplexAccessor(COMPLEX_SPLIT_ACCESSOR, true)(aspect); + it(`should not apply formatter for not complex field: ${KBN_FIELD_TYPES.STRING}`, () => + shouldNotApplyFormatterForNotComplexField(KBN_FIELD_TYPES.STRING)); - expect(typeof accessor).toBe('function'); - expect((accessor as AccessorFn)({ 'col-0-2': 'some value' })).toBe('some value'); - }); + it(`should not apply formatter for not complex field: ${KBN_FIELD_TYPES.NUMBER}`, () => + shouldNotApplyFormatterForNotComplexField(KBN_FIELD_TYPES.NUMBER)); - it('should return simple string accessor for not range (date histogram) aggregation', () => { - const aspect = { - aggType: BUCKET_TYPES.DATE_HISTOGRAM, - ...aspectBase, - }; - const accessor = getComplexAccessor(COMPLEX_SPLIT_ACCESSOR)(aspect); + it(`should not apply formatter for not complex field: ${KBN_FIELD_TYPES.DATE}`, () => + shouldNotApplyFormatterForNotComplexField(KBN_FIELD_TYPES.DATE)); - expect(typeof accessor).toBe('string'); - expect(accessor).toBe('col-0-2'); - }); + it(`should not apply formatter for not complex field: ${KBN_FIELD_TYPES.BOOLEAN}`, () => + shouldNotApplyFormatterForNotComplexField(KBN_FIELD_TYPES.BOOLEAN)); - it('should return simple string accessor when aspect has no formatter', () => { + it('should return simple string when aspect has no formatter', () => { const aspect = { - aggType: BUCKET_TYPES.RANGE, ...aspectBase, formatter: undefined, }; const accessor = getComplexAccessor(COMPLEX_SPLIT_ACCESSOR)(aspect); - expect(typeof accessor).toBe('string'); - expect(accessor).toBe('col-0-2'); + const val = 'data'; + const datum = { 'col-0-2': val }; + + expect(accessor?.(datum)).toBe(val); }); it('should return undefined when aspect has no accessor', () => { const aspect = { - aggType: BUCKET_TYPES.RANGE, ...aspectBase, accessor: null, }; + const datum = { 'col-0-2': 'data' }; + const accessor = getComplexAccessor(COMPLEX_SPLIT_ACCESSOR)(aspect); - expect(accessor).toBeUndefined(); + expect(accessor?.(datum)).toBeUndefined(); }); }); diff --git a/src/plugins/vis_types/xy/public/utils/accessors.tsx b/src/plugins/vis_types/xy/public/utils/accessors.tsx index 9566f819ba145..89e29e9f91d1c 100644 --- a/src/plugins/vis_types/xy/public/utils/accessors.tsx +++ b/src/plugins/vis_types/xy/public/utils/accessors.tsx @@ -7,7 +7,8 @@ */ import { AccessorFn, Accessor } from '@elastic/charts'; -import { BUCKET_TYPES } from '../../../../data/public'; +import { DatatableColumn } from '../../../../expressions'; +import { KBN_FIELD_TYPES } from '../../../../data/public'; import { FakeParams } from '../../../../visualizations/public'; import { Aspect } from '../types'; @@ -28,8 +29,25 @@ const getFieldName = (fieldName: string, index?: number) => { return `${fieldName}${indexStr}`; }; -export const isRangeAggType = (type: string | null) => - type === BUCKET_TYPES.DATE_RANGE || type === BUCKET_TYPES.RANGE || type === BUCKET_TYPES.IP_RANGE; +export const isSimpleField = (format: Aspect['format']) => { + const simpleFormats: string[] = [ + KBN_FIELD_TYPES.STRING, + KBN_FIELD_TYPES.NUMBER, + KBN_FIELD_TYPES.DATE, + KBN_FIELD_TYPES.BOOLEAN, + ]; + return simpleFormats.includes(format?.id ?? ''); +}; + +export const applyFormatter = (aspect: Aspect, value: unknown, shouldApply: boolean = true) => + shouldApply ? aspect.formatter?.(value) ?? value : value; + +export const applyFormatterIfSimpleField = (aspect: Aspect, value: unknown) => + applyFormatter(aspect, value, isSimpleField(aspect.format)); + +// complex field is a field, which has some specific structure, as `range`, for example, not pure value +export const applyFormatterIfComplexField = (aspect: Aspect, value: unknown) => + applyFormatter(aspect, value, !isSimpleField(aspect.format)); /** * Returns accessor function for complex accessor types @@ -37,27 +55,26 @@ export const isRangeAggType = (type: string | null) => * @param isComplex - forces to be functional/complex accessor */ export const getComplexAccessor = - (fieldName: string, isComplex: boolean = false) => - (aspect: Aspect, index?: number): Accessor | AccessorFn | undefined => { - if (!aspect.accessor || aspect.aggType === SHARD_DELAY) { + (fieldName: string) => + (aspect: Aspect, index?: number): AccessorFn | undefined => { + // SHARD_DELAY is used only for dev purpose and need to handle separately. + if (aspect.accessor === null || aspect.accessor === undefined || aspect.title === SHARD_DELAY) { return; } - - if (!((isComplex || isRangeAggType(aspect.aggType)) && aspect.formatter)) { - return aspect.accessor; - } - - const formatter = aspect.formatter; const accessor = aspect.accessor; + const fn: AccessorFn = (d) => { const v = d[accessor]; if (v === undefined) { return; } - const f = formatter(v); - return f; + // Because of the specific logic of chart, it cannot compare complex values to display, + // thats why it is necessary to apply formatters before its comparison while rendering. + // What about simple values, formatting them at this step is breaking the logic of intervals (xDomain). + // If the value will be formatted on this step, it will be rendered without any respect to the passed bounds + // and the chart will render not all the range, but only the part of range, which contains data. + return applyFormatterIfComplexField(aspect, v); }; - fn.fieldName = getFieldName(fieldName, index); return fn; @@ -77,3 +94,19 @@ export const getSplitSeriesAccessorFnMap = ( return m; }; + +// For percentile aggregation id is comming in the form `%d.%d`, where first `%d` is `id` and the second - `percents` +export const isPercentileIdEqualToSeriesId = (columnId: number | string, seriesColumnId: string) => + columnId.toString().split('.')[0] === seriesColumnId; + +export const isValidSeriesForDimension = + (seriesColumnId: string) => + ({ + id, + accessor, + }: { + id?: string | number; + accessor?: string | number | DatatableColumn | null; + }) => + (id === seriesColumnId || isPercentileIdEqualToSeriesId(id ?? '', seriesColumnId)) && + accessor !== null; diff --git a/src/plugins/vis_types/xy/public/utils/get_all_series.test.ts b/src/plugins/vis_types/xy/public/utils/get_all_series.test.ts index 6c6b78dfd73f7..0d8a04f674be4 100644 --- a/src/plugins/vis_types/xy/public/utils/get_all_series.test.ts +++ b/src/plugins/vis_types/xy/public/utils/get_all_series.test.ts @@ -78,8 +78,7 @@ const yAspects = [ format: { id: 'number', }, - aggType: 'count', - aggId: '1', + id: '1', params: {}, }, ]; @@ -92,8 +91,7 @@ const myltipleYAspects = [ format: { id: 'number', }, - aggType: 'count', - aggId: '1', + id: '1', params: {}, }, { @@ -106,8 +104,7 @@ const myltipleYAspects = [ pattern: '$0,0.[00]', }, }, - aggType: 'avg', - aggId: '4', + id: '4', params: {}, }, ]; diff --git a/src/plugins/vis_types/xy/public/utils/get_series_name_fn.test.ts b/src/plugins/vis_types/xy/public/utils/get_series_name_fn.test.ts index 88be9ab160896..405b1f2708055 100644 --- a/src/plugins/vis_types/xy/public/utils/get_series_name_fn.test.ts +++ b/src/plugins/vis_types/xy/public/utils/get_series_name_fn.test.ts @@ -23,8 +23,7 @@ const aspects = { missingBucketLabel: 'Missing', }, }, - aggType: 'terms', - aggId: '3', + id: '3', params: {}, }, ], @@ -38,8 +37,7 @@ const aspects = { pattern: 'YYYY-MM-DD', }, }, - aggType: 'date_histogram', - aggId: '2', + id: '2', params: { date: true, intervalESUnit: 'd', @@ -56,8 +54,7 @@ const aspects = { format: { id: 'number', }, - aggType: 'count', - aggId: '1', + id: '1', params: {}, }, ], @@ -112,8 +109,7 @@ describe('getSeriesNameFn', () => { format: { id: 'number', }, - aggType: 'count', - aggId: '1', + id: '1', params: {}, }, ], @@ -133,8 +129,7 @@ describe('getSeriesNameFn', () => { format: { id: 'number', }, - aggType: 'count', - aggId: '1', + id: '1', params: {}, }, ], diff --git a/src/plugins/vis_types/xy/public/utils/render_all_series.test.mocks.ts b/src/plugins/vis_types/xy/public/utils/render_all_series.test.mocks.ts index 5fe1b03dd8b93..d3898dbf82b0d 100644 --- a/src/plugins/vis_types/xy/public/utils/render_all_series.test.mocks.ts +++ b/src/plugins/vis_types/xy/public/utils/render_all_series.test.mocks.ts @@ -31,8 +31,7 @@ export const getVisConfig = (): VisConfig => { pattern: 'HH:mm', }, }, - aggType: 'date_histogram', - aggId: '2', + id: '2', params: { date: true, intervalESUnit: 'm', @@ -49,8 +48,7 @@ export const getVisConfig = (): VisConfig => { format: { id: 'number', }, - aggType: 'avg', - aggId: '3', + id: '3', params: {}, }, ], @@ -154,8 +152,7 @@ export const getVisConfigMutipleYaxis = (): VisConfig => { pattern: 'HH:mm', }, }, - aggType: 'date_histogram', - aggId: '2', + id: '2', params: { date: true, intervalESUnit: 'm', @@ -172,8 +169,7 @@ export const getVisConfigMutipleYaxis = (): VisConfig => { format: { id: 'number', }, - aggType: 'avg', - aggId: '3', + id: '3', params: {}, }, { @@ -183,8 +179,7 @@ export const getVisConfigMutipleYaxis = (): VisConfig => { format: { id: 'number', }, - aggType: 'avg', - aggId: '33', + id: '33', params: {}, }, ], @@ -288,8 +283,7 @@ export const getVisConfigPercentiles = (): VisConfig => { pattern: 'HH:mm', }, }, - aggType: 'date_histogram', - aggId: '2', + id: '2', params: { date: true, intervalESUnit: 'm', @@ -306,8 +300,7 @@ export const getVisConfigPercentiles = (): VisConfig => { format: { id: 'number', }, - aggType: 'percentiles', - aggId: '3.1', + id: '3.1', params: {}, }, { @@ -317,8 +310,7 @@ export const getVisConfigPercentiles = (): VisConfig => { format: { id: 'number', }, - aggType: 'percentiles', - aggId: '3.5', + id: '3.5', params: {}, }, { @@ -328,8 +320,7 @@ export const getVisConfigPercentiles = (): VisConfig => { format: { id: 'number', }, - aggType: 'percentiles', - aggId: '3.25', + id: '3.25', params: {}, }, { @@ -339,8 +330,7 @@ export const getVisConfigPercentiles = (): VisConfig => { format: { id: 'number', }, - aggType: 'percentiles', - aggId: '3.50', + id: '3.50', params: {}, }, { @@ -350,8 +340,7 @@ export const getVisConfigPercentiles = (): VisConfig => { format: { id: 'number', }, - aggType: 'percentiles', - aggId: '3.75', + id: '3.75', params: {}, }, { @@ -361,8 +350,7 @@ export const getVisConfigPercentiles = (): VisConfig => { format: { id: 'number', }, - aggType: 'percentiles', - aggId: '3.95', + id: '3.95', params: {}, }, { @@ -372,8 +360,7 @@ export const getVisConfigPercentiles = (): VisConfig => { format: { id: 'number', }, - aggType: 'percentiles', - aggId: '3.99', + id: '3.99', params: {}, }, ], diff --git a/src/plugins/vis_types/xy/public/utils/render_all_series.tsx b/src/plugins/vis_types/xy/public/utils/render_all_series.tsx index f8ca1d059ae4f..535500746fb8d 100644 --- a/src/plugins/vis_types/xy/public/utils/render_all_series.tsx +++ b/src/plugins/vis_types/xy/public/utils/render_all_series.tsx @@ -7,7 +7,6 @@ */ import React from 'react'; - import { AreaSeries, CurveType, @@ -20,18 +19,16 @@ import { ColorVariant, LabelOverflowConstraint, } from '@elastic/charts'; - import { DatatableRow } from '../../../../expressions/public'; -import { METRIC_TYPES } from '../../../../data/public'; - import { ChartType } from '../../common'; -import { SeriesParam, VisConfig } from '../types'; +import { AxisMode, ChartMode, InterpolationMode, SeriesParam, VisConfig } from '../types'; +import { isValidSeriesForDimension } from './accessors'; /** * Matches vislib curve to elastic charts * @param type curve type */ -const getCurveType = (type?: 'linear' | 'cardinal' | 'step-after'): CurveType => { +const getCurveType = (type?: InterpolationMode): CurveType => { switch (type) { case 'cardinal': return CurveType.CURVE_MONOTONE_X; @@ -82,26 +79,20 @@ export const renderAllSeries = ( interpolate, type, }) => { - const yAspects = aspects.y.filter(({ aggId, aggType, accessor }) => { - if ( - aggType === METRIC_TYPES.PERCENTILES || - aggType === METRIC_TYPES.PERCENTILE_RANKS || - aggType === METRIC_TYPES.STD_DEV - ) { - return aggId?.includes(paramId) && accessor !== null; - } else { - return aggId === paramId && accessor !== null; - } - }); + const yAspects = aspects.y.filter(({ id, accessor }) => + isValidSeriesForDimension(paramId)({ id, accessor }) + ); + if (!show || !yAspects.length) { return null; } - const yAccessors = yAspects.map((aspect) => aspect.accessor) as string[]; + + const yAccessors: string[] = yAspects.map((aspect) => aspect.accessor ?? ''); const id = `${type}-${yAccessors[0]}`; const yAxisScale = yAxes.find(({ groupId: axisGroupId }) => axisGroupId === groupId)?.scale; - const isStacked = mode === 'stacked' || yAxisScale?.mode === 'percentage'; - const stackMode = yAxisScale?.mode === 'normal' ? undefined : yAxisScale?.mode; + const isStacked = mode === ChartMode.Stacked || yAxisScale?.mode === AxisMode.Percentage; + const stackMode = yAxisScale?.mode === AxisMode.Normal ? undefined : yAxisScale?.mode; // needed to seperate stacked and non-stacked bars into unique pseudo groups const pseudoGroupId = isStacked ? `__pseudo_stacked_group-${groupId}__` : groupId; // set domain of stacked groups to use actual groupId not pseudo groupdId @@ -144,7 +135,6 @@ export const renderAllSeries = ( case ChartType.Area: case ChartType.Line: const markSizeAccessor = showCircles ? aspects.z?.accessor ?? undefined : undefined; - return ( { const [showLegend, setShowLegend] = useState(() => { - // TODO: Check when this bwc can safely be removed + // @TODO: Check when this bwc can safely be removed const bwcLegendStateDefault = props.visParams.addLegend == null ? true : props.visParams.addLegend; return props.uiState?.get('vis.legendOpen', bwcLegendStateDefault) as boolean; @@ -210,21 +210,22 @@ const VisComponent = (props: VisComponentProps) => { const config = getConfig(visData, visParams); const timeZone = getTimeZone(); + const xDomain = config.xAxis.scale.type === ScaleType.Ordinal ? undefined : getXDomain(config.aspects.x.params); - const hasBars = visParams.seriesParams.some( - ({ type, data: { id: paramId } }) => - type === ChartType.Histogram && - config.aspects.y.find(({ aggId }) => aggId === paramId) !== undefined - ); + + const hasBars = visParams.seriesParams.some(({ type }) => type === ChartType.Histogram); + const adjustedXDomain = config.xAxis.scale.type === ScaleType.Ordinal ? undefined : getAdjustedDomain(visData.rows, config.aspects.x, timeZone, xDomain, hasBars); + const legendPosition = useMemo( () => config.legend.position ?? Position.Right, [config.legend.position] ); + const isDarkMode = getThemeService().useDarkMode(); const getSeriesName = getSeriesNameFn(config.aspects, config.aspects.y.length > 1); @@ -280,6 +281,7 @@ const VisComponent = (props: VisComponentProps) => { palettesRegistry, ] ); + const xAccessor = getXAccessor(config.aspects.x); const splitSeriesAccessors = useMemo( @@ -290,10 +292,10 @@ const VisComponent = (props: VisComponentProps) => { [config.aspects.series] ); const splitChartColumnAccessor = config.aspects.splitColumn - ? getComplexAccessor(COMPLEX_SPLIT_ACCESSOR, true)(config.aspects.splitColumn) + ? getComplexAccessor(COMPLEX_SPLIT_ACCESSOR)(config.aspects.splitColumn) : undefined; const splitChartRowAccessor = config.aspects.splitRow - ? getComplexAccessor(COMPLEX_SPLIT_ACCESSOR, true)(config.aspects.splitRow) + ? getComplexAccessor(COMPLEX_SPLIT_ACCESSOR)(config.aspects.splitRow) : undefined; const renderSeries = useMemo( diff --git a/src/plugins/visualizations/common/expression_functions/xy_dimension.ts b/src/plugins/visualizations/common/expression_functions/xy_dimension.ts index 82538fea8605a..480e1e524a0fb 100644 --- a/src/plugins/visualizations/common/expression_functions/xy_dimension.ts +++ b/src/plugins/visualizations/common/expression_functions/xy_dimension.ts @@ -42,6 +42,7 @@ interface Arguments { params: string; aggType: string; label: string; + id?: number | string; } export type ExpressionValueXYDimension = ExpressionValueBoxed< @@ -52,7 +53,8 @@ export type ExpressionValueXYDimension = ExpressionValueBoxed< params: DateHistogramParams | HistogramParams | FakeParams | {}; accessor: number | DatatableColumn; format: SerializedFieldFormat; - } + id?: number | string; + } & { params: { integersOnly?: boolean } } >; export const xyDimension = (): ExpressionFunctionDefinition< @@ -93,6 +95,12 @@ export const xyDimension = (): ExpressionFunctionDefinition< defaultMessage: 'Params', }), }, + id: { + types: ['string', 'number'], + help: i18n.translate('visualizations.function.xyDimension.id.help', { + defaultMessage: 'ID', + }), + }, }, fn: (context, args) => { return { @@ -102,6 +110,7 @@ export const xyDimension = (): ExpressionFunctionDefinition< params: JSON.parse(args.params!), accessor: args.visDimension.accessor as number, format: args.visDimension.format, + id: args.id, }; }, }); diff --git a/src/plugins/visualizations/public/vis_schemas.ts b/src/plugins/visualizations/public/vis_schemas.ts index 115e13ece45ff..4d72902148534 100644 --- a/src/plugins/visualizations/public/vis_schemas.ts +++ b/src/plugins/visualizations/public/vis_schemas.ts @@ -23,7 +23,8 @@ export interface SchemaConfig { label: string; format: SerializedFieldFormat; params: SchemaConfigParams; - aggType: string; + aggType?: string; + id?: number | string; } export interface Schemas { @@ -85,7 +86,7 @@ export const getVisSchemas = ( format: formatAgg.toSerializedFieldFormat(), params, label, - aggType: agg.type.name, + id: agg.id, }; };