mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Lens] Improve outside label placement for pie/donut charts (#115966)
* 🐛 Shrink pie/donut chart to allow more space for tiny slices * ✅ Add unit tests * ✨ Handler for small slices in Visualize pie Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c32191007d
commit
90395c5589
5 changed files with 140 additions and 4 deletions
|
@ -234,9 +234,21 @@ const PieComponent = (props: PieComponentProps) => {
|
|||
syncColors,
|
||||
]
|
||||
);
|
||||
|
||||
const rescaleFactor = useMemo(() => {
|
||||
const overallSum = visData.rows.reduce((sum, row) => sum + row[metricColumn.id], 0);
|
||||
const slices = visData.rows.map((row) => row[metricColumn.id] / overallSum);
|
||||
const smallSlices = slices.filter((value) => value < 0.02).length;
|
||||
if (smallSlices) {
|
||||
// shrink up to 20% to give some room for the linked values
|
||||
return 1 / (1 + Math.min(smallSlices * 0.05, 0.2));
|
||||
}
|
||||
return 1;
|
||||
}, [visData.rows, metricColumn]);
|
||||
|
||||
const config = useMemo(
|
||||
() => getConfig(visParams, chartTheme, dimensions),
|
||||
[chartTheme, visParams, dimensions]
|
||||
() => getConfig(visParams, chartTheme, dimensions, rescaleFactor),
|
||||
[chartTheme, visParams, dimensions, rescaleFactor]
|
||||
);
|
||||
const tooltip: TooltipProps = {
|
||||
type: visParams.addTooltip ? TooltipType.Follow : TooltipType.None,
|
||||
|
|
66
src/plugins/vis_types/pie/public/utils/get_config.test.ts
Normal file
66
src/plugins/vis_types/pie/public/utils/get_config.test.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { getConfig } from './get_config';
|
||||
import { createMockPieParams } from '../mocks';
|
||||
|
||||
const visParams = createMockPieParams();
|
||||
|
||||
describe('getConfig', () => {
|
||||
it('should cap the outerSizeRatio to 1', () => {
|
||||
expect(getConfig(visParams, {}, { width: 400, height: 400 }).outerSizeRatio).toBe(1);
|
||||
});
|
||||
|
||||
it('should not have outerSizeRatio for split chart', () => {
|
||||
expect(
|
||||
getConfig(
|
||||
{
|
||||
...visParams,
|
||||
dimensions: {
|
||||
...visParams.dimensions,
|
||||
splitColumn: [
|
||||
{
|
||||
accessor: 1,
|
||||
format: {
|
||||
id: 'number',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{},
|
||||
{ width: 400, height: 400 }
|
||||
).outerSizeRatio
|
||||
).toBeUndefined();
|
||||
|
||||
expect(
|
||||
getConfig(
|
||||
{
|
||||
...visParams,
|
||||
dimensions: {
|
||||
...visParams.dimensions,
|
||||
splitRow: [
|
||||
{
|
||||
accessor: 1,
|
||||
format: {
|
||||
id: 'number',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{},
|
||||
{ width: 400, height: 400 }
|
||||
).outerSizeRatio
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should not set outerSizeRatio if dimensions are not defined', () => {
|
||||
expect(getConfig(visParams, {}).outerSizeRatio).toBeUndefined();
|
||||
});
|
||||
});
|
|
@ -13,7 +13,8 @@ const MAX_SIZE = 1000;
|
|||
export const getConfig = (
|
||||
visParams: PieVisParams,
|
||||
chartTheme: RecursivePartial<Theme>,
|
||||
dimensions?: PieContainerDimensions
|
||||
dimensions?: PieContainerDimensions,
|
||||
rescaleFactor: number = 1
|
||||
): RecursivePartial<PartitionConfig> => {
|
||||
// On small multiples we want the labels to only appear inside
|
||||
const isSplitChart = Boolean(visParams.dimensions.splitColumn || visParams.dimensions.splitRow);
|
||||
|
@ -32,7 +33,9 @@ export const getConfig = (
|
|||
const usingOuterSizeRatio =
|
||||
dimensions && !isSplitChart
|
||||
? {
|
||||
outerSizeRatio: MAX_SIZE / Math.min(dimensions?.width, dimensions?.height),
|
||||
outerSizeRatio:
|
||||
// Cap the ratio to 1 and then rescale
|
||||
rescaleFactor * Math.min(MAX_SIZE / Math.min(dimensions?.width, dimensions?.height), 1),
|
||||
}
|
||||
: null;
|
||||
const config: RecursivePartial<PartitionConfig> = {
|
||||
|
|
|
@ -376,5 +376,50 @@ describe('PieVisualization component', () => {
|
|||
expect(component.find(VisualizationContainer)).toHaveLength(1);
|
||||
expect(component.find(EmptyPlaceholder).prop('icon')).toEqual(LensIconChartDonut);
|
||||
});
|
||||
|
||||
test('it should dynamically shrink the chart area to when some small slices are detected', () => {
|
||||
const defaultData = getDefaultArgs().data;
|
||||
const emptyData: LensMultiTable = {
|
||||
...defaultData,
|
||||
tables: {
|
||||
first: {
|
||||
...defaultData.tables.first,
|
||||
rows: [
|
||||
{ a: 60, b: 'I', c: 200, d: 'Row 1' },
|
||||
{ a: 1, b: 'J', c: 0.1, d: 'Row 2' },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const component = shallow(
|
||||
<PieComponent args={args} {...getDefaultArgs()} data={emptyData} />
|
||||
);
|
||||
expect(component.find(Partition).prop('config')?.outerSizeRatio).toBeCloseTo(1 / 1.05);
|
||||
});
|
||||
|
||||
test('it should bound the shrink the chart area to ~20% when some small slices are detected', () => {
|
||||
const defaultData = getDefaultArgs().data;
|
||||
const emptyData: LensMultiTable = {
|
||||
...defaultData,
|
||||
tables: {
|
||||
first: {
|
||||
...defaultData.tables.first,
|
||||
rows: [
|
||||
{ a: 60, b: 'I', c: 200, d: 'Row 1' },
|
||||
{ a: 1, b: 'J', c: 0.1, d: 'Row 2' },
|
||||
{ a: 1, b: 'K', c: 0.1, d: 'Row 3' },
|
||||
{ a: 1, b: 'G', c: 0.1, d: 'Row 4' },
|
||||
{ a: 1, b: 'H', c: 0.1, d: 'Row 5' },
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const component = shallow(
|
||||
<PieComponent args={args} {...getDefaultArgs()} data={emptyData} />
|
||||
);
|
||||
expect(component.find(Partition).prop('config')?.outerSizeRatio).toBeCloseTo(1 / 1.2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -204,6 +204,16 @@ export function PieComponent(
|
|||
} else if (categoryDisplay === 'inside') {
|
||||
// Prevent links from showing
|
||||
config.linkLabel = { maxCount: 0 };
|
||||
} else {
|
||||
// if it contains any slice below 2% reduce the ratio
|
||||
// first step: sum it up the overall sum
|
||||
const overallSum = firstTable.rows.reduce((sum, row) => sum + row[metric!], 0);
|
||||
const slices = firstTable.rows.map((row) => row[metric!] / overallSum);
|
||||
const smallSlices = slices.filter((value) => value < 0.02).length;
|
||||
if (smallSlices) {
|
||||
// shrink up to 20% to give some room for the linked values
|
||||
config.outerSizeRatio = 1 / (1 + Math.min(smallSlices * 0.05, 0.2));
|
||||
}
|
||||
}
|
||||
}
|
||||
const metricColumn = firstTable.columns.find((c) => c.id === metric)!;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue