From 85dae266ebe2448e7ec2a918f0d3d783947e7fe8 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Tue, 15 Dec 2020 14:42:02 -0700 Subject: [PATCH] [Maps] unify legend for percentiles, interpolate, and custom ordinal color breaks (#85343) * [Maps] unify legend for percentiles, interpolate, and custom ordinal color breaks * unify mapbox style expression * snapshot updates * update jest expects * use less than and great than symbol for first and last stop * tslint * do not show legend if customColorRamp is not provided * update functional test expects * update functional test expect * update mapbox expect expressions in join functional tests * update mvt expects * revert mapbox style expression changes for interpole color ramp * add greater then and equal to symbol for last stop * disable stop input for first ordinal color stop Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/maps/common/i18n_getters.ts | 8 - .../components/color/color_stops_ordinal.js | 5 +- .../dynamic_color_property.test.tsx.snap | 24 +-- .../dynamic_color_property.test.tsx | 24 ++- .../properties/dynamic_color_property.tsx | 171 ++++++++++-------- x-pack/test/functional/apps/maps/joins.js | 16 +- 6 files changed, 137 insertions(+), 111 deletions(-) diff --git a/x-pack/plugins/maps/common/i18n_getters.ts b/x-pack/plugins/maps/common/i18n_getters.ts index 02b1d1adcffc1..a128038e321fc 100644 --- a/x-pack/plugins/maps/common/i18n_getters.ts +++ b/x-pack/plugins/maps/common/i18n_getters.ts @@ -9,14 +9,6 @@ import { i18n } from '@kbn/i18n'; import { $Values } from '@kbn/utility-types'; import { ES_SPATIAL_RELATIONS } from './constants'; -export const UPTO = i18n.translate('xpack.maps.upto', { - defaultMessage: 'up to', -}); - -export const GREAT_THAN = i18n.translate('xpack.maps.greatThan', { - defaultMessage: 'greater than', -}); - export function getAppTitle() { return i18n.translate('xpack.maps.appTitle', { defaultMessage: 'Maps', diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/color/color_stops_ordinal.js b/x-pack/plugins/maps/public/classes/styles/vector/components/color/color_stops_ordinal.js index d52c769c95a7d..875e780d6f480 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/color/color_stops_ordinal.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/color/color_stops_ordinal.js @@ -39,7 +39,7 @@ export const ColorStopsOrdinal = ({ return error; }; - const renderStopInput = (stop, onStopChange) => { + const renderStopInput = (stop, onStopChange, index) => { function handleOnChangeEvent(event) { const sanitizedValue = parseFloat(event.target.value); const newStopValue = isNaN(sanitizedValue) ? '' : sanitizedValue; @@ -50,9 +50,10 @@ export const ColorStopsOrdinal = ({ aria-label={i18n.translate('xpack.maps.styles.colorStops.ordinalStop.stopLabel', { defaultMessage: 'Stop', })} - value={stop} + value={index === 0 ? '' : stop} onChange={handleOnChangeEvent} compressed + disabled={index === 0} /> ); }; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/__snapshots__/dynamic_color_property.test.tsx.snap b/x-pack/plugins/maps/public/classes/styles/vector/properties/__snapshots__/dynamic_color_property.test.tsx.snap index 7607510a10b3e..a4c20ff5e0a72 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/__snapshots__/dynamic_color_property.test.tsx.snap +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/__snapshots__/dynamic_color_property.test.tsx.snap @@ -166,7 +166,7 @@ exports[`renderLegendDetailRow ordinal Should render custom ordinal legend with color="#FF0000" isLinesOnly={false} isPointsOnly={true} - label="0_format" + label="< 10_format" styleName="lineColor" /> @@ -177,7 +177,7 @@ exports[`renderLegendDetailRow ordinal Should render custom ordinal legend with color="#00FF00" isLinesOnly={false} isPointsOnly={true} - label="10_format" + label=">= 10_format" styleName="lineColor" /> @@ -229,7 +229,7 @@ exports[`renderLegendDetailRow ordinal Should render interpolate bands 1`] = ` color="#ecf1f7" isLinesOnly={false} isPointsOnly={true} - label="0_format" + label="< 13_format" styleName="lineColor" /> @@ -240,7 +240,7 @@ exports[`renderLegendDetailRow ordinal Should render interpolate bands 1`] = ` color="#d9e3ef" isLinesOnly={false} isPointsOnly={true} - label="13_format" + label="13_format up to 25_format" styleName="lineColor" /> @@ -251,7 +251,7 @@ exports[`renderLegendDetailRow ordinal Should render interpolate bands 1`] = ` color="#c5d5e7" isLinesOnly={false} isPointsOnly={true} - label="25_format" + label="25_format up to 38_format" styleName="lineColor" /> @@ -262,7 +262,7 @@ exports[`renderLegendDetailRow ordinal Should render interpolate bands 1`] = ` color="#b2c7df" isLinesOnly={false} isPointsOnly={true} - label="38_format" + label="38_format up to 50_format" styleName="lineColor" /> @@ -273,7 +273,7 @@ exports[`renderLegendDetailRow ordinal Should render interpolate bands 1`] = ` color="#9eb9d8" isLinesOnly={false} isPointsOnly={true} - label="50_format" + label="50_format up to 63_format" styleName="lineColor" /> @@ -284,7 +284,7 @@ exports[`renderLegendDetailRow ordinal Should render interpolate bands 1`] = ` color="#8bacd0" isLinesOnly={false} isPointsOnly={true} - label="63_format" + label="63_format up to 75_format" styleName="lineColor" /> @@ -295,7 +295,7 @@ exports[`renderLegendDetailRow ordinal Should render interpolate bands 1`] = ` color="#769fc8" isLinesOnly={false} isPointsOnly={true} - label="75_format" + label="75_format up to 88_format" styleName="lineColor" /> @@ -306,7 +306,7 @@ exports[`renderLegendDetailRow ordinal Should render interpolate bands 1`] = ` color="#6092c0" isLinesOnly={false} isPointsOnly={true} - label="88_format" + label=">= 88_format" styleName="lineColor" /> @@ -358,7 +358,7 @@ exports[`renderLegendDetailRow ordinal Should render percentile bands 1`] = ` color="#e5ecf4" isLinesOnly={false} isPointsOnly={true} - label="up to 50th: 5572_format" + label="< 50th: 5572_format" styleName="lineColor" /> @@ -413,7 +413,7 @@ exports[`renderLegendDetailRow ordinal Should render percentile bands 1`] = ` color="#6092c0" isLinesOnly={false} isPointsOnly={true} - label="greater than 99th: 16857_format" + label=">= 99th: 16857_format" styleName="lineColor" /> diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx index f120773086b8d..4e6c38eaf38b7 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx @@ -371,7 +371,7 @@ describe('get mapbox color expression (via internal _getMbColor)', () => { expect(colorProperty._getMbColor()).toBeNull(); }); - describe('pre-defined color ramp', () => { + describe('interpolate color ramp', () => { test('should return null when color ramp is not provided', async () => { const dynamicStyleOptions = { type: COLOR_MAP_TYPE.ORDINAL, @@ -457,7 +457,16 @@ describe('get mapbox color expression (via internal _getMbColor)', () => { const colorProperty = makeProperty(dynamicStyleOptions); expect(colorProperty._getMbColor()).toEqual([ 'step', - ['coalesce', ['feature-state', 'foobar'], 9], + [ + 'coalesce', + [ + 'case', + ['==', ['feature-state', 'foobar'], null], + 9, + ['max', ['min', ['to-number', ['feature-state', 'foobar']], 100], 10], + ], + 9, + ], 'rgba(0,0,0,0)', 10, '#f7faff', @@ -483,7 +492,16 @@ describe('get mapbox color expression (via internal _getMbColor)', () => { const colorProperty = makeProperty(dynamicStyleOptions, undefined, field); expect(colorProperty._getMbColor()).toEqual([ 'step', - ['coalesce', ['get', 'foobar'], 9], + [ + 'coalesce', + [ + 'case', + ['==', ['get', 'foobar'], null], + 9, + ['max', ['min', ['to-number', ['get', 'foobar']], 100], 10], + ], + 9, + ], 'rgba(0,0,0,0)', 10, '#f7faff', diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx index cc76a1d258563..428ccfbc5335c 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.tsx @@ -15,7 +15,6 @@ import { getColorPalette, } from '../../color_palettes'; import { COLOR_MAP_TYPE, DATA_MAPPING_FUNCTION } from '../../../../../common/constants'; -import { GREAT_THAN, UPTO } from '../../../../../common/i18n_getters'; import { isCategoricalStopsInvalid, getOtherCategoryLabel, @@ -26,6 +25,9 @@ import { ColorDynamicOptions, OrdinalColorStop } from '../../../../../common/des import { LegendProps } from './style_property'; import { getOrdinalSuffix } from '../../../util/ordinal_suffix'; +const UP_TO = i18n.translate('xpack.maps.legend.upto', { + defaultMessage: 'up to', +}); const EMPTY_STOPS = { stops: [], defaultColor: null }; const RGBA_0000 = 'rgba(0,0,0,0)'; @@ -133,11 +135,15 @@ export class DynamicColorProperty extends DynamicStyleProperty { + return this._options.customColorRamp + ? this._options.customColorRamp.reduce( + (accumulatedStops: Array, nextStop: OrdinalColorStop) => { + return [...accumulatedStops, nextStop.stop, nextStop.color]; + }, + [] + ) + : []; + } + _getColorPaletteStops() { if (this._options.useCustomColorPalette && this._options.customColorPalette) { if (isCategoricalStopsInvalid(this._options.customColorPalette)) { @@ -276,96 +298,89 @@ export class DynamicColorProperty extends DynamicStyleProperty { - return { - color: ordinalColorStop.color, - symbolId, - label: this.formatField(ordinalColorStop.stop), - }; - }); - } - - if (this.getDataMappingFunction() === DATA_MAPPING_FUNCTION.PERCENTILES) { + let colorStops: Array | null = null; + let getValuePrefix: ((i: number, isNext: boolean) => string) | null = null; + if (this._options.useCustomColorRamp) { + if (!this._options.customColorRamp) { + return []; + } + colorStops = this._getCustomRampColorStops(); + } else if (this.getDataMappingFunction() === DATA_MAPPING_FUNCTION.PERCENTILES) { const percentilesFieldMeta = this.getPercentilesFieldMeta(); if (!percentilesFieldMeta) { return []; } - const colorStops = getPercentilesMbColorRampStops( + colorStops = getPercentilesMbColorRampStops( this._options.color ? this._options.color : null, percentilesFieldMeta ); - if (!colorStops || colorStops.length <= 2) { + getValuePrefix = function (i: number, isNext: boolean) { + const percentile = isNext + ? parseFloat(percentilesFieldMeta[i / 2 + 1].percentile) + : parseFloat(percentilesFieldMeta[i / 2].percentile); + + return `${percentile}${getOrdinalSuffix(percentile)}: `; + }; + } else { + const rangeFieldMeta = this.getRangeFieldMeta(); + if (!rangeFieldMeta || !this._options.color) { return []; } - - const breaks = []; - const lastStopIndex = colorStops.length - 2; - for (let i = 0; i < colorStops.length; i += 2) { - const hasNext = i < lastStopIndex; - const stopValue = colorStops[i]; - const formattedStopValue = this.formatField(dynamicRound(stopValue)); - const color = colorStops[i + 1] as string; - const percentile = parseFloat(percentilesFieldMeta[i / 2].percentile); - const percentileLabel = `${percentile}${getOrdinalSuffix(percentile)}`; - - let label = ''; - if (!hasNext) { - label = `${GREAT_THAN} ${percentileLabel}: ${formattedStopValue}`; - } else { - const nextStopValue = colorStops[i + 2]; - const formattedNextStopValue = this.formatField(dynamicRound(nextStopValue)); - const nextPercentile = parseFloat(percentilesFieldMeta[i / 2 + 1].percentile); - const nextPercentileLabel = `${nextPercentile}${getOrdinalSuffix(nextPercentile)}`; - - if (i === 0) { - label = `${UPTO} ${nextPercentileLabel}: ${formattedNextStopValue}`; - } else { - const begin = `${percentileLabel}: ${formattedStopValue}`; - const end = `${nextPercentileLabel}: ${formattedNextStopValue}`; - label = `${begin} ${UPTO} ${end}`; - } - } - - breaks.push({ - color, - label, - symbolId, - }); + if (rangeFieldMeta.delta === 0) { + const colors = getColorPalette(this._options.color); + // map to last color. + return [ + { + color: colors[colors.length - 1], + label: this.formatField(dynamicRound(rangeFieldMeta.max)), + symbolId, + }, + ]; } - return breaks; - } - - if (!this._options.color) { - return []; + colorStops = getOrdinalMbColorRampStops( + this._options.color ? this._options.color : null, + rangeFieldMeta.min, + rangeFieldMeta.max + ); } - const rangeFieldMeta = this.getRangeFieldMeta(); - if (!rangeFieldMeta) { + if (!colorStops || colorStops.length <= 2) { return []; } - const colors = getColorPalette(this._options.color); - - if (rangeFieldMeta.delta === 0) { - // map to last color. - return [ - { - color: colors[colors.length - 1], - label: this.formatField(dynamicRound(rangeFieldMeta.max)), - symbolId, - }, - ]; - } + const breaks = []; + const lastStopIndex = colorStops.length - 2; + for (let i = 0; i < colorStops.length; i += 2) { + const hasNext = i < lastStopIndex; + const stopValue = colorStops[i]; + const formattedStopValue = this.formatField(dynamicRound(stopValue)); + const color = colorStops[i + 1] as string; + const valuePrefix = getValuePrefix ? getValuePrefix(i, false) : ''; + + let label = ''; + if (!hasNext) { + label = `>= ${valuePrefix}${formattedStopValue}`; + } else { + const nextStopValue = colorStops[i + 2]; + const formattedNextStopValue = this.formatField(dynamicRound(nextStopValue)); + const nextValuePrefix = getValuePrefix ? getValuePrefix(i, true) : ''; + + if (i === 0) { + label = `< ${nextValuePrefix}${formattedNextStopValue}`; + } else { + const begin = `${valuePrefix}${formattedStopValue}`; + const end = `${nextValuePrefix}${formattedNextStopValue}`; + label = `${begin} ${UP_TO} ${end}`; + } + } - return colors.map((color, index) => { - const rawStopValue = rangeFieldMeta.min + rangeFieldMeta.delta * (index / colors.length); - return { + breaks.push({ color, - label: this.formatField(dynamicRound(rawStopValue)), + label, symbolId, - }; - }); + }); + } + return breaks; } _getCategoricalBreaks(symbolId?: string): Break[] { diff --git a/x-pack/test/functional/apps/maps/joins.js b/x-pack/test/functional/apps/maps/joins.js index 0e2850dafbccc..9c769b8d9f59d 100644 --- a/x-pack/test/functional/apps/maps/joins.js +++ b/x-pack/test/functional/apps/maps/joins.js @@ -69,14 +69,14 @@ export default function ({ getPageObjects, getService }) { expect(split[0]).to.equal('max prop1'); //bands 1-8 - expect(split[1]).to.equal('3'); - expect(split[2]).to.equal('4.13'); - expect(split[3]).to.equal('5.25'); - expect(split[4]).to.equal('6.38'); - expect(split[5]).to.equal('7.5'); - expect(split[6]).to.equal('8.63'); - expect(split[7]).to.equal('9.75'); - expect(split[8]).to.equal('11'); + expect(split[1]).to.equal('< 4.13'); + expect(split[2]).to.equal('4.13 up to 5.25'); + expect(split[3]).to.equal('5.25 up to 6.38'); + expect(split[4]).to.equal('6.38 up to 7.5'); + expect(split[5]).to.equal('7.5 up to 8.63'); + expect(split[6]).to.equal('8.63 up to 9.75'); + expect(split[7]).to.equal('9.75 up to 11'); + expect(split[8]).to.equal('>= 11'); }); it('should decorate feature properties with join property', async () => {