Skip to content

Commit

Permalink
[Lens] Color stop UI follow-ups (#123924)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

Co-authored-by: Kibana Machine <[email protected]>
Co-authored-by: Marta Bondyra <[email protected]>
  • Loading branch information
3 people authored Feb 2, 2022
1 parent a5ee534 commit e5a8cec
Show file tree
Hide file tree
Showing 17 changed files with 233 additions and 310 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -253,8 +253,8 @@ describe('GaugeComponent', function () {
},
} as GaugeRenderProps;
const goal = shallowWithIntl(<GaugeComponent {...customProps} />).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 = {
Expand All @@ -281,8 +281,8 @@ describe('GaugeComponent', function () {
},
} as GaugeRenderProps;
const goal = shallowWithIntl(<GaugeComponent {...customProps} />).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 = {
Expand All @@ -294,7 +294,7 @@ describe('GaugeComponent', function () {
stops: [-20, -60, 80],
range: 'percent',
rangeMin: 0,
rangeMax: 4,
rangeMax: 100,
},
};
const customProps = {
Expand Down Expand Up @@ -407,7 +407,7 @@ describe('GaugeComponent', function () {
stops: [20, 60, 80],
range: 'percent',
rangeMin: 0,
rangeMax: 10,
rangeMax: 100,
},
};
const customProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,73 @@ 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;
}
const colorsOutOfRangeSmaller = Math.max(
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(
Expand Down Expand Up @@ -179,7 +221,7 @@ export const GaugeComponent: FC<GaugeRenderProps> = 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];
Expand All @@ -193,8 +235,8 @@ export const GaugeComponent: FC<GaugeRenderProps> = memo(
<Goal
id="goal"
subtype={subtype}
base={min}
target={goal && goal >= 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}
Expand All @@ -205,6 +247,8 @@ export const GaugeComponent: FC<GaugeRenderProps> = 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)`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,13 @@ export const HeatmapComponent: FC<HeatmapRenderProps> = 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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 },
Expand All @@ -153,7 +153,7 @@ describe('Color Ranges', () => {
const component = renderColorRanges(props);

act(() => {
pageObjects.distributeEqually(component).simulate('click');
pageObjects.distributeValues(component).simulate('click');
});

component.update();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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;
Expand All @@ -37,7 +35,6 @@ export function ColorRanges({
showExtraActions = true,
dispatch,
}: ColorRangesProps) {
const { dataBounds } = useContext(ColorRangesContext);
const [colorRangesValidity, setColorRangesValidity] = useState<
Record<string, ColorRangeValidation>
>({});
Expand All @@ -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 (
<EuiFlexGroup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,17 @@ export function ColorRangesExtraActions({
dispatch({ type: 'reversePalette', payload: { dataBounds, palettes } });
}, [dispatch, dataBounds, palettes]);

const onDistributeEqually = useCallback(() => {
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 (
<EuiFlexGroup justifyContent="flexStart" gutterSize="none" wrap={true}>
<EuiFlexItem grow={false}>
Expand All @@ -59,13 +66,13 @@ export function ColorRangesExtraActions({
delay="regular"
>
<EuiButtonEmpty
data-test-subj={`lnsPalettePanel_dynamicColoring_addColorRange`}
data-test-subj={`lnsPalettePanel_dynamicColoring_addColor`}
iconType="plusInCircle"
color="primary"
aria-label={i18n.translate(
'xpack.lens.dynamicColoring.customPalette.addColorRangeAriaLabel',
'xpack.lens.dynamicColoring.customPalette.addColorAriaLabel',
{
defaultMessage: 'Add color range',
defaultMessage: 'Add color',
}
)}
size="xs"
Expand All @@ -74,52 +81,66 @@ export function ColorRangesExtraActions({
onClick={onAddColorRange}
>
<FormattedMessage
id="xpack.lens.dynamicColoring.customPalette.addColorRange"
defaultMessage="Add color range"
id="xpack.lens.dynamicColoring.customPalette.addColor"
defaultMessage="Add color"
/>
</EuiButtonEmpty>
</TooltipWrapper>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj={`lnsPalettePanel_dynamicColoring_reverseColors`}
iconType="sortable"
color="primary"
aria-label={i18n.translate('xpack.lens.dynamicColoring.customPaletteAriaLabel', {
defaultMessage: 'Reverse colors',
})}
size="xs"
flush="left"
onClick={onReversePalette}
disabled={shouldDisableReverse}
<TooltipWrapper
tooltipContent={oneColorRangeWarn}
condition={shouldDisableReverse}
position="top"
delay="regular"
>
<FormattedMessage
id="xpack.lens.dynamicColoring.customPalette.reverseColors"
defaultMessage="Reverse colors"
/>
</EuiButtonEmpty>
<EuiButtonEmpty
data-test-subj={`lnsPalettePanel_dynamicColoring_reverseColors`}
iconType="sortable"
color="primary"
aria-label={i18n.translate('xpack.lens.dynamicColoring.customPaletteAriaLabel', {
defaultMessage: 'Reverse colors',
})}
size="xs"
flush="left"
onClick={onReversePalette}
disabled={shouldDisableReverse}
>
<FormattedMessage
id="xpack.lens.dynamicColoring.customPalette.reverseColors"
defaultMessage="Reverse colors"
/>
</EuiButtonEmpty>
</TooltipWrapper>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj={`lnsPalettePanel_dynamicColoring_distributeEqually`}
iconType={DistributeEquallyIcon}
color="primary"
aria-label={i18n.translate(
'xpack.lens.dynamicColoring.customPalette.distributeEquallyAriaLabel',
{
defaultMessage: 'Distribute equally',
}
)}
size="xs"
flush="left"
disabled={shouldDisableDistribute}
onClick={onDistributeEqually}
<TooltipWrapper
tooltipContent={oneColorRangeWarn}
condition={shouldDisableDistribute}
position="top"
delay="regular"
>
<FormattedMessage
id="xpack.lens.dynamicColoring.customPalette.distributeEqually"
defaultMessage="Distribute equally"
/>
</EuiButtonEmpty>
<EuiButtonEmpty
data-test-subj={`lnsPalettePanel_dynamicColoring_distributeValues`}
iconType={DistributeEquallyIcon}
color="primary"
aria-label={i18n.translate(
'xpack.lens.dynamicColoring.customPalette.distributeValuesAriaLabel',
{
defaultMessage: 'Distribute values',
}
)}
size="xs"
flush="left"
disabled={shouldDisableDistribute}
onClick={onDistributeValues}
>
<FormattedMessage
id="xpack.lens.dynamicColoring.customPalette.distributeValues"
defaultMessage="Distribute values"
/>
</EuiButtonEmpty>
</TooltipWrapper>
</EuiFlexItem>
</EuiFlexGroup>
);
Expand Down
Loading

0 comments on commit e5a8cec

Please sign in to comment.