Skip to content

Commit

Permalink
[Lens] Dynamic Coloring Design Tweaks (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Marcialis authored May 17, 2021
1 parent 75f70c2 commit 1576a61
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 221 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.lnsDynamicColoringRow {
align-items: center;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
} from '../../shared_components/';
import { PalettePanelContainer } from './palette_panel_container';
import { findMinMaxByColumnId } from './shared_utils';
import './dimension_editor.scss';

const idPrefix = htmlIdGenerator()();

Expand Down Expand Up @@ -245,10 +246,11 @@ export function TableDimensionEditor(
</EuiFormRow>
{hasDynamicColoring && (
<EuiFormRow
className="lnsDynamicColoringRow"
display="columnCompressed"
fullWidth
label={i18n.translate('xpack.lens.paletteTableGradient.label', {
defaultMessage: 'Color palette',
defaultMessage: 'Color',
})}
>
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export function PalettePanelContainer({
>
<strong>
{i18n.translate('xpack.lens.table.palettePanelTitle', {
defaultMessage: 'Color palette settings',
defaultMessage: 'Edit color',
})}
</strong>
</h2>
Expand Down
332 changes: 169 additions & 163 deletions x-pack/plugins/lens/public/shared_components/coloring/color_stops.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import React, { useState, useEffect } from 'react';
import type { FocusEvent } from 'react';
import { i18n } from '@kbn/i18n';
import {
EuiFormRow,
EuiFieldNumber,
EuiColorPicker,
EuiButtonIcon,
EuiFlexItem,
EuiFlexGroup,
EuiButtonEmpty,
EuiSpacer,
EuiScreenReaderOnly,
} from '@elastic/eui';
import { DEFAULT_COLOR } from './constants';
Expand Down Expand Up @@ -70,178 +70,184 @@ export const CustomStops = ({

return (
<>
<EuiFlexGroup>
{sortedReason ? (
<EuiScreenReaderOnly>
<div>
<p aria-live="assertive">
{sortedReason
? i18n.translate('xpack.lens.dynamicColoring.customPalette.sortReason', {
defaultMessage: 'Color stops have been sorted due to new stop value {value}',
values: {
value: sortedReason,
},
})
: null}
</p>
</div>
<p aria-live="assertive">
{i18n.translate('xpack.lens.dynamicColoring.customPalette.sortReason', {
defaultMessage: 'Color stops have been sorted due to new stop value {value}',
values: {
value: sortedReason,
},
})}
</p>
</EuiScreenReaderOnly>
<EuiFlexItem data-test-subj={`${dataTestPrefix}_dynamicColoring_custom_stops`}>
{localColorStops.map(({ color, stop }, index) => {
const prevStopValue = Number(localColorStops[index - 1]?.stop ?? -Infinity);
const nextStopValue = Number(localColorStops[index + 1]?.stop ?? Infinity);
const errorMessages = [];
// do not show color error messages if number field is already in error
if (!isValidColor(color) && errorMessages.length === 0) {
errorMessages.push(
i18n.translate('xpack.lens.dynamicColoring.customPalette.hexWarningLabel', {
defaultMessage: 'Color must provide a valid hex value',
})
);
}
return (
<EuiFormRow
key={index}
display="rowCompressed"
isInvalid={Boolean(errorMessages.length)}
error={errorMessages[0]}
data-test-subj={`${dataTestPrefix}_dynamicColoring_stop_row_${index}`}
onBlur={(e: FocusEvent<HTMLDivElement>) => {
// sort the stops when the focus leaves the row container
const shouldSort = Number(stop) > nextStopValue || prevStopValue > Number(stop);
const isFocusStillInContent =
(e.currentTarget as Node)?.contains(e.relatedTarget as Node) || popoverInFocus;
if (shouldSort && !isFocusStillInContent) {
setLocalColorStops(
[...localColorStops].sort(
({ stop: stopA }, { stop: stopB }) => Number(stopA) - Number(stopB)
)
);
setSortReason(stop);
}
}}
>
<EuiFlexGroup gutterSize="s" responsive={false}>
<EuiFlexItem>
<EuiFieldNumber
compressed
data-test-subj={`${dataTestPrefix}_dynamicColoring_stop_value_${index}`}
value={stop}
min={-Infinity}
onChange={({ target }) => {
const newStopString = target.value.trim();
const newColorStops = [...localColorStops];
newColorStops[index] = {
color,
stop: newStopString,
};
setLocalColorStops(newColorStops);
}}
append={rangeType === 'percent' ? '%' : undefined}
) : null}

<EuiFlexGroup
data-test-subj={`${dataTestPrefix}_dynamicColoring_custom_stops`}
direction="column"
gutterSize="s"
>
{localColorStops.map(({ color, stop }, index) => {
const prevStopValue = Number(localColorStops[index - 1]?.stop ?? -Infinity);
const nextStopValue = Number(localColorStops[index + 1]?.stop ?? Infinity);

// TODO: Commented out the below error message code because 1) the individual color stops no longer use an `EuiFormRow` component and 2) I don't think such error message are needed, as the `EuiColorPicker` dropdown already has the hex field displaying such an error in the case of an invalid hex value. Instead of having a redundant error message, can we instead change it so that if an invalid hex value remains on blur of the `EuiColorPicker`, we simply revert to last known good hex value?
// const errorMessages = [];
// // do not show color error messages if number field is already in error
// if (!isValidColor(color) && errorMessages.length === 0) {
// errorMessages.push(
// i18n.translate('xpack.lens.dynamicColoring.customPalette.hexWarningLabel', {
// defaultMessage: 'Color must provide a valid hex value',
// })
// );
// }

return (
<EuiFlexItem
key={index}
// TODO: Commented out the below props, as they no longer apply to this component (as it has changed to an `EuiFlexItem`). Please see above comment for an alternative to adding a duplicative error message.
// isInvalid={Boolean(errorMessages.length)}
// error={errorMessages[0]}
data-test-subj={`${dataTestPrefix}_dynamicColoring_stop_row_${index}`}
onBlur={(e: FocusEvent<HTMLDivElement>) => {
// sort the stops when the focus leaves the row container
const shouldSort = Number(stop) > nextStopValue || prevStopValue > Number(stop);
const isFocusStillInContent =
(e.currentTarget as Node)?.contains(e.relatedTarget as Node) || popoverInFocus;
if (shouldSort && !isFocusStillInContent) {
setLocalColorStops(
[...localColorStops].sort(
({ stop: stopA }, { stop: stopB }) => Number(stopA) - Number(stopB)
)
);
setSortReason(stop);
}
}}
>
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
<EuiFlexItem>
<EuiFieldNumber
compressed
data-test-subj={`${dataTestPrefix}_dynamicColoring_stop_value_${index}`}
value={stop}
min={-Infinity}
onChange={({ target }) => {
const newStopString = target.value.trim();
const newColorStops = [...localColorStops];
newColorStops[index] = {
color,
stop: newStopString,
};
setLocalColorStops(newColorStops);
}}
append={rangeType === 'percent' ? '%' : undefined}
aria-label={i18n.translate(
'xpack.lens.dynamicColoring.customPalette.stopAriaLabel',
{
defaultMessage: 'Stop {index}',
values: {
index: index + 1,
},
}
)}
/>
</EuiFlexItem>

<EuiFlexItem
data-test-subj={`${dataTestPrefix}_dynamicColoring_stop_color_${index}`}
>
<EuiColorPicker
key={stop}
onChange={(newColor) => {
const newColorStops = [...localColorStops];
newColorStops[index] = { color: newColor, stop };
setLocalColorStops(newColorStops);
}}
secondaryInputDisplay="top"
color={color}
isInvalid={!isValidColor(color)}
showAlpha
compressed
onFocus={() => setPopoverInFocus(true)}
onBlur={() => setPopoverInFocus(false)}
/>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<TooltipWrapper
tooltipContent={i18n.translate(
'xpack.lens.dynamicColoring.customPalette.deleteButtonDisabled',
{
defaultMessage:
'This color stop cannot be deleted, as two or more stops are required',
}
)}
condition={!shouldEnableDelete}
>
<EuiButtonIcon
iconType="trash"
color="danger"
aria-label={i18n.translate(
'xpack.lens.dynamicColoring.customPalette.stopAriaLabel',
'xpack.lens.dynamicColoring.customPalette.deleteButtonAriaLabel',
{
defaultMessage: 'Stop {index}',
values: {
index: index + 1,
},
defaultMessage: 'Delete',
}
)}
/>
</EuiFlexItem>
<EuiFlexItem
data-test-subj={`${dataTestPrefix}_dynamicColoring_stop_color_${index}`}
>
<EuiColorPicker
key={stop}
onChange={(newColor) => {
const newColorStops = [...localColorStops];
newColorStops[index] = { color: newColor, stop };
setLocalColorStops(newColorStops);
}}
secondaryInputDisplay="top"
color={color}
isInvalid={!isValidColor(color)}
showAlpha
compressed
onFocus={() => setPopoverInFocus(true)}
onBlur={() => setPopoverInFocus(false)}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<TooltipWrapper
tooltipContent={i18n.translate(
'xpack.lens.dynamicColoring.customPalette.deleteButtonDisabled',
title={i18n.translate(
'xpack.lens.dynamicColoring.customPalette.deleteButtonLabel',
{
defaultMessage:
'This color stop cannot be deleted, as two or more stops are required',
defaultMessage: 'Delete',
}
)}
condition={!shouldEnableDelete}
>
<EuiButtonIcon
iconType="trash"
color="danger"
aria-label={i18n.translate(
'xpack.lens.dynamicColoring.customPalette.deleteButtonAriaLabel',
{
defaultMessage: 'Delete',
}
)}
title={i18n.translate(
'xpack.lens.dynamicColoring.customPalette.deleteButtonLabel',
{
defaultMessage: 'Delete',
}
)}
onClick={() => {
const newColorStops = localColorStops.filter((_, i) => i !== index);
setLocalColorStops(newColorStops);
}}
data-test-subj={`${dataTestPrefix}_dynamicColoring_removeStop_${index}`}
isDisabled={!shouldEnableDelete}
/>
</TooltipWrapper>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFormRow>
);
})}
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
data-test-subj={`${dataTestPrefix}_dynamicColoring_addStop`}
iconType="plusInCircle"
color="primary"
aria-label={i18n.translate('xpack.lens.dynamicColoring.customPalette.addColorStop', {
defaultMessage: 'Add color stop',
})}
size="xs"
onClick={() => {
const newColorStops = [...localColorStops];
const length = newColorStops.length;
const { max } = getDataMinMax(rangeType, dataBounds);
const step = getStepValue(
colorStops,
newColorStops.map(({ color, stop }) => ({ color, stop: Number(stop) })),
max
);
const prevColor = localColorStops[length - 1].color || DEFAULT_COLOR;
const newStop = step + Number(localColorStops[length - 1].stop);
newColorStops.push({
color: prevColor,
stop: String(newStop),
});
setLocalColorStops(newColorStops);
}}
>
{i18n.translate('xpack.lens.dynamicColoring.customPalette.addColorStop', {
defaultMessage: 'Add color stop',
})}
</EuiButtonEmpty>
</EuiFlexItem>
onClick={() => {
const newColorStops = localColorStops.filter((_, i) => i !== index);
setLocalColorStops(newColorStops);
}}
data-test-subj={`${dataTestPrefix}_dynamicColoring_removeStop_${index}`}
isDisabled={!shouldEnableDelete}
/>
</TooltipWrapper>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
);
})}
</EuiFlexGroup>

<EuiSpacer size="s" />

<EuiButtonEmpty
data-test-subj={`${dataTestPrefix}_dynamicColoring_addStop`}
iconType="plusInCircle"
color="primary"
aria-label={i18n.translate('xpack.lens.dynamicColoring.customPalette.addColorStop', {
defaultMessage: 'Add color stop',
})}
size="xs"
flush="left"
onClick={() => {
const newColorStops = [...localColorStops];
const length = newColorStops.length;
const { max } = getDataMinMax(rangeType, dataBounds);
const step = getStepValue(
colorStops,
newColorStops.map(({ color, stop }) => ({ color, stop: Number(stop) })),
max
);
const prevColor = localColorStops[length - 1].color || DEFAULT_COLOR;
const newStop = step + Number(localColorStops[length - 1].stop);
newColorStops.push({
color: prevColor,
stop: String(newStop),
});
setLocalColorStops(newColorStops);
}}
>
{i18n.translate('xpack.lens.dynamicColoring.customPalette.addColorStop', {
defaultMessage: 'Add color stop',
})}
</EuiButtonEmpty>
</>
);
};
Loading

0 comments on commit 1576a61

Please sign in to comment.