mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Lens] data layer & reference layer as separate types (#126885)
* [Lens] data layer & reference layer as separate types * types better (thanks Marco ) * CR comment
This commit is contained in:
parent
79905bac04
commit
849542e9f3
37 changed files with 534 additions and 363 deletions
|
@ -7,7 +7,6 @@
|
|||
|
||||
import rison from 'rison-node';
|
||||
import type { TimeRange } from '../../../../src/plugins/data/common/query';
|
||||
import { LayerType } from './types';
|
||||
|
||||
export const PLUGIN_ID = 'lens';
|
||||
export const APP_ID = 'lens';
|
||||
|
@ -43,10 +42,10 @@ export const LegendDisplay = {
|
|||
HIDE: 'hide',
|
||||
} as const;
|
||||
|
||||
export const layerTypes: Record<string, LayerType> = {
|
||||
export const layerTypes = {
|
||||
DATA: 'data',
|
||||
REFERENCELINE: 'referenceLine',
|
||||
};
|
||||
} as const;
|
||||
|
||||
// might collide with user-supplied field names, try to make as unique as possible
|
||||
export const DOCUMENT_FIELD_NAME = '___records___';
|
||||
|
|
|
@ -5,30 +5,28 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { PaletteOutput } from '../../../../../../src/plugins/charts/common';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../src/plugins/expressions/common';
|
||||
import type { LayerType } from '../../types';
|
||||
import { layerTypes } from '../../constants';
|
||||
import { axisConfig, YConfig } from './axis_config';
|
||||
import type { SeriesType } from './series_type';
|
||||
import { layerTypes } from '../../../constants';
|
||||
import type { PaletteOutput } from '../../../../../../../src/plugins/charts/common';
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../../src/plugins/expressions/common';
|
||||
import { axisConfig, YConfig } from '../axis_config';
|
||||
import type { SeriesType } from '../series_type';
|
||||
|
||||
export interface XYLayerConfig {
|
||||
hide?: boolean;
|
||||
export interface XYDataLayerConfig {
|
||||
layerId: string;
|
||||
xAccessor?: string;
|
||||
layerType: typeof layerTypes.DATA;
|
||||
accessors: string[];
|
||||
yConfig?: YConfig[];
|
||||
seriesType: SeriesType;
|
||||
xAccessor?: string;
|
||||
hide?: boolean;
|
||||
yConfig?: YConfig[];
|
||||
splitAccessor?: string;
|
||||
palette?: PaletteOutput;
|
||||
layerType: LayerType;
|
||||
}
|
||||
export interface ValidLayer extends XYDataLayerConfig {
|
||||
xAccessor: NonNullable<XYDataLayerConfig['xAccessor']>;
|
||||
}
|
||||
|
||||
export interface ValidLayer extends XYLayerConfig {
|
||||
xAccessor: NonNullable<XYLayerConfig['xAccessor']>;
|
||||
}
|
||||
|
||||
export type LayerArgs = XYLayerConfig & {
|
||||
export type DataLayerArgs = XYDataLayerConfig & {
|
||||
columnToLabel?: string; // Actually a JSON key-value pair
|
||||
yScaleType: 'time' | 'linear' | 'log' | 'sqrt';
|
||||
xScaleType: 'time' | 'linear' | 'ordinal';
|
||||
|
@ -37,17 +35,17 @@ export type LayerArgs = XYLayerConfig & {
|
|||
palette: PaletteOutput;
|
||||
};
|
||||
|
||||
export type LayerConfigResult = LayerArgs & { type: 'lens_xy_layer' };
|
||||
export type DataLayerConfigResult = DataLayerArgs & { type: 'lens_xy_data_layer' };
|
||||
|
||||
export const layerConfig: ExpressionFunctionDefinition<
|
||||
'lens_xy_layer',
|
||||
export const dataLayerConfig: ExpressionFunctionDefinition<
|
||||
'lens_xy_data_layer',
|
||||
null,
|
||||
LayerArgs,
|
||||
LayerConfigResult
|
||||
DataLayerArgs,
|
||||
DataLayerConfigResult
|
||||
> = {
|
||||
name: 'lens_xy_layer',
|
||||
name: 'lens_xy_data_layer',
|
||||
aliases: [],
|
||||
type: 'lens_xy_layer',
|
||||
type: 'lens_xy_data_layer',
|
||||
help: `Configure a layer in the xy chart`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
|
@ -60,7 +58,7 @@ export const layerConfig: ExpressionFunctionDefinition<
|
|||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
layerType: { types: ['string'], options: Object.values(layerTypes), help: '' },
|
||||
layerType: { types: ['string'], options: [layerTypes.DATA], help: '' },
|
||||
seriesType: {
|
||||
types: ['string'],
|
||||
options: [
|
||||
|
@ -115,9 +113,9 @@ export const layerConfig: ExpressionFunctionDefinition<
|
|||
types: ['palette'],
|
||||
},
|
||||
},
|
||||
fn: function fn(input: unknown, args: LayerArgs) {
|
||||
fn: function fn(input: unknown, args: DataLayerArgs) {
|
||||
return {
|
||||
type: 'lens_xy_layer',
|
||||
type: 'lens_xy_data_layer',
|
||||
...args,
|
||||
};
|
||||
},
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { XYDataLayerConfig } from './data_layer_config';
|
||||
import { XYReferenceLineLayerConfig } from './reference_line_layer_config';
|
||||
export * from './data_layer_config';
|
||||
export * from './reference_line_layer_config';
|
||||
|
||||
export type XYLayerConfig = XYDataLayerConfig | XYReferenceLineLayerConfig;
|
|
@ -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; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { ExpressionFunctionDefinition } from '../../../../../../../src/plugins/expressions/common';
|
||||
import { layerTypes } from '../../../constants';
|
||||
import { YConfig } from '../axis_config';
|
||||
|
||||
export interface XYReferenceLineLayerConfig {
|
||||
layerId: string;
|
||||
layerType: typeof layerTypes.REFERENCELINE;
|
||||
accessors: string[];
|
||||
yConfig?: YConfig[];
|
||||
}
|
||||
export type ReferenceLineLayerArgs = XYReferenceLineLayerConfig & {
|
||||
columnToLabel?: string;
|
||||
};
|
||||
export type ReferenceLineLayerConfigResult = ReferenceLineLayerArgs & {
|
||||
type: 'lens_xy_referenceLine_layer';
|
||||
};
|
||||
|
||||
export const referenceLineLayerConfig: ExpressionFunctionDefinition<
|
||||
'lens_xy_referenceLine_layer',
|
||||
null,
|
||||
ReferenceLineLayerArgs,
|
||||
ReferenceLineLayerConfigResult
|
||||
> = {
|
||||
name: 'lens_xy_referenceLine_layer',
|
||||
aliases: [],
|
||||
type: 'lens_xy_referenceLine_layer',
|
||||
help: `Configure a layer in the xy chart`,
|
||||
inputTypes: ['null'],
|
||||
args: {
|
||||
layerId: {
|
||||
types: ['string'],
|
||||
help: '',
|
||||
},
|
||||
layerType: { types: ['string'], options: [layerTypes.REFERENCELINE], help: '' },
|
||||
accessors: {
|
||||
types: ['string'],
|
||||
help: 'The columns to display on the y axis.',
|
||||
multi: true,
|
||||
},
|
||||
yConfig: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
types: ['lens_xy_yConfig' as any],
|
||||
help: 'Additional configuration for y axes',
|
||||
multi: true,
|
||||
},
|
||||
columnToLabel: {
|
||||
types: ['string'],
|
||||
help: 'JSON key-value pairs of column ID to label',
|
||||
},
|
||||
},
|
||||
fn: function fn(input: unknown, args: ReferenceLineLayerArgs) {
|
||||
return {
|
||||
type: 'lens_xy_referenceLine_layer',
|
||||
...args,
|
||||
};
|
||||
},
|
||||
};
|
|
@ -8,7 +8,7 @@
|
|||
import type { AxisExtentConfigResult, AxisTitlesVisibilityConfigResult } from './axis_config';
|
||||
import type { FittingFunction } from './fitting_function';
|
||||
import type { GridlinesConfigResult } from './grid_lines_config';
|
||||
import type { LayerArgs } from './layer_config';
|
||||
import type { DataLayerArgs } from './layer_config';
|
||||
import type { LegendConfigResult } from './legend_config';
|
||||
import type { TickLabelsConfigResult } from './tick_labels_config';
|
||||
import type { LabelsOrientationConfigResult } from './labels_orientation_config';
|
||||
|
@ -27,7 +27,7 @@ export interface XYArgs {
|
|||
yRightExtent: AxisExtentConfigResult;
|
||||
legend: LegendConfigResult;
|
||||
valueLabels: ValueLabelConfig;
|
||||
layers: LayerArgs[];
|
||||
layers: DataLayerArgs[];
|
||||
fittingFunction?: FittingFunction;
|
||||
axisTitlesVisibilitySettings?: AxisTitlesVisibilityConfigResult;
|
||||
tickLabelsVisibilitySettings?: TickLabelsConfigResult;
|
||||
|
|
|
@ -117,7 +117,7 @@ export const xyChart: ExpressionFunctionDefinition<
|
|||
},
|
||||
layers: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
types: ['lens_xy_layer'] as any,
|
||||
types: ['lens_xy_data_layer', 'lens_xy_referenceLine_layer'] as any,
|
||||
help: 'Layers of visual series',
|
||||
multi: true,
|
||||
},
|
||||
|
|
|
@ -15,7 +15,13 @@ import type {
|
|||
import type { Datatable } from '../../../../src/plugins/expressions/common';
|
||||
import type { PaletteContinuity } from '../../../../src/plugins/charts/common';
|
||||
import type { PaletteOutput } from '../../../../src/plugins/charts/common';
|
||||
import { CategoryDisplay, LegendDisplay, NumberDisplay, PieChartTypes } from './constants';
|
||||
import {
|
||||
CategoryDisplay,
|
||||
layerTypes,
|
||||
LegendDisplay,
|
||||
NumberDisplay,
|
||||
PieChartTypes,
|
||||
} from './constants';
|
||||
|
||||
export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat;
|
||||
|
||||
|
@ -73,7 +79,7 @@ export type RequiredPaletteParamTypes = Required<CustomPaletteParams> & {
|
|||
maxSteps?: number;
|
||||
};
|
||||
|
||||
export type LayerType = 'data' | 'referenceLine';
|
||||
export type LayerType = typeof layerTypes[keyof typeof layerTypes];
|
||||
|
||||
// Shared by XY Chart and Heatmap as for now
|
||||
export type ValueLabelConfig = 'hide' | 'inside' | 'outside';
|
||||
|
|
|
@ -14,7 +14,10 @@ import {
|
|||
} from '../common/expressions/xy_chart/axis_config';
|
||||
import { gridlinesConfig } from '../common/expressions/xy_chart/grid_lines_config';
|
||||
import { labelsOrientationConfig } from '../common/expressions/xy_chart/labels_orientation_config';
|
||||
import { layerConfig } from '../common/expressions/xy_chart/layer_config';
|
||||
import {
|
||||
dataLayerConfig,
|
||||
referenceLineLayerConfig,
|
||||
} from '../common/expressions/xy_chart/layer_config';
|
||||
import { legendConfig } from '../common/expressions/xy_chart/legend_config';
|
||||
import { tickLabelsConfig } from '../common/expressions/xy_chart/tick_labels_config';
|
||||
import { xyChart } from '../common/expressions/xy_chart/xy_chart';
|
||||
|
@ -43,7 +46,8 @@ export const setupExpressions = (
|
|||
counterRate,
|
||||
metricChart,
|
||||
yAxisConfig,
|
||||
layerConfig,
|
||||
dataLayerConfig,
|
||||
referenceLineLayerConfig,
|
||||
formatColumn,
|
||||
legendConfig,
|
||||
renameColumns,
|
||||
|
|
|
@ -127,7 +127,7 @@ Object {
|
|||
"linear",
|
||||
],
|
||||
},
|
||||
"function": "lens_xy_layer",
|
||||
"function": "lens_xy_data_layer",
|
||||
"type": "function",
|
||||
},
|
||||
],
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { LayerArgs } from '../../common/expressions';
|
||||
import { DataLayerArgs } from '../../common/expressions';
|
||||
import { layerTypes } from '../../common';
|
||||
import { Datatable } from '../../../../../src/plugins/expressions/public';
|
||||
import { getAxesConfiguration } from './axes_configuration';
|
||||
|
@ -219,7 +219,7 @@ describe('axes_configuration', () => {
|
|||
},
|
||||
};
|
||||
|
||||
const sampleLayer: LayerArgs = {
|
||||
const sampleLayer: DataLayerArgs = {
|
||||
layerId: 'first',
|
||||
layerType: layerTypes.DATA,
|
||||
seriesType: 'line',
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { FormatFactory } from '../../common';
|
||||
import { AxisExtentConfig, XYLayerConfig } from '../../common/expressions';
|
||||
import { AxisExtentConfig, XYDataLayerConfig } from '../../common/expressions';
|
||||
import { Datatable } from '../../../../../src/plugins/expressions/public';
|
||||
import type {
|
||||
IFieldFormat,
|
||||
|
@ -33,7 +33,7 @@ export function isFormatterCompatible(
|
|||
return formatter1.id === formatter2.id;
|
||||
}
|
||||
|
||||
export function groupAxesByType(layers: XYLayerConfig[], tables?: Record<string, Datatable>) {
|
||||
export function groupAxesByType(layers: XYDataLayerConfig[], tables?: Record<string, Datatable>) {
|
||||
const series: {
|
||||
auto: FormattedMetric[];
|
||||
left: FormattedMetric[];
|
||||
|
@ -97,7 +97,7 @@ export function groupAxesByType(layers: XYLayerConfig[], tables?: Record<string,
|
|||
}
|
||||
|
||||
export function getAxesConfiguration(
|
||||
layers: XYLayerConfig[],
|
||||
layers: XYDataLayerConfig[],
|
||||
shouldRotate: boolean,
|
||||
tables?: Record<string, Datatable>,
|
||||
formatFactory?: FormatFactory
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
import { getColorAssignments } from './color_assignment';
|
||||
import type { FormatFactory, LensMultiTable } from '../../common';
|
||||
import type { LayerArgs } from '../../common/expressions';
|
||||
import type { DataLayerArgs } from '../../common/expressions';
|
||||
import { layerTypes } from '../../common';
|
||||
|
||||
describe('color_assignment', () => {
|
||||
const layers: LayerArgs[] = [
|
||||
const layers: DataLayerArgs[] = [
|
||||
{
|
||||
yScaleType: 'linear',
|
||||
xScaleType: 'linear',
|
||||
|
|
|
@ -122,6 +122,7 @@ export function getAccessorColorConfig(
|
|||
if (isReferenceLayer(layer)) {
|
||||
return getReferenceLineAccessorColorConfig(layer);
|
||||
}
|
||||
|
||||
const layerContainsSplits = Boolean(layer.splitAccessor);
|
||||
const currentPalette: PaletteOutput = layer.palette || { type: 'palette', name: 'default' };
|
||||
const totalSeriesCount = colorAssignments[currentPalette.name]?.totalSeriesCount;
|
||||
|
|
|
@ -27,13 +27,13 @@ import type { LensMultiTable } from '../../common';
|
|||
import { layerTypes } from '../../common';
|
||||
import { xyChart } from '../../common/expressions';
|
||||
import {
|
||||
layerConfig,
|
||||
dataLayerConfig,
|
||||
legendConfig,
|
||||
tickLabelsConfig,
|
||||
gridlinesConfig,
|
||||
XYArgs,
|
||||
LegendConfig,
|
||||
LayerArgs,
|
||||
DataLayerArgs,
|
||||
AxesSettingsConfig,
|
||||
XYChartProps,
|
||||
labelsOrientationConfig,
|
||||
|
@ -212,7 +212,7 @@ const dateHistogramData: LensMultiTable = {
|
|||
},
|
||||
};
|
||||
|
||||
const dateHistogramLayer: LayerArgs = {
|
||||
const dateHistogramLayer: DataLayerArgs = {
|
||||
layerId: 'timeLayer',
|
||||
layerType: layerTypes.DATA,
|
||||
hide: false,
|
||||
|
@ -254,7 +254,7 @@ const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable => ({
|
|||
rows,
|
||||
});
|
||||
|
||||
const sampleLayer: LayerArgs = {
|
||||
const sampleLayer: DataLayerArgs = {
|
||||
layerId: 'first',
|
||||
layerType: layerTypes.DATA,
|
||||
seriesType: 'line',
|
||||
|
@ -268,7 +268,7 @@ const sampleLayer: LayerArgs = {
|
|||
palette: mockPaletteOutput,
|
||||
};
|
||||
|
||||
const createArgsWithLayers = (layers: LayerArgs[] = [sampleLayer]): XYArgs => ({
|
||||
const createArgsWithLayers = (layers: DataLayerArgs[] = [sampleLayer]): XYArgs => ({
|
||||
xTitle: '',
|
||||
yTitle: '',
|
||||
yRightTitle: '',
|
||||
|
@ -392,8 +392,8 @@ describe('xy_expression', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('layerConfig produces the correct arguments', () => {
|
||||
const args: LayerArgs = {
|
||||
test('dataLayerConfig produces the correct arguments', () => {
|
||||
const args: DataLayerArgs = {
|
||||
layerId: 'first',
|
||||
layerType: layerTypes.DATA,
|
||||
seriesType: 'line',
|
||||
|
@ -406,10 +406,10 @@ describe('xy_expression', () => {
|
|||
palette: mockPaletteOutput,
|
||||
};
|
||||
|
||||
const result = layerConfig.fn(null, args, createMockExecutionContext());
|
||||
const result = dataLayerConfig.fn(null, args, createMockExecutionContext());
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'lens_xy_layer',
|
||||
type: 'lens_xy_data_layer',
|
||||
...args,
|
||||
});
|
||||
});
|
||||
|
@ -556,7 +556,7 @@ describe('xy_expression', () => {
|
|||
});
|
||||
|
||||
describe('date range', () => {
|
||||
const timeSampleLayer: LayerArgs = {
|
||||
const timeSampleLayer: DataLayerArgs = {
|
||||
layerId: 'first',
|
||||
layerType: layerTypes.DATA,
|
||||
seriesType: 'line',
|
||||
|
@ -649,7 +649,7 @@ describe('xy_expression', () => {
|
|||
});
|
||||
|
||||
describe('axis time', () => {
|
||||
const defaultTimeLayer: LayerArgs = {
|
||||
const defaultTimeLayer: DataLayerArgs = {
|
||||
layerId: 'first',
|
||||
layerType: layerTypes.DATA,
|
||||
seriesType: 'line',
|
||||
|
@ -707,7 +707,7 @@ describe('xy_expression', () => {
|
|||
});
|
||||
test('it should disable the new time axis for a vertical bar with break down dimension', () => {
|
||||
const { data } = sampleArgs();
|
||||
const timeLayer: LayerArgs = {
|
||||
const timeLayer: DataLayerArgs = {
|
||||
...defaultTimeLayer,
|
||||
seriesType: 'bar',
|
||||
};
|
||||
|
@ -734,7 +734,7 @@ describe('xy_expression', () => {
|
|||
|
||||
test('it should enable the new time axis for a stacked vertical bar with break down dimension', () => {
|
||||
const { data } = sampleArgs();
|
||||
const timeLayer: LayerArgs = {
|
||||
const timeLayer: DataLayerArgs = {
|
||||
...defaultTimeLayer,
|
||||
seriesType: 'bar_stacked',
|
||||
};
|
||||
|
@ -1227,7 +1227,7 @@ describe('xy_expression', () => {
|
|||
test('onBrushEnd returns correct context data for number histogram data', () => {
|
||||
const { args } = sampleArgs();
|
||||
|
||||
const numberLayer: LayerArgs = {
|
||||
const numberLayer: DataLayerArgs = {
|
||||
layerId: 'numberLayer',
|
||||
layerType: layerTypes.DATA,
|
||||
hide: false,
|
||||
|
@ -1436,7 +1436,7 @@ describe('xy_expression', () => {
|
|||
test('onElementClick returns correct context data for numeric histogram', () => {
|
||||
const { args } = sampleArgs();
|
||||
|
||||
const numberLayer: LayerArgs = {
|
||||
const numberLayer: DataLayerArgs = {
|
||||
layerId: 'numberLayer',
|
||||
layerType: layerTypes.DATA,
|
||||
hide: false,
|
||||
|
@ -1757,7 +1757,7 @@ describe('xy_expression', () => {
|
|||
|
||||
test('it applies histogram mode to the series for single series', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
const firstLayer: LayerArgs = {
|
||||
const firstLayer: DataLayerArgs = {
|
||||
...args.layers[0],
|
||||
accessors: ['b'],
|
||||
seriesType: 'bar',
|
||||
|
@ -1772,7 +1772,7 @@ describe('xy_expression', () => {
|
|||
|
||||
test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true };
|
||||
const firstLayer: DataLayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true };
|
||||
delete firstLayer.splitAccessor;
|
||||
const component = shallow(
|
||||
<XYChart {...defaultProps} data={data} args={{ ...args, layers: [firstLayer] }} />
|
||||
|
@ -1783,9 +1783,17 @@ describe('xy_expression', () => {
|
|||
|
||||
test('it applies histogram mode to more than one the series for unstacked line/area chart', () => {
|
||||
const { data, args } = sampleArgs();
|
||||
const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'line', isHistogram: true };
|
||||
const firstLayer: DataLayerArgs = {
|
||||
...args.layers[0],
|
||||
seriesType: 'line',
|
||||
isHistogram: true,
|
||||
};
|
||||
delete firstLayer.splitAccessor;
|
||||
const secondLayer: LayerArgs = { ...args.layers[0], seriesType: 'line', isHistogram: true };
|
||||
const secondLayer: DataLayerArgs = {
|
||||
...args.layers[0],
|
||||
seriesType: 'line',
|
||||
isHistogram: true,
|
||||
};
|
||||
delete secondLayer.splitAccessor;
|
||||
const component = shallow(
|
||||
<XYChart
|
||||
|
@ -2874,7 +2882,7 @@ describe('xy_expression', () => {
|
|||
toDate: new Date('2019-01-03T05:00:00.000Z'),
|
||||
},
|
||||
};
|
||||
const timeSampleLayer: LayerArgs = {
|
||||
const timeSampleLayer: DataLayerArgs = {
|
||||
layerId: 'first',
|
||||
layerType: layerTypes.DATA,
|
||||
seriesType: 'line',
|
||||
|
|
|
@ -53,7 +53,7 @@ import { EmptyPlaceholder } from '../../../../../src/plugins/charts/public';
|
|||
import { KibanaThemeProvider } from '../../../../../src/plugins/kibana_react/public';
|
||||
import type { ILensInterpreterRenderHandlers, LensFilterEvent, LensBrushEvent } from '../types';
|
||||
import type { LensMultiTable, FormatFactory } from '../../common';
|
||||
import type { LayerArgs, SeriesType, XYChartProps } from '../../common/expressions';
|
||||
import type { DataLayerArgs, SeriesType, XYChartProps } from '../../common/expressions';
|
||||
import { visualizationTypes } from './types';
|
||||
import { VisualizationContainer } from '../visualization_container';
|
||||
import { isHorizontalChart, getSeriesColor } from './state_helpers';
|
||||
|
@ -77,7 +77,7 @@ import {
|
|||
ReferenceLineAnnotations,
|
||||
} from './expression_reference_lines';
|
||||
import { computeOverallDataDomain } from './reference_line_helpers';
|
||||
import { isDataLayer, isReferenceLayer } from './visualization_helpers';
|
||||
import { getReferenceLayers, isDataLayer } from './visualization_helpers';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
@ -255,7 +255,7 @@ export function XYChart({
|
|||
const layersById = filteredLayers.reduce((memo, layer) => {
|
||||
memo[layer.layerId] = layer;
|
||||
return memo;
|
||||
}, {} as Record<string, LayerArgs>);
|
||||
}, {} as Record<string, DataLayerArgs>);
|
||||
|
||||
const handleCursorUpdate = useActiveCursor(chartsActiveCursorService, chartRef, {
|
||||
datatables: Object.values(data.tables),
|
||||
|
@ -349,7 +349,7 @@ export function XYChart({
|
|||
);
|
||||
};
|
||||
|
||||
const referenceLineLayers = layers.filter((layer) => isReferenceLayer(layer));
|
||||
const referenceLineLayers = getReferenceLayers(layers);
|
||||
const referenceLinePaddings = getReferenceLineRequiredPaddings(referenceLineLayers, yAxesMap);
|
||||
|
||||
const getYAxesStyle = (groupId: 'left' | 'right') => {
|
||||
|
@ -985,7 +985,7 @@ export function XYChart({
|
|||
);
|
||||
}
|
||||
|
||||
function getFilteredLayers(layers: LayerArgs[], data: LensMultiTable) {
|
||||
function getFilteredLayers(layers: DataLayerArgs[], data: LensMultiTable) {
|
||||
return layers.filter((layer) => {
|
||||
const { layerId, xAccessor, accessors, splitAccessor } = layer;
|
||||
return (
|
||||
|
|
|
@ -8,11 +8,10 @@
|
|||
import { LineAnnotation, RectAnnotation } from '@elastic/charts';
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { PaletteOutput } from 'src/plugins/charts/common';
|
||||
import { chartPluginMock } from 'src/plugins/charts/public/mocks';
|
||||
import { FieldFormat } from 'src/plugins/field_formats/common';
|
||||
import { layerTypes, LensMultiTable } from '../../common';
|
||||
import { LayerArgs, YConfig } from '../../common/expressions';
|
||||
import { LensMultiTable } from '../../common';
|
||||
import { ReferenceLineLayerArgs, YConfig } from '../../common/expressions';
|
||||
import {
|
||||
ReferenceLineAnnotations,
|
||||
ReferenceLineAnnotationsProps,
|
||||
|
@ -20,12 +19,6 @@ import {
|
|||
|
||||
const paletteService = chartPluginMock.createPaletteRegistry();
|
||||
|
||||
const mockPaletteOutput: PaletteOutput = {
|
||||
type: 'palette',
|
||||
name: 'mock',
|
||||
params: {},
|
||||
};
|
||||
|
||||
const row: Record<string, number> = {
|
||||
xAccessorFirstId: 1,
|
||||
xAccessorSecondId: 2,
|
||||
|
@ -57,18 +50,12 @@ const histogramData: LensMultiTable = {
|
|||
},
|
||||
};
|
||||
|
||||
function createLayers(yConfigs: LayerArgs['yConfig']): LayerArgs[] {
|
||||
function createLayers(yConfigs: ReferenceLineLayerArgs['yConfig']): ReferenceLineLayerArgs[] {
|
||||
return [
|
||||
{
|
||||
layerId: 'firstLayer',
|
||||
layerType: layerTypes.REFERENCE_LINE,
|
||||
hide: false,
|
||||
yScaleType: 'linear',
|
||||
xScaleType: 'linear',
|
||||
isHistogram: false,
|
||||
seriesType: 'bar_stacked',
|
||||
layerType: 'referenceLine',
|
||||
accessors: (yConfigs || []).map(({ forAccessor }) => forAccessor),
|
||||
palette: mockPaletteOutput,
|
||||
yConfig: yConfigs,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -13,7 +13,7 @@ import { RectAnnotation, AnnotationDomainType, LineAnnotation, Position } from '
|
|||
import type { PaletteRegistry } from 'src/plugins/charts/public';
|
||||
import type { FieldFormat } from 'src/plugins/field_formats/common';
|
||||
import { euiLightVars } from '@kbn/ui-theme';
|
||||
import type { LayerArgs, YConfig } from '../../common/expressions';
|
||||
import type { ReferenceLineLayerArgs, YConfig } from '../../common/expressions';
|
||||
import type { LensMultiTable } from '../../common/types';
|
||||
import { hasIcon } from './xy_config_panel/shared/icon_select';
|
||||
|
||||
|
@ -55,7 +55,7 @@ export const computeChartMargins = (
|
|||
|
||||
// Note: it does not take into consideration whether the reference line is in view or not
|
||||
export const getReferenceLineRequiredPaddings = (
|
||||
referenceLineLayers: LayerArgs[],
|
||||
referenceLineLayers: ReferenceLineLayerArgs[],
|
||||
axesMap: Record<'left' | 'right', unknown>
|
||||
) => {
|
||||
// collect all paddings for the 4 axis: if any text is detected double it.
|
||||
|
@ -181,7 +181,7 @@ function getMarkerToShow(
|
|||
}
|
||||
|
||||
export interface ReferenceLineAnnotationsProps {
|
||||
layers: LayerArgs[];
|
||||
layers: ReferenceLineLayerArgs[];
|
||||
data: LensMultiTable;
|
||||
formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>;
|
||||
paletteService: PaletteRegistry;
|
||||
|
|
|
@ -12,7 +12,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers';
|
|||
import { ComponentType, ReactWrapper } from 'enzyme';
|
||||
import type { LensMultiTable } from '../../common';
|
||||
import { layerTypes } from '../../common';
|
||||
import type { LayerArgs } from '../../common/expressions';
|
||||
import type { DataLayerArgs } from '../../common/expressions';
|
||||
import { getLegendAction } from './get_legend_action';
|
||||
import { LegendActionPopover } from '../shared_components';
|
||||
|
||||
|
@ -27,7 +27,7 @@ const sampleLayer = {
|
|||
xScaleType: 'ordinal',
|
||||
yScaleType: 'linear',
|
||||
isHistogram: false,
|
||||
} as LayerArgs;
|
||||
} as DataLayerArgs;
|
||||
|
||||
const tables = {
|
||||
first: {
|
||||
|
|
|
@ -9,11 +9,11 @@ import React from 'react';
|
|||
import type { LegendAction, XYChartSeriesIdentifier } from '@elastic/charts';
|
||||
import type { LensFilterEvent } from '../types';
|
||||
import type { LensMultiTable, FormatFactory } from '../../common';
|
||||
import type { LayerArgs } from '../../common/expressions';
|
||||
import type { DataLayerArgs } from '../../common/expressions';
|
||||
import { LegendActionPopover } from '../shared_components';
|
||||
|
||||
export const getLegendAction = (
|
||||
filteredLayers: LayerArgs[],
|
||||
filteredLayers: DataLayerArgs[],
|
||||
tables: LensMultiTable['tables'],
|
||||
onFilter: (data: LensFilterEvent['data']) => void,
|
||||
formatFactory: FormatFactory,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { XYLayerConfig } from '../../common/expressions';
|
||||
import { XYDataLayerConfig } from '../../common/expressions';
|
||||
import { FramePublicAPI } from '../types';
|
||||
import { computeOverallDataDomain, getStaticValue } from './reference_line_helpers';
|
||||
|
||||
|
@ -51,7 +51,7 @@ describe('reference_line helpers', () => {
|
|||
// accessor id has no hit in data
|
||||
expect(
|
||||
getStaticValue(
|
||||
[{ layerId: 'id-a', seriesType: 'area' } as XYLayerConfig], // missing xAccessor for groupId == x
|
||||
[{ layerId: 'id-a', seriesType: 'area' } as XYDataLayerConfig], // missing xAccessor for groupId == x
|
||||
'x',
|
||||
{
|
||||
activeData: getActiveData([
|
||||
|
@ -69,7 +69,7 @@ describe('reference_line helpers', () => {
|
|||
seriesType: 'area',
|
||||
layerType: 'data',
|
||||
accessors: ['d'],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
], // missing hit of accessor "d" in data
|
||||
'yLeft',
|
||||
{
|
||||
|
@ -88,7 +88,7 @@ describe('reference_line helpers', () => {
|
|||
seriesType: 'area',
|
||||
layerType: 'data',
|
||||
accessors: ['a'],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
], // missing yConfig fallbacks to left axis, but the requested group is yRight
|
||||
'yRight',
|
||||
{
|
||||
|
@ -107,7 +107,7 @@ describe('reference_line helpers', () => {
|
|||
seriesType: 'area',
|
||||
layerType: 'data',
|
||||
accessors: ['a'],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
], // same as above with x groupId
|
||||
'x',
|
||||
{
|
||||
|
@ -130,7 +130,7 @@ describe('reference_line helpers', () => {
|
|||
layerType: 'data',
|
||||
accessors: ['a'],
|
||||
yConfig: [{ forAccessor: 'a', axisMode: 'right' }],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
'yRight',
|
||||
{
|
||||
|
@ -155,7 +155,7 @@ describe('reference_line helpers', () => {
|
|||
seriesType: 'area',
|
||||
layerType: 'data',
|
||||
accessors: ['a'],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
'yLeft',
|
||||
{
|
||||
|
@ -178,7 +178,7 @@ describe('reference_line helpers', () => {
|
|||
layerType: 'data',
|
||||
accessors: ['a'],
|
||||
yConfig: [{ forAccessor: 'a', axisMode: 'right' }],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
'yRight',
|
||||
{
|
||||
|
@ -205,7 +205,7 @@ describe('reference_line helpers', () => {
|
|||
seriesType: 'area',
|
||||
layerType: 'data',
|
||||
accessors: ['a', 'b'],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
'yLeft',
|
||||
{ activeData: tables },
|
||||
|
@ -220,7 +220,7 @@ describe('reference_line helpers', () => {
|
|||
seriesType: 'area',
|
||||
layerType: 'data',
|
||||
accessors: ['a', 'b'],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
'yRight',
|
||||
{ activeData: tables },
|
||||
|
@ -243,7 +243,7 @@ describe('reference_line helpers', () => {
|
|||
seriesType: 'area',
|
||||
layerType: 'data',
|
||||
accessors: ['a', 'b'],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
'yLeft',
|
||||
{ activeData: tables },
|
||||
|
@ -258,7 +258,7 @@ describe('reference_line helpers', () => {
|
|||
seriesType: 'area',
|
||||
layerType: 'data',
|
||||
accessors: ['a', 'b'],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
'yRight',
|
||||
{ activeData: tables },
|
||||
|
@ -277,7 +277,7 @@ describe('reference_line helpers', () => {
|
|||
layerType: 'data',
|
||||
xAccessor: 'a',
|
||||
accessors: [],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
'x', // this is influenced by the callback
|
||||
{
|
||||
|
@ -300,7 +300,7 @@ describe('reference_line helpers', () => {
|
|||
layerType: 'data',
|
||||
xAccessor: 'a',
|
||||
accessors: [],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
'x',
|
||||
{
|
||||
|
@ -324,7 +324,7 @@ describe('reference_line helpers', () => {
|
|||
for (const seriesType of ['bar_stacked', 'bar_horizontal_stacked', 'area_stacked'])
|
||||
expect(
|
||||
computeOverallDataDomain(
|
||||
[{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYLayerConfig],
|
||||
[{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig],
|
||||
['a', 'b', 'c'],
|
||||
getActiveData([
|
||||
{
|
||||
|
@ -350,7 +350,7 @@ describe('reference_line helpers', () => {
|
|||
])
|
||||
expect(
|
||||
computeOverallDataDomain(
|
||||
[{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYLayerConfig],
|
||||
[{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig],
|
||||
['a', 'b', 'c'],
|
||||
getActiveData([
|
||||
{
|
||||
|
@ -375,7 +375,7 @@ describe('reference_line helpers', () => {
|
|||
[
|
||||
{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] },
|
||||
{ layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] },
|
||||
] as XYLayerConfig[],
|
||||
] as XYDataLayerConfig[],
|
||||
['a', 'b', 'c', 'd', 'e', 'f'],
|
||||
getActiveData([
|
||||
{ id: 'id-a', rows: [{ a: 25, b: 100, c: 100 }] },
|
||||
|
@ -389,7 +389,7 @@ describe('reference_line helpers', () => {
|
|||
[
|
||||
{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] },
|
||||
{ layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] },
|
||||
] as XYLayerConfig[],
|
||||
] as XYDataLayerConfig[],
|
||||
['a', 'b', 'c', 'd', 'e', 'f'],
|
||||
getActiveData([
|
||||
{
|
||||
|
@ -425,7 +425,7 @@ describe('reference_line helpers', () => {
|
|||
[
|
||||
{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] },
|
||||
{ layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] },
|
||||
] as XYLayerConfig[],
|
||||
] as XYDataLayerConfig[],
|
||||
['a', 'b', 'c', 'd', 'e', 'f'],
|
||||
getActiveData([
|
||||
{ id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) },
|
||||
|
@ -443,7 +443,7 @@ describe('reference_line helpers', () => {
|
|||
[
|
||||
{ layerId: 'id-a', seriesType: nonStackedSeries, accessors: ['a', 'b', 'c'] },
|
||||
{ layerId: 'id-b', seriesType: stackedSeries, accessors: ['d', 'e', 'f'] },
|
||||
] as XYLayerConfig[],
|
||||
] as XYDataLayerConfig[],
|
||||
['a', 'b', 'c', 'd', 'e', 'f'],
|
||||
getActiveData([
|
||||
{ id: 'id-a', rows: [{ a: 100, b: 100, c: 100 }] },
|
||||
|
@ -465,7 +465,7 @@ describe('reference_line helpers', () => {
|
|||
[
|
||||
{ layerId: 'id-a', seriesType, xAccessor: 'c', accessors: ['a', 'b'] },
|
||||
{ layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] },
|
||||
] as XYLayerConfig[],
|
||||
] as XYDataLayerConfig[],
|
||||
['a', 'b', 'd', 'e'],
|
||||
getActiveData([
|
||||
{
|
||||
|
@ -492,7 +492,7 @@ describe('reference_line helpers', () => {
|
|||
[
|
||||
{ layerId: 'id-a', seriesType, accessors: ['c'] },
|
||||
{ layerId: 'id-b', seriesType, accessors: ['f'] },
|
||||
] as XYLayerConfig[],
|
||||
] as XYDataLayerConfig[],
|
||||
['c', 'f'],
|
||||
getActiveData([
|
||||
{
|
||||
|
@ -520,7 +520,7 @@ describe('reference_line helpers', () => {
|
|||
[
|
||||
{ layerId: 'id-a', seriesType, xAccessor: 'c', accessors: ['a', 'b'] },
|
||||
{ layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] },
|
||||
] as XYLayerConfig[],
|
||||
] as XYDataLayerConfig[],
|
||||
['a', 'b', 'd', 'e'],
|
||||
getActiveData([
|
||||
{
|
||||
|
@ -549,7 +549,7 @@ describe('reference_line helpers', () => {
|
|||
layerId: 'id-a',
|
||||
seriesType: 'area_stacked',
|
||||
accessors: ['a', 'b', 'c'],
|
||||
} as XYLayerConfig,
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
['a', 'b', 'c'],
|
||||
getActiveData([
|
||||
|
@ -568,7 +568,13 @@ describe('reference_line helpers', () => {
|
|||
).toEqual({ min: 0, max: 200 }); // it is stacked, so max is the sum and 0 is the fallback
|
||||
expect(
|
||||
computeOverallDataDomain(
|
||||
[{ layerId: 'id-a', seriesType: 'area', accessors: ['a', 'b', 'c'] } as XYLayerConfig],
|
||||
[
|
||||
{
|
||||
layerId: 'id-a',
|
||||
seriesType: 'area',
|
||||
accessors: ['a', 'b', 'c'],
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
['a', 'b', 'c'],
|
||||
getActiveData([
|
||||
{
|
||||
|
@ -602,7 +608,7 @@ describe('reference_line helpers', () => {
|
|||
[
|
||||
{ layerId: 'id-a', seriesType: 'area', accessors: ['a', 'b', 'c'] },
|
||||
{ layerId: 'id-b', seriesType: 'line', accessors: ['d', 'e', 'f'] },
|
||||
] as XYLayerConfig[],
|
||||
] as XYDataLayerConfig[],
|
||||
['a', 'b'],
|
||||
getActiveData([{ id: 'id-c', rows: [{ a: 100, b: 100 }] }]) // mind the layer id here
|
||||
)
|
||||
|
@ -613,7 +619,7 @@ describe('reference_line helpers', () => {
|
|||
[
|
||||
{ layerId: 'id-a', seriesType: 'bar', accessors: ['a', 'b', 'c'] },
|
||||
{ layerId: 'id-b', seriesType: 'bar_stacked' },
|
||||
] as XYLayerConfig[],
|
||||
] as XYDataLayerConfig[],
|
||||
['a', 'b'],
|
||||
getActiveData([])
|
||||
)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { groupBy, partition } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { layerTypes } from '../../common';
|
||||
import type { XYLayerConfig, YConfig } from '../../common/expressions';
|
||||
import type { XYDataLayerConfig, XYLayerConfig, YConfig } from '../../common/expressions';
|
||||
import { Datatable } from '../../../../../src/plugins/expressions/public';
|
||||
import type { AccessorConfig, DatasourcePublicAPI, FramePublicAPI, Visualization } from '../types';
|
||||
import { groupAxesByType } from './axes_configuration';
|
||||
|
@ -17,7 +17,7 @@ import type { XYState } from './types';
|
|||
import {
|
||||
checkScaleOperation,
|
||||
getAxisName,
|
||||
isDataLayer,
|
||||
getDataLayers,
|
||||
isNumericMetric,
|
||||
} from './visualization_helpers';
|
||||
import { generateId } from '../id_generator';
|
||||
|
@ -41,9 +41,7 @@ export function getGroupsToShow<T extends ReferenceLineBase & { config?: YConfig
|
|||
if (!state) {
|
||||
return [];
|
||||
}
|
||||
const dataLayers = state.layers.filter(({ layerType = layerTypes.DATA }) =>
|
||||
isDataLayer({ layerType })
|
||||
);
|
||||
const dataLayers = getDataLayers(state.layers);
|
||||
const groupsAvailable = getGroupsAvailableInData(dataLayers, datasourceLayers, tables);
|
||||
return referenceLayers
|
||||
.filter(({ label, config }: T) => groupsAvailable[label] || config?.length)
|
||||
|
@ -62,9 +60,7 @@ export function getGroupsRelatedToData<T extends ReferenceLineBase>(
|
|||
if (!state) {
|
||||
return [];
|
||||
}
|
||||
const dataLayers = state.layers.filter(({ layerType = layerTypes.DATA }) =>
|
||||
isDataLayer({ layerType })
|
||||
);
|
||||
const dataLayers = getDataLayers(state.layers);
|
||||
const groupsAvailable = getGroupsAvailableInData(dataLayers, datasourceLayers, tables);
|
||||
return referenceLayers.filter(({ label }: T) => groupsAvailable[label]);
|
||||
}
|
||||
|
@ -72,7 +68,7 @@ export function getGroupsRelatedToData<T extends ReferenceLineBase>(
|
|||
* Returns a dictionary with the groups filled in all the data layers
|
||||
*/
|
||||
export function getGroupsAvailableInData(
|
||||
dataLayers: XYState['layers'],
|
||||
dataLayers: XYDataLayerConfig[],
|
||||
datasourceLayers: Record<string, DatasourcePublicAPI>,
|
||||
tables: Record<string, Datatable> | undefined
|
||||
) {
|
||||
|
@ -88,10 +84,10 @@ export function getGroupsAvailableInData(
|
|||
}
|
||||
|
||||
export function getStaticValue(
|
||||
dataLayers: XYState['layers'],
|
||||
dataLayers: XYDataLayerConfig[],
|
||||
groupId: 'x' | 'yLeft' | 'yRight',
|
||||
{ activeData }: Pick<FramePublicAPI, 'activeData'>,
|
||||
layerHasNumberHistogram: (layer: XYLayerConfig) => boolean
|
||||
layerHasNumberHistogram: (layer: XYDataLayerConfig) => boolean
|
||||
) {
|
||||
const fallbackValue = 100;
|
||||
if (!activeData) {
|
||||
|
@ -124,7 +120,7 @@ export function getStaticValue(
|
|||
|
||||
function getAccessorCriteriaForGroup(
|
||||
groupId: 'x' | 'yLeft' | 'yRight',
|
||||
dataLayers: XYState['layers'],
|
||||
dataLayers: XYDataLayerConfig[],
|
||||
activeData: FramePublicAPI['activeData']
|
||||
) {
|
||||
switch (groupId) {
|
||||
|
@ -158,7 +154,7 @@ function getAccessorCriteriaForGroup(
|
|||
}
|
||||
|
||||
export function computeOverallDataDomain(
|
||||
dataLayers: Array<Pick<XYLayerConfig, 'seriesType' | 'accessors' | 'xAccessor' | 'layerId'>>,
|
||||
dataLayers: XYDataLayerConfig[],
|
||||
accessorIds: string[],
|
||||
activeData: NonNullable<FramePublicAPI['activeData']>,
|
||||
allowStacking: boolean = true
|
||||
|
@ -222,7 +218,7 @@ export function computeOverallDataDomain(
|
|||
}
|
||||
|
||||
function computeStaticValueForGroup(
|
||||
dataLayers: Array<Pick<XYLayerConfig, 'seriesType' | 'accessors' | 'xAccessor' | 'layerId'>>,
|
||||
dataLayers: XYDataLayerConfig[],
|
||||
accessorIds: string[],
|
||||
activeData: NonNullable<FramePublicAPI['activeData']>,
|
||||
minZeroOrNegativeBase: boolean = true,
|
||||
|
@ -275,8 +271,7 @@ export const getReferenceSupportedLayer = (
|
|||
frame?.datasourceLayers || {},
|
||||
frame?.activeData
|
||||
);
|
||||
const dataLayers =
|
||||
state?.layers.filter(({ layerType = layerTypes.DATA }) => isDataLayer({ layerType })) || [];
|
||||
const dataLayers = getDataLayers(state?.layers || []);
|
||||
const filledDataLayers = dataLayers.filter(
|
||||
({ accessors, xAccessor }) => accessors.length || xAccessor
|
||||
);
|
||||
|
|
|
@ -9,6 +9,7 @@ import { EuiIconType } from '@elastic/eui/src/components/icon/icon';
|
|||
import type { FramePublicAPI, DatasourcePublicAPI } from '../types';
|
||||
import type { SeriesType, XYLayerConfig, YConfig, ValidLayer } from '../../common/expressions';
|
||||
import { visualizationTypes } from './types';
|
||||
import { getDataLayers, isDataLayer } from './visualization_helpers';
|
||||
|
||||
export function isHorizontalSeries(seriesType: SeriesType) {
|
||||
return (
|
||||
|
@ -30,8 +31,8 @@ export function isStackedChart(seriesType: SeriesType) {
|
|||
return seriesType.includes('stacked');
|
||||
}
|
||||
|
||||
export function isHorizontalChart(layers: Array<{ seriesType: SeriesType }>) {
|
||||
return layers.every((l) => isHorizontalSeries(l.seriesType));
|
||||
export function isHorizontalChart(layers: XYLayerConfig[]) {
|
||||
return getDataLayers(layers).every((l) => isHorizontalSeries(l.seriesType));
|
||||
}
|
||||
|
||||
export function getIconForSeries(type: SeriesType): EuiIconType {
|
||||
|
@ -45,7 +46,7 @@ export function getIconForSeries(type: SeriesType): EuiIconType {
|
|||
}
|
||||
|
||||
export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => {
|
||||
if (layer.splitAccessor) {
|
||||
if (isDataLayer(layer) && layer.splitAccessor) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
|
@ -55,13 +56,14 @@ export const getSeriesColor = (layer: XYLayerConfig, accessor: string) => {
|
|||
|
||||
export const getColumnToLabelMap = (layer: XYLayerConfig, datasource: DatasourcePublicAPI) => {
|
||||
const columnToLabel: Record<string, string> = {};
|
||||
|
||||
layer.accessors.concat(layer.splitAccessor ? [layer.splitAccessor] : []).forEach((accessor) => {
|
||||
const operation = datasource.getOperationForColumnId(accessor);
|
||||
if (operation?.label) {
|
||||
columnToLabel[accessor] = operation.label;
|
||||
}
|
||||
});
|
||||
layer.accessors
|
||||
.concat(isDataLayer(layer) && layer.splitAccessor ? [layer.splitAccessor] : [])
|
||||
.forEach((accessor) => {
|
||||
const operation = datasource.getOperationForColumnId(accessor);
|
||||
if (operation?.label) {
|
||||
columnToLabel[accessor] = operation.label;
|
||||
}
|
||||
});
|
||||
return columnToLabel;
|
||||
};
|
||||
|
||||
|
|
|
@ -343,9 +343,6 @@ describe('#toExpression', () => {
|
|||
{
|
||||
layerId: 'referenceLine',
|
||||
layerType: layerTypes.REFERENCELINE,
|
||||
seriesType: 'area',
|
||||
splitAccessor: 'd',
|
||||
xAccessor: 'a',
|
||||
accessors: ['b', 'c'],
|
||||
yConfig: [{ forAccessor: 'a' }],
|
||||
},
|
||||
|
|
|
@ -11,12 +11,17 @@ import { PaletteRegistry } from 'src/plugins/charts/public';
|
|||
import { State } from './types';
|
||||
import { OperationMetadata, DatasourcePublicAPI } from '../types';
|
||||
import { getColumnToLabelMap } from './state_helpers';
|
||||
import type { ValidLayer, XYLayerConfig } from '../../common/expressions';
|
||||
import type {
|
||||
ValidLayer,
|
||||
XYLayerConfig,
|
||||
XYReferenceLineLayerConfig,
|
||||
YConfig,
|
||||
} from '../../common/expressions';
|
||||
import { layerTypes } from '../../common';
|
||||
import { hasIcon } from './xy_config_panel/shared/icon_select';
|
||||
import { defaultReferenceLineColor } from './color_assignment';
|
||||
import { getDefaultVisualValuesForLayer } from '../shared_components/datasource_default_values';
|
||||
import { isDataLayer, isReferenceLayer } from './visualization_helpers';
|
||||
import { isDataLayer } from './visualization_helpers';
|
||||
|
||||
export const getSortedAccessors = (datasource: DatasourcePublicAPI, layer: XYLayerConfig) => {
|
||||
const originalOrder = datasource
|
||||
|
@ -299,102 +304,142 @@ export const buildExpression = (
|
|||
hideEndzones: [state?.hideEndzones || false],
|
||||
valuesInLegend: [state?.valuesInLegend || false],
|
||||
layers: validLayers.map((layer) => {
|
||||
const columnToLabel = getColumnToLabelMap(layer, datasourceLayers[layer.layerId]);
|
||||
|
||||
const xAxisOperation =
|
||||
datasourceLayers &&
|
||||
datasourceLayers[layer.layerId].getOperationForColumnId(layer.xAccessor);
|
||||
|
||||
const isHistogramDimension = Boolean(
|
||||
xAxisOperation &&
|
||||
xAxisOperation.isBucketed &&
|
||||
xAxisOperation.scale &&
|
||||
xAxisOperation.scale !== 'ordinal'
|
||||
if (isDataLayer(layer)) {
|
||||
return dataLayerToExpression(
|
||||
layer,
|
||||
datasourceLayers[layer.layerId],
|
||||
metadata,
|
||||
paletteService
|
||||
);
|
||||
}
|
||||
return referenceLineLayerToExpression(
|
||||
layer,
|
||||
datasourceLayers[(layer as XYReferenceLineLayerConfig).layerId]
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'lens_xy_layer',
|
||||
arguments: {
|
||||
layerId: [layer.layerId],
|
||||
|
||||
hide: [Boolean(layer.hide)],
|
||||
|
||||
xAccessor: layer.xAccessor ? [layer.xAccessor] : [],
|
||||
yScaleType: [
|
||||
getScaleType(metadata[layer.layerId][layer.accessors[0]], ScaleType.Ordinal),
|
||||
],
|
||||
xScaleType: [
|
||||
getScaleType(metadata[layer.layerId][layer.xAccessor], ScaleType.Linear),
|
||||
],
|
||||
isHistogram: [isHistogramDimension],
|
||||
splitAccessor: layer.splitAccessor ? [layer.splitAccessor] : [],
|
||||
yConfig: layer.yConfig
|
||||
? layer.yConfig.map((yConfig) => ({
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'lens_xy_yConfig',
|
||||
arguments: {
|
||||
forAccessor: [yConfig.forAccessor],
|
||||
axisMode: yConfig.axisMode ? [yConfig.axisMode] : [],
|
||||
color: isReferenceLayer(layer)
|
||||
? [yConfig.color || defaultReferenceLineColor]
|
||||
: yConfig.color
|
||||
? [yConfig.color]
|
||||
: [],
|
||||
lineStyle: [yConfig.lineStyle || 'solid'],
|
||||
lineWidth: [yConfig.lineWidth || 1],
|
||||
fill: [yConfig.fill || 'none'],
|
||||
icon: hasIcon(yConfig.icon) ? [yConfig.icon] : [],
|
||||
iconPosition:
|
||||
hasIcon(yConfig.icon) || yConfig.textVisibility
|
||||
? [yConfig.iconPosition || 'auto']
|
||||
: ['auto'],
|
||||
textVisibility: [yConfig.textVisibility || false],
|
||||
},
|
||||
},
|
||||
],
|
||||
}))
|
||||
: [],
|
||||
seriesType: [layer.seriesType],
|
||||
layerType: [layer.layerType || layerTypes.DATA],
|
||||
accessors: layer.accessors,
|
||||
columnToLabel: [JSON.stringify(columnToLabel)],
|
||||
...(layer.palette
|
||||
? {
|
||||
palette: [
|
||||
{
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'theme',
|
||||
arguments: {
|
||||
variable: ['palette'],
|
||||
default: [
|
||||
paletteService
|
||||
.get(layer.palette.name)
|
||||
.toExpression(layer.palette.params),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const referenceLineLayerToExpression = (
|
||||
layer: XYReferenceLineLayerConfig,
|
||||
datasourceLayer: DatasourcePublicAPI
|
||||
): Ast => {
|
||||
return {
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'lens_xy_referenceLine_layer',
|
||||
arguments: {
|
||||
layerId: [layer.layerId],
|
||||
yConfig: layer.yConfig
|
||||
? layer.yConfig.map((yConfig) =>
|
||||
yConfigToExpression(yConfig, defaultReferenceLineColor)
|
||||
)
|
||||
: [],
|
||||
layerType: [layerTypes.REFERENCELINE],
|
||||
accessors: layer.accessors,
|
||||
columnToLabel: [JSON.stringify(getColumnToLabelMap(layer, datasourceLayer))],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const dataLayerToExpression = (
|
||||
layer: ValidLayer,
|
||||
datasourceLayer: DatasourcePublicAPI,
|
||||
metadata: Record<string, Record<string, OperationMetadata | null>>,
|
||||
paletteService: PaletteRegistry
|
||||
): Ast => {
|
||||
const columnToLabel = getColumnToLabelMap(layer, datasourceLayer);
|
||||
|
||||
const xAxisOperation = datasourceLayer?.getOperationForColumnId(layer.xAccessor);
|
||||
|
||||
const isHistogramDimension = Boolean(
|
||||
xAxisOperation &&
|
||||
xAxisOperation.isBucketed &&
|
||||
xAxisOperation.scale &&
|
||||
xAxisOperation.scale !== 'ordinal'
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'lens_xy_data_layer',
|
||||
arguments: {
|
||||
layerId: [layer.layerId],
|
||||
hide: [Boolean(layer.hide)],
|
||||
xAccessor: layer.xAccessor ? [layer.xAccessor] : [],
|
||||
yScaleType: [
|
||||
getScaleType(metadata[layer.layerId][layer.accessors[0]], ScaleType.Ordinal),
|
||||
],
|
||||
xScaleType: [getScaleType(metadata[layer.layerId][layer.xAccessor], ScaleType.Linear)],
|
||||
isHistogram: [isHistogramDimension],
|
||||
splitAccessor: layer.splitAccessor ? [layer.splitAccessor] : [],
|
||||
yConfig: layer.yConfig
|
||||
? layer.yConfig.map((yConfig) => yConfigToExpression(yConfig))
|
||||
: [],
|
||||
seriesType: [layer.seriesType],
|
||||
layerType: [layerTypes.DATA],
|
||||
accessors: layer.accessors,
|
||||
columnToLabel: [JSON.stringify(columnToLabel)],
|
||||
...(layer.palette
|
||||
? {
|
||||
palette: [
|
||||
{
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'theme',
|
||||
arguments: {
|
||||
variable: ['palette'],
|
||||
default: [
|
||||
paletteService
|
||||
.get(layer.palette.name)
|
||||
.toExpression(layer.palette.params),
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const yConfigToExpression = (yConfig: YConfig, defaultColor?: string): Ast => {
|
||||
return {
|
||||
type: 'expression',
|
||||
chain: [
|
||||
{
|
||||
type: 'function',
|
||||
function: 'lens_xy_yConfig',
|
||||
arguments: {
|
||||
forAccessor: [yConfig.forAccessor],
|
||||
axisMode: yConfig.axisMode ? [yConfig.axisMode] : [],
|
||||
color: yConfig.color ? [yConfig.color] : defaultColor ? [defaultColor] : [],
|
||||
lineStyle: [yConfig.lineStyle || 'solid'],
|
||||
lineWidth: [yConfig.lineWidth || 1],
|
||||
fill: [yConfig.fill || 'none'],
|
||||
icon: hasIcon(yConfig.icon) ? [yConfig.icon] : [],
|
||||
iconPosition:
|
||||
hasIcon(yConfig.icon) || yConfig.textVisibility
|
||||
? [yConfig.iconPosition || 'auto']
|
||||
: ['auto'],
|
||||
textVisibility: [yConfig.textVisibility || false],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@ import { getXyVisualization } from './visualization';
|
|||
import { Position } from '@elastic/charts';
|
||||
import { Operation, VisualizeEditorContext, Suggestion } from '../types';
|
||||
import type { State, XYSuggestion } from './types';
|
||||
import type { SeriesType, XYLayerConfig } from '../../common/expressions';
|
||||
import type { SeriesType, XYDataLayerConfig, XYLayerConfig } from '../../common/expressions';
|
||||
import { layerTypes } from '../../common';
|
||||
import { createMockDatasource, createMockFramePublicAPI } from '../mocks';
|
||||
import { LensIconChartBar } from '../assets/chart_bar';
|
||||
|
@ -32,7 +32,7 @@ function exampleState(): State {
|
|||
splitAccessor: 'd',
|
||||
xAccessor: 'a',
|
||||
accessors: ['b', 'c'],
|
||||
},
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ describe('xy_visualization', () => {
|
|||
return {
|
||||
...state,
|
||||
layers: types.map((t, i) => ({
|
||||
...state.layers[0],
|
||||
...(state.layers[0] as XYDataLayerConfig),
|
||||
layerId: `layer_${i}`,
|
||||
seriesType: t,
|
||||
})),
|
||||
|
@ -143,7 +143,7 @@ describe('xy_visualization', () => {
|
|||
const initialState = xyVisualization.initialize(() => 'l1');
|
||||
|
||||
expect(initialState.layers).toHaveLength(1);
|
||||
expect(initialState.layers[0].xAccessor).not.toBeDefined();
|
||||
expect((initialState.layers[0] as XYDataLayerConfig).xAccessor).not.toBeDefined();
|
||||
expect(initialState.layers[0].accessors).toHaveLength(0);
|
||||
|
||||
expect(initialState).toMatchInlineSnapshot(`
|
||||
|
@ -333,7 +333,6 @@ describe('xy_visualization', () => {
|
|||
{
|
||||
layerId: 'referenceLine',
|
||||
layerType: layerTypes.REFERENCELINE,
|
||||
seriesType: 'line',
|
||||
accessors: [],
|
||||
},
|
||||
],
|
||||
|
@ -345,7 +344,6 @@ describe('xy_visualization', () => {
|
|||
).toEqual({
|
||||
layerId: 'referenceLine',
|
||||
layerType: layerTypes.REFERENCELINE,
|
||||
seriesType: 'line',
|
||||
accessors: ['newCol'],
|
||||
yConfig: [
|
||||
{
|
||||
|
@ -432,7 +430,7 @@ describe('xy_visualization', () => {
|
|||
},
|
||||
]);
|
||||
|
||||
expect(state?.layers[0].palette).toStrictEqual({
|
||||
expect((state?.layers[0] as XYDataLayerConfig).palette).toStrictEqual({
|
||||
name: 'temperature',
|
||||
type: 'palette',
|
||||
});
|
||||
|
@ -787,7 +785,11 @@ describe('xy_visualization', () => {
|
|||
state: {
|
||||
...baseState,
|
||||
layers: [
|
||||
{ ...baseState.layers[0], accessors: ['a'], seriesType: 'bar_percentage_stacked' },
|
||||
{
|
||||
...baseState.layers[0],
|
||||
accessors: ['a'],
|
||||
seriesType: 'bar_percentage_stacked',
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
},
|
||||
frame,
|
||||
|
@ -1010,7 +1012,6 @@ describe('xy_visualization', () => {
|
|||
{
|
||||
layerId: 'referenceLine',
|
||||
layerType: layerTypes.REFERENCELINE,
|
||||
seriesType: 'line',
|
||||
accessors: [],
|
||||
yConfig: [{ axisMode: 'left', forAccessor: 'a' }],
|
||||
},
|
||||
|
@ -1073,8 +1074,8 @@ describe('xy_visualization', () => {
|
|||
|
||||
it('should compute no groups for referenceLines when the only data accessor available is a date histogram', () => {
|
||||
const state = getStateWithBaseReferenceLine();
|
||||
state.layers[0].xAccessor = 'b';
|
||||
state.layers[0].accessors = [];
|
||||
(state.layers[0] as XYDataLayerConfig).xAccessor = 'b';
|
||||
(state.layers[0] as XYDataLayerConfig).accessors = [];
|
||||
state.layers[1].yConfig = []; // empty the configuration
|
||||
// set the xAccessor as date_histogram
|
||||
frame.datasourceLayers.referenceLine.getOperationForColumnId = jest.fn((accessor) => {
|
||||
|
@ -1100,8 +1101,8 @@ describe('xy_visualization', () => {
|
|||
|
||||
it('should mark horizontal group is invalid when xAccessor is changed to a date histogram', () => {
|
||||
const state = getStateWithBaseReferenceLine();
|
||||
state.layers[0].xAccessor = 'b';
|
||||
state.layers[0].accessors = [];
|
||||
(state.layers[0] as XYDataLayerConfig).xAccessor = 'b';
|
||||
(state.layers[0] as XYDataLayerConfig).accessors = [];
|
||||
state.layers[1].yConfig![0].axisMode = 'bottom';
|
||||
// set the xAccessor as date_histogram
|
||||
frame.datasourceLayers.referenceLine.getOperationForColumnId = jest.fn((accessor) => {
|
||||
|
@ -1132,10 +1133,10 @@ describe('xy_visualization', () => {
|
|||
|
||||
it('should return groups in a specific order (left, right, bottom)', () => {
|
||||
const state = getStateWithBaseReferenceLine();
|
||||
state.layers[0].xAccessor = 'c';
|
||||
state.layers[0].accessors = ['a', 'b'];
|
||||
(state.layers[0] as XYDataLayerConfig).xAccessor = 'c';
|
||||
(state.layers[0] as XYDataLayerConfig).accessors = ['a', 'b'];
|
||||
// invert them on purpose
|
||||
state.layers[0].yConfig = [
|
||||
(state.layers[0] as XYDataLayerConfig).yConfig = [
|
||||
{ axisMode: 'right', forAccessor: 'b' },
|
||||
{ axisMode: 'left', forAccessor: 'a' },
|
||||
];
|
||||
|
@ -1170,8 +1171,8 @@ describe('xy_visualization', () => {
|
|||
|
||||
it('should ignore terms operation for xAccessor', () => {
|
||||
const state = getStateWithBaseReferenceLine();
|
||||
state.layers[0].xAccessor = 'b';
|
||||
state.layers[0].accessors = [];
|
||||
(state.layers[0] as XYDataLayerConfig).xAccessor = 'b';
|
||||
(state.layers[0] as XYDataLayerConfig).accessors = [];
|
||||
state.layers[1].yConfig = []; // empty the configuration
|
||||
// set the xAccessor as top values
|
||||
frame.datasourceLayers.referenceLine.getOperationForColumnId = jest.fn((accessor) => {
|
||||
|
@ -1197,8 +1198,8 @@ describe('xy_visualization', () => {
|
|||
|
||||
it('should mark horizontal group is invalid when accessor is changed to a terms operation', () => {
|
||||
const state = getStateWithBaseReferenceLine();
|
||||
state.layers[0].xAccessor = 'b';
|
||||
state.layers[0].accessors = [];
|
||||
(state.layers[0] as XYDataLayerConfig).xAccessor = 'b';
|
||||
(state.layers[0] as XYDataLayerConfig).accessors = [];
|
||||
state.layers[1].yConfig![0].axisMode = 'bottom';
|
||||
// set the xAccessor as date_histogram
|
||||
frame.datasourceLayers.referenceLine.getOperationForColumnId = jest.fn((accessor) => {
|
||||
|
@ -1262,7 +1263,7 @@ describe('xy_visualization', () => {
|
|||
};
|
||||
|
||||
const state = getStateWithBaseReferenceLine();
|
||||
state.layers[0].accessors = ['yAccessorId', 'yAccessorId2'];
|
||||
(state.layers[0] as XYDataLayerConfig).accessors = ['yAccessorId', 'yAccessorId2'];
|
||||
state.layers[1].yConfig = []; // empty the configuration
|
||||
|
||||
const options = xyVisualization.getConfiguration({
|
||||
|
@ -1282,8 +1283,12 @@ describe('xy_visualization', () => {
|
|||
it('should be excluded and not crash when a custom palette is used for data layer', () => {
|
||||
const state = getStateWithBaseReferenceLine();
|
||||
// now add a breakdown on the data layer with a custom palette
|
||||
state.layers[0].palette = { type: 'palette', name: 'custom', params: {} };
|
||||
state.layers[0].splitAccessor = 'd';
|
||||
(state.layers[0] as XYDataLayerConfig).palette = {
|
||||
type: 'palette',
|
||||
name: 'custom',
|
||||
params: {},
|
||||
};
|
||||
(state.layers[0] as XYDataLayerConfig).splitAccessor = 'd';
|
||||
|
||||
const options = xyVisualization.getConfiguration({
|
||||
state,
|
||||
|
@ -1306,7 +1311,7 @@ describe('xy_visualization', () => {
|
|||
...baseState.layers[0],
|
||||
splitAccessor: undefined,
|
||||
...layerConfigOverride,
|
||||
},
|
||||
} as XYDataLayerConfig,
|
||||
],
|
||||
},
|
||||
frame,
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { groupBy, uniq } from 'lodash';
|
||||
import { render } from 'react-dom';
|
||||
import { Position } from '@elastic/charts';
|
||||
import { FormattedMessage, I18nProvider } from '@kbn/i18n-react';
|
||||
|
@ -22,7 +21,7 @@ import { DimensionEditor } from './xy_config_panel/dimension_editor';
|
|||
import { LayerHeader } from './xy_config_panel/layer_header';
|
||||
import type { Visualization, AccessorConfig, FramePublicAPI } from '../types';
|
||||
import { State, visualizationTypes, XYSuggestion } from './types';
|
||||
import { SeriesType, XYLayerConfig, YAxisMode } from '../../common/expressions';
|
||||
import { SeriesType, XYDataLayerConfig, XYLayerConfig, YAxisMode } from '../../common/expressions';
|
||||
import { layerTypes } from '../../common';
|
||||
import { isHorizontalChart } from './state_helpers';
|
||||
import { toExpression, toPreviewExpression, getSortedAccessors } from './to_expression';
|
||||
|
@ -38,7 +37,9 @@ import {
|
|||
checkXAccessorCompatibility,
|
||||
defaultSeriesType,
|
||||
getAxisName,
|
||||
getDataLayers,
|
||||
getDescription,
|
||||
getFirstDataLayer,
|
||||
getLayersByType,
|
||||
getVisualizationType,
|
||||
isBucketed,
|
||||
|
@ -87,16 +88,12 @@ export const getXyVisualization = ({
|
|||
},
|
||||
|
||||
appendLayer(state, layerId, layerType) {
|
||||
const usedSeriesTypes = uniq(state.layers.map((layer) => layer.seriesType));
|
||||
const firstUsedSeriesType = getDataLayers(state.layers)?.[0]?.seriesType;
|
||||
return {
|
||||
...state,
|
||||
layers: [
|
||||
...state.layers,
|
||||
newLayerState(
|
||||
usedSeriesTypes.length === 1 ? usedSeriesTypes[0] : state.preferredSeriesType,
|
||||
layerId,
|
||||
layerType
|
||||
),
|
||||
newLayerState(firstUsedSeriesType || state.preferredSeriesType, layerId, layerType),
|
||||
],
|
||||
};
|
||||
},
|
||||
|
@ -175,6 +172,7 @@ export const getXyVisualization = ({
|
|||
if (isReferenceLayer(layer)) {
|
||||
return getReferenceConfiguration({ state, frame, layer, sortedAccessors, mappedAccessors });
|
||||
}
|
||||
const dataLayers = getDataLayers(state.layers);
|
||||
|
||||
const isHorizontal = isHorizontalChart(state.layers);
|
||||
const { left, right } = groupAxesByType([layer], frame.activeData);
|
||||
|
@ -185,24 +183,21 @@ export const getXyVisualization = ({
|
|||
(right.length && right.length < 2)
|
||||
);
|
||||
// Check also for multiple layers that can stack for percentage charts
|
||||
// Make sure that if multiple dimensions are defined for a single layer, they should belong to the same axis
|
||||
// Make sure that if multiple dimensions are defined for a single dataLayer, they should belong to the same axis
|
||||
const hasOnlyOneAccessor =
|
||||
layerHasOnlyOneAccessor &&
|
||||
getLayersByType(state, layerTypes.DATA).filter(
|
||||
dataLayers.filter(
|
||||
// check that the other layers are compatible with this one
|
||||
(dataLayer) => {
|
||||
(l) => {
|
||||
if (
|
||||
dataLayer.seriesType === layer.seriesType &&
|
||||
Boolean(dataLayer.xAccessor) === Boolean(layer.xAccessor) &&
|
||||
Boolean(dataLayer.splitAccessor) === Boolean(layer.splitAccessor)
|
||||
l.seriesType === layer.seriesType &&
|
||||
Boolean(l.xAccessor) === Boolean(layer.xAccessor) &&
|
||||
Boolean(l.splitAccessor) === Boolean(layer.splitAccessor)
|
||||
) {
|
||||
const { left: localLeft, right: localRight } = groupAxesByType(
|
||||
[dataLayer],
|
||||
frame.activeData
|
||||
);
|
||||
const { left: localLeft, right: localRight } = groupAxesByType([l], frame.activeData);
|
||||
// return true only if matching axis are found
|
||||
return (
|
||||
dataLayer.accessors.length &&
|
||||
l.accessors.length &&
|
||||
(Boolean(localLeft.length) === Boolean(left.length) ||
|
||||
Boolean(localRight.length) === Boolean(right.length))
|
||||
);
|
||||
|
@ -259,7 +254,7 @@ export const getXyVisualization = ({
|
|||
|
||||
getMainPalette: (state) => {
|
||||
if (!state || state.layers.length === 0) return;
|
||||
return state.layers[0].palette;
|
||||
return getFirstDataLayer(state.layers)?.palette;
|
||||
},
|
||||
|
||||
setDimension(props) {
|
||||
|
@ -371,14 +366,18 @@ export const getXyVisualization = ({
|
|||
if (!foundLayer) {
|
||||
return prevState;
|
||||
}
|
||||
const dataLayers = getDataLayers(prevState.layers);
|
||||
const newLayer = { ...foundLayer };
|
||||
if (newLayer.xAccessor === columnId) {
|
||||
delete newLayer.xAccessor;
|
||||
} else if (newLayer.splitAccessor === columnId) {
|
||||
delete newLayer.splitAccessor;
|
||||
// as the palette is associated with the break down by dimension, remove it together with the dimension
|
||||
delete newLayer.palette;
|
||||
} else if (newLayer.accessors.includes(columnId)) {
|
||||
if (isDataLayer(newLayer)) {
|
||||
if (newLayer.xAccessor === columnId) {
|
||||
delete newLayer.xAccessor;
|
||||
} else if (newLayer.splitAccessor === columnId) {
|
||||
delete newLayer.splitAccessor;
|
||||
// as the palette is associated with the break down by dimension, remove it together with the dimension
|
||||
delete newLayer.palette;
|
||||
}
|
||||
}
|
||||
if (newLayer.accessors.includes(columnId)) {
|
||||
newLayer.accessors = newLayer.accessors.filter((a) => a !== columnId);
|
||||
}
|
||||
|
||||
|
@ -388,10 +387,9 @@ export const getXyVisualization = ({
|
|||
|
||||
let newLayers = prevState.layers.map((l) => (l.layerId === layerId ? newLayer : l));
|
||||
// check if there's any reference layer and pull it off if all data layers have no dimensions set
|
||||
const layersByType = groupBy(newLayers, ({ layerType }) => layerType);
|
||||
// check for data layers if they all still have xAccessors
|
||||
const groupsAvailable = getGroupsAvailableInData(
|
||||
layersByType[layerTypes.DATA],
|
||||
dataLayers,
|
||||
frame.datasourceLayers,
|
||||
frame?.activeData
|
||||
);
|
||||
|
@ -453,9 +451,11 @@ export const getXyVisualization = ({
|
|||
|
||||
getErrorMessages(state, datasourceLayers) {
|
||||
// Data error handling below here
|
||||
const hasNoAccessors = ({ accessors }: XYLayerConfig) =>
|
||||
const hasNoAccessors = ({ accessors }: XYDataLayerConfig) =>
|
||||
accessors == null || accessors.length === 0;
|
||||
const hasNoSplitAccessor = ({ splitAccessor, seriesType }: XYLayerConfig) =>
|
||||
|
||||
const dataLayers = getDataLayers(state.layers);
|
||||
const hasNoSplitAccessor = ({ splitAccessor, seriesType }: XYDataLayerConfig) =>
|
||||
seriesType.includes('percentage') && splitAccessor == null;
|
||||
|
||||
const errors: Array<{
|
||||
|
@ -466,16 +466,15 @@ export const getXyVisualization = ({
|
|||
// check if the layers in the state are compatible with this type of chart
|
||||
if (state && state.layers.length > 1) {
|
||||
// Order is important here: Y Axis is fundamental to exist to make it valid
|
||||
const checks: Array<[string, (layer: XYLayerConfig) => boolean]> = [
|
||||
const checks: Array<[string, (layer: XYDataLayerConfig) => boolean]> = [
|
||||
['Y', hasNoAccessors],
|
||||
['Break down', hasNoSplitAccessor],
|
||||
];
|
||||
|
||||
// filter out those layers with no accessors at all
|
||||
const filteredLayers = state.layers.filter(
|
||||
({ accessors, xAccessor, splitAccessor, layerType }: XYLayerConfig) =>
|
||||
isDataLayer({ layerType }) &&
|
||||
(accessors.length > 0 || xAccessor != null || splitAccessor != null)
|
||||
const filteredLayers = dataLayers.filter(
|
||||
({ accessors, xAccessor, splitAccessor, layerType }) =>
|
||||
accessors.length > 0 || xAccessor != null || splitAccessor != null
|
||||
);
|
||||
for (const [dimension, criteria] of checks) {
|
||||
const result = validateLayersForDimension(dimension, filteredLayers, criteria);
|
||||
|
|
|
@ -10,7 +10,12 @@ import { uniq } from 'lodash';
|
|||
import { DatasourcePublicAPI, OperationMetadata, VisualizationType } from '../types';
|
||||
import { State, visualizationTypes, XYState } from './types';
|
||||
import { isHorizontalChart } from './state_helpers';
|
||||
import { SeriesType, XYLayerConfig } from '../../common/expressions';
|
||||
import {
|
||||
SeriesType,
|
||||
XYDataLayerConfig,
|
||||
XYLayerConfig,
|
||||
XYReferenceLineLayerConfig,
|
||||
} from '../../common/expressions';
|
||||
import { layerTypes } from '..';
|
||||
import { LensIconChartBarHorizontal } from '../assets/chart_bar_horizontal';
|
||||
import { LensIconChartMixedXy } from '../assets/chart_mixed_xy';
|
||||
|
@ -59,14 +64,15 @@ export function checkXAccessorCompatibility(
|
|||
state: XYState,
|
||||
datasourceLayers: Record<string, DatasourcePublicAPI>
|
||||
) {
|
||||
const dataLayers = getDataLayers(state.layers);
|
||||
const errors = [];
|
||||
const hasDateHistogramSet = state.layers.some(
|
||||
const hasDateHistogramSet = dataLayers.some(
|
||||
checkScaleOperation('interval', 'date', datasourceLayers)
|
||||
);
|
||||
const hasNumberHistogram = state.layers.some(
|
||||
const hasNumberHistogram = dataLayers.some(
|
||||
checkScaleOperation('interval', 'number', datasourceLayers)
|
||||
);
|
||||
const hasOrdinalAxis = state.layers.some(
|
||||
const hasOrdinalAxis = dataLayers.some(
|
||||
checkScaleOperation('ordinal', undefined, datasourceLayers)
|
||||
);
|
||||
if (state.layers.length > 1 && hasDateHistogramSet && hasNumberHistogram) {
|
||||
|
@ -109,7 +115,7 @@ export function checkScaleOperation(
|
|||
dataType: 'date' | 'number' | 'string' | undefined,
|
||||
datasourceLayers: Record<string, DatasourcePublicAPI>
|
||||
) {
|
||||
return (layer: XYLayerConfig) => {
|
||||
return (layer: XYDataLayerConfig) => {
|
||||
const datasourceAPI = datasourceLayers[layer.layerId];
|
||||
if (!layer.xAccessor) {
|
||||
return false;
|
||||
|
@ -121,11 +127,20 @@ export function checkScaleOperation(
|
|||
};
|
||||
}
|
||||
|
||||
export const isDataLayer = (layer: Pick<XYLayerConfig, 'layerType'>) =>
|
||||
layer.layerType === layerTypes.DATA;
|
||||
export const isDataLayer = (layer: Pick<XYLayerConfig, 'layerType'>): layer is XYDataLayerConfig =>
|
||||
layer.layerType === layerTypes.DATA || !layer.layerType;
|
||||
|
||||
export const isReferenceLayer = (layer: Pick<XYLayerConfig, 'layerType'>) =>
|
||||
layer?.layerType === layerTypes.REFERENCELINE;
|
||||
export const getDataLayers = (layers: XYLayerConfig[]) =>
|
||||
(layers || []).filter((layer): layer is XYDataLayerConfig => isDataLayer(layer));
|
||||
|
||||
export const getFirstDataLayer = (layers: XYLayerConfig[]) =>
|
||||
(layers || []).find((layer): layer is XYDataLayerConfig => isDataLayer(layer));
|
||||
|
||||
export const isReferenceLayer = (layer: XYLayerConfig): layer is XYReferenceLineLayerConfig =>
|
||||
layer.layerType === layerTypes.REFERENCELINE;
|
||||
|
||||
export const getReferenceLayers = (layers: XYLayerConfig[]) =>
|
||||
(layers || []).filter((layer): layer is XYReferenceLineLayerConfig => isReferenceLayer(layer));
|
||||
|
||||
export function getVisualizationType(state: State): VisualizationType | 'mixed' {
|
||||
if (!state.layers.length) {
|
||||
|
@ -133,8 +148,9 @@ export function getVisualizationType(state: State): VisualizationType | 'mixed'
|
|||
visualizationTypes.find((t) => t.id === state.preferredSeriesType) ?? visualizationTypes[0]
|
||||
);
|
||||
}
|
||||
const visualizationType = visualizationTypes.find((t) => t.id === state.layers[0].seriesType);
|
||||
const seriesTypes = uniq(state.layers.map((l) => l.seriesType));
|
||||
const dataLayers = getDataLayers(state?.layers);
|
||||
const visualizationType = visualizationTypes.find((t) => t.id === dataLayers?.[0].seriesType);
|
||||
const seriesTypes = uniq(dataLayers.map((l) => l.seriesType));
|
||||
|
||||
return visualizationType && seriesTypes.length === 1 ? visualizationType : 'mixed';
|
||||
}
|
||||
|
@ -241,8 +257,8 @@ export function getLayersByType(state: State, byType?: string) {
|
|||
|
||||
export function validateLayersForDimension(
|
||||
dimension: string,
|
||||
layers: XYLayerConfig[],
|
||||
missingCriteria: (layer: XYLayerConfig) => boolean
|
||||
layers: XYDataLayerConfig[],
|
||||
missingCriteria: (layer: XYDataLayerConfig) => boolean
|
||||
):
|
||||
| { valid: true }
|
||||
| {
|
||||
|
|
|
@ -10,7 +10,7 @@ import React from 'react';
|
|||
import moment from 'moment';
|
||||
import { Endzones } from '../../../../../src/plugins/charts/public';
|
||||
import type { LensMultiTable } from '../../common';
|
||||
import type { LayerArgs } from '../../common/expressions';
|
||||
import type { DataLayerArgs } from '../../common/expressions';
|
||||
import { search } from '../../../../../src/plugins/data/public';
|
||||
|
||||
export interface XDomain {
|
||||
|
@ -19,7 +19,7 @@ export interface XDomain {
|
|||
minInterval?: number;
|
||||
}
|
||||
|
||||
export const getAppliedTimeRange = (layers: LayerArgs[], data: LensMultiTable) => {
|
||||
export const getAppliedTimeRange = (layers: DataLayerArgs[], data: LensMultiTable) => {
|
||||
return Object.entries(data.tables)
|
||||
.map(([tableId, table]) => {
|
||||
const layer = layers.find((l) => l.layerId === tableId);
|
||||
|
@ -37,7 +37,7 @@ export const getAppliedTimeRange = (layers: LayerArgs[], data: LensMultiTable) =
|
|||
};
|
||||
|
||||
export const getXDomain = (
|
||||
layers: LayerArgs[],
|
||||
layers: DataLayerArgs[],
|
||||
data: LensMultiTable,
|
||||
minInterval: number | undefined,
|
||||
isTimeViz: boolean,
|
||||
|
|
|
@ -22,7 +22,7 @@ import {
|
|||
import { getSortedAccessors } from '../to_expression';
|
||||
import { updateLayer } from '.';
|
||||
import { TooltipWrapper } from '../../shared_components';
|
||||
import { isReferenceLayer } from '../visualization_helpers';
|
||||
import { isDataLayer, isReferenceLayer } from '../visualization_helpers';
|
||||
|
||||
const tooltipContent = {
|
||||
auto: i18n.translate('xpack.lens.configPanel.color.tooltip.auto', {
|
||||
|
@ -55,7 +55,6 @@ export const ColorPicker = ({
|
|||
}) => {
|
||||
const index = state.layers.findIndex((l) => l.layerId === layerId);
|
||||
const layer = state.layers[index];
|
||||
const disabled = Boolean(layer.splitAccessor);
|
||||
|
||||
const overwriteColor = getSeriesColor(layer, accessor);
|
||||
const currentColor = useMemo(() => {
|
||||
|
@ -87,6 +86,7 @@ export const ColorPicker = ({
|
|||
|
||||
const [color, setColor] = useState(currentColor);
|
||||
|
||||
const disabled = Boolean(isDataLayer(layer) && layer.splitAccessor);
|
||||
const handleColor: EuiColorPickerProps['onChange'] = (text, output) => {
|
||||
setColor(text);
|
||||
if (output.isValid || text === '') {
|
||||
|
|
|
@ -20,6 +20,7 @@ import { VisualOptionsPopover } from './visual_options_popover';
|
|||
import { getScaleType } from '../to_expression';
|
||||
import { TooltipWrapper } from '../../shared_components';
|
||||
import { getDefaultVisualValuesForLayer } from '../../shared_components/datasource_default_values';
|
||||
import { getDataLayers } from '../visualization_helpers';
|
||||
|
||||
type UnwrapArray<T> = T extends Array<infer P> ? P : T;
|
||||
type AxesSettingsConfigKeys = keyof AxesSettingsConfig;
|
||||
|
@ -103,7 +104,7 @@ function hasPercentageAxis(axisGroups: GroupsConfiguration, groupId: string, sta
|
|||
axisGroups
|
||||
.find((group) => group.groupId === groupId)
|
||||
?.series.some(({ layer: layerId }) =>
|
||||
state?.layers.find(
|
||||
getDataLayers(state?.layers).find(
|
||||
(layer) => layer.layerId === layerId && layer.seriesType.includes('percentage')
|
||||
)
|
||||
)
|
||||
|
@ -115,8 +116,9 @@ export const XyToolbar = memo(function XyToolbar(
|
|||
) {
|
||||
const { state, setState, frame, useLegacyTimeAxis } = props;
|
||||
|
||||
const dataLayers = getDataLayers(state?.layers);
|
||||
const shouldRotate = state?.layers.length ? isHorizontalChart(state.layers) : false;
|
||||
const axisGroups = getAxesConfiguration(state?.layers, shouldRotate, frame.activeData);
|
||||
const axisGroups = getAxesConfiguration(dataLayers, shouldRotate, frame.activeData);
|
||||
const dataBounds = getDataBounds(frame.activeData, axisGroups);
|
||||
|
||||
const tickLabelsVisibilitySettings = {
|
||||
|
@ -196,7 +198,7 @@ export const XyToolbar = memo(function XyToolbar(
|
|||
});
|
||||
};
|
||||
|
||||
const nonOrdinalXAxis = state?.layers.every(
|
||||
const nonOrdinalXAxis = dataLayers.every(
|
||||
(layer) =>
|
||||
!layer.xAccessor ||
|
||||
getScaleType(
|
||||
|
@ -206,7 +208,7 @@ export const XyToolbar = memo(function XyToolbar(
|
|||
);
|
||||
|
||||
// only allow changing endzone visibility if it could show up theoretically (if it's a time viz)
|
||||
const onChangeEndzoneVisiblity = state?.layers.every(
|
||||
const onChangeEndzoneVisiblity = dataLayers.every(
|
||||
(layer) =>
|
||||
layer.xAccessor &&
|
||||
getScaleType(
|
||||
|
@ -232,7 +234,7 @@ export const XyToolbar = memo(function XyToolbar(
|
|||
axisGroups
|
||||
.find((group) => group.groupId === 'left')
|
||||
?.series?.some((series) => {
|
||||
const seriesType = state.layers.find((l) => l.layerId === series.layer)?.seriesType;
|
||||
const seriesType = dataLayers.find((l) => l.layerId === series.layer)?.seriesType;
|
||||
return seriesType?.includes('bar') || seriesType?.includes('area');
|
||||
})
|
||||
);
|
||||
|
@ -249,7 +251,7 @@ export const XyToolbar = memo(function XyToolbar(
|
|||
axisGroups
|
||||
.find((group) => group.groupId === 'right')
|
||||
?.series?.some((series) => {
|
||||
const seriesType = state.layers.find((l) => l.layerId === series.layer)?.seriesType;
|
||||
const seriesType = dataLayers.find((l) => l.layerId === series.layer)?.seriesType;
|
||||
return seriesType?.includes('bar') || seriesType?.includes('area');
|
||||
})
|
||||
);
|
||||
|
@ -263,12 +265,12 @@ export const XyToolbar = memo(function XyToolbar(
|
|||
[setState, state]
|
||||
);
|
||||
|
||||
const filteredBarLayers = state?.layers.filter((layer) => layer.seriesType.includes('bar'));
|
||||
const filteredBarLayers = dataLayers.filter((layer) => layer.seriesType.includes('bar'));
|
||||
const chartHasMoreThanOneBarSeries =
|
||||
filteredBarLayers.length > 1 ||
|
||||
filteredBarLayers.some((layer) => layer.accessors.length > 1 || layer.splitAccessor);
|
||||
|
||||
const isTimeHistogramModeEnabled = state?.layers.some(
|
||||
const isTimeHistogramModeEnabled = dataLayers.some(
|
||||
({ xAccessor, layerId, seriesType, splitAccessor }) => {
|
||||
if (!xAccessor) {
|
||||
return false;
|
||||
|
|
|
@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { EuiIcon, EuiPopover, EuiSelectable, EuiText, EuiPopoverTitle } from '@elastic/eui';
|
||||
import type { VisualizationLayerWidgetProps, VisualizationType } from '../../types';
|
||||
import { State, visualizationTypes } from '../types';
|
||||
import { SeriesType } from '../../../common/expressions';
|
||||
import { SeriesType, XYDataLayerConfig } from '../../../common/expressions';
|
||||
import { isHorizontalChart, isHorizontalSeries } from '../state_helpers';
|
||||
import { trackUiEvent } from '../../lens_ui_telemetry';
|
||||
import { StaticHeader } from '../../shared_components';
|
||||
|
@ -45,7 +45,7 @@ function DataLayerHeader(props: VisualizationLayerWidgetProps<State>) {
|
|||
const [isPopoverOpen, setPopoverIsOpen] = useState(false);
|
||||
const { state, layerId } = props;
|
||||
const index = state.layers.findIndex((l) => l.layerId === layerId);
|
||||
const layer = state.layers[index];
|
||||
const layer = state.layers[index] as XYDataLayerConfig;
|
||||
const currentVisType = visualizationTypes.find(({ id }) => id === layer.seriesType)!;
|
||||
const horizontalOnly = isHorizontalChart(state.layers);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import { XYState } from '../../types';
|
|||
import { hasHistogramSeries } from '../../state_helpers';
|
||||
import { ValidLayer } from '../../../../common/expressions';
|
||||
import type { FramePublicAPI } from '../../../types';
|
||||
import { getDataLayers } from '../../visualization_helpers';
|
||||
|
||||
function getValueLabelDisableReason({
|
||||
isAreaPercentage,
|
||||
|
@ -49,24 +50,25 @@ export const VisualOptionsPopover: React.FC<VisualOptionsPopoverProps> = ({
|
|||
setState,
|
||||
datasourceLayers,
|
||||
}) => {
|
||||
const isAreaPercentage = state?.layers.some(
|
||||
const dataLayers = getDataLayers(state.layers);
|
||||
const isAreaPercentage = dataLayers.some(
|
||||
({ seriesType }) => seriesType === 'area_percentage_stacked'
|
||||
);
|
||||
|
||||
const hasNonBarSeries = state?.layers.some(({ seriesType }) =>
|
||||
const hasNonBarSeries = dataLayers.some(({ seriesType }) =>
|
||||
['area_stacked', 'area', 'line'].includes(seriesType)
|
||||
);
|
||||
|
||||
const hasBarNotStacked = state?.layers.some(({ seriesType }) =>
|
||||
const hasBarNotStacked = dataLayers.some(({ seriesType }) =>
|
||||
['bar', 'bar_horizontal'].includes(seriesType)
|
||||
);
|
||||
|
||||
const hasAreaSeries = state?.layers.some(({ seriesType }) =>
|
||||
const hasAreaSeries = dataLayers.some(({ seriesType }) =>
|
||||
['area_stacked', 'area', 'area_percentage_stacked'].includes(seriesType)
|
||||
);
|
||||
|
||||
const isHistogramSeries = Boolean(
|
||||
hasHistogramSeries(state?.layers as ValidLayer[], datasourceLayers)
|
||||
hasHistogramSeries(dataLayers as ValidLayer[], datasourceLayers)
|
||||
);
|
||||
|
||||
const isValueLabelsEnabled = !hasNonBarSeries && hasBarNotStacked && !isHistogramSeries;
|
||||
|
|
|
@ -16,6 +16,7 @@ import { ToolbarPopover, ValueLabelsSettings } from '../../../shared_components'
|
|||
import { MissingValuesOptions } from './missing_values_option';
|
||||
import { FillOpacityOption } from './fill_opacity_option';
|
||||
import { layerTypes } from '../../../../common';
|
||||
import { XYDataLayerConfig } from '../../../../common/expressions';
|
||||
|
||||
describe('Visual options popover', () => {
|
||||
let frame: FramePublicAPI;
|
||||
|
@ -52,7 +53,7 @@ describe('Visual options popover', () => {
|
|||
setState={jest.fn()}
|
||||
state={{
|
||||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'bar_stacked' }],
|
||||
layers: [{ ...state.layers[0], seriesType: 'bar_stacked' } as XYDataLayerConfig],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -68,7 +69,9 @@ describe('Visual options popover', () => {
|
|||
setState={jest.fn()}
|
||||
state={{
|
||||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'area_percentage_stacked' }],
|
||||
layers: [
|
||||
{ ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYDataLayerConfig,
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -85,7 +88,9 @@ describe('Visual options popover', () => {
|
|||
setState={jest.fn()}
|
||||
state={{
|
||||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'area_percentage_stacked' }],
|
||||
layers: [
|
||||
{ ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYDataLayerConfig,
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -101,7 +106,9 @@ describe('Visual options popover', () => {
|
|||
setState={jest.fn()}
|
||||
state={{
|
||||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'area_percentage_stacked' }],
|
||||
layers: [
|
||||
{ ...state.layers[0], seriesType: 'area_percentage_stacked' } as XYDataLayerConfig,
|
||||
],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -138,7 +145,7 @@ describe('Visual options popover', () => {
|
|||
setState={jest.fn()}
|
||||
state={{
|
||||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' }],
|
||||
layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig],
|
||||
fittingFunction: 'Carry',
|
||||
}}
|
||||
/>
|
||||
|
@ -155,7 +162,7 @@ describe('Visual options popover', () => {
|
|||
setState={jest.fn()}
|
||||
state={{
|
||||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' }],
|
||||
layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig],
|
||||
fittingFunction: 'Carry',
|
||||
}}
|
||||
/>
|
||||
|
@ -172,7 +179,7 @@ describe('Visual options popover', () => {
|
|||
setState={jest.fn()}
|
||||
state={{
|
||||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'line' }],
|
||||
layers: [{ ...state.layers[0], seriesType: 'line' } as XYDataLayerConfig],
|
||||
fittingFunction: 'Carry',
|
||||
}}
|
||||
/>
|
||||
|
@ -190,7 +197,7 @@ describe('Visual options popover', () => {
|
|||
setState={jest.fn()}
|
||||
state={{
|
||||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' }],
|
||||
layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig],
|
||||
fittingFunction: 'Carry',
|
||||
}}
|
||||
/>
|
||||
|
@ -207,7 +214,7 @@ describe('Visual options popover', () => {
|
|||
setState={jest.fn()}
|
||||
state={{
|
||||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'area' }],
|
||||
layers: [{ ...state.layers[0], seriesType: 'area' } as XYDataLayerConfig],
|
||||
fittingFunction: 'Carry',
|
||||
}}
|
||||
/>
|
||||
|
@ -230,7 +237,7 @@ describe('Visual options popover', () => {
|
|||
state={{
|
||||
...state,
|
||||
layers: [
|
||||
{ ...state.layers[0], seriesType: 'bar' },
|
||||
{ ...state.layers[0], seriesType: 'bar' } as XYDataLayerConfig,
|
||||
{
|
||||
seriesType: 'bar',
|
||||
layerType: layerTypes.DATA,
|
||||
|
|
|
@ -18,6 +18,7 @@ import { createMockFramePublicAPI, createMockDatasource } from '../../mocks';
|
|||
import { chartPluginMock } from 'src/plugins/charts/public/mocks';
|
||||
import { EuiColorPicker } from '@elastic/eui';
|
||||
import { layerTypes } from '../../../common';
|
||||
import { XYDataLayerConfig } from '../../../common/expressions';
|
||||
|
||||
describe('XY Config panels', () => {
|
||||
let frame: FramePublicAPI;
|
||||
|
@ -205,7 +206,10 @@ describe('XY Config panels', () => {
|
|||
setState={jest.fn()}
|
||||
accessor="bar"
|
||||
groupId="left"
|
||||
state={{ ...state, layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' }] }}
|
||||
state={{
|
||||
...state,
|
||||
layers: [{ ...state.layers[0], seriesType: 'bar_horizontal' } as XYDataLayerConfig],
|
||||
}}
|
||||
formatFactory={jest.fn()}
|
||||
paletteService={chartPluginMock.createPaletteRegistry()}
|
||||
panelRef={React.createRef()}
|
||||
|
|
|
@ -15,6 +15,7 @@ import { PaletteOutput } from 'src/plugins/charts/public';
|
|||
import { layerTypes } from '../../common';
|
||||
import { fieldFormatsServiceMock } from '../../../../../src/plugins/field_formats/public/mocks';
|
||||
import { themeServiceMock } from '../../../../../src/core/public/mocks';
|
||||
import { XYDataLayerConfig } from '../../common/expressions';
|
||||
|
||||
jest.mock('../id_generator');
|
||||
|
||||
|
@ -89,12 +90,14 @@ describe('xy_suggestions', () => {
|
|||
// Helper that plucks out the important part of a suggestion for
|
||||
// most test assertions
|
||||
function suggestionSubset(suggestion: VisualizationSuggestion<State>) {
|
||||
return suggestion.state.layers.map(({ seriesType, splitAccessor, xAccessor, accessors }) => ({
|
||||
seriesType,
|
||||
splitAccessor,
|
||||
x: xAccessor,
|
||||
y: accessors,
|
||||
}));
|
||||
return (suggestion.state.layers as XYDataLayerConfig[]).map(
|
||||
({ seriesType, splitAccessor, xAccessor, accessors }) => ({
|
||||
seriesType,
|
||||
splitAccessor,
|
||||
x: xAccessor,
|
||||
y: accessors,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -543,7 +546,7 @@ describe('xy_suggestions', () => {
|
|||
mainPalette,
|
||||
});
|
||||
|
||||
expect(suggestion.state.layers[0].palette).toEqual(mainPalette);
|
||||
expect((suggestion.state.layers as XYDataLayerConfig[])[0].palette).toEqual(mainPalette);
|
||||
});
|
||||
|
||||
test('ignores passed in palette for non splitted charts', () => {
|
||||
|
@ -559,7 +562,7 @@ describe('xy_suggestions', () => {
|
|||
mainPalette,
|
||||
});
|
||||
|
||||
expect(suggestion.state.layers[0].palette).toEqual(undefined);
|
||||
expect((suggestion.state.layers as XYDataLayerConfig[])[0].palette).toEqual(undefined);
|
||||
});
|
||||
|
||||
test('hides reduced suggestions if there is a current state', () => {
|
||||
|
@ -655,7 +658,7 @@ describe('xy_suggestions', () => {
|
|||
|
||||
expect(suggestions[0].hide).toEqual(false);
|
||||
expect(suggestions[0].state.preferredSeriesType).toEqual('line');
|
||||
expect(suggestions[0].state.layers[0].seriesType).toEqual('line');
|
||||
expect((suggestions[0].state.layers[0] as XYDataLayerConfig).seriesType).toEqual('line');
|
||||
});
|
||||
|
||||
test('makes a visible seriesType suggestion for unchanged table without split', () => {
|
||||
|
@ -779,7 +782,11 @@ describe('xy_suggestions', () => {
|
|||
|
||||
expect(rest).toHaveLength(visualizationTypes.length - 1);
|
||||
expect(suggestion.state.preferredSeriesType).toEqual('bar_horizontal');
|
||||
expect(suggestion.state.layers.every((l) => l.seriesType === 'bar_horizontal')).toBeTruthy();
|
||||
expect(
|
||||
(suggestion.state.layers as XYDataLayerConfig[]).every(
|
||||
(l) => l.seriesType === 'bar_horizontal'
|
||||
)
|
||||
).toBeTruthy();
|
||||
expect(suggestion.title).toEqual('Flip');
|
||||
});
|
||||
|
||||
|
|
|
@ -17,9 +17,10 @@ import {
|
|||
TableChangeType,
|
||||
} from '../types';
|
||||
import { State, XYState, visualizationTypes } from './types';
|
||||
import type { SeriesType, XYLayerConfig } from '../../common/expressions';
|
||||
import type { SeriesType, XYDataLayerConfig } from '../../common/expressions';
|
||||
import { layerTypes } from '../../common';
|
||||
import { getIconForSeries } from './state_helpers';
|
||||
import { getDataLayers, isDataLayer } from './visualization_helpers';
|
||||
|
||||
const columnSortOrder = {
|
||||
document: 0,
|
||||
|
@ -158,7 +159,8 @@ function flipSeriesType(seriesType: SeriesType) {
|
|||
|
||||
function getBucketMappings(table: TableSuggestion, currentState?: State) {
|
||||
const currentLayer =
|
||||
currentState && currentState.layers.find(({ layerId }) => layerId === table.layerId);
|
||||
currentState &&
|
||||
getDataLayers(currentState.layers).find(({ layerId }) => layerId === table.layerId);
|
||||
|
||||
const buckets = table.columns.filter((col) => col.operation.isBucketed);
|
||||
// reverse the buckets before prioritization to always use the most inner
|
||||
|
@ -416,7 +418,7 @@ function getSeriesType(
|
|||
const defaultType = 'bar_stacked';
|
||||
|
||||
const oldLayer = getExistingLayer(currentState, layerId);
|
||||
const oldLayerSeriesType = oldLayer ? oldLayer.seriesType : false;
|
||||
const oldLayerSeriesType = oldLayer && isDataLayer(oldLayer) ? oldLayer.seriesType : false;
|
||||
|
||||
const closestSeriesType =
|
||||
oldLayerSeriesType || (currentState && currentState.preferredSeriesType) || defaultType;
|
||||
|
@ -496,7 +498,8 @@ function buildSuggestion({
|
|||
splitBy = xValue;
|
||||
xValue = undefined;
|
||||
}
|
||||
const existingLayer: XYLayerConfig | {} = getExistingLayer(currentState, layerId) || {};
|
||||
const existingLayer: XYDataLayerConfig | {} =
|
||||
getExistingLayer(currentState, layerId) || ({} as XYDataLayerConfig);
|
||||
const accessors = yValues.map((col) => col.columnId);
|
||||
const newLayer = {
|
||||
...existingLayer,
|
||||
|
|
|
@ -11,7 +11,8 @@ import {
|
|||
counterRate,
|
||||
metricChart,
|
||||
yAxisConfig,
|
||||
layerConfig,
|
||||
dataLayerConfig,
|
||||
referenceLineLayerConfig,
|
||||
formatColumn,
|
||||
legendConfig,
|
||||
renameColumns,
|
||||
|
@ -39,7 +40,8 @@ export const setupExpressions = (
|
|||
counterRate,
|
||||
metricChart,
|
||||
yAxisConfig,
|
||||
layerConfig,
|
||||
dataLayerConfig,
|
||||
referenceLineLayerConfig,
|
||||
formatColumn,
|
||||
legendConfig,
|
||||
renameColumns,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue