Skip to content

Commit

Permalink
[Lens] Legend Statistics feature (#182357)
Browse files Browse the repository at this point in the history
## Summary

Fixes #183887

<img width="959" alt="Screenshot 2024-06-09 at 21 11 33"
src="https://github.com/elastic/kibana/assets/4283304/950a92cb-a70a-4413-9358-f4a8a5b7da5d">


<img width="513" alt="Screenshot 2024-06-09 at 21 11 38"
src="https://github.com/elastic/kibana/assets/4283304/36d8fe9d-fa40-4721-ac24-db0599076b9c">

- [x] popover width = 500 px
- [x] new combobox legend values component added
- [x] switch show value removed
- [x] location and alignment is in a single for row
- [x] auto legend width option to allow users to have the legend
automatically size based its contents - not there yet
- [x] no limit imposed of the number of values  
- [x] when a value is selected, list or table appears
- [x] when the list is selected, label truncations is not offered and
legend items should be forced to have truncation on and clamped to a
single line.
- [x] adds telemetry too - adds events on save if the legend statistics
change:

`lens_legend_stats`- triggered if any stats is in legend

`lens_legend_stats_${TYPE}` - triggered for specific types, if user has
2 statistics (eg. AVG and MIN) two events are triggered.

and counting how many they use
`lens_legend_stats_amount_1`
`lens_legend_stats_amount_2`
`lens_legend_stats_amount_3`
`lens_legend_stats_amount_4_7`
`lens_legend_stats_amount_above_8`

---------

Co-authored-by: Nick Partridge <[email protected]>
Co-authored-by: Marco Vettorello <[email protected]>
  • Loading branch information
3 people authored Jun 19, 2024
1 parent 57891ff commit 2f4997c
Show file tree
Hide file tree
Showing 46 changed files with 953 additions and 347 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type {
import type { DataView } from '@kbn/data-views-plugin/public';
import type { SavedObjectReference } from '@kbn/core/server';
import { AxesSettingsConfig } from '@kbn/visualizations-plugin/common';
import type { XYLegendValue } from '@kbn/visualizations-plugin/common/constants';
import { LegendValue } from '@elastic/charts';
import type { Chart, ChartConfig, ChartLayer } from '../types';
import { DEFAULT_LAYER_ID } from '../utils';
import { XY_ID } from './constants';
Expand Down Expand Up @@ -131,7 +131,7 @@ export const getXYVisualizationState = (
isVisible: false,
position: 'right',
showSingleSeries: false,
legendStats: ['currentAndLastValue' as XYLegendValue.CurrentAndLastValue],
legendStats: [LegendValue.CurrentAndLastValue],
},
valueLabels: 'show',
yLeftScale: 'linear',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@ import {
ValueFormats,
LegendDisplay,
} from '../types/expression_renderers';
import {
ExpressionValueVisDimension,
PartitionLegendValue,
} from '@kbn/visualizations-plugin/common';
import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common';
import { Datatable } from '@kbn/expressions-plugin/common/expression_types/specs';
import { waffleVisFunction } from './waffle_vis_function';
import { PARTITION_LABELS_VALUE, PARTITION_VIS_RENDERER_NAME } from '../constants';
import { ExecutionContext } from '@kbn/expressions-plugin/common';
import { LegendValue } from '@elastic/charts';

describe('interpreter/functions#waffleVis', () => {
const fn = functionWrapper(waffleVisFunction());
Expand All @@ -37,7 +35,7 @@ describe('interpreter/functions#waffleVis', () => {

const visConfig: WaffleVisConfig = {
addTooltip: true,
legendStats: [PartitionLegendValue.Value],
legendStats: [LegendValue.Value],
metricsToLabels: JSON.stringify({}),
legendDisplay: LegendDisplay.SHOW,
legendPosition: 'right',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
* Side Public License, v 1.
*/

import { Position } from '@elastic/charts';
import { LegendValue, Position } from '@elastic/charts';
import { ArgTypes } from '@storybook/addons';
import { PartitionLegendValue } from '@kbn/visualizations-plugin/common/constants';
import { EmptySizeRatios, LegendDisplay } from '../../../common';
import { ChartTypes } from '../../../common/types';

Expand Down Expand Up @@ -212,7 +211,7 @@ export const waffleArgTypes: ArgTypes = {
description: 'Legend stats',
type: { name: 'string', required: false },
table: { type: { summary: 'string' }, defaultValue: { summary: undefined } },
options: [PartitionLegendValue.Value],
options: [LegendValue.Value],
control: { type: 'select' },
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* Side Public License, v 1.
*/

import { LegendValue } from '@elastic/charts';
import { Datatable } from '@kbn/expressions-plugin/public';
import { PartitionLegendValue } from '@kbn/visualizations-plugin/common/constants';
import {
BucketColumns,
PartitionVisParams,
Expand Down Expand Up @@ -382,6 +382,6 @@ export const createMockWaffleParams = (): PartitionVisParams => {
},
],
},
legendStats: [PartitionLegendValue.Value],
legendStats: [LegendValue.Value],
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import { HorizontalAlignment, Position, VerticalAlignment } from '@elastic/charts';
import { i18n } from '@kbn/i18n';
import { LegendSize } from '@kbn/visualizations-plugin/common/constants';
import { LegendLayout, LegendSize } from '@kbn/visualizations-plugin/common/constants';
import { LEGEND_CONFIG } from '../constants';
import { LegendConfigFn } from '../types';

Expand Down Expand Up @@ -106,6 +106,26 @@ export const legendConfigFunction: LegendConfigFn = {
defaultMessage: 'Specifies the legend stats.',
}),
},
title: {
types: ['string'],
help: i18n.translate('expressionXY.legendConfig.title.help', {
defaultMessage: 'Specifies the legend title.',
}),
},
isTitleVisible: {
types: ['boolean'],
help: i18n.translate('expressionXY.legendConfig.isTitleVisible.help', {
defaultMessage: 'Specifies if the legend title is visible.',
}),
},
layout: {
types: ['string'],
help: i18n.translate('expressionXY.legendConfig.legendLayout.help', {
defaultMessage: 'Specifies the legend layout.',
}),
options: [LegendLayout.Table, LegendLayout.List],
strict: true,
},
},
async fn(input, args, handlers) {
const { legendConfigFn } = await import('./legend_config_fn');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import type {
} from '@kbn/expressions-plugin/common';
import {
LegendSize,
ExpressionValueVisDimension,
XYLegendValue,
LegendLayout,
ExpressionValueVisDimension,
} from '@kbn/visualizations-plugin/common';
import { EventAnnotationOutput } from '@kbn/event-annotation-plugin/common';

Expand Down Expand Up @@ -220,7 +221,11 @@ export interface LegendConfig {
/**
* metrics to display in the legend
*/

legendStats?: XYLegendValue[];
layout?: LegendLayout;
title?: string;
isTitleVisible?: boolean;
}

// Arguments to XY chart expression, with computed properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
VerticalAlignment,
XYChartSeriesIdentifier,
Tooltip,
LegendValue,
} from '@elastic/charts';
import { Datatable } from '@kbn/expressions-plugin/common';
import { EmptyPlaceholder } from '@kbn/charts-plugin/public';
Expand Down Expand Up @@ -58,7 +59,7 @@ import { XYChart, XYChartRenderProps } from './xy_chart';
import { ExtendedDataLayerConfig, XYProps, AnnotationLayerConfigResult } from '../../common/types';
import { DataLayers } from './data_layers';
import { SplitChart } from './split_chart';
import { LegendSize, XYLegendValue } from '@kbn/visualizations-plugin/common';
import { LegendSize } from '@kbn/visualizations-plugin/common';
import type { LayerCellValueActions } from '../types';

const onClickValue = jest.fn();
Expand Down Expand Up @@ -744,7 +745,7 @@ describe('XYChart component', () => {
{...defaultProps}
args={{
...args,
legend: { ...args.legend, legendStats: [XYLegendValue.CurrentAndLastValue] },
legend: { ...args.legend, legendStats: [LegendValue.CurrentAndLastValue] },
}}
/>
);
Expand All @@ -760,14 +761,14 @@ describe('XYChart component', () => {
...args,
legend: {
...args.legend,
legendStats: [XYLegendValue.CurrentAndLastValue],
legendStats: [LegendValue.CurrentAndLastValue],
},
layers: [dateHistogramLayer],
}}
/>
);
expect(component.find(Settings).at(0).prop('legendValues')).toEqual([
XYLegendValue.CurrentAndLastValue,
LegendValue.CurrentAndLastValue,
]);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,8 @@ export function XYChart({
showLegend={showLegend}
legendPosition={legend?.isInside ? legendInsideParams : legend.position}
legendSize={LegendSizeToPixels[legend.legendSize ?? DEFAULT_LEGEND_SIZE]}
legendValues={isHistogramViz ? legend.legendStats : []}
legendTitle={getLegendTitle(legend.title, dataLayers[0], legend.isTitleVisible)}
theme={[
{
barSeriesStyle: {
Expand Down Expand Up @@ -877,7 +879,6 @@ export function XYChart({
)
: undefined
}
legendValues={isHistogramViz ? legend.legendStats : []}
ariaLabel={args.ariaLabel}
ariaUseDefaultSummary={!args.ariaLabel}
orderOrdinalBinsBy={
Expand Down Expand Up @@ -1038,3 +1039,23 @@ export function XYChart({
</div>
);
}

const defaultLegendTitle = i18n.translate('expressionXY.xyChart.legendTitle', {
defaultMessage: 'Legend',
});

function getLegendTitle(
title: string | undefined,
layer?: CommonXYDataLayerConfig,
isTitleVisible?: boolean
) {
if (!isTitleVisible) {
return undefined;
}
if (typeof title === 'string' && title.length > 0) {
return title;
}
return layer?.splitAccessors?.[0]
? getColumnByAccessor(layer.splitAccessors?.[0], layer?.table.columns)?.name
: defaultLegendTitle;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import { PartitionLegendValue } from '@kbn/visualizations-plugin/common/constants';
import { LegendValue } from '@elastic/charts';
import { getConfiguration } from '.';
import { samplePieVis } from '../../sample_vis.test.mocks';

Expand Down Expand Up @@ -39,7 +39,7 @@ describe('getConfiguration', () => {
percentDecimals: 2,
primaryGroups: ['bucket-1'],
secondaryGroups: [],
legendStats: [PartitionLegendValue.Value],
legendStats: [LegendValue.Value],
truncateLegend: true,
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* Side Public License, v 1.
*/

import { LegendValue } from '@elastic/charts';
import { LegendDisplay, PartitionVisParams } from '@kbn/expression-partition-vis-plugin/common';
import { PartitionLegendValue } from '@kbn/visualizations-plugin/common/constants';
import {
CategoryDisplayTypes,
NumberDisplayTypes,
Expand All @@ -28,7 +28,7 @@ const getLayers = (
const showValuesInLegend =
vis.params.labels.values ??
(vis.params.legendStats
? vis.params.legendStats?.[0] === PartitionLegendValue.Value
? vis.params.legendStats?.[0] === LegendValue.Value
: vis.type.visConfig.defaults.showValuesInLegend);

return [
Expand All @@ -50,7 +50,7 @@ const getLayers = (
vis.params.legendDisplay ??
vis.type.visConfig.defaults.legendDisplay,
legendPosition: vis.params.legendPosition ?? vis.type.visConfig.defaults.legendPosition,
legendStats: showValuesInLegend ? [PartitionLegendValue.Value] : undefined,
legendStats: showValuesInLegend ? [LegendValue.Value] : undefined,
nestedLegend: vis.params.nestedLegend ?? vis.type.visConfig.defaults.nestedLegend,
percentDecimals:
vis.params.labels.percentDecimals ?? vis.type.visConfig.defaults.labels.percentDecimals,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import { Position, ScaleType as ECScaleType } from '@elastic/charts';
import { LegendValue, Position, ScaleType as ECScaleType } from '@elastic/charts';
import {
SeriesTypes,
Column,
Expand All @@ -15,7 +15,6 @@ import {
XYReferenceLineLayerConfig,
} from '@kbn/visualizations-plugin/common/convert_to_lens';
import { Vis } from '@kbn/visualizations-plugin/public';
import { XYLegendValue } from '@kbn/visualizations-plugin/common/constants';
import { Layer } from '..';
import { ChartType } from '../../../common';
import {
Expand Down Expand Up @@ -237,7 +236,7 @@ export const getConfiguration = (
maxLines: vis.params.maxLegendLines ?? vis.type.visConfig.defaults.maxLegendLines,
showSingleSeries: true,
legendStats: Boolean(vis.params.labels.show ?? vis.type.visConfig.defaults.labels?.show)
? [XYLegendValue.CurrentAndLastValue]
? [LegendValue.CurrentAndLastValue]
: undefined,
},
fittingFunction: fittingFunction
Expand Down
5 changes: 2 additions & 3 deletions src/plugins/vis_types/xy/public/to_ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import moment from 'moment';
import { Position, ScaleType as ECScaleType } from '@elastic/charts';
import { LegendValue, Position, ScaleType as ECScaleType } from '@elastic/charts';
import { i18n } from '@kbn/i18n';
import {
VisToExpressionAst,
Expand All @@ -20,7 +20,6 @@ import { buildExpression, buildExpressionFunction } from '@kbn/expressions-plugi
import { BUCKET_TYPES } from '@kbn/data-plugin/public';
import type { TimeRangeBounds } from '@kbn/data-plugin/common';
import type { PaletteOutput } from '@kbn/charts-plugin/common/expressions/palette/types';
import { XYLegendValue } from '@kbn/visualizations-plugin/common/constants';
import {
Dimensions,
Dimension,
Expand Down Expand Up @@ -48,7 +47,7 @@ const prepareLengend = (params: VisParams, legendSize?: LegendSize) => {
shouldTruncate: params.truncateLegend,
showSingleSeries: true,
legendSize,
legendStats: params.labels.show ? [XYLegendValue.CurrentAndLastValue] : undefined,
legendStats: params.labels.show ? [LegendValue.CurrentAndLastValue] : undefined,
});

return buildExpression([legend]);
Expand Down
33 changes: 27 additions & 6 deletions src/plugins/visualizations/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Side Public License, v 1.
*/

import { LegendValue } from '@elastic/charts';
import { METRIC_TYPES, BUCKET_TYPES } from '@kbn/data-plugin/common';

export const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit';
Expand Down Expand Up @@ -37,6 +38,11 @@ export enum LegendSize {
EXTRA_LARGE = 'xlarge',
}

export enum LegendLayout {
Table = 'table',
List = 'list',
}

export const LegendSizeToPixels = {
[LegendSize.AUTO]: undefined,
[LegendSize.SMALL]: 80,
Expand All @@ -52,10 +58,25 @@ export const SUPPORTED_AGGREGATIONS = [
...Object.values(BUCKET_TYPES),
] as const;

export enum XYLegendValue {
CurrentAndLastValue = 'currentAndLastValue',
}
export type XYLegendValue = Extract<
LegendValue,
| 'currentAndLastValue'
| 'lastValue'
| 'lastNonNullValue'
| 'average'
| 'median'
| 'max'
| 'min'
| 'firstValue'
| 'firstNonNullValue'
| 'total'
| 'count'
| 'distinctCount'
| 'variance'
| 'stdDeviation'
| 'range'
| 'difference'
| 'differencePercent'
>;

export enum PartitionLegendValue {
Value = 'value',
}
export type PartitionLegendValue = Extract<LegendValue, 'value' | 'percent'>;
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { HorizontalAlignment, LayoutDirection, Position, VerticalAlignment } fro
import { $Values } from '@kbn/utility-types';
import type { CustomPaletteParams, PaletteOutput } from '@kbn/coloring';
import { KibanaQueryOutput } from '@kbn/data-plugin/common';
import { LegendSize, XYLegendValue, PartitionLegendValue } from '../../constants';
import { LegendSize, type XYLegendValue, type PartitionLegendValue } from '../../constants';
import {
CategoryDisplayTypes,
PartitionChartTypes,
Expand Down
5 changes: 3 additions & 2 deletions src/plugins/visualizations/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export {
LegendSize,
LegendSizeToPixels,
DEFAULT_LEGEND_SIZE,
XYLegendValue,
PartitionLegendValue,
LegendLayout,
type XYLegendValue,
type PartitionLegendValue,
} from './constants';
Loading

0 comments on commit 2f4997c

Please sign in to comment.