Skip to content

Commit

Permalink
Small multiples in vis_type_xy plugin (#86880)
Browse files Browse the repository at this point in the history
* Small multiples in vis_type_xy plugin

* Fix tooltip and formatted split chart values

* update advanced settings wording

* Remove React import in files with no JSX and change the extension to .ts

* Simplify conditions

* fix bar interval on split charts in vislib

* Fix charts not splitting for terms boolean fields

* fix filtering for small multiples

* Change tests interval values from 100 to 1000000

* Revert "Change tests interval values from 100 to 1000000"

This reverts commit 92f9d1b.

* Fix tests for interval issue in vislib

(cherry picked from commit ef45b63)

* Revert axis_scale changes related to interval

* Enable _line_chart_split_chart test for new charts library

* Move chart splitter id to const

Co-authored-by: nickofthyme <nick.ryan.partridge@gmail.com>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 25, 2021
1 parent 349fd5f commit 191ad14
Show file tree
Hide file tree
Showing 20 changed files with 213 additions and 162 deletions.
2 changes: 1 addition & 1 deletion docs/management/advanced-options.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ of buckets to try to represent.

[horizontal]
[[visualization-visualize-chartslibrary]]`visualization:visualize:legacyChartsLibrary`::
Enables legacy charts library for area, line and bar charts in visualize. Currently, only legacy charts library supports split chart aggregation.
Enables legacy charts library for area, line and bar charts in visualize.

[[visualization-colormapping]]`visualization:colorMapping`::
**This setting is deprecated and will not be supported as of 8.0.**
Expand Down
62 changes: 54 additions & 8 deletions src/plugins/charts/public/static/utils/transform_click_event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ export interface BrushTriggerEvent {

type AllSeriesAccessors = Array<[accessor: Accessor | AccessorFn, value: string | number]>;

// TODO: replace when exported from elastic/charts
const DEFAULT_SINGLE_PANEL_SM_VALUE = '__ECH_DEFAULT_SINGLE_PANEL_SM_VALUE__';

/**
* returns accessor value from string or function accessor
* @param datum
Expand Down Expand Up @@ -82,6 +85,29 @@ const getAllSplitAccessors = (
value,
]);

/**
* Gets value from small multiple accessors
*
* Only handles single small multiple accessor
*/
function getSplitChartValue({
smHorizontalAccessorValue,
smVerticalAccessorValue,
}: Pick<XYChartSeriesIdentifier, 'smHorizontalAccessorValue' | 'smVerticalAccessorValue'>):
| string
| number
| undefined {
if (smHorizontalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE) {
return smHorizontalAccessorValue;
}

if (smVerticalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE) {
return smVerticalAccessorValue;
}

return;
}

/**
* Reduces matching column indexes
*
Expand All @@ -92,7 +118,8 @@ const getAllSplitAccessors = (
const columnReducer = (
xAccessor: Accessor | AccessorFn | null,
yAccessor: Accessor | AccessorFn | null,
splitAccessors: AllSeriesAccessors
splitAccessors: AllSeriesAccessors,
splitChartAccessor?: Accessor | AccessorFn
) => (
acc: Array<[index: number, id: string]>,
{ id }: Datatable['columns'][number],
Expand All @@ -101,6 +128,7 @@ const columnReducer = (
if (
(xAccessor !== null && validateAccessorId(id, xAccessor)) ||
(yAccessor !== null && validateAccessorId(id, yAccessor)) ||
(splitChartAccessor !== undefined && validateAccessorId(id, splitChartAccessor)) ||
splitAccessors.some(([accessor]) => validateAccessorId(id, accessor))
) {
acc.push([index, id]);
Expand All @@ -121,13 +149,18 @@ const rowFindPredicate = (
geometry: GeometryValue | null,
xAccessor: Accessor | AccessorFn | null,
yAccessor: Accessor | AccessorFn | null,
splitAccessors: AllSeriesAccessors
splitAccessors: AllSeriesAccessors,
splitChartAccessor?: Accessor | AccessorFn,
splitChartValue?: string | number
) => (row: Datatable['rows'][number]): boolean =>
(geometry === null ||
(xAccessor !== null &&
getAccessorValue(row, xAccessor) === geometry.x &&
yAccessor !== null &&
getAccessorValue(row, yAccessor) === geometry.y)) &&
getAccessorValue(row, yAccessor) === geometry.y &&
(splitChartAccessor === undefined ||
(splitChartValue !== undefined &&
getAccessorValue(row, splitChartAccessor) === splitChartValue)))) &&
[...splitAccessors].every(([accessor, value]) => getAccessorValue(row, accessor) === value);

/**
Expand All @@ -142,19 +175,28 @@ export const getFilterFromChartClickEventFn = (
table: Datatable,
xAccessor: Accessor | AccessorFn,
splitSeriesAccessorFnMap?: Map<string | number, AccessorFn>,
splitChartAccessor?: Accessor | AccessorFn,
negate: boolean = false
) => (points: Array<[GeometryValue, XYChartSeriesIdentifier]>): ClickTriggerEvent => {
const data: ValueClickContext['data']['data'] = [];

points.forEach((point) => {
const [geometry, { yAccessor, splitAccessors }] = point;
const splitChartValue = getSplitChartValue(point[1]);
const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap);
const columns = table.columns.reduce<Array<[index: number, id: string]>>(
columnReducer(xAccessor, yAccessor, allSplitAccessors),
columnReducer(xAccessor, yAccessor, allSplitAccessors, splitChartAccessor),
[]
);
const row = table.rows.findIndex(
rowFindPredicate(geometry, xAccessor, yAccessor, allSplitAccessors)
rowFindPredicate(
geometry,
xAccessor,
yAccessor,
allSplitAccessors,
splitChartAccessor,
splitChartValue
)
);
const newData = columns.map(([column, id]) => ({
table,
Expand All @@ -179,16 +221,20 @@ export const getFilterFromChartClickEventFn = (
* Helper function to get filter action event from series
*/
export const getFilterFromSeriesFn = (table: Datatable) => (
{ splitAccessors }: XYChartSeriesIdentifier,
{ splitAccessors, ...rest }: XYChartSeriesIdentifier,
splitSeriesAccessorFnMap?: Map<string | number, AccessorFn>,
splitChartAccessor?: Accessor | AccessorFn,
negate = false
): ClickTriggerEvent => {
const splitChartValue = getSplitChartValue(rest);
const allSplitAccessors = getAllSplitAccessors(splitAccessors, splitSeriesAccessorFnMap);
const columns = table.columns.reduce<Array<[index: number, id: string]>>(
columnReducer(null, null, allSplitAccessors),
columnReducer(null, null, allSplitAccessors, splitChartAccessor),
[]
);
const row = table.rows.findIndex(rowFindPredicate(null, null, null, allSplitAccessors));
const row = table.rows.findIndex(
rowFindPredicate(null, null, null, allSplitAccessors, splitChartAccessor, splitChartValue)
);
const data: ValueClickContext['data']['data'] = columns.map(([column, id]) => ({
table,
column,
Expand Down
10 changes: 5 additions & 5 deletions src/plugins/vis_type_vislib/public/vislib/lib/axis/axis_scale.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import d3 from 'd3';
import _ from 'lodash';
import { isNumber, reduce, times } from 'lodash';
import moment from 'moment';

import { InvalidLogScaleValues } from '../../errors';
Expand Down Expand Up @@ -62,7 +62,7 @@ export class AxisScale {

return d3[extent](
opts.reduce(function (opts, v) {
if (!_.isNumber(v)) v = +v;
if (!isNumber(v)) v = +v;
if (!isNaN(v)) opts.push(v);
return opts;
}, [])
Expand Down Expand Up @@ -90,7 +90,7 @@ export class AxisScale {
const y = moment(x);
const method = n > 0 ? 'add' : 'subtract';

_.times(Math.abs(n), function () {
times(Math.abs(n), function () {
y[method](interval);
});

Expand All @@ -100,7 +100,7 @@ export class AxisScale {
getAllPoints() {
const config = this.axisConfig;
const data = this.visConfig.data.chartData();
const chartPoints = _.reduce(
const chartPoints = reduce(
data,
(chartPoints, chart, chartIndex) => {
const points = chart.series.reduce((points, seri, seriIndex) => {
Expand Down Expand Up @@ -254,6 +254,6 @@ export class AxisScale {
}

validateScale(scale) {
if (!scale || _.isNaN(scale)) throw new Error('scale is ' + scale);
if (!scale || Number.isNaN(scale)) throw new Error('scale is ' + scale);
}
}
45 changes: 45 additions & 0 deletions src/plugins/vis_type_xy/public/chart_splitter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* and the Server Side Public License, v 1; you may not use this file except in
* compliance with, at your election, the Elastic License or the Server Side
* Public License, v 1.
*/

import React from 'react';
import { Accessor, AccessorFn, GroupBy, GroupBySort, SmallMultiples } from '@elastic/charts';

interface ChartSplitterProps {
splitColumnAccessor?: Accessor | AccessorFn;
splitRowAccessor?: Accessor | AccessorFn;
sort?: GroupBySort;
}

const CHART_SPLITTER_ID = '__chart_splitter__';

export const ChartSplitter = ({
splitColumnAccessor,
splitRowAccessor,
sort,
}: ChartSplitterProps) =>
splitColumnAccessor || splitRowAccessor ? (
<>
<GroupBy
id={CHART_SPLITTER_ID}
by={(spec, datum) => {
const splitTypeAccessor = splitColumnAccessor || splitRowAccessor;
if (splitTypeAccessor) {
return typeof splitTypeAccessor === 'function'
? splitTypeAccessor(datum)
: datum[splitTypeAccessor];
}
return spec.id;
}}
sort={sort || 'dataIndex'}
/>
<SmallMultiples
splitVertically={splitRowAccessor ? CHART_SPLITTER_ID : undefined}
splitHorizontally={splitColumnAccessor ? CHART_SPLITTER_ID : undefined}
/>
</>
) : null;
34 changes: 27 additions & 7 deletions src/plugins/vis_type_xy/public/components/detailed_tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,20 @@ import {
XYChartSeriesIdentifier,
} from '@elastic/charts';

import { BUCKET_TYPES } from '../../../data/public';

import { Aspects } from '../types';

import './_detailed_tooltip.scss';
import { fillEmptyValue } from '../utils/get_series_name_fn';
import { COMPLEX_SPLIT_ACCESSOR } from '../utils/accessors';
import { COMPLEX_SPLIT_ACCESSOR, isRangeAggType } from '../utils/accessors';

interface TooltipData {
label: string;
value: string;
}

// TODO: replace when exported from elastic/charts
const DEFAULT_SINGLE_PANEL_SM_VALUE = '__ECH_DEFAULT_SINGLE_PANEL_SM_VALUE__';

const getTooltipData = (
aspects: Aspects,
header: TooltipValue | null,
Expand All @@ -37,10 +38,7 @@ const getTooltipData = (
const data: TooltipData[] = [];

if (header) {
const xFormatter =
aspects.x.aggType === BUCKET_TYPES.DATE_RANGE || aspects.x.aggType === BUCKET_TYPES.RANGE
? null
: aspects.x.formatter;
const xFormatter = isRangeAggType(aspects.x.aggType) ? null : aspects.x.formatter;
data.push({
label: aspects.x.title,
value: xFormatter ? xFormatter(header.value) : `${header.value}`,
Expand Down Expand Up @@ -80,6 +78,28 @@ const getTooltipData = (
}
});

if (
aspects.splitColumn &&
valueSeries.smHorizontalAccessorValue !== undefined &&
valueSeries.smHorizontalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE
) {
data.push({
label: aspects.splitColumn.title,
value: `${valueSeries.smHorizontalAccessorValue}`,
});
}

if (
aspects.splitRow &&
valueSeries.smVerticalAccessorValue !== undefined &&
valueSeries.smVerticalAccessorValue !== DEFAULT_SINGLE_PANEL_SM_VALUE
) {
data.push({
label: aspects.splitRow.title,
value: `${valueSeries.smVerticalAccessorValue}`,
});
}

return data;
};

Expand Down
1 change: 0 additions & 1 deletion src/plugins/vis_type_xy/public/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ export { XYEndzones } from './xy_endzones';
export { XYCurrentTime } from './xy_current_time';
export { XYSettings } from './xy_settings';
export { XYThresholdLine } from './xy_threshold_line';
export { SplitChartWarning } from './split_chart_warning';
44 changes: 0 additions & 44 deletions src/plugins/vis_type_xy/public/components/split_chart_warning.tsx

This file was deleted.

7 changes: 6 additions & 1 deletion src/plugins/vis_type_xy/public/config/get_aspects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,19 @@ export function getEmptyAspect(): Aspect {
},
};
}
export function getAspects(columns: DatatableColumn[], { x, y, z, series }: Dimensions): Aspects {
export function getAspects(
columns: DatatableColumn[],
{ x, y, z, series, splitColumn, splitRow }: Dimensions
): Aspects {
const seriesDimensions = Array.isArray(series) || series === undefined ? series : [series];

return {
x: getAspectsFromDimension(columns, x) ?? getEmptyAspect(),
y: getAspectsFromDimension(columns, y) ?? [],
z: z && z?.length > 0 ? getAspectsFromDimension(columns, z[0]) : undefined,
series: getAspectsFromDimension(columns, seriesDimensions),
splitColumn: splitColumn?.length ? getAspectsFromDimension(columns, splitColumn[0]) : undefined,
splitRow: splitRow?.length ? getAspectsFromDimension(columns, splitRow[0]) : undefined,
};
}

Expand Down
2 changes: 2 additions & 0 deletions src/plugins/vis_type_xy/public/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ export interface Aspects {
y: Aspect[];
z?: Aspect;
series?: Aspect[];
splitColumn?: Aspect;
splitRow?: Aspect;
}

export interface AxisGrid {
Expand Down
Loading

0 comments on commit 191ad14

Please sign in to comment.