[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:
Andrew Tate 2022-08-30 08:03:22 -05:00 committed by GitHub
parent 50c0077aba
commit e11e6e6b30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 10 deletions

View file

@ -954,7 +954,7 @@ describe('MetricVisComponent', function () {
"stops": Array [], "stops": Array [],
}, },
Object { Object {
"max": 28.984375, "max": 57.96875,
"min": 0, "min": 0,
}, },
], ],

View file

@ -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 });
});
});

View file

@ -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]

View file

@ -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,
}; };

View file

@ -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,