[Lens] Threshold: set default color for new thresholds (#113008)
* 💄 Make dark grey default threshold color * ✅ Fix test * 👌 Integrate feedback * 👌 Fix bug * 👌 Filter threshold layers for color assignments * 👌 Small refactor * 🐛 Fix merging conflicts Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
fd0648daab
commit
218deff1cb
|
@ -8,9 +8,10 @@
|
||||||
import { uniq, mapValues } from 'lodash';
|
import { uniq, mapValues } from 'lodash';
|
||||||
import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public';
|
import type { PaletteOutput, PaletteRegistry } from 'src/plugins/charts/public';
|
||||||
import type { Datatable } from 'src/plugins/expressions';
|
import type { Datatable } from 'src/plugins/expressions';
|
||||||
|
import { euiLightVars } from '@kbn/ui-shared-deps-src/theme';
|
||||||
import type { AccessorConfig, FramePublicAPI } from '../types';
|
import type { AccessorConfig, FramePublicAPI } from '../types';
|
||||||
import { getColumnToLabelMap } from './state_helpers';
|
import { getColumnToLabelMap } from './state_helpers';
|
||||||
import type { FormatFactory } from '../../common';
|
import { FormatFactory, LayerType, layerTypes } from '../../common';
|
||||||
import type { XYLayerConfig } from '../../common/expressions';
|
import type { XYLayerConfig } from '../../common/expressions';
|
||||||
|
|
||||||
const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object';
|
const isPrimitive = (value: unknown): boolean => value != null && typeof value !== 'object';
|
||||||
|
@ -20,8 +21,11 @@ interface LayerColorConfig {
|
||||||
splitAccessor?: string;
|
splitAccessor?: string;
|
||||||
accessors: string[];
|
accessors: string[];
|
||||||
layerId: string;
|
layerId: string;
|
||||||
|
layerType: LayerType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const defaultThresholdColor = euiLightVars.euiColorDarkShade;
|
||||||
|
|
||||||
export type ColorAssignments = Record<
|
export type ColorAssignments = Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
|
@ -37,13 +41,15 @@ export function getColorAssignments(
|
||||||
): ColorAssignments {
|
): ColorAssignments {
|
||||||
const layersPerPalette: Record<string, LayerColorConfig[]> = {};
|
const layersPerPalette: Record<string, LayerColorConfig[]> = {};
|
||||||
|
|
||||||
layers.forEach((layer) => {
|
layers
|
||||||
const palette = layer.palette?.name || 'default';
|
.filter(({ layerType }) => layerType === layerTypes.DATA)
|
||||||
if (!layersPerPalette[palette]) {
|
.forEach((layer) => {
|
||||||
layersPerPalette[palette] = [];
|
const palette = layer.palette?.name || 'default';
|
||||||
}
|
if (!layersPerPalette[palette]) {
|
||||||
layersPerPalette[palette].push(layer);
|
layersPerPalette[palette] = [];
|
||||||
});
|
}
|
||||||
|
layersPerPalette[palette].push(layer);
|
||||||
|
});
|
||||||
|
|
||||||
return mapValues(layersPerPalette, (paletteLayers) => {
|
return mapValues(layersPerPalette, (paletteLayers) => {
|
||||||
const seriesPerLayer = paletteLayers.map((layer, layerIndex) => {
|
const seriesPerLayer = paletteLayers.map((layer, layerIndex) => {
|
||||||
|
@ -111,6 +117,13 @@ export function getAccessorColorConfig(
|
||||||
triggerIcon: 'disabled',
|
triggerIcon: 'disabled',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (layer.layerType === layerTypes.THRESHOLD) {
|
||||||
|
return {
|
||||||
|
columnId: accessor as string,
|
||||||
|
triggerIcon: 'color',
|
||||||
|
color: currentYConfig?.color || defaultThresholdColor,
|
||||||
|
};
|
||||||
|
}
|
||||||
const columnToLabel = getColumnToLabelMap(layer, frame.datasourceLayers[layer.layerId]);
|
const columnToLabel = getColumnToLabelMap(layer, frame.datasourceLayers[layer.layerId]);
|
||||||
const rank = colorAssignments[currentPalette.name].getRank(
|
const rank = colorAssignments[currentPalette.name].getRank(
|
||||||
layer,
|
layer,
|
||||||
|
@ -133,7 +146,7 @@ export function getAccessorColorConfig(
|
||||||
return {
|
return {
|
||||||
columnId: accessor as string,
|
columnId: accessor as string,
|
||||||
triggerIcon: customColor ? 'color' : 'disabled',
|
triggerIcon: customColor ? 'color' : 'disabled',
|
||||||
color: customColor ? customColor : undefined,
|
color: customColor ?? undefined,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -836,7 +836,6 @@ export function XYChart({
|
||||||
<ThresholdAnnotations
|
<ThresholdAnnotations
|
||||||
thresholdLayers={thresholdLayers}
|
thresholdLayers={thresholdLayers}
|
||||||
data={data}
|
data={data}
|
||||||
colorAssignments={colorAssignments}
|
|
||||||
syncColors={syncColors}
|
syncColors={syncColors}
|
||||||
paletteService={paletteService}
|
paletteService={paletteService}
|
||||||
formatters={{
|
formatters={{
|
||||||
|
|
|
@ -9,23 +9,21 @@ import React from 'react';
|
||||||
import { groupBy } from 'lodash';
|
import { groupBy } from 'lodash';
|
||||||
import { EuiIcon } from '@elastic/eui';
|
import { EuiIcon } from '@elastic/eui';
|
||||||
import { RectAnnotation, AnnotationDomainType, LineAnnotation } from '@elastic/charts';
|
import { RectAnnotation, AnnotationDomainType, LineAnnotation } from '@elastic/charts';
|
||||||
import type { PaletteRegistry, SeriesLayer } from 'src/plugins/charts/public';
|
import type { PaletteRegistry } from 'src/plugins/charts/public';
|
||||||
import type { FieldFormat } from 'src/plugins/field_formats/common';
|
import type { FieldFormat } from 'src/plugins/field_formats/common';
|
||||||
|
import { euiLightVars } from '@kbn/ui-shared-deps-src/theme';
|
||||||
import type { LayerArgs } from '../../common/expressions';
|
import type { LayerArgs } from '../../common/expressions';
|
||||||
import type { LensMultiTable } from '../../common/types';
|
import type { LensMultiTable } from '../../common/types';
|
||||||
import type { ColorAssignments } from './color_assignment';
|
|
||||||
|
|
||||||
export const ThresholdAnnotations = ({
|
export const ThresholdAnnotations = ({
|
||||||
thresholdLayers,
|
thresholdLayers,
|
||||||
data,
|
data,
|
||||||
colorAssignments,
|
|
||||||
formatters,
|
formatters,
|
||||||
paletteService,
|
paletteService,
|
||||||
syncColors,
|
syncColors,
|
||||||
}: {
|
}: {
|
||||||
thresholdLayers: LayerArgs[];
|
thresholdLayers: LayerArgs[];
|
||||||
data: LensMultiTable;
|
data: LensMultiTable;
|
||||||
colorAssignments: ColorAssignments;
|
|
||||||
formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>;
|
formatters: Record<'left' | 'right' | 'bottom', FieldFormat | undefined>;
|
||||||
paletteService: PaletteRegistry;
|
paletteService: PaletteRegistry;
|
||||||
syncColors: boolean;
|
syncColors: boolean;
|
||||||
|
@ -36,12 +34,11 @@ export const ThresholdAnnotations = ({
|
||||||
if (!thresholdLayer.yConfig) {
|
if (!thresholdLayer.yConfig) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const { columnToLabel, palette, yConfig: yConfigs, layerId } = thresholdLayer;
|
const { columnToLabel, yConfig: yConfigs, layerId } = thresholdLayer;
|
||||||
const columnToLabelMap: Record<string, string> = columnToLabel
|
const columnToLabelMap: Record<string, string> = columnToLabel
|
||||||
? JSON.parse(columnToLabel)
|
? JSON.parse(columnToLabel)
|
||||||
: {};
|
: {};
|
||||||
const table = data.tables[layerId];
|
const table = data.tables[layerId];
|
||||||
const colorAssignment = colorAssignments[palette.name];
|
|
||||||
|
|
||||||
const row = table.rows[0];
|
const row = table.rows[0];
|
||||||
|
|
||||||
|
@ -62,27 +59,7 @@ export const ThresholdAnnotations = ({
|
||||||
|
|
||||||
const formatter = formatters[groupId || 'bottom'];
|
const formatter = formatters[groupId || 'bottom'];
|
||||||
|
|
||||||
const seriesLayers: SeriesLayer[] = [
|
const defaultColor = euiLightVars.euiColorDarkShade;
|
||||||
{
|
|
||||||
name: columnToLabelMap[yConfig.forAccessor],
|
|
||||||
totalSeriesAtDepth: colorAssignment.totalSeriesCount,
|
|
||||||
rankAtDepth: colorAssignment.getRank(
|
|
||||||
thresholdLayer,
|
|
||||||
String(yConfig.forAccessor),
|
|
||||||
String(yConfig.forAccessor)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const defaultColor = paletteService.get(palette.name).getCategoricalColor(
|
|
||||||
seriesLayers,
|
|
||||||
{
|
|
||||||
maxDepth: 1,
|
|
||||||
behindText: false,
|
|
||||||
totalSeries: colorAssignment.totalSeriesCount,
|
|
||||||
syncColors,
|
|
||||||
},
|
|
||||||
palette.params
|
|
||||||
);
|
|
||||||
|
|
||||||
const props = {
|
const props = {
|
||||||
groupId,
|
groupId,
|
||||||
|
@ -99,7 +76,7 @@ export const ThresholdAnnotations = ({
|
||||||
|
|
||||||
const sharedStyle = {
|
const sharedStyle = {
|
||||||
strokeWidth: yConfig.lineWidth || 1,
|
strokeWidth: yConfig.lineWidth || 1,
|
||||||
stroke: (yConfig.color || defaultColor) ?? '#f00',
|
stroke: yConfig.color || defaultColor,
|
||||||
dash: dashStyle,
|
dash: dashStyle,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -179,7 +156,7 @@ export const ThresholdAnnotations = ({
|
||||||
})}
|
})}
|
||||||
style={{
|
style={{
|
||||||
...sharedStyle,
|
...sharedStyle,
|
||||||
fill: (yConfig.color || defaultColor) ?? '#f00',
|
fill: yConfig.color || defaultColor,
|
||||||
opacity: 0.1,
|
opacity: 0.1,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { OperationMetadata, DatasourcePublicAPI } from '../types';
|
||||||
import { getColumnToLabelMap } from './state_helpers';
|
import { getColumnToLabelMap } from './state_helpers';
|
||||||
import type { ValidLayer, XYLayerConfig } from '../../common/expressions';
|
import type { ValidLayer, XYLayerConfig } from '../../common/expressions';
|
||||||
import { layerTypes } from '../../common';
|
import { layerTypes } from '../../common';
|
||||||
|
import { defaultThresholdColor } from './color_assignment';
|
||||||
|
|
||||||
export const getSortedAccessors = (datasource: DatasourcePublicAPI, layer: XYLayerConfig) => {
|
export const getSortedAccessors = (datasource: DatasourcePublicAPI, layer: XYLayerConfig) => {
|
||||||
const originalOrder = datasource
|
const originalOrder = datasource
|
||||||
|
@ -334,9 +335,9 @@ export const buildExpression = (
|
||||||
arguments: {
|
arguments: {
|
||||||
forAccessor: [yConfig.forAccessor],
|
forAccessor: [yConfig.forAccessor],
|
||||||
axisMode: yConfig.axisMode ? [yConfig.axisMode] : [],
|
axisMode: yConfig.axisMode ? [yConfig.axisMode] : [],
|
||||||
color: yConfig.color ? [yConfig.color] : [],
|
color: [yConfig.color || defaultThresholdColor],
|
||||||
lineStyle: yConfig.lineStyle ? [yConfig.lineStyle] : [],
|
lineStyle: [yConfig.lineStyle || 'solid'],
|
||||||
lineWidth: yConfig.lineWidth ? [yConfig.lineWidth] : [],
|
lineWidth: [yConfig.lineWidth || 1],
|
||||||
fill: [yConfig.fill || 'none'],
|
fill: [yConfig.fill || 'none'],
|
||||||
icon: yConfig.icon ? [yConfig.icon] : [],
|
icon: yConfig.icon ? [yConfig.icon] : [],
|
||||||
},
|
},
|
||||||
|
|
|
@ -347,9 +347,6 @@ describe('xy_visualization', () => {
|
||||||
{
|
{
|
||||||
axisMode: 'bottom',
|
axisMode: 'bottom',
|
||||||
forAccessor: 'newCol',
|
forAccessor: 'newCol',
|
||||||
icon: undefined,
|
|
||||||
lineStyle: 'solid',
|
|
||||||
lineWidth: 1,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
@ -448,12 +448,7 @@ export const getXyVisualization = ({
|
||||||
if (!hasYConfig) {
|
if (!hasYConfig) {
|
||||||
newLayer.yConfig = [
|
newLayer.yConfig = [
|
||||||
...(newLayer.yConfig || []),
|
...(newLayer.yConfig || []),
|
||||||
// TODO: move this
|
|
||||||
// add a default config if none is available
|
|
||||||
{
|
{
|
||||||
icon: undefined,
|
|
||||||
lineStyle: 'solid',
|
|
||||||
lineWidth: 1,
|
|
||||||
// override with previous styling,
|
// override with previous styling,
|
||||||
...previousYConfig,
|
...previousYConfig,
|
||||||
// but keep the new group & id config
|
// but keep the new group & id config
|
||||||
|
|
|
@ -13,9 +13,13 @@ import { EuiFormRow, EuiColorPicker, EuiColorPickerProps, EuiToolTip, EuiIcon }
|
||||||
import type { PaletteRegistry } from 'src/plugins/charts/public';
|
import type { PaletteRegistry } from 'src/plugins/charts/public';
|
||||||
import type { VisualizationDimensionEditorProps } from '../../types';
|
import type { VisualizationDimensionEditorProps } from '../../types';
|
||||||
import { State } from '../types';
|
import { State } from '../types';
|
||||||
import { FormatFactory } from '../../../common';
|
import { FormatFactory, layerTypes } from '../../../common';
|
||||||
import { getSeriesColor } from '../state_helpers';
|
import { getSeriesColor } from '../state_helpers';
|
||||||
import { getAccessorColorConfig, getColorAssignments } from '../color_assignment';
|
import {
|
||||||
|
defaultThresholdColor,
|
||||||
|
getAccessorColorConfig,
|
||||||
|
getColorAssignments,
|
||||||
|
} from '../color_assignment';
|
||||||
import { getSortedAccessors } from '../to_expression';
|
import { getSortedAccessors } from '../to_expression';
|
||||||
import { updateLayer } from '.';
|
import { updateLayer } from '.';
|
||||||
import { TooltipWrapper } from '../../shared_components';
|
import { TooltipWrapper } from '../../shared_components';
|
||||||
|
@ -56,6 +60,9 @@ export const ColorPicker = ({
|
||||||
const overwriteColor = getSeriesColor(layer, accessor);
|
const overwriteColor = getSeriesColor(layer, accessor);
|
||||||
const currentColor = useMemo(() => {
|
const currentColor = useMemo(() => {
|
||||||
if (overwriteColor || !frame.activeData) return overwriteColor;
|
if (overwriteColor || !frame.activeData) return overwriteColor;
|
||||||
|
if (layer.layerType === layerTypes.THRESHOLD) {
|
||||||
|
return defaultThresholdColor;
|
||||||
|
}
|
||||||
|
|
||||||
const datasource = frame.datasourceLayers[layer.layerId];
|
const datasource = frame.datasourceLayers[layer.layerId];
|
||||||
const sortedAccessors: string[] = getSortedAccessors(datasource, layer);
|
const sortedAccessors: string[] = getSortedAccessors(datasource, layer);
|
||||||
|
|
Loading…
Reference in a new issue