[XY] Mark size configuration. (#130361)

* Added tests for the case when markSizeRatio and markSizeAccessor are specified.

* Added markSizeAccessor to extendedDataLayer and xyVis.

* Fixed markSizeRatio default value.

* Added `size` support from `pointseries`.

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Yaroslav Kuznietsov 2022-05-19 16:32:00 +03:00 committed by GitHub
parent d9e6ef3f23
commit dd8bd6fdb3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 438 additions and 42 deletions

View file

@ -10,7 +10,7 @@ import { Position } from '@elastic/charts';
import type { PaletteOutput } from '@kbn/coloring';
import { Datatable, DatatableRow } from '@kbn/expressions-plugin';
import { LayerTypes } from '../constants';
import { DataLayerConfig, XYProps } from '../types';
import { DataLayerConfig, ExtendedDataLayerConfig, XYProps } from '../types';
export const mockPaletteOutput: PaletteOutput = {
type: 'palette',
@ -35,7 +35,7 @@ export const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable =
id: 'c',
name: 'c',
meta: {
type: 'date',
type: 'string',
field: 'order_date',
sourceParams: { type: 'date-histogram', params: { interval: 'auto' } },
params: { id: 'string' },
@ -61,6 +61,21 @@ export const sampleLayer: DataLayerConfig = {
table: createSampleDatatableWithRows([]),
};
export const sampleExtendedLayer: ExtendedDataLayerConfig = {
layerId: 'first',
type: 'extendedDataLayer',
layerType: LayerTypes.DATA,
seriesType: 'line',
xAccessor: 'c',
accessors: ['a', 'b'],
splitAccessor: 'd',
columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}',
xScaleType: 'ordinal',
isHistogram: false,
palette: mockPaletteOutput,
table: createSampleDatatableWithRows([]),
};
export const createArgsWithLayers = (
layers: DataLayerConfig | DataLayerConfig[] = sampleLayer
): XYProps => ({

View file

@ -0,0 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`extendedDataLayerConfig throws the error if markSizeAccessor doesn't have the corresponding column in the table 1`] = `"Provided column name or index is invalid: nonsense"`;
exports[`extendedDataLayerConfig throws the error if markSizeAccessor is provided to the not line/area chart 1`] = `"\`markSizeAccessor\` can't be used. Dots are applied only for line or area charts"`;

View file

@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`layeredXyVis it should throw error if markSizeRatio is lower then 1 or greater then 100 1`] = `"Mark size ratio must be greater or equal to 1 and less or equal to 100"`;
exports[`layeredXyVis it should throw error if markSizeRatio is lower then 1 or greater then 100 2`] = `"Mark size ratio must be greater or equal to 1 and less or equal to 100"`;
exports[`layeredXyVis it should throw error if markSizeRatio is specified if no markSizeAccessor is present 1`] = `"Mark size ratio can be applied only with \`markSizeAccessor\`"`;

View file

@ -1,5 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`xyVis it should throw error if markSizeRatio is lower then 1 or greater then 100 1`] = `"Mark size ratio must be greater or equal to 1 and less or equal to 100"`;
exports[`xyVis it should throw error if markSizeRatio is lower then 1 or greater then 100 2`] = `"Mark size ratio must be greater or equal to 1 and less or equal to 100"`;
exports[`xyVis it should throw error if markSizeRatio is specified while markSizeAccessor is not 1`] = `"Mark size ratio can be applied only with \`markSizeAccessor\`"`;
exports[`xyVis it should throw error if minTimeBarInterval applied for not time bar chart 1`] = `"\`minTimeBarInterval\` argument is applicable only for time bar charts."`;
exports[`xyVis it should throw error if minTimeBarInterval is invalid 1`] = `"Provided x-axis interval is invalid. The interval should include quantity and unit names. Examples: 1d, 24h, 1w."`;

View file

@ -128,6 +128,10 @@ export const commonXYArgs: CommonXYFn['args'] = {
types: ['string'],
help: strings.getAriaLabelHelp(),
},
markSizeRatio: {
types: ['number'],
help: strings.getMarkSizeRatioHelp(),
},
minTimeBarInterval: {
types: ['string'],
help: strings.getMinTimeBarIntervalHelp(),

View file

@ -0,0 +1,74 @@
/*
* 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 { ExtendedDataLayerArgs } from '../types';
import { createMockExecutionContext } from '@kbn/expressions-plugin/common/mocks';
import { mockPaletteOutput, sampleArgs } from '../__mocks__';
import { LayerTypes } from '../constants';
import { extendedDataLayerFunction } from './extended_data_layer';
describe('extendedDataLayerConfig', () => {
test('produces the correct arguments', async () => {
const { data } = sampleArgs();
const args: ExtendedDataLayerArgs = {
seriesType: 'line',
xAccessor: 'c',
accessors: ['a', 'b'],
splitAccessor: 'd',
xScaleType: 'linear',
isHistogram: false,
palette: mockPaletteOutput,
markSizeAccessor: 'b',
};
const result = await extendedDataLayerFunction.fn(data, args, createMockExecutionContext());
expect(result).toEqual({
type: 'extendedDataLayer',
layerType: LayerTypes.DATA,
...args,
table: data,
});
});
test('throws the error if markSizeAccessor is provided to the not line/area chart', async () => {
const { data } = sampleArgs();
const args: ExtendedDataLayerArgs = {
seriesType: 'bar',
xAccessor: 'c',
accessors: ['a', 'b'],
splitAccessor: 'd',
xScaleType: 'linear',
isHistogram: false,
palette: mockPaletteOutput,
markSizeAccessor: 'b',
};
expect(
extendedDataLayerFunction.fn(data, args, createMockExecutionContext())
).rejects.toThrowErrorMatchingSnapshot();
});
test("throws the error if markSizeAccessor doesn't have the corresponding column in the table", async () => {
const { data } = sampleArgs();
const args: ExtendedDataLayerArgs = {
seriesType: 'line',
xAccessor: 'c',
accessors: ['a', 'b'],
splitAccessor: 'd',
xScaleType: 'linear',
isHistogram: false,
palette: mockPaletteOutput,
markSizeAccessor: 'nonsense',
};
expect(
extendedDataLayerFunction.fn(data, args, createMockExecutionContext())
).rejects.toThrowErrorMatchingSnapshot();
});
});

View file

@ -32,6 +32,10 @@ export const extendedDataLayerFunction: ExtendedDataLayerFn = {
help: strings.getAccessorsHelp(),
multi: true,
},
markSizeAccessor: {
types: ['string'],
help: strings.getMarkSizeAccessorHelp(),
},
table: {
types: ['datatable'],
help: strings.getTableHelp(),

View file

@ -10,6 +10,7 @@ import { validateAccessor } from '@kbn/visualizations-plugin/common/utils';
import { ExtendedDataLayerArgs, ExtendedDataLayerFn } from '../types';
import { EXTENDED_DATA_LAYER, LayerTypes } from '../constants';
import { getAccessors, normalizeTable } from '../helpers';
import { validateMarkSizeForChartType } from './validate';
export const extendedDataLayerFn: ExtendedDataLayerFn['fn'] = async (data, args, context) => {
const table = args.table ?? data;
@ -18,6 +19,8 @@ export const extendedDataLayerFn: ExtendedDataLayerFn['fn'] = async (data, args,
validateAccessor(accessors.xAccessor, table.columns);
validateAccessor(accessors.splitAccessor, table.columns);
accessors.accessors.forEach((accessor) => validateAccessor(accessor, table.columns));
validateMarkSizeForChartType(args.markSizeAccessor, args.seriesType);
validateAccessor(args.markSizeAccessor, table.columns);
const normalizedTable = normalizeTable(table, accessors.xAccessor);

View file

@ -0,0 +1,76 @@
/*
* 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 { layeredXyVisFunction } from '.';
import { createMockExecutionContext } from '@kbn/expressions-plugin/common/mocks';
import { sampleArgs, sampleExtendedLayer } from '../__mocks__';
import { XY_VIS } from '../constants';
describe('layeredXyVis', () => {
test('it renders with the specified data and args', async () => {
const { data, args } = sampleArgs();
const { layers, ...rest } = args;
const result = await layeredXyVisFunction.fn(
data,
{ ...rest, layers: [sampleExtendedLayer] },
createMockExecutionContext()
);
expect(result).toEqual({
type: 'render',
as: XY_VIS,
value: { args: { ...rest, layers: [sampleExtendedLayer] } },
});
});
test('it should throw error if markSizeRatio is lower then 1 or greater then 100', async () => {
const { data, args } = sampleArgs();
const { layers, ...rest } = args;
expect(
layeredXyVisFunction.fn(
data,
{
...rest,
markSizeRatio: 0,
layers: [sampleExtendedLayer],
},
createMockExecutionContext()
)
).rejects.toThrowErrorMatchingSnapshot();
expect(
layeredXyVisFunction.fn(
data,
{
...rest,
markSizeRatio: 101,
layers: [sampleExtendedLayer],
},
createMockExecutionContext()
)
).rejects.toThrowErrorMatchingSnapshot();
});
test('it should throw error if markSizeRatio is specified if no markSizeAccessor is present', async () => {
const { data, args } = sampleArgs();
const { layers, ...rest } = args;
expect(
layeredXyVisFunction.fn(
data,
{
...rest,
markSizeRatio: 10,
layers: [sampleExtendedLayer],
},
createMockExecutionContext()
)
).rejects.toThrowErrorMatchingSnapshot();
});
});

View file

@ -10,7 +10,12 @@ import { XY_VIS_RENDERER } from '../constants';
import { appendLayerIds, getDataLayers } from '../helpers';
import { LayeredXyVisFn } from '../types';
import { logDatatables } from '../utils';
import { validateMinTimeBarInterval, hasBarLayer } from './validate';
import {
validateMarkSizeRatioLimits,
validateMinTimeBarInterval,
hasBarLayer,
errors,
} from './validate';
export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers) => {
const layers = appendLayerIds(args.layers ?? [], 'layers');
@ -19,7 +24,14 @@ export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers)
const dataLayers = getDataLayers(layers);
const hasBar = hasBarLayer(dataLayers);
validateMarkSizeRatioLimits(args.markSizeRatio);
validateMinTimeBarInterval(dataLayers, hasBar, args.minTimeBarInterval);
const hasMarkSizeAccessors =
dataLayers.filter((dataLayer) => dataLayer.markSizeAccessor !== undefined).length > 0;
if (!hasMarkSizeAccessors && args.markSizeRatio !== undefined) {
throw new Error(errors.markSizeRatioWithoutAccessor());
}
return {
type: 'render',
@ -28,6 +40,7 @@ export const layeredXyVisFn: LayeredXyVisFn['fn'] = async (data, args, handlers)
args: {
...args,
layers,
markSizeRatio: hasMarkSizeAccessors && !args.markSizeRatio ? 10 : args.markSizeRatio,
ariaLabel:
args.ariaLabel ??
(handlers.variables?.embeddableTitle as string) ??

View file

@ -8,8 +8,10 @@
import { i18n } from '@kbn/i18n';
import { isValidInterval } from '@kbn/data-plugin/common';
import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common';
import { AxisExtentModes, ValueLabelModes } from '../constants';
import {
SeriesType,
AxisExtentConfigResult,
DataLayerConfigResult,
CommonXYDataLayerConfigResult,
@ -18,7 +20,23 @@ import {
} from '../types';
import { isTimeChart } from '../helpers';
const errors = {
export const errors = {
markSizeAccessorForNonLineOrAreaChartsError: () =>
i18n.translate(
'expressionXY.reusable.function.dataLayer.errors.markSizeAccessorForNonLineOrAreaChartsError',
{
defaultMessage:
"`markSizeAccessor` can't be used. Dots are applied only for line or area charts",
}
),
markSizeRatioLimitsError: () =>
i18n.translate('expressionXY.reusable.function.xyVis.errors.markSizeLimitsError', {
defaultMessage: 'Mark size ratio must be greater or equal to 1 and less or equal to 100',
}),
markSizeRatioWithoutAccessor: () =>
i18n.translate('expressionXY.reusable.function.xyVis.errors.markSizeRatioWithoutAccessor', {
defaultMessage: 'Mark size ratio can be applied only with `markSizeAccessor`',
}),
extendBoundsAreInvalidError: () =>
i18n.translate('expressionXY.reusable.function.xyVis.errors.extendBoundsAreInvalidError', {
defaultMessage:
@ -117,6 +135,30 @@ export const validateValueLabels = (
}
};
export const validateMarkSizeForChartType = (
markSizeAccessor: ExpressionValueVisDimension | string | undefined,
seriesType: SeriesType
) => {
if (markSizeAccessor && !seriesType.includes('line') && !seriesType.includes('area')) {
throw new Error(errors.markSizeAccessorForNonLineOrAreaChartsError());
}
};
export const validateMarkSizeRatioLimits = (markSizeRatio?: number) => {
if (markSizeRatio !== undefined && (markSizeRatio < 1 || markSizeRatio > 100)) {
throw new Error(errors.markSizeRatioLimitsError());
}
};
export const validateMarkSizeRatioWithAccessor = (
markSizeRatio: number | undefined,
markSizeAccessor: ExpressionValueVisDimension | string | undefined
) => {
if (markSizeRatio !== undefined && !markSizeAccessor) {
throw new Error(errors.markSizeRatioWithoutAccessor());
}
};
export const validateMinTimeBarInterval = (
dataLayers: CommonXYDataLayerConfigResult[],
hasBar: boolean,

View file

@ -50,6 +50,37 @@ describe('xyVis', () => {
});
});
test('it should throw error if markSizeRatio is lower then 1 or greater then 100', async () => {
const { data, args } = sampleArgs();
const { layers, ...rest } = args;
expect(
xyVisFunction.fn(
data,
{
...rest,
...{ ...sampleLayer, markSizeAccessor: 'b' },
markSizeRatio: 0,
referenceLineLayers: [],
annotationLayers: [],
},
createMockExecutionContext()
)
).rejects.toThrowErrorMatchingSnapshot();
expect(
xyVisFunction.fn(
data,
{
...rest,
...{ ...sampleLayer, markSizeAccessor: 'b' },
markSizeRatio: 101,
referenceLineLayers: [],
annotationLayers: [],
},
createMockExecutionContext()
)
).rejects.toThrowErrorMatchingSnapshot();
});
test('it should throw error if minTimeBarInterval is invalid', async () => {
const { data, args } = sampleArgs();
const { layers, ...rest } = args;
@ -129,4 +160,24 @@ describe('xyVis', () => {
)
).rejects.toThrowErrorMatchingSnapshot();
});
test('it should throw error if markSizeRatio is specified while markSizeAccessor is not', async () => {
const { data, args } = sampleArgs();
const { layers, ...rest } = args;
const { layerId, layerType, table, type, ...restLayerArgs } = sampleLayer;
expect(
xyVisFunction.fn(
data,
{
...rest,
...restLayerArgs,
referenceLineLayers: [],
annotationLayers: [],
markSizeRatio: 5,
},
createMockExecutionContext()
)
).rejects.toThrowErrorMatchingSnapshot();
});
});

View file

@ -51,6 +51,10 @@ export const xyVisFunction: XyVisFn = {
types: ['vis_dimension', 'string'],
help: strings.getSplitRowAccessorHelp(),
},
markSizeAccessor: {
types: ['vis_dimension', 'string'],
help: strings.getMarkSizeAccessorHelp(),
},
},
async fn(data, args, handlers) {
const { xyVisFn } = await import('./xy_vis_fn');

View file

@ -23,8 +23,11 @@ import {
hasHistogramBarLayer,
validateExtent,
validateFillOpacity,
validateMarkSizeRatioLimits,
validateValueLabels,
validateMinTimeBarInterval,
validateMarkSizeForChartType,
validateMarkSizeRatioWithAccessor,
} from './validate';
const createDataLayer = (args: XYArgs, table: Datatable): DataLayerConfigResult => {
@ -63,6 +66,7 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => {
isHistogram,
yConfig,
palette,
markSizeAccessor,
...restArgs
} = args;
@ -72,6 +76,9 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => {
validateAccessor(dataLayers[0].splitAccessor, data.columns);
dataLayers[0].accessors.forEach((accessor) => validateAccessor(accessor, data.columns));
validateMarkSizeForChartType(dataLayers[0].markSizeAccessor, args.seriesType);
validateAccessor(dataLayers[0].markSizeAccessor, data.columns);
const layers: XYLayerConfig[] = [
...appendLayerIds(dataLayers, 'dataLayers'),
...appendLayerIds(referenceLineLayers, 'referenceLineLayers'),
@ -105,6 +112,8 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => {
const hasNotHistogramBars = !hasHistogramBarLayer(dataLayers);
validateValueLabels(args.valueLabels, hasBar, hasNotHistogramBars);
validateMarkSizeRatioWithAccessor(args.markSizeRatio, dataLayers[0].markSizeAccessor);
validateMarkSizeRatioLimits(args.markSizeRatio);
return {
type: 'render',
@ -113,6 +122,8 @@ export const xyVisFn: XyVisFn['fn'] = async (data, args, handlers) => {
args: {
...restArgs,
layers,
markSizeRatio:
dataLayers[0].markSizeAccessor && !args.markSizeRatio ? 10 : args.markSizeRatio,
ariaLabel:
args.ariaLabel ??
(handlers.variables?.embeddableTitle as string) ??

View file

@ -35,19 +35,24 @@ export function getDataLayers(layers: XYExtendedLayerConfigResult[]) {
);
}
export function getAccessors<T, U extends { splitAccessor?: T; xAccessor?: T; accessors: T[] }>(
args: U,
table: Datatable
) {
export function getAccessors<
T,
U extends { splitAccessor?: T; xAccessor?: T; accessors: T[]; markSizeAccessor?: T }
>(args: U, table: Datatable) {
let splitAccessor: T | string | undefined = args.splitAccessor;
let xAccessor: T | string | undefined = args.xAccessor;
let accessors: Array<T | string> = args.accessors ?? [];
if (!splitAccessor && !xAccessor && !(accessors && accessors.length)) {
let markSizeAccessor: T | string | undefined = args.markSizeAccessor;
if (!splitAccessor && !xAccessor && !(accessors && accessors.length) && !markSizeAccessor) {
const y = table.columns.find((column) => column.id === PointSeriesColumnNames.Y)?.id;
xAccessor = table.columns.find((column) => column.id === PointSeriesColumnNames.X)?.id;
splitAccessor = table.columns.find((column) => column.id === PointSeriesColumnNames.COLOR)?.id;
accessors = y ? [y] : [];
markSizeAccessor = table.columns.find(
(column) => column.id === PointSeriesColumnNames.SIZE
)?.id;
}
return { splitAccessor, xAccessor, accessors };
return { splitAccessor, xAccessor, accessors, markSizeAccessor };
}

View file

@ -121,6 +121,10 @@ export const strings = {
i18n.translate('expressionXY.xyVis.ariaLabel.help', {
defaultMessage: 'Specifies the aria label of the xy chart',
}),
getMarkSizeRatioHelp: () =>
i18n.translate('expressionXY.xyVis.markSizeRatio.help', {
defaultMessage: 'Specifies the ratio of the dots at the line and area charts',
}),
getMinTimeBarIntervalHelp: () =>
i18n.translate('expressionXY.xyVis.xAxisInterval.help', {
defaultMessage: 'Specifies the min interval for time bar chart',
@ -169,6 +173,10 @@ export const strings = {
i18n.translate('expressionXY.dataLayer.accessors.help', {
defaultMessage: 'The columns to display on the y axis.',
}),
getMarkSizeAccessorHelp: () =>
i18n.translate('expressionXY.dataLayer.markSizeAccessor.help', {
defaultMessage: 'Mark size accessor',
}),
getYConfigHelp: () =>
i18n.translate('expressionXY.dataLayer.yConfig.help', {
defaultMessage: 'Additional configuration for y axes',

View file

@ -100,6 +100,7 @@ export interface DataLayerArgs {
xAccessor?: string | ExpressionValueVisDimension;
hide?: boolean;
splitAccessor?: string | ExpressionValueVisDimension;
markSizeAccessor?: string | ExpressionValueVisDimension;
columnToLabel?: string; // Actually a JSON key-value pair
xScaleType: XScaleType;
isHistogram: boolean;
@ -118,10 +119,12 @@ export interface ExtendedDataLayerArgs {
xAccessor?: string;
hide?: boolean;
splitAccessor?: string;
markSizeAccessor?: string;
columnToLabel?: string; // Actually a JSON key-value pair
xScaleType: XScaleType;
isHistogram: boolean;
palette: PaletteOutput;
// palette will always be set on the expression
yConfig?: YConfigResult[];
table?: Datatable;
}
@ -203,6 +206,7 @@ export interface XYArgs extends DataLayerArgs {
hideEndzones?: boolean;
valuesInLegend?: boolean;
ariaLabel?: string;
markSizeRatio?: number;
minTimeBarInterval?: string;
splitRowAccessor?: ExpressionValueVisDimension | string;
splitColumnAccessor?: ExpressionValueVisDimension | string;
@ -231,6 +235,7 @@ export interface LayeredXYArgs {
hideEndzones?: boolean;
valuesInLegend?: boolean;
ariaLabel?: string;
markSizeRatio?: number;
minTimeBarInterval?: string;
}
@ -257,6 +262,7 @@ export interface XYProps {
hideEndzones?: boolean;
valuesInLegend?: boolean;
ariaLabel?: string;
markSizeRatio?: number;
minTimeBarInterval?: string;
splitRowAccessor?: ExpressionValueVisDimension | string;
splitColumnAccessor?: ExpressionValueVisDimension | string;

View file

@ -324,6 +324,7 @@ exports[`XYChart component it renders area 1`] = `
"maxLines": 0,
},
},
"markSizeRatio": undefined,
}
}
tooltip={
@ -645,7 +646,7 @@ exports[`XYChart component it renders area 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -735,7 +736,7 @@ exports[`XYChart component it renders area 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -868,6 +869,7 @@ exports[`XYChart component it renders bar 1`] = `
"maxLines": 0,
},
},
"markSizeRatio": undefined,
}
}
tooltip={
@ -1189,7 +1191,7 @@ exports[`XYChart component it renders bar 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -1279,7 +1281,7 @@ exports[`XYChart component it renders bar 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -1412,6 +1414,7 @@ exports[`XYChart component it renders horizontal bar 1`] = `
"maxLines": 0,
},
},
"markSizeRatio": undefined,
}
}
tooltip={
@ -1733,7 +1736,7 @@ exports[`XYChart component it renders horizontal bar 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -1823,7 +1826,7 @@ exports[`XYChart component it renders horizontal bar 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -1956,6 +1959,7 @@ exports[`XYChart component it renders line 1`] = `
"maxLines": 0,
},
},
"markSizeRatio": undefined,
}
}
tooltip={
@ -2277,7 +2281,7 @@ exports[`XYChart component it renders line 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -2367,7 +2371,7 @@ exports[`XYChart component it renders line 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -2500,6 +2504,7 @@ exports[`XYChart component it renders stacked area 1`] = `
"maxLines": 0,
},
},
"markSizeRatio": undefined,
}
}
tooltip={
@ -2821,7 +2826,7 @@ exports[`XYChart component it renders stacked area 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -2911,7 +2916,7 @@ exports[`XYChart component it renders stacked area 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -3044,6 +3049,7 @@ exports[`XYChart component it renders stacked bar 1`] = `
"maxLines": 0,
},
},
"markSizeRatio": undefined,
}
}
tooltip={
@ -3365,7 +3371,7 @@ exports[`XYChart component it renders stacked bar 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -3455,7 +3461,7 @@ exports[`XYChart component it renders stacked bar 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -3588,6 +3594,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = `
"maxLines": 0,
},
},
"markSizeRatio": undefined,
}
}
tooltip={
@ -3909,7 +3916,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -3999,7 +4006,7 @@ exports[`XYChart component it renders stacked horizontal bar 1`] = `
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -4132,6 +4139,7 @@ exports[`XYChart component split chart should render split chart if both, splitR
"maxLines": 0,
},
},
"markSizeRatio": undefined,
}
}
tooltip={
@ -4210,7 +4218,7 @@ exports[`XYChart component split chart should render split chart if both, splitR
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -4708,7 +4716,7 @@ exports[`XYChart component split chart should render split chart if both, splitR
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -4798,7 +4806,7 @@ exports[`XYChart component split chart should render split chart if both, splitR
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -4931,6 +4939,7 @@ exports[`XYChart component split chart should render split chart if splitColumnA
"maxLines": 0,
},
},
"markSizeRatio": undefined,
}
}
tooltip={
@ -5009,7 +5018,7 @@ exports[`XYChart component split chart should render split chart if splitColumnA
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -5506,7 +5515,7 @@ exports[`XYChart component split chart should render split chart if splitColumnA
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -5596,7 +5605,7 @@ exports[`XYChart component split chart should render split chart if splitColumnA
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -5729,6 +5738,7 @@ exports[`XYChart component split chart should render split chart if splitRowAcce
"maxLines": 0,
},
},
"markSizeRatio": undefined,
}
}
tooltip={
@ -5807,7 +5817,7 @@ exports[`XYChart component split chart should render split chart if splitRowAcce
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -6304,7 +6314,7 @@ exports[`XYChart component split chart should render split chart if splitRowAcce
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},
@ -6394,7 +6404,7 @@ exports[`XYChart component split chart should render split chart if splitRowAcce
},
"type": "date-histogram",
},
"type": "date",
"type": "string",
},
"name": "c",
},

View file

@ -13,6 +13,7 @@ import {
AreaSeries,
Axis,
BarSeries,
ColorVariant,
Fit,
GeometryValue,
GroupBy,
@ -687,6 +688,40 @@ describe('XYChart component', () => {
expect(component.find(Settings).at(0).prop('showLegendExtra')).toEqual(true);
});
test('applies the mark size ratio', () => {
const { args } = sampleArgs();
const markSizeRatioArg = { markSizeRatio: 50 };
const component = shallow(
<XYChart {...defaultProps} args={{ ...args, ...markSizeRatioArg }} />
);
expect(component.find(Settings).at(0).prop('theme')).toEqual(
expect.objectContaining(markSizeRatioArg)
);
});
test('applies the mark size accessor', () => {
const { args } = sampleArgs();
const markSizeAccessorArg = { markSizeAccessor: 'b' };
const component = shallow(
<XYChart
{...defaultProps}
args={{ ...args, layers: [{ ...args.layers[0], ...markSizeAccessorArg }] }}
/>
);
const dataLayers = component.find(DataLayers).dive();
const lineArea = dataLayers.find(LineSeries).at(0);
expect(lineArea.prop('markSizeAccessor')).toEqual(markSizeAccessorArg.markSizeAccessor);
const expectedSeriesStyle = expect.objectContaining({
point: expect.objectContaining({
visible: true,
fill: ColorVariant.Series,
}),
});
expect(lineArea.prop('areaSeriesStyle')).toEqual(expectedSeriesStyle);
expect(lineArea.prop('lineSeriesStyle')).toEqual(expectedSeriesStyle);
});
test('it renders bar', () => {
const { args } = sampleArgs();
const component = shallow(
@ -2132,6 +2167,7 @@ describe('XYChart component', () => {
mode: 'full',
type: 'axisExtentConfig',
},
markSizeRatio: 1,
layers: [
{
layerId: 'first',
@ -2219,6 +2255,7 @@ describe('XYChart component', () => {
mode: 'full',
type: 'axisExtentConfig',
},
markSizeRatio: 1,
yLeftScale: 'linear',
yRightScale: 'linear',
layers: [
@ -2292,6 +2329,7 @@ describe('XYChart component', () => {
mode: 'full',
type: 'axisExtentConfig',
},
markSizeRatio: 1,
yLeftScale: 'linear',
yRightScale: 'linear',
layers: [

View file

@ -48,17 +48,15 @@ import {
getAnnotationsLayers,
getDataLayers,
Series,
getFormattedTablesByLayers,
validateExtent,
getFormat,
} from '../helpers';
import {
getFormattedTablesByLayers,
getFilteredLayers,
getReferenceLayers,
isDataLayer,
getAxesConfiguration,
GroupsConfiguration,
getLinesCausedPaddings,
validateExtent,
} from '../helpers';
import { getXDomain, XyEndzones } from './x_domain';
import { getLegendAction } from './legend_action';
@ -571,6 +569,7 @@ export function XYChart({
shouldRotate
),
},
markSizeRatio: args.markSizeRatio,
}}
baseTheme={chartBaseTheme}
tooltip={{

View file

@ -226,9 +226,14 @@ const getSeriesName: GetSeriesNameFn = (
return splitColumnId ? data.seriesKeys[0] : columnToLabelMap[data.seriesKeys[0]] ?? null;
};
const getPointConfig = (xAccessor?: string, emphasizeFitting?: boolean) => ({
visible: !xAccessor,
const getPointConfig = (
xAccessor: string | undefined,
markSizeAccessor: string | undefined,
emphasizeFitting?: boolean
) => ({
visible: !xAccessor || markSizeAccessor !== undefined,
radius: xAccessor && !emphasizeFitting ? 5 : 0,
fill: markSizeAccessor ? ColorVariant.Series : undefined,
});
const getLineConfig = () => ({ visible: true, stroke: ColorVariant.Series, opacity: 1, dash: [] });
@ -276,7 +281,7 @@ export const getSeriesProps: GetSeriesPropsFn = ({
fillOpacity,
formattedDatatableInfo,
}): SeriesSpec => {
const { table } = layer;
const { table, markSizeAccessor } = layer;
const isStacked = layer.seriesType.includes('stacked');
const isPercentage = layer.seriesType.includes('percentage');
const isBarChart = layer.seriesType.includes('bar');
@ -294,6 +299,14 @@ export const getSeriesProps: GetSeriesPropsFn = ({
: undefined;
const splitFormatter = formatFactory(splitHint);
const markSizeColumnId = markSizeAccessor
? getAccessorByDimension(markSizeAccessor, table.columns)
: undefined;
const markFormatter = formatFactory(
markSizeAccessor ? getFormat(table.columns, markSizeAccessor) : undefined
);
// what if row values are not primitive? That is the case of, for instance, Ranges
// remaps them to their serialized version with the formatHint metadata
// In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on
@ -326,6 +339,8 @@ export const getSeriesProps: GetSeriesPropsFn = ({
id: splitColumnId ? `${splitColumnId}-${accessor}` : accessor,
xAccessor: xColumnId || 'unifiedX',
yAccessors: [accessor],
markSizeAccessor: markSizeColumnId,
markFormat: (value) => markFormatter.convert(value),
data: rows,
xScaleType: xColumnId ? layer.xScaleType : 'ordinal',
yScaleType:
@ -346,14 +361,14 @@ export const getSeriesProps: GetSeriesPropsFn = ({
stackMode: isPercentage ? StackMode.Percentage : undefined,
timeZone,
areaSeriesStyle: {
point: getPointConfig(xColumnId, emphasizeFitting),
point: getPointConfig(xColumnId, markSizeColumnId, emphasizeFitting),
...(fillOpacity && { area: { opacity: fillOpacity } }),
...(emphasizeFitting && {
fit: { area: { opacity: fillOpacity || 0.5 }, line: getLineConfig() },
}),
},
lineSeriesStyle: {
point: getPointConfig(xColumnId, emphasizeFitting),
point: getPointConfig(xColumnId, markSizeColumnId, emphasizeFitting),
...(emphasizeFitting && { fit: { line: getLineConfig() } }),
},
name(d) {