diff --git a/src/plugins/vis_types/pie/public/__snapshots__/pie_fn.test.ts.snap b/src/plugins/vis_types/pie/public/__snapshots__/pie_fn.test.ts.snap
index fb51717d1adc0..4298654af512d 100644
--- a/src/plugins/vis_types/pie/public/__snapshots__/pie_fn.test.ts.snap
+++ b/src/plugins/vis_types/pie/public/__snapshots__/pie_fn.test.ts.snap
@@ -47,6 +47,7 @@ Object {
"splitRow": undefined,
},
"distinctColors": false,
+ "emptySizeRatio": 0.3,
"isDonut": true,
"labels": Object {
"percentDecimals": 2,
diff --git a/src/plugins/vis_types/pie/public/editor/collections.ts b/src/plugins/vis_types/pie/public/editor/collections.ts
index d65e933a8835c..64e4694eb449f 100644
--- a/src/plugins/vis_types/pie/public/editor/collections.ts
+++ b/src/plugins/vis_types/pie/public/editor/collections.ts
@@ -7,6 +7,7 @@
*/
import { i18n } from '@kbn/i18n';
+import { EMPTY_SIZE_RATIOS } from './constants';
import { LabelPositions, ValueFormats } from '../types';
export const getLabelPositions = [
@@ -38,3 +39,27 @@ export const getValuesFormats = [
value: ValueFormats.VALUE,
},
];
+
+export const emptySizeRatioOptions = [
+ {
+ id: 'emptySizeRatioOption-small',
+ value: EMPTY_SIZE_RATIOS.SMALL,
+ label: i18n.translate('visTypePie.emptySizeRatioOptions.small', {
+ defaultMessage: 'Small',
+ }),
+ },
+ {
+ id: 'emptySizeRatioOption-medium',
+ value: EMPTY_SIZE_RATIOS.MEDIUM,
+ label: i18n.translate('visTypePie.emptySizeRatioOptions.medium', {
+ defaultMessage: 'Medium',
+ }),
+ },
+ {
+ id: 'emptySizeRatioOption-large',
+ value: EMPTY_SIZE_RATIOS.LARGE,
+ label: i18n.translate('visTypePie.emptySizeRatioOptions.large', {
+ defaultMessage: 'Large',
+ }),
+ },
+];
diff --git a/src/plugins/vis_types/pie/public/editor/components/pie.test.tsx b/src/plugins/vis_types/pie/public/editor/components/pie.test.tsx
index ac02b33b92add..d6dc4c0734e42 100644
--- a/src/plugins/vis_types/pie/public/editor/components/pie.test.tsx
+++ b/src/plugins/vis_types/pie/public/editor/components/pie.test.tsx
@@ -135,4 +135,18 @@ describe('PalettePicker', function () {
expect(findTestSubject(component, 'visTypePieValueDecimals').length).toBe(1);
});
});
+
+ it('renders the donut size button group for the elastic charts implementation', async () => {
+ component = mountWithIntl();
+ await act(async () => {
+ expect(findTestSubject(component, 'visTypePieEmptySizeRatioButtonGroup').length).toBe(1);
+ });
+ });
+
+ it('not renders the donut size button group for the vislib implementation', async () => {
+ component = mountWithIntl();
+ await act(async () => {
+ expect(findTestSubject(component, 'visTypePieEmptySizeRatioButtonGroup').length).toBe(0);
+ });
+ });
});
diff --git a/src/plugins/vis_types/pie/public/editor/components/pie.tsx b/src/plugins/vis_types/pie/public/editor/components/pie.tsx
index 50e599b5ef5f3..97f427d7b91b7 100644
--- a/src/plugins/vis_types/pie/public/editor/components/pie.tsx
+++ b/src/plugins/vis_types/pie/public/editor/components/pie.tsx
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import React, { useState, useEffect } from 'react';
+import React, { useState, useEffect, useCallback } from 'react';
import { METRIC_TYPE } from '@kbn/analytics';
import {
EuiPanel,
@@ -17,6 +17,7 @@ import {
EuiIconTip,
EuiFlexItem,
EuiFlexGroup,
+ EuiButtonGroup,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
@@ -33,11 +34,15 @@ import { TruncateLabelsOption } from './truncate_labels';
import { PaletteRegistry } from '../../../../../charts/public';
import { DEFAULT_PERCENT_DECIMALS } from '../../../common';
import { PieVisParams, LabelPositions, ValueFormats, PieTypeProps } from '../../types';
-import { getLabelPositions, getValuesFormats } from '../collections';
+import { emptySizeRatioOptions, getLabelPositions, getValuesFormats } from '../collections';
import { getLegendPositions } from '../positions';
export interface PieOptionsProps extends VisEditorOptionsProps, PieTypeProps {}
+const emptySizeRatioLabel = i18n.translate('visTypePie.editors.pie.emptySizeRatioLabel', {
+ defaultMessage: 'Inner area size',
+});
+
function DecimalSlider({
paramName,
value,
@@ -96,6 +101,14 @@ const PieOptions = (props: PieOptionsProps) => {
fetchPalettes();
}, [props.palettes]);
+ const handleEmptySizeRatioChange = useCallback(
+ (sizeId) => {
+ const emptySizeRatio = emptySizeRatioOptions.find(({ id }) => id === sizeId)?.value;
+ setValue('emptySizeRatio', emptySizeRatio);
+ },
+ [setValue]
+ );
+
return (
<>
@@ -116,6 +129,23 @@ const PieOptions = (props: PieOptionsProps) => {
value={stateParams.isDonut}
setValue={setValue}
/>
+ {props.showElasticChartsOptions && stateParams.isDonut && (
+
+ value === stateParams.emptySizeRatio)
+ ?.id ?? 'emptySizeRatioOption-small'
+ }
+ onChange={handleEmptySizeRatioChange}
+ data-test-subj="visTypePieEmptySizeRatioButtonGroup"
+ />
+
+ )}
{props.showElasticChartsOptions && (
<>
diff --git a/src/plugins/vis_types/pie/public/editor/constants.ts b/src/plugins/vis_types/pie/public/editor/constants.ts
new file mode 100644
index 0000000000000..6fd0c7ab0a4a6
--- /dev/null
+++ b/src/plugins/vis_types/pie/public/editor/constants.ts
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+export enum EMPTY_SIZE_RATIOS {
+ SMALL = 0.3,
+ MEDIUM = 0.54,
+ LARGE = 0.7,
+}
diff --git a/src/plugins/vis_types/pie/public/pie_fn.test.ts b/src/plugins/vis_types/pie/public/pie_fn.test.ts
index 9ba21cdc847e5..9a9c2410481bd 100644
--- a/src/plugins/vis_types/pie/public/pie_fn.test.ts
+++ b/src/plugins/vis_types/pie/public/pie_fn.test.ts
@@ -10,6 +10,7 @@ import { functionWrapper } from '../../../expressions/common/expression_function
import { createPieVisFn } from './pie_fn';
import { PieVisConfig } from './types';
import { Datatable } from '../../../expressions/common/expression_types/specs';
+import { EMPTY_SIZE_RATIOS } from './editor/constants';
describe('interpreter/functions#pie', () => {
const fn = functionWrapper(createPieVisFn());
@@ -23,6 +24,7 @@ describe('interpreter/functions#pie', () => {
addLegend: true,
legendPosition: 'right',
isDonut: true,
+ emptySizeRatio: EMPTY_SIZE_RATIOS.SMALL,
nestedLegend: true,
truncateLegend: true,
maxLegendLines: true,
diff --git a/src/plugins/vis_types/pie/public/pie_fn.ts b/src/plugins/vis_types/pie/public/pie_fn.ts
index 74e8127712399..041ecb5b7e6e5 100644
--- a/src/plugins/vis_types/pie/public/pie_fn.ts
+++ b/src/plugins/vis_types/pie/public/pie_fn.ts
@@ -117,6 +117,12 @@ export const createPieVisFn = (): VisTypePieExpressionFunctionDefinition => ({
}),
default: false,
},
+ emptySizeRatio: {
+ types: ['number'],
+ help: i18n.translate('visTypePie.function.args.emptySizeRatioHelpText', {
+ defaultMessage: 'Defines donut inner empty area size',
+ }),
+ },
palette: {
types: ['string'],
help: i18n.translate('visTypePie.function.args.paletteHelpText', {
diff --git a/src/plugins/vis_types/pie/public/to_ast.ts b/src/plugins/vis_types/pie/public/to_ast.ts
index fbfffbb77d5fb..531b8516464f2 100644
--- a/src/plugins/vis_types/pie/public/to_ast.ts
+++ b/src/plugins/vis_types/pie/public/to_ast.ts
@@ -54,6 +54,7 @@ export const toExpressionAst: VisToExpressionAst = async (vis, par
maxLegendLines: vis.params.maxLegendLines,
distinctColors: vis.params?.distinctColors,
isDonut: vis.params.isDonut,
+ emptySizeRatio: vis.params.emptySizeRatio,
palette: vis.params?.palette?.name,
labels: prepareLabels(vis.params.labels),
metric: schemas.metric.map(prepareDimension),
diff --git a/src/plugins/vis_types/pie/public/types/types.ts b/src/plugins/vis_types/pie/public/types/types.ts
index fb5efb5971805..4745ee0f95ed5 100644
--- a/src/plugins/vis_types/pie/public/types/types.ts
+++ b/src/plugins/vis_types/pie/public/types/types.ts
@@ -13,6 +13,7 @@ import type { SerializedFieldFormat } from '../../../../field_formats/common';
import { ExpressionValueVisDimension } from '../../../../visualizations/public';
import { ExpressionValuePieLabels } from '../expression_functions/pie_labels';
import { PaletteOutput, ChartsPluginSetup } from '../../../../charts/public';
+import { EMPTY_SIZE_RATIOS } from '../editor/constants';
export interface Dimension {
accessor: number;
@@ -38,6 +39,7 @@ interface PieCommonParams {
maxLegendLines: number;
distinctColors: boolean;
isDonut: boolean;
+ emptySizeRatio?: EMPTY_SIZE_RATIOS;
}
export interface LabelsParams {
diff --git a/src/plugins/vis_types/pie/public/utils/get_config.ts b/src/plugins/vis_types/pie/public/utils/get_config.ts
index 9f67155145820..67852a773d697 100644
--- a/src/plugins/vis_types/pie/public/utils/get_config.ts
+++ b/src/plugins/vis_types/pie/public/utils/get_config.ts
@@ -54,7 +54,7 @@ export const getConfig = (
sectorLineStroke: chartTheme.lineSeriesStyle?.point?.fill,
sectorLineWidth: 1.5,
circlePadding: 4,
- emptySizeRatio: visParams.isDonut ? 0.3 : 0,
+ emptySizeRatio: visParams.isDonut ? visParams.emptySizeRatio : 0,
...usingMargin,
};
if (!visParams.labels.show) {
diff --git a/src/plugins/vis_types/pie/public/vis_type/pie.ts b/src/plugins/vis_types/pie/public/vis_type/pie.ts
index f10af053bd161..845b2bae1c89d 100644
--- a/src/plugins/vis_types/pie/public/vis_type/pie.ts
+++ b/src/plugins/vis_types/pie/public/vis_type/pie.ts
@@ -14,6 +14,7 @@ import { DEFAULT_PERCENT_DECIMALS } from '../../common';
import { PieVisParams, LabelPositions, ValueFormats, PieTypeProps } from '../types';
import { toExpressionAst } from '../to_ast';
import { getPieOptions } from '../editor/components';
+import { EMPTY_SIZE_RATIOS } from '../editor/constants';
export const getPieVisTypeDefinition = ({
showElasticChartsOptions = false,
@@ -39,6 +40,7 @@ export const getPieVisTypeDefinition = ({
maxLegendLines: 1,
distinctColors: false,
isDonut: true,
+ emptySizeRatio: EMPTY_SIZE_RATIOS.SMALL,
palette: {
type: 'palette',
name: 'default',
diff --git a/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts b/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts
index 4f2736d739b11..ce909152e71b9 100644
--- a/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts
+++ b/x-pack/plugins/lens/common/expressions/pie_chart/pie_chart.ts
@@ -101,6 +101,10 @@ export const pie: ExpressionFunctionDefinition<
help: '',
types: ['palette'],
},
+ emptySizeRatio: {
+ types: ['number'],
+ help: '',
+ },
},
inputTypes: ['lens_multitable'],
fn(data: LensMultiTable, args: PieExpressionArgs) {
diff --git a/x-pack/plugins/lens/common/expressions/pie_chart/types.ts b/x-pack/plugins/lens/common/expressions/pie_chart/types.ts
index a7aa92369dce2..0a7d705ec3fbc 100644
--- a/x-pack/plugins/lens/common/expressions/pie_chart/types.ts
+++ b/x-pack/plugins/lens/common/expressions/pie_chart/types.ts
@@ -20,6 +20,7 @@ export interface SharedPieLayerState {
showValuesInLegend?: boolean;
nestedLegend?: boolean;
percentDecimals?: number;
+ emptySizeRatio?: number;
legendMaxLines?: number;
truncateLegend?: boolean;
}
diff --git a/x-pack/plugins/lens/public/pie_visualization/constants.ts b/x-pack/plugins/lens/public/pie_visualization/constants.ts
index bfb263b415891..e32320bb75ff0 100644
--- a/x-pack/plugins/lens/public/pie_visualization/constants.ts
+++ b/x-pack/plugins/lens/public/pie_visualization/constants.ts
@@ -6,3 +6,9 @@
*/
export const DEFAULT_PERCENT_DECIMALS = 2;
+
+export enum EMPTY_SIZE_RATIOS {
+ SMALL = 0.3,
+ MEDIUM = 0.54,
+ LARGE = 0.7,
+}
diff --git a/x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts b/x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts
index 4f16ab01ba19c..2bafa5a1ff8e0 100644
--- a/x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts
+++ b/x-pack/plugins/lens/public/pie_visualization/partition_charts_meta.ts
@@ -14,6 +14,7 @@ import { LensIconChartPie } from '../assets/chart_pie';
import { LensIconChartTreemap } from '../assets/chart_treemap';
import { LensIconChartMosaic } from '../assets/chart_mosaic';
import { LensIconChartWaffle } from '../assets/chart_waffle';
+import { EMPTY_SIZE_RATIOS } from './constants';
import type { SharedPieLayerState } from '../../common/expressions';
import type { PieChartTypes } from '../../common/expressions/pie_chart/types';
@@ -37,6 +38,11 @@ interface PartitionChartMeta {
value: SharedPieLayerState['numberDisplay'];
inputDisplay: string;
}>;
+ emptySizeRatioOptions?: Array<{
+ id: string;
+ value: EMPTY_SIZE_RATIOS;
+ label: string;
+ }>;
};
legend: {
flat?: boolean;
@@ -110,6 +116,30 @@ const numberOptions: PartitionChartMeta['toolbarPopover']['numberOptions'] = [
},
];
+const emptySizeRatioOptions: PartitionChartMeta['toolbarPopover']['emptySizeRatioOptions'] = [
+ {
+ id: 'emptySizeRatioOption-small',
+ value: EMPTY_SIZE_RATIOS.SMALL,
+ label: i18n.translate('xpack.lens.pieChart.emptySizeRatioOptions.small', {
+ defaultMessage: 'Small',
+ }),
+ },
+ {
+ id: 'emptySizeRatioOption-medium',
+ value: EMPTY_SIZE_RATIOS.MEDIUM,
+ label: i18n.translate('xpack.lens.pieChart.emptySizeRatioOptions.medium', {
+ defaultMessage: 'Medium',
+ }),
+ },
+ {
+ id: 'emptySizeRatioOption-large',
+ value: EMPTY_SIZE_RATIOS.LARGE,
+ label: i18n.translate('xpack.lens.pieChart.emptySizeRatioOptions.large', {
+ defaultMessage: 'Large',
+ }),
+ },
+];
+
export const PartitionChartsMeta: Record = {
donut: {
icon: LensIconChartDonut,
@@ -122,6 +152,7 @@ export const PartitionChartsMeta: Record = {
toolbarPopover: {
categoryOptions,
numberOptions,
+ emptySizeRatioOptions,
},
legend: {
getShowLegendDefault: (bucketColumns) => bucketColumns.length > 1,
diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx
index 4bf7fcf9f8925..9fb016dc0a2d7 100644
--- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx
+++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx
@@ -82,6 +82,7 @@ export function PieComponent(
legendPosition,
nestedLegend,
percentDecimals,
+ emptySizeRatio,
legendMaxLines,
truncateLegend,
hideLabels,
@@ -229,7 +230,7 @@ export function PieComponent(
config.fillLabel = { textColor: 'rgba(0,0,0,0)' };
}
} else {
- config.emptySizeRatio = shape === 'donut' ? 0.3 : 0;
+ config.emptySizeRatio = shape === 'donut' ? emptySizeRatio : 0;
if (hideLabels || categoryDisplay === 'hide') {
// Force all labels to be linked, then prevent links from showing
diff --git a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts
index f4c951cece3c2..f7e76567743e4 100644
--- a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts
+++ b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts
@@ -8,9 +8,8 @@
import type { Ast } from '@kbn/interpreter/common';
import type { PaletteRegistry } from 'src/plugins/charts/public';
import type { Operation, DatasourcePublicAPI } from '../types';
-import { DEFAULT_PERCENT_DECIMALS } from './constants';
+import { DEFAULT_PERCENT_DECIMALS, EMPTY_SIZE_RATIOS } from './constants';
import { shouldShowValuesInLegend } from './render_helpers';
-
import type { PieVisualizationState } from '../../common/expressions';
import { getDefaultVisualValuesForLayer } from '../shared_components/datasource_default_values';
@@ -59,6 +58,7 @@ function expressionHelper(
categoryDisplay: [layer.categoryDisplay],
legendDisplay: [layer.legendDisplay],
legendPosition: [layer.legendPosition || 'right'],
+ emptySizeRatio: [layer.emptySizeRatio ?? EMPTY_SIZE_RATIOS.SMALL],
showValuesInLegend: [shouldShowValuesInLegend(layer, state.shape)],
percentDecimals: [
state.shape === 'waffle'
diff --git a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx
index 997ebb2e3787f..6910a3ea0966c 100644
--- a/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx
+++ b/x-pack/plugins/lens/public/pie_visualization/toolbar.tsx
@@ -14,6 +14,7 @@ import {
EuiSuperSelect,
EuiRange,
EuiHorizontalRule,
+ EuiButtonGroup,
} from '@elastic/eui';
import type { Position } from '@elastic/charts';
import type { PaletteRegistry } from 'src/plugins/charts/public';
@@ -54,9 +55,19 @@ const legendOptions: Array<{
},
];
+const emptySizeRatioLabel = i18n.translate('xpack.lens.pieChart.emptySizeRatioLabel', {
+ defaultMessage: 'Inner area size',
+});
+
export function PieToolbar(props: VisualizationToolbarProps) {
const { state, setState, frame } = props;
const layer = state.layers[0];
+ const {
+ categoryOptions,
+ numberOptions,
+ emptySizeRatioOptions,
+ isDisabled: isToolbarPopoverDisabled,
+ } = PartitionChartsMeta[state.shape].toolbarPopover;
const onStateChange = useCallback(
(part: Record) => {
@@ -118,14 +129,17 @@ export function PieToolbar(props: VisualizationToolbarProps {
+ const emptySizeRatio = emptySizeRatioOptions?.find(({ id }) => id === sizeId)?.value;
+ onStateChange({ emptySizeRatio });
+ },
+ [emptySizeRatioOptions, onStateChange]
+ );
+
if (!layer) {
return null;
}
- const {
- categoryOptions,
- numberOptions,
- isDisabled: isToolbarPopoverDisabled,
- } = PartitionChartsMeta[state.shape].toolbarPopover;
const defaultTruncationValue = getDefaultVisualValuesForLayer(
state,
@@ -193,6 +207,32 @@ export function PieToolbar(props: VisualizationToolbarProps
+ {emptySizeRatioOptions?.length ? (
+
+
+ value === layer.emptySizeRatio)?.id ??
+ 'emptySizeRatioOption-small'
+ }
+ onChange={onEmptySizeRatioChange}
+ data-test-subj="lnsEmptySizeRatioButtonGroup"
+ />
+
+
+ ) : null}
{
it('should allow creation of lens xy chart', async () => {
@@ -768,5 +769,28 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await filterBar.removeFilter('extension.raw');
});
+
+ it('should show visual options button group for a donut chart', async () => {
+ await PageObjects.visualize.navigateToNewVisualization();
+ await PageObjects.visualize.clickVisType('lens');
+ await PageObjects.lens.switchToVisualization('donut');
+
+ const hasVisualOptionsButton = await PageObjects.lens.hasVisualOptionsButton();
+ expect(hasVisualOptionsButton).to.be(true);
+
+ await PageObjects.lens.openVisualOptions();
+ await retry.try(async () => {
+ expect(await PageObjects.lens.hasEmptySizeRatioButtonGroup()).to.be(true);
+ });
+ });
+
+ it('should not show visual options button group for a pie chart', async () => {
+ await PageObjects.visualize.navigateToNewVisualization();
+ await PageObjects.visualize.clickVisType('lens');
+ await PageObjects.lens.switchToVisualization('pie');
+
+ const hasVisualOptionsButton = await PageObjects.lens.hasVisualOptionsButton();
+ expect(hasVisualOptionsButton).to.be(false);
+ });
});
}
diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts
index d1990d47a37e5..f55b05bc65a05 100644
--- a/x-pack/test/functional/page_objects/lens_page.ts
+++ b/x-pack/test/functional/page_objects/lens_page.ts
@@ -590,6 +590,9 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
await colorPickerInput.type(color);
await PageObjects.common.sleep(1000); // give time for debounced components to rerender
},
+ hasVisualOptionsButton() {
+ return testSubjects.exists('lnsVisualOptionsButton');
+ },
async openVisualOptions() {
await retry.try(async () => {
await testSubjects.click('lnsVisualOptionsButton');
@@ -1236,5 +1239,9 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont
const filterIn = await testSubjects.find(`legend-${value}-filterIn`);
await filterIn.click();
},
+
+ hasEmptySizeRatioButtonGroup() {
+ return testSubjects.exists('lnsEmptySizeRatioButtonGroup');
+ },
});
}