mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[Lens] improve metric palette behavior (#139596)
* handle single-value case * match continuity between metric types * fix collapse-by handling, polish logic * update snapshot Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
50c0077aba
commit
e11e6e6b30
5 changed files with 94 additions and 10 deletions
|
@ -954,7 +954,7 @@ describe('MetricVisComponent', function () {
|
||||||
"stops": Array [],
|
"stops": Array [],
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"max": 28.984375,
|
"max": 57.96875,
|
||||||
"min": 0,
|
"min": 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* 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 { Datatable, DatatableRow } from '@kbn/expressions-plugin/common';
|
||||||
|
import { getDataBoundsForPalette } from './palette_data_bounds';
|
||||||
|
|
||||||
|
describe('palette data bounds', () => {
|
||||||
|
const buildTableWithRows = (rows: DatatableRow[]) => {
|
||||||
|
const table: Datatable = {
|
||||||
|
type: 'datatable',
|
||||||
|
columns: Object.keys(rows).map((key) => ({
|
||||||
|
id: key,
|
||||||
|
name: key,
|
||||||
|
meta: { type: 'number' },
|
||||||
|
})),
|
||||||
|
rows,
|
||||||
|
};
|
||||||
|
|
||||||
|
return table;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('single value scenarios', () => {
|
||||||
|
it('creates a range with the metric value in the middle', () => {
|
||||||
|
const accessors = {
|
||||||
|
metric: 'first',
|
||||||
|
};
|
||||||
|
expect(getDataBoundsForPalette(accessors, buildTableWithRows([{ first: 100 }]))).toEqual({
|
||||||
|
min: 0,
|
||||||
|
max: 200,
|
||||||
|
});
|
||||||
|
expect(getDataBoundsForPalette(accessors, buildTableWithRows([{ first: -100 }]))).toEqual({
|
||||||
|
min: -200,
|
||||||
|
max: 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses maximum dimension when available', () => {
|
||||||
|
expect(
|
||||||
|
getDataBoundsForPalette(
|
||||||
|
{ metric: 'metric', max: 'max' },
|
||||||
|
buildTableWithRows([{ metric: 200, max: 100 }])
|
||||||
|
)
|
||||||
|
).toEqual({ min: 0, max: 100 });
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getDataBoundsForPalette(
|
||||||
|
{ metric: 'metric', max: 'max' },
|
||||||
|
buildTableWithRows([
|
||||||
|
{ metric: 200, max: 100 },
|
||||||
|
{ metric: 400, max: 150 },
|
||||||
|
]),
|
||||||
|
1
|
||||||
|
)
|
||||||
|
).toEqual({ min: 0, max: 150 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses minimum and maximum metric when breakdown but no max', () => {
|
||||||
|
expect(
|
||||||
|
getDataBoundsForPalette(
|
||||||
|
{ metric: 'metric', breakdownBy: 'breakdown' },
|
||||||
|
buildTableWithRows([{ metric: -100 }, { metric: 100 }, { metric: 200 }, { metric: 300 }])
|
||||||
|
)
|
||||||
|
).toEqual({ min: -100, max: 300 });
|
||||||
|
});
|
||||||
|
});
|
|
@ -19,6 +19,17 @@ export const getDataBoundsForPalette = (
|
||||||
|
|
||||||
const smallestMetric = Math.min(...data.rows.map((row) => row[accessors.metric]));
|
const smallestMetric = Math.min(...data.rows.map((row) => row[accessors.metric]));
|
||||||
const greatestMetric = Math.max(...data.rows.map((row) => row[accessors.metric]));
|
const greatestMetric = Math.max(...data.rows.map((row) => row[accessors.metric]));
|
||||||
|
|
||||||
|
if (
|
||||||
|
!accessors.max &&
|
||||||
|
!accessors.breakdownBy &&
|
||||||
|
(typeof rowNumber !== 'undefined' || data.rows.length === 1)
|
||||||
|
) {
|
||||||
|
// dealing with a single metric and no max
|
||||||
|
const metricValue = greatestMetric;
|
||||||
|
return metricValue < 0 ? { min: metricValue * 2, max: 0 } : { min: 0, max: metricValue * 2 };
|
||||||
|
}
|
||||||
|
|
||||||
const greatestMaximum = accessors.max
|
const greatestMaximum = accessors.max
|
||||||
? rowNumber
|
? rowNumber
|
||||||
? data.rows[rowNumber][accessors.max]
|
? data.rows[rowNumber][accessors.max]
|
||||||
|
|
|
@ -15,6 +15,7 @@ export const defaultPaletteParams: RequiredPaletteParamTypes = {
|
||||||
...sharedDefaultParams,
|
...sharedDefaultParams,
|
||||||
maxSteps: 5,
|
maxSteps: 5,
|
||||||
name: DEFAULT_PALETTE_NAME,
|
name: DEFAULT_PALETTE_NAME,
|
||||||
|
continuity: 'all',
|
||||||
rangeType: 'number',
|
rangeType: 'number',
|
||||||
steps: DEFAULT_COLOR_STEPS,
|
steps: DEFAULT_COLOR_STEPS,
|
||||||
};
|
};
|
||||||
|
|
|
@ -267,14 +267,18 @@ function PrimaryMetricEditor(props: SubProps) {
|
||||||
|
|
||||||
const hasDynamicColoring = Boolean(state?.palette);
|
const hasDynamicColoring = Boolean(state?.palette);
|
||||||
|
|
||||||
const startWithPercentPalette = Boolean(state.maxAccessor || state.breakdownByAccessor);
|
const supportsPercentPalette = Boolean(
|
||||||
|
state.maxAccessor ||
|
||||||
|
(state.breakdownByAccessor && !state.collapseFn) ||
|
||||||
|
state?.palette?.params?.rangeType === 'percent'
|
||||||
|
);
|
||||||
|
|
||||||
const activePalette = state?.palette || {
|
const activePalette = state?.palette || {
|
||||||
type: 'palette',
|
type: 'palette',
|
||||||
name: (startWithPercentPalette ? defaultPercentagePaletteParams : defaultNumberPaletteParams)
|
name: (supportsPercentPalette ? defaultPercentagePaletteParams : defaultNumberPaletteParams)
|
||||||
.name,
|
.name,
|
||||||
params: {
|
params: {
|
||||||
...(startWithPercentPalette ? defaultPercentagePaletteParams : defaultNumberPaletteParams),
|
...(supportsPercentPalette ? defaultPercentagePaletteParams : defaultNumberPaletteParams),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -282,7 +286,8 @@ function PrimaryMetricEditor(props: SubProps) {
|
||||||
{
|
{
|
||||||
metric: state.metricAccessor!,
|
metric: state.metricAccessor!,
|
||||||
max: state.maxAccessor,
|
max: state.maxAccessor,
|
||||||
breakdownBy: state.breakdownByAccessor,
|
// if we're collapsing, pretend like there's no breakdown to match the activeData
|
||||||
|
breakdownBy: !state.collapseFn ? state.breakdownByAccessor : undefined,
|
||||||
},
|
},
|
||||||
frame.activeData?.[state.layerId]
|
frame.activeData?.[state.layerId]
|
||||||
);
|
);
|
||||||
|
@ -401,11 +406,7 @@ function PrimaryMetricEditor(props: SubProps) {
|
||||||
activePalette={activePalette}
|
activePalette={activePalette}
|
||||||
dataBounds={currentMinMax}
|
dataBounds={currentMinMax}
|
||||||
displayInfinity={true}
|
displayInfinity={true}
|
||||||
showRangeTypeSelector={Boolean(
|
showRangeTypeSelector={supportsPercentPalette}
|
||||||
state.breakdownByAccessor ||
|
|
||||||
state.maxAccessor ||
|
|
||||||
activePalette.params?.rangeType === 'percent'
|
|
||||||
)}
|
|
||||||
setPalette={(newPalette) => {
|
setPalette={(newPalette) => {
|
||||||
setState({
|
setState({
|
||||||
...state,
|
...state,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue