From e5a8cec25e33b7ba306a03b394f674838a808145 Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Wed, 2 Feb 2022 14:40:42 +0300 Subject: [PATCH] [Lens] Color stop UI follow-ups (#123924) * Round min/max values when we are turning off auto auto mode * Fix the behavior so that we don't see the warning "The specified value '-Infinity' cannot be parsed or is out of range." in console. * Allow to use correct max value in percentage mode for heatmap * Change the wording of the `Add color range` button to `Add color` and `Distribute equally` button to `Distribute values` * Don't show percent symbol append for the EuiFieldText components that are set to be the current data's min or max value * Adds tooltips for the min, max, and pencil icon buttons * Add support of changing the opacity * Add tooltips for disabled color range action buttons (Reverse colors, Distribute values) * Change the `Maximum value should be greater than preceding values` error message to be `Maximum value must be greater than preceding values` * Apply sorting if min on mid value more than max instead of showing error * Fix test * Change increment by +1 for adding new color ranges * Fix CI * Allow customize max/min values in color ranges for gauge * Fix CI * Change Outside Data Bounds Message from warning to info type * Remove warnings about value outside databounds * Fix Checks * Add tests * Fix some remarks * Update x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_item_buttons.tsx Co-authored-by: Marta Bondyra Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Marta Bondyra --- .../components/gauge_component.test.tsx | 14 +-- .../public/components/gauge_component.tsx | 62 +++++++++-- .../public/components/heatmap_component.tsx | 9 +- .../color_ranges/color_ranges.test.tsx | 14 +-- .../coloring/color_ranges/color_ranges.tsx | 9 +- .../color_ranges_extra_actions.tsx | 103 +++++++++++------- .../color_ranges/color_ranges_item.tsx | 33 +++--- .../color_ranges_item_buttons.tsx | 63 ++++++----- .../color_ranges_validation.test.ts | 85 +-------------- .../color_ranges/color_ranges_validation.tsx | 83 ++------------ .../utils/color_ranges_crud.test.ts | 11 +- .../color_ranges/utils/color_ranges_crud.ts | 8 +- .../coloring/color_ranges/utils/utils.test.ts | 13 ++- .../coloring/color_ranges/utils/utils.ts | 27 +++-- .../coloring/palette_configuration.tsx | 5 +- .../shared_components/coloring/utils.ts | 3 + .../visualizations/gauge/dimension_editor.tsx | 1 - 17 files changed, 233 insertions(+), 310 deletions(-) diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx index 0dbd4d2cade04..935c4d66a4506 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.test.tsx @@ -212,7 +212,7 @@ describe('GaugeComponent', function () { stops: [10, 20, 30] as unknown as ColorStop[], range: 'number', rangeMin: 0, - rangeMax: 20, + rangeMax: 30, }, }; const customProps = { @@ -253,8 +253,8 @@ describe('GaugeComponent', function () { }, } as GaugeRenderProps; const goal = shallowWithIntl().find(Goal); - expect(goal.prop('ticks')).toEqual([0, 1, 2, 3, 10]); - expect(goal.prop('bands')).toEqual([0, 1, 2, 3, 10]); + expect(goal.prop('ticks')).toEqual([0, 1, 2, 3, 4, 10]); + expect(goal.prop('bands')).toEqual([0, 1, 2, 3, 4, 10]); }); it('sets proper color bands and ticks on color bands if palette steps are smaller than minimum', () => { const palette = { @@ -281,8 +281,8 @@ describe('GaugeComponent', function () { }, } as GaugeRenderProps; const goal = shallowWithIntl().find(Goal); - expect(goal.prop('ticks')).toEqual([0, 10]); - expect(goal.prop('bands')).toEqual([0, 10]); + expect(goal.prop('ticks')).toEqual([0, 4, 10]); + expect(goal.prop('bands')).toEqual([0, 4, 10]); }); it('sets proper color bands and ticks on color bands if percent palette steps are smaller than 0', () => { const palette = { @@ -294,7 +294,7 @@ describe('GaugeComponent', function () { stops: [-20, -60, 80], range: 'percent', rangeMin: 0, - rangeMax: 4, + rangeMax: 100, }, }; const customProps = { @@ -407,7 +407,7 @@ describe('GaugeComponent', function () { stops: [20, 60, 80], range: 'percent', rangeMin: 0, - rangeMax: 10, + rangeMax: 100, }, }; const customProps = { diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx index a63746505a35c..dfd7755c47681 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx @@ -29,7 +29,11 @@ declare global { } } -function normalizeColors({ colors, stops, range }: CustomPaletteState, min: number) { +function normalizeColors( + { colors, stops, range, rangeMin, rangeMax }: CustomPaletteState, + min: number, + max: number +) { if (!colors) { return; } @@ -37,23 +41,61 @@ function normalizeColors({ colors, stops, range }: CustomPaletteState, min: numb stops.filter((stop, i) => (range === 'percent' ? stop < 0 : stop < min)).length, 0 ); - return colors.slice(colorsOutOfRangeSmaller); + let updatedColors = colors.slice(colorsOutOfRangeSmaller); + + let correctMin = rangeMin; + let correctMax = rangeMax; + if (range === 'percent') { + correctMin = min + rangeMin * ((max - min) / 100); + correctMax = min + rangeMax * ((max - min) / 100); + } + + if (correctMin > min && isFinite(correctMin)) { + updatedColors = [`rgba(255,255,255,0)`, ...updatedColors]; + } + + if (correctMax < max && isFinite(correctMax)) { + updatedColors = [...updatedColors, `rgba(255,255,255,0)`]; + } + + return updatedColors; } function normalizeBands( - { colors, stops, range }: CustomPaletteState, + { colors, stops, range, rangeMax, rangeMin }: CustomPaletteState, { min, max }: { min: number; max: number } ) { if (!stops.length) { const step = (max - min) / colors.length; return [min, ...colors.map((_, i) => min + (i + 1) * step)]; } + let firstRanges = [min]; + let lastRanges = [max]; + let correctMin = rangeMin; + let correctMax = rangeMax; + if (range === 'percent') { + correctMin = min + rangeMin * ((max - min) / 100); + correctMax = min + rangeMax * ((max - min) / 100); + } + + if (correctMin > min && isFinite(correctMin)) { + firstRanges = [min, correctMin]; + } + + if (correctMax < max && isFinite(correctMax)) { + lastRanges = [correctMax, max]; + } + if (range === 'percent') { - const filteredStops = stops.filter((stop) => stop >= 0 && stop <= 100); - return [min, ...filteredStops.map((step) => min + step * ((max - min) / 100)), max]; + const filteredStops = stops.filter((stop) => stop > 0 && stop < 100); + return [ + ...firstRanges, + ...filteredStops.map((step) => min + step * ((max - min) / 100)), + ...lastRanges, + ]; } const orderedStops = stops.filter((stop, i) => stop < max && stop > min); - return [min, ...orderedStops, max]; + return [...firstRanges, ...orderedStops, ...lastRanges]; } function getTitle( @@ -179,7 +221,7 @@ export const GaugeComponent: FC = memo( }, } ); - const colors = palette?.params?.colors ? normalizeColors(palette.params, min) : undefined; + const colors = palette?.params?.colors ? normalizeColors(palette.params, min, max) : undefined; const bands: number[] = (palette?.params as CustomPaletteState) ? normalizeBands(args.palette?.params as CustomPaletteState, { min, max }) : [min, max]; @@ -193,8 +235,8 @@ export const GaugeComponent: FC = memo( = min && goal <= max ? goal : undefined} + base={bands[0]} + target={goal && goal >= bands[0] && goal <= bands[bands.length - 1] ? goal : undefined} actual={formattedActual} tickValueFormatter={({ value: tickValue }) => tickFormatter.convert(tickValue)} bands={bands} @@ -205,6 +247,8 @@ export const GaugeComponent: FC = memo( const index = bands && bands.indexOf(val.value) - 1; return colors && index >= 0 && colors[index] ? colors[index] + : val.value <= bands[0] + ? colors[0] : colors[colors.length - 1]; } : () => `rgba(255,255,255,0)` diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx index 3713a4b3c01df..969db9263a4e6 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx @@ -271,8 +271,13 @@ export const HeatmapComponent: FC = memo( // adds a very small number to the max value to make sure the max value will be included const smattering = 0.00001; - const endValue = - (paletteParams?.range === 'number' ? paletteParams.rangeMax : max) + smattering; + let endValue = max + smattering; + if (paletteParams?.rangeMax || paletteParams?.rangeMax === 0) { + endValue = + (paletteParams?.range === 'number' + ? paletteParams.rangeMax + : min + ((max - min) * paletteParams.rangeMax) / 100) + smattering; + } const overwriteColors = uiState?.get('vis.colors') ?? null; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges.test.tsx b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges.test.tsx index 872bff882fbff..3010c9fcc47e9 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges.test.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges.test.tsx @@ -14,18 +14,18 @@ import { PaletteRegistry } from 'src/plugins/charts/public'; import { ColorRangesContext } from './color_ranges_context'; const extraActionSelectors = { - addColorRange: '[data-test-subj^="lnsPalettePanel_dynamicColoring_addColorRange"]', + addColor: '[data-test-subj^="lnsPalettePanel_dynamicColoring_addColor"]', reverseColors: '[data-test-subj^="lnsPalettePanel_dynamicColoring_reverseColors"]', - distributeEqually: '[data-test-subj="lnsPalettePanel_dynamicColoring_distributeEqually"]', + distributeValues: '[data-test-subj="lnsPalettePanel_dynamicColoring_distributeValues"]', }; const pageObjects = { getAddColorRangeButton: (component: ReactWrapper) => - component.find(extraActionSelectors.addColorRange).first(), + component.find(extraActionSelectors.addColor).first(), reverseColors: (component: ReactWrapper) => component.find(extraActionSelectors.reverseColors).first(), - distributeEqually: (component: ReactWrapper) => - component.find(extraActionSelectors.distributeEqually).first(), + distributeValues: (component: ReactWrapper) => + component.find(extraActionSelectors.distributeValues).first(), }; function renderColorRanges(props: ColorRangesProps) { @@ -142,7 +142,7 @@ describe('Color Ranges', () => { }); }); - it('should distribute equally ranges when use click on "Distribute equally" button', () => { + it('should distribute equally ranges when use click on "Distribute values" button', () => { props.colorRanges = [ { color: '#aaa', start: 0, end: 2 }, { color: '#bbb', start: 3, end: 4 }, @@ -153,7 +153,7 @@ describe('Color Ranges', () => { const component = renderColorRanges(props); act(() => { - pageObjects.distributeEqually(component).simulate('click'); + pageObjects.distributeValues(component).simulate('click'); }); component.update(); diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges.tsx b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges.tsx index 76cab5ba743d3..c22ac8f1d860b 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useState, useEffect, Dispatch, useContext } from 'react'; +import React, { useState, useEffect, Dispatch } from 'react'; import { EuiFlexGroup, EuiTextColor, EuiFlexItem } from '@elastic/eui'; @@ -22,8 +22,6 @@ import type { PaletteConfigurationActions } from '../types'; import { defaultPaletteParams } from '../constants'; -import { ColorRangesContext } from './color_ranges_context'; - export interface ColorRangesProps { colorRanges: ColorRange[]; paletteConfiguration: CustomPaletteParamsConfig | undefined; @@ -37,7 +35,6 @@ export function ColorRanges({ showExtraActions = true, dispatch, }: ColorRangesProps) { - const { dataBounds } = useContext(ColorRangesContext); const [colorRangesValidity, setColorRangesValidity] = useState< Record >({}); @@ -48,8 +45,8 @@ export function ColorRanges({ const rangeType = paletteConfiguration?.rangeType ?? defaultPaletteParams.rangeType; useEffect(() => { - setColorRangesValidity(validateColorRanges(colorRanges, dataBounds, rangeType)); - }, [colorRanges, rangeType, dataBounds]); + setColorRangesValidity(validateColorRanges(colorRanges)); + }, [colorRanges]); return ( { + const onDistributeValues = useCallback(() => { dispatch({ type: 'distributeEqually', payload: { dataBounds, palettes } }); }, [dataBounds, dispatch, palettes]); + const oneColorRangeWarn = i18n.translate( + 'xpack.lens.dynamicColoring.customPalette.oneColorRange', + { + defaultMessage: `Requires more than one color`, + } + ); + return ( @@ -59,13 +66,13 @@ export function ColorRangesExtraActions({ delay="regular" > - - - + + + + - - - + + + + ); diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_item.tsx b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_item.tsx index a6d66a9177ad5..7264704cc5251 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_item.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_item.tsx @@ -17,7 +17,6 @@ import { EuiIcon, EuiColorPickerSwatch, EuiButtonIcon, - EuiToolTip, EuiFieldNumberProps, } from '@elastic/eui'; @@ -39,7 +38,6 @@ import { checkIsMaxContinuity, checkIsMinContinuity, } from '../../../../../../../src/plugins/charts/common'; -import { getOutsideDataBoundsWarningMessage } from './color_ranges_validation'; export interface ColorRangesItemProps { colorRange: ColorRange; @@ -81,25 +79,13 @@ const getActionButton = (mode: ColorRangeItemMode) => { return mode === 'edit' ? ColorRangeAutoDetectButton : ColorRangeEditButton; }; -const getAppend = ( - rangeType: CustomPaletteParams['rangeType'], - mode: ColorRangeItemMode, - validation?: ColorRangeValidation -) => { +const getAppend = (rangeType: CustomPaletteParams['rangeType'], mode: ColorRangeItemMode) => { const items: EuiFieldNumberProps['append'] = []; - if (rangeType === 'percent') { + if (rangeType === 'percent' && mode !== 'auto') { items.push('%'); } - if (mode !== 'auto' && validation?.warnings.length) { - items.push( - - - - ); - } - return items; }; @@ -127,8 +113,12 @@ export function ColorRangeItem({ (e: FocusEvent) => { const prevStartValue = colorRanges[index - 1]?.start ?? Number.NEGATIVE_INFINITY; const nextStartValue = colorRanges[index + 1]?.start ?? Number.POSITIVE_INFINITY; + const lastEndValue = colorRanges[colorRanges.length - 1]?.end ?? Number.POSITIVE_INFINITY; - const shouldSort = colorRange.start > nextStartValue || prevStartValue > colorRange.start; + const shouldSort = + colorRange.start > nextStartValue || + prevStartValue > colorRange.start || + (!isLast && colorRange.start > lastEndValue); const isFocusStillInContent = (e.currentTarget as Node)?.contains(e.relatedTarget as Node) || popoverInFocus; @@ -136,7 +126,7 @@ export function ColorRangeItem({ dispatch({ type: 'sortColorRanges', payload: { dataBounds, palettes } }); } }, - [colorRange.start, colorRanges, dispatch, index, popoverInFocus, dataBounds, palettes] + [colorRange.start, colorRanges, dispatch, index, popoverInFocus, dataBounds, palettes, isLast] ); const onValueChange = useCallback( @@ -190,6 +180,7 @@ export function ColorRangeItem({ } secondaryInputDisplay="top" color={colorRange.color} + showAlpha={true} onFocus={() => setPopoverInFocus(true)} onBlur={() => { setPopoverInFocus(false); @@ -205,11 +196,13 @@ export function ColorRangeItem({ compressed fullWidth={true} isInvalid={!isValid} - value={mode !== 'auto' ? localValue : ''} + value={ + mode !== 'auto' && localValue !== undefined && isFinite(localValue) ? localValue : '' + } disabled={isDisabled} onChange={onValueChange} placeholder={mode === 'auto' ? getPlaceholderForAutoMode(isLast) : ''} - append={getAppend(rangeType, mode, validation)} + append={getAppend(rangeType, mode)} onBlur={onLeaveFocus} data-test-subj={`lnsPalettePanel_dynamicColoring_range_value_${index}`} prepend={{isLast ? '\u2264' : '\u2265'}} diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_item_buttons.tsx b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_item_buttons.tsx index 3f289395f7b7d..8e29a4fa2c6bf 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_item_buttons.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_item_buttons.tsx @@ -82,26 +82,28 @@ export function ColorRangeEditButton({ }); }, [isLast, dispatch, continuity, dataBounds, palettes]); - const title = i18n.translate('xpack.lens.dynamicColoring.customPalette.editButtonAriaLabel', { - defaultMessage: 'Edit', - }); + let tooltipContent = isLast + ? i18n.translate('xpack.lens.dynamicColoring.customPalette.setCustomMinValue', { + defaultMessage: `Set custom maximum value`, + }) + : i18n.translate('xpack.lens.dynamicColoring.customPalette.setCustomMaxValue', { + defaultMessage: `Set custom minimum value`, + }); + + if (disableSwitchingContinuity) { + tooltipContent = i18n.translate( + 'xpack.lens.dynamicColoring.customPalette.disallowedEditMinMaxValues', + { + defaultMessage: `You cannot set custom value for current configuration`, + } + ); + } return ( - + + + + ); } diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_validation.test.ts b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_validation.test.ts index a645d637bc6a5..d32936f89ebfa 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_validation.test.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_validation.test.ts @@ -27,99 +27,20 @@ describe('Color ranges validation', () => { color: '#aaa', }, ]; - const validation = validateColorRanges(colorRanges, { min: 0, max: 100 }, 'number'); + const validation = validateColorRanges(colorRanges); expect(validation['0']).toEqual({ errors: [], - warnings: [], isValid: true, }); expect(validation['1']).toEqual({ errors: ['invalidColor'], - warnings: [], isValid: false, }); expect(validation.last).toEqual({ errors: ['greaterThanMaxValue'], - warnings: [], isValid: false, }); }); - - it('should return correct warnings for color ranges', () => { - const colorRanges = [ - { - start: 0, - end: 10, - color: '#aaa', - }, - { - start: 10, - end: 20, - color: '#bbb', - }, - { - start: 20, - end: 35, - color: '#ccc', - }, - ]; - const validation = validateColorRanges(colorRanges, { min: 5, max: 30 }, 'number'); - expect(validation['0']).toEqual({ - errors: [], - warnings: ['lowerThanDataBounds'], - isValid: true, - }); - expect(validation['1']).toEqual({ - errors: [], - warnings: [], - isValid: true, - }); - expect(validation.last).toEqual({ - errors: [], - warnings: ['greaterThanDataBounds'], - isValid: true, - }); - }); - - it('should not return warnings for color ranges in number mode if we get fallback as data bounds', () => { - const colorRanges = [ - { - start: 0, - end: 10, - color: '#aaa', - }, - { - start: 10, - end: 20, - color: '#bbb', - }, - { - start: 20, - end: 35, - color: '#ccc', - }, - ]; - const validation = validateColorRanges( - colorRanges, - { min: 5, max: 30, fallback: true }, - 'number' - ); - expect(validation['0']).toEqual({ - errors: [], - warnings: [], - isValid: true, - }); - expect(validation['1']).toEqual({ - errors: [], - warnings: [], - isValid: true, - }); - expect(validation.last).toEqual({ - errors: [], - warnings: [], - isValid: true, - }); - }); }); describe('isAllColorRangesValid', () => { @@ -141,10 +62,10 @@ describe('Color ranges validation', () => { color: '#ccc', }, ]; - let isValid = isAllColorRangesValid(colorRanges, { min: 5, max: 40 }, 'number'); + let isValid = isAllColorRangesValid(colorRanges); expect(isValid).toBeFalsy(); colorRanges[colorRanges.length - 1].end = 30; - isValid = isAllColorRangesValid(colorRanges, { min: 5, max: 40 }, 'number'); + isValid = isAllColorRangesValid(colorRanges); expect(isValid).toBeTruthy(); }); }); diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_validation.tsx b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_validation.tsx index 30cfe38066378..d8e5083f5e971 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_validation.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/color_ranges_validation.tsx @@ -5,23 +5,16 @@ * 2.0. */ import { i18n } from '@kbn/i18n'; -import { getDataMinMax, isValidColor } from '../utils'; +import { isValidColor } from '../utils'; import type { ColorRange, ColorRangeAccessor } from './types'; -import type { DataBounds } from '../types'; - -import { CustomPaletteParams } from '../../../../common'; /** @internal **/ type ColorRangeValidationErrors = 'invalidColor' | 'invalidValue' | 'greaterThanMaxValue'; -/** @internal **/ -type ColorRangeValidationWarnings = 'lowerThanDataBounds' | 'greaterThanDataBounds'; - /** @internal **/ export interface ColorRangeValidation { errors: ColorRangeValidationErrors[]; - warnings: ColorRangeValidationWarnings[]; isValid: boolean; } @@ -44,7 +37,7 @@ export const getErrorMessages = (colorRangesValidity: Record { - for (const warning of warnings) { - switch (warning) { - case 'lowerThanDataBounds': - return i18n.translate('xpack.lens.dynamicColoring.customPalette.lowerThanDataBounds', { - defaultMessage: 'This value is outside the minimum data bound', - }); - case 'greaterThanDataBounds': - return i18n.translate('xpack.lens.dynamicColoring.customPalette.greaterThanDataBounds', { - defaultMessage: 'This value is outside the maximum data bound', - }); - } - } -}; - -const checkForComplianceWithDataBounds = (value: number, minMax?: [number, number]) => { - const warnings: ColorRangeValidationWarnings[] = []; - if (minMax) { - const [min, max] = minMax; - - if (value < min) { - warnings.push('lowerThanDataBounds'); - } - if (value > max) { - warnings.push('greaterThanDataBounds'); - } - } - - return warnings; -}; - /** @internal **/ -export const validateColorRange = ( - colorRange: ColorRange, - accessor: ColorRangeAccessor, - minMax?: [number, number] -) => { +export const validateColorRange = (colorRange: ColorRange, accessor: ColorRangeAccessor) => { const errors: ColorRangeValidationErrors[] = []; - let warnings: ColorRangeValidationWarnings[] = []; if (Number.isNaN(colorRange[accessor])) { errors.push('invalidValue'); @@ -102,53 +59,33 @@ export const validateColorRange = ( if (colorRange.start > colorRange.end) { errors.push('greaterThanMaxValue'); } - warnings = [...warnings, ...checkForComplianceWithDataBounds(colorRange.end, minMax)]; - } else { - if (!isValidColor(colorRange.color)) { - errors.push('invalidColor'); - } - warnings = [...warnings, ...checkForComplianceWithDataBounds(colorRange.start, minMax)]; + } else if (!isValidColor(colorRange.color)) { + errors.push('invalidColor'); } return { isValid: !errors.length, errors, - warnings, } as ColorRangeValidation; }; export const validateColorRanges = ( - colorRanges: ColorRange[], - dataBounds: DataBounds, - rangeType: CustomPaletteParams['rangeType'] + colorRanges: ColorRange[] ): Record => { - let minMax: [number, number] | undefined; - - if ((dataBounds.fallback && rangeType === 'percent') || !dataBounds.fallback) { - const { min, max } = getDataMinMax(rangeType, dataBounds); - minMax = [min, max]; - } - const validations = colorRanges.reduce>( (acc, item, index) => ({ ...acc, - [index]: validateColorRange(item, 'start', minMax), + [index]: validateColorRange(item, 'start'), }), {} ); return { ...validations, - last: validateColorRange(colorRanges[colorRanges.length - 1], 'end', minMax), + last: validateColorRange(colorRanges[colorRanges.length - 1], 'end'), }; }; -export const isAllColorRangesValid = ( - colorRanges: ColorRange[], - dataBounds: DataBounds, - rangeType: CustomPaletteParams['rangeType'] -) => { - return Object.values(validateColorRanges(colorRanges, dataBounds, rangeType)).every( - (colorRange) => colorRange.isValid - ); +export const isAllColorRangesValid = (colorRanges: ColorRange[]) => { + return Object.values(validateColorRanges(colorRanges)).every((colorRange) => colorRange.isValid); }; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/color_ranges_crud.test.ts b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/color_ranges_crud.test.ts index 837c66eeb1e5e..abfd64c6a84a9 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/color_ranges_crud.test.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/color_ranges_crud.test.ts @@ -23,16 +23,7 @@ describe('addColorRange', () => { ]; }); - it('should add new color range with the corresponding interval', () => { - expect(addColorRange(colorRanges, 'number', { min: 0, max: 81 })).toEqual([ - { color: '#aaa', start: 20, end: 40 }, - { color: '#bbb', start: 40, end: 60 }, - { color: '#ccc', start: 60, end: 80 }, - { color: '#ccc', start: 80, end: 81 }, - ]); - }); - - it('should add new color range with the interval equal 1 if new range out of max bound', () => { + it('should add new color range with the corresponding interva', () => { colorRanges[colorRanges.length - 1].end = 80; expect(addColorRange(colorRanges, 'number', { min: 0, max: 80 })).toEqual([ { color: '#aaa', start: 20, end: 40 }, diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/color_ranges_crud.ts b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/color_ranges_crud.ts index 6a2e92d284f01..b391c2bfac28e 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/color_ranges_crud.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/color_ranges_crud.ts @@ -5,7 +5,6 @@ * 2.0. */ import { getDataMinMax, roundValue } from '../../utils'; -import { calculateMaxStep } from './utils'; import type { ColorRange, ColorRangeAccessor } from '../types'; import type { DataBounds } from '../../types'; @@ -43,12 +42,7 @@ export const addColorRange = ( const { max: dataMax } = getDataMinMax(rangeType, dataBounds); const max = Math.max(dataMax, lastEnd); - const step = calculateMaxStep( - newColorRanges.map((item) => item.start), - max - ); - - let insertEnd = roundValue(Math.min(lastStart + step, max)); + let insertEnd = roundValue(Math.min(lastStart + 1, max)); if (insertEnd === Number.NEGATIVE_INFINITY) { insertEnd = 1; diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/utils.test.ts b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/utils.test.ts index daebb02e44e46..582ca454f82ce 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/utils.test.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/utils.test.ts @@ -9,7 +9,7 @@ import { sortColorRanges, calculateMaxStep, toColorStops, getValueForContinuity describe('utils', () => { it('sortColorRanges', () => { - const colorRanges = [ + let colorRanges = [ { color: '#aaa', start: 55, end: 40 }, { color: '#bbb', start: 40, end: 60 }, { color: '#ccc', start: 60, end: 80 }, @@ -19,6 +19,17 @@ describe('utils', () => { { color: '#aaa', start: 55, end: 60 }, { color: '#ccc', start: 60, end: 80 }, ]); + + colorRanges = [ + { color: '#aaa', start: 55, end: 90 }, + { color: '#bbb', start: 90, end: 60 }, + { color: '#ccc', start: 60, end: 80 }, + ]; + expect(sortColorRanges(colorRanges)).toEqual([ + { color: '#aaa', start: 55, end: 60 }, + { color: '#ccc', start: 60, end: 80 }, + { color: '#bbb', start: 80, end: 90 }, + ]); }); it('calculateMaxStep', () => { diff --git a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/utils.ts b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/utils.ts index 300df9b3b317b..6f0f3d5038916 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/utils.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/color_ranges/utils/utils.ts @@ -25,15 +25,21 @@ export const isLastItem = (accessor: ColorRangeAccessor) => accessor === 'end'; * @internal */ export const sortColorRanges = (colorRanges: ColorRange[]) => { - const maxValue = colorRanges[colorRanges.length - 1].end; + const lastRange = colorRanges[colorRanges.length - 1]; - return [...colorRanges] + return [...colorRanges, { start: lastRange.end, color: lastRange.color }] .sort(({ start: startA }, { start: startB }) => Number(startA) - Number(startB)) - .map((newColorRange, i, array) => ({ - color: newColorRange.color, - start: newColorRange.start, - end: i !== array.length - 1 ? array[i + 1].start : maxValue, - })); + .reduce((sortedColorRange, newColorRange, i, array) => { + const color = i === array.length - 2 ? array[i + 1].color : newColorRange.color; + if (i !== array.length - 1) { + sortedColorRange.push({ + color, + start: newColorRange.start, + end: array[i + 1].start, + }); + } + return sortedColorRange; + }, [] as ColorRange[]); }; /** @@ -91,16 +97,17 @@ export const getValueForContinuity = ( if (checkIsMaxContinuity(continuity)) { value = Number.POSITIVE_INFINITY; } else { - value = + value = roundValue( colorRanges[colorRanges.length - 1].start > max ? colorRanges[colorRanges.length - 1].start + 1 - : max; + : max + ); } } else { if (checkIsMinContinuity(continuity)) { value = Number.NEGATIVE_INFINITY; } else { - value = colorRanges[0].end < min ? colorRanges[0].end - 1 : min; + value = roundValue(colorRanges[0].end < min ? colorRanges[0].end - 1 : min); } } diff --git a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx index 104b8e4319e40..46aede7432b04 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx +++ b/x-pack/plugins/lens/public/shared_components/coloring/palette_configuration.tsx @@ -17,7 +17,6 @@ import './palette_configuration.scss'; import type { CustomPaletteParams, RequiredPaletteParamTypes } from '../../../common'; import { toColorRanges, getFallbackDataBounds } from './utils'; -import { defaultPaletteParams } from './constants'; import { ColorRanges, ColorRangesContext } from './color_ranges'; import { isAllColorRangesValid } from './color_ranges/color_ranges_validation'; import { paletteConfigurationReducer } from './palette_configuration_reducer'; @@ -52,12 +51,10 @@ export function CustomizablePalette({ useDebounce( () => { - const rangeType = - localState.activePalette?.params?.rangeType ?? defaultPaletteParams.rangeType; if ( (localState.activePalette !== activePalette || colorRangesToShow !== localState.colorRanges) && - isAllColorRangesValid(localState.colorRanges, dataBounds, rangeType) + isAllColorRangesValid(localState.colorRanges) ) { setPalette(localState.activePalette); } diff --git a/x-pack/plugins/lens/public/shared_components/coloring/utils.ts b/x-pack/plugins/lens/public/shared_components/coloring/utils.ts index 16cb843f3dfb4..d855999ecfcb6 100644 --- a/x-pack/plugins/lens/public/shared_components/coloring/utils.ts +++ b/x-pack/plugins/lens/public/shared_components/coloring/utils.ts @@ -248,6 +248,9 @@ export function calculateStop( oldInterval: number, newInterval: number ) { + if (oldInterval === 0) { + return newInterval + newMin; + } return roundValue(newMin + ((stopValue - oldMin) * newInterval) / oldInterval); } diff --git a/x-pack/plugins/lens/public/visualizations/gauge/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/gauge/dimension_editor.tsx index 18084a8c3db51..a6da226e654de 100644 --- a/x-pack/plugins/lens/public/visualizations/gauge/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/gauge/dimension_editor.tsx @@ -167,7 +167,6 @@ export function GaugeDimensionEditor( palettes={props.paletteService} activePalette={activePalette} dataBounds={currentMinMax} - disableSwitchingContinuity={true} setPalette={(newPalette) => { // if the new palette is not custom, replace the rangeMin with the artificial one if (