Skip to content

Commit

Permalink
[7.x] [Lens] New value labels config option for bar charts (#81776) (#…
Browse files Browse the repository at this point in the history
…82849)

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
dej611 and kibanamachine authored Nov 9, 2020
1 parent e411757 commit 9af652e
Show file tree
Hide file tree
Showing 19 changed files with 502 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { EuiIconLegend } from '../assets/legend';

const typeToIconMap: { [type: string]: string | IconType } = {
legend: EuiIconLegend as IconType,
values: 'visText',
values: 'number',
};

export interface ToolbarPopoverProps {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ const createArgsWithLayers = (layers: LayerArgs[] = [sampleLayer]): XYArgs => ({
isVisible: false,
position: Position.Top,
},
valueLabels: 'hide',
axisTitlesVisibilitySettings: {
type: 'lens_xy_axisTitlesVisibilityConfig',
x: true,
Expand Down Expand Up @@ -1867,6 +1868,7 @@ describe('xy_expression', () => {
yTitle: '',
yRightTitle: '',
legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top },
valueLabels: 'hide',
tickLabelsVisibilitySettings: {
type: 'lens_xy_tickLabelsConfig',
x: true,
Expand Down Expand Up @@ -1952,6 +1954,7 @@ describe('xy_expression', () => {
yTitle: '',
yRightTitle: '',
legend: { type: 'lens_xy_legendConfig', isVisible: false, position: Position.Top },
valueLabels: 'hide',
tickLabelsVisibilitySettings: {
type: 'lens_xy_tickLabelsConfig',
x: true,
Expand Down Expand Up @@ -2023,6 +2026,7 @@ describe('xy_expression', () => {
yTitle: '',
yRightTitle: '',
legend: { type: 'lens_xy_legendConfig', isVisible: true, position: Position.Top },
valueLabels: 'hide',
tickLabelsVisibilitySettings: {
type: 'lens_xy_tickLabelsConfig',
x: true,
Expand Down
79 changes: 73 additions & 6 deletions x-pack/plugins/lens/public/xy_visualization/expression.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import {
GeometryValue,
XYChartSeriesIdentifier,
StackMode,
RecursivePartial,
Theme,
VerticalAlignment,
HorizontalAlignment,
} from '@elastic/charts';
import { I18nProvider } from '@kbn/i18n/react';
import {
Expand Down Expand Up @@ -131,6 +135,11 @@ export const xyChart: ExpressionFunctionDefinition<
defaultMessage: 'Define how missing values are treated',
}),
},
valueLabels: {
types: ['string'],
options: ['hide', 'inside'],
help: '',
},
tickLabelsVisibilitySettings: {
types: ['lens_xy_tickLabelsConfig'],
help: i18n.translate('xpack.lens.xyChart.tickLabelsSettings.help', {
Expand Down Expand Up @@ -214,6 +223,40 @@ export const getXyChartRenderer = (dependencies: {
},
});

function mergeThemeWithValueLabelsStyling(
theme: RecursivePartial<Theme>,
valuesLabelMode: string = 'hide',
isHorizontal: boolean
) {
const VALUE_LABELS_MAX_FONTSIZE = 15;
const VALUE_LABELS_MIN_FONTSIZE = 10;
const VALUE_LABELS_VERTICAL_OFFSET = -10;
const VALUE_LABELS_HORIZONTAL_OFFSET = 10;

if (valuesLabelMode === 'hide') {
return theme;
}
return {
...theme,
...{
barSeriesStyle: {
...theme.barSeriesStyle,
displayValue: {
fontSize: { min: VALUE_LABELS_MIN_FONTSIZE, max: VALUE_LABELS_MAX_FONTSIZE },
fill: { textInverted: true, textBorder: 2 },
alignment: isHorizontal
? {
vertical: VerticalAlignment.Middle,
}
: { horizontal: HorizontalAlignment.Center },
offsetX: isHorizontal ? VALUE_LABELS_HORIZONTAL_OFFSET : 0,
offsetY: isHorizontal ? 0 : VALUE_LABELS_VERTICAL_OFFSET,
},
},
},
};
}

function getIconForSeriesType(seriesType: SeriesType): IconType {
return visualizationTypes.find((c) => c.id === seriesType)!.icon || 'empty';
}
Expand Down Expand Up @@ -254,7 +297,7 @@ export function XYChart({
onClickValue,
onSelectRange,
}: XYChartRenderProps) {
const { legend, layers, fittingFunction, gridlinesVisibilitySettings } = args;
const { legend, layers, fittingFunction, gridlinesVisibilitySettings, valueLabels } = args;
const chartTheme = chartsThemeService.useChartsTheme();
const chartBaseTheme = chartsThemeService.useChartsBaseTheme();

Expand Down Expand Up @@ -396,6 +439,16 @@ export function XYChart({
return style;
};

const shouldShowValueLabels =
// No stacked bar charts
filteredLayers.every((layer) => !layer.seriesType.includes('stacked')) &&
// No histogram charts
!isHistogramViz;

const baseThemeWithMaybeValueLabels = !shouldShowValueLabels
? chartTheme
: mergeThemeWithValueLabelsStyling(chartTheme, valueLabels, shouldRotate);

const colorAssignments = getColorAssignments(args.layers, data, formatFactory);

return (
Expand All @@ -408,7 +461,7 @@ export function XYChart({
}
legendPosition={legend.position}
showLegendExtra={false}
theme={chartTheme}
theme={baseThemeWithMaybeValueLabels}
baseTheme={chartBaseTheme}
tooltip={{
headerFormatter: (d) => safeXAccessorLabelRenderer(d.value),
Expand Down Expand Up @@ -613,6 +666,10 @@ export function XYChart({
});
}

const yAxis = yAxesConfiguration.find((axisConfiguration) =>
axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor)
);

const seriesProps: SeriesSpec = {
splitSeriesAccessors: splitAccessor ? [splitAccessor] : [],
stackAccessors: seriesType.includes('stacked') ? [xAccessor as string] : [],
Expand Down Expand Up @@ -649,9 +706,7 @@ export function XYChart({
palette.params
);
},
groupId: yAxesConfiguration.find((axisConfiguration) =>
axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor)
)?.groupId,
groupId: yAxis?.groupId,
enableHistogramMode:
isHistogram &&
(seriesType.includes('stacked') || !splitAccessor) &&
Expand Down Expand Up @@ -723,7 +778,19 @@ export function XYChart({
case 'bar_horizontal':
case 'bar_horizontal_stacked':
case 'bar_horizontal_percentage_stacked':
return <BarSeries key={index} {...seriesProps} />;
const valueLabelsSettings = {
displayValueSettings: {
// This format double fixes two issues in elastic-chart
// * when rotating the chart, the formatter is not correctly picked
// * in some scenarios value labels are not strings, and this breaks the elastic-chart lib
valueFormatter: (d: unknown) => yAxis?.formatter?.convert(d) || '',
showValueLabel: shouldShowValueLabels && valueLabels !== 'hide',
isAlternatingValueLabel: false,
isValueContainedInElement: true,
hideClippedValue: true,
},
};
return <BarSeries key={index} {...seriesProps} {...valueLabelsSettings} />;
case 'area_stacked':
case 'area_percentage_stacked':
return (
Expand Down
23 changes: 22 additions & 1 deletion x-pack/plugins/lens/public/xy_visualization/state_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
*/

import { EuiIconType } from '@elastic/eui/src/components/icon/icon';
import { SeriesType, visualizationTypes, LayerConfig, YConfig } from './types';
import { FramePublicAPI } from '../types';
import { SeriesType, visualizationTypes, LayerConfig, YConfig, ValidLayer } from './types';

export function isHorizontalSeries(seriesType: SeriesType) {
return (
Expand Down Expand Up @@ -37,3 +38,23 @@ export const getSeriesColor = (layer: LayerConfig, accessor: string) => {
layer?.yConfig?.find((yConfig: YConfig) => yConfig.forAccessor === accessor)?.color || null
);
};

export function hasHistogramSeries(
layers: ValidLayer[] = [],
datasourceLayers?: FramePublicAPI['datasourceLayers']
) {
if (!datasourceLayers) {
return false;
}
const validLayers = layers.filter(({ accessors }) => accessors.length);

return validLayers.some(({ layerId, xAccessor }: ValidLayer) => {
const xAxisOperation = datasourceLayers[layerId].getOperationForColumnId(xAccessor);
return (
xAxisOperation &&
xAxisOperation.isBucketed &&
xAxisOperation.scale &&
xAxisOperation.scale !== 'ordinal'
);
});
}
Loading

0 comments on commit 9af652e

Please sign in to comment.